背景介绍

软件平台

The Robot Operating System (ROS) is a set of software libraries and tools that help you build robot applications. From drivers to state-of-the-art algorithms, and with powerful developer tools, ROS has what you need for your next robotics project. And it’s all open source.


Robot操作系统(ROS)是一组软件库和工具,可帮助您构建机器人应用程序。从驱动程序到最先进的算法,再加上强大的开发工具,ROS为您的下一个机器人项目提供了所需的一切。而且这一切都是开源的。

硬件平台

x3

地平线旭日®️X3派是一款面向生态开发者的嵌入式AI开发板,接口兼容树莓派,具有5Tops端侧推理与4核ARM A53处理能力。可同时多路Camera Sensor的输入并支持H.264/H.265编解码。结合地平线的高性能AI工具链与机器人开发平台,助力开发者快速落地解决方案。

ROS 订阅机制

ROS 2将复杂的系统分解为许多模块化节点。 主题是 ROS 图的重要组成部分,充当节点交换消息的总线。

Topic-SinglePublisherandSingleSubscriber

一个节点可以将数据发布到任意数量的主题,同时订阅任意数量的主题。

Topic-MultiplePublishersandMultipleSubscribers

主题是数据在节点之间移动的主要方式之一,因此也是在系统的不同部分之间移动的主要方式之一。

(不得不说官方文档还是写的很不错的)

自定义订阅者

(这里参考了很多官方文档)

创建包

打开一个新终端并获取 ROS 2 安装,以便命令正常工作。ros2

导航到上一教程中创建的目录。ros2_ws

回想一下,包应该在目录中创建,而不是在工作区的根目录中创建。 因此,导航到 并运行包创建命令:srcros2_ws/src

ros2 pkg create --build-type ament_python py_pubsub

您的终端将返回一条消息,验证您的软件包及其所有必要文件和文件夹的创建。

编写订阅节点

导航到 py_pubsub

回想一下,此目录是一个 Python 包,与它嵌套的 ROS 2 包同名。ros2_ws/src/py_pubsub/py_pubsub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

import rclpy
from rclpy.node import Node

from std_msgs.msg import String


class MinimalSubscriber(Node):

def __init__(self):
super().__init__('minimal_subscriber')
self.subscription = self.create_subscription(
String,
'topic',
self.listener_callback,
10)
self.subscription # prevent unused variable warning

def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)


def main(args=None):
rclpy.init(args=args)

minimal_subscriber = MinimalSubscriber()

rclpy.spin(minimal_subscriber)

# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_subscriber.destroy_node()
rclpy.shutdown()


if __name__ == '__main__':
main()

添加依赖

将一个级别导航回目录,其中已为您创建了目录和文件。ros2_ws/src/py_pubsub setup.py setup.cfg package.xml

使用文本编辑器打开。package.xml

如上一教程所述,请确保填写<description> <maintainer> <license>标记:

1
2
3
<description>Examples of minimal publisher/subscriber using rclpy<description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>

在上述行之后,添加与节点的导入语句对应的以下依赖项:

1
2
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>

这声明了包的需求以及何时执行其代码

添加入口点

打开setup.py文件。 同样,将 、 和 字段与您的 :maintainer maintainer_email description licensepackage.xml

1
2
3
4
5
maintainer='YourName',
maintainer_email='you@email.com',
description='Examples of minimal publisher/subscriber using rclpy',
license='Apache License 2.0',

在字段的括号内添加以下行:

1
2
3
4
5
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
],
},

不要忘记保存。

检查设置.cfg

文件的内容应自动正确填充,如下所示:setup.cfg

1
2
3
4
[develop]
script-dir=$base/lib/py_pubsub
[install]
install-scripts=$base/lib/py_pubsub

这只是告诉设置工具将您的可执行文件放入 ,因为会在那里寻找它们。

您现在可以构建软件包,获取本地安装文件并运行它,但让我们先创建订阅者节点,以便您可以看到完整的系统在工作。

消息格式

参考旭日x3的ai_msgs

ai_msgs

自定义的ai msg,包括人/物/车等检测框roi,跟踪track id,抓拍,特征,手势识别等结果。用于算法模型推理后,发布推理结果。

message详细说明如下:

PerceptionTargets.msg

感知结果的消息定义,一般每帧图像对应一个感知结果消息。消息包含:

1、std_msgs/Header header

消息头,包含stamp和frame_id,和用于模型推理的图片header一致,用于表示此消息所对应的图片。

2、int16 fps

感知结果的输出帧率,即算法模型推理处理帧率,小于0无效。

当fps小于sensor的图像输出帧率时,说明算法模型推理耗时比较长,需要对推理流程进行优化。

3、Perf[] perfs

性能统计信息,比如记录每个模型推理的耗时。

当有多个模型时,可以通过记录每个模型的性能信息发现模型推理流程的性能瓶颈。同时当发生推理异常导致无ai消息输出时,也能够根据性能统计信息中的模型名,判断是哪个模型没有输出,实现快速缩小排查范围。

4、Target[] targets

感知目标集合。

5、Target[] disappeared_targets

消失目标集合。

CaptureTargets.msg

抓拍结果的消息定义。消息包含:

1、std_msgs/Header header

