In July 2024 a human-centred design professor emailed me about his lab's NAO, a two-foot humanoid robot. He wanted it connected to AI and holding a real conversation. The next afternoon he walked the robot over to my office, handed me the credentials and left it with me.
NAO, two feet tall.
Out of the box, NAO talks like a phone tree. It listens for keywords and plays back scripted replies. The lab's research data couldn't go to a public AI service, so the work had to run on the university's own AI infrastructure.
Getting it onto the campus network came first. Nothing gets past DNS until its hardware address is registered, and the only way to read the robot's was a cable from the back of its head to my Mac. Once it was registered, I pressed the chest button for an IP and logged in over ssh with the credentials from the docs. One cat /etc/os-release showed Gentoo1, close enough to the Arch I use daily that the filesystem held no surprises. The SDK docs are hard to find, partly in French and partly gone, so the afternoon went to spotting something odd on the robot and hunting down the docs for it. The qi command line then set the order of the work, with speech first, motion second and the audio APIs last. By the end of day one it could talk and walk on command.
NAOqi, the robot's operating layer, exposes every part of the machine as a network service, including the microphones, speakers, motors and gesture library2. That lets the robot stay the body while a laptop on the same Wi-Fi does the thinking. Each turn of conversation sends only a few hundred bytes of text between them.
A body and a brain
The conversation program is about 150 lines of Python. It connects, loads a persona, then loops through listening, transcribing, asking the model, and speaking the answer with gestures.
session = qi.Session()
session.connect(f"tcp://{ip}:9559")
history = [{"role": "system", "content": open("prompt.txt").read().strip()}]
while True:
user_input = listen_and_transcribe() # the laptop listens
history.append({"role": "user", "content": user_input})
response = get_gpt_response(history)
history.append({"role": "assistant", "content": response})
nao_speak_with_animations(session, response) # the robot answers
NAOqi's animated-speech mode picks gestures to match what the robot is saying, and it takes stage directions inline with the words:
nao_speak_with_animations(
session,
"^start(animations/Stand/Gestures/Hey_1) Hello! I am NAO, and I'm "
"ready for a conversation. ^wait(animations/Stand/Gestures/Hey_1)",
)
NAO doesn't balance itself out of the box, so one careless "walk" has you lunging to catch it. The inline stage directions also mean certain words can set off a gesture by accident. Python versioning on a fourteen-year-old SDK is archaeology. The hardest problem was knowing when a person has finished talking. Cut the mic too early and the robot interrupts them. Cut it too late and the robot feels broken. So the laptop does the listening, through a speech SDK that already makes that call well, and the robot carries the voice and the body.
For the first real conversation, I asked the robot for the longest word in the English language. NAO tracks your face while you talk, which is one of its built-in background behaviours, and a few gestures run on top of that.
Next the professor wanted to run it himself, and that is what the five days were for. The deliverable was a setup script that builds the whole environment on a fresh Mac. It tests the AI connection and the robot connection separately, so he could tell which half had failed without me in the room. By mid-September he was running it from his own desktop and writing his own personas. He kept it going for the next ten months.
Current project status.
-
OpenNAO, the robot's operating system, is a Gentoo-based Linux distribution. doc.aldebaran.com/2-0/dev/tools/opennao. ↩
-
NAOqi exposes the robot's subsystems (audio, motion, speech, memory) as services callable over TCP on port 9559, with Python bindings. SDK reference: doc.aldebaran.com. ↩