--- title: Reachy_Mini_Teleop emoji: 🎮 colorFrom: blue colorTo: indigo sdk: static pinned: false short_description: Local gamepad puppeteering with independent antenna control. tags: - reachy_mini - reachy_mini_python_app --- # Reachy Mini Teleop Local, expressive gamepad control for Reachy Mini Lite. Control the head, body, height, and both antennas independently. A browser panel shows app/controller status, switches Precision/Turbo mode, and provides Stop. The controller connects to this Windows computer. The browser panel is local; it does not provide remote robot access or camera/audio streaming. ## Start here 1. Power/connect Reachy Mini and open **Reachy Mini Control**. Connect the robot and wait until it is ready. Close other apps that command its movements. 2. Connect your Xbox or DualSense controller by USB or Bluetooth. Center the sticks and release the triggers before starting. 3. Double-click **start-local.cmd** in this folder. It checks the locked Python environment, starts Teleop, and opens **http://127.0.0.1:8042**. 4. Keep the terminal open. Use the gamepad to move the robot. 5. Click **Stop** in the browser, or press **Ctrl+C** in the terminal. Allow about three seconds for the neutral return. Motors remain enabled to support the head. Closing the browser tab does **not** stop Teleop. Force-closing the terminal, losing power, or a broken daemon connection cannot guarantee a neutral return. The browser Stop button is a normal software stop, not an emergency hardware stop. ## First-time setup Requires Windows, Python **3.12**, and [uv](https://docs.astral.sh/uv/getting-started/installation/). The launcher creates/updates the environment using the committed lock file. Initial setup needs internet access; a prepared environment can run offline. No Hugging Face login is required for this local app. From PowerShell in this folder: ```powershell uv sync --locked --python 3.12 uv run --no-sync python -m teleop.main --check ``` The check connects to the daemon's WebSocket and reads telemetry. It sends no motion commands and does not initialize camera/audio. For normal operation: ```powershell .\start-local.cmd ``` For offline operation after setup, bypass the launcher's environment check: ```powershell .\.venv\Scripts\python.exe -m teleop.main --open-browser ``` The app also retains its `reachy_mini_apps` entry point for discovery by Reachy Mini Control. The launcher is the supported Windows workflow tested here. ## Controls | Input | Action | |---|---| | Left stick | Head yaw and pitch relative to the body | | Right stick horizontal | Body rotation; head follows while preserving relative yaw | | Right stick vertical | Head height | | L2 / LT | Left antenna outward | | R2 / RT | Right antenna outward | | L1 / LB | Left antenna inward | | R1 / RB | Right antenna inward | | D-pad up | Turbo | | D-pad down | Precision | | Browser mode switch | Same Precision/Turbo state | | Browser Stop / terminal Ctrl+C | Smooth neutral return, then exit | Both antenna channels remain enabled. The SDK receives antennas in **right, left** order. SDL's controller mapping handles Xbox/DualSense layout differences. Unknown controllers are rejected rather than assigned guessed button indices; configure `SDL_GAMECONTROLLERCONFIG` if your controller needs a custom mapping. The app streams one atomic head/body/antenna target at approximately 60 Hz, with elapsed-time integration, smoothing, and per-channel speed limits. Turbo doubles rates but retains pose bounds. Conservative limits are head/body relative yaw +/-45 degrees, pitch +/-20 degrees, body +/-90 degrees, height -20 to +30 mm, and antennas +/-60 degrees. Height-dependent yaw/pitch limits narrow near either height extreme. These are conservative heuristics, **not collision detection**; the daemon also applies its own limits. Startup begins at measured pose, then converges gradually into app limits if necessary. Sticks hold the last position when released; they do not automatically recenter the head. Controller loss stops the loop and requests a three-second neutral return. A lost daemon connection stops commands and logs an error; it cannot send a return move. ## Pollen Telepresence or this app? Pollen's current [Telepresence app](https://huggingface.co/spaces/pollen-robotics/telepresence) is the easier choice for remote viewing, phone/touch control, robot camera, and two-way audio. It also supports physical gamepads: its current source reads whichever stick has the larger deflection and uses that to steer head yaw/pitch, with body yaw taking over beyond the head's relative yaw limit. This app remains useful for **local expressive puppeteering**: separate sticks for head versus body/height, independent trigger/shoulder antenna control, Precision/Turbo modes, and holding poses when controls are released. The reviewed Pollen Telepresence code does not provide those antenna/height controls. Its motion loop is 20 Hz; ours targets 60 Hz with explicit smoothing, but that alone does not prove smoother hardware movement. An attended comparison is still needed. We recommend Pollen Telepresence for general remote teleoperation and keeping this app for the richer local performance controls. There is no need to rebuild Pollen's camera/audio stack here. Sources reviewed October 24, 2026: - [Pollen Telepresence README](https://huggingface.co/spaces/pollen-robotics/telepresence/blob/main/README.md) - [Gamepad input](https://huggingface.co/spaces/pollen-robotics/telepresence/blob/main/src/components/joystick/useGamepadDeflection.ts) - [Motion controller](https://huggingface.co/spaces/pollen-robotics/telepresence/blob/main/src/components/joystick/useHeadVelocityControl.ts) - [Reachy Mini Python SDK](https://huggingface.co/docs/reachy_mini/en/SDK/python-sdk) ## Troubleshooting - **Zenoh / port 7447:** an old Python environment is being used. This app pins SDK 1.11.0 to match the installed daemon. Run the launcher from this folder; there is another Teleop copy at `Desktop\teleop` that was not updated. - **Port 8000 / connection refused:** open Reachy Mini Control and connect the robot. The new `--check` command explains missing/unsupported daemon readiness. - **GStreamer 1.28.8 / cp312 installation error:** Windows media components are constrained to the Desktop-compatible 1.28.7 wheels. Use the committed lock and Python 3.12; do not install GStreamer independently into this environment. - **`exclude-newer = "7 days"` warning:** this project's own uv settings isolate it from the parent SDK checkout's newer uv configuration format. - **Waiting for controller:** ensure the pad is connected to the same computer, not just the robot or another Bluetooth host. Restart after pairing if needed. - **Port 8042 already in use:** stop the existing Teleop instance before starting another. Do not run two robot-control apps simultaneously. - **Antenna motion looks choppy:** the earlier hardware issue may still need attention. Software keeps both channels available; do not force the mechanism. The browser status and extension bar show commanded app state, not independently verified physical movement. Watch the terminal for errors. ## Development checks ```powershell uv run --no-sync python -m unittest discover -s tests -v uv run --no-sync python -m teleop.main --check ``` Tests use fake robot/controller input. Physical controls, direction, feel, antenna behavior, and Stop/disconnect acceptance still require an attended hardware trial.