02장 ROS2 사용방법(Topic, Service, Action 등)

1. Nodes : 하나의 실행가능한 프로그램(각 명령에 따른 실행 화면 딸 것.)

그렇다면 ROS2를 어떻게 사용할 수 있을까?

ROS에서는 노드(node)라는 최소 단위의 실행가능한 프로세스를 가지고 있다. 이는 하나의 목적을 가진 실행가능한 프로그램을 의미하며, 각 노드는 메시지 통신으로 데이터를 주고 받는다. 이러한 노드끼리의 통신방법은 하나로 정해진 것이 아니기에 1-1장에서 진행한 Turtlesim을 통해 ROS의 통신방법에 대해 자세히 알아보도록 하겠다.

먼저 01-1장을 참고하여 Turtlesim을 구동한 후에 다음의 명령어를 따라해보며, Node에 대해 알아보자.

먼저 현재 작동중인 nodes의 항목을 보여주는 명령이다. 다음의 명령어를 실행시키면 현재 가동중인 node list를 보여준다.

$ ros2 node list

여기서 같은 node를 다른 이름으로 띄우고 싶다면 다음의 명령어를 활용하여 node 명을 my_turtle로 바꿀 수 있다.

$ ros2 run turtlesim turtlesim_node __node:=my_turtle

또한 다음의 명령어를 활용하여 <node_name>에 특정 노드 명을 적으면 노드에 대한 정보(Publisher, Subscriber, Service server, Service client list)를 확인할 수 있다. 이에 대해 간단하게 소개하자 Node에는 Publisher, Subscriber, Service server, Service client 등의 역활이 있으며, Node의 의사소통 방식으로 Topic, Service, Action이 있다고 생각하면 편합니다.

$ ros2 node info <node_name>

갑자기 너무 많은 단어들이 나와서 혼동이 올 수도 있는데, 이제 Topic, Service, Action에 대해 설명하며 Node의 역활에 대해서도 자세히 다룰예정이다. 먼저 Topic에 대해 알아보자.

2. Topics

Topic 메시지 통신은 정보를 보내는 Publisher Node와 정보를 받는 Subscriber Node가 Topic 메시지 형태로 정보를 송수신하는 것이다. Topic은 Node간의 메시지 통신으로 다른 시스템(Subscriber Node)에 데이터를 지속적으로 보낼 수 있다.

Topic의 특징을 정리해보면?

  • 비동기식 단방향 메시지 송수신 방식

  • Publisher와 Subscriber 간의 통신

  • 1:N, N:1, N:N 통신도 가능

  • 통신을 중단하기 전까지 연속적으로 메시지를 보내는 것

그렇다면 이제 Topic을 사용해보자.(물론, 그 전에 turtlesim을 실행시키고 진행하도록 하자)

먼저 활성화된인 Topic의 목록을를 확인하기 위해서는 list라는 명령을 통해 반환한있다. Topic 유형도 반환하고 싶다면 ' -t'를 추가하여 확인한다.

$ ros2 topic list

Topic에 대한는 정보를 알아보려면 node의 정보를 띄우는 명령어에서 node를 topic으로 바꾸면 된다. turtle1/pose의 Topic에 대한 정보를 알기 위해 다음과 같은 명령어를 쓴다.

$ ros2 topic info /turtle1/pose

이제 직접 Publish하는 방법을 알아보자. 즉, 명령을 보내는 것이다.

$ ros2 topic pub <topic_name> <msg_type> '<args>'

예제는 /turtle1/cmd_vel이 geometry_msgs/msg/Twist와 같은 message 유형으로 {linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}하라는 것이다.

$ ros2 topic pub --once /turtle1/cmd_vel geometry_msgs/msg/Twist '{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}'

여기 --once는 message를 한번 실행 후 종료하라는 의미이며, 지속적으로 작동하려면 --once대신 --rate 1을 입력하여 1Hz마다 꾸준히 message를 보낼 수 있다. 그리고 <args>는 YAML 문법을 따라야한다.

추가적으로 message 유형(type)이란 message를 보낼때 지켜야할 양식이라고 생각하면 되고, message 유형에 대한 정보를 얻고 싶다면 <msg_type>에 유형을 적을 수 있다.

ros2 interface show <msg_type>

다음은 앞서 간단히 다룬 GUI(Graphic User Interface)도구, rqt를 이용한 Publish하는 방법을 알아보기 위해 rqt를 열자!

$ rqt

이후 상단 메뉴에의 plugins -> Topics -> Message Publisher 항목으로 이동하자. rqt tool에서도 topic 명령을 보낼 수 있다.

checkbox를 해제하면 동작이 멈추는 것을 확인할 수 있음

또한 rqt를 활용하면 Node사이의 관계도 시각화 할 수 있다. 단순히 rqt의 메뉴에서 Plugins > Introspection > Nodes Graph 라는 경로를 선택하여 graph로 나타낼 수 있지만 터미널에 rqt_graph를 입력하여 실행중인 Node사이의 관계를 확인할 수 있다.

