自律移動ロボットの内部状態(センサーの接続状況、バッテリー残量、現在のモード)を、外部から一目で確認できることは、開発中の診断だけでなく日常的な運用でも欠かせない。newbotではこれを、エッジ機(Raspberry Pi 5)上で動くシンプルなWebダッシュボードとして実装している。

状態可視化ダッシュボードで障害物ガードの検知距離とSLAM俯瞰図を表示している様子

実際に運用中のダッシュボード画面。

依存を最小限にする設計

ダッシュボードはrclpy(ROS2のPythonクライアントライブラリ)とPython標準ライブラリのhttp.serverのみで構築しており、追加のROSパッケージには依存していない。ブラウザで接続すると、PC接続状況・コントローラー接続状況・マイコンとのシリアル接続状況・カメラ接続状況・LiDAR接続状況・正面カメラのライブ映像・モーター回転数・バッテリー残量・非常停止/状態機械・ミッション状態がリアルタイムに表示される。

設計上の原則として、ダッシュボード自身は制御系へ一切publishしない(コントローラーの再ペアリング要求という単一の例外を除く)。あくまで読み取り専用の可視化ツールという位置づけを徹底している。

接続判定の考え方: 「最後にメッセージを受けてからの経過時間」

各コンポーネントの接続状態は、対応するトピックのメッセージが最後に届いてからの経過時間で判定している。例えばマイコンとのシリアルリンクは特定のトピックの到着間隔が1秒を超えると切断とみなし、カメラは2秒、上位のPC(重処理機)は3秒といった具合に、コンポーネントごとに異なるタイムアウト値を設定している。これは、通信途絶を検知する安全監督ノードのハートビート判定と同じ考え方を可視化層にも適用したものである。

カメラ映像配信

正面カメラの映像は、センサーデータ向けのQoS設定(ベストエフォート)で購読し、受信フレームをJPEGへエンコードしてmultipart形式でストリーム配信している。ブラウザ側は<img>タグ一つで表示でき、追加のクライアント側コードを必要としない。エンコード処理自体がエッジ機のCPU負荷になるため、配信レートは最大10fpsに意図的に抑えている。未受信・受信途絶時にはプレースホルダー画像を返す設計にしており、映像が表示されない場合でも壊れたアイコンとして見えないようにしている。

画像データの取り回しには、ROSの画像変換ライブラリ(cv_bridge)をあえて使っていない。エッジ機にこのライブラリが導入されていなかったため、カメラドライバが配信するrgb8形式の生データを、幅×高さ×3チャンネルのnumpy配列として直接組み立て、それをOpenCVのエンコード関数へそのまま渡す実装にしている。追加のROS依存パッケージを増やさないという設計方針を、画像処理の入口でも一貫させた形である。

実装時には、QoS設定の不一致でメッセージが一切届かない(エラーも出ない)という気づきにくい罠を踏んでいる。センサー系のトピックはベストエフォートQoSで配信される一方、購読側を既定のQoS(信頼性重視)のままにすると接続自体は成立するのにメッセージが届かない。両端のQoS設定を明示的に確認・一致させることで解決した。

Pi5起動時の自動起動とキオスク表示

エッジ機の電源を入れるだけで、駆動系のソフトウェアとこのダッシュボードが自動的に起動するよう、システム起動時に自動実行される設定にしている。エッジ機には表示用のディスプレイが接続されているため、その画面にブラウザをフルスクリーン(キオスクモード)で自動起動し、ダッシュボードを常時表示する構成も導入した。UIはスクロール不要の1画面固定レイアウトにしている。

導入にあたっては、通信方式としてWebSocketではなく400ミリ秒間隔のポーリング(/api/statusへの定期リクエスト)を選んでいる。rosbridge等の追加パッケージに依存しない設計にしたのは、エッジ機のroot権限操作が対話的なパスワード入力を必要とし、無人でのパッケージ導入ができない制約があったためである。

キオスク表示の自動起動を組むにあたっては、いくつか実機ならではの罠を踏んでいる。デスクトップ環境のセッション管理に依存するsystemdユニットの起動待ちが原因でsystemctlコマンド自体がハングする問題や、Waylandとの互換性検出の問題、そして最も深刻だったのは、キオスク表示を止めるためのpkillコマンドのパターンが広すぎて、SSHセッション自体を巻き添えで切断してしまう事故である。この事故以来、プロセスを名前でkillする操作は、対象を必要十分な範囲まで絞り込んだパターンで行うことを徹底している。

「電源を入れてもダッシュボードが出てこない」の原因調査

キオスク表示を導入した直後、電源を入れてもダッシュボードが画面に出てこず、その都度手動でサービスを再起動する必要がある、という問題に遭遇した。原因は2つ重なっていた。

