PawAI / Go2
A multimodal embodied-interaction system I built with Unitree Go2, Jetson, and ROS2—from perception and speech to safety gates and observability.
A multimodal embodied-interaction system I built with Unitree Go2, Jetson, and ROS2—from perception and speech to safety gates and observability.
PawAI is an embodied-interaction robot dog I built on top of a Unitree Go2 Pro, integrating NVIDIA Jetson, Intel RealSense D435, ROS2, and multimodal AI.
My goal was not to put a chatbot inside a robot dog, nor to demo face, speech, gesture, and object recognition as isolated features.
The real question was: can a robot connect seeing, hearing, understanding, deciding, and acting into an explainable, degradable loop that also knows when not to move?

Behind this finished photo are more than eight months of hardware modifications, model selection, real-robot incidents, and system refactoring.
The core of PawAI can be summarized in four steps:
PawAI’s hard rule: the LLM may propose; only the Executive releases production sport motion. Audio and hard-stop remain independent paths, while hard-stop still needs dedicated audit and validation.
This video moves from real-time perception and voice interaction to safety refusal and PawAI Studio’s decision trace. It is a June 2026 system snapshot—not proof that every experimental capability below has reached product reliability.
This demo is narrated in Chinese. The surrounding English text explains each stage and what to watch for.
Full demo: PawAI—autonomous object finding and embodied interaction through multimodal perception fusion.
I began with the Go2 ROS2 and WebRTC drivers. Making the robot move, reading a sensor, or calling one predefined action was not the hardest part.
The difficult questions appeared when face, object, pose, speech, navigation, and an LLM entered the same system: who decides what happens next, and who owns the body when two modules want it to move?
If a cloud service fails, perception is wrong, or the LLM invents something, how does the system return to a safe state?
Those questions gradually turned PawAI from a collection of feature prototypes into a systems-engineering project. The focus shifted from “adding one more model” to three more important goals:
When PawAI began, I was primarily a web and software developer with almost no robot-hardware integration experience.
Our first serious robotics challenge was a Go2 Pro, whose development path is more restricted than the Go2 EDU and relies heavily on WebRTC and community tooling.
My rough personal estimate is more than 500 hours invested in this project.
Nights, weekends, and gaps between classes went into wiring, rerunning tests, reading logs, recording bags, tuning parameters, and discarding solutions that had looked reasonable the day before.
Before the public release, the repository recorded 138 active commit days and more than 1,300 commits authored by me. A later lessons-learned document named over 70 failure modes.
Those numbers do not measure quality or hours, but they preserve the density of the iteration.
On one Sunday in November 2025, I worked from after lunch until 1 a.m. My diary ended with: “I worked until one in the morning, yet it made me feel that engineering might truly be my calling.”
PawAI is one workspace containing ten core ROS2 packages. Perception, decision, action, and shared contracts are separated into layers.
Dependencies point toward common interfaces instead of letting every feature call every other feature directly.

