ARTICLE DETAIL

资讯详情

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

C++ 第 33 课:事件循环——spin() 到底在干什么

C++ 第 33 课:事件循环——spin() 到底在干什么 上一课标准答案MyNode是C 类不是运行时对象。真正的对象要等你写auto node std::make_sharedMyNode();之后才创建出来。因为MyNode继承了rclcpp::Node这个对象同时具备 ROS2 Node 的能力。auto node std::make_sharedMyNode();这里的node是一个std::shared_ptrMyNode也就是管理MyNode对象的智能指针。可以画成node │ ▼ MyNode对象 ├── Node父类部分 └── MyNode自己的部分rclcpp::init(argc, argv);是【ROS2 新知识】作用是初始化 ROS2 C 运行环境让后面的 Node、Publisher、Subscriber 等 ROS2 功能可以工作。rclcpp::spin(node);不能理解成“什么也不干地卡住”。更准确地说让 ROS2 持续处理这个 Node 的事件。包括消息、Timer、Service、Action 等事件。如果没有spin()程序创建完节点后通常会一路执行到结束Subscriber 没有持续处理事件的机会所以即使消息到了callback 也可能根本不会被执行。第 33 课事件循环——spin()到底在干什么这节非常关键因为它决定你是否真正理解 ROS2 的运行方式。先把两个概念分开【旧知识】 普通C代码通常顺序执行 【ROS2新知识】 ROS2大量采用“事件驱动”1. 普通 C 是什么感觉例如int main() { std::cout 1 std::endl; std::cout 2 std::endl; std::cout 3 std::endl; return 0; }执行顺序1 ↓ 2 ↓ 3 ↓ 程序结束非常明确我写哪一句CPU 就按顺序执行哪一句。2. 但机器人程序不能只按一次顺序跑完假设一个机械臂控制节点需要等待VLA动作 关节状态 相机数据 Timer 急停消息问题是这些东西什么时候来不知道。例如0.2秒后可能有JointState 1秒后可能有相机图像 1.3秒后VLA才输出动作所以程序不能这样getVLAAction(); getJointState(); getCameraImage();因为你不知道这些数据什么时候准备好。ROS2 更适合我先注册好“事件来了该怎么办”然后持续等待。3. 什么叫“事件”事件可以非常简单地理解成某件值得程序处理的事情发生了。例如新Topic消息到达 Timer到时间 Service请求到达 Action请求到达这些都叫事件。4. callback 是“事件发生后的处理办法”例如我们提前写void callback(const Msg::SharedPtr msg) { std::cout msg-data; }意思只是如果消息来了我想这么处理。注意定义 callback ≠ callback 正在执行。这和你定义普通函数一样void test() { }只有被调用时它才执行。5. 那到底谁调用 callback这正是rclcpp::spin(node);背后的核心机制之一。可以粗略理解为spin开始 ↓ 检查有没有事件 ↓ 没有 ↓ 继续等待 ↓ 有事件 ↓ 找到对应callback ↓ 执行callback ↓ 执行完 ↓ 继续等待下一个事件这就是事件循环 Event Loop6. 你可以把spin()想成前台值班员假设一个 Node 是机器人控制中心。它已经登记了收到JointState → 调 jointCallback 收到VLA Action → 调 actionCallback Timer到时间 → 调 timerCallback然后rclcpp::spin(node);相当于“从现在开始值班有事情发生就按登记表处理。”于是spin │ ├─ /joint_states来了 │ ↓ │ jointCallback() │ ├─ /vla_action来了 │ ↓ │ actionCallback() │ ├─ Timer到时间 │ ↓ │ timerCallback() │ └─ 继续值班……7. 为什么 callback 看起来是“自动执行”严格来说不是 callback 有魔法。真正发生的是你提前把callback注册给ROS2 ↓ spin持续等待事件 ↓ 事件发生 ↓ ROS2执行器发现事件 ↓ 调用你之前注册的callback所以callback 并不是“自己执行”而是 ROS2 在事件发生后替你调用。这个理解非常重要。8. 把 Subscriber 放进来假设Subscriber监听 /number注册 callbackvoid numberCallback( const std_msgs::msg::Int32::SharedPtr msg) { std::cout msg-data; }然后rclcpp::spin(node);程序进入持续事件处理状态。此时 Publisher 发data 100整个过程Publisher ↓ /number ↓ 新消息进入ROS2 ↓ spin正在处理事件 ↓ 发现Subscriber有新消息 ↓ 调用numberCallback(msg) ↓ msg-data ↓ 100 ↓ callback结束 ↓ spin继续等待这就是 ROS2 Topic 通信真正“跑起来”的方式。9.spin()为什么通常一直不返回看rclcpp::spin(node); rclcpp::shutdown();一般运行 ROS2 节点时你会发现程序长期停在spin(node);不是程序坏了。而是它正在持续值班。正常情况下可能一直等待 处理 等待 处理 ……直到你终止节点例如Ctrl C然后spin()才退出再往下rclcpp::shutdown();10.spin()和while(true)有点像但不要完全等同你以前学过while (true) { }它会一直循环。所以从直觉上rclcpp::spin(node);确实有一点像while (ROS2还在运行) { 等待事件; 处理事件; }但 ROS2 的真实内部机制比这个复杂。现阶段这样理解足够spin() ROS2 的持续事件处理循环。11. Node、Subscriber、callback、spin 各负责什么这四个必须分清概念作用NodeROS2 中工作的程序单元Subscriber监听某个 Topiccallback消息来了以后具体怎么处理spin持续等待并调度这些事件所以不是Subscriber一直自己跑而是更接近Node里面有Subscriber ↓ spin让Node持续处理事件 ↓ Subscriber消息到达 ↓ callback执行12. VLA 场景下一下就很清楚了以后你的 ROS2 控制节点可能VLAController Node │ ├── Subscriber/vla_action │ ↓ │ actionCallback() │ ├── Subscriber/joint_states │ ↓ │ jointStateCallback() │ └── Publisher/joint_command程序rclcpp::spin(node);以后JointState 来了/joint_states ↓ jointStateCallback() ↓ 更新当前机械臂状态VLA Action 来了/vla_action ↓ actionCallback() ↓ 读取目标动作 ↓ 发布机器人控制命令然后继续spin所以这是一个长期在线的机器人程序。13. 为什么机器人软件特别适合这种结构因为机器人世界本来就是异步的相机不会等VLA VLA不会精确每10ms输出一次 机械臂反馈不断到来 传感器各有自己的频率如果所有东西都强行顺序执行等相机 ↓ 等VLA ↓ 等机械臂 ↓ 再等相机程序会非常笨重。ROS2 用事件机制可以让谁的数据来了 ↓ 就处理谁这也是你后面理解整个机器人软件架构的基础。14. 再看完整main()int main(int argc, char** argv) { rclcpp::init(argc, argv); auto node std::make_sharedMyNode(); rclcpp::spin(node); rclcpp::shutdown(); return 0; }现在每一行都可以翻译main() 程序入口 ↓ rclcpp::init(...) 初始化ROS2环境 ↓ make_sharedMyNode() 创建MyNode节点对象 ↓ spin(node) 让节点持续等待并处理ROS2事件 ↓ shutdown() 退出ROS2 ↓ return 0 程序正常结束这就是一个 ROS2 节点程序最基本的生命周期。15. 一个容易混淆的问题Publisher 也需要 spin 吗分情况。如果 Publisher 只是创建 ↓ 发一次 ↓ 结束理论上不一定非要长期 spin。但是典型 ROS2 节点通常还会有Timer Subscriber Service Action这些都依赖持续事件处理。例如“每秒发布一次”的 Publisher通常会使用Timer ↓ spin ↓ Timer到时间 ↓ callback ↓ publish所以实际 ROS2 程序里spin(node);非常常见。本课压缩成一句话spin(node)让 ROS2 节点进入持续事件处理状态消息、Timer、Service 等事件发生后ROS2 调用对应 callback处理完后继续等待。本课小练习1callback 为什么不是自己“自动运行”的2spin(node)最核心的作用是什么3消息处理完成以后spin()会直接结束程序吗4下面流程补全Publisher ↓ Topic ↓ Subscriber ↓ ______ ↓ 处理消息5VLA Controller 同时订阅/vla_action /joint_states如果/joint_states先来了ROS2 应该优先发生什么
返回列表