TT Lab
ブログ

한국어 English 日本語 中文

はじめる
ブログ

ブログ

ROS 2とGazeboでロボットの走行シミュレーション:車輪が語る「完璧」を信じない

ステータス:ドラフト(2026-10-18)、実測。 計測は2026-10-17にホームラボのKubernetesの単一一時Pod上で行いました。ノードはミニPC 1台(Intel Core Ultra系 22スレッド、メモリ 31 GiB)であり、Podの上限はCPU 3コア、メモリ 4 GiBであり、GPUは /dev/dri 割り当てませんでした(特権なし、追加のcapabilityなし)。イメージは ros:jazzy(ros-base、Ubuntu 24.04.5)にaptで ros-jazzy-ros-gz·ros-jazzy-turtlebot3-gazebo を追加しました。バージョンはGazebo Sim 8.15.0(Harmonic)、ros_gz 1.0.24、turtlebot3_gazebo 2.3.7です。繰り返し回数はBが2回、Cが3回、その他は1回であり、表の値は実行ごとに1回測定した値です(中央値ではありません)。生データ(rosbag2 mcap、サマリーJSON、1秒間隔のcgroupサンプル)は作業ディレクトリのROS測定フォルダにあり、記事にはノード名とアドレスをマスクした値のみを記載しています。

目次

1. 直観:目を閉じて歩数を数えるロボット

目を閉じて「前に10歩、左に90度」を4回繰り返せば、元の場所に戻ると簡単に信じ込んでしまいます。歩数は正確に数えたからです。しかし、床が滑りやすいと1歩分が足りなくなり、曲がる時に足が空転すると90度ではなく80度しか曲がりません。歩数しか数えない人はその違いに気づきません。

車輪ロボットの オドメトリ(odom) こそがまさに歩数カウントです。車輪が何回転したかを積分し、「今ここにいる」と言います。シミュレータはこれに1つの要素を追加します。物理エンジンが把握している 実際の位置(正解の姿勢) です。この記事は、2つの値を並べて記録すると何が見えてくるのかを実測値で示します。

2. 構成:1つのPod内でのヘッドレスシミュレーション

파드 하나 안에서 도는 헤드리스 시뮬레이션 쿠버네티스 임시 파드: CPU 상한 3코어, 메모리 상한 4 GiB, GPU 없음 gz sim -s (화면 없음)물리 1 ms, 라이다·IMU ros_gz_bridgegz 토픽 ↔ ROS 2 토픽 주행 노드 (50 Hz)/odom 보고 /cmd_vel 냄 rosbag2 기록기mcap 파일 한 개 /odom /cmd_vel 기록하는 토픽: /odom /cmd_vel /imu /gt_poses(정답 자세) /scan 정답 자세는 gz 의 dynamic_pose 를 브리지로 꺼내 odom 과 나란히 기록합니다. 실행 A: 원본 월드(렌더링 라이다) B: 물리만 C: B + 실시간 제한 해제
図:1つのPod内で動作するヘッドレスシミュレーション:gz sim、ブリッジ、走行ノード、レコーダー

イメージのコストも測定しました。 ros:jazzy は圧縮時283 MiBであり、ダウンロードに18.3秒かかりました。ここにシミュレーションパッケージをaptで追加すると、768個のパッケージ、554 MBをダウンロードしてインストール後に2,637 MB増加し、272秒かかりました。参考として、レジストリのマニフェストでのみ確認した osrf/ros:jazzy-desktop は圧縮時1,203.9 MiB、 jazzy-simulation は698.3 MiBです(ダウンロードしていません)。毎回インストールする時間が惜しいのであれば、このステップをイメージとして固めることが次の作業になります。

3. 4つのワールド

名前 違い
A TurtleBot3の空のワールドのオリジナルです。Sensorsシステム(ogre2)とレンダリングが必要な gpu_lidar があるため、 --headless-rendering で起動しました。
A2 Aに静的な箱を2つ配置しました。空のワールドではLiDARの値がすべて無限大になり、レンダリングが正しく行われているか確認できないためです。
B Sensorsシステムを除外しました。床はインラインの平面であり、摩擦はオリジナルの床と同じくmu 100、mu2 50です。レンダリングもネットワークも使用しません。
C Bから real_time_factor を0に変更し、リアルタイム性に縛られず最大速度で動作させます。

4. 走行ノード:時間ではなくodomを見て移動する

