系列文章目录
文章目录
- 系列文章目录
- 前言
- 一、创建用户自定义的msg和srv文件
- 1. 创建用于接口的package
- 2. 修改ros相关的配置文件
- 3. 构建ros_dds_comm_interface包
- 二、编写pub/sub程序
- 三、配置discovery server
- 四、编写fastrtps程序
- 五、配置ros与远程fastdds程序连接
- ros端
- 远程
- 注意项
- references
前言
本文介绍了ros环境中的节点与无ros环境程序之间的通讯。
ros = plumbing + tools + capabilities + community
其中plumbing采用DDS协议,ros2默认其中Fast-DDS,因此ros2节点默认是通过fastdds与其他节点(应用)进行通讯的,本文介绍的是在ros2环境下编写的节点与远程主机无ros环境通过fastrtps库编写的程序之间的通讯。
一、创建用户自定义的msg和srv文件
ros2中的类型与C++, python, DDS type 之间的对应关系
https://docs.ros.org/en/humble/Concepts/Basic/About-Interfaces.html
1. 创建用于接口的package
该文件是DDS与ros2节点之间通讯的接口文件
.msg:
string name
uint32 second
uint32 nanosecond
uint32 payload_size
byte[] payload
在ros中新建接口文件:
ros2 pkg create --build-type ament_cmake --license Apache-2.0 ros_dds_comm_interface
切换至ros_dds_comm_interface目录,并创建.msg
文件放入msg目录内:
cd ros_dds_comm_interface
mkdir msg
cd msg
touch DdsEvt.msg # 将上面.msg的内容填入
2. 修改ros相关的配置文件
在包ros_dds_comm_interface内修改:
CMakeLists.txt内的find_package(ament_cmake REQUIRED)下一行添加
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}"msg/DdsEvt.msg"# DEPENDENCIES geometry_msgs # Add packages that above messages depend on, in this case geometry_msgs for Sphere.msg
)
在package.xml的<package>标签下添加:
<!-- <depend>geometry_msgs</depend> --><buildtool_depend>rosidl_default_generators</buildtool_depend><exec_depend>rosidl_default_runtime</exec_depend><member_of_group>rosidl_interface_packages</member_of_group>
3. 构建ros_dds_comm_interface包
cd <your ros root dir>
colcon build --packages-select ros_dds_comm_interface
这一步会生成支持C++和Python的接口文件,即可用Python使用该接口也可用C++。
二、编写pub/sub程序
在目录build/ros_dds_comm_interface/rosidl_adapter/ros_dds_comm_interface/msg
下找到DdsEvt.idl文件,其内容如下:
module ros_dds_comm_interface {module msg {struct DdsEvt {string name;uint32 second;uint32 nanosecond;uint32 payload_size;sequence<octet> payload;};};
};
通过该idl文件生成fastdds接口代码
fastddsgen -example CMake -typeros2 -d <target_dir> DdsEvt.idl
需要注意,先找到接口包生成的头文件<workspace>/build/ros_dds_comm_interface/rosidl_generator_cpp/ros_dds_comm_interface/msg/dds_evt.hpp
该文件就是从.msg生成的头文件,文件名为DdsEvt.msg生成的头文件os_dds_comm_interface/msg/dds_evt.hpp,类名DdsEvt
publisher.cpp
#include <chrono>
#include <functional>
#include <memory>
#include <string>#include "rclcpp/rclcpp.hpp"
#include "ros_dds_comm_interface/msg/dds_evt.hpp"using namespace std::chrono_literals;/* This example creates a subclass of Node and uses std::bind() to register a* member function as a callback from the timer. */class MinimalPublisher : public rclcpp::Node
{
public:MinimalPublisher(): Node("minimal_publisher"), count_(0){publisher_ = this->create_publisher<ros_dds_comm_interface::msg::DdsEvt>("InterfaceTestTopic", 10);timer_ = this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this));}private:void timer_callback(){auto message = ros_dds_comm_interface::msg::DdsEvt();// std_msgs::msg::String();message.name = "Hello, world! " + std::to_string(count_);message.second = count_++;RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.name.c_str());publisher_->publish(message);}rclcpp::TimerBase::SharedPtr timer_;rclcpp::Publisher<ros_dds_comm_interface::msg::DdsEvt>::SharedPtr publisher_;size_t count_;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<MinimalPublisher>());rclcpp::shutdown();return 0;
}
subscriber.cpp
#include <functional>
#include <memory>#include "rclcpp/rclcpp.hpp"
#include "ros_dds_comm_interface/msg/dds_evt.hpp" // CHANGEusing std::placeholders::_1;class MinimalSubscriber : public rclcpp::Node
{
public:MinimalSubscriber(): Node("minimal_subscriber"){subscription_ = this->create_subscription<ros_dds_comm_interface::msg::DdsEvt>( // CHANGE"InterfaceTestTopic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));}private:void topic_callback(const ros_dds_comm_interface::msg::DdsEvt & msg) const // CHANGE{RCLCPP_INFO_STREAM(this->get_logger(), "I heard: '" << msg.name << "'"); // CHANGE}rclcpp::Subscription<ros_dds_comm_interface::msg::DdsEvt>::SharedPtr subscription_; // CHANGE
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<MinimalSubscriber>());rclcpp::shutdown();return 0;
}
修改cmake文件:
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(ros_dds_comm_interface REQUIRED)add_executable(talker src/publisher.cpp)
ament_target_dependencies(talker rclcpp ros_dds_comm_interface)
add_executable(listener src/subscriber.cpp)
ament_target_dependencies(listener rclcpp ros_dds_comm_interface)install(TARGETStalkerlistenerDESTINATION lib/${PROJECT_NAME})
修改xml文件:
<buildtool_depend>ament_cmake</buildtool_depend><depend>ros_dds_comm_interface</depend>
构建并验证是否正常
cd <workspace>
colcon build
on one terminal:
source install/setup.bash
ros2 run cpp_pubsub talker
on another terminal:
source install/setup.bash
ros2 run cpp_pubsub listener
此时两个终端应该能看到数据收发
三、配置discovery server
在一个新终端中开启discovery服务
fastdds discovery -i 0 -l 0.0.0.0 -p 11811
# 输入fastdds discovery -h有更多帮助信息
如果服务在本机上,则需要设置下面的地址为127.0.0.1
talker终端:
export ROS_DISCOVERY_SERVER=127.0.0.1:11811
ros2 run cpp_pubsub talker
listener终端:
export ROS_DISCOVERY_SERVER=127.0.0.1:11811
ros2 run cpp_pubsub listener
此时应该能看到两个终端的收发。
此时ros2 topic list不能看到对应topic,需要开启SUPER_CLIENT
模式,具体详见下面的references
5 ros2 检视
export FASTRTPS_DEFAULT_PROFILES_FILE=<path/to/>super_client_configuration_file.xml
四、编写fastrtps程序
相关库安装见:https://blog.csdn.net/surfaceyan/article/details/137162608
在ros2中显示的topic为InterfaceTestTopic
,fastdds中对应的topic为rt/InterfaceTestTopic
,data type通过工具生成已经填入为ros_dds_comm_interface::msg::dds_::DdsEvt_
据此修改例子:https://github.com/eProsima/Fast-DDS/tree/master/examples/cpp/discovery_server
五、配置ros与远程fastdds程序连接
ros端
new terminal
fastdds discovery -i 0 -l 0.0.0.0 -p 11811
new terminal
# udpv4
export ROS_DISCOVERY_SERVER=127.0.0.1:11811
ros2 run cpp_pubsub talker
export ROS_DISCOVERY_SERVER=TCPv4:[127.0.0.1]:42100
远程
./DiscoveryServerExample publisher -c <ros主机ip> -p <fastdds discovery端口号:11811 >
此时应该能看到ros2节点和远程无ros主机进行通讯
以固定周期发送数据:
yes | while read line; do echo "$line"; sleep 0.01; done | ./DdsEvt publisher
注意项
不同版本的DDS存在兼容性问题。
需要注意要保持通讯实体之间用相同的DDS版本
根据topic所属的ROS2子系统,ROS 2 会向DDS中的topic添加特殊的token。更多信息请参考references [6, 7]。
有关fastddsgen的选项参考[5]
references
- https://github.com/briancaglioni/FASTDDS_ROS2_Pub_Sub_Guide
- https://docs.ros.org/en/humble/Concepts/Basic/About-Interfaces.html
- https://docs.ros.org/en/humble/Tutorials/Advanced/Discovery-Server/Discovery-Server.html
- https://fast-dds.docs.eprosima.com/en/stable/fastdds/ros2/discovery_server/ros2_discovery_server.html
- https://fast-dds.docs.eprosima.com/en/latest/fastddsgen/usage/usage.html
- https://fast-dds.docs.eprosima.com/en/stable/fastdds/use_cases/rosbag_capture/rosbag_capture.html
- https://design.ros2.org/articles/topic_and_service_names.html#examples-of-ros-names-to-dds-concepts