はじめに

ROSCon 2026 が終わった直後の 9月下旬、ロボット用ミドルウェア ROS 2 のコミュニティ掲示板(ROS Discourse)に、手持ちの機材で試せる 2 つの道具が相次いで出ました。

  • shared_buffer_backend(9/26)— カメラ画像のような大きなデータを同じマシンの共有メモリに置き、別プロセスの C++/Python ノードへ、中身をコピーせずに渡す実験的なバックエンド
  • Linorobot2 Cockpit(9/28)— Raspberry Pi Pico 2 W や ESP32 を USB で挿し、ブラウザのボタン 1 つで micro-ROS のファームを書き込み、SLAM と Nav2 まで立ち上げるロボット用の管理画面。テスター募集中
  • NoDL(9/29)— ノードの入出力を YAML で宣言しておく「ノード定義言語」。ROSCon の講演の振り返りとして紹介された

この記事のゴールは、手持ちのマイコン基板のセンサを ROS 2 のトピックに乗せ、PC 側では画像をコピーせずに処理するという構成を、仕組みから描けるようになることです。そのために、ROS 2 の通信の基礎、ゼロコピーの意味、マイコンを ROS 2 に繋ぐ micro-ROS の構成を順に見てから、2 つの道具を当てはめます。

📌 この記事の3行まとめ
  • ROS 2 のノードはトピックでデータをやり取りし、別プロセスの間では DDS がデータを運ぶ。 画像のように大きいデータほど、運ぶためのコピーが重くなる
  • shared_buffer_backend は、画像の中身を共有メモリに残し、小さな「引換券」だけを DDS で送る。 ROS 2 Lyrical の rosidl::Buffer の仕組みに乗り、Python の受信側でも NumPy でそのまま読める
  • Linorobot2 Cockpit は、マイコンに micro-ROS を入れてロボットとして立ち上げるまでをブラウザにまとめた。 Pico 2 W・Pico W・ESP32・ESP32-S3 に対応する。基板なしの仮想マイコンでも、SLAM・Nav2・自律探索(106 秒で 62.3 m² を走査して原点に帰還)まで動いた。個人のプロジェクトで、まだリリース候補の段階

🧭 1. ROS 2 の通信の基礎 — ノード・トピック・DDS

ノードとトピック

ROS 2 のプログラムは、ノードという小さな部品の集まりです。カメラから画像を取るノード、画像から物体を見つけるノード、モーターを回すノード、というように役割ごとに分けます。ノード同士はトピックという名前付きの通り道にデータ(メッセージ)を流してつながります。送る側を publisher、受ける側を subscriber と呼びます。

flowchart TD CAM["カメラノード(publisher)"] -->|"/image_raw"| INF["推論ノード(subscriber・Python)"] CAM -->|"/image_raw"| REC["記録ノード(subscriber)"] INF -->|"/detections"| CTRL["制御ノード"] CTRL -->|"/cmd_vel"| MOTOR["モーターノード(マイコン)"] classDef mcu fill:#fde68a,stroke:#b45309,color:#1f2937 class MOTOR mcu

1 つのトピックに複数の受け手がいてもかまいません。送る側は、誰が受け取るかを知らずに流すだけです。この「疎結合」のおかげで、ノードを差し替えたり、別のマシンに移したりしやすくなります。

DDS:トピックを運ぶ下の層

トピックの中身を実際に運ぶのは、ROS 2 の下で動くミドルウェアです。標準は DDS(Data Distribution Service)という産業用の通信規格で、Fast DDS などの実装を RMW(ROS ミドルウェアインターフェース)という差し替え口から選びます。DDS は相手を自動で見つけ、メッセージを直列化(バイト列に変換)してから、ネットワークや共有メモリを通して届けます。

プロセス内通信とプロセス間通信

ノードをどう OS のプロセスに配置するかで、データの運ばれ方が変わります。

