ROS2概念学习笔记(一)

2023-05-11 Views3328字16 min read

一、节点(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'.
EOF