Complete Reachy Mini robot control interface documentation, covering all parameter ranges, controllable degrees of freedom, and usage limitations.
| Interface Type | Latency | Use Case | Implemented Demos | Status |
|---|---|---|---|---|
| REST API | 20-50ms | Single commands, config queries | ✅ All Implemented | Complete |
| WebSocket | <10ms | Real-time control, state streaming | ⏳ Planned | Planned |
| Zenoh | 10-20ms | Python SDK development | ⏳ Planned | Planned |
| BLE | 100-500ms | Configuration, debugging | ⏳ Planned | Planned |
| API Endpoint | Function | Demo File | Status |
|---|---|---|---|
/move/goto |
Motion control | 02_basic_body_rotation |
✅ |
/move/goto |
Nod head motion | 03_basic_nod_head |
✅ |
/move/goto |
Shake head motion | 04_basic_shake_head |
✅ |
/volume/* |
Audio control | 01_basic_audio_control |
✅ |
/motors/* |
Motor control | Used in all motion demos | ✅ |
Base URL: http://192.168.137.225:8000
Features: HTTP-based request-response mode, suitable for single commands and configuration queries, latency 20-50ms
| Object | Parameter | Range | Unit | Description |
|---|---|---|---|---|
| Head Pose | x | ±0.05 | m | Front-back position, forward is positive |
| y | ±0.05 | m | Left-right position, left is positive | |
| z | -0.03 ~ +0.08 | m | Up-down position, up is positive | |
| roll | ±25 | deg | Roll angle, right tilt is positive | |
| pitch | ±35 | deg | Pitch angle, head up is positive | |
| yaw | ±160 | deg | Yaw angle, left turn is positive | |
| Antennas | antennas[0] | -80 ~ +80 | deg | Left antenna angle |
| antennas[1] | -80 ~ +80 | deg | Right antenna angle | |
| Body | body_yaw | -160 ~ +160 | deg | Body yaw angle |
| Motion Params | duration | 0.1 ~ 10.0 | s | Motion duration |
Interpolation Methods:
| Method | Description |
|---|---|
linear |
Linear interpolation, constant velocity motion |
minjerk |
Minimum jerk interpolation, smooth motion (recommended) |
ease |
Ease interpolation, smooth acceleration/deceleration |
cartoon |
Cartoon interpolation, elastic effect |
POST /move/goto - Smooth motion to target (with interpolation)
Request example:
{
"head_pose": {"x": 0, "y": 0, "z": 0.05, "roll": 0, "pitch": 15, "yaw": 0},
"antennas": [30, -30],
"body_yaw": 0,
"duration": 2.0,
"interpolation": "minjerk"
}Response:
{"uuid": "123e4567-e89b-12d3-a456-426614174000"}POST /move/set_target - Set target immediately (no trajectory)
Request example:
{
"target_head_pose": {
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0}
},
"target_antennas": [0.0, 0.0],
"target_body_yaw": 0.0
}POST /move/goto_joint_positions - Joint space motion
| Object | Parameter | Range | Unit | Description |
|---|---|---|---|---|
| Head Joints | head_joint_positions[0] | ±2.79 | rad | yaw_body (-160°~+160°) |
| head_joint_positions[1] | -0.84 ~ +1.40 | rad | roll_sp_1 (-48°~+80°) | |
| head_joint_positions[2] | -0.84 ~ +1.40 | rad | roll_sp_2 (-48°~+80°) | |
| head_joint_positions[3] | -0.84 ~ +1.40 | rad | roll_sp_3 (-48°~+80°) | |
| head_joint_positions[4] | -1.22 ~ +1.40 | rad | pitch_sp_1 (-70°~+80°) | |
| head_joint_positions[5] | -0.84 ~ +1.40 | rad | pitch_sp_2 (-48°~+80°) | |
| head_joint_positions[6] | -1.22 ~ +1.40 | rad | pitch_sp_3 (-70°~+80°) | |
| Antenna Joints | antennas_joint_positions[0] | ±1.40 | rad | Left antenna (-80°~+80°) |
| antennas_joint_positions[1] | ±1.40 | rad | Right antenna (-80°~+80°) |
POST /move/play/wake_up - Wake up animation
POST /move/play/goto_sleep - Go to sleep animation
POST /move/play/recorded-move-dataset/{dataset}/{move} - Play preset motion
- Example:
/move/play/recorded-move-dataset/pollen-robotics/reachy-mini-dances-library/another_one_bites_the_dust
GET /move/running - Get running motions
POST /move/stop - Stop motion
| Object | Interface | Return Data | Value Range |
|---|---|---|---|
| Head Pose | GET /state/present_head_pose |
x, y, z, roll, pitch, yaw | See section 1.1 |
| Body Yaw | GET /state/present_body_yaw |
Radian value | -2.79 ~ +2.79 |
| Antenna Position | GET /state/present_antenna_joint_positions |
[Left, Right] radians | ±1.40 |
| Full State | GET /state/full |
All states | - |
GET /state/full Query Parameters:
| Parameter | Default | Description |
|---|---|---|
| with_control_mode | true | Control mode |
| with_head_pose | true | Current head pose |
| with_target_head_pose | false | Target head pose |
| with_head_joints | false | Head joint angles |
| with_target_head_joints | false | Target joint angles |
| with_body_yaw | true | Body yaw |
| with_target_body_yaw | false | Target body yaw |
| with_antenna_positions | true | Antenna positions |
| with_target_antenna_positions | false | Target antenna positions |
| with_passive_joints | false | Passive joints |
| use_pose_matrix | false | Use matrix format |
| Control Object | Parameter | Description |
|---|---|---|
| Motor Mode | enabled |
Motor enabled, rigid control (mode 3) |
disabled |
Motor disabled, manual movement allowed (mode 0) | |
gravity_compensation |
Gravity compensation, maintain pose (mode 5) |
Interface List:
- GET
/motors/status- Get motor status - POST
/motors/set_mode/{mode}- Set motor mode
| Object | Parameter | Range | Default | Unit |
|---|---|---|---|---|
| Volume | volume | 0 ~ 100 | 50 | % |
| Mic Volume | volume | 0 ~ 100 | 50 | % |
| Sample Rate | sample_rate | 16000 | - | Hz |
| Channels | channels | 2 | - | - |
| Sound Direction | doa_angle | 0 ~ π | - | rad |
Sound Direction Guide:
| Radian | Direction |
|---|---|
| 0 | Left |
| π/2 | Front/Back |
| π | Right |
GET /volume/current - Get speaker volume
Response:
{"volume": 50, "device": "reachy_mini_audio", "platform": "Linux"}POST /volume/set - Set speaker volume
Request:
{"volume": 75}Response:
{"volume": 75, "device": "reachy_mini_audio", "platform": "Linux"}POST /volume/test-sound - Play test sound
Response:
{"status": "ok", "message": "Test sound played"}GET /volume/microphone/current - Get microphone gain
Response:
{"volume": 60, "device": "reachy_mini_audio", "platform": "Linux"}POST /volume/microphone/set - Set microphone gain
Request:
{"volume": 80}| Interface | Method | Description |
|---|---|---|
/apps/list-available |
GET | List available apps |
/apps/install |
POST | Install app |
/apps/start-app/{app_name} |
POST | Start app |
/apps/stop-current-app |
POST | Stop app |
/apps/current-app-status |
GET | Get app status |
Install app request example:
{"source": "huggingface", "app_id": "pollen-robotics/reachy_mini_conversation_app"}| Engine Type | Description |
|---|---|
AnalyticalKinematics |
Analytical solution (default, fastest) |
PlacoKinematics |
Optimization solution (with collision detection) |
NNKinematics |
Neural network (requires model) |
| Interface | Method | Description |
|---|---|---|
/kinematics/info |
GET | Get kinematics info |
/kinematics/urdf |
GET | Get URDF model |
/kinematics/stl/{filename} |
GET | Get STL file (3D visualization) |
| Interface | Method | Description |
|---|---|---|
/daemon/start |
POST | Start daemon |
/daemon/stop |
POST | Stop daemon |
/daemon/restart |
POST | Restart daemon |
/daemon/status |
GET | Get status |
Features: Bidirectional real-time communication, latency <10ms, supports 60Hz+ high-frequency control
Connection: ws://192.168.137.225:8000/move/ws/set_target
| Object | Parameter | Range | Unit | Description |
|---|---|---|---|---|
| Head Pose | position.x | ±0.05 | m | Front-back position |
| position.y | ±0.05 | m | Left-right position | |
| position.z | -0.03 ~ +0.08 | m | Up-down position | |
| rotation (quaternion) | Unit quaternion | - | Orientation | |
| Antennas | target_antennas[0] | -80 ~ +80 | deg | Left antenna |
| target_antennas[1] | -80 ~ +80 | deg | Right antenna | |
| Body | target_body_yaw | -160 ~ +160 | deg | Body yaw |
Send message format (60Hz+):
{
"target_head_pose": {
"position": {"x": 0.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0}
},
"target_antennas": [0.0, 0.0],
"target_body_yaw": 0.0
}Receive error feedback:
{"status": "error", "detail": "error message"}Connection: ws://192.168.137.225:8000/state/ws/full?frequency=30
| Parameter | Range | Default | Description |
|---|---|---|---|
| frequency | 1 ~ 100 | 10 | Update frequency (Hz) |
| with_head_pose | true/false | true | Include head pose |
| with_head_joints | true/false | false | Include joint angles |
| with_antenna_positions | true/false | true | Include antenna positions |
| use_pose_matrix | true/false | false | Use matrix format |
Receive message (continuous stream):
{
"control_mode": "enabled",
"head_pose": {"x": 0.0, "y": 0.0, "z": 0.0, "roll": 0.0, "pitch": 0.0, "yaw": 0.0},
"body_yaw": 0.0,
"antennas_position": [0.0, 0.0],
"timestamp": "2025-12-26T10:00:00Z"
}Connection: ws://192.168.137.225:8000/move/ws/updates
| Event Type | Description |
|---|---|
move_started |
Motion started |
move_completed |
Motion completed |
move_failed |
Motion failed |
move_cancelled |
Motion cancelled |
Receive event:
{
"type": "move_started",
"uuid": "123e4567-e89b-12d3-a456-426614174000",
"details": ""
}Features: SDK main communication method, latency 10-20ms, high bandwidth, supports high-frequency control
Client mode (specify IP):
{"mode": "client", "connect": {"endpoints": ["tcp/192.168.137.225:7447"]}}Peer mode (auto-discovery):
{"mode": "peer", "scouting": {"multicast": {"enabled": true}}}| Topic | Direction | Controllable/Return Objects | Data Type |
|---|---|---|---|
reachy_mini/command |
→ | Head pose, antennas, body yaw, motor mode | dict |
reachy_mini/joint_positions |
← | All 9 joint positions | list |
reachy_mini/head_pose |
← | Head 4x4 pose matrix | ndarray |
reachy_mini/daemon_status |
← | Daemon status | dict |
reachy_mini/task |
→ | Task request | dict |
reachy_mini/task_progress |
← | Task progress | dict |
reachy_mini/recorded_data |
← | Recorded data | bytes |
Set target pose:
{
"head_pose": [[4x4 matrix]], # Head pose matrix
"antennas_joint_positions": [0.5, -0.5], # Antenna radians [-1.40, +1.40]
"body_yaw": 0.0 # Body yaw radians [-2.79, +2.79]
}Enable torque:
{"torque": True}Enable gravity compensation:
{"gravity_compensation": True}Joint positions (reachy_mini/joint_positions):
[
yaw_body, # [0] ±2.79 rad (±160°)
roll_sp_1, # [1] -0.84~+1.40 rad (-48°~+80°)
roll_sp_2, # [2] -0.84~+1.40 rad (-48°~+80°)
roll_sp_3, # [3] -0.84~+1.40 rad (-48°~+80°)
pitch_sp_1, # [4] -1.22~+1.40 rad (-70°~+80°)
pitch_sp_2, # [5] -0.84~+1.40 rad (-48°~+80°)
pitch_sp_3, # [6] -1.22~+1.40 rad (-70°~+80°)
antenna_left, # [7] ±1.40 rad (±80°)
antenna_right # [8] ±1.40 rad (±80°)
]Features: Close-range configuration, debugging, emergency control, latency 100-500ms
| Item | Value |
|---|---|
| BLE Device Name | ReachyMini |
| PIN Code | Last 5 digits of serial number (dfu-util -l to query) |
- Install nRF Connect (Android/iOS)
- Scan and connect to "ReachyMini" device
- Unknown Service → WRITE to send hex commands
| Command | Hexadecimal | Description |
|---|---|---|
| PIN Verification | 50494E5F3030303033 |
PIN_00033 (replace with actual PIN) |
| Query Status | 535441545553 |
STATUS |
| Reset Hotspot | 434D445F484F5453504F54 |
CMD_HOTSPOT |
| Restart Daemon | 434D445F524553544152545F4441454D4F4E |
CMD_RESTART_DAEMON |
| Software Reset | 434D445F534F4654574152455F5245534554 |
CMD_SOFTWARE_RESET |
Official tool: https://pollen-robotics.github.io/reachy_mini/
Supports Chrome-based browsers, can connect via webpage directly.
| Part | DOF Count | Joint Name | Range (deg) | Range (rad) |
|---|---|---|---|---|
| Head | 7 | yaw_body | ±160° | ±2.79 |
| roll_sp_1/2/3 | -48° ~ +80° | -0.84 ~ +1.40 | ||
| pitch_sp_1/2/3 | -70° ~ +80° | -1.22 ~ +1.40 | ||
| Antennas | 2 | Left antenna | ±80° | ±1.40 |
| Right antenna | ±80° | ±1.40 | ||
| Body | 1 | body_yaw | ±160° | ±2.79 |
Total: 10 controllable degrees of freedom
| Axis | Range | Unit | Description |
|---|---|---|---|
| X (front-back) | ±0.05 | m | Forward is positive |
| Y (left-right) | ±0.05 | m | Left is positive |
| Z (up-down) | -0.03 ~ +0.08 | m | Up is positive |
| Roll | ±25 | deg | Right tilt is positive |
| Pitch | ±35 | deg | Head up is positive |
| Yaw | ±160 | deg | Left turn is positive |
| Scenario | Recommended Interface | Reason |
|---|---|---|
| Real-time Tracking | WebSocket /move/ws/set_target |
60Hz+, low latency |
| State Monitoring | WebSocket /state/ws/full |
Adjustable frequency, complete data |
| Single Motion | REST /move/goto |
Simple and reliable |
| Python Development | Zenoh SDK | Official support |
| Web Application | REST + WebSocket | Browser compatible |
| Mobile App | REST API | Standard HTTP |
| Configuration/Debug | BLE | Close-range configuration |
{"x": 0.0, "y": 0.0, "z": 0.0, "roll": 0.0, "pitch": 0.0, "yaw": 0.0}{"position": {"x": 0.0, "y": 0.0, "z": 0.0}, "rotation": {"x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0}}{"m": [r11, r12, r13, x, r21, r22, r23, y, r31, r32, r33, z, 0, 0, 0, 1]}- Coordinate System: Right-handed coordinate system, X axis forward, Z axis up
- Angle Units: API uses degrees, internal uses radians
- Safety Limits: All joints have software/hardware limit protection
- Motor Protection: Avoid staying at limit positions for extended periods
- Network Latency: WebSocket <10ms, REST 20-50ms
- Control Frequency: Recommended not to exceed 60Hz to avoid command congestion
- Priority: goto commands during motion will be ignored (need to stop first)
const controlWS = new WebSocket('ws://192.168.137.225:8000/move/ws/set_target');
const stateWS = new WebSocket('ws://192.168.137.225:8000/state/ws/full?frequency=60');
// Real-time control (60Hz)
function setHeadPose(x, y, z, roll, pitch, yaw) {
const command = {
target_head_pose: {
position: { x, y, z },
rotation: eulerToQuaternion(roll, pitch, yaw)
}
};
controlWS.send(JSON.stringify(command));
}
// State callback
stateWS.onmessage = (event) => {
const state = JSON.parse(event.data);
console.log('Head position:', state.head_pose);
};import requests
# Smooth motion to target
data = {
"head_pose": {"x": 0, "y": 0, "z": 0.05, "roll": 0, "pitch": 15, "yaw": 0},
"antennas": [30, -30],
"body_yaw": 0,
"duration": 2.0,
"interpolation": "minjerk"
}
response = requests.post('http://192.168.137.225:8000/move/goto', json=data)
print(f"Motion UUID: {response.json()['uuid']}")import requests
response = requests.get('http://192.168.137.225:8000/state/full')
state = response.json()
print(f"Head pose: {state['head_pose']}")
print(f"Antenna positions: {state['antennas_position']}")
print(f"Control mode: {state['control_mode']}")import requests
base_url = "http://192.168.137.225:8000"
# Get current volume
response = requests.get(f"{base_url}/volume/current")
print(f"Volume: {response.json()['volume']}%")
# Set volume
requests.post(f"{base_url}/volume/set", json={"volume": 80})
# Play test sound
requests.post(f"{base_url}/volume/test-sound")
# Get microphone gain
response = requests.get(f"{base_url}/volume/microphone/current")
print(f"Microphone gain: {response.json()['volume']}%")
# Set microphone gain
requests.post(f"{base_url}/volume/microphone/set", json={"volume": 70})from reachy_mini import ReachyMini
# Initialize robot
robot = ReachyMini()
# ===== Audio Playback =====
# Play audio file
robot.media.play_sound("wake_up.wav")
# Audio stream playback
robot.media.start_playing()
import numpy as np
# Generate or load audio data (float32, -1.0 to 1.0)
audio_data = np.random.uniform(-0.5, 0.5, 16000).astype(np.float32)
robot.media.push_audio_sample(audio_data)
robot.media.stop_playing()
# Get audio output parameters
sample_rate = robot.media.get_output_audio_samplerate() # 16000 Hz
channels = robot.media.get_output_channels() # 2 (stereo)
# ===== Audio Recording =====
# Start recording
robot.media.start_recording()
# Get audio sample
audio_sample = robot.media.get_audio_sample() # Returns numpy array or bytes
if audio_sample is not None:
print(f"Sampled {len(audio_sample)} audio points")
# Stop recording
robot.media.stop_recording()
# Get audio input parameters
input_sample_rate = robot.media.get_input_audio_samplerate() # 16000 Hz
input_channels = robot.media.get_input_channels() # 2 (stereo)
# ===== Sound Source Direction Detection =====
# Get sound direction
doa_result = robot.media.audio.get_DoA()
if doa_result:
angle_radians, speech_detected = doa_result
angle_degrees = angle_radians * 180 / 3.14159
print(f"Sound direction: {angle_degrees:.1f}°")
print(f"Speech detected: {speech_detected}")
# ===== Low-level Hardware Control =====
from reachy_mini.media.audio_control_utils import init_respeaker_usb
# Initialize ReSpeaker device
respeaker = init_respeaker_usb()
if respeaker:
# Read microphone gain
mic_gain = respeaker.read("AUDIO_MGR_MIC_GAIN")
print(f"Microphone gain: {mic_gain}")
# Set microphone gain (float value)
respeaker.write("AUDIO_MGR_MIC_GAIN", [2.5])
# LED control
respeaker.write("LED_BRIGHTNESS", [80]) # Brightness 0-100
respeaker.write("LED_COLOR", [0xFF0000]) # RGB red
respeaker.write("LED_EFFECT", [1]) # Effect mode
# Read firmware version
version = respeaker.read("VERSION")
print(f"Firmware version: {version}")from reachy_mini import ReachyMini
import numpy as np
import wave
robot = ReachyMini()
# Start recording
robot.media.start_recording()
print("Recording...")
# Record for 5 seconds
import time
sample_rate = robot.media.get_input_audio_samplerate()
duration = 5 # seconds
all_samples = []
start_time = time.time()
while time.time() - start_time < duration:
sample = robot.media.get_audio_sample()
if sample is not None:
all_samples.append(sample)
time.sleep(0.01)
robot.media.stop_recording()
print("Recording complete")
# Save as WAV file
if all_samples:
audio_array = np.concatenate(all_samples)
# Convert to int16 PCM
audio_int16 = (audio_array * 32767).astype(np.int16)
with wave.open("recording.wav", "wb") as wav_file:
wav_file.setnchannels(1)
wav_file.setsampwidth(2)
wav_file.setframerate(sample_rate)
wav_file.writeframes(audio_int16.tobytes())
print("Saved as recording.wav")from reachy_mini import ReachyMini
import numpy as np
robot = ReachyMini()
# Use pyttsx3 offline TTS (requires: pip install pyttsx3)
import pyttsx3
engine = pyttsx3.init()
engine.setProperty('rate', 150) # Speech rate
engine.setProperty('volume', 0.9) # Volume
# Save speech to file and play
engine.save_to_file('Hello, I am Reachy Mini!', 'hello.wav')
engine.runAndWait()
# Play
robot.media.play_sound("hello.wav")from reachy_mini.io.audio_ws import AsyncWebSocketAudioStreamer
import numpy as np
# Connect to audio stream server
streamer = AsyncWebSocketAudioStreamer(
"ws://192.168.137.225:8765/audio",
keep_alive_interval=2.0
)
# Send audio (supports bytes, int16 or float32)
audio_chunk = np.random.uniform(-0.3, 0.3, 2048).astype(np.float32)
streamer.send_audio_chunk(audio_chunk)
# Receive audio
while True:
received_audio = streamer.get_audio_chunk(timeout=0.1)
if received_audio is not None:
print(f"Received audio: {len(received_audio)} samples")
# Process received audio...
# Close connection
streamer.close()