配置 データの運ばれ方
同じプロセス(プロセス内通信) C++ の std::unique_ptr で所有権を渡せば、コピーなしで受け手に届く
別のプロセス(プロセス間通信) DDS が直列化して運び、受け手側で元に戻す

ROS 2 の公式ドキュメントは、プロセス内通信で std::unique_ptr を使って送受信するとゼロコピーで届くと説明しています。ただし、同じプロセスに入れられるのは基本的に C++ のノードです。推論を Python で書きたいとき、あるいは 1 つのノードが落ちても全体を巻き込まないようにプロセスを分けたいときには、この方法が使えません。


🔁 2. ゼロコピーとは・shared_buffer_backend が何をするか

ゼロコピー=中身を運ばず、置き場所を共有する

ゼロコピーとは、データの中身を送り手から受け手へ写さずに、同じメモリを両者が読むことで受け渡すやり方です。640×480 のカラー画像は 1 枚で約 0.9 MB、30 fps なら毎秒 27 MB を超えます。これを毎回直列化して運び、受け手の数だけ写すと、CPU とメモリ帯域が通信に食われていきます。

ROS 2 にはもともと、コピーを減らす仕組みが複数あります。どれも使える場面に条件があります。

仕組み 効く範囲 条件(一次資料の記述)
プロセス内通信 同じプロセスの中 std::unique_ptr で所有権を渡す
Loaned messages プロセス間(RMW が確保したメモリ) ミドルウェアが配置できる型に限られる
Fast DDS の Data-sharing プロセス間 型が有界(bounded)であること。完全なゼロコピーには plain な型も必要
shared_buffer_backend 同じホスト・同じユーザーのプロセス間 ROS 2 Lyrical の rosidl::Buffer・可変長の uint8[] などの配列

画像のメッセージ sensor_msgs/msg/Image は、画素データが可変長の配列です。そのため「有界の型」が必要な仕組みには素直に乗りません。shared_buffer_backend は、この可変長の配列そのものを共有メモリに置く、という別の場所から攻めています。

土台:ROS 2 Lyrical の rosidl::Buffer

2026年5月に出た長期サポート版 ROS 2 Lyrical Luth(2031年5月までサポート)では、C++ のメッセージで uint8[] などの可変長配列を表す型が std::vector から rosidl::Buffer に変わりました。見た目は std::vector と同じように使えますが、中身の置き場所を差し替え可能なバックエンドに任せられます。CPU のメモリ、GPU のメモリ、あるいはベンダーが用意した別の領域です。公式のリリースノートは、GPU で作ったデータを CPU に写して送り、受け手でまた GPU に戻す無駄をなくすための仕組みと説明しています。なお、Lyrical の時点でこの機能を使えるのは rmw_fastrtps_cpp(Fast DDS)の publisher と subscriber だけで、Zenoh への対応は進行中です。

shared_buffer_backend の動き

shared_buffer_backend は、この rosidl::Buffer のバックエンドとしてCPU の共有メモリを提供します。開発者は以前 memfd という名前で構想を掲示板に書いており、今回改名して公開しました。

flowchart TD PUB["publisher プロセス"] -->|"① 画素を書く"| SHM[("共有メモリ(Linux は memfd)")] PUB -->|"② 引換券だけを DDS で送る"| SUB["subscriber プロセス(C++/Python)"] SUB -.->|"③ 同じ領域を map して読む"| SHM
  1. publisher は画像の画素を共有メモリに確保した領域へ書く(Linux は名前のない memfd、Windows はセッション内の名前付きファイルマッピング)
  2. DDS のメッセージには、幅・高さ・エンコードといった通常のフィールドと、プロセス ID・ブロック ID・サイズなどを書いた小さな記述子(descriptor)だけを載せる
  3. subscriber は記述子を受け取り、同じ領域を自分のアドレス空間に map して直接読む。Linux ではファイル記述子を Unix ドメインソケット(SCM_RIGHTS)で受け渡す

