Skip to content

Go2 EDU: Hardware en profundidad

Este documento va más allá del bridge de alto nivel y explora todo lo que el Go2 EDU expone al desarrollador: cada sensor, cada joint, cada topic DDS y cada llamada de bajo nivel disponible para investigación.

Variantes del Go2 y qué incluye cada una

Característica Go2 Air Go2 Pro Go2-X Go2 EDU Std Go2 EDU Plus
Precio aprox. $2 590 $3 799 $7 600 $13 000 $16 000+
SDK / ROS 2 ✗ ✗ ✓ ✓ ✓
Compute CPU básico CPU básico Orin NX Orin Nano 40 TOPS Orin NX 100 TOPS
LiDAR L1 4D L1 4D L1 4D L1 4D + XT16 L1 4D + upgradeable
Batería 8 000 mAh 8 000 mAh 8 000 mAh 15 000 mAh 15 000 mAh
Carga útil 3 kg 3 kg 5 kg 5 kg 8 kg (12 kg límite)
D1 arm (acc.) ✗ ✗ Opcional Opcional ✓ Compatible

Sin SDK no hay ROS 2

El Go2 Air y Pro no dan acceso al SDK. Para el curso (U1-U12) y cualquier investigación personalizada necesitas Go2-X, EDU Standard o EDU Plus. Esto no puede desbloquearse con firmware — es una diferencia de hardware.

Arquitectura de cómputo

NVIDIA Jetson Orin NX (EDU Plus, 100 TOPS)

CPU: 8-core ARM Cortex-A78AE (64-bit)
GPU: 1024-core NVIDIA Ampere
DLA: 2× Deep Learning Accelerator
RAM: 16 GB LPDDR5
Almacenamiento: 64 GB eMMC + microSD
TOPS: 100 (combinando CPU + GPU + DLA)

El Orin NX puede ejecutar: - Inferencia en tiempo real de modelos ViT / YOLOv8 (GPU) - SLAM 3D (Point-LIO) a 10 Hz (CPU + DLA) - Dos nodos ROS 2 + bridge + teleop simultáneos sin saturación

NVIDIA Jetson Orin Nano (EDU Standard, 40 TOPS)

CPU: 6-core ARM Cortex-A78AE
GPU: 1024-core NVIDIA Ampere (escala reducida)
RAM: 8 GB LPDDR5
TOPS: 40

Suficiente para el curso completo (U1-U12) y la mayoría de proyectos de investigación. Insuficiente para inferencia de modelos >1B parámetros en tiempo real.

Sistema de actuación: 12 motores

El Go2 tiene 4 patas × 3 joints = 12 DOF:

Pelvis
├── Pierna delantera izquierda (FL)
│   ├── FL_hip    (abducción/aducción)
│   ├── FL_thigh  (flexión del muslo)
│   └── FL_calf   (flexión de la pantorrilla)
├── Pierna delantera derecha (FR)
│   ├── FR_hip
│   ├── FR_thigh
│   └── FR_calf
├── Pierna trasera izquierda (RL)
│   ├── RL_hip
│   ├── RL_thigh
│   └── RL_calf
└── Pierna trasera derecha (RR)
    ├── RR_hip
    ├── RR_thigh
    └── RR_calf

Cada motor: - Torque máximo: 45 N·m - Velocidad: hasta 21 rad/s - Resolución de posición: ~0.001 rad - Control: posición, velocidad o torque (según modo)

Desde el SDK2 (LowCmd), puedes controlar cada joint individualmente:

from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowCmd_

cmd = LowCmd_()
# Joint 0 = FL_hip, 1 = FL_thigh, 2 = FL_calf
# Joint 3 = FR_hip, ...
# Joint 9 = RL_hip, ...
cmd.motor_cmd[1].q   = 0.67   # ángulo target (rad)
cmd.motor_cmd[1].kp  = 60.0   # rigidez proporcional
cmd.motor_cmd[1].kd  = 5.0    # amortiguamiento
cmd.motor_cmd[1].tau = 0.0    # torque feedforward

LowCmd requiere 10 ms de ciclo exacto

El control de bajo nivel a través de LowCmd requiere enviar comandos cada 2–10 ms. Si el ciclo se interrumpe más de 100 ms, el robot entra en modo de seguridad y cae. Usa el API de SportClient (alto nivel) a menos que seas desarrollador de gaits.

Suite de sensores

LiDAR L1 — Unitree 4D

El LiDAR L1 es el sensor principal de percepción del Go2:

Parámetro Valor
Canales 16 (por rotación)
FOV vertical 90° (−45° a +45°)
FOV horizontal 360°
Rango mínimo 0.05 m
Rango máximo 20 m (típico), 30 m (interior)
Frecuencia 10 Hz
Puntos por segundo ~240 000
Salida PointCloud2 en /utlidar/cloud
Precisión ±2 cm
# Ver la nube de puntos en tiempo real
ros2 topic echo /utlidar/cloud --no-arr   # solo metadatos
ros2 topic hz /utlidar/cloud              # frecuencia actual