走行ノードは「5秒間直進」のようなオープンループではありません。 /odom を確認しながら角 (1,0)、(1,1)、(0,1)、(0,0) を順番に訪れるクローズドループであり、最大0.2 m/s、0.6 rad/s、シミュレーション時間基準で50 Hzです。実機のウェイポイント走行と同じ方式でなければ、「目標に対する誤差」がコントローラ、オドメトリ、物理演算の合算として読み取れないためです。核心は直進段階の数行です。

if self.phase == 'drive':
    tx, ty = CORNERS[self.leg]
    dx, dy = tx - x, ty - y
    # 진행 방향 위로 남은 거리. 지나치면 음수가 된다.
    seg_heading = HEADINGS[self.leg - 1] if self.leg else 0.0
    along = dx * math.cos(seg_heading) + dy * math.sin(seg_heading)
    if along <= POS_TOL:                      # 5 mm 안이면 멈추고 돌기 시작
        self.send(0.0, 0.0)
        self.mark('corner')
        self.phase = 'turn'
        return
    bearing = math.atan2(dy, dx) if math.hypot(dx, dy) > 0.05 else seg_heading
    w = max(-W_MAX, min(W_MAX, 2.0 * wrap(bearing - th)))
    v = max(0.02, min(V_MAX, 0.8 * along))
    self.send(v, w)

(TT Labの実験コード square_driver.py から抜粋し、コメントは記事に合わせて短縮または追加しました。回転目標は角ごとに絶対方位 0、π/2、π、−π/2 として与え、回転誤差が蓄積しないようにしました。)

5. 結果:リアルタイム性を維持するコストと角の誤差

実行 RTF 1周のウォールクロック 角の実際の誤差 mm (1→4) 終点位置の実際の誤差 odom↔実際の最大 CPU平均 anon最大 gz RSS
A レンダリング LiDAR 1.000 47.5 s 11, 33, 40, 25 16 mm 41 mm 0.91コア 467 MiB 515 MiB
A2 A + 箱 1.000 47.5 s 11, 47, 66, 43 50 mm 74 mm 0.92コア 466 MiB 509 MiB
B 物理のみ 1.000 47.6 s 17, 23, 14, 5 16 mm 26 mm 0.73コア 196 MiB 156 MiB
B 繰り返し 1.000 47.5 s 15, 33, 28, 13 16 mm 32 mm 0.74コア 196 MiB 未測定
C 最高速 11.11 4.3 s 19, 27, 20, 7 16 mm 39 mm 2.88コア 196 MiB 156 MiB
C 繰り返し 2 11.76 4.1 s 20, 28, 20, 8 17 mm 39 mm 2.54コア 196 MiB 未測定
C 繰り返し 3 10.88 4.4 s 19, 28, 20, 7 16 mm 39 mm 2.86コア 196 MiB 未測定

RTF(リアルタイム係数)は、 /odom 冒頭のシミュレーション時間の増加量を、レコーダーが受信したウォールクロック時間の増加量で割った値です。CPUはPodのcgroupの cpu.stat を1秒ごとに読み取って走行区間を平均し、メモリは memory.stat のanonの最大値です。 memory.current は3.0–3.6 GiBと表示されますが、大部分はaptのインストール時に生成されたページキャッシュであるため、実際に占有しているメモリはanonとして確認します。

렌더링을 빼고 실시간 제한을 풀면 한 바퀴 벽시계 시간(s) A 원본(렌더링 라이다) 47.5 A2 A + 상자 두 개 47.5 B 물리만 47.6 C 물리만 + 최대 속도 4.3 CPU 평균(코어, 상한 3) A 원본(렌더링 라이다) 0.91 A2 A + 상자 두 개 0.92 B 물리만 0.73 C 물리만 + 최대 속도 2.88 anon 메모리 최대(MiB) A 원본(렌더링 라이다) 467 A2 A + 상자 두 개 466 B 물리만 196 C 물리만 + 최대 속도 196 C 의 RTF(실시간 대비 속도)는 11.11. 같은 51.7 s 시뮬레이션이 4.3 s 에 끝납니다.
図:レンダリングの有無とリアルタイム制限に応じたウォールクロック時間、CPU、メモリ

6. 床の摩擦の1行を書き忘れた場合

レンダリングのないワールド(B)を最初に作成する際、オリジナルの床モデルをインライン平面に変更したものの、摩擦の設定を記述しませんでした。その結果、物理エンジン(ODE)のデフォルト値であるmu 1が適用されます。修正後の床には次の1行が含まれます。

FRIC = '<surface><friction><ode><mu>100</mu><mu2>50</mu2></ode></friction></surface>'  # 원본 바닥과 같게