条件が合わないとき(別のホスト、別のユーザーなど)は、通常の CPU バッファの経路に自動で戻ります。固定サイズのブロックをプールで使い回し、世代番号と読み手の数で「読んでいる最中の上書き」を防ぐ仕組みも入っています。

送る側のコード変更は小さく、掲示板の例では次の 1 行が中心です。

image.data = shared_buffer::allocate_buffer(byte_count);

受ける側は Python でも、NumPy で共有メモリをそのまま配列として読めます。

from shared_buffer import read_buffer
view = read_buffer(message.data)
pixels = np.frombuffer(view, dtype=np.uint8)

作者のカメラ実験

作者は USB カメラ(V4L2・640×480・MJPEG を bgr8 に変換・公称 30 Hz)で、通常の CPU バッファと shared_buffer を比べた簡単な実験を載せています。作者自身が「統制されたベンチマークではない」と断ったうえでの結果は次のとおりです。

  • 通常の CPU バッファ経路では、作者の Fast DDS 環境で別プロセスの C++ 受信側の受信レートが 30 Hz 付近に落ち着かず、受信 0 の区間も出た
  • shared_buffer を有効にすると、C++ と Python の受信側がどちらも起動後に 30 Hz 付近で安定した

作者は、通常経路のどこが原因かはまだ切り分けておらず、Fast DDS 一般の性能比較として読まないでほしいと書いています。ベンチマークの詳細は別リポジトリにまとめられています。実験に使ったカメラノードと受信側のコードは、shared_buffer_backend 本体ではなく、作者の別のワークスペース dskkato/amp にあります。現時点ではソースからのビルドが前提で、Lyrical と Rolling へのパッケージ配布(Bloom でのリリース)の準備が進んでいます。


🔌 3. micro-ROS とは — マイコンを ROS 2 のノードにする

PC 側の ROS 2 は Linux の上で動き、DDS の相手探しや通信にそれなりのメモリを使います。数百 KB の RAM しかないマイコンでは、そのままでは動きません。そこで作られたのが micro-ROS です。

micro-ROS は、マイコンの上でノード・publish/subscribe・サービスといった ROS 2 の主な概念を使えるようにするソフトウェア群です。C 言語の API(ROS 2 標準の rcl に、マイコン向けの拡張 rclc を足したもの)で書き、初期化が終わった後は動的なメモリ確保なしで動きます。FreeRTOS・Zephyr・NuttX といった RTOS の上で動き、ESP-IDF や Zephyr のツールチェーンとも統合されています。

flowchart TD MCU["Pico 2 W/ESP32(micro-ROS クライアント・rclc)"] -->|"USB シリアル/Wi-Fi UDP(XRCE-DDS)"| AGENT["micro_ros_agent(PC)"] AGENT -->|"DDS"| GRAPH["ROS 2 のノード群(SLAM・Nav2 など)"]

構成の要はクライアントとエージェントの分担です。マイコン側は DDS の全機能を持たず、軽量な Micro XRCE-DDS のクライアントとして動きます。PC 側で動くエージェント(micro_ros_agent)がマイコンの代理として DDS の世界に参加し、トピックを中継します。このため PC 側からは、マイコンのノードも ros2 topic list などのいつもの道具で普通のノードと同じように見えます。micro-ROS の公式ページによると、XRCE-DDS のクライアントは 512 B 程度のメッセージを扱う publisher と subscriber の一式で、Flash 75 KB 未満・RAM 約 3 KB に収まります。つなぎ方はシリアル、Ethernet/Wi-Fi 上の UDP、6LoWPAN、Bluetooth が組み込みで用意されています。

基板ごとの対応は、使う開発環境で変わります。PlatformIO 用の公式ライブラリ micro_ros_platformio の対応表には、ESP32(esp32dev・シリアル/Wi-Fi/Ethernet)と、Raspberry Pi Pico/Pico 2(pico/pico2・シリアル)が載っています。対応する ROS 2 のディストリビューションは Humble・Jazzy・Kilted・Rolling です。


