【ROS/DDS】FastDDS :编写FastDDS程序实现与ROS2 通讯(四)
FastDDS :编写 FastDDS 程序实现与 ROS2 通讯(四)
为什么使用 FastDDS 与 ROS 2 通信
ROS 2 本身已经具备了通信功能,但 FastDDS 的引入为 ROS 2 提供了更强大、灵活的通信能力。FastDDS 作为 ROS 2 默认的 DDS 实现,提供了更高效、更实时的通信性能,并能够满足大规模分布式系统、实时响应、低延迟等需求。
下面将深入讨论为什么在某些应用场景下,使用 FastDDS 与 ROS 2 进行通信相比于直接使用 ROS 2 提供的通信机制具有更大的优势,并列举相关的应用场景。
为什么使用 FastDDS 与 ROS 2 通信
可扩展性与可靠性
FastDDS 支持分布式系统,特别适合需要大规模设备互联的应用场景。它能够通过 QoS(Quality of Service)策略来控制数据传输的可靠性、实时性等,这使得开发者能够在不同的硬件和网络环境下实现可靠的数据交换。
- 多设备系统:例如在多机器人协作的场景中,FastDDS 能够帮助机器人之间高效、稳定地交换数据,尤其是在较大的分布式系统中,能够实现对节点的高效管理和通信。
** 与 ROS 2 的无缝集成**
FastDDS 与 ROS 2 本身的设计理念完全契合。ROS 2 已经决定使用 DDS 作为其默认的通信中间件,而 FastDDS 就是这一标准的一个轻量级、高性能实现。它不仅可以与 ROS 2 的 API 完美兼容,还能保证高效的数据交换。
- ROS 2 对 DDS 的依赖:ROS 2 从设计之初就依赖 DDS 协议来实现通信,而 FastDDS 是符合 DDS 标准的一种轻量级实现。因此,在 ROS 2 系统中,开发者无需额外的配置或复杂的桥接,可以直接使用 FastDDS 提供的高效功能。
兼容 ROS 1 和 ROS 2
Fast DDS 可以与 ROS 1 和 ROS 2 都兼容,因此如果你的系统中同时存在 ROS 1 和 ROS 2 节点,Fast DDS 可以充当一个桥梁,将这两者连接在一起,解决不同版本之间的通信问题。
- ROS 1 和 ROS 2 互通:假设你的项目中需要将一个 ROS 1 设备与一个 ROS 2 设备连接,可以使用 Fast DDS 作为中介来实现这两个版本之间的消息交换。这样,你无需重构整个系统,而只需使用 Fast DDS 进行桥接。
** 支持跨平台通信**
FastDDS 支持多平台的跨设备、跨操作系统通信,它能够在不同的操作系统(如 Linux、Windows)以及不同的硬件平台(如 x86、ARM)之间进行高效的通信。
- 跨平台部署:在一些系统中,不同的硬件平台和操作系统可能需要进行通信,FastDDS 可以帮助开发者在这些多平台环境中实现透明的数据交换。
使用 FastDDS 与 ROS 2 通信的应用场景
以下是一些特别适合使用 FastDDS 与 ROS 2 通信 的典型应用场景,说明在这些场景中,直接使用 ROS 2 提供的通信机制可能无法满足需求,或效率较低,而 FastDDS 提供了必要的性能提升。
** 自动驾驶系统**
自动驾驶系统需要高效的实时数据交换,以确保不同模块(如感知、规划、控制、执行等)之间能够无缝合作。
- 高实时性要求:自动驾驶系统中涉及到的传感器数据(如激光雷达、摄像头、雷达等)的采集与处理需要实时传输。使用 FastDDS,能够在这些系统中实现低延迟的数据传输,确保机器人能够在毫秒级别做出反应。
- 分布式计算:自动驾驶系统常常由多个子系统组成(如计算平台、传感器模块、执行器等)。通过 FastDDS,可以高效地在这些分布式系统之间进行通信,确保各个模块的实时协作。
** 多机器人协作**
在多机器人系统中,每个机器人都需要与其他机器人交换数据,例如共享地图、协作完成任务等。
- 大规模分布式系统:在多机器人协作中,每个机器人都充当一个独立的节点,它们之间需要不断交换数据。使用 FastDDS,能够确保不同机器人之间进行高效、可靠的数据交换。
- 消息频繁且体积大:多个机器人同时产生的数据量非常庞大,FastDDS 的高吞吐量和低延迟特性能确保即使在极为复杂的环境中也能保持流畅的通信。
工业自动化与机器人控制
在工业自动化场景下,控制系统和机器人之间的通信对实时性和可靠性有非常高的要求。
- 实时控制系统:例如在工业生产线中,机器人和传感器需要实时交换信息,以保证生产过程的高效性和安全性。FastDDS 能够提供低延迟的通信,确保系统能够实时反应。
- 复杂的控制指令传输:在多关节机器人的控制中,控制指令通常需要实时、准确地传送给各个关节执行器。FastDDS 通过支持 QoS 策略,能够确保指令的可靠性和及时性。
智能制造与数字孪生
在智能制造和数字孪生场景中,设备的实时状态需要与数据中心和其他设备共享,以实现对整个工厂或生产线的数字化仿真和实时监控。
- 大数据量和实时性:智能制造中会涉及大量设备的数据,如温度、湿度、设备运行状态等,这些数据需要高效地传输到中央服务器进行分析。FastDDS 的高吞吐量和低延迟特性能保证这些实时数据快速传递。
- 分布式设备管理:智能制造通常有多个设备和机器,每个设备都需要与中央控制系统进行通信,FastDDS 的可扩展性使得它能处理更大规模的设备系统。
远程操作和遥控
在一些远程操作的场景中,操作者需要与远程设备或机器人进行通信,并实时控制其操作。
- 高可靠性和低延迟:在远程控制环境中,通信的实时性和可靠性至关重要。FastDDS 提供的 QoS 策略允许开发者确保低延迟、高可靠的数据交换。
- 实时视频与传感器数据:远程操作系统通常需要实时接收设备的传感器数据以及视频流,FastDDS 能够高效处理这些大数据流,确保数据的实时传输。
开发与 ROS2 通讯的 FastDDS
(一)ROS2 自定义接口功能包
创建工作空间
首先,在根目录下面创建一个新的工作空间 ros2_fastdds_ws
和新的功能包 topic_pkg
,然后新建脚本文件 topic_interface_pub.cpp和topic_interface_sub.cpp,然后用VScode打开test_w文件夹。
mkdir -p ./ros2_fastdds_ws/src
cd ros2_fastdds_ws/src
ros2 pkg create topic_pkg --build-type ament_cmake --dependencies rclcpp std_msgs --node-name topic_interface_pub
touch ./topic_pkg/src/topic_interface_sub.cpp
创建功能接口
- 进入 src 目录,然后创建接口功能包 interface_pkg,然后在 interface_pkg 功能包下创建 msg 文件夹,并且创建一个 Student.msg 文件,注意该文件名首字母必须大写
cd ./src/
ros2 pkg create interface_pkg --build-type ament_cmake
mkdir -p ./interface_pkg/msg
touch ./interface_pkg/msg/Student.msg
- 在 Student.msg 文件下自定义数据格式
string name
int32 age
float64 height
- 在 CMakeLists.txt 中添加以下代码
# 编译的依赖包
find_package(rosidl_default_generators REQUIRED)#为接口文件生成源代码,接口名字首字母必须大写
rosidl_generate_interfaces(${PROJECT_NAME}"msg/Student.msg"
)
- 在 package.xml 中添加以下代码
<!--编译依赖--><build_depend>rosidl_default_generators</build_depend><!--执行依赖--><exec_depend>rosidl_default_runtime</exec_depend><!--声明当前所属的功能包组--><member_of_group>rosidl_interface_packages</member_of_group>
- 编译一下 interface_pkg 功能包
cd ..
colcon build --packages-select interface_pkg
- 打开 install/interface_pkg 目录,可以看见在 include 目录下面生成了 C++ 对应的自定义接口头文件,在 local 目录下生成了 Python 对应的自定义接口库
编写 Publisher 与 Subscriber 代码
- 注意在之前工作空间中已创建的包中修改两个 cpp 代码
/topic_pkg/src/topic_interface_pub.cpp
/topic_pkg/src/topic_interface_sub.cpp
- 重新编写发布者节点
/***
功能:以某个固定频率发布学生信息
***/#include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
#include "std_msgs/msg/string.hpp" // 字符串消息类型
#include "interface_pkg/msg/student.hpp" // 自定义接口using namespace std::chrono_literals; //设置持续时间相关class PublisherNode : public rclcpp::Node
{public:PublisherNode(): Node("topic_interface_pub") // ROS2节点父类初始化{// 创建发布者对象(消息类型、话题名、队列长度)publisher_ = this->create_publisher<interface_pkg::msg::Student>("topic_interface", 10); // 创建一个定时器,定时执行回调函数timer_ = this->create_wall_timer(500ms, std::bind(&PublisherNode::timer_callback, this)); }private:// 创建定时器周期执行的回调函数void timer_callback() {// 创建一个Student类型的消息对象auto stu = interface_pkg::msg::Student(); stu.name = "小明" ; stu.age = 8;stu.height = 1.8;// 发布话题消息 publisher_->publish(stu); // 输出日志信息,提示已经完成话题发布 RCLCPP_INFO(this->get_logger(), "发布的消息: '%s','%d','%.2f'",stu.name.c_str(),stu.age,stu.height); }rclcpp::TimerBase::SharedPtr timer_; // 定时器指针rclcpp::Publisher<interface_pkg::msg::Student>::SharedPtr publisher_; // 发布者指针};// ROS2节点主入口main函数
int main(int argc, char * argv[])
{// ROS2 C++接口初始化rclcpp::init(argc, argv); // 创建ROS2节点对象并进行初始化 rclcpp::spin(std::make_shared<PublisherNode>()); // 关闭ROS2 C++接口rclcpp::shutdown(); return 0;
}
-
重新编写订阅者节点
_/***_
_功能:订阅发布者发布的学生信息_
_***/_#include "rclcpp/rclcpp.hpp" _// ROS2 C++接口库_
#include "std_msgs/msg/string.hpp" _// 字符串消息类型_
#include "interface_pkg/msg/student.hpp" _// 自定义接口_using std::placeholders::_1;class SubscriberNode : public rclcpp::Node
{public:SubscriberNode