ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

C++ 补充说明:ROS2 Subscriber 为什么常用智能指针接收消息?

2026/10/7 2:51:48 拓冰建站 浏览量
C++ 补充说明:ROS2 Subscriber 为什么常用智能指针接收消息? 在刚开始学习 ROS2 C 的 Publisher / Subscriber 时我一直有一个疑问[this](std_msgs::msg::Int32::ConstSharedPtr msg) { receiveNumber(msg); }这里为什么突然出现了一个ConstSharedPtrPublisher 明明发送的是消息为什么到了 Subscriber 之后却变成了一个“智能指针”这个智能指针是不是直接指向 Topic例如/number理解清楚之后我发现这里其实混合了三个完全不同的概念Topic、消息对象、智能指针。问题 1Publisher 发布的到底是什么例如 Publisher 中std_msgs::msg::Int32 msg; msg.data 5; publisher_-publish(msg);这里的msg是一个真正的Int32消息对象。可以简单理解为msg ┌──────────────┐ │ data 5 │ └──────────────┘然后publisher_-publish(msg);表示通过 Publisher 发布这条消息。需要特别注意Publisher 发布的并不是这个 C 对象本身的内存地址。不是把 msg 发给 Subscriber因为 Publisher 和 Subscriber 可能属于两个不同进程甚至可能运行在两台不同计算机上。一个进程中的内存地址对另一个进程通常没有直接意义。因此从概念上理解Publisher中的消息对象 data 5 ↓ ROS2 / DDS通信 ↓ 传递消息内容 ↓ Subscriber一侧得到对应消息问题 2那为什么 Subscriber 最后得到的是一个智能指针当消息到达 Subscriber 一侧以后ROS2 已经获得了一条接收到的消息对象。例如Subscriber侧 Int32消息对象 ┌──────────────┐ │ data 5 │ └──────────────┘接下来 ROS2 要调用我们提前注册好的 callback。如果每次调用 callback 都重新完整复制一次消息对象ROS2中的消息对象 ↓ 重新复制整个对象 ↓ callback中的新对象对于简单的Int32来说问题不大。但如果以后收到的是Camera Image PointCloud LaserScan 大型机器人状态消息消息可能非常大。因此 ROS2 C 中很常见的一种方式是不再为了进入 callback 而额外完整复制一个消息对象而是把指向已有消息对象的智能指针交给 callback。于是std_msgs::msg::Int32::ConstSharedPtr msg可以理解为msg │ ▼ Subscriber侧收到的Int32消息对象 ┌──────────────┐ │ data 5 │ └──────────────┘所以msg-data就是顺着msg这个智能指针 ↓ 找到Int32消息对象 ↓ 访问里面的data最终得到5问题 3智能指针是不是指向/numberTopic不是。这是最容易混淆的地方。msgstd_msgs::msg::Int32::ConstSharedPtr msg指向的是某一条具体收到的Int32消息对象。而/number是Topic 的名称。Topic 是在创建 Subscription 时确定的subscription_ this-create_subscriptionstd_msgs::msg::Int32( number, 10, callback );这里实际上定义了三个重要信息std_msgs::msg::Int32 → 我要接收什么类型 number → 我要监听哪个Topic callback → 消息到了以后执行什么因此更准确的关系是Subscription对象 │ ├── 监听Topic/number │ ├── 消息类型Int32 │ └── 保存callback而不是msg指针 ↓ /number问题 4为什么msg知道自己指向的是Int32因为它的类型已经写得非常明确std_msgs::msg::Int32::ConstSharedPtr msg可以从右往左理解msg → 一个智能指针 ConstSharedPtr → 指向只读对象的shared_ptr类型 Int32 → 被指向的对象是Int32消息因此msg-data编译器知道msg指向的是Int32对象因此这个对象具有data成员。问题 5为什么 Subscriber 能和 Publisher 匹配上不单单因为msg是Int32。真正重要的是 Publisher 和 Subscriber 在通信配置上能够对应。例如 Publisherthis-create_publisherstd_msgs::msg::Int32( number, 10 );Subscriberthis-create_subscriptionstd_msgs::msg::Int32( number, 10, callback );两边共同满足Topic名称 number 消息类型 Int32所以它们能够通过 ROS2 通信。可以简单理解为Publisher 我往 number 频道发送 Int32 Subscriber 我监听 number 频道里的 Int32于是可以匹配。问题 6Publisher 端的msg和 Subscriber 端的msg是同一个对象吗通常不要这样理解。Publisher 中std_msgs::msg::Int32 msg;这里的msg是 Publisher 一侧创建的对象。Subscriber 中std_msgs::msg::Int32::ConstSharedPtr msg这里的msg是 Subscriber callback 得到的智能指针。变量名虽然都叫msg但它们不是同一个变量。概念上应该理解Publisher侧消息对象 data 5 ↓ ROS2通信系统 ↓ Subscriber侧得到消息 data 5 ↑ │ msg 智能指针真正传递过去的是消息数据。不是 Publisher 中那个 C 指针地址。问题 7为什么这里用-而不是.Publisher 中std_msgs::msg::Int32 msg; msg.data 5;这里msg 对象本身所以用msg.dataSubscriber 中std_msgs::msg::Int32::ConstSharedPtr msg;这里msg 智能指针所以使用msg-data可以记成对象 . 指针 -问题 8为什么是ConstSharedPtr而不是普通SharedPtrSharedPtr的主要意义之一是多个地方可以安全共享对同一个消息对象的生命周期管理。而ConstSharedPtr又进一步表示callback 主要以只读方式访问这条消息。所以msg-data可以读取。设计意图是不随意修改收到的原始消息。问题 9智能指针是不是意味着 ROS2 全程零拷贝不能这样理解。使用ConstSharedPtr可以减少 callback 边界上不必要的完整对象复制并方便管理消息生命周期。但是 Publisher 到 Subscriber 的完整通信过程还可能涉及序列化 DDS传输 反序列化 进程间通信 进程内优化 零拷贝机制具体是否发生复制与通信方式和 ROS2 配置有关。因此更准确的说法是ConstSharedPtr让 callback 可以共享已有的接收消息对象而不必简单地每次都按值复制整个消息。问题 10[this]和msg又是什么关系Subscriber callback[this](std_msgs::msg::Int32::ConstSharedPtr msg) { receiveNumber(msg); }这里[this]和msg是两件完全不同的事。thisthis ↓ 当前NumberSubscriber对象它回答谁在处理消息而msgmsg ↓ 当前收到的Int32消息对象它回答处理什么消息所以可以记this 我是谁 msg 我收到了什么例如receiveNumber(msg);可以理解为this 找到当前NumberSubscriber对象 msg 提供当前收到的消息 ↓ 当前对象处理这条消息最后的完整数据流Publisherstd_msgs::msg::Int32 msg; msg.data 5; publisher_-publish(msg);概念流程Publisher端 msg.data 5 ↓ Publisher.publish(msg) ↓ ROS2 / DDS通信 ↓ /number Topic ↓ Subscription监听到消息 ↓ Subscriber callback变为可执行 ↓ Executor执行callback ↓ ROS2提供ConstSharedPtr msg ↓ msg指向Subscriber侧收到的Int32对象 ↓ msg-data ↓ 得到5因此最重要的四句话是Publisher 传递的是消息数据不是 Publisher 内存中的对象地址。Subscription 才负责监听/numberTopicmsg智能指针并不指向 Topic。Subscriber callback 中的msg指向的是这一次收到的消息对象。使用智能指针可以避免 callback 处不必要的大对象复制并帮助管理消息对象生命周期。理解了这四点ROS2 Subscriber 中的std_msgs::msg::Int32::ConstSharedPtr msg就不再是一段需要死记的语法而是有明确工程意义的设计。