🖥️ 4. Linorobot2 Cockpit — ブラウザからロボットを立ち上げる

何をしてくれるのか

Linorobot2 Cockpit は、linorobot2 形式の移動ロボット(2 輪差動・4 輪スキッドステア・メカナム)向けの、ブラウザで操作する管理画面です。PC 側の ROS 2 一式を Docker のコンテナで動かすので、ホストに ROS 2 を入れる必要はありません。ROS 2 は Jazzy(Ubuntu 24.04)と Lyrical(Ubuntu 26.04)の両方に対応しています。ただし既定のイメージは、Ubuntu 26.04 のホストでも :jazzy です。Lyrical にするには環境変数 ROS_DISTRO=lyrical で切り替えます(docker-compose.yml のコメントより)。イメージは約 1.2 GB で、Ubuntu 26.04 機での初回の取得は 46 秒でした。

README が「1-Click」と呼ぶ、基板を挿したときの流れは次のとおりです。

flowchart TD A["USB で基板を挿して Start 1-Click"] --> B["マイコンの種類を自動判別"] B --> C["ビルド済みの micro-ROS ファームを書き込む"] C --> D["設定を Flash の 4 KB 領域に書き込む"] D --> E["micro_ros_agent を起動"] E --> F["/odom と /imu/data が 50 Hz 出ているか検査"] F --> G["6 種類の走行試験"] G --> H["SLAM Toolbox と Nav2 を起動し、地図を保存"]

ファームはリリースに含まれるビルド済みのものを書き込むので、手元でのコンパイルは不要です。ピン割り当て・センサー・足回りの寸法・ナビゲーションの制限は、1 つの YAML ファイル(~/linorobot2-config/)にまとめて管理します。ESP32 などでは、ArduinoOTA による無線での書き込みにも対応しています。

対応ボード

ボード マイコン 既定のつなぎ方
Raspberry Pi Pico 2/Pico 2 W RP2350 USB シリアル
Raspberry Pi Pico/Pico W RP2040 USB シリアル
ESP32 DevKit ESP32 シリアル/Wi-Fi(UDP)
ESP32-S3 ESP32-S3 ネイティブ USB/Wi-Fi
Sim MCU(仮想) PC 上のソフトウェア UDP

ファームの設定ファイルには、Pico 2 W を Wi-Fi でつなぐ構成と、各ボードの Lyrical 向けの構成も用意されています。

基板がなくても試せる

面白いのは、モーターもセンサーも配線していない段階から、一連の流れを試せることです。PC 上の仮想マイコン「Sim MCU」や、シミュレーションモードで書き込んだ素の基板が、オドメトリ・IMU・仮想の部屋を走査する LiDAR のデータを本物の micro-ROS で流します。モーターのトルク特性、電池の電圧降下、摩擦、エンコーダーの量子化まで模擬すると README は書いています。はんだ付けの前に、SLAM と Nav2 の設定を詰めておけるわけです。

基板なし(Sim MCU)で 1-Click を通した結果

Ubuntu 26.04 の PC で Cockpit(画面の表示は v20260925-dev)を起動し、LAN の別の PC のブラウザから操作しました。USB に何も挿さずに開くと、「No MCU board detected」と表示され、仮想マイコンのロボット bare_sim に自動で切り替わります。

基板を挿さずに開いた Cockpit。No MCU board detected と出て、Sim MCU のロボット bare_sim に切り替わる。この状態で Start 1-Click を押せる

基板を挿さずに開いた Cockpit。No MCU board detected と出て、Sim MCU のロボット bare_sim に切り替わる。この状態で Start 1-Click を押せる

Start 1-Click を押すと、ヘッダの Agent(micro_ros_agent)と Bringup が running に変わります。

1-Click の実行後。ヘッダの Agent と Bringup が running になる

1-Click の実行後。ヘッダの Agent と Bringup が running になる

