RoombaとROS2で楽しい電子工作
かれこれこの半年ほど、細々と2週間に一度程度の頻度でRoombaを題材に電子工作をしている。 ようやく少し成果がまとまったので振り返りを兼ねて書き出す。
現状の仕上がり
こういうのは写真がないと面白くないのでね。
使っているのはこんな感じ
- Roomba(643, 中古品)
- Raspberry Pi 5(メモリ16GB、with ディスク64GB/Ubuntu 24/ROS2 Jazzy)
- モバイルバッテリー(藤本電業、5V3A)
- Nintendo Switch用リモートコントローラー
- Roomba・RPi接続用USBシリアルケーブル
- ToFセンサー(距離センサー)
- ブレッドボード
- ジャンパーワイヤー(オス - メス)
できるようになったのは
- 手動での操作(コントローラーorキーボード)
- 壁にぶつからずに自動壁沿い走行
というところ。 これをROS2、Bazel、C++を使って実現している。ソースコードはここにある。 tkhm/roomba-ros2-work
分からないことが多すぎて形から入っていたり、お金で解決してしまっていたりして、いわゆる電子工作の踏んでいくステップとは違うし、その道に詳しい人的には気になりポイントも多そうだけど、本人としては楽しくやっているのでヨシッと。
ここからは、なぜこの構成にしたのか、どんなステップでここに辿り着いたのかをざっくりと書いていく。
前提の構成とモチベーション
元々、日曜大工的なノリでソフトウェアを書くのは好きで、アイデアを書き溜めて、時間ができた時にそれを形にしていっていた。 ただ、生成AIが出てきて、精度もかなり上がってきたこともあって、ソフトウェアとしてなにか明確にアイデアを書き出せたものは大体もう(要求する品質水準にもよるけど)できて当たり前、動いて当たり前という感じになってきた結果、ソフトウェアを書いて喜びを見出すのが個人的には難しくなってきてしまった。
そんなとき、そういえば以前ROS2で遊んでいたのは楽しかったなという記憶が蘇った。 ROS2といえばPythonかC++で書くのがメジャーで、C++は仕事でも使うことがあったのでC++を書きたい、そしてC++を使う際にBazelはビルドシステムとして結構好きだったしROS2で使うColconやCMakeListは好みではなかったからそこを避けて何か楽しみたい、というところから始まった。
そしてまた別のところでは、RoombaはROS2に対応しているので簡単に動かせるらしい、という理解が曖昧なまま耳心地の良い情報だけを入手した。(後にこれは誤った理解だったとわかるのだけど、とはいえ題材選びとしては結果として良かった)
手元には10年前に買ったRaspberry Pi 3B(以下、RPi 3)がある。Linuxのインストールされたパソコンもある。細かいことはわからないけどきっとこれなら大丈夫。 そこで、ROS2、Bazel、C++、Roombaという制約のもと、この物語は始まった。
前提を覆す誤解の数々
事前の雑なリサーチではうまくいきそうに思えていたものの、いざ具体的な検討を始めるとたくさんの誤解がそこにあった。
誤解1 ROS2のサポート環境
実行環境に制約があることをなぜか疑っていなかったのだが、ROS2のサポートは基本的にはUbuntuが主戦場だった。1 そして手元にあるのは、AMDのUbuntuとARMのMacとARMのRaspberry Pi(RPi OS)。 ARMのMacでそのままビルドするのは困難、AMDからARMへのクロスコンパイルは基本的に余計な困難を招く、さりとてコンテナ上でビルドや実行はしたくない。そうなると、Raspberry Piの上にUbuntuを入れて動かすのが現実的な線のようだと思えてきた。
誤解2 ROS2のビルドと実行環境
昔ながらのJavaやRustのバイナリーは、ビルド環境で十分なリソースがあれば実現でき、そこで仕上がったリリースのバイナリーを実行すれば使用可能だった。(つまり、大きくリソースを必要としないなら、) ROS2でも同じような理屈が通用すると思い込んでいたのだけど、これは難航した。前述の通り、クロスコンパイルするか、あるいはサポートされていないMac上でどうにかビルドするか、といったところが必要そうだった。 しかしクロスコンパイルや非サポート環境からのビルドというのは、やりたいと思っている内容とはかなり別のベクトルへの、強めの努力を求められる。
誤解3 ROS2関連のビルドに必要なリソース
RPi 3Bではなんと1GBしかメモリがない。 そのリソース量ではビルドはできなかった。並列ビルド処理数を1などにすればもしかしたらいけたのかもしれないが、だとするとビルドのたびに時間がかかりすぎる。
誤解4 ROS2のBazelによるサポート
Bazelは徐々に人気も出てきていて、使い勝手もまあまあ良いように思う。が、まだこれがC++における第一線と言い切ることはできそうになかった。 コミュニティ上でも質問をしている人はいたが、コミュニティマネージャーから、ROS2は産業上のニーズ以外に、教育や研究の目的にも使われている。2つのビルドシステムをサポートするのはコストが高まりすぎるし、一方で今のビルドシステムから移行すれば互換性は少なくとも損なわれる。損なわれて失うものに対して得られるものが不明瞭で移る必要性が感じられない、とまあそういうことだった。2 つまり、公式はBazelをサポートする計画を持っていないので、前提の部分が覆りかねないないようだった。
誤解5 RoombaのROS2サポート
何を勘違いしたのか。Roomba販売元だったiRobot社は教育目的の、RoombaをベースにしたロボットであるCreate 3を販売していた。確かにこれはROS2をネイティブにサポートしている。
しかし、Roomba自体はそうではなかった。
同じく教育目的のCreate 2では非公式(?)のサポートパッケージ(create_autonomy)があり、これに手を加えた有志によるメンテナンス版を使うことでROS2サポート可能なようで、その点でコミュニティとしてはまだ続いていると言えるのだけど。この手の話では有名なYutaka Kondoさんの取り組み3もあったが、よく理解する必要がありそうな雰囲気を感じた。
誤解への手打ちと手応え
それぞれの誤解ポイントに対して、誤解は誤解としてそこにあるのだが、迂回や対策を講じて、無事話を前に進めることができた。
-
誤解1 ROS2のサポート環境 RPi OSはやはりRPiの特性にあった形で使いやすく構成されていて好きではあった。 また、後述するRPi 5では、筐体の外に物理ボタンがつき、これを2度押すことでRPiの電源を落とすことができる。(Ubuntuでは、自分の理解では不可) しかし、そこ以外に強くこだわる理由はないので、RPi 5で使用するOSはUbuntu 24を入れた。
-
誤解2 ROS2のビルドと実行環境 これは素直にビルド可能な環境を整えるしかないように思った。仮にMacでビルドができるようになったとして、それをRPiにデプロイするという行為も実のところ面倒な気がして、だとしたらRPi上でビルドできるようにするのがいいのではないかと考えた。 そこで、RPi 3Bのリソースでは不足することが明らかだったのでRPi 5の強い端末を買った。保有していたLinux端末は古くあまり強くもなかったので、開発機を買ったと思えばそれもいいのではないかと納得することにした。
-
誤解3 ROS2関連のビルドに必要なリソース 同上。電子工作は安いお金でうまくやってなんぼ、というイメージがあって、そうできないのは能力的に劣っていることの証明とすら感じることがあったが……事実、この方面についての理解がからっきしだったし、それでもやってみたい気持ちはあったので、強い端末を買うことで解決。
- 誤解4 ROS2のBazelによるサポート
ColconというビルドシステムでサポートされているROS2をBazelで使えるようにするための先人の取り組みがいくつかあった。確認時点で明確にJazzyのバージョンに言及があった
mvukov/rules_ros2を試し、最低限の振る舞いを確認できた(ありがたい!)ので確定とした。- mvukov/rules_ros2 :最近でも活発な更新がある、少なくともUbuntu 24.04/Jazzyでの検証のブランチがある
- ApexAI/rules_ros :更新頻度が低めで、Jazzy対応も入るか不明(と書いていたらちょうど更新が入っていた)、ROS2に乗せた自社ソフトのためというのが第一義っぽいので更新が継続されるか危うい
- RobotLocomotion/drake-ros :更新頻度が低め、DrakeというTRIが作っているソフトのためのROS結合っぽく、どれほど真剣に更新をして使っていこうとしているのか不透明(ただし、他のアプローチについての比較検証の記載がある)
- 誤解5 RoombaのROS2サポート 公式なサポートはない。が、そもそもRoombaの良い点はOpen Interface(OI)と呼ばれる仕様が明確に公開されていて、少し努力をすればシリアル通信によって必要なデータのやり取りができそうだとわかった。 具体的にはRoombaとRPiをシリアル変換ケーブルでつないだ上4で次のようなプログラムでなんとなく動かせるのがわかったので、ROS2で呼び出すプログラムの中で、OIの抽象化したコードと、いわゆるHardware Abstraction Layer(HAL)層のようなイメージのROS2とRoombaの間を繋ぐようなコードとを書けばいいだけかと思ってあまり心配しないことにした。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import serial
import time
s = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)
# Start
s.write(bytes([128]))
time.sleep(0.1)
# Safe mode
s.write(bytes([131]))
time.sleep(0.1)
# 曲データ登録(ID=0, 音符1つ: A4=69, 長さ=16)
s.write(bytes([140, 0, 1, 69, 16]))
time.sleep(0.1)
# 曲再生(ID=0)
s.write(bytes([141, 0]))
そして壁沿い走行
ようやく環境や前提の部分が整ったので、簡単なところから取り掛かり出した。ラジコンのようにキーボード操作やワイヤレスコントローラーでRoombaを制御するという取り組み。(キーボードはすぐだったが、コントローラーはどのボタンが何に該当し、それをどこに紐づけるか、といった点をボタンを押しながら確かめる必要があり、少し手間がかかった)
その上で、自動での走行。Roombaにはセンサー類がついているから壁沿走行も可能だろうと勝手に思い込んでいた。しかし実際には、RoombaについているIRセンサーは距離が短く、基本的には接触を避けるよりは減速によって衝撃を和らげる用途で、接触センサーで壁に軽くぶつかって方向を転換、というのが基本の想定の様子。5
実際の掃除の時は、壁にぶつかると方向を変えて走るのであまりそういうシーンに出くわさないが、壁沿いを走らせようとするとこのセンサーではうまく捉えきれず、何度も何度も壁にぶつかりながら走っていく。その様は見ていてもあまり好ましいものではない。
そこで横距離を、素材から受ける影響を最小限にしながら安価で簡易的に得る方法として、Time of Flight(ToF)というセンサーを使用することにした。 これにより、横距離が離れすぎていたら近づき、近づきすぎていたら離れることで、壁への接触回数をグッと下げることができた。
危ないところまで行ったらコントローラーで制御モードを手動に戻して、いい向き、いい方向に変えて走らせてあげる。そんなことができるようになったので嬉しい。
これからの改善
この手の電子工作に門外漢すぎて、わからないことが多く、というか正直、これは電子工作と言っていいのかもわからないレベルだと個人的には思っている。とはいえ、既に見えている課題ややりたいこともあるのでまずは書いてみる。
壁沿い走行の改善
わずかな凸凹の壁(例えばカーテン)は今の造りでそれなりに走るようになった。しかし、少し凸凹が大きいと、横位置しか見ていないため本体が接触し位置をリカバリーするような挙動になってしまう。 当然ながら、廊下から部屋に入っていくようなのも無理。部屋一周などもやってみたいが、今のパフォーマンスでは考えられそうにない。
壁の状況の変化に強くなるために、センサーを追加して補強することを考えている。 右横に向くセンサーを主センサーとしつつ、例えば右斜前に向くセンサーで接近状況を捕捉すれば接触回数は明らかに減らせるのではないかと思っている。
接続面の改善
電子工作の普通、がわからない。 ToFセンサーを使い出したら線が伸びていて、それをどうにかして繋げばいいのはわかったのだけど、果たしてどう繋ぐか、がわからず、ブレッドボードに直で挿してみたり(意外とこれでもうまくいく、抜けるけど)、あるいはジャンパーワイヤーのメスの口に直接差し込んでみたり(意外とこれもうまくいく、やっぱり抜けるけど)ということをしている。
これについて、秋月電子の親切な店員の方に教えてもらったり、生成AIとの壁打ちをしたりして見えてきたのは、熱収縮チューブによって外れにくくするということと、コネクターを圧着ペンチなどを使って自作するということと、ハンダでつけて固定するということが選択肢だった。
世の中には、電子工作をやるというのにハンダづけをなぜか避ける人がいる、と嘆きの投稿をRedditなどでも見かけたのだが、まさに自分もハンダづけには苦手意識がある。実際、友人の話を聞いても、扱いの不始末で様々なものを溶かしたり接触が不良だったりする話を聞き、不器用な自分は可能なら避けたい選択肢として見ている。
そもそもRPiやブレッドボードもRoombaの上にポテっと置いてある。これは、多分普通、ではない。固定が必要だと思う。ToFセンサーもその辺に余っていた筆入れの側面にマスキングテープで貼り付けているだけ、電源はなんとその筆入れの中に入れているだけ。
こう言った方面をきっちりやってこそ、電子工作として次のステップだと思うので安定稼働のための方法は考えたいところ。とりあえず(相変わらずだが)形から入るということで、熱収縮チューブ、ワイヤーストリッパー、圧着ペンチは購入した。(熱収縮チューブは測らずに買ったら細すぎて線が入らず買い直しました、とほほ)
前走車フォロー機能の追加
自動車でいうアダプティブクルーズコントロール(ACC)ではないけれど、目の前の別の車両についていくような隊列走行ができたらなんだか面白いように思った。 本格的にやろうとするとかなりいいセンサーを入れないと厳しそうに思うのでArUco(アルコ)マーカーと呼ばれる位置や角度、姿勢を高速に検知できるマーカーを使った方法でやってみようかなと思っている。
おわりに
電子工作入門の本の例だとなんだかあまりわくわくしなくて、結局少し高価で大掛かりな仕掛けになってしまっているけど、やはり目に見えて動くものは面白い。時間はかかるけど、引き続きじっくりと楽しんでいきたい。
-
Jazzy Jalisco (jazzy) — ROS 2 Documentation: Rolling documentation を見るとわかるが、高い技術力を持っていると自負がないなら、基本的にはTier1かTier2のサポートのある層のみが選択肢となるように思う。つまりそれは、Linux系なら基本的にUbuntuのみ。 ↩︎
-
Adding Bazel as an official alternative build system for ROS2 Iron? - ROS / ROS General - Open Robotics Discourse におけるamacneilやKatherineの回答が明確だったように思う。 ↩︎
-
初版「ROS2ではじめよう 次世代ロボットプログラミング」ROS 2移行章公開 | Yutaka Kondo にどのようにROS2に対応するかなどを詳細に書いてくれている、のだが、逆にこのレベルで対応しないとできないという点にハードルを感じた。 ↩︎
-
3の記事の内容の中でこのケーブルについては触れられている。完成品を買うか、自作するか。今だったら少しは理解が進んだので作れるかもしれないが、当時は自作のイメージが全くついていなかったので買って良かった。 ↩︎
-
Subsumption architectureというらしい。ここでの解説がわかりやすい。ロボット掃除機を買った時の寂しさ | Jun Mukai’s blog 『単純な「まっすぐ進む」「障害物にぶつかったら曲がる」「ゴミを見つけたらその方を向く」「ゴミを持っている時はゴミ捨て場の方を向く」みたいなルールだけを持っている。』とのこと。 ↩︎
