# naowalk A keyboard teleop client for the NAO robot: live video, walking, head control, two-way audio (hear the robot's mics, make it speak via TTS), and a YouTube music player that downloads a track with `yt-dlp` and plays it through the robot's own speakers. ## Requirements **System packages** (Debian/Ubuntu names — adjust for your OS): ```bash sudo apt install portaudio19-dev ffmpeg sshpass openssh-client ``` - `portaudio19-dev` — needed to build/install `pyaudio` - `ffmpeg` — needed by `yt-dlp` to extract audio to mp3 - `sshpass` + `openssh-client` — used to `scp` downloaded tracks onto the robot and to run `mkdir` over `ssh` (the script assumes the default `nao`/`nao` credentials) **NAOqi Python SDK (`qi`)** — not on PyPI. Download the `pynaoqi-python` wheel that matches your OS/Python version from the Aldebaran/SoftBank Robotics developer site and install it: ```bash pip install ./pynaoqi-*.whl ``` **Python packages:** ```bash pip install -r requirements.txt ``` ## Usage ```bash python3 naowalk.py --ip [options] ``` | Flag | Default | Description | |---|---|---| | `--ip` | `127.0.0.1` | Robot's IP address | | `--port` | `9559` | NAOqi port | | `--audio-device-index` | auto | Output device index for hearing the robot's mic on your machine | | `--mic-channel` | `left` | Which robot mic to stream (`left`, `right`, `front`, `rear`) — pick a working one, see Troubleshooting | | `--mic-gain` | `8.0` | Amplification applied to the streamed mic audio | | `--video-scale` | `2.0` | Display window scale (base feed is 320x240) | ## Controls | Key | Action | |---|---| | `W`/`A`/`S`/`D` or arrows | Walk forward/turn/back | | `I`/`K`/`J`/`L` | Head pitch/yaw | | `Space` | Reset head to center | | `1` | Wave gesture | | `M` | Open music search (type query, `Enter` to search, `↑↓` to select, `Enter`/`1`-`5` to play) | | `P` | Pause / resume music (resume restarts the track from the beginning — see Notes) | | `X` | Stop music | | `-` / `=` | Volume down/up | | `T` | Type a message for the robot to speak (TTS) | | `Esc` | Quit (or cancel current mode) | ## How it works - **`SoundReceiver`** — registered as a NAOqi service that receives the robot's mic stream and plays it out your local speakers via `pyaudio`, with gain and a prebuffer to smooth out network jitter. - **`NaoMusicPlayer`** — searches YouTube with `yt-dlp`, downloads and converts the top match to mp3, `scp`s it to the robot, and plays it with `ALAudioPlayer`. - **`NaoTeleop`** — the main loop: pygame window for video/input, keyboard handling, head/walk control, and wiring everything above together. ## Notes / design decisions - **No seek.** Seeking was removed entirely — it added the most complexity for the least-used feature. `P` is a simple play/stop toggle; resuming restarts the track from the beginning rather than tracking a playback position. - **`moveInit()` on startup.** The robot used to need its walk buttons jittered for a few seconds before its first walk would work. `run()` now calls `ALMotionProxy::moveInit()` once at startup, which settles the walk engine into the right pose before any real move command — this is the documented NAOqi fix for exactly this symptom. - **`_safe()` helper.** Most one-off NAOqi calls (setting angles, volume, stopping movement, unsubscribing services) are wrapped in a small `self._safe(label, fn)` helper instead of a repeated `try/except` block, so one failed call logs and moves on without crashing the teleop loop or the shutdown sequence. ## Troubleshooting - **Mic sounds dead / silent on one channel:** some robots have one or more physical mics that produce no signal. Test all four with a quick energy check, then pick a working channel with `--mic-channel`. - **Robot falls over on the very first walk command:** make sure you're on a build that includes the `moveInit()` startup call described above; if it's still happening, increase the `time.sleep(1.0)` after `goToPosture` to give the posture more time to settle first. - **`scp`/`ssh` failures for music:** confirm the robot is reachable at `--ip` and that `nao`/`nao` are still the correct credentials; the script hardcodes them via `sshpass`.