画面下の Output 欄に出たログの抜粋です。

   Sequence: Config -> Firmware -> Probe -> Flash -> Bringup -> Topics -> SLAM -> Nav2 -> Map
[1/6] [CONFIG] Simulated MCU: no firmware, so no header to generate.
[4/6] [BRINGUP] Launching the bringup stack (controller=sim, distro=jazzy)...
  Waiting for the micro-ROS agent handshake and /odom/unfiltered (transport=udp4, up to 30 s)...
  ✅ micro-ROS connected (/odom/unfiltered has a publisher).
✅ /odom        : PASS (50.8 Hz >= 10.0 Hz)
✅ /imu/data    : PASS (49.8 Hz >= 7.0 Hz)
✅ /scan        : PASS (8.5 Hz >= 5.0 Hz)
[5/6] [SLAM] Launching SLAM Toolbox (distro=jazzy)...
  ✅ /map is publishing.
     map spans x -5.03..+3.97, y -3.00..+3.05
[6/6] [NAV2] Launching Nav2 (distro=jazzy)...
  ✅ Nav2 stack active.
  Frontier exploration (explore_lite), up to 900 s...
  [explore]     3 s  exploration_started  (known area 6.3 m²)
  [explore]    71 s  exploration_complete  (known area 62.1 m²)
✅ EXPLORE: returned_to_origin after 106 s; known area 62.3 m²

読み方は次のとおりです。

  • ファームの工程は飛ばされる:Sim MCU には書き込む基板がないので、[1/6]〜[3/6] は「何もしない」と表示して先へ進む
  • micro-ROS は本物の経路でつながる:PC 上の仮想マイコンが UDP(udp4)で micro_ros_agent と握手し、/odom/unfiltered を出し始める
  • トピックの検査:/odom 50.8 Hz、/imu/data 49.8 Hz、/scan 8.5 Hz。それぞれ下限(10・7・5 Hz)を上回って PASS
  • SLAM と Nav2:SLAM Toolbox が /map を出し、Nav2 が起動する
  • 自律探索:explore_lite が地図の未知の境界(フロンティア)へ向かって走り、71 秒で探索完了、106 秒で原点に戻った。既知の面積は 62.3 m²

Map Viewer タブで Connect を押すと、rosbridge 経由で /map・/scan・ロボットの位置がブラウザに描かれます。RViz も X サーバーも要りません。

Map Viewer。SLAM で作った部屋の地図(黒い線が壁)、LiDAR の点(赤)、ロボットの位置(青い丸)が描かれる。URL 欄のアドレスは自宅 LAN 内のもの

Map Viewer。SLAM で作った部屋の地図(黒い線が壁)、LiDAR の点(赤)、ロボットの位置(青い丸)が描かれる。URL 欄のアドレスは自宅 LAN 内のもの

つまずきやすい点:ポート 8000 と 9090

最初に踏みやすい穴はポートです。docker-compose.yml は network_mode: host で、管理画面(supervisor)の 8000 と rosbridge の 9090 をホストでそのまま使います。ほかのコンテナなどがこの 2 つを使っていると、起動に失敗してコンテナが再起動を繰り返します。

ERROR: [Errno 98] error while attempting to bind on address ('0.0.0.0', 8000): address already in use

docker compose ps の状態が Restarting になっている、docker compose logs | grep token に同じトークンの行が何度も出る、のが兆候です。

ポートは設定では変えにくくなっています。

ポート 役割 変え方
8000 管理画面 環境変数 COCKPIT_PORT で変えられるが、docker-compose.yml が渡していない
9090 rosbridge(Map Viewer) 設定 YAML の supervisor.rosbridge_port は起動側で読まれておらず、変えられない

回避策は、docker-compose.yml と同じフォルダに docker-compose.override.yml を置き、ホストのネットワークを共有せずにポートを付け替えることです。

services:
  cockpit:
    network_mode: bridge
    ports:
      - "8001:8000"
      - "9091:9090"

