스터디 노트
article thumbnail
Published 2021. 8. 9. 16:04
ROS 노드 작성과 빌드 maengkyun/Pixhawk

해당 블로그 참조하면서 따라해봤다

http://enssionaut.com/board_robotics/942 

 

로봇 - ROS(Robot Operating System) 개념과 활용 - 5. 바닥부터 node 작성하기

지금까지는 기존에 있던 패키지 노드를 수정하여 사용해 왔습니다. 이번에는 노드를 아예 처음부터 새로 작성하는 법에 대해 알아보겠습니다. 또한 노드 내부에서 사용되는 ROS의 기본 구조에

enssionaut.com

 

ROS는 기본적으로 python으로 동작하지만 roscpp 라이브러리를 통해 C++소스를 작성할 수 있다.

메시지의 형식은 사정 정의되어 있고 std_msgs, common_msgs, 사용자 정의 메시지가 있다.

 

std_msgs 는 bool, char, int, float, string등 주요 쓰이는 자료형들이 포함되어있다.

 

common_msgs는 actionlib_msgs, diagnostic_msgs, geometry_msgs, nav_msgs, sensor_msg가 포함되어있고 주로 로봇을 구성하는 패키지에서 쓰이는 자료형이 포함

 

sensor_msgs는 여러 센서 측정값의 데이터인 CompressedImage, FluidPressure, Imu, Jointstate, LaserScan, MagneticField, Range등이 포함되있고 필요에따라 사전 정의된 메시지 형식 말고도 직접 메시지 형식을 만들어 사용하기도 한다.

 

소스 내에서 실제로 publisher 선언을 하는 부분은 ros::Nodehandle 클래스의 advertise()를 이용. advertise 호출되면 마스터가 해당 토픽의 메시지를 subscribe하고자 하는 노드에 그 토픽으로 publish하고자 하는 노드가 있음을 전달(알림)

 

그 이후 publisher 와 subscriber가 직접 연결. 이후 advertise()는 해당 토픽으로 publish 가능한 객체인 ros::Publisher 클래스를 반환하여 그 객체의 publish()를 이용해 원하는 메시지를 발행할 수 있다. 

 

advertise()는 함수 오버로딩이 되어있고 가당 간단한 형태는 매개변수로 토픽명과 큐 사이즈를 가진다. 큐 사이즈는 발행하는 메시지를 몇 개 까지 저장해놓을 것인지 정하는 값

 

아래 작성된 예제 소스에서 advertise()를 이용해 i_am_the_publisher라는 ros::Publisher 객체를 생성하고, 그 객체의 publish를 이용해 std_msgs::Int32 자료형의 testmsg객체가 가진 data 값을 메시지로 발행.

 

그 외 publisher 노드 소스에 포함된 내용은 ROS 관련 의존 요소 포함, 노드 초기화, 루프문으로 구성되어있다고 한다. ROS 관렪 헤더인 ros.h와 표준 자료형 메시지 패키지인 std_msgs/Int32.h 를 포함 시켰다. init을 통해 초기화하고 노드이름을 지정한다

 

노드명 매개변수에는 "name_of_pub_node"라는 노드 이름을 인자로 주었고 rqt_graph에서 노드와 메시지를 확인할 때 지금 지정한 이름이 뜬다.

 

루프 내부에서 testmsg 메시지 객체의 data 값에 count 값을 대입해 주므로 data 값도 0부터 1씩 증가하고, 이 값이 publish 된다. ROS_INFO()를 이용하면 노드가 돌아가는 터미널 창에 무언가를 출력할 수있다. 여기서 testmsg객체의 data값을 출력하도록 하였는데, 포맷 스트링을 %c로 주어서 data값이 ASCII로 출력되도록 함

 

Subscriber 노드전달받은 메시지 데이터를 decimal 값으로 터미널 출력Publisher의 advertise()와 같이 Nodehandle 클래스의 subscriber()를 이용해 구독자 선언.subscribe()는 세 가지 파라미터로 토픽명, 받은 메시지를 저장할 큐 사이즈, callback 함수를 가진다.subscribe()는 메시지를 받아서 callback 함수에 전달한다.--> 콜백 함수에서 받은 메시지를 이용해 무엇을 할 것인지 처리할 수 있다. 

 

subscriber()는 ros::Subscriber객체를 반환한다. subscriber()호출 후에는 ros::spin()함수를 이용해 반복 구독을 수행하고 callback을 통해 지속적으로 요청.(계속 대기상태)

 

아래에선 "name_of_topic"이라는 토픽을 가진 메시지를 10진수 정수형으로 터미널에 출력하는 노드

 

설정 파일 작성

 