Read the diagram from the bottom up. The Go2 driver and hardware “do the real work.” The Executive decides whether it is allowed. The Brain decides what is appropriate to propose. Perception nodes only turn the world into events.
Between them, go2_interfaces and pawai_contracts define a shared language for events, skill proposals, and execution results.
This separation prevents two failures. A perception module cannot command the robot simply because it detected something. Adding a new model also does not require rewriting Brain, Studio, and the Go2 driver together.
Each ROS2 node can be started, stopped, tested, or replaced independently while the system continues to communicate through stable contracts.
PawAI does not stop at saying that it “uses AI.” Every perception and speech path has an explicit model, execution location, resource budget, and fallback. This is the main stack used for the June 2026 demo and public release.
| Capability | Actual model and pipeline | Runs on | Why it was chosen | Current boundary |
|---|---|---|---|---|
| Face | YuNet 2023mar detection + SFace 2021dec embedding + IOU tracking / identity hysteresis | Jetson CPU | YuNet reached 71.3 FPS in a single-model benchmark without taking YOLO’s GPU | Low light, distance, and tracking jitter can still change identity results |
| Gesture | MediaPipe Gesture Recognizer + custom geometric / temporal rules | Jetson CPU | About 7.2 FPS and 0% GPU, leaving room for the other models | Static gestures are stronger; dynamic wave has not passed the baseline |
| Pose | MediaPipe Pose + COCO-17 adapter + geometric classifier | Jetson CPU | About 18.5 FPS without filling the GPU | Awkward viewpoints can create false landmarks; no reliable fall-care claim |
| Object and color | YOLO26n ONNX + ONNX Runtime TensorRT EP FP16 + OpenCV 12-color HSV | Jetson GPU + CPU | About 15 FPS; YOLO predicts class while HSV adds lightweight color semantics | Distant small objects and cup / bottle confusion remain limitations |
| ASR | GPU-server SenseVoice → Jetson SenseVoice Local → Whisper Local | Remote GPU / Jetson | The cloud path prioritizes recognition quality while local paths preserve degradation options | Microphone, VAD, echo, and network conditions still affect end-to-end behavior |
| LLM | OpenRouter openai/gpt-5.4-mini → conditional google/gemini-3-flash-preview → RuleBrain | Cloud / Jetson rules | The primary path writes natural language and skill proposals; rules provide a floor | The LLM never receives direct actuation authority |
| TTS | Long / emotional: google/gemini-3.1-flash-tts-preview → edge-tts → Piper; short / safety: edge-tts → Piper | Cloud / Jetson CPU | Separate quality, speed, and offline-availability lanes | Provider, audio format, and Go2 playback can fail independently |
| Decision orchestration | LangGraph Conversation Engine + Skill Registry + Executive | Jetson | Splits world state, persona, memory, model decisions, and traces into observable stages | LangGraph proposes; Executive releases production motion |
The table looks like a clean answer, but model selection reversed three times. Gesture and pose moved from RTMPose saturating the GPU to DWPose TensorRT keypoint drift and JetPack 6 deployment friction.
Only hardware experiments overturned our earlier belief that MediaPipe could not run on Jetson.
The real selection criterion was never single-model accuracy alone. It was: on 8GB of unified memory, can face, pose, gesture, object, and speech still coexist without taking the whole system down?
When someone tells PawAI to “introduce yourself,” the path is not one LLM call. It is a six-step observable flow:
/event/speech_intent_recognized or /brain/text_input.SkillPlan.SAY, MOTION, or NAV steps in order.The system may therefore speak a valid reply while rejecting the accompanying action proposal. Having permission to answer and having permission to move the robot are separate things in PawAI.
PawAI Brain is not an agent that can simply do whatever it generates. Its output is a proposal that passes through three layers before execution:

This structure is not decoration; the robot can physically move. A model may be wrong, perception may be incomplete, or several events may arrive at once.
Defaulting to no motion, rejecting unauthorized skills, and preserving human intervention matter more than making the demo look intelligent.
The public claim is narrower: the deterministic Safety Gate and skill allowlist exist and have unit-test protection.
That does not mean PawAI never hallucinates, nor that every safety scenario has passed end-to-end hardware validation.
PawAI uses LangGraph to orchestrate the Conversation Engine. It does not control motors. The implementation has 12 graph stages; the list below groups them into nine concepts that are easier to read.
The early llm_bridge_node handled the ROS adapter, persona, model fallback, conversation history, environment context, JSON parsing, and TTS output in one place. As it grew, hidden bugs became difficult to locate.
In one case, Chinese replies kept being cut off around a comma. The cause was not the model, but a stale Jetson install/ tree containing an old truncation rule.
That incident led me to extract conversation orchestration into the pawai_brain package.
LangGraph turns one conversation round into a pipeline similar to this:
reply, an optional skill, and arguments.The Conversation Engine intentionally returns a small contract:
{ "reply": "I am PawAI, a robot dog learning to understand its environment.", "skill": "self_introduce", "args": {}}The reply may become speech; the skill is only a proposal. A low-risk skill such as show_status can pass the gate and execute.
A longer motion sequence may remain trace-only and be triggered from Studio under human control. Navigation and high-risk actions are never unlocked simply because the LLM suggested them.
LangGraph did not replace the previous path in one step. Feature flags let the legacy and LangGraph engines run as primary or shadow.
Only the primary could publish /brain/chat_candidate; the shadow received the same input but produced traces only.
This let me compare replies, structured outputs, and failures before switching the main path. If the new architecture failed, the old path still worked.
Brain depended only on the stable chat_candidate contract and did not need to know which engine produced it.
PawAI does not send everything to the cloud, nor does it require Jetson to perform every task alone. Deployment is split across four roles:
| Location | Primary responsibility | Behavior during failure |
|---|---|---|
| Jetson Orin Nano | ROS2 runtime, perception, Brain, Executive, and some local speech | Keeps local perception, rules, and safety paths available |
| Development / operator laptop | PawAI Studio, presentation, and human control | A Studio failure cannot bypass or change the Executive |
| Cloud model services | Higher-quality ASR, LLM, and TTS | Falls back to alternate providers, local models, or RuleBrain |
| Unitree Go2 | Sport API, Megaphone, and physical motion | Production sport motion uses Executive; audio and hard-stop use independent paths |
ASR, LLM, and TTS are designed with layered fallback. The Jetson microphone path can move from SenseVoice cloud to local / Whisper.
The LLM can fall back to another model and RuleBrain, while TTS can fall back to edge-tts / Piper. Safety, the allowlist, and Executive remain local.
That does not mean the fully offline voice loop has passed validation. Studio’s laptop-capture path currently depends on cloud SenseVoice, so it remains a single point of failure.
Offline-first is an architectural direction, not a baseline-passed public capability.
The repository still preserves direct legacy / demo publishers, which production flags must disable. This is technical debt that remains visible; the design constraint is not yet a universal structural fact across the entire repo.
With those publishers disabled, a Brain failure produces no new proposals and an Executive failure closes the regular sport-motion mainline.
Audio and hard-stop stay deliberately independent; the latter still needs dedicated audit and validation.
After integration, the common problem was not “there is no data.” Data arrived and a module decided, but no action followed.
Terminal logs alone could not quickly reveal where an event had been ignored, degraded, or blocked.
I built PawAI Studio as a conversational console that combines chat, perception panels, and decision traces. The operator can see perception events, Brain proposals, Safety Gate results, and Executive decisions in the same interface.

