ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C++ 第 35 课:Service——“我问你,你回答我”

C++ 第 35 课:Service——“我问你,你回答我” 两条不同的“因果链”Publisher → Topic → Subscriber → callback → 处理消息描述的是一条消息怎么从发送端走到接收端。而spin / timer描述的是ROS2 节点什么时候去处理事件、什么事件会触发 callback。把这两层分开就清楚了。一、先看 Publisher → Topic → Subscriber 这条链假设 Publisher 发布msg.data 100; publisher_-publish(msg);数据流是Publisher │ │ 发送 data100 ▼ Topic: /number │ ▼ Subscriber到这里意思只是Subscriber 对应的 Topic 上有一条新消息可以处理了。但还差一步谁真正让 callback 执行这时候才轮到spin()。二、spin()像“事件处理总管”执行rclcpp::spin(node);之后可以粗略理解成一直检查这个 Node 有没有待处理事件例如 Node 里可能同时有Subscriber Timer Service Actionspin()大概做有没有Subscriber消息 有没有Timer到期 有没有Service请求 有没有其他事件如果发现/number 有新消息它就去调用这个 Subscriber 对应的 callback所以完整链应该是Publisher ↓ Topic ↓ Subscriber收到新消息 ↓ 消息成为“待处理事件” ↓ spin发现这个事件 ↓ 调用Subscriber callback ↓ 处理消息 ↓ callback结束 ↓ 回到spin继续等待这个版本更完整。三、所以spin()不在 Publisher 和 Topic 中间不要理解成Publisher ↓ spin ↓ Topic不对。spin()主要是在接收端/节点内部负责调度事件。可以画成Publisher │ ▼ Topic │ ▼ Subscriber │ │ 新消息等待处理 ▼ ┌─────────────────┐ │ spin │ │ 发现Subscriber事件│ └────────┬────────┘ ▼ callback ▼ 处理消息四、那 Timer 又在哪里Timer 和 Topic 没有直接上下级关系。Timer 是另外一种能够产生 callback 的事件源。Subscriber 的触发来源是消息到了Timer 的触发来源是时间到了所以一个 Node 可能这样Node │ spin │ ┌─────────┴─────────┐ │ │ Subscriber有消息 Timer到时间 │ │ ▼ ▼ subscriberCallback timerCallback这张图建议你记住。五、举一个具体例子假设一个 ROS2 节点同时有Subscriber 监听 /vla_action Timer 每100ms执行一次程序进入rclcpp::spin(node);然后真实运行可能是0 ms ↓ spin等待 50 ms ↓ /vla_action来了一条消息 ↓ spin发现Subscriber事件 ↓ actionCallback() ↓ 处理VLA动作 ↓ 回到spin 100 ms ↓ Timer到期 ↓ spin发现Timer事件 ↓ timerCallback() ↓ 发布机器人状态 ↓ 回到spin 180 ms ↓ 又来一条VLA Action ↓ actionCallback() 200 ms ↓ Timer再次到期 ↓ timerCallback()所以spin()是总的事件调度循环Subscriber 和 Timer 都是在给spin()提供“待处理事件”。六、把三者关系压缩成一句话Subscriber消息来了 → 产生事件 → spin调callbackTimer时间到了 → 产生事件 → spin调callbackspin持续等待和处理这些事件这就是三者的关系。七、再看 Publisher它又是什么角色Publisher 通常是callback 执行以后可能使用的一个“发消息工具”。例如 Timer callback[this]() { publishNumber(); }然后void publishNumber() { std_msgs::msg::Int32 msg; msg.data 100; publisher_-publish(msg); }完整链spin ↓ Timer到时间 ↓ timer callback ↓ publishNumber() ↓ Publisher发消息 ↓ Topic ↓ 另一个Node的Subscriber ↓ 另一个Node的spin发现事件 ↓ Subscriber callback ↓ 处理消息现在你应该能看出一个节点里的 Timer callback完全可以触发 Publisher然后这个 Publisher 又让另一个节点产生 Subscriber 事件。这就是 ROS2 节点之间真正“动起来”的样子。八、VLA 场景再串一次以后非常可能是VLA Node │ │ 模型推理完成 │ ▼ Publisher │ ▼ /vla_action │ ▼ RobotController Node 的 Subscriber │ ▼ 这个Node的spin发现新消息 │ ▼ actionCallback() │ ▼ 更新机械臂目标同时 RobotController 还有Timer每20ms ↓ spin发现Timer事件 ↓ controlCallback() ↓ 把最新目标发给机械臂于是VLA Action 只是“更新目标” Timer 负责“周期执行控制”这个结构以后非常常见。上一课答案Timer 和 Subscriber 的触发条件Timer → 时间到了 Subscriber → 消息到了std::chrono::seconds(1)表示1 秒的时间长度。在 Timer 中就是周期为 1 秒。[this]() { publishNumber(); }是Timer 到期后执行的 callback。[this]允许 lambda 使用当前 Node 对象的成员比如publishNumber()。10 ms1秒 1000ms 1000 / 10 100理想情况下约100 Hz也就是每秒执行 100 次。补全spin ↓ Timer到时间 ↓ Timer callback ↓ publishNumber() ↓ Publisher发消息第 35 课Service——“我问你你回答我”到现在我们学的 Topic 是Publisher → 不断发消息 Subscriber → 监听消息这是单向、持续的数据流。但有些事情不是“广播消息”而是我要明确问你一个问题然后等你给我答案。例如机械臂客户端 “机械臂准备好了吗” 服务端 “准备好了。”这种通信更适合Service1. Topic 和 Service 的直觉区别Topic 像电台广播。Publisher“当前关节角度30°” “当前关节角度31°” “当前关节角度32°”一直发。Subscriber谁想听谁听。Service 更像打客服电话。你主动“请帮我查询一次机器人状态。”对方处理后明确返回“当前状态Ready。”也就是Request ↓ 处理 ↓ Response2. Service 里有两个角色Server服务提供者我能处理某种请求。例如Robot Server 可以处理“是否Ready”的查询Client请求方我需要调用这个服务。例如VLA节点 询问机器人是否Ready所以Client ↓ Request Server ↓ Response Client3. 和 Topic 对比TopicPublisher ↓ Topic ↓ SubscriberServiceClient ↓ Request Service Server ↓ Response Client最大的不同Topic 没有天然的“一问一答”Service 天生就是请求—响应。4. 一个最简单的例子假设请求a 3 b 5Service Server 负责a b返回8流程Client 发送 Request a3, b5 ↓ Server callback ↓ 计算 35 ↓ Response sum8 ↓ Client收到结果5. Server 仍然使用 callback注意Service 仍然没有抛弃我们前面学的事件机制。Server 通常会注册一个Service callback然后Client请求到达 ↓ 产生Service事件 ↓ spin发现事件 ↓ 执行Service callback ↓ 填Response所以你会发现spin callback是 ROS2 很统一的核心思维。6. 和 Subscriber 的区别Subscriber callback收到消息 ↓ 处理消息Service callback收到请求 ↓ 处理请求 ↓ 还要给对方准备一个ResponseService 多了一步回答。7. VLA/机器人什么时候适合 Service例如很适合 Service“机械臂是否初始化完成” “请执行一次复位。” “请获取一次设备状态。” “请清除错误。”因为这些像一次请求 → 一个明确结果。不适合 Service例如相机连续图像 JointState连续反馈 VLA不断输出动作这些是连续数据流更适合Topic。8. 一个简单判断法问自己“我是想持续接收数据还是想主动问一次并得到回答”持续数据Topic一问一答Service9. Service 和 spin 的关系完整流程rclcpp::spin(node) ↓ 持续等待事件 ↓ Client发来Request ↓ Service事件出现 ↓ spin发现 ↓ 调用Service callback ↓ 生成Response ↓ 返回给Client ↓ 继续spin所以Service 同样依赖事件循环。10. 一个 ROS2 Node 可以同时拥有RobotController Node │ ├── Subscriber │ /vla_action │ ├── Publisher │ /joint_command │ ├── Timer │ 每20ms控制 │ └── Service /reset_robot也就是一个 Node 不是只能干一种事。这点以后一定要习惯。本课核心对比机制典型用途触发Topic连续数据流消息到达Timer周期任务时间到达Service一次请求/响应请求到达spin统一等待、调度这些事件持续运行本课小练习/joint_states连续发布关节状态更适合 Topic 还是 Service“机械臂是否已经初始化完成”更适合 Topic 还是 ServiceService 中两个角色分别叫什么Service Server 的 callback 一般比 Subscriber callback 多做什么补全Client ↓ ______ ↓ Server callback ↓ ______ ↓ Client
返回列表