iT邦幫忙

2025 iThome 鐵人賽

DAY 29
0
Rust

30 天玩轉 Zenoh:Rust 助力物聯網、機器人與自駕的高速通訊系列 第 29

Day 29: Zenoh 在機器人系統的應用全景 Part 4 - ROS-Z:以原生 Zenoh 釋放 ROS 2 潛力

  • 分享至 

  • xImage
  •  

Zenoh 在機器人系統的應用全景 Part 4 - ROS-Z:以原生 Zenoh 釋放 ROS 2 潛力

消除中介軟體抽象層,為新一代機器人帶來前所未有的效能與安全

今天想來分享筆者最近在基於 Zenoh、以 Rust 開發的機器人框架:ros-z!


architecture

中介軟體的代價:我們默默接受的現實

ROS 2 社群一直強調中介軟體獨立是一大架構優勢,能讓團隊自由選擇 DDS 實作,針對不同需求調整,也保有供應商彈性。可是這份彈性背後,藏著一筆我們心知肚明的代價:效能負擔

在傳統的 ROS 2 系統裡,每則訊息都得穿過多層抽象:

Your Application
       ↓
ROS 2 Client Library (rclcpp/rclpy)
       ↓
RCL (ROS Client Library - C)
       ↓
RMW (ROS Middleware Interface)
       ↓
Middleware Implementation (Zenoh/DDS)
       ↓
Network Transport

每一層都帶來延遲、記憶體分配和潛在的低效率。各程式語言與中介軟體間的切換,還會增加編解碼成本。對於開發高頻控制、自主車輛或大型機器人隊伍的團隊來說,這些毫秒的累積和記憶體消耗,正是一種真實的瓶頸。

若是能完全剔除這些層呢?

ROS-Z 啟程:垂直整合的願景

ROS-Z 是 ZettaScale 在 垂直優化 的一次大膽嘗試,從零開始、用 Rust 直接基於 Zenoh 協定重寫 ROS 2,完全跳過中介軟體抽象層。

核心理念

ROS-Z 並不把 Zenoh 當成 ROS 2 RMW 層裡的又一選項,而是干脆 將 Zenoh 立為基礎,直接省略整個中間層:

Your Application
       ↓
ROS 2 Client Libraries (native or compatible)
       ↓
ROS-Z / RCL-Z (Rust implementation)
       ↓
Zenoh Core (Rust)
       ↓
Network Transport

結果是 少了兩層抽象,直接整合,能靠 Zenoh 原生功能帶來零翻譯負擔。

三條實作路徑,一個目標

ROS-Z 提供三種互補方案,打造原生 Zenoh ROS 2 體驗:

  1. ros-z - 純 Rust API,面向 Rust 原生機器人應用
  2. rcl-z - 替換 C 語言 RCL,讓現有 rclcpp/rclpy 應用跑在原生 Zenoh 堆疊上
  3. 完整互通 - 與所有基於 rmw_zenoh 的 ROS 2 節點無縫通訊

Rust 的優勢:安全與效能的結合

ROS-Z 最獨特之處在於採用 Rust,這語言專為機器人系統的挑戰量身打造——高效能、記憶體安全和無懼併發。

編譯時型別安全訊息傳遞

傳統 ROS 2 主要靠執行時型別檢查與手動記憶體管理。ROS-Z 利用 Rust 的型別系統,打造 編譯期保障

// Define a publisher with compile-time type safety
let zpub = node
    .create_pub::<Vector3D>("vector")
    .with_type_info(TypeInfo::new(
        "geometry_msgs::msg::dds_::Vector3_",
        TypeHash::from_rihs_string("RIHS01_cc12fe83...").unwrap(),
    ))
    .build()?;

// Publish with zero-copy potential
zpub.publish(&msg)?;

優點:

  • 執行期零型別錯誤,編譯期就驗證完畢
  • 無垃圾收集,記憶體安全有保障
  • 零成本抽象,在編譯期優化消除

