humanoid-companion
A small humanoid robot you can talk to. It walks with a reinforcement-learned policy, waves, nods and dances while it talks, shows a face with lip sync, and speaks — and its brain is a language model you run yourself (Ollama, llama.cpp, vLLM, or any OpenAI-compatible API).
Today its body is a simulated ROBOTIS OP3 in MuJoCo, driven by exactly the control loop a real robot would run. The goal is to put the same code on a real 50 cm robot.
▶ Watch with sound (35 s). Left: the robot's face. Right: its body, in real time.
| # | Person | Robot says | Face · gesture | Body |
|---|---|---|---|---|
| 1 | Hi! I'm back. | "Hi! Welcome back — I missed you!" | happy · wave | waves, standing |
| 2 | Can you dance for me? | "Absolutely! Let me show you my best dance moves." | happy · dance | dances, then walks the plan it made: 0.79 m and 136° |
| 3 | Please walk forward a little and then turn left. | "On it!" | happy | 0.61 m forward, 82° left |
| 4 | You did amazing today! | "Thank you! That means so much to me!" | happy · celebrate | both arms up |
| 5 | (goodbye) | «Дзякуй, Яўген! Да сустрэчы!» | happy · wave | waves goodbye in a native Belarusian voice |
Every reply was generated live by a local model; no safety stops. The robot never says "owner": give it your name and it uses it.
What is inside
| Walking | A PPO policy trained in MuJoCo Playground (MJX) and exported to plain NumPy (matches Brax within 1e-5). A 50 Hz control loop runs it with safety stops: tilt, joint limits, a deadline watchdog, emergency stop. Bundled: policies/op3_walk.npz (300 KB). |
| Brain | Plain language in, velocity commands out. The model answers in a JSON schema; its answer is never trusted: parsed, validated, clamped to the trained command ranges and a speed cap. Stop words never reach the model. Any failure means "stand still". |
| Conversation | A warm, cheerful persona. Each reply is {say, expression, gesture, action}, validated before use. The model can only ask to walk; the walking planner decides. |
| Face | A web page (7 expressions, lip sync from the voice with Web Audio, captions) and a Python renderer of the same face for videos. The page also shows the body's camera live. |
| Gestures | Wave, nod, celebrate, look around, dance: head and arm offsets layered over the walking policy, legs left to the policy for balance. Each one measured for stability before it was allowed. |
| Voice | Speech and transcription over the OpenAI audio API (Kokoro, Whisper). A Belarusian goodbye in a native voice through belarusian-tts. No speech server: the robot shows captions and still moves. |
| Training | train.py (Brax PPO, CPU or GPU), a hardened environment (pushes, action latency, encoder offsets, domain randomization), evaluation, export, speed sweep. |
Quick start
Python 3.12 and uv. macOS or Linux; no GPU needed to run it.
git clone https://github.com/YauhenBichel/humanoid-companion && cd humanoid-companion
uv sync --frozen
# 1. Watch it walk (bundled policy, C MuJoCo, 10 s at 0.5 m/s)
uv run humanoid-sim --vx 0.5 --seconds 10 --record walk.mp4
bin/viewer --vx 0.4 --seconds 20 # live 3D viewer on macOS (mjpython)
# 2. Give it a brain: any OpenAI-compatible chat server, e.g. Ollama
ollama pull llama3.1:8b
export HUMANOID_LLM_MODEL=llama3.1:8b # HUMANOID_LLM_BASE_URL defaults to Ollama, 127.0.0.1:11434/v1
export HUMANOID_USER_NAME=Alex # optional: it calls you by name
# 3. Talk to it: face and body side by side in your browser; type, it answers, speaks and moves
uv run humanoid-talk --open
A voice (optional). The robot speaks through any server with the OpenAI speech API. Kokoro is small and natural:
docker run -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-cpu # HUMANOID_TTS_BASE_URL defaults to 127.0.0.1:8880/v1
For speaking to it (humanoid-talk --mic, needs uv sync --extra voice), run a Whisper server with
the OpenAI transcription API, e.g. Speaches on port 8000.
For the native Belarusian goodbye, run belarusian-tts
(port 11810) and set HUMANOID_USER_NAME_BE to your name in Belarusian (e.g. Яўген).
Record a conversation as a split-screen video with sound:
uv run humanoid-talk --text "Hi! I'm back." --text "Can you dance for me?" \
--text "Please walk forward a little and then turn left." --record demo/
Configuration
| Variable | Default | What |
|---|---|---|
HUMANOID_LLM_BASE_URL |
http://127.0.0.1:11434/v1 (Ollama) |
chat completions with JSON-schema answers |
HUMANOID_LLM_MODEL |
(none; Ollama needs one) | the model name |
HUMANOID_LLM_API_KEY |
(none) | sent as a bearer token, for hosted APIs |
HUMANOID_TTS_BASE_URL |
http://127.0.0.1:8880/v1 (Kokoro-FastAPI) |
/audio/speech |
HUMANOID_TTS_VOICE |
af_heart |
a Kokoro voice |
HUMANOID_STT_BASE_URL |
http://127.0.0.1:8000/v1 (Speaches) |
/audio/transcriptions |
HUMANOID_BE_TTS_URL |
http://127.0.0.1:11810/v1 (belarusian-tts) |
the Belarusian goodbye |
HUMANOID_USER_NAME, HUMANOID_USER_NAME_BE |
(none) | your name, and its Belarusian form for the goodbye |
How it works
flowchart LR
you([you: text or voice]) --> conv[conversation<br/>persona + JSON schema]
conv -- "say, expression, gesture" --> face[face page<br/>lip sync, captions]
conv -- "say" --> tts[speech server]
tts --> face
conv -- "action: walk + instruction" --> brain[walking planner<br/>schema, clamps, stop words]
brain -- "vx, vy, yaw rate, duration" --> loop[50 Hz control loop<br/>safety stops]
gest[gestures<br/>head + arm offsets] --> loop
loop <--> io[RobotIO<br/>MuJoCo today, servos later]
llm[(your LLM)] -.-> conv
llm -.-> brain
One turn: the face shows thinking; the model replies; the speech and the walking plan are fetched at the same time; then the robot speaks while its body acts. Arm gestures come before a walk, never during it (measured: both arms up tips it over while walking).
The deployment path is written for a real robot: RobotIO is the only thing that knows about
MuJoCo. The observation builder reproduces Playground's exactly (including its one-step action
memory and sensor timing), which the parity tests pin.
Results (simulation)
Walking, bundled policy, 10 s at a commanded 0.5 m/s in C MuJoCo (not the MJX it was trained in, so this is also a sim-to-sim test): 5.27 m (0.53 m/s), 0.35 m sideways drift, no safety stop in 500 control steps. In training's own evaluation (128 episodes in MJX): mean 0.519 m/s, 0 falls.
Speed range (docs/speed-sweep.md): walks at 0.3–0.8 m/s forward (top ~0.65 m/s) and backwards at 0.3 m/s. Commands of 0.2 m/s and below make it stand still — a dead zone from Playground's reward shaping (mujoco_playground#361) — so the planner raises slow commands to the slowest real walk.
Gestures (docs/gesture-stability.md): all five pass standing; nod and look-around also pass while walking. A raised arm alone tipped the robot over until the other arm counter-balanced.
Training: 103 M environment steps in 96 minutes on a 16-core CPU (AMD Ryzen AI MAX+ 395, JAX split into 16 CPU devices: 2.8× faster than one). It also runs on that chip's GPU — see strix-halo-jax.
Train your own
uv run python -m humanoid_companion.train --name baseline --cpu-devices 16 --num-envs 4096 --num-timesteps 100000000
uv run python -m humanoid_companion.evaluate --run runs/baseline --no-video
uv run python -m humanoid_companion.export --run runs/baseline # -> exports/baseline/policy.npz
uv run python -m humanoid_companion.speed_sweep --policy exports/baseline/policy.npz --name baseline
uv run humanoid-talk --open --policy exports/baseline/policy.npz --speed-sweep speed-sweep-baseline.json
--randomize trains the hardened environment (pushes, latency, encoder offsets, randomized
friction, masses and gains) meant for a real robot.
Roadmap
- Hardened policy (domain randomization) and its sim-to-sim numbers.
DynamixelRobotIO: the real servos and IMU, after a system identification.- A real OP3-class robot walking with this code; the face on a small screen on its head.
- Seeing: a camera, and the model told what is in front of it.
Ideas and pull requests are welcome: CONTRIBUTING.md.
Contributors
Licence and disclaimer
Apache-2.0 (LICENSE, NOTICE). The OP3 model and the Op3Joystick environment come from MuJoCo Playground (Apache-2.0). Not affiliated with ROBOTIS or Google DeepMind.
This is research software for a simulated robot, provided as is. It is not a safety-rated robot controller: a real robot running it can fall, pinch or hit, so keep people clear and a hand on the power switch. It is not a medical or care device and must not be used as one.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file humanoid_companion-0.1.0.tar.gz.
File metadata
- Download URL: humanoid_companion-0.1.0.tar.gz
- Upload date:
- Size: 6.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b2d71779c9380790f8287c9382e734d3bc67e565a763ec0021812a890e6107c
|
|
| MD5 |
6602d1b7a09c6420e9544c960b09aba7
|
|
| BLAKE2b-256 |
40e69f5c4245612ffddb42c07d2052532d74e6c438953fb74420fca860bcde47
|
File details
Details for the file humanoid_companion-0.1.0-py3-none-any.whl.
File metadata
- Download URL: humanoid_companion-0.1.0-py3-none-any.whl
- Upload date:
- Size: 342.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfc6cbde80a67677184dd853e30a979a70f7a6a823454d9fd4c95912594c2031
|
|
| MD5 |
dab0ccc38b0c4e09abe7859deb301a1d
|
|
| BLAKE2b-256 |
98100efcd37c8306ef161dea02ce7e15081e9a436bac1ed2f2837688ff4c98e3
|