ROS 프로그래밍 전에 알아둬야 할 사항
1. 표준단위로 SI 단위 사용
2. 좌표 표현 방식
x: forward y: left z: up
오른손법칙
3. 회전할때 시계 반대방향이 +, 시계방향이 -
표준 단위
angle: radian / frequency: hertz / force: newton / power: watt / voltage: volt / length: meter / mass: kiligram
time: second / current: ampere / temperature: celsius
프로그래밍 규칙은 위키 참조
https://wiki.ros.org/CppStyleGuide
ROS 기본 프로그래밍
Topic
1) 패키지 생성
$cd ~/catkin_ws/src
(~는 home폴더에 있는 자기 이름 폴더)에 있는 catkin_ws폴더에 있는 패키지를 모아놓은 폴더로 이동.
$catkin_create_pkg ros_tutorials_topic message_generation std_msgs roscpp
ros_tutorials_topic이라는 패키지를 만들고 이 패키지는 message_generation, std_msgs, roscpp에 의존성을 갖는다.
$cd ros_tutorials_topic
이 폴더로 이동
$ls
리스트를 뜻하고 폴더안에 있는 파일들을 보여준다.
2) 패키지 설정 파일(package.xml) 수정
ROS의 필수 설정 파일 중 하나인 package.xml은 패키지 정보를 담은 XML 파일로서 패키지 이름, 저작자, 라이선스, 의존성 패키지 등을 기술하고 있다.
$gedit package.xml
gedit는 우분투의 가장 기본적인 메모장
<?xml version="1.0"?>
<package format="2">
<name>ros_tutorials_topic</name> //패키지 이름
<version>0.1.0</version>
<description>ROS turtorial package to learn the topic</description> // 패키지의 간략한 설명을 넣어주면 된다. (위키에서 검색하는데 사용돼서 중요하다.)
<license>Apache 2.0</license>
<author email="pyo@robotis.com">Yoonseok Pyo</author>
<maintainer email="pyo@robotis.com">Yoonseok Pyo</maintainer>
<url type="website">http://www.robotis.com</url>
<url type="repository">https://github.com/ROBOTIS-GIT/ros_tutorials.git</url> // 소스코드 주소
<url type="bugtracker">https://github.com/ROBOTIS-GIT/ros_tutorials/issues</url > // 버그 제보 주소
<buildtool_depend>catkin</buildtool_depend>
<depend>roscpp</depend> // C++를 사용한다는 뜻
<depend>std_msgs</depend> // standard한 메시지를 사용할거다.
<depend>message_generation</depend> // 새로운 메시지를 만들거다.
<export></export >
</package>
3) 빌드 설정 파일(CMakeLists.txt) 수정
$ gedit CMakeLists.txt
실제 노드를 빌드할 때 즉 소스코드를 작성하고 실행파일을 만들 때 사용되는 옵션을 작성해놓은 것.
cmake_minimum_required(VERSION 2.8.3)
project(ros_tutorials_topic)
패키지랑 동일한 이름으로 해야한다.
find_package(catkin REQUIRED COMPONENTS message_generation std_msgs roscpp)
add_message_files(FILES MsgTutorial.msg)
새로 만들것이기 때문에 새로 만들 메시지 이름을 쓰기
generate_messages(DEPENDENCIES std_msgs)
catkin_package(
LIBRARIES ros_tutorials_topic
CATKIN_DEPENDS std_msgs roscpp
)
## 인클루드 디렉터리를 설정한다.
include_directories(${catkin_INCLUDE_DIRS})
뒤에 include붙이면 본인 include파일 사용 가능
add_executable(topic_publisher src/topic_publisher.cpp)
topic_publisher라는 노드를 만들꺼고 그 이름을 갖는 노드를 만들 때 참고하는 소스코드는 src/topic_publisher.cpp이다.
add_dependencies(topic_publisher ${${PROJECT_NAME}_EXPORTED_TARGETS}
헤더파일 먼저 빌드하고 topic_publisher 메시지를 빌드한다는 뜻
${catkin_EXPORTED_TARGETS})
target_link_libraries(topic_publisher ${catkin_LIBRARIES})
add_executable(topic_subscriber src/topic_subscriber.cpp)
add_dependencies(topic_subscriber ${${PROJECT_NAME}_EXPORTED_TARGETS}
${catkin_EXPORTED_TARGETS})
target_link_libraries(topic_subscriber ${catkin_LIBRARIES})
4) 메시지 파일 작성
$ roscd ros_tutorials_topic
roscd하면 그 뒤의 패키지 이름이 있는곳으로 이동
$ mkdir msg
ros_tutorials_topic 패키지에 msg라는 메시지 폴더를 신규 작성
$ cd msg
작성한 msg 폴더로 이동
$ gedit MsgTutorial.msg
MsgTutorial.msg 파일 신규 작성 및 내용 수정
여기에 time stamp, int32 data 이 두 개의 메시지 형을 넣어준다.
$ cd .. → ros_tutorials_topic 패키지 폴더로 이동
5) 퍼블리셔 노드 작성
$ cd src //ros_tutorials_topic 패키지의 소스 폴더인 src 폴더로 이동
$ gedit topic_publisher.cpp // 소스 파일 신규 작성 및 내용 수정
켜진 gedit에 다음 내용을 넣으면 된다.
#include "ros/ros.h" // ROS 기본 헤더파일
#include "ros_tutorials_topic/MsgTutorial.h"// MsgTutorial 메시지 파일 헤더(빌드 후 자동 생성됨)
int main(int argc, char **argv) // 노드 메인 함수
{
ros::init(argc, argv, "topic_publisher"); // 노드명 초기화
ros::NodeHandle nh; // ROS 시스템과 통신을 위한 노드 핸들 선언
노드 핸들을 선언한다는것은 특정 노드와 관련된 ROS 리소스(퍼블리셔, 서브스크라이버)등을 관리하고 접근하기 위한 인터페이스를 생성하는 것을 의미한다. 이를 통해 노드는 다른 노드와 데이터를 주고받을 수 있으며, 다양한 ROS 기능을 활용할 수 있게된다.
// 퍼블리셔 선언, ros_tutorials_topic 패키지의 MsgTutorial 메시지 파일을 이용한
// 퍼블리셔 ros_tutorial_pub 를 작성한다. 토픽명은 "ros_tutorial_msg" 이며,
// 퍼블리셔 큐(queue) 사이즈를 100개로 설정한다는 것이다
ros::Publisher ros_tutorial_pub = nh.advertise<ros_tutorials_topic::MsgTutorial>("ros_tutorial_msg", 100);
// 루프 주기를 설정한다. "10" 이라는 것은 10Hz를 말하는 것으로 0.1초 간격으로 반복된다
ros::Rate loop_rate(10);
// MsgTutorial 메시지 파일 형식으로 msg 라는 메시지를 선언
ros_tutorials_topic::MsgTutorial msg;
// 메시지에 사용될 변수 선언
int count = 0;
while (ros::ok())
{
msg.stamp = ros::Time::now(); // 현재 시간을 msg의 하위 stamp 메시지에 담는다
msg.data = count; // count라는 변수 값을 msg의 하위 data 메시지에 담는다
ROS_INFO("send msg = %d", msg.stamp.sec); // stamp.sec 메시지를 표시한다
ROS_INFO("send msg = %d", msg.stamp.nsec); // stamp.nsec 메시지를 표시한다
ROS_INFO("send msg = %d", msg.data); // data 메시지를 표시한다
ros_tutorial_pub.publish(msg); // 메시지를 발행한다
loop_rate.sleep(); // 위에서 정한 루프 주기에 따라 슬립에 들어간다
++count; // count 변수 1씩 증가
}
return 0;
}
위의 코드에서 ROS_INFO는 printf로 보면 된다.
6) 서브스크라이버 노드 작성
$ roscd ros_tutorials_topic/src
패키지의 소스 폴더인 src 폴더로 이동
$ gedit topic_subscriber.cpp
소스 파일 신규 작성 및 내용 수정
켜진 gedit에 다음 내용을 넣으면 된다.
#include "ros/ros.h" // ROS 기본 헤더파일
#include "ros_tutorials_topic/MsgTutorial.h" // MsgTutorial 메시지 파일 헤더 (빌드 후 자동 생성됨)
// 메시지 콜백 함수로써, 밑에서 설정한 ros_tutorial_msg라는 이름의 토픽
// 메시지를 수신하였을 때 동작하는 함수이다
// 입력 메시지로는 ros_tutorials_topic 패키지의 MsgTutorial 메시지를 받도록 되어있다
void msgCallback(const ros_tutorials_topic::MsgTutorial::ConstPtr& msg)
{
ROS_INFO("recieve msg = %d", msg->stamp.sec); // stamp.sec 메시지를 표시한다
ROS_INFO("recieve msg = %d", msg->stamp.nsec); // stamp.nsec 메시지를 표시한다
ROS_INFO("recieve msg = %d", msg->data); // data 메시지를 표시한다
}
int main(int argc, char **argv) // 노드 메인 함수
{
ros::init(argc, argv, "topic_subscriber"); // 노드명 초기화
ros::NodeHandle nh; // ROS 시스템과 통신을 위한 노드 핸들 선언
// 서브스크라이버 선언, ros_tutorials_topic 패키지의 MsgTutorial 메시지 파일을 이용한
// 서브스크라이버 ros_tutorial_sub 를 작성한다. 토픽명은 "ros_tutorial_msg" 이며,
// 서브스크라이버 큐(queue) 사이즈를 100개로 설정한다는 것이다
ros::Subscriber ros_tutorial_sub = nh.subscribe("ros_tutorial_msg", 100, msgCallback);
// 콜백함수 호출을 위한 함수로써, 메시지가 수신되기를 대기,
// 수신되었을 경우 콜백함수를 실행한다
ros::spin();
return 0;
}
7) ROS 노드 빌드
$ cd ~/catkin_ws
catkin 폴더로 이동
$ catkin_make
catkin 빌드 실행
두 줄을 $ cm 으로 대체 가능하다.
8) 퍼블리셔 실행
$ rosrun ros_tutorials_topic topic_publisher
별개로
$ rostopic list //현재 ros 네트워크상에 모든 topic을 리스트
$ rostopic info /ros_tutorial_msg // 메시지의 타입과 누가 퍼블리시 하고있는지 서브스크라이브 하고있는지 표시
9) 서브스크라이버 실행
$ rosrun ros_tutorials_topic topic_subscriber
Service
1) 패키지 생성
$ cd ~/catkin_ws/src
$ catkin_create_pkg ros_tutorials_service message_generation std_msgs roscpp
$ cd ros_tutorials_service
$ ls
2) 패키지 설정 파일(package.xml) 수정
$ gedit package.xml
<?xml version="1.0"?>
<package>
<name>ros_tutorials_service</name>
<version>0.1.0</version>
<description>ROS turtorial package to learn the service</description>
<license>Apache License 2.0</license>
<author email="pyo@robotis.com">Yoonseok Pyo</author>
<maintainer email="pyo@robotis.com">Yoonseok Pyo</maintainer>
<url type="bugtracker">https://github.com/ROBOTIS-GIT/ros_tutorials/issues</url>
<url type="repository">https://github.com/ROBOTIS-GIT/ros_tutorials.git</url>
<url type="website">http://www.robotis.com>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>std_msgs</build_depend>
<build_depend>message_generation</build_depend>
<run_depend>roscpp</run_depend>
<run_depend>std_msgs</run_depend>
<run_depend>message_runtime</run_depend>
<export></export>
</package>
3) 빌드 설정 파일(CMakeLists.txt) 수정
$ gedit CMakeLists.txt
cmake_minimum_required(VERSION 2.8.3)
project(ros_tutorials_service)
## 캐킨 빌드를 할 때 요구되는 구성요소 패키지이다.
## 의존성 패키지로 message_generation, std_msgs, roscpp이며 이 패키지들이 존재하지 않으면 빌드 도중에 에러가 난다.
find_package(catkin REQUIRED COMPONENTS message_generation std_msgs roscpp)
## 서비스 선언: SrvTutorial.srv
add_service_files(FILES SrvTutorial.srv)
## 의존하는 메시지를 설정하는 옵션이다.
## std_msgs가 설치되어 있지 않다면 빌드 도중에 에러가 난다.
generate_messages(DEPENDENCIES std_msgs)
## 캐킨 패키지 옵션으로 라이브러리, 캐킨 빌드 의존성, 시스템 의존 패키지를 기술한다.
catkin_package(
LIBRARIES ros_tutorials_service
CATKIN_DEPENDS std_msgs roscpp
)
## 인클루드 디렉터리를 설정한다.
include_directories(${catkin_INCLUDE_DIRS})
## service_server 노드에 대한 빌드 옵션이다.
## 실행 파일, 타겟 링크 라이브러리, 추가 의존성 등을 설정한다.
add_executable(service_server src/service_server.cpp)
add_dependencies(service_server ${${PROJECT_NAME}_EXPORTED_TARGETS}
${catkin_EXPORTED_TARGETS})
target_link_libraries(service_server ${catkin_LIBRARIES})
## service_client 노드에 대한 빌드 옵션이다.
add_executable(service_client src/service_client.cpp)
add_dependencies(service_client ${${PROJECT_NAME}_EXPORTED_TARGETS}
${catkin_EXPORTED_TARGETS})
target_link_libraries(service_client ${catkin_LIBRARIES})
4) 서비스 파일 작성
$ roscd ros_tutorials_service
패키지 폴더로 이동
$ mkdir srv
패키지에 srv라는 서비스 폴더를 신규 작성
$ cd srv
작성한 srv 폴더로 이동
$ gedit SrvTutorial.srv
SrvTutorial.srv 파일 신규 작성 및 내용 수정
gedit에 request 부분인
int64 a
int64 b
--- <- request와 response를 구분하는 구분자
response 부분인
int64 result 나눠서 작성하기.
5) 서비스 서버 노드 작성
$ roscd ros_tutorials_service/src → 패키지의 소스 폴더인 src 폴더로 이동
$ gedit service_server.cpp → 소스 파일 신규 작성 및 내용 수정
#include "ros/ros.h" // ROS 기본 헤더 파일
#include "ros_tutorials_service/SrvTutorial.h" // SrvTutorial 서비스 파일 헤더 (빌드후 자동 생성됨)
// 서비스 요청이 있을 경우, 아래의 처리를 수행한다
// 서비스 요청은 req, 서비스 응답은 res로 설정하였다
bool calculation(ros_tutorials_service::SrvTutorial::Request &req,
ros_tutorials_service::SrvTutorial::Response &res)
{
// 서비스 요청시 받은 a와 b 값을 더하여 서비스 응답 값에 저장한다
res.result = req.a + req.b;
// 서비스 요청에 사용된 a, b 값의 표시 및 서비스 응답에 해당되는 result 값을 출력한다
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: %ld", (long int)res.result);
return true;
}
int main(int argc, char **argv) // 노드 메인 함수
{
ros::init(argc, argv, "service_server"); // 노드명 초기화
ros::NodeHandle nh; // 노드 핸들 선언
// 서비스 서버 선언, ros_tutorials_service 패키지의 SrvTutorial 서비스 파일을 이용한
// 서비스 서버 ros_tutorials_service_server를 선언한다
// 서비스명은 ros_tutorial_srv이며 서비스 요청이 있을 때,
// calculation라는 함수를 실행하라는 설정이다
ros::ServiceServer ros_tutorials_service_server= nh.advertiseService("ros_tutorial_srv", calculation);
ROS_INFO("ready srv server!");
ros::spin(); // 서비스 요청을 대기한다
return 0;
}
6) 서비스 클라이언트 노드 작성
$ roscd ros_tutorials_service/src → 패키지의 소스 폴더인 src 폴더로 이동
$ gedit service_client.cpp → 소스 파일 신규 작성 및 내용 수정
#include "ros/ros.h" // ROS 기본 헤더 파일
#include "ros_tutorials_service/SrvTutorial.h" // SrvTutorial 서비스 파일 헤더 (빌드후 자동 생성됨)
#include <cstdlib> // atoll 함수 사용을 위한 라이브러리
int main(int argc, char **argv) // 노드 메인 함수
{
ros::init(argc, argv, "service_client"); // 노드명 초기화
if (argc != 3) // 입력값 오류 처리
{
ROS_INFO("cmd : rosrun ros_tutorials_service service_client arg0 arg1");
ROS_INFO("arg0: double number, arg1: double number");
return 1;
}
ros::NodeHandle nh; // ROS 시스템과 통신을 위한 노드 핸들 선언
// 서비스 클라이언트 선언, ros_tutorials_service 패키지의 SrvTutorial 서비스 파일을 이용한
// 서비스 클라이언트 ros_tutorials_service_client를 선언한다
// 서비스명은 "ros_tutorial_srv"이다
ros::ServiceClient ros_tutorials_service_client = nh.serviceClient<ros_tutorials_service::SrvTutorial>("ros_tutorial_srv");
// srv라는 이름으로 SrvTutorial 서비스 파일을 이용하는 서비스를 선언한다
ros_tutorials_service::SrvTutorial srv;
// 서비스 요청 값으로 노드가 실행될 때 입력으로 사용된 매개변수를 각각의 a, b에 저장한다
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
// 서비스를 요청하고, 요청이 받아들여졌을 경우, 응답 값을 표시한다
if (ros_tutorials_service_client.call(srv))
{
ROS_INFO("send srv, srv.Request.a and b: %ld, %ld", (long int)srv.request.a, (long int)srv.request.b);
ROS_INFO("receive srv, srv.Response.result: %ld", (long int)srv.response.result);
}
else
{
ROS_ERROR("Failed to call service ros_tutorial_srv");
return 1;
}
return 0;
}
7) ROS 노드 빌드
$ cd ~/catkin_ws && catkin_make
8) 서비스 서버 실행
$ rosrun ros_tutorials_service service_server
9) 서비스 클라이언트 실행
$ rosrun ros_tutorials_service service_client 2 3
2와 3은 다른 숫자를 넣어도 상관없다.
**서비스는 토픽과는 달리 일회성이므로 rqt_graph를 통해서 확인할 수 없다.
Parameter
1) 파라미터를 활용한 노드 작성
$ roscd ros_tutorials_service/src
패키지의 소스 코드 폴더인 src 폴더로 이동
$ gedit service_server.cpp
소스 파일 내용 수정
#include "ros/ros.h" // ROS 기본 헤더파일
#include "ros_tutorials_service/SrvTutorial.h" // SrvTutorial 서비스 파일 헤더 (빌드 후 자동 생성됨)
#define PLUS 1 // 덧셈
#define MINUS 2 // 빼기
#define MULTIPLICATION 3 // 곱하기
#define DIVISION 4 // 나누기
int g_operator = PLUS;
맨 처음 연산을 PLUS를 기준으로 한다.
// 서비스 요청이 있을 경우, 아래의 처리를 수행한다
// 서비스 요청은 req, 서비스 응답은 res로 설정하였다
bool calculation(ros_tutorials_service::SrvTutorial::Request &req,
ros_tutorials_service::SrvTutorial::Response &res)
{
// 서비스 요청시 받은 a와 b 값을 파라미터 값에 따라 연산자를 달리한다.
// 계산한 후 서비스 응답 값에 저장한다
switch(g_operator)
{
case PLUS:
res.result = req.a + req.b; break;
case MINUS:
res.result = req.a - req.b; break;
case MULTIPLICATION:
res.result = req.a * req.b; break;
case DIVISION:
if(req.b == 0){
res.result = 0; break;
}
else{
res.result = req.a / req.b; break;
}
default:
res.result = req.a + req.b; break;
}
// 서비스 요청에 사용된 a, b값의 표시 및 서비스 응답에 해당되는 result 값을 출력한다
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.result);
return true;
}
int main(int argc, char **argv) // 노드 메인 함수
{
ros::init(argc, argv, "service_server"); // 노드명 초기화
ros::NodeHandle nh; // ROS 시스템과 통신을 위한 노드 핸들 선언
nh.setParam("calculation_method", PLUS); // 매개변수 초기설정
// 서비스 서버 선언, ros_tutorials_service 패키지의 SrvTutorial 서비스 파일을 이용한
// 서비스 서버 service_server를 작성한다. 서비스명은 "ros_tutorial_srv"이며,
// 서비스 요청이 있을 때, calculation라는 함수를 실행하라는 설정이다.
ros::ServiceServer ros_tutorial_service_server = nh.advertiseService("ros_tutorial_srv", calculation);
ROS_INFO("ready srv server!");
ros::Rate r(10); // 10 hz
while (1)
{
nh.getParam("calculation_method", g_operator); // 연산자를 매개변수로부터 받은 값으로 변경한다
ros::spinOnce(); // 콜백함수 처리루틴
r.sleep(); // 루틴 반복을 위한 sleep 처리
}
return 0;
}
2) 노드 빌드 및 실행
$ cd ~/catkin_ws && catkin_make
$ rosrun ros_tutorials_service service_server
roslaunch 사용법
rosrun: 하나의 노드를 실행하는 명령어이다.
roslaunch: 하나 이상의 정해진 노드를 실행시킬 수 있다.
그 밖의 기능으로 노드를 실행할 때 패키지의 매개변수나 노드 이름 변경, 노드 네임스페이스 설정, ROS_ROOT 및 ROS_PACKAGE_PATH 설정, 환경 변수 변경 등의 옵션을 붙일 수 있는 ROS 명령어이다.
roslaunch 명령어를 쓸 때 —screen 옵션을 추가해주면 해당 터미널에 실행되는 모든 노드들의 출력들이 터미널 스크린에 표시되기 때문에 쉽게 디버깅 할 수 있게된다.
'FOSCAR-(Autonomous Driving) > ROS 스터디' 카테고리의 다른 글
[2024 ROS 스터디] 김기태 #3주차 - ROS 기본 프로그래밍 (0) | 2024.07.21 |
---|---|
[2024 ROS 스터디] 초라핀스카 베로니카 #3주차 - ROS 기본 프로그래밍 (0) | 2024.07.18 |
[2024 ROS 스터디] 이연수 #2주차 - ROS 용어 및 도구 (0) | 2024.07.14 |
[2024 ROS 스터디] 김기태 #2주차 - ROS의 중요컨셉&명령어와 도구 (1) | 2024.07.14 |
[2024 ROS 스터디] 초라핀스카 베로니카 #2주차 - ROS에서 쓰이는 용어 및 도구 (0) | 2024.07.13 |