複数ロボット¶
1 つのシミュレータで、同じワールドに複数台のカチャカを動かせます。ロボットごとに ROS 2 の名前空間、TF フレーム、kachaka-api のポートが分かれ、
map 座標系は全台で共通です。fleet.launch.py は保存した地図の上で全台の Nav2 を起動します。各ロボットは LiDAR でほかのロボットを見て、
コストマップにもほかのロボットのフットプリントを持ちます。

1 つの地図の上で 2 台の経路が交差する(scripts/record_demo.sh fleet)。
クイックスタート¶
既定の部屋に 2 台を置き、共通の地図でナビゲーションします。python3 と ros2 のコマンドは ROS 2 のコンテナ
(docker compose -f docker/compose.yaml run --rm shell)で実行します。
# 1. 部屋の地図。map 座標系の原点はロボット 0 のスポーン位置(maps/room.yaml)
python3 scripts/make_maps.py --spawn -1.7 0.2 0
# 2. 2 台のロボットでシミュレータを起動(ホストで)
scripts/run_sim.sh --no-cameras --no-shelves --robot -1.7 0.2 0 --robot 0.3 -1.8 1.5708
# 3. 2 台分の Nav2。ロボット 1 の初期位置を map 座標系で渡す
ros2 launch kachaka_sim_bringup fleet.launch.py map:=/maps/room.yaml robots:=2 initial_poses:=2.0,-2.0,1.5708
# 4. ロボット 1 にゴールを送る
ros2 action send_goal /kachaka_1/navigate_to_pose nav2_msgs/action/NavigateToPose \
'{pose: {header: {frame_id: map}, pose: {position: {x: 2.0, y: 0.5}, orientation: {z: 0.7071, w: 0.7071}}}}'
シミュレータは準備ができる前に、追加したロボットごとに 1 行出力します:
[kachaka] robot 1: ROS namespace '/kachaka_1', frames 'kachaka_1/...', start (2.000, -2.000, 1.571) in the map frame, kachaka-api on port 26401
[kachaka] running: ROS namespace '/kachaka', world 'room', charger at (-2.27, 0.0, 0.0), kachaka-api on port 26400
ここに出る start が initial_poses に渡す値です。
ロボットの配置¶
--robot X Y YAW はロボットを 1 台、ワールド座標で追加します。何回でも指定でき、--x、--y、--yaw の代わりになります。
最初の --robot がロボット 0、次がロボット 1 です。エレベーターのあるレイアウトでは 1 台しか使えません。
名前¶
ロボット 0 は 1 台のときと同じ名前を使うので、1 台用に書いたコードはそのまま動きます。ロボット i(1、2、…)には _i が付きます:
| ロボット 0 | ロボット i | |
|---|---|---|
| プリム | /World/kachaka |
/World/kachaka_i |
| トピック | /kachaka/... |
/kachaka_i/... |
| TF フレーム | odom、base_footprint、… |
kachaka_i/odom、kachaka_i/base_footprint、… |
Nav2(fleet.launch.py 使用時) |
/navigate_to_pose、/initialpose、… |
/kachaka_i/navigate_to_pose、/kachaka_i/initialpose、… |
| kachaka-api | --api-port(26400) |
--api-port + i(26401、…) |
| ノイズのシード | --seed |
--seed + 1000 i |
/clock、/tf、map 座標系は全台で 1 つです。map 座標系の原点はロボット 0 のスポーン位置です。
ナビゲーション¶
fleet.launch.py map:=<yaml> robots:=N は、ロボット 0 に navigation.launch.py(RViz と、ロボット 0 のコマンド・充電器・シェルフのサーバーを含む)を、
ほかのロボットには /kachaka_i 以下に robot_nav.launch.py を起動します。ロボットごとに AMCL・プランナー・コントローラーがあり、
nav2_params.yaml はそのロボットのフレームとトピックに書き換えて使います。
| 引数 | 既定値 | |
|---|---|---|
map |
全台で共通の地図の YAML | |
robots |
2 | シミュレータが動かすロボットの台数 |
initial_poses |
ロボット 1、2、… の x,y,yaw(map 座標系)を続けて並べる |
|
rviz |
true | RViz。ロボットごとにモデルを表示する |
- 初期位置。 ロボット 0 の AMCL は地図の原点、つまりスポーン位置から始まります。
initial_posesでほかのロボットの初期位置を渡すと、 全台が最初から自己位置を持ち、RViz にも表示されます。渡さなかったロボットは、自分の/kachaka_i/initialposeに位置が来るまで待ちます。 - 地図。 フリートを起動する前に作っておきます。部屋やレイアウトなら
make_maps.py、またはロボット 0 だけで slam_toolbox を使います。 map 座標系の原点はロボット 0 のスポーン位置にしてください。複数台で SLAM をする launch ファイルはありません。 - ゴール。 RViz の 2D Goal Pose はロボット 0 に送られます。ほかのロボットには、クイックスタートのようにそれぞれの
navigate_to_poseアクションで、 または自作のノードから送ります。 - ロボット同士。 LiDAR に映るのは、ほかのロボットの幅 24 cm のうち 9 cm だけです(車体から突き出たレーザーのハウジング。それ以外は走査面より低い)。
そこで
fleet_outlinesが、各ロボットの Nav2 のフットプリントを AMCL の位置に置いて、ほかのロボットのコストマップとコリジョンモニターに{namespace}/other_robotsとして渡します(RViz では黄色の輪郭、0.5 秒先は青)。 LiDAR の高さにある車体の部分(ハウジングと後部の出っ張り。公式の車体メッシュの断面から作る)は走査と 2D で交差させるだけで、当たり判定はありません。 そのため、シェルフの底板(0.107 m)は今までどおり車体の上を通ります。 - 交通整理はしません。 各ロボットの Nav2 は互いに調整しません。狭い所で正面から出会った 2 台は、ゴールが中断されるまで互いをふさぐことがあります。 譲り合いはシミュレータの外のフリートマネージャーの仕事です。
ロボットごとの kachaka-api¶
各ロボットはそれぞれのポートで kachaka-api を提供します。ロボット 0 の API はすべて使えます(kachaka-apiを参照)。 コマンド・シェルフ・場所を扱うサーバーはロボット 0 のものなので、ロボット i の API でできることは限られます:
| ロボット i のポートで | 使えるか |
|---|---|
位置とバッテリー(get_robot_pose、get_battery_info) |
使える。位置は fleet.launch.py の起動後は共通の map 座標系、起動前はロボット i の odom 座標系 |
手動操作(set_manual_control_enabled、set_robot_velocity) |
使える |
コマンド(start_command、move_to_pose、rotate_in_place など) |
使えない: エラー −6(サーバーが使えない) |
場所とシェルフ(get_locations、get_shelves) |
使えない: 呼び出しが戻らないので呼ばないこと |
from kachaka_api import KachakaApiClient
robot1 = KachakaApiClient("127.0.0.1:26401")
print(robot1.get_robot_pose())
ロボット 1 以降にないもの¶
- カメラ(前、後ろ、ToF)と物体検出。
- シェルフ: シェルフとそのドッキングはロボット 0 のものです。
- 専用の充電器: ロボット 0 の充電器を共有します。
- ロボット i の
sim/resetはロボット i だけをスポーン位置に戻します。ロボット 0 のリセットはワールドもリセットします。
ドア、自動ドア、動く人はどのロボットにも反応します。
テスト¶
tests/ros_multi_check.py |
向かい合った 2 台: トピック・TF・オドメトリが分かれ、それぞれ自分の命令で動き、互いが見える |
tests/ros_multi_nav_check.py |
1 つの地図の上の 2 台が直角に交差するように走り、触れずに着く |
scripts/smoke_test.sh は前者を、smoke_test.sh --nav は両方を実行します。期待するシミュレータの起動コマンドは各ファイルの docstring にあります。