# Con RViz2
ros2 run rviz2 rviz2
# Fixed Frame: base_link
# Add → PointCloud2 → /utlidar/cloud

Para convertir a LaserScan 2D (lo que usa el bridge del curso):

ros2 run pointcloud_to_laserscan pointcloud_to_laserscan_node \
  --ros-args \
  -p target_frame:=base_scan \
  -p min_height:=0.1 \
  -p max_height:=0.5 \
  -p angle_min:=-3.14159 \
  -p angle_max:=3.14159

Intel RealSense D435i (profundidad + IMU)

Canal Topic Tipo
RGB /camera/color/image_raw sensor_msgs/Image
Profundidad /camera/depth/image_rect_raw sensor_msgs/Image
Nube 3D /camera/depth/color/points sensor_msgs/PointCloud2
IMU interno /camera/imu sensor_msgs/Imu
# Usar la cámara de profundidad para estimar distancia a obstáculo frontal
from sensor_msgs.msg import Image
import numpy as np

def depth_cb(self, msg: Image):
    # El D435i publica uint16 en mm
    data = np.frombuffer(msg.data, dtype=np.uint16)
    data = data.reshape(msg.height, msg.width)
    centro = data[msg.height // 2, msg.width // 2]
    dist_m = centro / 1000.0
    self.get_logger().info(f"Distancia frontal: {dist_m:.2f} m")

5 Cámaras fish-eye estéreo

El Go2 EDU lleva 5 cámaras de ojo de pez (amplio ángulo) que permiten: - Visión 360° del entorno inmediato - Estimación de profundidad stereo - Detección de obstáculos en las patas

Topics (nombres aproximados, varían por firmware): - /front_camera/image_raw - /rear_camera/image_raw - /left_camera/image_raw - /right_camera/image_raw - /top_camera/image_raw

4 Sensores de fuerza en las patas

Cada pata tiene un sensor que mide la fuerza de contacto con el suelo:

from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_

def lowstate_cb(self, msg: LowState_):
    # foot_force: [FL, FR, RL, RR] en Newtons
    fl_force = msg.foot_force[0]
    fr_force = msg.foot_force[1]
    rl_force = msg.foot_force[2]
    rr_force = msg.foot_force[3]

    # Si la fuerza es 0, esa pata está en el aire
    patas_en_suelo = sum(1 for f in msg.foot_force if f > 10.0)
    self.get_logger().info(f"Patas en suelo: {patas_en_suelo}/4")

Aplicaciones: detección de terreno irregular, clasificación de superficie, control adaptativo de contacto.

IMU (dentro del LiDAR L1)

El LiDAR L1 contiene una IMU que publica:

# Via DDS directo:
from unitree_sdk2py.idl.unitree_go.msg.dds_ import LowState_

def state_cb(self, msg: LowState_):
    imu = msg.imu_state
    roll  = imu.rpy[0]   # radianes
    pitch = imu.rpy[1]
    yaw   = imu.rpy[2]

    gyro_x = imu.gyroscope[0]   # rad/s
    gyro_y = imu.gyroscope[1]
    gyro_z = imu.gyroscope[2]

    acc_x = imu.accelerometer[0]   # m/s²

En ROS 2 (a través del bridge): /imu/data (si el bridge lo republica).

Temas DDS — mapa completo

El SDK2 usa Fast DDS sobre la red del robot. Topics disponibles:

Nombre DDS Descripción IDL
rt/sportmodestate Posición, velocidad, modo de marcha SportModeState_
rt/lf/lowstate Estado de joints, IMU, fuerza de patas, batería LowState_
rt/lf/lowcmd Comando de joints de bajo nivel LowCmd_
rt/utlidar/cloud Nube de puntos del LiDAR L1 PointCloud2
rt/utlidar/imu IMU del LiDAR IMUState_
rt/api/sport/request Comandos del SportClient Request_
rt/api/sport/response Respuestas del SportClient Response_
rt/audio/sdk/tomic Audio desde el micrófono del robot Audio

SportModeState — lo que lee el bridge de /odom

from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_

# Campos principales:
msg.position        # [x, y, z] en metros (frame del mundo)
msg.velocity        # [vx, vy, vz] m/s
msg.imu_state.rpy   # [roll, pitch, yaw] radianes
msg.imu_state.gyroscope     # velocidades angulares rad/s
msg.imu_state.accelerometer # aceleración m/s²
msg.foot_position_body      # posición de cada pata relativa al cuerpo
msg.foot_speed_body         # velocidad de cada pata
msg.mode                    # modo de marcha actual
msg.gait_type               # tipo de gait (0=idle, 1=trot, 2=run)
msg.foot_raise_height       # altura de elevación de pata (m)
msg.body_height             # altura del cuerpo sobre el suelo (m)

Modos de marcha y comportamientos

Gait types disponibles

ID Nombre Descripción
0 Idle Sin movimiento de patas
1 Trot Trote (2 patas en el aire, diagonales)
2 Running trot Trote rápido
3 Climbing Modo de escalada de escalones
4 Crawl Rastro (4 puntos de apoyo siempre)

Sport behaviors completos (via SportClient)

ID Nombre Descripción
1001 Damp Amortiguamiento — robot afloja joints
1002 BalanceStand Se para y equilibra en lugar
1003 StopMove Para el movimiento
1004 StandUp Se levanta desde el suelo
1005 StandDown Se sienta
1006 RecoveryStand Recuperación si volcó
1007 Euler(roll, pitch, yaw) Inclina el cuerpo en ángulos
1008 Move(vx, vy, vyaw) Movimiento base
1009 Sit Se sienta
1010 RiseSit Se levanta desde sentado
1013 BodyHeight(h) Cambia altura del cuerpo
1014 FootRaiseHeight(h) Cambia altura de elevación de pata
1016 Hello Saluda con la pata
1017 Stretch Se estira
1022 Dance1 Baile 1
1023 Dance2 Baile 2
1028 Pose(pitch) Postura de modelo
1030 FrontFlip Voltereta frontal
1031 FrontJump Salto frontal
1032 FrontPounce Salto sobre objetivo

Para behaviors parametrizados (Euler, Move, BodyHeight) usa el payload JSON del SportClient:

import json

# Cambiar altura del cuerpo a 0.25 m
data = json.dumps({"data": 0.25})
sport_client.SendApiRequest(1013, data)

Brazo D1 — manipulación embarcada

El Unitree D1 es el brazo robótico diseñado para el Go2:

Característica Valor
DOF 6 (+ 1 gripper)
Alcance 600 mm
Carga máxima 500 g (extendido)
Peso del brazo 2.5 kg
Interfaces CAN bus o Ethernet
SDK unitree_arm_sdk

El D1 se monta sobre el cuerpo del Go2 EDU (con la plataforma de montaje). El control se realiza con un SDK separado que se integra con el de locomoción:

from unitree_arm_sdk import ArmClient

arm = ArmClient("d1")
arm.init()

# Mover a posición cartesiana (x, y, z en metros desde la base del brazo)
arm.moveTo(x=0.3, y=0.0, z=0.1)

# Abrir/cerrar gripper (0.0=cerrado, 1.0=abierto)
arm.setGripper(0.8)

Para integración con ROS 2:

# Paquete de la comunidad con interfaz ROS 2 para D1:
git clone https://github.com/unitreerobotics/unitree_arm_ros2.git

Variante Go2-W (con ruedas)

El Go2-W añade ruedas sobre las patas traseras. Las patas delanteras siguen siendo articuladas. Esto permite: - Velocidad aumentada en terreno plano (modo rueda) - Capacidad de escalar obstáculos (modo pata) - Transición automática entre modos

La interfaz ROS 2 es idéntica al Go2 estándar — cmd_vel, odom, scan, /sport/*. El SDK añade control de las ruedas pero el bridge del curso funciona sin cambios.

Red y configuración de entorno

# 1. Conectar Go2 EDU por Ethernet (IP típica: 192.168.123.161)
# O por WiFi (el Go2 actúa como AP: SSID Unitree_Go2_XXXX)

# 2. Configurar la interfaz de red:
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export UNITREE_WIRED_ETHERNET="eth0"    # o wlan0 para WiFi

# 3. Verificar conectividad
ping 192.168.123.161

# 4. Verificar que el DDS está disponible (requiere unitree_sdk2py)
python3 -c "
from unitree_sdk2py.core.channel import ChannelFactory
ChannelFactory.Instance().Init(0, 'eth0')
print('DDS OK')
"

# 5. Ver topics DDS disponibles
# (no es ros2 topic list — es el bus DDS interno)
from unitree_sdk2py.core.channel import ChannelFactory

Acceso SSH al robot

# SSH al Jetson Orin integrado
ssh unitree@192.168.123.18    # contraseña: 123

# Desde el Jetson puedes correr el bridge directamente:
python3 ~/unitree_ros2_bridge.py --network_interface eth0

Herramientas de diagnóstico

# Estado de batería en tiempo real
ros2 topic echo /battery_state

# Verificar odometría (posición del robot)
ros2 topic echo /odom --field pose.pose.position

# Frecuencia del LiDAR
ros2 topic hz /scan     # ~10 Hz esperado
ros2 topic hz /utlidar/cloud  # ~10 Hz en el DDS

# Ver la nube de puntos del LiDAR
ros2 run rviz2 rviz2 &

# Calibrar IMU (necesario una vez por sesión en autonomy_stack_go2)
ros2 run calibrate_imu calibrate_imu