Skip to main content

SpaceMouse teleoperation plugin and Cartesian control adapters for LeRobot.

Project description

lerobot-teleoperator-spacemouse

PyPI License Python

A LeRobot plugin that turns a 3Dconnexion SpaceMouse into a 6-DoF robot teleoperator.

Push the puck to move the end-effector in X/Y/Z. Tilt/twist to rotate. Press the side buttons to open or close the gripper. Works out of the box with SO-101 / SO-ARM follower robots via built-in inverse kinematics, and supports any custom URDF through a profile API.


Features

  • 6-DoF teleoperation — translation and rotation deltas from SpaceMouse physical axes
  • IK mode — converts end-effector targets to joint commands using LeRobot's RobotKinematics and a bundled SO-101 URDF (no calibration file needed)
  • Direct EEF mode — passes Cartesian targets straight to robots that already accept ee.* action keys
  • Gripper control — left/right buttons open and close the gripper at a configurable speed
  • Axis remapping & sign flip — remap any physical SpaceMouse axis to any robot axis; flip signs per axis
  • Deadzone & timeout — configurable deadzone and input-stale timeout prevent drift when the puck is released
  • Workspace clamping — optional per-axis position limits and max-step rate limiter
  • Custom robot profiles — register your own URDF + frame + motor list at runtime

Installation

pip install lerobot-teleoperator-spacemouse

SO-101 / SO-ARM IK support is included by default. For other robot families (e.g. Dynamixel-based arms), register a custom kinematics profile — see Extending to Other Robots.


Quick Start

SO-101 follower over USB

lerobot-teleoperate \
  --robot.type=so101_follower \
  --robot.port=/dev/ttyACM0 \
  --robot.id=my_robot \
  --teleop.type=spacemouse \
  --teleop.adapter.mode=ik \
  --fps=60

Conservative first run (smaller steps, display enabled)

lerobot-teleoperate \
  --robot.type=so101_follower \
  --robot.port=/dev/ttyACM0 \
  --robot.id=my_robot \
  --teleop.type=spacemouse \
  --teleop.adapter.mode=ik \
  --teleop.deadzone=0.1 \
  --teleop.input_timeout_s=0.08 \
  --teleop.adapter.translation_step_m=0.001 \
  --teleop.adapter.rotation_step_rad=0.005 \
  --display_data=true

Test your SpaceMouse (no robot needed)

lerobot-teleoperator-spacemouse-test

This prints raw SpaceMouse state for 30 seconds so you can verify axes and buttons before connecting a robot.


Configuration Reference

All options are set via --teleop.<option> or --teleop.adapter.<option> on the command line.

Teleoperator options (--teleop.*)

Option Default Description
device None HID device path. Auto-detected when None.
deadzone 0.08 Ignore inputs below this fraction of full deflection.
rescale_after_deadzone True Rescale remaining range to [0, 1] after deadzone.
max_axis_value 1.0 Clamp raw axis values to [-max, max].
input_timeout_s 0.08 Zero-out input if no new state arrives within this window (seconds).
read_drain_count 32 Max HID reads per get_action() call to drain the queue.
x_axis "y" SpaceMouse attribute name mapped to robot target_x.
y_axis "x" SpaceMouse attribute name mapped to robot target_y.
z_axis "z" SpaceMouse attribute name mapped to robot target_z.
wx_axis "roll" SpaceMouse attribute name mapped to robot target_wx.
wy_axis "pitch" SpaceMouse attribute name mapped to robot target_wy.
wz_axis "yaw" SpaceMouse attribute name mapped to robot target_wz.
x_sign 1.0 Sign multiplier for target_x. Use -1.0 to flip.
y_sign -1.0 Sign multiplier for target_y.
z_sign 1.0 Sign multiplier for target_z.
wx_sign 1.0 Sign multiplier for target_wx.
wy_sign 1.0 Sign multiplier for target_wy.
wz_sign 1.0 Sign multiplier for target_wz.
use_gripper True Enable gripper button control.
gripper_open_button 1 Button index that opens the gripper.
gripper_close_button 0 Button index that closes the gripper.
require_enable_button False Require a dedicated button to enable motion.
idle_enabled False Whether enabled=True is sent when the device is idle.

Adapter options (--teleop.adapter.*)