turtle1이라는 Node에 데이터를 보는 속도인 Hertz를 확인하는 방법. 데이터를 보내는(publish) 비율에 대한 평균 데이터 값을 반환한다.

$ ros2 topic hz /turtle1/pose 

+ turtle1이라는 Node의 Bandwidth를 확인하는 방법

$ ros2 topic bw /turtle1/pose

이 외에도 Topic에 대한 많은 기능이 있지만 전부 다루기에는 지루해질 수 있기에 Topic이 무슨 통신인지 기억할 정도의 실습으로 마치겠다. 바로 Service를 공부하러 넘어가기보다 개념적으로라도 Topic에 대한 내용을 정리하고 기억하도록 하자! 또한 실습을 제대로 따라갔다면 현시점에 많은 Node가 실행되어 있을 것이다. 터미널창 자체는 control + c로 종료하고 rqt와 같은 QUI도구들은 창을 닫아 종료한 후 다음 실습을 이어가도록 하자!

3. Services

Services는 call-and-response model을 기반으로 하며, client가 호출할때만 server로 데이터가 제공된다.

Service의 특징을 정리해보면?

  • 동기식 양방향 메시지 송수신 방식

  • Service server와 Service client 간의 통신

  • Request/Response

먼저 작동중인 Service list를 확인하려면 다음과 같은 명령어를 쓴다. (물론, 그 전에 turtlesim을 실행시키고 진행하도록 하자)

$ ros2 service list -t

그렇다면 이제 Service을 사용해보자. Service를 이용하여 명령을 하는 방법은 다음과 같다. <service_type>은 topic의 <msg_type>을 보는 방법과 동일하며, <arguments> 도 topic과 유사하게 YAML문법에을 따라 작성하면 된다.

$ ros2 service call <service_name> <service_type> <arguments>

Service를 이용하여 turtlesim가 거북이 한마리에서 한마리를 더 생성해보도록 하겠다. 이를 위해서는 spawn을 활용하여 거북이의 이름과 위치값을 지정해 준 후 생성할 수 있다. 다음 명령어에 자신이 원하는 값을 대입하여 거북이를 한마리 더 생성해보자!

$ ros2 service call /spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2, name: 'my_turtle'}"

참고 : name : ' '으로 이름값을 설정해주지 않은 거북이의 이름은 'turtle1', 'turtle2',,,, 이렇게 1씩 증가한 이름이 기본값(default)로 설정되어있다. 따라서 ros2 run turtlesim turtlesim_node로 생성한 첫번째 거북이의 이름은 'turtle1'이며, ros2 service call /spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2, name: ' '}"로 생성한 두번째 거북이의 이름이 'turtle2'가 된다. 물론 이름을 지정해주면 해당 이름을 가진 거북이가 생성된다.

다음은 rqt를 이용한 Services하는 방법을 알아보기 위해 rqt를 열자!

$ rqt

이후 상단 메뉴의 plugins -> Services -> Service caller 항목으로 이동한 후 /clear 우측 역삼각형을 누른후 /spawn을 찾고 call을 눌러 거북이 추가적으로 생성할 수 있다.

4. Action

action은 service와 기능적으로 유사한 client-server model을 사용하며, client가 server에게 goal을 보내면 server node는 goal을 확인한 후 Feedback과 result를 반환한다(하나의 응답만 반환하는 service와 달리 꾸준한 Feedback을 제공한다.). 또한 service와 달리 preemptable(실행 중 취소가능) 특성을 가지고 있다.

Action의 특징을 정리해보면?

  • 비동기식 양방향 메시지 송수신 방식

  • Action server와 Action client 간의 통신

  • Goal(목표)/Feedback(피드백)/Result(결)

그렇다면 이제 Action을 사용해보자.(물론, 그 전에 turtlesim을 실행시키고 진행하도록 하자)

'turtle_teleop_key'라는 Node를 실행시킨 후 G|B|V|C|D|E|R|T keys를 활용하여 거북이를 움직였다면 다음과 같은 goal을 전달완료했다는 메시지가 표시된다.

[INFO] [turtlesim]: Rotation goal completed successfully

이 후 'F' key를 눌러 회전을 중지시킨다면 아래와 같은 메시지가 표시되는데, 이는 앞서 언급한 실행중인 메시지를 도중에 중단할 수 있는 Action의 preemptable 특성이다.

[INFO] [turtlesim]: Rotation goal canceled

먼저 작동중인 Action list를 확인하려면 다음과 같은 명령어를 쓰면 현재 실행중인 action의 list를 반환해 줄 것이다('turtle_teleop_key'라는 Node를 실행하고 있다면, turtle1/rotate_absolute만 반환될 것이다).

$ ros2 action list -t 
/turtle1/rotate_absolute [turtlesim/action/RotateAbsolute]