靈活序列化架構

ROS-Z 建立 trait 制序列化介面,支援多種格式且維持型別安全:

pub trait ZMessage {
    type Serdes: ZSerializer + ZDeserializer;

    fn serialize(&self) -> Vec<u8> {
        Self::Serdes::serialize(self)
    }

    fn deserialize(input: &[u8]) -> Self {
        Self::Serdes::deserialize(input)
    }
}

// Automatically implements for CDR-compatible types
impl<T> ZMessage for T
where T: Serialize + Deserialize<'a> + 'a
{
    type Serdes = CdrSerdes<T>;
}

帶來的好處包括:

  • 與現有 ROS 2 訊息 CDR 格式相容
  • 可支援 Protobuf 高效格式
  • 可針對訊息類型自訂序列化策略
  • 適用時可用零拷貝反序列化

無懼併發

Rust 所有權模型幫助消除傳統機器人系統的併發錯誤:

pub struct ZPub<T: ZMessage> {
    sn: AtomicUsize,                    // Lock-free sequence numbers
    inner: zenoh::pubsub::Publisher<'static>,
    _lv_token: LivelinessToken,         // RAII resource management
    _phantom_data: PhantomData<T>,
}

impl<T: ZMessage> ZPub<T> {
    pub fn publish(&self, msg: &T) -> Result<()> {
        self.inner
            .put(msg.serialize())
            .attachment(self.new_attchment())
            .wait()
    }

    pub async fn async_publish(&self, msg: &T) -> Result<()> {
        self.inner.put(msg.serialize()).await
    }
}

重點:

  • &self 接收保證發佈天生線程安全
  • 原子操作避免鎖資源的序號成本
  • 原生 async/await 實現非阻塞併發發佈
  • RAII 令牌保證即使 panic 也能自動清理

零成本 FFI 保持 C/C++ 相容

RCL-Z 演示 Rust C FFI 功力,確保與既有 ROS 2 應用直接兼容:

// rcl-z/build.rs leverages bindgen and CXX
cxx_build::bridge("src/type_support.rs")
    .file("src/serde_bridge.cc")
    .include("include")
    .includes(include_dirs)
    .std("c++17")
    .compile("serde_bridge");

// Safe Rust implementation exposed as C API
#[no_mangle]
pub extern "C" fn rcl_publisher_init(
    publisher: *mut rcl_publisher_t,
    node: *const rcl_node_t,
    type_support: *const rosidl_message_type_support_t,
    topic_name: *const c_char,
    options: *const rcl_publisher_options_t,
) -> rcl_ret_t {
    rclz_try! {
        // Safe Rust implementation
        let pub_impl = node_impl.new_pub(type_support, topic_name, options)?;
        publisher.assign_impl(pub_impl)?;
        Result::Ok(())
    }
}

優勢:

  • 內部實現完整記憶體安全
  • FFI 呼叫無額外開銷
  • 可替換現有 rclcpp/rclpy

智慧指標與資源管理

ROS-Z 利用 Rust 所有權系統,自動處理資源管理:

pub struct ZNode {
    entity: NodeEntity,
    session: Arc<Session>,              // Reference-counted Zenoh session
    lv_token: LivelinessToken,          // Auto-cleanup on drop
    endpoint_counter: AtomicUsize,
}

impl ZNode {
    pub fn create_pub<T: ZMessage>(&self, topic: &str)
        -> ZPubBuilder<T>
    {
        let entity = EndpointEntity {
            id: self.endpoint_counter.fetch_add(1, AcqRel),
            node: self.entity.clone(),
            kind: EntityKind::Publisher,
            topic: topic.to_string(),
            type_info: None,
            qos: Default::default(),
        };

        ZPubBuilder {
            entity,
            session: Arc::clone(&self.session),
            _phantom_data: PhantomData,
        }
    }
}

ZNode 被丟棄時:

  • LivelinessToken 自動取消 Zenoh 中的註冊
  • Arc<Session> 引用自動遞減
  • 不需要手動清理,資源不會外洩