1つ目は、キオスク側のURL待機処理のタイムアウトが60秒と短すぎたこと。タイムアウト後に「まだ起動していないURL」を開いてしまうと、ブラウザがエラーページのまま固まり、自動では再読み込みしない。これは応答があるまで無期限に待つよう修正した。

2つ目は、起動直後のディスク入出力の競合による全体的な遅さだった。実際のタイムラインを記録すると、キオスク起動からWaylandのソケット待ちが始まり、デスクトップ環境自体の起動に約2分、続くダッシュボードの起動にさらに42秒を要し、電源投入から画面表示までトータルで約4分かかっていた。起動直後は駆動系のROSノード群(自己位置推定・LiDARドライバ・カメラを含む十数個)がストレージの入出力を占有し、デスクトップ環境とダッシュボードの起動を押しのけていたことが判明している。対処として、駆動系サービスの優先度(nice値)を下げ、ダッシュボードの優先度を上げることで、画面表示を先に通すよう調整した。走行中のCPUに余裕があるかぎり動作の応答性には影響せず、起動中は機体も動かないため安全上の懸念もない変更である。

この調査を通じて得た教訓は、「立ち上がらない」ように見える現象でも、ログの各行のタイムスタンプを引き算してみると、実際には壊れているのではなく単に遅いだけだと分かることがある、という点である。

キオスク画面に被る余計なUIとの戦い

キオスク表示を作り込む過程では、機能面のバグとは別に、画面に意図せず表示される付随的なUI要素とも個別に向き合う必要があった。

もっとも危険だったのは、この翻訳ポップアップを消そうとして踏みかけた回り道である。ブラウザの設定ファイルを狙って自前で先に作っておくというアプローチを試したところ、ブラウザ自身がそのファイルを不正なプロファイルとみなして即座に終了するようになり、システム側の自動再起動設定と相まって無限に再起動を繰り返すループに陥った。設定ファイルは必ずブラウザ自身に生成させ、外部から手書きで作ってはならないという教訓が残っている。

ファームウェア書き込み時の運用上の注意

エッジ機上のsystemdサービスはESP32との通信ノードを自動的に再起動し続ける設定になっているため、ファームウェアの書き込み中にこのサービスを止めずに行うと、シリアルポートを両者が奪い合って書き込みに失敗する。書き込み前にサービスを一時停止し、書き込み完了後に再開する、という手順を運用ルールとして徹底している。

復旧手段のない安全機構という設計上の反省

安全機構を作り込んでいく過程で、「安全機構は正しく発火したのに、現場では解除できない」という事態を複数回経験している。通信途絶を検知する監視ノードは、異常を検知すると非常停止要求を送り続けるフラグを立てるだけで、それを解除する手段を持たない実装だったため、管理者権限でのプロセス再起動以外に復旧できなかった。同様に、自己位置推定ノードが追跡を失っても、プロセス自体は生き続けて壊れた値を出し続け、内部状態をリセットするサービスも存在しなかった。

復旧に管理者権限が必要な安全機構は、現場でいずれ電源ごと落とされることになり、かえって安全性を損なう。 この反省から、非常停止フラグを外部からのリクエストで解除できるようにし、異常が継続している間は次の周期で再度発火する(安全性は緩めない)設計に改めた。自己位置推定ノードについても、監視ノードが異常を検知した際にプロセスごと作り直す構成に変更している。この考え方(「発火したあと、現場の人間が管理者権限なしで戻せるか」)は、他の安全機構を見直す際のチェック項目としても使っている。

誤検知の閾値を実測で見直した経緯

異常検知の閾値は、機体の性能だけを根拠に決めると誤作動の温床になることも実機で学んでいる。自己位置推定と車輪オドメトリの食い違いを検知する閾値は、当初「機体の最高速度の4倍」という一見妥当な値で設定していたが、これは人がロボットを持ち上げて床に置き直すだけの動作で簡単に超えてしまう値だった。実際の異常時(暴走)の推定値は、この閾値の実測値よりさらに一桁以上大きかったため、閾値を実際の異常事例の数値から逆算して引き上げ、日常的な取り扱い動作では誤作動しないよう調整している。

診断作業で繰り返し踏んだ罠

開発・診断の過程で、同じ種類の勘違いを複数回繰り返したことも記録に残している。配信レートが正常に見えても中身の値が壊れている場合があること、トピックの型を確認せずに購読すると無言でメッセージがゼロ件のまま「壊れている」と誤診しがちなこと、プロセス名でのフィルタリングが自分自身にもマッチしてしまうこと、ros2 launchを停止しても子プロセスが残存し二重起動を招くことがあること、1回限りのpublishはDDSの検出処理が完了する前に消えてしまうことがあることなど、いずれも「壊れているように見えるが実際には別の理由で観測できていないだけ」というパターンで共通している。特に、間接的な計測を重ねるより、実際に可視化して目で見た方が原因特定が速いという教訓は、このダッシュボード自体を作る動機にもなっている。

参考リンク

#ROS2