消息头,包含stamp和frame_id,和用于模型推理的图片header一致,用于表示此消息所对应的图片。

2、Perf.msg

性能统计信息,比如记录每个模型推理的耗时

4、Target[] targets

抓拍目标集合。

Perf.msg

性能统计信息。

1、string type

类型,用于表示处理模块。例如type为模型名时,表示对此模型推理的性能统计。

2、builtin_interfaces/Time stamp_start

开始处理的时间戳。

3、builtin_interfaces/Time stamp_end

处理完成的时间戳。

Target.msg

目标消息。

1、string type

目标类型名称,如:人、车、动物、物体,具体值可以定义为为person/car/object/animal

2、uint64 track_id

目标跟踪ID号。

3、Roi[] rois

目标的检测框。一个目标可能包含多个检测框,如同时具有人体、人头和人脸检测框。

4、Attribute[] attributes

属性。一个目标可能包含多个属性信息,如同时具有年龄、性别和手势结果。

5、Point[] points

关键点。一个目标可能包含多个关键点信息,如同时具有人脸关键点、人体骨骼点、人手关键点

6、Capture[] captures

跟踪目标抓拍图信息,包含抓拍图、特征、特征的底库检索结果信息。

Roi.msg

roi感知消息,如:人体检测框、人头检测框、人脸检测框、人手检测框。

1、string type

roi类型,如body/head/face/hand。

2、sensor_msgs/RegionOfInterest rect

检测框。

3、float32 score

检测结果的置信度。

Attribute.msg

属性感知消息,如:年龄、性别、手势、眼镜、口罩、活体信息、车辆类型、车辆颜色、车辆速度、车辆所在车道等信息。

1、string type

属性类型,如年龄:age,性别:gender, 手势:gesture。

2、float32 value

属性数值。

如age数值定义举例:

​ val为实际年龄数值

gender数值定义举例:

​ “1”: “男”, “-1”: “女”

gesture数值定义举例:

​ 0: Background, // 无手势

​ 1: FingerHeart, // 比心

​ 2: ThumbUp, // 大拇指向上

​ 3: Victory, // V手势

​ 4: Mute, // 嘘

​ 10: Palm, // 手掌

​ 11: Okay, // OK手势

​ 12: ThumbRight, // 大拇指向右

​ 13: ThumbLeft, // 大拇指向左

​ 14: Awesome, // 666手势

3、float32 confidence

属性结果的置信度。

Point.msg

关键点感知结果,如:人脸关键点、人体骨骼点、人手关键点。

1、string type

类型,如body_kps/face_kps/hand_kps。

2、geometry_msgs/Point32[] point

关键点数值。

3、float32[] confidence

每个关键点的置信度,维度和关键点数值相同。

Capture.msg

抓拍信息。

1、std_msgs/Header header

抓拍图对应原视频帧的timestamp和frame_id。

2、sensor_msgs/Image img

抓拍图。

3、float32[] features

抓拍图对应的特征数据,数据长度为0时表示无特征。

4、DBResult db_result

特征的底库检索结果。只有当有特征时底库检索结果有效。

DBResult.msg

底库检索结果。

1、string db_type

底库名称。

2、string match_id

匹配目标ID。

3、float32 distance

特征比对距离。

4、float32 similarity

特征比对相似度。

构建和运行

编译

colcon build --packages-select py_pubsub

打开一个新终端,导航到 ,并获取安装文件:

. install/setup.bash

现在运行说话者节点:

ros2 run py_pubsub listener

我的代码修改

其实Python脚本代码可以直接运行,无需编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

import rclpy
from rclpy.node import Node

from ai_msgs.msg import PerceptionTargets


class MinimalSubscriber(Node):

def __init__(self):
super().__init__('minimal_subscriber')
self.subscription = self.create_subscription(
PerceptionTargets,
'hobot_mono2d_body_detection',
self.listener_callback,
10)
self.subscription # prevent unused variable warning

def listener_callback(self, msg):
if len(msg.targets) > 1:
self.get_logger().info('I heard:' + str(msg.targets[0].type) + str(msg.fps))# "%s"' % msg.data
else:
self.get_logger().info('dont')
# https://c-gitlab.horizon.ai/HHP/box/hobot_msgs/-/tree/develop/ai_msgs

def main(args=None):
rclpy.init(args=args)

minimal_subscriber = MinimalSubscriber()

rclpy.spin(minimal_subscriber)

# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_subscriber.destroy_node()
rclpy.shutdown()


if __name__ == '__main__':
main()


结果展示

1
2
3
4
5
6
7
8
9
10
11

[INFO] [1672741631.264257258] [minimal_subscriber]: I heard:person30
[INFO] [1672741631.295972687] [minimal_subscriber]: I heard:person30
[INFO] [1672741631.327739324] [minimal_subscriber]: I heard:person30
[INFO] [1672741631.363461421] [minimal_subscriber]: I heard:person30
[INFO] [1672741631.392870207] [minimal_subscriber]: I heard:person30
[INFO] [1672741631.429643377] [minimal_subscriber]: I heard:person30
[INFO] [1672741631.465771636] [minimal_subscriber]: I heard:person30
[INFO] [1672741631.496358035] [minimal_subscriber]: I heard:person30