ROS2を使っていると、「ノード間の通信、もうちょっと軽くならないかな……」と思う場面、多くないですか?
今回は、ROS2の通信を高速・軽量化するために試した Zenoh, 共有メモリ通信, Composition の3つの手法をざっくり紹介しつつ、実際にやってみた結果を共有します。
検証環境
項目 | 内容 |
---|---|
ROS2 バージョン | jazzy |
OS | Ubuntu 20.04 上の Ubuntu 22.04 Docker コンテナ |
rmw_zenoh_cpp | 0.2.3 |
rmw_dds_common | 3.1.0 |
rmw_cyclonedds_cpp | 2.2.2 |
rmw_fastrtps_shared_cpp | 8.4.1 |
Zenoh:軽量・分散・リアルタイムな通信プロトコル
Zenoh は、Pub/Sub・Query(リクエスト/リプライ)・データ保存までを1つでこなす通信ミドルウェア。IoTやロボットなどのリアルタイム・分散通信にピッタリの設計になっています。
特徴
特徴 | 説明 |
---|---|
Pub/Sub + Query対応 | 必要なデータだけオンデマンド取得も可能 |
超軽量・低レイテンシ | エッジ環境でもサクサク動く |
ピアツーピア通信 | ブローカー不要でスケーラブル |
ROS2対応 | Zenoh-Bridgeやrmw_zenohで統合可能 |
導入方法(2パターン)
① Zenoh Bridge for DDS
既存のROS2ノード(DDS)を変更せず、Bridge経由でZenoh通信が可能。段階的な導入に向いています。
cargo install zenohd # Rustが必要
cargo install zenoh-plugin-dds # binが無いと言われるので、buildが必要
sudo apt install build-essential clang
git clone https://github.com/eclipse-zenoh/zenoh.git
git clone https://github.com/eclipse-zenoh/zenoh-plugin-dds.git
cd zenoh
cargo build --release
cd ../zenoh-plugin-dds
cargo build --release
ただし、docker container環境では立ち上げできなかったので、深追いしてない。
② rmw_zenoh
DDSを完全にZenohに置き換える方法。軽量&高速が必要なローカル環境、またはマルチキャスト不可なネットワークで有効。
以下apt installだと、zenoh-c のbuildが必要になり、buildしても動かなかったので、sourceからbuildした。
sudo apt update
sudo apt install ros-jazzy-rmw-zenoh-cpp
$ ros2 run demo_nodes_cpp talker
/opt/ros/jazzy/lib/demo_nodes_cpp/talker: symbol lookup error: /opt/ros/jazzy/lib/librmw_zenoh_cpp.so: undefined symbol: zc_reply_keyexpr_default
[ros2run]: Process exited with failure 127
sourceからbuild
mkdir ~/ws_rmw_zenoh/src -p && cd ~/ws_rmw_zenoh/src
git clone https://github.com/ros2/rmw_zenoh.git -b jazzy
cd ~/ws_rmw_zenoh
rosdep install --from-paths src --ignore-src --rosdistro jazzy -y
source /opt/ros/jazzy/setup.bash
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release
source ~/ws_rmw_zenoh/install/setup.bash
動かし方
zenoh router起動
# terminal 1
ros2 run rmw_zenoh_cpp rmw_zenohd
# terminal 2
export RMW_IMPLEMENTATION=rmw_zenoh_cpp
ros2 run demo_nodes_cpp talker
# terminal 3
export RMW_IMPLEMENTATION=rmw_zenoh_cpp
ros2 run demo_nodes_cpp listener
共有メモリ通信:ゼロコピーの爆速IPC
共有メモリを使うと、複数のプロセスが同じメモリ領域を参照できるため、シリアライズ・コピー不要の超高速通信が可能になります。
実装例(SysV API)
ROS2ではないが、Linuxの実装を試してみると、以下の感じ。
DDSごとの対応状況
DDS実装 | 共有メモリ対応 | 備考 |
---|---|---|
Fast DDS | ✅ 独自SHM対応(XML設定) | |
Cyclone DDS | ✅ Iceoryx連携(RouDi必要) | |
RTI Connext | 一部対応(商用、Iceoryx非連携) |
fastDDSでの設定
<?xml version="1.0" encoding="UTF-8" ?>
<dds>
<profiles>
<!-- SHM トランスポート定義 -->
<transport_descriptors>
<transport_descriptor>
<transport_id>shm_transport</transport_id>
<type>SHM</type>
<segment_size>2536870912</segment_size>
</transport_descriptor>
</transport_descriptors>
<!-- Participant プロファイル -->
<participant profile_name="shm_profile" is_default_profile="true">
<rtps>
<userTransports>
<transport_id>shm_transport</transport_id>
</userTransports>
</rtps>
</participant>
</profiles>
</dds>
export FASTRTPS_DEFAULT_PROFILES_FILE=/path/fastdds_shm_profile.xml
cycloneDDSでの設定 / 立ち上げ
<?xml version="1.0" encoding="UTF-8" ?>
<CycloneDDS xmlns="https://cdds.io/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://cdds.io/config https://raw.githubusercontent.com/eclipse-cyclonedds/cyclonedds/iceoryx/etc/cyclonedds.xsd">
<Domain id="any">
<SharedMemory>
<Enable>true</Enable>
<LogLevel>info</LogLevel>
</SharedMemory>
</Domain>
</CycloneDDS>
export CYCLONEDDS_URI=/path/cyclonedds.xml
- iceoryx roudiの事前の立ち上げが必要
iox-roudi
- talker
export CYCLONEDDS_URI=file://$PWD/cyclonedds.xml
. ~/ros2_ws/install/local_setup.bash
RMW_IMPLEMENTATION=rmw_cyclonedds_cpp ros2 run demo_nodes_cpp talker
- listener
export CYCLONEDDS_URI=file://$PWD/cyclonedds.xml
. ~/ros2_ws/install/local_setup.bash
RMW_IMPLEMENTATION=rmw_cyclonedds_cpp ros2 run demo_nodes_cpp listener
おそらくcyclone DDSでの共有メモリは、さらに設定をしないと、大容量通信で使えず今回検証では使用していない。追って要調整。
Composition:ノードを1プロセスにまとめて効率化!
Compositionを使えば、複数のノードを同じプロセス内で動かせるため、起動時間短縮・通信最適化・メモリ削減が期待できます。
- 起動高速化(プロセス1つ)
- DDSのloan機能でゼロコピー通信も可能
- シンプルなノード統合が可能
ベンチマークしてみた!
環境
- CPU
- 11th Gen Intel(R) Core(TM) i7-1195G7 @ 2.90GHz
- mem
- 32GB
検証データ
100MBデータをPub/Sub (1Hz)
RMW実装 | CPU使用率 | 備考 |
---|---|---|
Fast DDS | pub 4〜10% / sub 3〜6% | |
CycloneDDS | pub 10%〜28% / sub 10〜20% | |
Zenoh | pub 10〜20% / sub 7〜12% | 200MB超でSubscribeできなくなった |
Fast DDS + SHM | pub 1〜6% / sub 3〜6% | |
Composition | pub/sub 3〜5% |
1GBデータをPub/Sub(1Hz)
RMW実装 | CPU | メモリ |
---|---|---|
Fast DDS | pub 40〜50% / sub 80% | pub 35% / sub 35% |
CycloneDDS | 通信不可 | - |
Zenoh | 通信不可 | - |
Fast DDS + SHM | pub 10〜30% / sub 50% | pub 11% / sub 17% |
Composition | pub/sub 30〜40% | pub/sub 14% |
100kHzで「Hello world」Pub/Subしたとき
RMW実装 | CPU使用率 |
---|---|
Fast DDS | pub/sub 60% |
CycloneDDS | pub/sub 50% |
Zenoh | pub 130% / sub 100%(落ちた) |
Fast DDS + SHM | pub/sub 60% |
Rviz2での表示可否
実装 | Rviz表示可? |
---|---|
Fast DDS | ✅ OK |
CycloneDDS | ✅ OK |
Zenoh | ✅ OK |
Fast DDS + SHM | ✅ OK |
まとめ
結論としては、
Composition > Fast DDS + SHM > Fast DDS ≒ CycloneDDS >> Zenoh
- Zenoh は現時点では大容量/高速通信に弱く、発展途上な印象でした。
- 共有メモリはシンプル&高速だけど、やはり普通にcompositionにしたほうが早いので、使いどころが難しい
- 軽量・高速通信が求められるロボットやIoT開発では、DDSだけでなくZenohや共有メモリの活用も重要です。
この記事が少しでもROS2のチューニングの参考になれば嬉しいです!