下の図は、同じ走行ノードが2つのワールドで走行した記録です。図をクリックすると、rosbagに残った実際の軌跡を4倍速で再生します。

바퀴로 센 위치(odom)와 실제 위치 같은 주행 노드, 같은 1 m 정사각형. 바닥 마찰 한 줄만 다릅니다. ▶ 누르면 재생(4배속) 바닥 마찰을 적은 월드 odom 이 본 끝 위치 오차: 5 mm 실제 끝 위치 오차: 16 mm, 방향 -1.0° 바닥 마찰을 빠뜨린 월드 odom 이 본 끝 위치 오차: 5 mm 실제 끝 위치 오차: 0.50 m, 방향 -38° 실제(시뮬레이터 정답) odom(바퀴 회전으로 적분) 목표 정사각형 두 위치 사이 거리
図:同じ走行ノードが摩擦の異なる2つのワールドに残したodomと実際の軌跡
実行 回転あたりの実際の角度 角の実際の誤差 mm (1→4) 終点位置の実際の誤差 / 向き odomが認識した終点位置の誤差
B(摩擦あり) 89.2, 89.7, 89.7, 89.7° 17, 23, 14, 5 16 mm / −0.98° 5 mm
B 摩擦なし 80.3, 79.7, 80.3, 80.9° 10, 138, 344, 487 503 mm / −37.99° 5 mm
C 摩擦なし 78.0, 79.2, 80.5, 80.9° 19, 164, 399, 533 549 mm / −40.24° 4 mm

その場での回転時に車輪がスリップし、90°ではなく約80°しか回転せず、そのずれが角ごとに蓄積しました。しかし、odomは車輪の関節角度を積分するため、スリップを検知できません。そのため、odomは最後まで「5 mm以内に戻ってきた」と報告しました。もしodomのみで結果を判定していた場合、このワールドはテストをパスしていたことになります。

正解の姿勢が正しいかどうかも個別に確認しました。実行終了後に gz model -m burger -p で問い合わせた位置を /gt_poses の最後の値と比較しました。Cの摩擦なしの実行において、 gz model は(−0.2505, 0.4883) m、向き−40.0°を返し、 /gt_poses は(−0.2505, 0.4884) m、−40.2°でした。摩擦のあるBでは、2つの値は(−0.0130, 0.0099) mで一致していました。

教訓。 シミュレーションでロボットのパフォーマンスを判定する際は、ロボットが自己推定した位置とシミュレータの正解の位置を 一緒に 記録し、判定は正解側を基準に行います。推定位置だけを見ると、物理設定の設定ミスがロボットの成功に見えてしまいます。逆に正解だけを見ると、推定器がどれほどずれているのかが分かりません。両者の差(図の赤い線)がオドメトリのドリフトであり、これが実際のロボットで位置推定器を別途用意する理由です。

7. GPUなしで動作するLiDAR

AとA2はレンダリングによって距離を測定する gpu_lidar(360ビーム、5 Hz)を使用します。PodにはGPUも /dev/dri もありませんでしたが、 --headless-rendering(EGL)で起動し、RTF 1.000を維持しました。

8. CIで使用可能な最高速モード

Cは real_time_factor を0に設定したBです。1 mの正方形1周(シミュレーション時間 47.7–51.7 s)がウォールクロック時間 4.1–4.4 sで終了し、RTFは10.88–11.76でした。

PRごとに「ロボットが1mの正方形を2cmの誤差以内で周回する」テストを4秒間で実行できることを意味します。ただし、その判定はセクション6のように正解姿勢を用いて行う必要があります。

9. 発生した問題

10. 制限事項

11. 次回予告

次回は卓球ロボットに進みます。車輪の代わりにボール1個をMuJoCo上に配置し、反発と回転がシミュレーターの設定1行でどれほど影響を受けるかを計測します。 ロボットアーム自体の基礎(座標系、逆運動学)については、連載中の「卓球ロボットアームの基礎」シリーズ(目次)で取り上げています。

ティーティーの一言。 歩数だけ数えていれば、僕はいつも元の場所に戻ってきているんだ。だけど床が滑りやすいと、実際には半mほど横に立っていたりする。だからシミュレーターでは、必ず「本当の自分の位置」も一緒に記録しておくんだよ!

参考文献

すべてのリンクは2026年10月18日にアクセスし、閲覧できることを確認しました。バージョンは計測に使用したものです。

ログインするといいねできます

コメント

まだコメントはありません。

ログインするとコメントできます