ROS2概念学习笔记(一)
一、节点(Node)
节点是ROS的最小执行单元,每个节点应该只负责单一一个目的。节点之间通过主题(topic)、服务(services)、动作(actions)或参数(parameters)等方式进行通信。
ROS系统就是由许许多多的节点组成。
一个或多个节点组成一个可执行程序(C++程序,python程序等)。
一个或多个可行执程序组成一个包(package)。
例如,我们通过以下命令安装我们的第一个ROS包,也就是小海龟仿真,
$ sudo apt update
$ sudo apt install ros-humble-turtlesim
通过以下命令查看turtlesim包有哪些可执行程序,
$ ros2 pkg executables turtlesim
turtlesim draw_square
turtlesim mimic
turtlesim turtle_teleop_key
turtlesim turtlesim_node
回顾我们熟悉的ROS命令:
# ros2 run <package_name> <executable_name>
$ ros2 run turtlesim turtlesim_node
其中,turtlesim是一个包,而turtlesim_node是一个可执行程序,也就是通过执行turtlesim包的turtlesim_node程序创建了一个/turtlesim。(怎么有点类和实例的味道?)
假如,我们想查看当前系统中运行这哪些节点,
$ ros2 node list
$ /turtlesim
当前只运行了/turtlesim 这一个节点。在启动另外一个控制节点,
$ ros2 run turtlesim turtle_teleop_key
# 查看节点
$ ros2 node list
$ /turtlesim
/teleop_turtle
我们可以通过Remapping修改节点的属性,比如我们想启动一个新的/turtlesim并将名字改为/my_turtle,
$ ros2 run turtlesim turtlesim_node --ros-args --remap __node:=my_turtle
$ ros2 node list
$ /my_turtle
/turtlesim
/teleop_turtle
知道节点的名字,我们就可以通过以下命令查看节点的更多信息
$ ros2 node info <node_name>
$ ros2 node info /my_turtle
$ /my_turtle
Subscribers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/turtle1/cmd_vel: geometry_msgs/msg/Twist
Publishers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/rosout: rcl_interfaces/msg/Log
/turtle1/color_sensor: turtlesim/msg/Color
/turtle1/pose: turtlesim/msg/Pose
Service Servers:
/clear: std_srvs/srv/Empty
/kill: turtlesim/srv/Kill
/my_turtle/describe_parameters: rcl_interfaces/srv/DescribeParameters
/my_turtle/get_parameter_types: rcl_interfaces/srv/GetParameterTypes
/my_turtle/get_parameters: rcl_interfaces/srv/GetParameters
/my_turtle/list_parameters: rcl_interfaces/srv/ListParameters
/my_turtle/set_parameters: rcl_interfaces/srv/SetParameters
/my_turtle/set_parameters_atomically: rcl_interfaces/srv/SetParametersAtomically
/reset: std_srvs/srv/Empty
/spawn: turtlesim/srv/Spawn
/turtle1/set_pen: turtlesim/srv/SetPen
/turtle1/teleport_absolute: turtlesim/srv/TeleportAbsolute
/turtle1/teleport_relative: turtlesim/srv/TeleportRelative
Service Clients:
Action Servers:
/turtle1/rotate_absolute: turtlesim/action/RotateAbsolute
Action Clients:
二、主题(topic)
话题是单向消息发送/接收的一种通信方式,一个节点发送一个接受就完事。不过主题这种通信方式是一个一对一、一对多、多对一、多对多的。
需要引入另外一个信息(Message)的概念,信息定义了主题的数据格式。吐槽一下:主题和信息这两个数据可真是够怪异的,居然信息是数据格式,主题才是数据载体,按照我们的生活习惯应该反过来才对。Nodes send data over topics using messages.
例如,下面定义了小海龟姿态的信息数据格式,
# turtlesim/msg/Pose.msg
float32 x
float32 y
float32 theta
float32 linear_velocity
float32 angular_velocity
我们再次启动两个节点观察它们通信的主题,
$ ros2 run turtlesim turtlesim_node
$ ros2 run turtlesim turtle_teleop_key
然后,我们就可以通过以下命令查看
$ ros2 topic list
/parameter_events
/rosout
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose
ros2 topic list -t 会额外输出每个主题的信息类型,
$ ros2 topic list -t
/parameter_events [rcl_interfaces/msg/ParameterEvent]
/rosout [rcl_interfaces/msg/Log]
/turtle1/cmd_vel [geometry_msgs/msg/Twist]
/turtle1/color_sensor [turtlesim/msg/Color]
/turtle1/pose [turtlesim/msg/Pose]
打开rqt_graph程序查看节点和主题(取消隐藏复选框,另外不同版本的输出略有不同),
为了方便debug,我们可以从终端打印主题内部的数据,
# ros2 topic echo <topic_name>
$ ros2 topic echo /turtle1/cmd_vel
linear:
x: 2.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0
---
回到 rqt_graph并去掉debug的复选框,如果没有去掉的话,这时候我们发现多了一个节点/_ros2cli_26646订阅/turtle1/cmd_vel 这个主题。这个节点就是上面的echo命令创建的。
同样,我们可以通过以下命令查看主题的相关信息,
$ ros2 topic info /turtle1/cmd_vel
Type: geometry_msgs/msg/Twist
Publisher count: 1
Subscription count: 2
我们知道主题对应的信息类型之后,可以查看具体数据结构,例如上面主题的信息类型为geometry_msgs/msg/Twist,表示geometry_msgs包有一个msg名为Twist,那么查看的命令为
$ ros2 interface show geometry_msgs/msg/Twist
#This expresses velocity in free space broken into its linear and angular parts.
Vector3 linear
float64 x
float64 y
float64 z
Vector3 angular
float64 x
float64 y
float64 z
知道了主题,也知道了信息类型,那么我们在debug的时候就可以通过终端直接发布主题,如,
# ros2 topic pub <topic_name> <msg_type> '<args>'
$ 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}}"
publisher: beginning loop
publishing #1: geometry_msgs.msg.Twist(linear=geometry_msgs.msg.Vector3(x=2.0, y=0.0, z=0.0), angular=geometry_msgs.msg.Vector3(x=0.0, y=0.0, z=1.8))
--once表示仅发布一条主题然后程序退出。我们也可以看到小海龟转了一个小弯,
我们也可以以一定的频率持续发布主题,比如1Hz,
$ ros2 topic pub --rate 1 /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}}"
那么小海龟就会在转圈圈,
我们可以查看某个主题的发布频率,如
$ ros2 topic hz /turtle1/pose
average rate: 59.354
min: 0.005s max: 0.027s std dev: 0.00284s window: 58
总结一下:主题这种通信方式就是一个节点作为发布者以一定的频率发布主题,另外一个节点作为订阅者订阅了该主题之后就可以接受这条主题,主题的内容由信息类型定义。一个节点可以作为发布者发布不同的主题,同时也可以作为订阅者订阅不同的主题。例如小海龟节点作为订阅者订阅了两条主题,同时作为发布者发布了4条主题,如下所示。
$ /my_turtle
Subscribers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/turtle1/cmd_vel: geometry_msgs/msg/Twist
Publishers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/rosout: rcl_interfaces/msg/Log
/turtle1/color_sensor: turtlesim/msg/Color
/turtle1/pose: turtlesim/msg/Pose
三、服务(services)
上面那种主题的通信方式是发布者以固定的频率持续输出数据,而订阅者只能被动接受的单向通信。显然有些功能并不需要怎么高频率的通信。服务是以请求/响应双向通信的方式,客服端有通信需求了就想服务器发起通信请求,服务器才响应并把数据发给客户端。如下图所示,服务这种通信方式,需要有一个节点作为服务端,另外一个作为客户端,服务定义了请求和响应的数据格式。服务可以是一对一也可以是一对多。
同样,启动两个节点看看它们启用了哪些服务通信,
$ ros2 run turtlesim turtlesim_node
$ ros2 run turtlesim turtle_teleop_key
查看激活的服务,
$ ros2 service list -t
/clear [std_srvs/srv/Empty]
/kill [turtlesim/srv/Kill]
/reset [std_srvs/srv/Empty]
/spawn [turtlesim/srv/Spawn]
...
/turtle1/set_pen [turtlesim/srv/SetPen]
/turtle1/teleport_absolute [turtlesim/srv/TeleportAbsolute]
/turtle1/teleport_relative [turtlesim/srv/TeleportRelative]
...
不同的服务属于不同的类型,类型正是定义了请求和响应的数据格式。
单独查看某个服务的类型,
$ ros2 service type /clear
std_srvs/srv/Empty
查看某个类型下有哪些服务,
$ ros2 service find std_srvs/srv/Empty
/clear
/reset
查看某个类型定义的数据格式,
$ ros2 interface show std_srvs/srv/Empty
---
返回---说明该服务的请求和响应不需要发送数据。
/Spawn是生成小海龟的服务,发起请求时需要给定小海龟的坐标和朝向,也可以指定小海龟的名字,服务响应之后返回小海龟的名字。
$ ros2 interface show turtlesim/srv/Spawn
float32 x
float32 y
float32 theta
string name # Optional. A unique name will be created and returned if this is empty
---
string name
从终端直接发起服务请求,例如请求清空小海龟的运动的轨迹,
# ros2 service call <service_name> <service_type> <arguments>
$ ros2 service call /clear std_srvs/srv/Empty
请求再生成一个小海龟,
$ ros2 service call /spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2, name: ''}"
requester: making request: turtlesim.srv.Spawn_Request(x=2.0, y=2.0, theta=0.2, name='')
response:
turtlesim.srv.Spawn_Response(name='turtle2')
四、动作(action)
动作是构建在主题和服务之上的一种通信方式,适用于那些需要长时间的任务。想象一个例子,你的老板让你给他写一个本子(动作客户端发起目的服务请求),你回好的!(动作服务端响应目的服务),接着你的老板说本子给我吧(动作客户端发起结果服务请求),但是写本子是需要时间的啊,没办法马上给他,只能给他持续的反馈(反馈主题,动作服务端是发布者,动作客户端是订阅者):撰写进度10%……20%……80%……100%%,然后把本子甩给老板(动作服务端响应结果服务)。动作通信大概就是这么个工作方式,结合下图再理解一下,
启动节点,
$ ros2 run turtlesim turtlesim_node
$ ros2 run turtlesim turtle_teleop_key
在启动控制节点turtle_teleop_key之后,终端出现以下提示信息,其中第二行“用G|B|V|C|D|E|R|T键旋转到绝对方向,按F键取消”这一功能就是通过动作来通信的。
Use arrow keys to move the turtle.
Use G|B|V|C|D|E|R|T keys to rotate to absolute orientations. 'F' to cancel a rotation.
查看动作
$ ros2 action list -t
/turtle1/rotate_absolute [turtlesim/action/RotateAbsolute]
动作名称为/turtle1/rotate_absolute,类型turtlesim/action/RotateAbsolute。
查看动作信息
$ ros2 action info /turtle1/rotate_absolute
Action: /turtle1/rotate_absolute
Action clients: 1
/teleop_turtle
Action servers: 1
/turtlesim
查看动作通信的数据结构
$ ros2 interface show turtlesim/action/RotateAbsolute
# The desired heading in radians
float32 theta
---
# The angular displacement in radians to the starting position
float32 delta
---
# The remaining rotation in radians
float32 remaining
第一部分是目的服务的数据结构,第二部分是结果服务的数据结构,第三部分是反馈主题的数据结构。
从终端发送目的命令
# ros2 action send_goal <action_name> <action_type> <values>
$ ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: -1.57}" --feedback
Sending goal:
theta: -1.57
Goal accepted with ID: e6092c831f994afda92f0086f220da27
Feedback:
remaining: -3.1268222332000732
Feedback:
remaining: -3.1108222007751465
…
Result:
delta: 3.1200008392333984
Goal finished with status: SUCCEEDED
五、参数(parameter)
参数就是节点的配置信息,在生成节点的时候需要进行读取。
启动节点,
$ ros2 run turtlesim turtlesim_node
$ ros2 run turtlesim turtle_teleop_key
查看各个节点的参数,
$ ros2 param list
/teleop_turtle:
qos_overrides./parameter_events.publisher.depth
qos_overrides./parameter_events.publisher.durability
qos_overrides./parameter_events.publisher.history
qos_overrides./parameter_events.publisher.reliability
scale_angular
scale_linear
use_sim_time
/turtlesim:
background_b
background_g
background_r
qos_overrides./parameter_events.publisher.depth
qos_overrides./parameter_events.publisher.durability
qos_overrides./parameter_events.publisher.history
qos_overrides./parameter_events.publisher.reliability
use_sim_time
获取特定节点的特定参数,
# ros2 param get <node_name> <parameter_name>
$ ros2 param get /turtlesim background_g
Integer value is: 86
设置定节点的特定参数,
# ros2 param set <node_name> <parameter_name> <value>
$ ros2 param set /turtlesim background_r 150
Set parameter successful
保存某个节点的参数到文件
# ros2 param dump <node_name>
$ ros2 param dump /turtlesim > turtlesim.yaml
/turtlesim:
ros__parameters:
background_b: 255
background_g: 86
background_r: 150
qos_overrides:
/parameter_events:
publisher:
depth: 1000
durability: volatile
history: keep_last
reliability: reliable
use_sim_time: false
从配置文件中加载参数到节点,
# ros2 param load <node_name> <parameter_file>
$ ros2 param load /turtlesim turtlesim.yaml
Set parameter background_b successful
Set parameter background_g successful
Set parameter background_r successful
Set parameter qos_overrides./parameter_events.publisher.depth failed: parameter 'qos_overrides./parameter_events.publisher.depth' cannot be set because it is read-only
Set parameter qos_overrides./parameter_events.publisher.durability failed: parameter 'qos_overrides./parameter_events.publisher.durability' cannot be set because it is read-only
Set parameter qos_overrides./parameter_events.publisher.history failed: parameter 'qos_overrides./parameter_events.publisher.history' cannot be set because it is read-only
Set parameter qos_overrides./parameter_events.publisher.reliability failed: parameter 'qos_overrides./parameter_events.publisher.reliability' cannot be set because it is read-only
Set parameter use_sim_time successful
在节点启动时加载配置文件中的参数,
# ros2 run <package_name> <executable_name> --ros-args --params-file <file_name>
$ ros2 run turtlesim turtlesim_node --ros-args --params-file turtlesim.yaml
六、查阅日志
rqt_console是一个日志查看工具,
$ ros2 run rqt_console rqt_console
七、记录并回放实验数据
ROS是通过bag这个包记录并回放实验数据的,不过好像只能记录主题数据。
安装bag包
$ sudo apt-get install ros-humble-ros2bag \
ros-humble-rosbag2-storage-default-plugins
启动节点
$ ros2 run turtlesim turtlesim_node
$ ros2 run turtlesim turtle_teleop_key
控制节点中使用方向键移动小海龟(Use arrow keys to move the turtle)就是通过主题/turtle1/cmd_vel实现的,/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: 0.0
---
解释:Consider that you are in some space, then there are 3 axes - x, y and z which are mutually perpendicular to each other and their point of intersection is called the origin (x = 0, y = 0, z = 0). This can be a frame of reference i.e. you can define various points and directions w.r.t. them.
The x, y, and z in Twist.linear are the linear velocities in x, y and z directions w.r.t. that frame of reference.
Similarly, the x, y, and z in Twist.angular are the angular velocities about the x, y and z directions respectively w.r.t. the same frame of reference.
Since you have a ground robot, most probably your angular velocity will be in z i.e. robot's turning speed. And your linear velocity will be mostly in x i.e. robot's moving straight speed. This is the case for the Turtlebot 2 at least.
/turtle1/cmd_vel 主题可以控制小海龟的速度和方向。同时,我们一般还会记录小海龟是姿态/turtle1/pose,
记录示例,
$ mkdir bag_files
$ cd bag_files
$ ros2 bag record -o subset /turtle1/cmd_vel /turtle1/pose
-o指定记录名字(bag_file_name),Ctrl+C停止记录
查看记录信息
$ ros2 bag info subset
Files: subset.db3
Bag size: 228.5 KiB
Storage id: sqlite3
Duration: 48.47s
Start: Oct 11 2019 06:09:09.12 (1570799349.12)
End Oct 11 2019 06:09:57.60 (1570799397.60)
Messages: 3013
Topic information: Topic: /turtle1/cmd_vel | Type: geometry_msgs/msg/Twist | Count: 9 | Serialization Format: cdr
Topic: /turtle1/pose | Type: turtlesim/msg/Pose | Count: 3004 | Serialization Format: cdr
记录回放
$ ros2 bag play subset
[INFO] [rosbag2_storage]: Opened database 'subset'.