이후 작동중인 turtle1/rotate_absolute이라는 Action 정보를 확인해보자./teleop_turtle라는 Action clients가 /turtlesim라는 Action servers에게 action을 부여하고 있음을 확인할 수 있다.

$ ros2 action info /turtle1/rotate_absolute
Action: /turtle1/rotate_absolute
Action clients: 1
    /teleop_turtle
Action servers: 1
    /turtlesim

Action도 Service와 명령어만 다르고 비슷한 문법을 활용하면 Command를 통해서 Action을 부여할 수 있다(여기서도 <values>는 YAML format을 따른다.).

ros2 action send_goal <action_name> <action_type> <values>

turtle1/rotate_absolute이라는 Node의 Action server에 각도를 -1.5도 틀라는 Goal data를 보내려면 다음과 같은 명령어를 쓴다.

$ ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute {'theta: -1.5'} --feedback

이 Goal의 Feedback을 확인하고 싶다면 마지막에 --feedback을 추가해주면 된다.

5. Parameters : nodes가 사용하는 parameters

  • Parameters

    • YAML 파일

    • 지원하는 데이터타입(datatype)

      • bool

      • int64

      • float64

      • string

      • bytes[]

      • bool[]

      • int64[]

      • float64[]

      • string[]

    • eventCallBack 함수를 제공한다.

  • APIs

    • set_parameters()

    • get_parameters()

    • list_parameters()

먼저 작동중인 Parameter를 확인하려면 다음과 같은 명령어를 쓴다.

$ ros2 param list

6. rosbag2 : Nodes 사이의 통신을 기록

rosbag2의 특징을 살펴보자.

  • node간 통신 메시지를 녹화하고 재생할 수 있는 OSRF package

  • 다양한 DDS QoS 지원예정

  • 다양한 MiddleWare 지원예정 (e.g. protobuf or ZeroMQ)

  • 데이터 저장 방식의 다양화 (e.g. HDF5, ROS 1 format, SQlite 등)

  • ROS2의 default format : SQLite3

  • 현재 record, play, info 명령어 사용가능

ROS2는 ROS1과 데이터 저장방식이 다르기에 통신을 기록하는 bag를 사용하기 위해서는 아래의 명령어들을 이용하여 plugin을 설치해야한다.

$ sudo apt install ros-dashing-rosbag2-storage-default-plugins

추가로 설치해야하는 plugins

$ sudo apt install ros-dashing-ros2bag ros-dashing-rosbag2-transport ros-dashing-rosbag2-storage-default-plugins
$ sudo apt install ros-dashing-rosbag2-bag-v2-plugins

여기서 문제!!

  1. Node간의 통신인 Topic, Service, Action을 나타내는 Diagram을 하나로 정리하여 표현하여라!(통신별 특징을 생각하며 내용을 표현해보세요)

  2. Service를 이용하여 생성한 거북이를 지우는 방법은? + 거북이가 다닌 경로를 지우는 방법은?(관련 내용을 찾아보며 turtlesim을 활용한 다양한 예제도 학습해 보세요!)

정답!

1. Total(Topic, Service, Action) Diagram

2. 추가로 생성한 거북이를 지우는 방법

  • rqt(GUI)를 이용한 방법

rqt를 연다.

Plugins→Services→Service Caller

/clear 우측 역삼각형을 누른후 /spawn을 찾고 call을 눌러 거북이 생성

우측에 역삼각형을 누른 후 /kill을 찾습니다.

생성한 거북이 이름을 적고 Call을 누르면 거북이가 사라집니다.

  • rqt(CLI)를 이용한 방법

거북이 생성시에 거북이 이름을 적어줘야 지울 수 있습니다.

'my_turtle'이라는 이름의 거북이 생성

$ ros2 service call /spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2, name: 'my_turtle'}"

'my_turtle'이라는 이름의 거북이 삭

$ ros2 service call /kill turtlesim/srv/Kill "{name: 'my_turtle'}"

'my_turtle'이라는 이름의 거북이가 다닌 경로를 지우

$ ros2 service call /clear std_srvs/srv/Empty "{name: 'my_turtle'}"

1) 거북이 이름을 넣지 않았을때 지우는 방법 : 'reset'

$ ros2 service call /reset std_srvs/srv/Empty

2) 거북이 이름을 넣지 않았을때 지우는 방법 : 기본값 이름으로 지우기

  • 처음 생성된 거북이는 'turtle1'이라는 기본값의 이름을 가진다.

  • 이후 spawn/으로 생성된 거북이는 'turtle2'라는 기본값을 가진다.

  • turtle1, 2, 3,,,순서의 이름을 가진 거북이가 생성되므로 다음 명령어로 이름을 정하지 않은 거북이를 사라지게 할 수 있다.$ ros2 service call /kill turtlesim/srv/Kill "{name: 'turtle(숫자)'}"

Last updated