docker compose up -d をやり直すと、docker compose ps は Up で 0.0.0.0:8001->8000/tcp, 0.0.0.0:9091->9090/tcp になります。注意点は 3 つです。

  • ログのトークン付き URL は :8000 のまま表示されるので、:8001 に読み替えて開く
  • LAN の別の PC から見るなら、ポートの前に 127.0.0.1: を付けない。"127.0.0.1:8001:8000" と書くと、その PC の中からしか開けない
  • Map Viewer の URL 欄は既定で :9090 を指すので、Connect の前に ws://<robot-computer>:9091 に書き換える

network_mode: bridge で動くのは、Sim MCU(仮想マイコンはコンテナの中で動く)と USB でつなぐ基板(/dev を直接渡す)です。Wi-Fi でつなぐ基板は、ホストのネットワークを共有する既定の構成が必要なので、この回避策は使えません。

規模について

Cockpit は個人が開発しているプロジェクトです。2026年10月1日時点で GitHub のスターは 0、公開されているのはリリース候補(rc-20260927.3)で、掲示板の告知の閲覧数は 42 でした。作者は ROS Discourse と GitHub Discussions でテスターを募っています。完成品というより、マイコンから Nav2 までを一気通貫でつなぐ設計の見本として読むのがよいと考えています。


📝 5. NoDL — ノードの入出力を宣言しておく

NoDL(Node Definition Language)は、ノードが持つパラメータ・publisher・subscriber・サービス・アクションを YAML で宣言しておくための言語です。ROSCon 2026 の講演「The Legible Node」で紹介され、ROSGraph ワーキンググループで開発されています。宣言があれば、実行中のノードが仕様どおりかを確かめたり、ドキュメントやノードのひな形を生成したりできます。既存のノードから ros2 nodl describe <ノード名> で宣言を書き出すこともできます。アプリ全体のノードのつながりを、動かさずに描き出す機能も開発中です。マイコン側のノードが何を出すかを文書として固定しておく用途にも向いていそうです。


🛠️ 6. 手持ちの Pico 2 W/ESP32 で試すチェックリスト

どちらも、まずは実機を動かさない段階から始められます。

Cockpit を仮想マイコンで動かす

必要なもの:USB 付きの Linux PC(README の言う「ロボット用コンピュータ」)。

sudo apt install -y git
git clone https://github.com/hippo5329/linorobot2-cockpit.git
cd linorobot2-cockpit
bash scripts/install_docker.sh     # 必要ならルートレス Docker を入れる
docker compose up -d
docker compose logs | grep token   # 認証付きの URL が表示される
  • 表示された http://<PC>:8000/?token=… をブラウザで開ける — 8000/9090 が使用中だと再起動を繰り返すので、先に docker compose ps が Up かを見る。ふさがっていたら 4 章の override で 8001/9091 に付け替える
  • 基板を挿さずに Start 1-Click を押すと Sim MCU が使われ、パイプラインが最後まで進む — トピック検査 PASS、SLAM・Nav2 起動、自律探索は 106 秒で原点に帰還
  • Map Viewer タブで Connect を押すと、地図と LiDAR の点が描かれる — ポートを付け替えた場合は URL 欄を :9091 に直してから

Pico 2 W/ESP32 を挿して動かす

  • 基板だけ(モーターを繋がない状態)を USB で挿して Start 1-Click を押すと、ボードの種類が判別されてファームが書き込まれる
  • micro_ros_agent が起動し、/odom と /imu/data のレート検査が通る
  • モーターと車輪を付ける場合は、走行試験で動き出すので車輪を浮かせるか、広い場所で行う

shared_buffer_backend を試す(Ubuntu 26.04+ROS 2 Lyrical)

ROS 2 Lyrical は、ros-lyrical-ros-base と ros-dev-tools の 2 つを入れれば足ります(GUI ツール入りの desktop は不要)。入れ方は公式ドキュメントの Installing on Ubuntu(Lyrical・deb パッケージ) のとおりです。

