Your program does not call a robot function: it publishes and subscribes to messages on a DDS bus. In this lesson you set up the manufacturer's bench (simulator, SDK, and CycloneDDS 0.10.2), bring up the simulated G1, and read the first state message, through the same path you would use with the real robot.
1The path: your program → SDK → DDS → robot or simulator
2The bench in ~/fth with pinned versions, including the compiled CycloneDDS
3High level (ready-made locomotion) × low level (motor by motor)
4Domain and interface: the mistake that makes the robot look dead
Goal: set up the manufacturer's bench (simulator, SDK, CycloneDDS 0.10.2) and read the G1's first state message over DDS.
01 · From your program to the motor
Click each block.
Click each block in the diagram to see what it does.
Swapping the simulator for the robot changes the domain and the interface, not your code.
02 · Three things to know before installing
Click each one.
1. High × low
High level: the manufacturer's locomotion controller walks and you ask for a velocity (LocoClient in the "sport" service: SetVelocity, Move, Damp, ZeroTorque).
Low level: you command each motor through rt/lowcmd. First, the official example releases the internal controller (MotionSwitcherClient, CheckMode and ReleaseMode), otherwise the two fight over the motors.
2. Domain and interface
The simulator uses domain 1 on the lo interface (config.py in unitree_mujoco). The SDK examples use domain 0 and the name of the network card connected to the robot.
A program and a robot on different domains can't see each other: the script waits and the robot looks dead. The "Common error" tab shows the message.
Real robot networkThe unitree_ros2 README puts the PC on a fixed IP in the 192.168.123.x subnet (example: 192.168.123.99) and the interface goes by name (enp3s0).
3. Which G1
The base G1 costs US$ 13.5 thousand on the official page (read on 10/19/2026), but it has no secondary development: no SDK. To program as in this course, you need the EDU version, sold on request.
Resellers cited by the training research (Oct/2026): US$ 43.5 thousand to 73.9 thousand (reported; varies by version and reseller). The simulator costs nothing and speaks the same DDS.
03 · Set up the bench (once)
A venv in ~/fth (the same one from course 0, if you took it), the simulator and SDK pinned to commits, and CycloneDDS 0.10.2 compiled: pip alone fails on aarch64 without the C library. You need python3.12 (with venv), git, cmake, and a C compiler.
cyclonedds C 0.10.2 instalado em ~/fth/cyclonedds/install
cyclonedds 0.10.2 · unitree_sdk2py 1.0.1 · mujoco 3.16.0
The two commits and the three versions. The build takes a few minutes; the compiler warning is hidden in /dev/null.
04 · Bring up the simulated G1 and read the state
kit/sim.py runs the unitree_mujoco bridge with no window, in the background, for 8 s; ler_estado.py is another process that only subscribes to rt/lowstate. The G1 hangs from the elastic band (the simulator's gantry).
class Estado:
"""Assina rt/lowstate e guarda a última mensagem e quantas chegaram."""
def __init__(self):
from unitree_sdk2py.core.channel import ChannelSubscriber
from unitree_sdk2py.idl.unitree_hg.msg.dds_ import LowState_
self.ultima, self.n, self.t_ultima = None, 0, 0.0
self.sub = ChannelSubscriber("rt/lowstate", LowState_)
self.sub.Init(self._chegou, 10)
Shown · only with the real robot, after the checklist
# Trecho de example/g1/low_level/g1_low_level_example.py (unitree_sdk2_python 814556d)
ChannelFactoryInitialize(0, sys.argv[1]) # domínio 0 + nome da placa ligada ao robô
self.msc = MotionSwitcherClient()
self.msc.SetTimeout(5.0)
self.msc.Init()
status, result = self.msc.CheckMode()
while result['name']: # libera o controle interno antes do baixo nível
self.msc.ReleaseMode()
status, result = self.msc.CheckMode()
time.sleep(1)
Example from the repository
simulador: G1 29 motores · DDS domínio 1 em lo · 8 s · suspenso: sim
tópico: rt/lowstate · tipo: unitree_hg LowState_
IMU quatérnio (w, x, y, z): [1.0, 0.0, 0.1, 0.0] · tronco em pé (inclinação < 20°): sim
junta 0 (left_hip_pitch): q = 0.0 rad · torque estimado = 0 N·m
motores no pacote: 35 (o G1 de 29 GDL usa os índices 0 a 28)
saída: 0
simulador: fim em t = 8.0 s · robô suspenso (pés fora do chão)
The packet has 35 motor slots; the 29-DoF G1 uses the first 29. Loose motors: zero torque.
ERRO: nenhuma mensagem em rt/lowstate em 10 s. O simulador está rodando? (kit/sim.py)
saída: 1
No message in 10 s: the simulator is not running, or your program is on another domain or interface.
05 · Practice
Bench ready and first message read.
E1 · Set up the bench
15 min
Run the two setup blocks. If cmake complains, install cmake and a C compiler with your system's package manager and run the second block again.
Run the simulator block with the reading and explain why the packet has 35 motors if the G1 has 29.
Done when:python3 kit/ler_estado.py; echo "saída: $?" (with sim.py running) → motores no pacote: 35 (o G1 de 29 GDL usa os índices 0 a 28) and saída: 0.
E3 · See the domain error
3 min
Run only ler_estado.py, without the simulator, and wait the 10 s.
Done when:ERRO: nenhuma mensagem em rt/lowstate em 10 s and saída: 1.
06 · Lesson quiz
The explanation appears after your answer.
Question 1 of 4
ler_estado.py waited 10 s and then failed. What is the most likely cause?skill
Correct
Publisher and subscriber only find each other on the same domain and the same interface. The simulator uses domain 1 on lo; the SDK example uses domain 0.
Not this one
Publisher and subscriber only find each other on the same domain and the same interface. The simulator uses domain 1 on lo; the SDK example uses domain 0.Review: Three things to know before installing
Question 2 of 4
Which topic carries the G1's state (IMU and motors)?
Correct
State comes on rt/lowstate and commands go on rt/lowcmd, both from the unitree_hg IDL. /robot_state and RobotState_ were from an old course and do not exist in the SDK.
Not this one
State comes on rt/lowstate and commands go on rt/lowcmd, both from the unitree_hg IDL. /robot_state and RobotState_ were from an old course and do not exist in the SDK.Review: From your program to the motor
Question 3 of 4
Before sending low-level commands to the real robot, what does the official example do?
Correct
CheckMode and ReleaseMode until the internal control is gone; otherwise your rt/lowcmd and the manufacturer's controller fight over the motors. In the simulator this service does not exist.
Not this one
CheckMode and ReleaseMode until the internal control is gone; otherwise your rt/lowcmd and the manufacturer's controller fight over the motors. In the simulator this service does not exist.Review: Set up the bench (once)
Question 4 of 4
Why does the course compile CycloneDDS instead of just using pip?
Correct
The cyclonedds 0.10.2 Python package needs the C library; on aarch64 there was no prebuilt binary. You compile tag 0.10.2 and point CYCLONEDDS_HOME to it.
Not this one
The cyclonedds 0.10.2 Python package needs the C library; on aarch64 there was no prebuilt binary. You compile tag 0.10.2 and point CYCLONEDDS_HOME to it.Review: Set up the bench (once)
Quiz complete
You got {a} of {n} right. Get them all right and the lesson is marked as completed.
Choose an option. The explanation appears after your attempt.
07 · In three sentences
BusProgram and robot talk over DDS: rt/lowstate and rt/lowcmd.
DomainSimulator on domain 1 on lo; robot on 0 on the network card.
Benchunitree_mujoco 1eb6642, SDK 814556d and CycloneDDS 0.10.2.