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.1.tar.gz (19.5 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.1.tar.gz.

File metadata

File hashes

Hashes for lerobot_teleoperator_spacemouse-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b47d7db060f9b87bc698ea660423d958b181f5524d92ce05965a4d0e18c7e4c3
MD5 3fb861ecb258e595098994d4f28d0ac4
BLAKE2b-256 ef1b84f870715bf2ed47a3732ff214f9a9c30fcbcd3cf62e0f576c010fa5553b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for lerobot_teleoperator_spacemouse-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9898fc9380167e1124bb5aedc076f840ca61c7811a9f25ebcf313fa9921fbbc3
MD5 7bd01262c96314c351246d3e3ab2f597
BLAKE2b-256 096490f7fae50804a3a7830d7d160b74245f029fc30d51ed6f79f908e0a1f6bd

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