
目录摘要一、ROS2 消息对象的常见写法1.1 创建消息对象的基本格式1.2 以 Twist 为例创建速度消息1.3 给消息字段赋值1让机器人向前运动2让机器人原地旋转3让机器人停止二、ROS2 发布器 Publisher 的写法2.1 发布器的基本定义方式2.2 创建发布器2.3 发布消息2.4 完整发布器示例代码2.5 发布器代码重点解释1包含头文件2创建发布器3创建定时器4发布速度消息三、ROS2 订阅器 Subscriber 的写法3.1 订阅器的基本定义方式3.2 创建订阅器3.3 回调函数中接收消息3.4 完整订阅器示例代码3.5 发布端和订阅端访问字段的区别1发布端通常使用点号2订阅端通常使用箭头摘要上一篇文章主要讲解了ROS2 中常见的消息类型例如std_msgs、geometry_msgs、nav_msgs、sensor_msgs并重点说明了geometry_msgs::msg::Twist、Pose、PoseStamped、TransformStamped等消息的含义。链接如下ROS2 常见消息类型保姆级教程-CSDN博客https://blog.csdn.net/m0_58954356/article/details/161693378?spm1001.2014.3001.5502这一篇开始进入 ROS2 话题通信实战重点讲清楚几个问题1. ROS2 中如何创建消息对象2. Publisher 发布器怎么写3. Subscriber 订阅器怎么写本文仍然以移动机器人常用的速度控制消息geometry_msgs::msg::Twist为例带大家从代码层面真正看懂 ROS2 的话题发布、话题订阅和消息字段查看方法。一、ROS2 消息对象的常见写法1.1 创建消息对象的基本格式ROS2 中创建消息对象一般采用下面这种格式包名::msg::消息类型 变量名;例如std_msgs::msg::String msg; geometry_msgs::msg::Twist vel_cmd; nav_msgs::msg::Odometry odom; sensor_msgs::msg::LaserScan scan;这和普通 C 定义变量非常像。普通 C 中可以这样定义变量int age; double speed; std::string name;ROS2 中定义消息对象则是geometry_msgs::msg::Twist vel_cmd;其中geometry_msgs::msg::Twist 消息类型 vel_cmd 消息对象名也就是说vel_cmd是一个变量只不过它的类型不是int、double而是 ROS2 中的Twist消息类型。1.2 以 Twist 为例创建速度消息Twist 常用于 /cmd_vel 速度控制话题移动机器人控制中经常会看到geometry_msgs::msg::Twist vel_cmd;这句代码的意思是创建一个 Twist 类型的速度消息对象名字叫 vel_cmd。Twist里面主要包含两部分Vector3 linear; // 线速度 Vector3 angular; // 角速度完整字段如下vel_cmd.linear.x; vel_cmd.linear.y; vel_cmd.linear.z; vel_cmd.angular.x; vel_cmd.angular.y; vel_cmd.angular.z;对于大多数地面移动机器人来说最常用的是vel_cmd.linear.x; // 前进 / 后退速度 vel_cmd.angular.z; // 左转 / 右转角速度1.3 给消息字段赋值1让机器人向前运动例如geometry_msgs::msg::Twist vel_cmd; vel_cmd.linear.x 0.2; vel_cmd.angular.z 0.0;含义是机器人以 0.2 m/s 的速度向前运动不旋转。这里linear.x 0.2表示沿机器人前方方向前进。angular.z 0.0表示不绕 z 轴旋转。2让机器人原地旋转如果写成vel_cmd.linear.x 0.0; vel_cmd.angular.z 0.5;含义是机器人不前进只原地旋转。其中angular.z 0.5表示绕 z 轴旋转单位通常是rad/s也就是弧度每秒。3让机器人停止如果想让机器人停止一般写成vel_cmd.linear.x 0.0; vel_cmd.angular.z 0.0;并且发布出去pub_vel-publish(vel_cmd);需要注意的是机器人停止时最好不是简单地“不发消息”而是主动发布一个零速度消息。也就是linear.x 0.0 angular.z 0.0这样底盘控制节点才能明确知道机器人需要停止。二、ROS2 发布器 Publisher 的写法2.1 发布器的基本定义方式Publisher 需要指定消息类型如果要发布Twist消息发布器一般这样定义rclcpp::Publishergeometry_msgs::msg::Twist::SharedPtr pub_vel;可以拆开理解rclcpp::Publishergeometry_msgs::msg::Twist表示这是一个发布Twist消息的发布器。SharedPtr表示智能指针类型。pub_vel是发布器对象的名字。完整意思就是定义一个发布 geometry_msgs::msg::Twist 消息的发布器名字叫 pub_vel。2.2 创建发布器使用 create_publisher 创建发布器在 ROS2 节点类里面通常使用create_publisher创建发布器。例如pub_vel this-create_publishergeometry_msgs::msg::Twist(/cmd_vel, 10);这句代码可以拆成this-create_publishergeometry_msgs::msg::Twist表示创建一个发布Twist消息的发布器。/cmd_vel表示发布到/cmd_vel话题。10表示队列深度。所以这句代码整体含义是创建一个发布器这个发布器向 /cmd_vel 话题发布 Twist 类型消息队列深度为 10。2.3 发布消息先创建消息再赋值最后 publish发布消息的基本流程是1. 创建消息对象2. 给消息字段赋值3. 调用 publish 发布出去代码如下geometry_msgs::msg::Twist vel_cmd; vel_cmd.linear.x 0.1; vel_cmd.angular.z 0.0; pub_vel-publish(vel_cmd);其中pub_vel-publish(vel_cmd);表示把vel_cmd这条速度消息发布出去。如果pub_vel发布的是/cmd_vel话题那么底盘控制节点订阅到这个话题后就可以控制机器人运动。2.4 完整发布器示例代码周期发布 /cmd_vel 速度消息下面是一个简单的 ROS2 C 发布器节点功能是周期性发布速度消息让机器人向前运动。#include rclcpp/rclcpp.hpp #include geometry_msgs/msg/twist.hpp using namespace std::chrono_literals; class CmdVelPublisher : public rclcpp::Node { public: CmdVelPublisher() : Node(cmd_vel_publisher) { pub_vel this-create_publishergeometry_msgs::msg::Twist(/cmd_vel, 10); timer this-create_wall_timer( 500ms, std::bind(CmdVelPublisher::timer_callback, this) ); } private: void timer_callback() { geometry_msgs::msg::Twist vel_cmd; vel_cmd.linear.x 0.1; vel_cmd.linear.y 0.0; vel_cmd.linear.z 0.0; vel_cmd.angular.x 0.0; vel_cmd.angular.y 0.0; vel_cmd.angular.z 0.0; pub_vel-publish(vel_cmd); RCLCPP_INFO( this-get_logger(), Publishing: linear.x %.2f, angular.z %.2f, vel_cmd.linear.x, vel_cmd.angular.z ); } private: rclcpp::Publishergeometry_msgs::msg::Twist::SharedPtr pub_vel; rclcpp::TimerBase::SharedPtr timer; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto node std::make_sharedCmdVelPublisher(); rclcpp::spin(node); rclcpp::shutdown(); return 0; }2.5 发布器代码重点解释1包含头文件#include rclcpp/rclcpp.hpp #include geometry_msgs/msg/twist.hpp其中rclcpp/rclcpp.hpp是ROS2 C 客户端库头文件。geometry_msgs/msg/twist.hpp是Twist消息类型对应的头文件。只要代码里使用geometry_msgs::msg::Twist就需要包含#include geometry_msgs/msg/twist.hpp2创建发布器pub_vel this-create_publishergeometry_msgs::msg::Twist(/cmd_vel, 10);这句表示创建/cmd_vel话题发布器。消息类型是geometry_msgs::msg::Twist话题名字是/cmd_vel3创建定时器timer this-create_wall_timer( 500ms, std::bind(CmdVelPublisher::timer_callback, this) );这句表示每隔500ms执行一次timer_callback()所以这个节点会周期性发布速度消息。4发布速度消息pub_vel-publish(vel_cmd);这句才是真正把速度消息发布出去。如果没有这句前面只是创建了消息、给消息赋值并没有真正发到话题中。三、ROS2 订阅器 Subscriber 的写法3.1 订阅器的基本定义方式Subscriber 也需要指定消息类型如果要订阅Twist消息订阅器一般这样定义rclcpp::Subscriptiongeometry_msgs::msg::Twist::SharedPtr sub_vel;可以拆成rclcpp::Subscriptiongeometry_msgs::msg::Twist表示这是一个订阅Twist消息的订阅器。SharedPtr表示智能指针类型。sub_vel是订阅器对象名。完整含义是定义一个订阅 geometry_msgs::msg::Twist 消息的订阅器名字叫 sub_vel。3.2 创建订阅器使用 create_subscription 创建订阅器创建订阅器一般这样写sub_vel this-create_subscriptiongeometry_msgs::msg::Twist( /cmd_vel, 10, std::bind(CmdVelSubscriber::cmd_vel_callback, this, std::placeholders::_1) );其中geometry_msgs::msg::Twist表示订阅的消息类型。/cmd_vel表示订阅的话题名字。10表示队列深度。std::bind(CmdVelSubscriber::cmd_vel_callback, this, std::placeholders::_1)表示收到消息后执行cmd_vel_callback回调函数。3.3 回调函数中接收消息回调函数一般这样写void cmd_vel_callback(const geometry_msgs::msg::Twist::SharedPtr msg) { RCLCPP_INFO( this-get_logger(), Received: linear.x %.2f, angular.z %.2f, msg-linear.x, msg-angular.z ); }这里需要特别注意msg是一个指针所以访问字段时使用msg-linear.x msg-angular.z而不是msg.linear.x msg.angular.z3.4 完整订阅器示例代码订阅 /cmd_vel 并打印速度下面代码订阅/cmd_vel话题并打印收到的速度信息。#include rclcpp/rclcpp.hpp #include geometry_msgs/msg/twist.hpp class CmdVelSubscriber : public rclcpp::Node { public: CmdVelSubscriber() : Node(cmd_vel_subscriber) { sub_vel this-create_subscriptiongeometry_msgs::msg::Twist( /cmd_vel, 10, std::bind(CmdVelSubscriber::cmd_vel_callback, this, std::placeholders::_1) ); } private: void cmd_vel_callback(const geometry_msgs::msg::Twist::SharedPtr msg) { RCLCPP_INFO( this-get_logger(), Received: linear.x %.2f, angular.z %.2f, msg-linear.x, msg-angular.z ); } private: rclcpp::Subscriptiongeometry_msgs::msg::Twist::SharedPtr sub_vel; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto node std::make_sharedCmdVelSubscriber(); rclcpp::spin(node); rclcpp::shutdown(); return 0; }3.5 发布端和订阅端访问字段的区别1发布端通常使用点号发布端一般是普通对象geometry_msgs::msg::Twist vel_cmd;所以访问字段使用vel_cmd.linear.x vel_cmd.angular.z2订阅端通常使用箭头订阅端回调函数中收到的msg通常是智能指针const geometry_msgs::msg::Twist::SharedPtr msg所以访问字段使用msg-linear.x msg-angular.z可以简单记成普通对象用 .指针对象用 -对比如下场景写法说明发布端消息对象vel_cmd.linear.xvel_cmd是普通对象订阅端消息指针msg-linear.xmsg是指针