架構比較:ROS-Z、rmw_zenoh 以及 zenoh-plugin-ros2dds

為了解 ROS-Z 在 Zenoh-for-ROS-2 範疇的獨特優勢,我們比較三大實作:

方面 ROS-Z rmw_zenoh zenoh-plugin-ros2dds
層級 替代 RCL + RMW 實作 RMW 介面 DDS ↔ Zenoh 橋接
語言 Rust + C FFI C++ + Zenoh-C 綁定 Rust + DDS 整合
整合方式 原生 Zenoh 透過 RMW 抽象 DDS 到 Zenoh 協定橋
相容性 RCL API 兼容 (rcl-z) 完整 RMW 兼容 原有 DDS 節點完整支援
抽象層數 省略 2 層 標準 ROS 2 技術棧 橋接 + 既有堆疊
效能 潛力最高 高 (部份抽象開銷) 中等 (橋接 + DDS)

快速上手 ROS-Z

安裝

# Clone the repository
git clone https://github.com/ZettaScaleLabs/ros-z
cd ros-z

# Build with Cargo
cargo build --release

# For RCL-Z (C compatibility layer)
cd rcl-z
export AMENT_PREFIX_PATH=/opt/ros/rolling
cargo build --release -p rcl-z

你的第一個 ROS-Z 發佈者 (純 Rust)

use ros_z::{Builder, context::ZContextBuilder, ros_msg::Vector3D, entity::{TypeInfo, TypeHash}};

fn main() -> ros_z::Result<()> {
    // 建立 Zenoh context
    let ctx = ZContextBuilder::default().build()?;

    // 建立節點
    let node = ctx.create_node("my_node").build()?;

    // 建立發布者
    let pub = node
        .create_pub::<Vector3D>("vector")
        .with_type_info(TypeInfo::new(
            "geometry_msgs::msg::dds_::Vector3_",
            TypeHash::from_rihs_string("RIHS01_cc12fe...").unwrap(),
        ))
        .build()?;

    // 發佈循環
    loop {
        let msg = Vector3D { x: 1.0, y: 2.0, z: 3.0 };
        pub.publish(&msg)?;
        std::thread::sleep(std::time::Duration::from_secs(1));
    }
}

配合現有 ROS 2 應用用 RCL-Z

# 預先載入 RCL-Z 函式庫以取代現有 rclcpp 應用
LD_PRELOAD=/path/to/librcl_z.so ros2 run demo_nodes_cpp talker

# 現有程式便跑在原生 Zenoh 堆疊上!

與 rmw_zenoh 無縫互通

ROS-Z 節點能與任何 rmw_zenoh ROS 2 節點輕鬆通訊:

# 終端機 1:使用 rmw_zenoh 執行標準 ROS 2 訂閱者
export RMW_IMPLEMENTATION=rmw_zenoh_cpp
ros2 run demo_nodes_cpp listener

# 終端機 2:執行 ROS-Z 發佈者
cargo run --example z_pub

給 ROS 2 的啟示:垂直整合很重要

ROS-Z 的價值不僅是程式碼,更是一個 概念驗證,證明用現代協定如 Zenoh 垂直整合的可能性:

  1. 抽象層有明顯代價,有時值得,有時則不然
  2. 記憶體安全和效能不衝突,Rust 證明可兼得
  3. 原生整合啟用更多 RMW 層無法呈現的功能
  4. 互通性可行,多重實作能共存交流

一起加入探索吧

ROS-Z 是開源且歡迎大家貢獻:


上一篇
Day 28: Zenoh 在機器人系統的應用全景 Part 4 - 深入探討 rmw_zenoh,新一代的ROS 2 Middleware
下一篇
Day 30: 三十天的 Zenoh 旅程:從好奇到深入,我們學到了什麼?
系列文
30 天玩轉 Zenoh:Rust 助力物聯網、機器人與自駕的高速通訊30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言