ROS Node가 실행되고 Topic 통신이 이루어지기 위해서 Master에게 자신을 등록하고 Master에 의한 Node간 TCPROS / UDPROS 연결이 이루어져야 합니다.
개념 정리와 예시, 그리고 데모를 통해 ROS의 통신 시스템에 대해 배워봅시다.
RPC - remote procedure call 란, 분산 네트워크 환경에서의 프로그래밍을 용이하게 하기 위해 등장한 기술로, 원격에 정의된 함수를 로컬에서 호출하는 식으로 사용이 가능합니다.
이 둘을 결합한 XML-RPC는 RPC 기술 과정에서 XML을 사용하는 것입니다. 서버-클라이언트 정보가 XML 문서로 만들어져 응답하게 됩니다.
POST /xmlrpc HTTP 1.0
User-Agent: myXMLRPCClient/1.0
Host: 192.168.124.2
Content-Type: text/xml
Content-Length: 169
<?xml version="1.0"?>
<methodCall>
<methodName>circleArea</methodName>
<params>
<param>
<value><double>2.41</double></value>
</param>
</params>
</methodCall>
HTTP/1.1 200 OK
Date: Sat, 06 Oct 2001 23:20:04 GMT
Server: Apache.1.3.12 (Unix)
Connection: close
Content-Type: text/xml
Content-Length: 124
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><double>18.24668429131</double></value>
</param>
</params>
</methodResponse>
ROS는 Topic, Service 통신을 위해 standard TCP/UDP socket에 기반하여 독자적인 Header와 프로토콜을 가진 TCPROS/UDPROS를 만들었습니다. 이들에 대해 하나씩 살펴보겠습니다.
Connection Header는 Node간 통신이 이루어지기 위한 typing, routing 데이터를 포함하고 있으며 TCPROS와 UDPROS 별도 다른 형태를 갖고 있습니다. 공식 문서에서 명확히 밝히고 있는 TCPROS의 Connection Header를 분석해봅시다.
4-byte length + [4-byte field length + field=value ]*
* All length fields are little-endian integers
* Field names and values are strings.
rostopic pub /chatter std_msgs/String "hello”
에 대한 hex output은 아래와 같습니다.전체 header 길이 176 바이트 ⇒ (b0 00 00 00, little-endian 사용)
b0 00 00 00
20 00 00 00
6d 65 73 73 61 67 65 5f 64 65 66 69 6e 69 74 69 6f 6e 3d 73 74 72 69 6e 67
20 64 61 74 61 0a 0a
25 00 00 00
63 61 6c 6c 65 72 69 64 3d 2f 72 6f 73 74 6f 70 69 63 5f 34 37 36 37 5f 31
33 31 36 39 31 32 37 34 31 35 35 37
0a 00 00 00
6c 61 74 63 68 69 6e 67 3d 31
27 00 00 00
6d 64 35 73 75 6d 3d 39 39 32 63 65 38 61 31 36 38 37 63 65 63 38 63 38 62
64 38 38 33 65 63 37 33 63 61 34 31 64 31
0e 00 00 00
74 6f 70 69 63 3d 2f 63 68 61 74 74 65 72
14 00 00 00
74 79 70 65 3d 73 74 64 5f 6d 73 67 73 2f 53 74 72 69 6e 67
09 00 00 00
05 00 00 00
68 65 6c 6c 6f
6d 65 73 73 61 67 65 5f 64 65 66 69 6e 69 74 69 6f 6e 3d 73 74 72 69 6e 67-
m e s s a g e _ d e f i n i t i o n = s t r i n g-
20 64 61 74 61 0a 0a
d a t a \n \n
63 61 6c 6c 65 72 69 64 3d 2f 72 6f 73 74 6f 70 69 63 5f 34 37 36 37 5f 31 33-
c a l l e r i d = / r o s t o p i c _ 4 7 6 7 _ 1 3-
31 36 39 31 32 37 34 31 35 35 37
1 6 9 1 2 7 4 1 5 5 7
6c 61 74 63 68 69 6e 67 3d 31
l a t c h i n g = 1
6d 64 35 73 75 6d 3d 39 39 32 63 65 38 61 31 36 38 37 63 65 63 38 63 38 62 64-
m d 5 s u m = 9 9 2 c e 8 a 1 6 8 7 c e c 8 c 8 b d-
38 38 33 65 63 37 33 63 61 34 31 64 31
8 8 3 e c 7 3 c a 4 1 d 1
74 6f 70 69 63 3d 2f 63 68 61 74 74 65 72
t o p i c = / c h a t t e r
74 79 70 65 3d 73 74 64 5f 6d 73 67 73 2f 53 74 72 69 6e 67
t y p e = s t d _ m s g s / S t r i n g
09 00 00 00
05 00 00 00
68 65 6c 6c 6f
h e l l o
이렇게 Topic response는 특정 필드와 데이터로 구성되어 있습니다.
사용되는 모든 필드에 대한 데이터는 링크로 대체하겠습니다.
TCPROS는 ROS의 Topic, Service 통신 시 Inbound connection으로 TCP 소켓을 사용하는 방식입니다. 일전 살펴본 Connection Header에서와 같이 특정 필드들을 결합하여 Header와 Data를 송수신합니다.
만약 header가 ’topic’ 필드를 포함하고 있다면 ROS Topic 연결로 이루어지고 ‘service’ 필드를 포함하고 있다면 ROS Service 연결로 이루어지는 방식입니다.
TCPROS subscriber의 Request
md5sum 이란? 파일을 다운받거나, 이동하거나, 복사한 후에 원본파일과 동일한 파일인지 확인하는 목적으로 쓰이는 해시 알고리즘입니다. (ex - git의 SHA-1)
TCPROS publisher의 Response
TCPROS service client의 Request
optional 필드들
UDPROS는 ROS의 Topic, Service 통신 시 Inbound connection으로 UDP 소켓을 사용하는 방식입니다. UDPROS는 standard UDP datagram과 serialized된 message data를 사용하며, 로봇의 제어와 센서 데이터를 다루는 것과 같이 빠른 통신에 사용됩니다.
UDPROS header format은 아래와 같으며 TCPROS에 비해 훨씬 간단합니다.
+---------------------------------+
| Connection ID |
+---------------------------------+
|Opcode | Msg ID | Block # |
+---------------------------------+
추가 특징
ROS Master와 Node에 의해 Topic과 Service가 이루어지는 과정을 총정리해봅시다.
ROS 내부의 통신이 이루어지는 과정을 배운 만큼 WireShark를 통해 실제 오가는 패킷을 분석해 봅시다.
sudo apt update
sudo apt install wireshark
sudo wireshark
ip.addr == <my-ip-addr> || tcp.port == 11311
# Terminal
roscore
ROS Master를 종료할 시에도 실행되고 있는 모든 서비스들은 11311포트를 통해 종료 신호를 Request-Response 합니다.
rostopic pub /chatter std_msgs/String "hello"
<methodCall>
<methodName>
registerService
</methodName>
<params>
<param>
<value>
<string>
/rostopic_18957_1673097448137
</string>
</value>
</param>
<param>
<value>
<string>
/rostopic_18957_1673097448137/get_loggers
</string>
</value>
</param>
<param>
<value>
<string>
rosrpc://166.104.135.89:44311
</string>
</value>
</param>
<param>
<value>
<string>
http://166.104.135.89:33575/
</string>
</value>
</param>
</params>
</methodCall>
<?xml
version='1.0'
?>
<methodCall>
<methodName>
registerPublisher
</methodName>
<params>
<param>
<value>
<string>
/rostopic_18957_1673097448137
</string>
</value>
</param>
<param>
<value>
<string>
/chatter
</string>
</value>
</param>
<param>
<value>
<string>
std_msgs/String
</string>
</value>
</param>
<param>
<value>
<string>
http://166.104.135.89:33575/
</string>
</value>
</param>
</params>
</methodCall>
$ rostopic echo /chatter
data: "hello"
---
$ rostopic info /chatter
Type: std_msgs/String
Publishers:
* /rostopic_18957_1673097448137 (http://166.104.135.89:33575/)
Subscribers: None
ip.addr == <my-ip-addr> || tcp.port == 33575
(/chatter publisher의 포트가 33575였음)
$ rostopic echo /chatter
data: "hello"
---
$ rostopic info /chatter
Type: std_msgs/String
Publishers:
* /rostopic_18957_1673097448137 (http://166.104.135.89:33575/)
Subscribers:
* /rostopic_21890_1673098174449 (http://166.104.135.89:36479/)
<methodCall>
<methodName>
registerPublisher
</methodName>
<params>
<param>
<value>
<string>
/rostopic_21890_1673098174449
</string>
</value>
</param>
<param>
<value>
<string>
/rosout
</string>
</value>
</param>
<param>
<value>
<string>
rosgraph_msgs/Log
</string>
</value>
</param>
<param>
<value>
<string>
http://166.104.135.89:36479/
</string>
</value>
</param>
</params>
</methodCall>
<methodResponse>
<params>
<param>
<value>
<array>
<data>
<value>
<int>
1
</int>
</value>
<value>
<string>
current system state
</string>
</value>
<value>
<array>
<data>
<value>
<array>
<data>
<value>
<string>
/rosout_agg
</string>
</value>
<value>
<string>
rosgraph_msgs/Log
</string>
</value>
</data>
</array>
</value>
<value>
<array>
<data>
<value>
<string>
/rosout
</string>
</value>
<value>
<string>
rosgraph_msgs/Log
</string>
</value>
</data>
</array>
</value>
<value>
<array>
<data>
<value>
<string>
/chatter
</string>
</value>
<value>
<string>
std_msgs/String
</string>
</value>
</data>
</array>
</value>
</data>
</array>
</value>
</data>
</array>
</value>
</param>
</params>
</methodResponse>
다음으로, turtlesim을 통해 topic message data의 변화를 살펴보고자 합니다.
$ rosrun turtlesim turtlesim_node
$ rosnode info /turtlesim
--------------------------------------------------------------------------------
Node [/turtlesim]
Publications:
* /rosout [rosgraph_msgs/Log]
* /turtle1/color_sensor [turtlesim/Color]
* /turtle1/pose [turtlesim/Pose]
Subscriptions:
* /turtle1/cmd_vel [unknown type]
Services:
* /clear
* /kill
* /reset
* /spawn
* /turtle1/set_pen
* /turtle1/teleport_absolute
* /turtle1/teleport_relative
* /turtlesim/get_loggers
* /turtlesim/set_logger_level
contacting node http://166.104.135.89:39875/ ...
Pid: 29120
Connections:
* topic: /rosout
* to: /rosout
* direction: outbound (33435 - 166.104.135.89:60856) [26]
* transport: TCPROS
$ rostopic info /turtle1/cmd_vel
Type: geometry_msgs/Twist
Publishers: None
Subscribers:
* /turtlesim (http://166.104.135.89:39875/)
ip.addr == <my-ip> || tcp.port == 39875
rosrun turtlesim turtle_teleop_key
XML-RPC 통신들이 이루어진 뒤로, TCP 통신이 이어지는 모습을 확인할 수 있습니다.
TCP Data 필드의 내용에는 아래와 같은 데이터가 포함되어 있습니다.
callerid=/teleop_turtle
latching=0'md5sum=acffd30cd6b6de30f120938c17c593fbjmessage_definition=##
## Severity level constants
##
byte DEBUG=1 #debug level
byte INFO=2 #general level
byte WARN=4 #warning level
byte ERROR=8 #error level
byte FATAL=16 #fatal/critical level
##
## Fields
##
Header header
byte level
string name # name of the node
string msg # message
string file # file the message came from
string function # function the message came from
uint32 line # line the message came from
string[] topics # topic names that the node publishes
================================================================================
MSG: std_msgs/Header
# Standard metadata for higher-level stamped data types.
# This is generally used to communicate timestamped data
# in a particular coordinate frame.
#
# sequence ID: consecutively increasing ID
uint32 seq
#Two-integer timestamp that is expressed as:
# * stamp.sec: seconds (stamp_secs) since epoch (in Python the variable is called 'secs')
# * stamp.nsec: nanoseconds since stamp_secs (in Python the variable is called 'nsecs')
# time-handling sugar is provided by the client library
time stamp
#Frame this data is associated with
string frame_id
topic=/rosouttype=rosgraph_msgs/Log
callerid=/teleop_turtle
latching=0'md5sum=9f195f881246fdfa2798d1d3eebca84armessage_definition=# This expresses velocity in free space broken into its linear and angular parts.
Vector3 linear
Vector3 angular
================================================================================
MSG: geometry_msgs/Vector3
# This represents a vector in free space.
# It is only meant to represent a direction. Therefore, it does not
# make sense to apply a translation to it (e.g., when applying a
# generic rigid transformation to a Vector3, tf2 will only apply the
# rotation). If you want your data to be translatable too, use the
# geometry_msgs/Point message instead.
float64 x
float64 y
float64 z
topic=/turtle1/cmd_veltype=geometry_msgs/Twist
⇒ 기본적으로 sequence / timestamp가 변화하며, 미묘하게 data가 다른 것을 알 수 있습니다.
$ echo $ROS_MASTER_URI
http://192.168.0.1:11311
서로 다른 디바이스에서 동작하는 ROS 시스템일지라도 같은 MASTER URI를 갖도록 하면 원격 통신이 가능합니다. 이를 통해 원격 시각화와 원격 제어를 주로 실행합니다.
ROS_MASTER_URI를 사용하는 방법은 다음과 같습니다.
ROS_IP와 ROS_HOSTNAME는 서로 양립할 수 없고, 둘 중 하나를 사용할 수 있습니다.
Config:
menu_enable: true
ros_option: menu
default_ros_domain_id: 30
Menu:
ROS 1 noetic:
option_num: 1
ROS_version: 1
distro_name: noetic
ros1_path: /opt/ros/noetic
master_ip: # set if roscore isn't on this computer
cmds:
# - source ${HOME}/catkin_ws/devel/setup.${shell}
# - source_plugin openvino_bashrc
# PC 1 - ROS Master를 실행시키고 로봇 등장시키기
roslaunch smb_gazebo smb_gazebo.launch
# PC 2 - Master URI 변경 후 로봇 조종
rosrun teleop_twist_keyboard teleop_twist_keyboard.py
자료출처