Studio is not valuable because a polished interface proves a capability works. Its value is the evidence chain: why the robot reacted, why it did not react, and which layer stopped it.
I did not want “supports many features” to replace real validation, so PawAI separates implementation capability, development observations, and trusted measured evidence.
| Capability | Current status | What that means |
|---|---|---|
| Registered-user recognition | Passed under controlled conditions | One registered user, limited distance and lighting; not general identity verification or access control |
| Nearby cup detection | Passed under controlled conditions | About one meter, cup-only scenario; not a claim that all 80 object classes are reliable |
| Fixed voice commands | Passed under controlled conditions | Classifies a limited command set into system intents; not reliable free-form voice control |
| Brain safety gate | Mechanism and unit tests pass | Can block unauthorized skills; does not prove that Brain never hallucinates |
| Dynamic waving and voice stop | Not passed | Failures are retained and are not presented as current results |
| Pose / fall scenarios | Insufficient evidence | Development signals may appear in Studio, but no care or safety claim is made |
| Autonomous navigation and dynamic avoidance | Experimental | Tested only with human supervision and an emergency-stop procedure; no complete autonomy claim |
This is not a list of weaknesses. To me, it records PawAI’s most important transition: from demonstrating features to taking responsibility only for what the evidence supports.
The finished diagram can make it look as if we always knew where Brain, Executive, LangGraph, and the Safety Gate belonged.
In reality, many boundaries were written only after hardware moved incorrectly, models misclassified a scene, or the demo pipeline locked up.
aiortc 1.14 / STUN compatibility problem. Pinning 1.9 and removing STUN cut DataChannel startup from timeout to roughly 0.5 seconds.install/, not three models.Move{x} and kept the previous command alive. Wall impacts forced StopMove(1003), deduplication, heartbeats, and one sport-motion arbiter.These incidents are not trophies. Every collision and false detection meant the safety design was incomplete. What matters is that we kept the failures and converted them into contracts, tests, arbitration rules, and narrower public claims.
Go2 Pro does not expose the same complete official unitree_sdk2 / unitree_ros2 path as the Go2 EDU. We extended a community WebRTC bridge instead, while treating every unknown command conservatively because the physical risk was real.

October 2025: not a finished system, but a team of robotics beginners facing a Go2 Pro for the first time.
I started from abizovnuralem/go2_ros2_sdk, the WebRTC DataChannel, and Sport Mode APIs.
On November 8, Windows finally made the Go2 stand and sit. Ten days later, aiortc / STUN incompatibility made control almost disappear again.
Then came Windows, a Mac VM, Ubuntu, ROS2, RViz2, Foxglove, dual NICs, and DDS. A broken ROS2 installer, VM crashes, old CycloneDDS syntax, and SCTP timeouts stacked together. Network topology alone consumed several days.
The early loop was direct: capture an image, ask a model to understand it, then generate movement. On December 9, obtaining one image took three hours before I found that a stale ros2 daemon cache was blocking DDS discovery.
By mid-December, visual avoidance was only about 50–60% stable during development. We tested Depth Anything, calibrated the Go2 wide-angle camera, and connected a Mac VM, Windows, and a remote GPU through tunnels.
2025-12-15 | Visual Navigation v1. A historical living-room prototype, not a claim of reliable general-purpose autonomy.

