HORUS/blog

2026年7月11日 · ros2 · python · 入门

我用 8 行 Python 重写了我的 ROS 2 节点

一个 ROS 2 发布/订阅节点需要一个类、一个 __init__、一个 spin 循环,还有一个 launch 文件。同样的机器人在 HORUS 里只要八行——而且消息永远不用序列化。

如果你写过 ROS 2 的 Python 节点,你一定熟悉它的样子:继承 Node、调用 super().__init__('name')、创建 publisher、创建带回调的 subscriber,然后把自己交给 rclpy.spin。接着还要再写一个文件——launch 文件——才能真正把它跑起来。

下面是同一个「传感器→控制器」机器人,用 HORUS 的 Python API 写的完整代码:

import horus

def controller_tick(node):
    s = node.recv("sensor.data")
    if s is not None:
        node.send("motor.cmd", {"voltage": (1.0 - s["position"]) * 0.5})

horus.run(
    horus.Node(name="ctrl", subs=["sensor.data"], pubs=["motor.cmd"], tick=controller_tick, rate=1000),
)

没有类的样板代码,没有单独的 launch 文件,也不用 colcon buildhorus.run(...) 本身就是 launch 文件。而且节点以 1 kHz 运行——由 rate=1000 决定,而不是靠你手动调的 sleep

真正的区别不只是少打字

有意思的差别在底层。在 ROS 2 里,sensor.data 跨进程时要经过 DDS——每条消息都要序列化、 传输、再反序列化。默认 DDS 栈公开可查的参考值是 REP 2014 给出的小消息约 5 µs,负载更大、QoS 更严只会更高。在 HORUS 里,Python 检测节点和控制器共享同一块共享内存环形缓冲区。消息就是 类型的内存布局,根本没有序列化这一步。同一个话题在 ROS 2 里要花微秒级,在这里只需纳秒级

这也是为什么 Python 节点和 C++ 控制器可以订阅同一个话题而不需要任何胶水代码:三种语言 之所以在传输格式上达成一致,是因为传输格式就是那串字节本身。

什么时候该用,什么时候不该用

如果你在做原型、循环频率只有 10 Hz,那 ROS 2 的开销根本看不出来,而且它的生态极其庞大——就用 它。HORUS 真正发挥价值,是在时序真正较真的时候:1 kHz 的电机环、你想放进同一个进程的 「相机→推理→执行」流水线,或者一个绝不能错过截止时间的控制器。在那里,「消息层的延迟从微秒级降到 纳秒级,而且最坏情况可以被界定」就不再是跑分上的花哨数字,而是机器人能不能稳住控制环的分水岭。

如果你正从 ROS 2 迁移过来,迁移指南 把每一个概念——Node、Topic、Service、Action、tf2、QoS——都映射到了 HORUS 的对应物。

整个项目开源,采用 Apache-2.0 许可证。如果这个 8 行版本让你有点好奇,上面的代码就是仓库里的一个 真实示例。

Found this useful? Share it:Discuss on HNShare on X