xArm Robot¤
XArmRobot は UFactory xArm7 を GELLO (Dynamixel ベースのテレオペコントローラ) で操作する leader/follower 構成のロボットです。RakudaRobot / KochRobot と同じ ComposedRobot インターフェースを実装しており、connect/teleoperation/record/record_parallel が同じ手順で使えます。
構成要素¤
ロボットアーム¤
- Leader Arm (
XArmLeader): GELLO Dynamixel テレオペコントローラ。robopy のDynamixelBus経由で接続され、GELLO submodule 依存はありません。 - Follower Arm (
XArmFollower): UFactory xArm7 本体。xarm-python-sdkで TCP/IP 経由接続、50 Hz の背景スレッドで速度制限付きに指令を送ります。 - Pair System (
XArmPairSys): Leader→Follower 伝搬と 0.8 rad 不整合チェック + 25 ステップ安全アライメントを実行。
センサー統合¤
- カメラ: Intel RealSense
- タクタイル: DIGIT
- オーディオ: PyAudio
セットアップ¤
xArm 本体および GELLO リーダーを robopy から利用するまでのハードウェア・ソフトウェア準備手順です。既にセットアップ済みのロボットを借用する場合は、本節をスキップして構いません。
出典
本節の手順は xArm_Modules リポジトリ (https://github.com/keio-crl/xArm_Modules) の README に準拠しています。
1. 環境 (uv)¤
robopy は uv で管理されています。未インストールの場合は公式インストーラまたは pip で導入してください。
インストール後、uv コマンドが実行できることを確認します。
2. GELLO ハードウェア (Dynamixel モーター ID 割り当て)¤
GELLO 本体の組み立てと、Dynamixel モーターへの ID 割り当てを行います。
- GELLO の各種部品 (3D プリント品・購入品) を用意して組み立てます。
- Dynamixel Wizard を導入します。インストールガイドは ROBOTIS 公式マニュアルを参照してください: https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_wizard2/ インストール時に記載されているコマンドはすべて実行してください (飛ばすとエラーの原因になります)。
- Dynamixel Wizard を起動し、まず U2D2 単体 で接続確認を行います。
- 一度完全に切断しソフトも閉じた上で、モーターを 1 つだけ U2D2 に接続 → ソフト再起動 → ポートスキャンを行い、検出されたモーターに ID 1 を割り当てます。
- 以降、同様の手順で 2 個目以降のモーターにも順次 ID を割り当てます。
- 全モーターに ID を割り当て終えたら、通常手順で GELLO 本体へ組み込みます。
3. GELLO ソフトウェアキャリブレーション¤
GELLO 本体を PC に接続した状態で、シリアル ID を確認します。
表示された ID を控えておきます。
次に、GELLO 用の gello_get_offset.py で各モーターのオフセットとグリッパ開閉角度を取得します (gello_software リポジトリのスクリプトを使用)。
python scripts/gello_get_offset.py \
--start-joints 0 0 0 0 0 0 0 \
--joint-signs 1 1 1 1 1 1 1 \
--port /dev/serial/by-id/<先ほど控えた ID>
実行すると、次のような出力が得られます。
best offsets : ['3.142' ..... ]
best offsets function of pi : [2*np.pi/2, ..... ]
gripper open(degrees) 202.xxxxxx
gripper close(degrees) 160.xxxxxx
これらの値は、GELLO で読み取ったモーター値を xArm に転送する際の補正量です。gello/agents/gello_agent.py の PORT_CONFIG_MAP に以下のようなエントリを追加してください (閉じ } の直前)。
"/dev/serial/by-id/<控えた ID>": DynamixelRobotConfig(
joint_ids=(1, 2, 3, 4, 5, 6, 7),
joint_offsets=(
# best offsets function of pi をそのまま並べる
2 * np.pi / 2,
...
),
joint_signs=(1, 1, 1, 1, 1, 1, 1),
# gripper_config は (モーター ID, gripper open, gripper close) で、全て整数
gripper_config=(8, 202, 160),
),
robopy 側での利用
robopy ではこれらの値を GelloArmConfig (joint_offsets / joint_signs / gripper_open_deg / gripper_close_deg) に設定します。
4. シミュレータ上での動作確認¤
GUI アプリを起動するため、SSH 接続ではなくロボット実機が繋がっている PC 上で 以下を実行します。ターミナルを 2 つ開いてください。
1 つ目 — シミュレータ起動:
GUI が開いたら、もう一方のターミナルで:
Going to start position で実行が止まる場合は、GELLO の姿勢がホームポジションから外れているのが原因です。できるだけホームに近い姿勢へ手で戻してからやり直してください。
実行が進むと、シミュレータ上の xArm が GELLO に追従します。モーターの向きや動きが明らかにおかしい場合は、gello/agents/gello_agent.py の joint_offsets を直接微調整してください。
(例: モーター ID 4 が 90° ずれていたケースでは、ラジアンで 1/2 π ずれているため joint_offsets の 4 番目から np.pi / 2 を引いて補正)
ここまで一致させれば、GELLO キャリブレーションは完了です。
5. xArm 本体の接続¤
5.1 ケーブル類¤
- xArm 本体とコントローラ Box を 2 本のケーブルで接続します。
- Box の電源ケーブルを接続します。
- 上部の非常停止スイッチを回して押せる状態にします。
- LAN ケーブルは、PC に直結ではなくルーター (またはハブ) に接続します (純正の取扱説明書は PC 直結を指示していますが、無視してください)。
- Box の電源ボタンを押して起動します。
5.2 WebUI での疎通確認¤
コントローラ Box の LAN 端子の下に貼られているシールで、Box の IP アドレスを確認します (192.168.1.XXX の形式)。
ブラウザで以下にアクセスすると UFactory WebUI が起動し、xArm の状態確認・操作が行えます。
ここまで疎通できれば、xArm のハードウェア側セットアップは完了です。XArmConfig(follower_ip="192.168.1.XXX", ...) としてこの IP を指定すれば、robopy から接続できます。
6. カメラ権限 (RealSense / Web カメラ)¤
RealSense や USB Web カメラから画像が取得できない場合、/dev/video* の権限不足が原因であることが多いです。全てのカメラを接続した状態で次を実行してください (sudo 権限が必要)。
基本的な使用方法¤
設定の作成¤
import numpy as np
from robopy.config.robot_config import XArmConfig, XArmWorkspaceBounds
config = XArmConfig(
follower_ip="192.168.1.240", # xArm 本体の IP
leader_port=None, # None なら /dev/serial/by-id から自動検出
workspace_bounds=XArmWorkspaceBounds(), # mm 単位のクリッピング
start_joints=np.deg2rad([0, -90, 90, -90, -90, 0, 0]).astype(np.float32),
control_frequency=50.0, # xArmAPI 制御スレッド Hz
max_delta=0.05, # 1 ステップあたり最大ジョイント移動量 [rad]
)
ロボットの操作¤
from robopy.robots.xarm import XArmRobot
robot = XArmRobot(config)
try:
robot.connect()
robot.teleoperation(max_seconds=30.0) # GELLO → xArm を 30 秒間
finally:
robot.disconnect()
作業領域の制限 (restriction プリセット)¤
毎回 XArmWorkspaceBounds(min_x=..., max_x=...) を組み立てる代わりに、名前付きプリセットを XArmConfig.restriction に列挙して制約を切り替えられます。
from robopy.config.robot_config import XArmConfig
config = XArmConfig(
follower_ip="192.168.1.240",
restriction=["default", "drawer"], # 複数指定すると積集合 (intersection)
)
XArmFollower は XArmWorkspaceBounds と等価な mm 単位の Cartesian クリップを行います。restriction のプリセット名は resolve_workspace_bounds で解決され、複数指定時は すべての box の積集合 (各軸で max of mins / min of maxes) が適用されます。workspace_bounds を併用した場合も同じ集合に含まれます。
提供プリセット¤
XARM_WORKSPACE_PRESETS に定義されています (mm 単位、xArm ベース座標)。
| 名前 | 用途 | 範囲 (mm) |
|---|---|---|
default |
legacy xarm_modules/config/robot_area.yaml 由来の広い既定値 |
x=[390.4, 1e5], y=[-257.5, 314.0], z=[25.0, 1e4] |
drawer |
引き出し操作向けの保守的な箱 | x=[400, 700], y=[-200, 200], z=[50, 400] |
プリセットの追加¤
src/robopy/config/robot_config/xarm_config.py の XARM_WORKSPACE_PRESETS 辞書にエントリを追加するだけで、restriction=["my_task"] で参照できるようになります。
XARM_WORKSPACE_PRESETS["tabletop"] = XArmWorkspaceBounds(
min_x=380.0, max_x=700.0,
min_y=-300.0, max_y=300.0,
min_z=10.0, max_z=300.0,
)
未知のプリセット名・矛盾する組み合わせ (積集合が空) を渡すと、XArmFollower インスタンス化時に ValueError が発生します。
部分領域での z 下限の引き上げ (XArmZFloorZone)¤
XArmWorkspaceBounds の各軸クリップは AABB 1 個分しか表現できないため、 「x がこの範囲のときだけ z の下限を上げたい」 (例: 引き出し上面に当てたくない) のような条件付きの制約は、XArmWorkspaceBounds.z_floor_zones に XArmZFloorZone を渡して表現します。
ゾーンは (x, y) 平面上の軸並行矩形 + その範囲内での min_z で定義します。クリップ時、エンドエフェクタの (x, y) 位置がいずれかのゾーンに入っていれば、そのゾーンの min_z がグローバル min_z を上書きします (どのゾーンにも入らなければグローバル値そのまま)。
from robopy.config.robot_config import (
XArmConfig, XArmWorkspaceBounds, XArmZFloorZone,
)
# 例: ベース正面 x∈[450, 650], y∈[-150, 150] mm に drawer がある。
# その footprint 内では z は drawer 上面 + クリアランス (220 mm) 以上に保ち、
# それ以外の領域では通常の min_z (25 mm) のまま自由に動かしたい。
drawer_keepout = XArmZFloorZone(
min_x=450.0, max_x=650.0,
min_y=-150.0, max_y=150.0,
min_z=220.0,
)
config = XArmConfig(
follower_ip="192.168.1.240",
workspace_bounds=XArmWorkspaceBounds(
z_floor_zones=(drawer_keepout,),
),
)
クリップ順序は xarm_follower._set_position / _send_cartesian で次のように適用されます。
pose[0] = clip(pose[0], min_x, max_x)
pose[1] = clip(pose[1], min_y, max_y)
min_z = workspace.effective_min_z(pose[0], pose[1]) # ← ゾーン判定はクリップ後の (x, y) で
pose[2] = clip(pose[2], min_z, max_z)
複数ゾーンを同時に登録でき、(x, y) が複数のゾーンに同時に入る場合は min_z の 最大値 が採用されます (常に最も保守的)。restriction プリセット側で定義した z_floor_zones と、明示的な workspace_bounds で渡したゾーンは union (連結) で合成されます (AABB は intersection ですが、ゾーンは独立した障害物表現なので片方だけ残すと意味が壊れるため)。
| 制約の種類 | データ構造 | 合成方法 |
|---|---|---|
| 全体の AABB | XArmWorkspaceBounds の min_* / max_* |
各 box の 積集合 (max of mins / min of maxes) |
| 部分領域の z 下限 | z_floor_zones: Tuple[XArmZFloorZone, ...] |
全 box から 連結 (union)、評価時は (x, y) を含むゾーンの max |
XArmZFloorZone の min_x > max_x などフットプリントが反転している場合は、構築時に ValueError が発生します。
斜めの障害物・円筒形の障害物
現状の XArmZFloorZone フットプリントは軸並行矩形 (AABB) のみです。斜めの drawer や円筒形の柱を表現したい場合は、複数の小さなゾーンで近似するか、フットプリント表現を多角形 (凸) に拡張する必要があります。
データ収集¤
xArm では legacy xarm_modules/saver.py 相当のデータ収集を XArmRobot の 3 種類の record* API で行います。RakudaExpHandler のような専用ハンドラはまだ無いため、記録は XArmRobot を直接使い、保存は numpy で手動という形が基本パターンです。
API 一覧¤
| メソッド | 用途 | リーダー | Follower | sensors |
|---|---|---|---|---|
record |
シーケンシャル記録 (低 fps, シンプル) | GELLO ライブ | GELLO 追従 | 毎フレーム同期読み |
record_parallel |
並列記録 (推奨、高 fps) | GELLO ライブ | GELLO 追従 | スレッドプール並列読み |
record_with_fixed_leader |
記録済み leader 軌道で再生しつつ収集 | 配列から補間再生 | 追従 | 並列読み |
send |
記録なしで軌道を再生のみ | 配列から補間再生 | 追従 | 読まない |
記録データの構造 (XArmObs)¤
obs.arms.leader # (frames, 8) — 7 joints [rad] + gripper [0, 1]
obs.arms.follower # (frames, 8) — 同上 (xArm 本体 or sim の状態)
obs.arms.ee_pos_quat # (frames, 7) — [x, y, z (m), qx, qy, qz, qw]
obs.sensors.cameras # Dict[str, (frames, H, W, 3) | None] — RealSense RGB
obs.sensors.tactile # Dict[str, (frames, 3, H, W) | None] — DIGIT (CHW に transpose 済)
obs.sensors.audio # Dict[str, (frames, channels, samples) | None]
センサーは XArmConfig.sensors に設定したもののみ、辞書に 名前キーで入ります。未接続センサーは None、XArmConfig.sensors=None なら辞書は空です。
シーケンシャル記録 (record)¤
メインスレッドで「テレオペ 1 ステップ → センサー読み」を交互に行う単純ループ。低 fps の動作確認に向きます。センサー処理時間が長いと fps が維持できないため、高 fps には record_parallel を使ってください。
並列記録 (record_parallel) — 推奨¤
テレオペを独立スレッドで teleop_hz Hz で動かし、メインループは fps Hz でキュー最新値 + センサーを取得します。センサー読みも ThreadPoolExecutor で並列化。
obs = robot.record_parallel(
max_frame=500,
fps=20, # 記録 (保存) のフレームレート
teleop_hz=100, # 内部テレオペループの Hz (fps 以上にする)
max_processing_time_ms=40.0, # 1 フレーム処理の上限 (超えるとスキップ検知)
)
teleop_hz >= fpsが前提。通常teleop_hz = 4 × fps程度が安定。max_processing_time_msを超えるとskipped_framesにカウントされ、終了時にlogger.infoで出ます。sensor が重い場合はここを上げる。- ログ:
record_parallel completed: frames=N, skipped=K, avg_proc=XXms
軌道再生つき収集 (record_with_fixed_leader)¤
過去に記録した leader 軌道 (max_frame, 8) を時間線形補間しつつ follower に送り、実機応答 + センサーを記録します。方策評価・再現実験に使います。
import numpy as np
# 過去データの leader 軌道を読み込み (例: 前回の record_parallel で保存した配列)
prior = np.load("data/trajectory_001/leader.npy") # (N, 8)
obs = robot.record_with_fixed_leader(
max_frame=prior.shape[0],
leader_action=prior,
fps=20,
teleop_hz=100,
)
記録なし再生 (send)¤
保存は不要だが軌道だけ再生したい場合 (デモ、微調整ループなど):
終了時に Rich の Table で送信フレーム数・実効 Hz を出力します。
センサー設定つきの完全例¤
XArmConfig.sensors に XArmSensorParams を渡すと、connect() 内でカメラ・触覚・音声を初期化し、記録時に同期取得されます。
import numpy as np
from robopy.config.robot_config import (
XArmConfig, XArmSensorParams, XArmWorkspaceBounds,
)
from robopy.config.sensor_config.params_config import CameraParams
from robopy.robots.xarm import XArmRobot
config = XArmConfig(
follower_ip="192.168.1.240",
workspace_bounds=XArmWorkspaceBounds(),
start_joints=np.deg2rad([0, -90, 90, -90, -90, 0, 0]).astype(np.float32),
sensors=XArmSensorParams(
cameras=[
CameraParams(name="wrist", width=640, height=480, fps=30),
CameraParams(name="env", width=640, height=480, fps=30),
],
),
)
robot = XArmRobot(config)
try:
robot.connect()
obs = robot.record_parallel(max_frame=300, fps=20, teleop_hz=80)
finally:
robot.disconnect()
from robopy.config.sensor_config.params_config import (
CameraParams, TactileParams, AudioParams,
)
config = XArmConfig(
follower_ip="192.168.1.240",
sensors=XArmSensorParams(
cameras=[CameraParams(name="wrist", width=640, height=480, fps=30)],
tactile=[TactileParams(serial_num="D20542", name="tip", fps=30)],
audio=[AudioParams(name="mic", sample_rate=16000, channels=1)],
),
)
記録データの保存¤
xArm 向け専用の保存ハンドラは現状未実装です。legacy saver.py と同じ形式で保存するなら、以下のようにして numpy で書き出します。
from pathlib import Path
import numpy as np
def save_xarm_obs(obs, save_dir: str | Path, seq_id: int = 1) -> None:
"""legacy xarm_modules/saver.py の出力形式に合わせて保存する。
Layout:
save_dir/action_state_001.npy # (T, 8) leader (= teleop 時の GELLO)
save_dir/observation_state_001.npy # (T, 8) follower (xArm 実際の状態)
save_dir/hand_001.npy # (T, 7) ee_pos_quat
save_dir/robot_image_001.npy # (T, H, W, 3) 手先カメラ (あれば)
save_dir/env_image_001.npy # (T, H, W, 3) 環境カメラ (あれば)
save_dir/tactile_<name>_001.npy # (T, 3, H, W)
save_dir/audio_<name>_001.npy # (T, C, N)
"""
save_dir = Path(save_dir)
save_dir.mkdir(parents=True, exist_ok=True)
tag = f"{seq_id:03d}"
np.save(save_dir / f"action_state_{tag}.npy", obs.arms.leader)
np.save(save_dir / f"observation_state_{tag}.npy", obs.arms.follower)
np.save(save_dir / f"hand_{tag}.npy", obs.arms.ee_pos_quat)
if obs.sensors is not None:
for name, arr in obs.sensors.cameras.items():
if arr is not None:
np.save(save_dir / f"camera_{name}_{tag}.npy", arr)
for name, arr in obs.sensors.tactile.items():
if arr is not None:
np.save(save_dir / f"tactile_{name}_{tag}.npy", arr)
for name, arr in obs.sensors.audio.items():
if arr is not None:
np.save(save_dir / f"audio_{name}_{tag}.npy", arr)
呼び出し例:
obs = robot.record_parallel(max_frame=500, fps=20, teleop_hz=100)
save_xarm_obs(obs, "data/experiment_001", seq_id=1)
統合保存ハンドラ (将来)
Rakuda の RakudaExpHandler 相当 (record_save / HDF5 統合 / メタデータ自動付与) が必要な場合は、src/robopy/utils/exp_interface/rakuda_exp_handler.py を参考に xarm_exp_handler.py を追加するのが今後の改善方針です。
性能チューニング¤
fpsを下げる、またはteleop_hzを上げる (テレオペスレッドの更新頻度不足)max_processing_time_msを1/fps * 1000 * 0.8程度に設定して、超過頻度を logger で観察- 重いセンサー (例: 高解像度カメラ 4 台) を同時記録する場合は解像度を落とす
- 終了時ログの
skipped=の割合が 10% 超なら、max_processing_time_msを緩めるか fps を下げる - USB 帯域 (RealSense 複数台) が原因の場合は、USB3 ポートを分散する
XArmConfig.max_deltaを上げると追従速度 ↑ / 安全度 ↓ (既定 0.05 rad/step)XArmConfig.control_frequencyを上げると内部スレッドの指令頻度 ↑ (既定 50 Hz)cartesian_speed/cartesian_mvaccは xArm SDK への引数で、これも上げると速くなる
データ記録の最小例¤
import numpy as np
from robopy.config.robot_config import (
XArmConfig, XArmSensorParams, XArmWorkspaceBounds,
)
from robopy.config.sensor_config.params_config import CameraParams
from robopy.robots.xarm import XArmRobot
def collect() -> None:
config = XArmConfig(
follower_ip="192.168.1.240",
workspace_bounds=XArmWorkspaceBounds(),
start_joints=np.deg2rad([0, -90, 90, -90, -90, 0, 0]).astype(np.float32),
sensors=XArmSensorParams(
cameras=[CameraParams(name="wrist", width=640, height=480, fps=30)],
),
)
robot = XArmRobot(config)
try:
robot.connect()
# 初期姿勢を GELLO で合わせたあと Enter を待つ
input("GELLO を xArm の姿勢に合わせたら Enter で開始...")
obs = robot.record_parallel(
max_frame=500,
fps=20,
teleop_hz=100,
max_processing_time_ms=40.0,
)
print("leader: ", obs.arms.leader.shape)
print("follower:", obs.arms.follower.shape)
print("ee: ", obs.arms.ee_pos_quat.shape)
# save_xarm_obs(obs, "data/experiment_001", seq_id=1)
finally:
robot.disconnect()
if __name__ == "__main__":
collect()
シミュレータ上で先に動作確認する (強く推奨)¤
実機の xArm7 を GELLO で動かす前に、UFactory Studio / UFactory Simulator を使って動作確認 することを強く推奨します。
GELLO 側の挙動 (joint offset, gripper 正規化, 速度制限) やワークスペース境界のクリッピングが意図通りかを、衝突・落下などのリスクなしで検証できます。
シミュレータとは
UFactory Studio は UFactory 公式の無料デスクトップアプリで、実機コントローラ Box と同じ xArm プロトコル (TCP/IP) を話す仮想 xArm を提供します。xarm-python-sdk から見ると実機と区別がつかないため、robopy 側のコードは 一切変更不要 で、follower_ip をシミュレータのアドレスに向けるだけで動きます。
1. UFactory Studio のインストール¤
UFactory 公式ダウンロードページから、利用 OS 向けのインストーラを取得します。
- 公式ダウンロード: https://www.ufactory.cc/download-ufactory-studio/
- 対応 OS: Windows / macOS / Linux (Ubuntu)
- xArm7 を選択してインストール
ヘッドレスサーバで使う場合
Studio GUI が使えない環境 (ヘッドレス Linux など) では、UFactory が提供する xArm Firmware Simulator (Docker) が利用できる場合があります。詳細は UFactory Developer 向けドキュメント (https://github.com/xArm-Developer) を参照してください。
2. Studio 内で Simulation/Virtual Robot を有効化¤
Studio を起動後、Simulation Mode または Virtual Robot を有効にし、ロボットモデルに xArm7 を選択します。Studio の起動画面で IP アドレス (通常は 127.0.0.1 またはホスト PC の LAN IP) が表示されるのでメモします。
UI の詳細は Studio のバージョンによって異なるため、公式マニュアル (Studio User Manual) を参照してください。
3. robopy 側の設定変更¤
XArmConfig.follower_ip をシミュレータ側に向けるだけで、他は実機と同じ設定のまま動作します。
config = XArmConfig(
follower_ip="127.0.0.1", # ← シミュレータの IP に変更
leader_port=None, # GELLO は実機を USB 接続
workspace_bounds=XArmWorkspaceBounds(),
start_joints=np.deg2rad([0, -90, 90, -90, -90, 0, 0]).astype(np.float32),
)
robot = XArmRobot(config)
robot.connect() # 内部で XArmAPI("127.0.0.1", is_radian=True) が呼ばれる
robot.teleoperation(max_seconds=30.0)
robot.disconnect()
4. 接続確認 (sanity check)¤
シミュレータ接続だけを先に検証したい場合は、XArmFollower 単体で確認できます。
from robopy.config.robot_config import XArmConfig
from robopy.robots.xarm import XArmFollower
cfg = XArmConfig(follower_ip="127.0.0.1")
follower = XArmFollower(cfg)
follower.connect()
print("joints:", follower.get_joint_state())
print("ee_pos_quat:", follower.get_ee_pos_quat())
follower.disconnect()
Studio の 3D ビュー上で xArm が表示されていれば、get_servo_angle / get_position_aa の値がそれと一致するはずです。
5. GELLO 連携の検証フロー¤
# 1. GELLO のみ接続、フォロワーはシミュレータ
config = XArmConfig(follower_ip="127.0.0.1")
robot = XArmRobot(config)
robot.connect()
# 2. 1 ステップだけ実行してログを確認
obs = robot.robot_system.teleoperate_step()
print("leader: ", obs.leader) # GELLO の状態
print("follower:", obs.follower) # シミュレータ xArm の状態
# 3. 短時間のテレオペで 3D ビュー上の動きを確認
robot.teleoperation(max_seconds=10.0)
robot.disconnect()
Studio の 3D ビュー上で GELLO の動きに xArm モデルが追従していれば OK です。
実機に切り替える前のチェックリスト¤
シミュレータでの確認後、実機に戻す際は以下を確認してください:
-
follower_ipを実機の IP (既定値"192.168.1.240") に戻した - 非常停止スイッチが手の届く位置にある
-
workspace_boundsが設置環境に対して安全に設定されている -
start_jointsがホームポジション相当になっている -
max_deltaが小さすぎ/大きすぎない (既定 0.05 rad/step) - GELLO と xArm 本体の初期姿勢差が 0.8 rad 未満 (
_align_leader_followerで自動検出) - 初回は
max_seconds=5程度の短時間テレオペで動作確認
シミュレータと実機の差分¤
| 項目 | シミュレータ | 実機 |
|---|---|---|
FK (get_forward_kinematics) |
同じ | 同じ |
set_position / get_servo_angle |
理想追従、遅延ほぼなし | 実際のモータ応答、ギア誤差あり |
set_collision_sensitivity |
Studio 設定に依存 | 実機キャリブレーションに依存 |
set_collision_rebound |
視覚的リバウンドのみ | 実機で物理的に戻る |
グリッパ (get/set_gripper_position) |
Studio 設定により擬似動作 | 実機の電動グリッパ |
| collision / 力覚 | 物理衝突は発生しない | 安全設定に従い停止 |
シミュレータでは物理衝突が発生しないため、GELLO 側で無理な姿勢 (シンギュラリティ直近など) を取っても止まりません。実機では _clear_error_states による復帰ループが走ることがあるため、最初の実機テストは 必ず有人監視下 で行ってください。
最小テレオペ例¤
完全な動作例は examples/robot/xarm_teleoperate.py にあります。
from robopy.config.robot_config import XArmConfig, XArmWorkspaceBounds
from robopy.robots.xarm import XArmRobot
import numpy as np
def xarm_teleoperate() -> None:
config = XArmConfig(
follower_ip="127.0.0.1", # シミュレータ
workspace_bounds=XArmWorkspaceBounds(),
start_joints=np.deg2rad([0, -90, 90, -90, -90, 0, 0]).astype(np.float32),
)
robot = XArmRobot(config)
try:
robot.connect()
robot.teleoperation(max_seconds=30.0)
finally:
robot.disconnect()
if __name__ == "__main__":
xarm_teleoperate()
SpaceMouse によるテレオペ (GELLO 不使用)¤
GELLO リーダーの代わりに 3Dconnexion SpaceMouse で xArm7 をテレオペできます。フルの動作例は examples/robot/xarm_spacemouse_teleop.py を参照してください。
ドライバは robopy.robots.xarm.spacemouse_agent.run_spacemouse_teleop で、so101 と共通の SpaceMouseConfig を受け取ります。
感度の設計¤
SpaceMouse の各軸 raw 値は ±1.0 の範囲で読まれます。teleop ループは制御ステップごとに次の式でデルタを計算して command_cartesian_relative に渡します。
dt = 1 / control_hz
dx_mm = raw[:3] * linear_speed * dt * 1000 # m/s -> mm/step
drx_rad = raw[3:] * angular_speed * dt # rad/s
つまり linear_speed と angular_speed がそのまま 「フル入力時の最大 EE 速度」 = 感度 になります:
- 感度を 下げる (
linear_speed=0.02など): SpaceMouse を最大まで倒しても 0.02 m/s (= 2 cm/s) 以上は出ません。微細作業向け。 - 感度を 上げる (
linear_speed=0.30など): 30 cm/s まで出るので大きく動かしたいときに。 - 中間入力では線形にスケール (raw=0.5 なら最大速度の半分)、
deadzone以下は完全にゼロ。
設定例¤
from robopy.config.input_config.spacemouse_config import SpaceMouseConfig
from robopy.config.robot_config import XArmConfig, XArmWorkspaceBounds
from robopy.robots.xarm.spacemouse_agent import run_spacemouse_teleop
from robopy.robots.xarm.xarm_follower import XArmFollower
# 感度低めの設定 (微細位置決め向け)
sm_cfg = SpaceMouseConfig(
linear_speed=0.03, # 最大 3 cm/s — フルチルトでもゆっくり動く
angular_speed=0.20, # 最大 0.20 rad/s ≒ 11.5 deg/s
deadzone=0.05, # 微小入力は無視
control_hz=50,
input_smoothing=0.7, # 高めにすると揺れが減るが追従が遅れる
)
follower = XArmFollower(XArmConfig(
follower_ip="192.168.1.240",
workspace_bounds=XArmWorkspaceBounds(),
))
follower.connect()
try:
run_spacemouse_teleop(
follower,
cfg=sm_cfg,
gripper_toggle_button=0, # ボタン 0 でグリッパ open/close トグル
max_seconds=None, # Ctrl-C で停止
)
finally:
follower.disconnect()
パラメータの目安¤
| パラメータ | 単位 | 意味 | 既定値 | 調整ヒント |
|---|---|---|---|---|
linear_speed |
m/s | フル入力時の EE 並進速度 (= 並進感度) | 0.10 |
慣れていない場合 0.03 ~ 0.05 から始める |
angular_speed |
rad/s | フル入力時の EE 回転速度 (= 回転感度) | 0.5 |
0.2 ~ 0.3 程度が扱いやすい |
deadzone |
-- | 絶対値がこれ未満の raw 入力をゼロ化 | 0.05 |
手の震えで誤動作するなら上げる |
input_smoothing |
-- (EMA) | 0 = フィルタなし、1 に近いほど滑らか・遅延↑ | 0.5 |
ジッタが気になるなら 0.7~0.8 |
control_hz |
Hz | teleop ループ周波数 | 50 |
XArmConfig.control_frequency と揃えるのが基本 |
ワークスペース制限との併用
linear_speed を上げてもエンドエフェクタは XArmWorkspaceBounds (とゾーン) で常にクリップされます。SpaceMouse から大きな delta が来てもベース正面・drawer 上空などの境界外には出ません。
トラブルシューティング¤
シミュレータに接続できない¤
- Studio が起動しており、仮想ロボットが xArm7 として有効になっているか確認
XArmConfig.follower_ipが Studio 表示の IP と一致しているか確認- ファイアウォールで TCP 接続がブロックされていないか確認 (特に Windows)
127.0.0.1で繋がらない場合はホスト PC の LAN IP を試す
GELLO のポートが自動検出されない¤
# 明示的にポート指定
config = XArmConfig(
leader_port="/dev/serial/by-id/usb-FTDI_USB__-__Serial_Converter_XXXXXXXX-if00-port0",
follower_ip="127.0.0.1",
)
ls /dev/serial/by-id/ で GELLO のデバイス ID を確認できます。
初期姿勢差が大きすぎる¤
_align_leader_follower が RuntimeError: Leader/Follower initial mismatch > 0.8 rad で失敗する場合、GELLO を start_joints (既定 [0, -90, 90, -90, -90, 0, 0] deg) に近い姿勢に手で動かしてから connect() を呼び直してください。
関連クラス¤
XArmConfig— ロボット設定XArmPairSys— Leader/Follower 協調制御XArmLeader— GELLO テレオペコントローラXArmFollower— xArm7 本体ラッパGelloArmConfig— GELLO キャリブレーションXArmWorkspaceBounds— Cartesian ワークスペース境界XArmZFloorZone— 部分領域での z 下限引き上げ (障害物回避用)