Every added sensor forced us to revisit weight, power, cable routing, cooling, and serviceability.
The January 7 presentation became a turning point. A professor called out the latency and safety limits of “capture → LLM → move.” We shifted toward Jetson, D435, real-time ROS2 perception, and lower-level action safety.
Face, Speech, Gesture, Pose, and Object did not arrive together. On March 19, I spent an evening building the first benchmark framework, adapters, reporter, gates, and tests.
We then evaluated 16 models and exposed package, precision, and resource-contention failures.
We hit onnxruntime-cpu replacing the GPU build, NumPy 2.x conflicts, an older YuNet crashing on a newer OpenCV, and RTMPose placing hand landmarks on faces. The stack finally converged on YuNet / SFace, MediaPipe, YOLO26n, and SenseVoice.
Speech was not complete when a microphone produced text. VAD, echo, Whisper hallucinations, the Go2 speaker, and 5.3–14.5 second latency forced us to validate the microphone, USB speaker, ASR, TTS, and Echo Gate separately.
2026-03-18 | An early speech-chain test. Producing sound was still far from stable interaction.
Jetson, D435, RPLIDAR, microphones, and speakers do not attach themselves to a Go2.
We handled voltage, current spikes, USB contact, heat, weight balance, field of view, and LiDAR orientation. I also 3D-printed the custom back cover and brackets before the software had a stable place to run.
2026-04-25 | With the custom 3D-printed back cover installed, the Jetson, D435, and RPLIDAR could finally stand on the Go2 as one complete stack.
The month was unforgiving. One pip install ultralytics broke Torch, NumPy, and every model together. RPLIDAR then brought ARM64 crashes, Cartographer problems, reversed orientation, a shaking mount, and four maps that had to be discarded.
The early llm_bridge_node.py grew beyond 1,100 lines while handling ROS, persona, fallback, history, JSON, and TTS.
Once errors became impossible to localize, I extracted the Conversation Engine into LangGraph and introduced the new path in shadow mode.
On May 2, a recovery action accidentally sent Damp(1001), releasing motor force and dropping the Go2.
Later short navigation exposed that Move{x:0} did not stop, sensor orientation was wrong, and safety distance was measured in the wrong frame.
Acceptance tests offered no miracle. Face and speech were relatively stable; object detection was poor, gestures misfired, and pose definitions were unclear. We stopped using “implemented” as a synonym for “reliable.”
Conversation Engine, LangGraph, TTS, Go2 audio, LiDAR, and the five-scene demo all entered together. Face, cup, pose, and gesture competed for TTS, state, and body control.
Features did not add—they multiplied one another’s failure surface.
We fixed a 10-second fusion window that missed pose + cup, a phase gate that silently suppressed events, repeated greetings, TTS cooldowns, and multiple /webrtc_req publishers.
A LiDAR launcher missing --ros-args also made healthy hardware look dead.
The June 4 trusted baseline forced the story back to reality. Of 15 capabilities, only face, fixed voice commands, and a nearby cup passed narrow conditions.
Wave and voice stop failed; several others remained insufficient, and overall readiness was not_ready.
The June 17 rehearsal still produced more trouble than expected. On June 18, the live 18-minute presentation completed successfully. My diary reduced the entire experience to one line:
Everything in the process went wrong; the result went right.
On June 23, we organized ten core ROS2 packages, bilingual documentation, and a newcomer bring-up path before publishing the first repository release.
It remains a multimodal embodied-interaction prototype, not a finished autonomous-robot product.
By the later stages, PawAI had developed a conservative workflow that fits physical robots well:
This workflow emerged from repeated integration failures. It helped PawAI evolve from a student project with many features into an engineering system that knows what it can currently prove—and when it must stop.
PawAI was a team capstone. This page focuses on the system integration, Brain, speech, hardware bring-up, testing, and public-release work that I personally owned or deeply contributed to.
My primary work was turning separate hardware, perception models, and AI features into a system that could be operated, observed, and validated:
Face, speech, or object recognition may work alone. Once they share a moving robot, they compete for GPU, camera, audio, state, and the action exit. Coordination—not model count—is the real difficulty.
Having a Navigation Action, Safety Gate, or Studio display proves that a path exists. It does not prove success rate, reliability, or safety in a real environment.
Voice stop failing, dynamic waving not passing, and navigation drifting off course explain more about how I learned robotics systems than an edited success video.
PawAI preserved not only features, but also methods for stopping claims, degrading safely, and validating again.
PawAI will continue to improve, but the principle stays the same: establish reproducible evidence before expanding public claims.
The next stage includes:
Source code and public documentation: GitHub — roy4222/PawAI