Tbot 3Dナビシャーシ SDK 開発ドキュメント
rosbridge WebSocket ベース、ROS環境不要でクロス言語制御。各 Topic / Service のリクエスト・レスポンス項目(型+説明)を完全掲載。
接続:
共通レスポンス形式:各 Service 呼び出し後、rosbridge は service_response を返します。下記「レスポンス」の JSON は values の内容です。
ws://192.168.1.151:9090共通レスポンス形式:各 Service 呼び出し後、rosbridge は service_response を返します。下記「レスポンス」の JSON は values の内容です。
開発者向けリソース
1. プロトコル概要
| 方向 | 数 | 名前 |
|---|---|---|
| 購読 Topic(シャーシ→アプリ) | 5 | /fast_lio_localization、/aft_pgo_path、/map_list_kitty、/ebot_goal_status_local_planner、/ebot_work_state |
| 呼び出し Service(アプリ→シャーシ) | 9 | /start_mapping_kitty、/save_map_kitty、/select_map_and_start_kitty、/delete_map_kitty、/docking_kitty、/navigate_to_pose(ナビゲーション + キャンセル)、/add_goal_kitty、/delete_goal_kitty |
2. Topic購読(シャーシ → アプリ)
購読します。受信メッセージは msg フィールドにあります。
3. Service一覧
| Service | サービス型 | 主なフィールド |
|---|---|---|
| /start_mapping_kitty | std_srvs/Trigger | 空リクエスト → success,message |
| /save_map_kitty | kitty_runtime_manager/SaveMap | filepath → success,message |
| /select_map_and_start_kitty | kitty_runtime_manager/SaveMap | filepath → success,message |
| /delete_map_kitty | kitty_runtime_manager/DeleteMap | map_name → success,message |
| /docking_kitty | std_srvs/Trigger | 空リクエスト → success,message |
| /navigate_to_pose | nav_msgs/GetPlan | goal.pose.position.x/y/z → plan.poses[] |
| /add_goal_kitty | kitty_runtime_manager/AddGoal | map_name,goal_name,x,y,theta → success,message |
| /delete_goal_kitty | kitty_runtime_manager/DeleteGoal | map_name,goal_name → success,message |
2.1 ロボット位置 nav_msgs/Odometry
シャーシが現在の姿勢を定期的に送信します。
受信 msg:
{
"pose": { "pose": {
"position": {"x": 1.23, "y": 4.56, "z": 0.0},
"orientation": {"x": 0, "y": 0, "z": 0.5, "w": 0.866}
}}
}| フィールド | 型 | 説明 |
|---|---|---|
| pose.pose.position.x | float64 | ロボット x 座標(m) |
| pose.pose.position.y | float64 | ロボット y 座標(m) |
| pose.pose.position.z | float64 | ロボット z 座標(m、シャーシ座標系では常に 0) |
| pose.pose.orientation.x/y/z/w | float64 | 四元数による姿勢。フロントエンドでは yaw = atan2(2*(w*z+x*y), 1-2*(z*z+y*y)) でラジアン角を算出 |
2.2 グローバルパス nav_msgs/Path
ナビ開始後約5秒ごとに計5回送信(約30秒)。
受信 msg:
{ "poses": [
{"pose":{"position":{"x":0,"y":0,"z":0}}},
{"pose":{"position":{"x":1,"y":2,"z":0}}}
]}| フィールド | 型 | 説明 |
|---|---|---|
| poses | array[PoseStamped] | 計画パスの姿勢配列 |
| poses[].pose.position.x | float64 | パス点 x 座標(m) |
| poses[].pose.position.y | float64 | パス点 y 座標(m) |
| poses[].pose.position.z | float64 | パス点 z(常に 0) |
2.3 マップリスト kitty_runtime_manager/MapList
購読後、全マップと保存済みウェイポイントを返します。
受信 msg:
{ "maps": [
{"map_name":"Lobby","map_path":"/maps/floor1",
"goals":[
{"name":"Reception","x":1.0,"y":2.0,"theta":0.5},
{"name":"Elevator","x":3.0,"y":4.0,"theta":1.2}
]}
]}| フィールド | 型 | 説明 |
|---|---|---|
| maps | array[Map] | マップ配列 |
| maps[].map_name | string | マップ表示名 |
| maps[].map_path | string | マップファイルパス(内部) |
| maps[].goals | array[Goal] | 保存済みウェイポイント |
| maps[].goals[].name | string | ウェイポイント名 |
| maps[].goals[].x | float64 | ウェイポイント x(m) |
| maps[].goals[].y | float64 | ウェイポイント y(m) |
| maps[].goals[].theta | float64 | ウェイポイント向き(ラジアン) |
2.4 ナビ状態 std_msgs/Float64MultiArray
Topic:/ebot_goal_status_local_planner。
受信 msg:
{"data": [1]}| フィールド | 型 | 説明 |
|---|---|---|
| data[0] | float64 | 1 = ゴール到着;0 = 未到着。ドッキングでは実際に充電開始した時のみ 1 を送信 |
2.5 バッテリー・充電状態 std_msgs/Float64MultiArray
Topic:/ebot_work_state。
受信 msg:
{"data": [2, 75.5]}| フィールド | 型 | 説明 |
|---|---|---|
| data[0] | float64 | 充電状態:1=充電中 2=未充電 |
| data[1] | float64 | バッテリー残量(0-100) |
3.1 マッピング開始 std_srvs/Trigger
サービス:/start_mapping_kitty
リクエスト:
{}(空リクエスト)レスポンス values:
{ "success": true, "message": "Mapping started" }| フィールド | 型 | 方向 | 説明 |
|---|---|---|---|
| success | bool | レスポンス | 操作成功したか |
| message | string | レスポンス | 結果メッセージ(失敗時は原因) |
3.2 マップ保存 kitty_runtime_manager/SaveMap
サービス:/save_map_kitty
リクエスト:
{ "filepath": "map_name" }レスポンス values:
{ "success": true, "message": "Map saved" }| フィールド | 型 | 方向 | 説明 |
|---|---|---|---|
| filepath | string | リクエスト | map_name(パスなし) |
| success | bool | レスポンス | 操作成功したか |
| message | string | レスポンス | 結果メッセージ |
3.3 マップ選択&ナビ開始 kitty_runtime_manager/SaveMap
サービス:/select_map_and_start_kitty
リクエスト:
{ "filepath": "map_name" }レスポンス values:
{ "success": true, "message": "Map selected and navigation started" }| フィールド | 型 | 方向 | 説明 |
|---|---|---|---|
| filepath | string | リクエスト | map_name(パスなし) |
| success | bool | レスポンス | 操作成功したか |
| message | string | レスポンス | 結果メッセージ |
3.4 マップ削除 kitty_runtime_manager/DeleteMap
サービス:/delete_map_kitty
リクエスト:
{ "map_name": "map_name" }レスポンス values:
{ "success": true, "message": "Deleted successfully" }| フィールド | 型 | 方向 | 説明 |
|---|---|---|---|
| map_name | string | リクエスト | 削除するmap_name |
| success | bool | レスポンス | 操作成功したか |
| message | string | レスポンス | 結果メッセージ |
3.5 充電ドックへ std_srvs/Trigger
サービス:/docking_kitty
リクエスト:
{}レスポンス values:
{ "success": true, "message": "Docking started" }| フィールド | 型 | 方向 | 説明 |
|---|---|---|---|
| success | bool | レスポンス | Docking開始フラグ(充電開始ではない) |
| message | string | レスポンス | 結果メッセージ |
3.6 指定位置へナビ nav_msgs/GetPlan
サービス:/navigate_to_pose
リクエスト:
{
"goal": { "pose": { "position": { "x": 1.0, "y": 2.0, "z": 0.0 } } }
}レスポンス values:
{ "plan": { "poses": [
{"pose":{"position":{"x":0,"y":0,"z":0}}},
{"pose":{"position":{"x":1,"y":2,"z":0}}}
]}}| フィールド | 型 | 方向 | 説明 |
|---|---|---|---|
| goal.pose.position.x | float64 | リクエスト | ウェイポイント x(m) |
| goal.pose.position.y | float64 | リクエスト | ウェイポイント y(m) |
| goal.pose.position.z | float64 | リクエスト | ウェイポイント z(常に 0) |
| plan.poses | array[PoseStamped] | レスポンス | 計画パス配列(描画可) |
ご注意到着確認は本レスポンスではなく、ebot_goal_status を購読し data[0]==1 で判定します。
3.7 ナビキャンセル nav_msgs/GetPlan
サービス:/navigate_to_pose((ナビと同じService、特殊座標でキャンセル))
リクエスト:
{
"goal": { "pose": { "position": { "x": 999999, "y": 888888, "z": 0 } } }
}レスポンス values:
{ "plan": { "poses": [] } }| フィールド | 型 | 方向 | 説明 |
|---|---|---|---|
| goal.pose.position.x | float64 | リクエスト | 固定 999999(キャンセルフラグ) |
| goal.pose.position.y | float64 | リクエスト | 固定 888888(キャンセルフラグ) |
| goal.pose.position.z | float64 | リクエスト | 固定値 0 |
| plan.poses | array | レスポンス | 空または切り捨てパス |
3.8 ウェイポイント追加 kitty_runtime_manager/AddGoal
サービス:/add_goal_kitty
リクエスト:
{
"map_name": "Lobby",
"goal_name": "Reception",
"x": 1.0, "y": 2.0, "theta": 0.5
}レスポンス values:
{ "success": true, "message": "Added successfully" }| フィールド | 型 | 方向 | 説明 |
|---|---|---|---|
| map_name | string | リクエスト | map_name |
| goal_name | string | リクエスト | ウェイポイント名 |
| x | float64 | リクエスト | ウェイポイント x(m) |
| y | float64 | リクエスト | ウェイポイント y(m) |
| theta | float64 | リクエスト | 向き角(ラジアン) |
| success | bool | レスポンス | 操作成功したか |
| message | string | レスポンス | 結果メッセージ |
3.9 ウェイポイント削除 kitty_runtime_manager/DeleteGoal
サービス:/delete_goal_kitty
リクエスト:
{ "map_name": "Lobby", "goal_name": "Reception" }レスポンス values:
{ "success": true, "message": "Deleted successfully" }| フィールド | 型 | 方向 | 説明 |
|---|---|---|---|
| map_name | string | リクエスト | map_name |
| goal_name | string | リクエスト | ウェイポイント名 |
| success | bool | レスポンス | 操作成功したか |
| message | string | レスポンス | 結果メッセージ |
4. マルチ言語開発例
各言語は簡単な例を表示。「完全なコードを表示」で全5 Topic + 9 Service の完全版を展開します。
4.1 Python(roslibpy)
依存関係:pip install roslibpy
import roslibpy, time
client = roslibpy.Ros(host='192.168.1.151', port=9090)
client.run()
# Subscribe to battery state
roslibpy.Topic(client,'/ebot_work_state','std_msgs/Float64MultiArray').subscribe(
lambda m: print(f"Battery {m['data'][1]}% charging={m['data'][0]==1}"))
# Dock to charger
roslibpy.Service(client,'/docking_kitty','std_srvs/Trigger').call(roslibpy.ServiceRequest(),
callback=lambda r: print("Docking:", r['message']))
try:
while True: time.sleep(1)
except KeyboardInterrupt: client.terminate()
import roslibpy, time, math
client = roslibpy.Ros(host='192.168.1.151', port=9090)
client.run()
# ===== Subscribe to all 5 Topics =====
# 1) Localization
def on_odom(m):
p = m['pose']['pose']['position']; o = m['pose']['pose']['orientation']
yaw = math.atan2(2*(o['w']*o['z']+o['x']*o['y']), 1-2*(o['z']**2+o['y']**2))
print(f"[Pose] x={p['x']:.2f} y={p['y']:.2f} yaw={math.degrees(yaw):.1f}°")
roslibpy.Topic(client,'/fast_lio_localization','nav_msgs/Odometry').subscribe(on_odom)
# 2) Global path
roslibpy.Topic(client,'/aft_pgo_path','nav_msgs/Path').subscribe(
lambda m: print(f"[Path] {len(m['poses'])} points"))
# 3) Map list
roslibpy.Topic(client,'/map_list_kitty','kitty_runtime_manager/MapList').subscribe(
lambda m: [print(f"[Map] {mp['map_name']} goals:{[g['name'] for g in mp['goals']]}") for mp in m['maps']])
# 4) Navigation status
roslibpy.Topic(client,'/ebot_goal_status_local_planner','std_msgs/Float64MultiArray').subscribe(
lambda m: m['data'][0]==1 and print("✅ Arrived"))
# 5) Battery
roslibpy.Topic(client,'/ebot_work_state','std_msgs/Float64MultiArray').subscribe(
lambda m: print(f"[Battery] {m['data'][1]:.0f}% {'Charging' if m['data'][0]==1 else 'Not charging'}"))
# ===== Call all 9 Services =====
def call(svc, args, srv):
r = roslibpy.Service(client, svc, srv).call(roslibpy.ServiceRequest(args))
print(f"[SVC] {svc} → {dict(r)}")
time.sleep(1)
call('/start_mapping_kitty', {}, 'std_srvs/Trigger')
call('/save_map_kitty', {'filepath':'test_map'}, 'kitty_runtime_manager/SaveMap')
call('/select_map_and_start_kitty', {'filepath':'test_map'}, 'kitty_runtime_manager/SaveMap')
call('/navigate_to_pose', {'goal':{'pose':{'position':{'x':1.0,'y':2.0,'z':0.0}}}}, 'nav_msgs/GetPlan')
call('/navigate_to_pose', {'goal':{'pose':{'position':{'x':999999,'y':888888,'z':0}}}}, 'nav_msgs/GetPlan')
call('/add_goal_kitty', {'map_name':'test_map','goal_name':'Reception','x':1.0,'y':2.0,'theta':0.5}, 'kitty_runtime_manager/AddGoal')
call('/delete_goal_kitty', {'map_name':'test_map','goal_name':'Reception'}, 'kitty_runtime_manager/DeleteGoal')
call('/docking_kitty', {}, 'std_srvs/Trigger')
call('/delete_map_kitty', {'map_name':'test_map'}, 'kitty_runtime_manager/DeleteMap')
try:
while True: time.sleep(1)
except KeyboardInterrupt: client.terminate()
4.2 JavaScript / HTML
依存なし、HTML