처음에 패키지 생성했을때 만들어진 package.xml과 CMakeLish.txt 파일을 수정해 빌드 관련 내용들을 수정내부 파일 주석들을 보면 각각의 파일을 어떤식으로 수정해야 하는지에 대해 안내됨 

 

package.xml 파일에는 패키지 이름, 버전, 간략한 설명, 작성자 연락처, 라이선스, 빌드 tool 의존성에 대한 정보가 들어있다.

 

CMakeLists.txt 파일에는 패키지 빌드와 catkin 에 관련된 설정 정보가 담김 버전, 파일 경로, 노드 리스트, 라이브러리 의존성 등 

 

예시에서는 cmake 버전, 프로젝트 이름, 의존패키지 목록, 포함 디렉토리, 노드 추가 순으로 작성됨

 

노드 추가는 앞에서 작성한 publisher와 subscriber 노드를 각각 추가하였다. add_executable은 노드를 rosrun이나 roslaunch를 이용해 실제로 실행시킬 때 사용하는 실행 파일의 이름과 소스 파일을 지정한다.

 

실행 파일 이름은 각각 pub_node와 sub_node로 지정하였다. target_link_libraries와 add_dependencies는 기본적인 내용대로 작성하였다.

 

모두 작성하고 난 다음에는 $cm 또는 ~catkin_ws/src 경로에서 catkin_make를 통해 작성한 노드 build.

include_directories(include ${catkin_INCLUDE_DIRS})

add_executable(pub_node src/nodetest_publisher.cpp)
target_link_libraries(pub_node ${catkin_LIBRARIES})
add_dependencies(pub_node nodetest_generate_messages_cpp)

add_executable(sub_node src/nodetest_subscriber.cpp)
target_link_libraries(sub_node ${catkin_LIBRARIES})
add_dependencies(sub_node nodetest_generate_message_cpp)

 

mkyun@mkyun-X7967T:~$ cd ~/catkin_ws/src
mkyun@mkyun-X7967T:~/catkin_ws/src$ catkin_create_pkg nodetest
Created file nodetest/CMakeLists.txt
Created file nodetest/package.xml
Successfully created files in /home/mkyun/catkin_ws/src/nodetest. Please adjust the values in package.xml.
mkyun@mkyun-X7967T:~/catkin_ws/src$ ls
CMakeLists.txt  mavlink  mavros  nodetest
mkyun@mkyun-X7967T:~/catkin_ws/src$ cd nodetest
mkyun@mkyun-X7967T:~/catkin_ws/src/nodetest$ mkdir src
mkyun@mkyun-X7967T:~/catkin_ws/src/nodetest$ cd src --> 해당 폴더내에 Node 작성

 

vscode를 이용했다 

publisher node - nodetest_publisher.cpp

#include "ros/ros.h" // ROS 관련 헤더 포함
#include "std_msgs/Int32.h" // 표준 자료형 메시지 패키지 포함

int main(int argc, char **argv){
    ros::init(argc, argv, "name_of_pub_node");//ROS 초기화. node 이름 지정
    ros::NodeHandle nh;
    ros::Publisher i_am_the_publisher = nh.advertise<std_msgs::Int32>("name_of_topic",100)

    ros::Rate loop_rate(5); // loop 주기 5hz

    int count = 0;

    while(ros::ok()){
        std_msgs::Int32 testmsg;

        testmsg.data = count;
        ROS_INFO("value: %c\n", testmsg.data);//터미널 출력

        i_am_the_publisher.publish(testmsg);//실제 메시지 publish 부분
        ros::spinOnce();
        loop_rate.sleep();//loop주기외에 남은시간동안 sleep
        count++;
    }
    return 0;
}

Subcriber.cpp

#include "ros/ros.h"
#include "std_msgs/Int32.h"

/**
 * 받은 메시지 데이터를 터미널에 출력하는 함수
 * 매개변수: std_msgs::int32 type 객체 smg
 */
void subscriberCallback(const std_msgs::Int32 msg){
    ROS_INFO("Received: %d\n",msg.data);

}

int main(int argc, char **argv){
    ros::init(argc, argv, "name_of_sub_node");
    ros::NodeHandle nh;

    ros::Subscriber i_am_the_subscriber = nh.subscribe("name_of_topic",100,subscriberCallback);
    ros::spin();
    return 0;

}

package.xml은 그대로둿고 

CMakeList.txt만 수정 했다

 

$ roscore                                      -> ROS master node 실행

$ rosrun nodetest pub_node       -> publisher node 실행

$ rosrun nodetest sub_node        -> subscriber node 실행

$ rosrun rqt_graph rqt_graph      -> rqt_graph node 실행(노드 관계 도식화)

 

 

 

실행결과

 

profile

스터디 노트

@myeongkyun

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!