cd ~/ros2_ws/src
git clone -b lyrical https://github.com/dskkato/shared_buffer_backend.git
cd ..
rosdep install --from-paths src --ignore-src -r -y
colcon build --symlink-install
source install/setup.bash

リポジトリに入っているのは shared_buffer・shared_buffer_backend・shared_buffer_backend_msgs・shared_buffer_backend_py の 4 パッケージです。手元ではまず、これらのビルドと付属のテストを通します。テストには、Fast DDS を使う launch テストも含まれています。

  • colcon test --packages-select shared_buffer shared_buffer_backend shared_buffer_backend_py shared_buffer_backend_msgs を実行する — 4 パッケージとも完走
  • colcon test-result --verbose で、失敗が 0 件になる — 168 件中、エラー 0・失敗 0(スキップ 24)

rosdep install で追加で入ったのは pybind11-dev だけでした。ビルドとテストの結果は次のとおりです。

$ colcon build --symlink-install
Finished <<< shared_buffer_backend_msgs [4.91s]
Finished <<< shared_buffer [3.37s]
Finished <<< shared_buffer_backend_py [5.03s]
Finished <<< shared_buffer_backend [7.21s]
Summary: 4 packages finished [15.6s]
$ colcon test-result --verbose
Summary: 168 tests, 0 errors, 0 failures, 24 skipped

掲示板のカメラ実験で使われた v4l2_camera(use_shared_buffer:=true で共有メモリを使う)と、受信レートを測る ros2topic_hz(--accept-buffer-backend shared_buffer)は、shared_buffer_backend のリポジトリには入っていません。作者の別のワークスペース dskkato/amp の中にあり、どちらも CUDA 版のバッファ(cuda_buffer)に依存しています。amp は Docker の中で ROS 2 Lyrical を動かす構成で、イメージは ROS 2 の公式イメージに「NVIDIA CUDA toolkit (nvcc)」などを足したもの、ホストには NVIDIA Container Toolkit が有効な Docker が必要と README にあります。カメラの例をそのまま追試するには、NVIDIA の GPU と CUDA の環境が要ります。


💭 所感 — 自作基板を ROS 2 の世界に出すなら

ここからは筆者の意見です。

📌 筆者の見方

筆者の手元には、温度・湿度・気圧・照度・CO2・粉塵を測る ESP32-S3 の自作基板があります。いまは値を 60 秒ごとに MQTT で送っていますが、micro-ROS で sensor_msgs のトピックとして出せば、同じ値を PC 側の ROS 2 の道具(記録・可視化・他のノード)にそのまま流せます。Cockpit は移動ロボット専用の作りですが、ブラウザで基板を判別し、ビルド済みファームを書き、設定を Flash の小さな領域に分けて書くという流れは、センサ基板の配布にもそのまま使える設計だと感じました。ファームを作り直さずに設定だけ差し替えられるのは、基板を何枚も作る人にとって大きな利点です。

shared_buffer_backend には別の期待があります。Raspberry Pi 5 のような小さなコンピュータでカメラ画像を Python の推論ノードに渡すとき、「C++ の同じプロセスにまとめないと重い」という制約が、推論を Python で書く人には大きな壁でした。プロセスを分けたまま、Python から NumPy で画像を直接読めるのは、その壁をちょうど崩す方向です。まだ実験段階で Fast DDS 限定ですが、パッケージとして配布される日を楽しみにしています。