Option Default Description
mode "auto" "ik", "eef", or "auto" (uses "eef" if robot exposes ee.* keys).
robot_profile "so101_follower" Built-in kinematics profile. Use "custom" to supply your own URDF.
translation_step_m 0.001 End-effector translation per frame at full SpaceMouse deflection (meters).
rotation_step_rad 0.005 End-effector rotation per frame at full deflection (radians).
workspace_min None [x, y, z] lower workspace bound (meters).
workspace_max None [x, y, z] upper workspace bound (meters).
max_ee_step_m 0.02 Maximum allowed end-effector step per frame (rate limiter).
position_weight 1.0 IK position tracking weight.
orientation_weight 0.01 IK orientation tracking weight.
gripper_speed_factor 2.0 Gripper velocity → position integration scale.
gripper_min 0.0 Gripper position lower bound (normalized joint units).
gripper_max 100.0 Gripper position upper bound (normalized joint units).

Linux HID Setup

The SpaceMouse uses the hidraw subsystem. On Linux you need the shared library and a udev rule.

conda environment:

conda install -c conda-forge libhidapi

Debian / Ubuntu system Python:

sudo apt-get install libhidapi-hidraw0 libhidapi-dev

udev rule (allows non-root access):

sudo tee /etc/udev/rules.d/99-spacemouse-hidraw.rules >/dev/null <<'EOF'
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="256f", MODE="0660", GROUP="plugdev", TAG+="uaccess"
EOF
sudo usermod -aG plugdev "$USER"
sudo udevadm control --reload-rules
sudo udevadm trigger

Unplug and replug the SpaceMouse. Log out and back in after the group change.


Extending to Other Robots

One-off custom URDF

lerobot-teleoperate \
  --teleop.type=spacemouse \
  --teleop.adapter.mode=ik \
  --teleop.adapter.robot_profile=custom \
  --teleop.adapter.urdf_path=/path/to/robot.urdf \
  --teleop.adapter.target_frame_name=gripper_tip \
  --teleop.adapter.motor_names='["joint1","joint2","joint3","joint4","joint5","gripper"]' \
  --teleop.adapter.gripper_name=gripper

Reusable profile (Python)

Register a profile from your own package and it becomes available as a named robot_profile:

from lerobot_teleoperator_spacemouse.profiles import KinematicsProfile, register_kinematics_profile

register_kinematics_profile(
    "my_arm",
    KinematicsProfile(
        urdf_resource=None,           # set urdf_path on the adapter instead
        target_frame_name="tip_link",
        motor_names=("j1", "j2", "j3", "j4", "j5", "gripper"),
        gripper_name="gripper",
    ),
)

Then use --teleop.adapter.robot_profile=my_arm.


Troubleshooting

Arm keeps moving after releasing the SpaceMouse — Increase --teleop.deadzone (e.g. 0.15) or lower --teleop.input_timeout_s (e.g. 0.05).

Motion is too fast / too slow — Tune --teleop.adapter.translation_step_m and --teleop.adapter.rotation_step_rad.

Axis direction is wrong — Flip the sign: --teleop.x_sign=-1, --teleop.y_sign=-1, etc.

Device not found / permission denied — Run lerobot-teleoperator-spacemouse-test first. Follow the Linux HID Setup section if it fails.


Development

git clone https://github.com/Jas000n/lerobot-teleoperator-spacemouse.git
cd lerobot-teleoperator-spacemouse
pip install -e ".[dev]"
pytest

License

Apache 2.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lerobot_teleoperator_spacemouse-0.1.4.tar.gz (19.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file lerobot_teleoperator_spacemouse-0.1.4.tar.gz.

File metadata

File hashes

Hashes for lerobot_teleoperator_spacemouse-0.1.4.tar.gz
Algorithm Hash digest
SHA256 28675c3515d80189b32ec0881581308e6fb6bc183f002fe71609367051ae1705
MD5 dee9d5694c60a8429cbcbb5ade696503
BLAKE2b-256 f82ef0c058cdc2c09437a72da8f22e1f062cc176feb79d0e2bb45d6cee683bec

See more details on using hashes here.

File details

Details for the file lerobot_teleoperator_spacemouse-0.1.4-py3-none-any.whl.

File metadata

File hashes

Hashes for lerobot_teleoperator_spacemouse-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 7fc0d4b42d6b47cb517c3d135fb249c879a37f8a737b0b9ef55749e774337db9
MD5 d01130110eb273e4e38a68fb854178ef
BLAKE2b-256 01fbeceb6e3adea72a920a35e22f1d107c5874de3e4e6057699601fefc04bfa2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page