FTH 4 · Operar un humanoide real|INEMA.CLUB-PRO

Clase 2 · 20 min

Por dentro del software: DDS, SDK, alto y bajo nivel

Tu programa no llama a una función del robot: publica y se suscribe a mensajes en un bus DDS. En esta clase armas la bancada del fabricante (simulador, SDK y CycloneDDS 0.10.2), levantas el G1 simulado y lees el primer mensaje de estado, por el mismo camino que usarías con el robot real.

Empezar la clase
Notebook sobre una bancada conectado por un cable de red a un robot humanoide desenfocado al fondo, con un switch de red de luces discretas.

En esta clase vas a

  1. 1El camino: tu programa → SDK → DDS → robot o simulador
  2. 2La bancada en ~/fth con versiones fijadas, incluido el CycloneDDS compilado
  3. 3Alto nivel (locomoción lista) × bajo nivel (motor por motor)
  4. 4Dominio e interfaz: el error que hace que el robot parezca muerto

Objetivo: armar la bancada del fabricante (simulador, SDK, CycloneDDS 0.10.2) y leer el primer mensaje de estado del G1 por DDS.

01De tu programa al motor

Haz clic en cada bloque.

Haz clic en cada bloque del diagrama para ver qué hace.

Cambiar el simulador por el robot cambia el dominio y la interfaz, no tu código.

02Tres cosas que debes saber antes de instalar

Haz clic en cada una.

1. Alto × bajo

Nivel alto: el control de locomoción del fabricante camina y tú pides velocidad (LocoClient en el servicio "sport": SetVelocity, Move, Damp, ZeroTorque).

Nivel bajo: tú mandas en cada motor por rt/lowcmd. Antes, el ejemplo oficial libera el control interno (MotionSwitcherClient, CheckMode y ReleaseMode); si no, los dos se disputan los motores.

03Armar el banco de trabajo (una vez)

Venv en ~/fth (el mismo del curso 0, si lo hiciste), simulador y SDK fijados en commits, y el CycloneDDS 0.10.2 compilado: pip solo falla en aarch64 sin la biblioteca C. Necesitas python3.12 (con venv), git, cmake y un compilador C.

Terminal · simulador y SDK
F=${FTH:-$HOME/fth}; mkdir -p $F && cd $F
[ -d .venv ] || python3.12 -m venv .venv; . .venv/bin/activate
pip install -q mujoco==3.16.0 numpy pygame==2.6.1 mcap==1.6.0
[ -d operar ] || git clone -q --depth 1 https://github.com/inematds/formacao-fth-operar-v72 operar
[ -d unitree_mujoco ] || git clone -q --filter=blob:none --sparse https://github.com/unitreerobotics/unitree_mujoco
(cd unitree_mujoco && git sparse-checkout set simulate_python unitree_robots/g1 && git checkout -q 1eb6642)
[ -d unitree_sdk2_python ] || git clone -q https://github.com/unitreerobotics/unitree_sdk2_python
(cd unitree_sdk2_python && git checkout -q 814556d)
for r in unitree_mujoco unitree_sdk2_python; do git -C $r log -1 --format="$r %h (%ad)" --date=short; done
Terminal · CycloneDDS 0.10.2 y SDK
F=${FTH:-$HOME/fth}; cd $F && . .venv/bin/activate
[ -d cyclonedds ] || git clone -q --depth 1 -b 0.10.2 https://github.com/eclipse-cyclonedds/cyclonedds 2>/dev/null
cmake -S cyclonedds -B cyclonedds/build -DCMAKE_INSTALL_PREFIX=$F/cyclonedds/install -DBUILD_EXAMPLES=OFF > /dev/null 2>&1
cmake --build cyclonedds/build --target install -j 4 > /dev/null 2>&1 && echo "cyclonedds C 0.10.2 instalado em $F/cyclonedds/install"
export CYCLONEDDS_HOME=$F/cyclonedds/install
pip install -q cyclonedds==0.10.2 && pip install -q -e unitree_sdk2_python
python3 -c "from importlib.metadata import version as v; import unitree_sdk2py; print('cyclonedds', v('cyclonedds'), '· unitree_sdk2py', v('unitree_sdk2py'), '· mujoco', v('mujoco'))"
Ejemplo del repositorio
unitree_mujoco 1eb6642 (2026-09-07)
unitree_sdk2_python 814556d (2026-09-21)
cyclonedds C 0.10.2 instalado em ~/fth/cyclonedds/install
cyclonedds 0.10.2 · unitree_sdk2py 1.0.1 · mujoco 3.16.0

Los dos commits y las tres versiones. La compilación tarda unos minutos; el aviso del compilador queda oculto en /dev/null.

04Levantar el G1 simulado y leer el estado

kit/sim.py ejecuta el puente de unitree_mujoco sin ventana, en segundo plano, durante 8 s; ler_estado.py es otro proceso que solo se suscribe a rt/lowstate. El G1 está colgado del elástico (el pórtico del simulador).

Terminal · simulador + lectura
F=${FTH:-$HOME/fth}; cd $F/operar && . $F/.venv/bin/activate
python3 kit/sim.py --s 8 --suspenso &
python3 kit/ler_estado.py; echo "saída: $?"
wait
Fragmento · kit/op.py
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)
Mostrado · solo con el robot real, después del 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)
Ejemplo del repositorio
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)

El paquete tiene 35 posiciones de motor; el G1 de 29 GDL usa las 29 primeras. Motores sueltos: torque cero.

05Práctica

Banco de pruebas listo y primer mensaje leído.

E1 · Montar el banco de pruebas

15 min

Corre los dos bloques de preparación. Si cmake se queja, instala cmake y un compilador C desde el gestor de paquetes de tu sistema y corre el segundo bloque de nuevo.

Listo cuando: la salida muestra cyclonedds 0.10.2 · unitree_sdk2py 1.0.1 · mujoco 3.16.0.

E2 · Leer el G1

5 min

Corre el bloque del simulador con la lectura y explica por qué el paquete tiene 35 motores si el G1 tiene 29.

Listo cuando: python3 kit/ler_estado.py; echo "saída: $?" (con el sim.py corriendo) → motores no pacote: 35 (o G1 de 29 GDL usa os índices 0 a 28) y saída: 0.

E3 · Ver el error del dominio

3 min

Corre solo el ler_estado.py, sin el simulador, y espera los 10 s.

Listo cuando: ERRO: nenhuma mensagem em rt/lowstate em 10 s y saída: 1.

06Prueba de la clase

La explicación aparece después de tu respuesta.

Pregunta 1 de 4

El ler_estado.py se quedó 10 s esperando y dio error. ¿Cuál es la causa más probable?competencia

07En tres frases

BusPrograma y robot conversan por DDS: rt/lowstate y rt/lowcmd.
DominioSimulador en el dominio 1 en lo; robot en el 0 en la placa de red.
Banco de pruebasunitree_mujoco 1eb6642, SDK 814556d y CycloneDDS 0.10.2.

Para ir más allá

Clase anteriorSiguiente clase