⚠️ 注意点

  • shared_buffer_backend は実験的:いまはソースからのビルドが前提です。ROS 2 Lyrical の rosidl::Buffer を使うため、Jazzy 以前では動きません。Lyrical 時点で rosidl::Buffer を使えるのは Fast DDS(rmw_fastrtps_cpp)だけです。掲示板のカメラの例は、CUDA 環境向けの別ワークスペース(amp)の一部です
  • 性能の数字は作者の簡単な実験:30 Hz で安定したという結果は、作者が統制されたベンチマークではないと明記しています。自分の環境で測って判断してください
  • Cockpit は個人のリリース候補:ファームの書き込みや Flash への設定の書き込みを自動で行います。大事な基板で試す前に、Sim MCU と予備の基板で流れを確かめると安心です
  • ポート 8000 と 9090 を先に空けておく:既定の構成はホストのネットワークをそのまま使うため、どちらかが使用中だとコンテナが再起動を繰り返します。付け替えは 4 章の docker-compose.override.yml で行えます(Wi-Fi でつなぐ基板では使えません)
  • 認証トークン付きの URL を外に出さない:Cockpit の管理画面はロボットの操作までできます。管理画面のポート(既定 8000)をインターネットに公開しないようにしましょう

✅ まとめ

  • ROS 2 のノードはトピックでつながり、別プロセスの間では DDS がデータを直列化して運ぶ。 画像のような大きなデータでは、このコピーが重くなる
  • shared_buffer_backend は、画素を共有メモリに置き、記述子だけを DDS で送る。 ROS 2 Lyrical の rosidl::Buffer に乗り、C++ と Python の受信側がコピーなしで読める。同じホスト・同じユーザーが条件で、合わなければ通常経路に戻る
  • micro-ROS は、マイコン側のクライアントと PC 側のエージェントで ROS 2 に参加する。 マイコンのノードも、PC からは普通のノードとして見える
  • Linorobot2 Cockpit は、Pico 2 W・Pico W・ESP32・ESP32-S3 をブラウザ操作で micro-ROS ロボットにし、SLAM と Nav2 まで立ち上げる。 基板なしの仮想マイコンでも、トピック検査・SLAM・Nav2・自律探索まで一周した。個人のプロジェクトで、まだリリース候補
  • 次の一手は、ポート 8000/9090 が空いているのを確かめて Cockpit を Sim MCU で一周させ、それから手持ちの Pico 2 W か ESP32 を挿してみること

よくある質問(FAQ)

Q: ゼロコピーなら、ROS 2 のプロセス内通信で十分ではありませんか?

A: 同じプロセスに入れられる C++ のノード同士なら、std::unique_ptr を使うプロセス内通信でコピーなしに渡せます。推論ノードを Python で書きたい場合や、障害の切り分けのためにプロセスを分けたい場合には使えません。shared_buffer_backend は、プロセスを分けたままコピーを避けるための選択肢です。

Q: shared_buffer_backend は Jazzy でも使えますか?

A: 使えません。ROS 2 Lyrical で導入された rosidl::Buffer のバックエンドとして作られています。Lyrical のリリースノートによると、この機能を使えるのは現時点で rmw_fastrtps_cpp(Fast DDS)の publisher と subscriber だけです。

Q: micro-ROS と ROS 2 本体は何が違いますか?

A: micro-ROS はマイコン向けの ROS 2 です。マイコン側は軽量な Micro XRCE-DDS のクライアントとして動き、PC 側のエージェントが代理で DDS に参加します。ノードやトピックといった考え方は同じで、PC 側からはマイコンのノードもいつもの ros2 コマンドで見えます。

Q: Cockpit を使うのに、PC に ROS 2 を入れる必要はありますか?

A: ありません。ROS 2 の一式は Docker のコンテナで動きます。USB 付きの Linux PC に Docker を入れ、docker compose up -d で起動して、ブラウザから操作します。

Q: ロボットの車体がなくても Cockpit を試せますか?

A: 試せます。基板を挿さずに始めると PC 上の仮想マイコン「Sim MCU」が使われ、オドメトリ・IMU・仮想の LiDAR のデータが本物の micro-ROS で流れます。1-Click で、トピックのレート検査、SLAM Toolbox での地図作り、Nav2 の起動、仮想の部屋の自律探索(106 秒で 62.3 m² を走査して原点に帰還)まで動きました。素の基板をシミュレーションモードで書き込めば、マイコンとの通信まで含めて確かめられます。

関連記事

参考