ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

C++ 第 31A 课:先把 ROS2 Publisher 讲成人话

C++ 第 31A 课:先把 ROS2 Publisher 讲成人话 上一课答案第 1 题class Dog : public Animal其中Animal → 父类 Dog → 子类第 2 题class MyNode : public rclcpp::Node表示定义一个自己的类MyNode它继承 ROS2 的rclcpp::Node因此拥有 ROS2 节点的基础能力。第 3 题MyNode()是MyNode的构造函数。创建MyNode对象时会自动调用。第 4 题Node(my_node)表示调用父类Node的构造函数并把节点名称设置为my_node。第 5 题因为rclcpp::Node已经提供了 ROS2 节点所需的大量基础能力比如Publisher Subscriber Timer Parameter Logger Service我们继承它就不需要自己从零实现 ROS2 通信系统。上一课跳得太快了。问题不在 Publisher/Subscriber 本身而是我一口气把ROS2、模板 、::、智能指针、this-、lambda、消息类型全塞进了一段代码里。我们先不继续下一课把第 31 课拆成几节微课。今天只搞懂Publisher 到底是什么以及一条 ROS2 发布代码怎么读。第 31A 课先把 ROS2 Publisher 讲成人话先完全忘掉这句rclcpp::Publisherstd_msgs::msg::Int32::SharedPtr publisher_;现在不看它。你先只理解一个现实场景。假设我们的机械臂程序里有两个程序程序 AVLA 程序 B机械臂控制器VLA 算出了“机械臂第 1 轴转到 30°”问题是程序 A 怎么把这个消息告诉程序 BROS2 提供了一套通信系统。最简单可以想象成微信VLA程序 │ │ 发消息 ▼ 【机器人动作群】 │ │ 收消息 ▼ 机械臂控制程序这里发消息的人 Publisher 微信群 Topic 收消息的人 Subscriber 发送的内容 Message今天我们只学发消息这一半Publisher ↓ Topic ↓ Message1. 如果不用 ROS2最简单的“发消息”应该长什么样假设我自己设计一个超级简单的程序Publisher publisher;意思创建一个发布器。然后publisher.publish(100);意思发布数字 100。就这么简单。你先把 ROS2 Publisher 的本质理解成publisher.publish(消息);也就是让发布器把一条消息发出去。2. ROS2 为什么看起来复杂因为 ROS2 还必须知道两件事第一你发到哪个频道第二你发送的是什么类型的数据比如我们说频道number 数据类型整数所以概念上相当于创建发布器 我要发送 整数 发送到 number频道真实 ROS2 才写成create_publisherstd_msgs::msg::Int32( number, 10 );别急着整句理解。我们开始拆。3. 先看number这个最简单number只是一个字符串。你已经学过std::string name Robot;所以number没什么特殊的。在这里它表示Topic 的名字。也就是频道名称。可以想成微信群名字 number所以number≈我要往 number 这个频道发东西4. Topic 到底是什么Topic 不是什么神秘对象。你现在先把它理解成ROS2 中用名字标识的消息频道。比如机器人里可能有/joint_states /camera/image /vla_action /cmd_vel分别可以理解成不同频道。例如/joint_states里面专门传机器人关节状态。而/vla_action里面以后可以传VLA 产生的机器人动作。5. 再看这一坨std_msgs::msg::Int32第一次看肯定很烦。我们拆成std_msgs :: msg :: Int32你以前学过std::cout其中::可以先理解成“里面的”所以std::cout可以粗略读std里面的cout一样的std_msgs::msg::Int32可以一层层读std_msgs 里面的 msg 里面的 Int32你现在不用关心为什么要分这么多层。只需要知道最终的Int32表示一种 ROS2 整数消息。6. 为什么不能直接发普通int你可能会问我已经有int x 100;为什么 ROS2 还要搞std_msgs::msg::Int32因为 ROS2 在不同程序、甚至不同电脑之间传数据。它需要一种大家都认识的统一“快递包装”。普通int x 100;可以理解成桌子上的数字 100。而std_msgs::msg::Int32 msg;可以理解成一个符合 ROS2 快递标准的盒子。这个盒子里面有msg.data于是msg.data 100;就是┌─────────────┐ │ ROS2消息盒子 │ │ │ │ data 100 │ └─────────────┘这样 ROS2 才方便把整个盒子送出去。7. 所以这两句一定要理解std_msgs::msg::Int32 msg;意思创建一个 ROS2 整数消息对象名字叫msg。然后msg.data 100;意思把数字100放进这个消息对象的data成员。是不是一下简单很多其实和你学过的 struct 很像。完全可以把它暂时想象成struct Int32 { int data; };然后Int32 msg; msg.data 100;ROS2 的Int32消息你现在就可以这样理解。8. 再看 我们之前学过std::vectordouble什么意思你已经知道一个装double的 vector。这里同样create_publisherstd_msgs::msg::Int32你可以理解成创建一个专门发送 Int32 消息的 Publisher。所以...暂时就翻译成“我要处理什么类型的数据”比如std::vectordouble→ 装double而create_publisherInt32→ 发布Int32先到这个深度就够。9. 现在重新看这半句create_publisherstd_msgs::msg::Int32( number, 10 );现在你应该至少能翻译出创建一个 Publisher它专门发送Int32整数消息发送到number这个 Topic。至于10今天甚至可以先不管。暂时把它记成ROS2 通信参数目前先默认写 10。后面我们专门学 QoS 时再解释。10. 那this-又来了完整一点this-create_publisherstd_msgs::msg::Int32( number, 10 );还记得this是什么意思吗就是当前这个对象自己。现在我们的类是class MyNode : public rclcpp::Node所以当前对象就是当前这个 ROS2 节点。那么this-create_publisher(...)就可以翻译成让当前这个 ROS2 节点创建一个 Publisher。你甚至可以把this-脑子里先翻译成“我这个节点的……”所以this-create_publisher(...)≈“我这个节点创建发布器。”11. 为什么这里是-因为this是一个指针。我们前面已经学过普通对象 . 指针 -所以this-create_publisher(...)用的是-因为this是“当前对象的地址”。可以画成this │ │ 地址 ▼ 当前 MyNode 对象 │ └── create_publisher()所以this-create_publisher()就是顺着this找到当前 Node 对象然后调用它的create_publisher()。12. 我们现在已经把右边读完了this-create_publisherstd_msgs::msg::Int32( number, 10 );整句话当前 ROS2 节点创建一个发布器这个发布器发送Int32整数消息使用numberTopic。很好。先到这里。13. 创建出来以后怎么办我们需要把这个 Publisher 保存下来。就像int x 5;右边5产生一个值。左边x保存它。一样publisher_ this-create_publisher...();左边publisher_就是保存创建好的 Publisher。所以你可以暂时把整句理解成publisher_ 我刚刚创建的发布器14.publisher_之后怎么用比如已经有消息std_msgs::msg::Int32 msg; msg.data 100;然后publisher_-publish(msg);又出现-。为什么因为publisher_是一个智能指针。所以publisher_ │ ▼ Publisher对象 │ └── publish()于是publisher_-publish(msg);翻译找到 Publisher 对象调用它的publish()把msg发出去。最终msg.data 100 ↓ publisher_-publish(msg) ↓ number Topic ↓ 100 被发送出去15. 你今天只需要真正看懂这四步先别管整份 ROS2 代码。只看std_msgs::msg::Int32 msg;创建一个整数消息盒子。然后msg.data 100;往盒子里放 100。然后 Publisher 已经创建好了publisher_最后publisher_-publish(msg);让 Publisher 把这个盒子发出去。整个逻辑就是创建盒子 ↓ 装入100 ↓ 找到发布器 ↓ 发送盒子16. 和 VLA 联系起来其实也一样现在我们发的是100以后 VLA 发的是机械臂动作例如概念上VLA输出 Joint1 30° Joint2 15° ...依旧是创建ROS2消息 ↓ 把VLA动作装进去 ↓ Publisher发布 ↓ Topic ↓ 机器人控制节点收到只是消息盒子变复杂而已。17. 现在先别学 Subscriber上一课的问题就是Publisher 还没吃透我马上又加 Subscriber lambda SharedPtr。所以我们现在把课程拆开31APublisher 是什么——现在这节31BPublisher 的完整最小程序31CSubscriber 是什么31DPublisher → Topic → Subscriber 完整链路等这四小节真正搞懂我们再继续spin()。先检查你有没有抓住核心你现在只需要能回答下面 4 个很小的问题std_msgs::msg::Int32 msg;是什么意思msg.data 100;是什么意思this-create_publisher...()大概是什么意思publisher_-publish(msg);大概是什么意思
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进