BACK_TO_BASE
ROS2与Unitree Go2的介绍和编程笔记
Engineering Notebook // Build Log
/
21:53:11
/
NOTEBOOK_ENTRY

ROS2与Unitree Go2的介绍和编程笔记

目录 1. 前言与概述 1 前言与概述 2. 环境准备 2 环境准备 3. 通讯架构详解 3 通讯架构详解 4. ROS2 话题与消息 API 4 ros2 话题与消息 api 5. ROS2 编程实例 5 ros2 编程实例 6. Isaac Lab 强化学习训练 6 isaac lab 强化学习训练 7. Sim to Real 部署 7 sim to real 部署 8. 参考资源与社区 8 参考资源与社区 1. 前言与概述 1.…

Notebook Time
11 min
Image Frames
1
View Tracks
186
ROSRobotic
FIELD_GUIDE

FIELD GUIDE

Use the guide rail to jump between sections.

目录


1. 前言与概述

1.1 Unitree Go2 简介

Unitree Go2 是宇树科技推出的消费级/教育级四足机器人(机器狗)。它具备以下核心特性:

特性说明
自由度12 个(每条腿 3 个关节:髋关节侧摆、髋关节前摆、膝关节)
主控芯片NVIDIA Jetson(部分型号)
传感器3D LiDAR、深度相机、IMU、脚部力传感器
通讯方式以太网(有线)/ WiFi(无线)
底层通讯协议CycloneDDS(与 ROS2 天然兼容)
重量约 15 kg
续航约 1-2 小时

1.2 学习路径

本教程的学习路径如下:

环境搭建 → 理解通讯架构 → 掌握 API → 编写控制程序 → 强化学习训练 → Sim-to-Real 部署

建议按顺序阅读,每一步都建立在前一步的基础上。


2. 环境准备

2.1 推荐系统环境

组件推荐版本
操作系统Ubuntu 22.04 LTS
ROS2 版本ROS2 Humble Hawksbill
DDS 实现CycloneDDS 0.10.2
Python3.10+

注意:Go2 底层使用 CycloneDDS 进行通讯,必须确保你的 ROS2 环境也使用 CycloneDDS 作为 RMW(ROS Middleware)实现,否则无法与机器人通讯。

2.2 安装 ROS2 Humble

如果尚未安装 ROS2 Humble,请参考 ROS2 官方安装指南,以下为关键步骤:

# 设置 locale
sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

# 添加 ROS2 APT 源
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
  -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] \
  http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | \
  sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

# 安装 ROS2 Humble 桌面版
sudo apt update
sudo apt install ros-humble-desktop

2.3 安装 CycloneDDS 及依赖

# 安装 CycloneDDS RMW 实现
sudo apt install ros-humble-rmw-cyclonedds-cpp

# 安装其他依赖
sudo apt install ros-humble-rosidl-generator-dds-idl
sudo apt install libyaml-cpp-dev

2.4 克隆并编译 unitree_ros2

# 克隆官方 ROS2 仓库
cd ~
git clone https://github.com/unitreerobotics/unitree_ros2

# 进入工作空间并编译
cd ~/unitree_ros2
source /opt/ros/humble/setup.bash
colcon build

编译成功后,source 工作空间:

source ~/unitree_ros2/install/setup.bash

提示:可以将上面两行 source 命令添加到 ~/.bashrc 中,避免每次打开终端都要手动执行。

2.5 网络连接配置

Go2 支持两种连接方式:

方式一:以太网连接(推荐,延迟低)

  1. 用网线连接 PC 和 Go2 背部的以太网口
  2. 在 PC 上配置静态 IP:
    • IP 地址:192.168.123.222
    • 子网掩码:255.255.255.0
    • 网关:留空
# 命令行方式配置(替换 eth0 为你的网卡名)
sudo ifconfig eth0 192.168.123.222 netmask 255.255.255.0
  1. 验证连接:
ping 192.168.123.161   # Go2 默认 IP

方式二:WiFi 连接

  1. 开启 Go2 后,在 PC 上搜索并连接名为 go2 的 WiFi 热点
  2. 默认密码:hotspot123

注意:WiFi 方式延迟较高,不建议用于低层实时控制场景。


3. 通讯架构详解

理解 Go2 的通讯架构是后续所有开发工作的基础。如果你之前没有接触过 DDS 或 ROS2 的通讯模型,本章会帮你建立起清晰的认知。

3.1 什么是 DDS?为什么 Go2 使用它?

DDS(Data Distribution Service) 是一种工业级的数据分发通讯标准。你可以把它理解为一个"智能信箱系统":

  • 发布者(Publisher) 把数据投进指定话题(Topic)的信箱
  • 订阅者(Subscriber) 从自己感兴趣的话题信箱中取出数据
  • 发布者和订阅者之间不需要知道彼此的存在,只要约定好"信箱名称"(话题名)和"信件格式"(消息类型)即可

与传统的客户端-服务器模式相比,DDS 的关键优势在于:

特性DDS(Go2 / ROS2)传统 TCP 通讯
中心节点不需要(去中心化)需要服务器
节点发现自动发现(同一网段内)需要手动配置地址
多对多通讯原生支持需要自行实现
实时性可配置 QoS 保证实时性取决于实现

Go2 选择 DDS 的最大好处是:ROS2 底层也使用 DDS。这意味着 Go2 的内部通讯和 ROS2 是同一套协议栈,你的 PC 上运行的 ROS2 节点可以直接与 Go2 内部的 DDS 话题通讯——不需要桥接层、不需要 SDK 封装,就像和另一个 ROS2 节点对话一样自然。

类比理解:想象 Go2 机器人内部本身就是一个"ROS2 节点",它一开机就在往 DDS 网络上发布自己的状态数据、监听控制指令。你只需要把你的 PC 接入同一个网络,就能直接收发数据。

3.2 CycloneDDS:Go2 指定的 DDS 实现

DDS 只是一个协议标准,具体的软件实现有很多种(就像 HTTP 协议可以由 Nginx、Apache 等不同软件实现)。常见的 DDS 实现包括:

DDS 实现说明
CycloneDDSEclipse 基金会开源项目,Go2 使用此实现
FastDDSROS2 的默认 DDS 实现
Connext DDS商业 DDS 实现

Go2 固定使用 CycloneDDS 0.10.2。这意味着你的 PC 端也必须使用 CycloneDDS,否则两边"说的不是同一种方言",无法通讯。这就是为什么前面环境准备章节强调必须安装 rmw-cyclonedds-cpp 并设置环境变量。

3.3 通讯架构图

graph LR
    subgraph yourPC [你的 PC]
        ROS2Node[ROS2 节点]
        CycloneDDS_PC[CycloneDDS]
    end

    subgraph go2Robot [Go2 机器人]
        MainBoard[主控板 Jetson]
        MCU[运动控制 MCU]
        Motors[12 个关节电机]
        Sensors[传感器 LiDAR/IMU/Camera]
        CycloneDDS_Go2[CycloneDDS]
    end

    ROS2Node <--> CycloneDDS_PC
    CycloneDDS_PC <-->|以太网 / WiFi| CycloneDDS_Go2
    CycloneDDS_Go2 <--> MainBoard
    MainBoard <--> MCU
    MCU <--> Motors
    Sensors --> MainBoard

上面的架构图展示了数据的完整流动路径。以"读取机器人状态"为例:

  1. Go2 的 IMU/电机编码器产生原始数据 → 传给主控板
  2. 主控板通过 CycloneDDS 将数据发布到话题(如 rt/lf/sportmodestate
  3. 数据通过以太网/WiFi 传输到你的 PC
  4. 你的 PC 上的 CycloneDDS 接收到数据,传递给你的 ROS2 节点
  5. 你的回调函数被触发,拿到最新的机器人状态

整个过程延迟通常在 1-5ms(以太网)10-50ms(WiFi)

3.4 环境变量配置

在运行任何与 Go2 交互的 ROS2 程序之前,必须设置以下环境变量:

# 指定 DDS 域 ID(必须与 Go2 一致,默认为 0)
export ROS_DOMAIN_ID=0

# 指定使用 CycloneDDS 作为 RMW 实现
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp

这两个变量的含义:

  • ROS_DOMAIN_ID:DDS 域 ID 相当于一个"频道号"。只有在同一个域 ID 下的节点才能互相发现和通讯。Go2 默认使用域 ID = 0,你的 PC 也必须设为 0。如果你的网络上有多台机器人,可以通过不同的域 ID 来隔离它们。
  • RMW_IMPLEMENTATION:RMW(ROS Middleware)是 ROS2 与底层 DDS 之间的抽象层。ROS2 默认使用 FastDDS,但 Go2 使用 CycloneDDS。如果两边 DDS 实现不一致,即使在同一网络、同一域 ID 下也无法通讯。

重要:如果不设置 RMW_IMPLEMENTATION,ROS2 会使用默认的 FastDDS,导致你的程序看不到 Go2 的任何话题。建议将这两行加入 ~/.bashrc,避免每次都要手动设置。

3.5 两种控制层级

Go2 提供两种控制层级,它们的关系类似于"自动挡汽车"和"手动挡汽车":

高层控制(Sport Mode) 就像自动挡——你只需要说"前进"、"左转",机器人内部的平衡控制器会自动处理所有关节的协调运动,保证不摔倒。

低层控制(Low-Level) 就像手动挡——你直接控制每个关节的角度和力矩,拥有完全的自由度,但也需要自己保证机器人的平衡和协调。

特性高层控制(Sport Mode)低层控制(Low-Level)
控制粒度运动指令(前进、转弯、站立等)单个关节角度/力矩
稳定性内置平衡控制器,不易摔倒需要自行保证平衡
使用难度简单,适合快速原型开发复杂,需要深入了解机器人动力学
适用场景导航、遥控、简单任务自定义步态、强化学习策略部署
运行位置MCU 上的 Sport Mode 程序你的 PC 直接控制电机

如何选择?一般原则是:如果 Sport Mode 能满足你的需求就用 Sport Mode。只有当你需要完全自定义机器人的运动方式(比如部署自己训练的强化学习策略)时,才需要切换到低层控制。


4. ROS2 话题与消息 API

在 ROS2 中,不同程序之间通过话题(Topic) 传递数据。每个话题有固定的名称消息类型。你可以把话题想象成一个"广播频道":

  • 发布者(Publisher) 向频道广播数据
  • 订阅者(Subscriber) 收听频道获取数据
  • 一个话题可以有多个发布者和订阅者

Go2 的所有 API 都是通过 ROS2 话题实现的。下面按控制层级分别介绍。

4.1 高层控制 API(Sport Mode)

当 Sport Mode 处于激活状态时(默认开启),你可以通过以下话题控制机器人执行预定义动作。高层控制的核心思想是请求-响应模式:你向 request 话题发送一个动作请求,机器人执行后在 response 话题返回结果。

核心话题

话题名称类型方向说明
rt/api/sport/requestunitree_api/msg/Request发布发送运动指令请求
rt/api/sport/responseunitree_api/msg/Response订阅接收指令执行结果
rt/lf/sportmodestateunitree_go/msg/SportModeState订阅机器人运动状态(位置、速度、姿态等)
rt/api/motion_switcher/requestunitree_api/msg/Request发布切换控制模式
rt/api/motion_switcher/responseunitree_api/msg/Response订阅模式切换结果

可用的高层动作

动作说明使用场景
StandUp从趴下状态站立起来启动机器人后的第一步
StandDown从站立状态安全趴下休息、切换到低层控制前必须先趴下
BalanceStand平衡站立(可抵抗轻微外力)需要原地站稳时
RecoveryStand恢复站立(摔倒后自动翻身站起)机器人意外摔倒后的恢复
Stretch伸展身体演示/娱乐
Damp阻尼模式(关节变软,可手动摆动腿部)需要手动摆放机器人姿态、检修时
Move移动(指定前进/侧移速度和旋转角速度)运动控制的核心指令

理解 Move 指令:Move 是最常用的高层指令。它接受三个参数:前进速度 vx(正值前进、负值后退)、侧向速度 vy(正值左移、负值右移)、旋转角速度 vyaw(正值左转、负值右转)。机器人内部的运动控制器会自动将这些速度指令转化为四条腿的协调步态。

4.2 低层控制 API(Low-Level)

当你需要直接控制每个关节时(例如部署自己训练的强化学习策略、实现全新的步态算法),需要先关闭 Sport Mode,然后使用低层控制 API。

低层控制的本质是直接控制 12 个关节电机。你需要以固定频率(通常 50-500Hz)持续发送每个关节的目标位置和增益参数,电机内部的 PD 控制器会驱动关节运动到目标位置。

核心话题

话题名称类型方向说明
/lowcmdunitree_go/msg/LowCmd发布向关节电机发送控制指令
/lowstateunitree_go/msg/LowState订阅读取关节状态和 IMU 数据
/utlidar/cloudsensor_msgs/msg/PointCloud2订阅LiDAR 点云数据

LowCmd 消息结构

LowCmd 用于控制 12 个关节电机。对于每个关节,你需要指定 5 个参数:

字段说明典型值范围
q目标关节角度(弧度)根据关节不同,约 -1.0 ~ 1.5 rad
dq目标关节速度(弧度/秒)通常设为 0(位置控制模式)
tau前馈力矩(牛·米)通常设为 0(纯 PD 控制)
kp位置比例增益(刚度)20 ~ 80(越大关节越硬)
kd速度微分增益(阻尼)0.5 ~ 2.0(越大运动越平缓)

PD 控制器详解

每个关节内部有一个 PD(比例-微分)控制器,电机实际施加的力矩按如下公式计算:

用大白话解释这个公式:

  • kp × (q_target - q_current)位置误差修正。关节离目标越远,施加的力矩越大,把关节"拉"向目标位置。kp 越大,这个"拉力"越强,关节越"硬"。
  • kd × (dq_target - dq_current)速度阻尼。如果关节运动太快,这一项会产生"刹车"力矩,避免过冲和振荡。kd 越大,运动越平稳但也越慢。
  • tau_ff前馈力矩。额外施加的力矩,用于补偿重力等已知外力。初学者通常设为 0。

调参建议:初学者建议从 kp=30, kd=1.0 开始。如果关节抖动,减小 kp 或增大 kd;如果关节太软跟不上目标,增大 kp。

LowState 消息结构

LowState 包含机器人的实时状态信息。这是你了解机器人当前"身体状况"的唯一途径:

字段说明用途
motor_state[i].q第 i 个关节当前角度(弧度)用于构建 RL 观测中的"关节角度"
motor_state[i].dq第 i 个关节当前角速度(弧度/秒)用于构建 RL 观测中的"关节速度"
motor_state[i].tau_est第 i 个关节当前估计力矩(牛·米)用于监控电机负载
imu_state.quaternionIMU 四元数 [w, x, y, z](姿态)用于计算机身倾斜角度和重力投影
imu_state.gyroscopeIMU 角速度 [roll, pitch, yaw](弧度/秒)RL 观测中的"基座角速度"
imu_state.accelerometerIMU 线加速度 [x, y, z](m/s²)用于估算机身线速度

关于 IMU 四元数:四元数是一种表示 3D 旋转的数学工具,避免了欧拉角的万向锁问题。如果你更习惯欧拉角,可以用 scipy.spatial.transform.Rotation.from_quat() 进行转换。

Go2 关节编号

Go2 的 12 个关节按如下顺序编号(0-11):

前左腿 (FL):  0-髋侧摆  1-髋前摆  2-膝关节
前右腿 (FR):  3-髋侧摆  4-髋前摆  5-膝关节
后左腿 (RL):  6-髋侧摆  7-髋前摆  8-膝关节
后右腿 (RR):  9-髋侧摆  10-髋前摆 11-膝关节

4.3 传感器话题

话题名称类型说明
rt/utlidar/range_info-LiDAR 距离信息
rt/utlidar/height_map_array-LiDAR 高度图
rt/utlidar/cloudPointCloud2LiDAR 3D 点云
rt/wirelesscontroller-遥控器状态

4.4 模式切换:从 Sport Mode 到 Low-Level

切换到低层控制模式是一个需要小心处理的过程。为什么要小心?因为 Sport Mode 本身就是一个持续运行的平衡控制器——它时刻在控制 12 个关节以保持机器人站立。如果你突然关闭 Sport Mode,所有关节瞬间失去控制力,机器人就像被抽掉骨头一样会直接瘫倒在地,可能损坏电机或机身结构。

正确的切换步骤:

1. 调用 SportClient.StandDown() 让机器人安全趴下
   → 机器人四条腿缓慢弯曲,机身平稳落地
2. 使用 MotionSwitcherClient 将模式从 "sport_mode" 切换到 "low_level"
   → Sport Mode 平衡控制器停止工作
3. 此时 /lowcmd 和 /lowstate 话题变为可用
   → 你可以开始读取关节状态
4. 开始以固定频率发布 LowCmd 消息控制关节
   → 你的程序接管了机器人的完全控制权

警告:切换到低层控制后,机器人不再有自动平衡能力。如果你停止发送 LowCmd 或发送了错误的指令,机器人可能做出危险动作。请确保:

  • 首次测试时使用挂绳或支架悬空机器人
  • 测试环境远离人员和易碎物品
  • 你的控制程序中有紧急停止逻辑(例如按键触发 Damp 模式)

切换回 Sport Mode

如果需要从低层控制切回 Sport Mode,步骤如下:

1. 停止发送 LowCmd,让关节进入 Damp(阻尼)状态
2. 使用 MotionSwitcherClient 切换回 "sport_mode"
3. 调用 RecoveryStand 让机器人重新站起来

5. ROS2 编程实例

本章通过三个由浅入深的 Python 示例,带你动手与 Go2 交互。每个示例都会逐段解释代码的作用,即使你没有 ROS2 编程经验也能跟上。

前置知识:ROS2 的 Python 程序通常遵循一个固定的模式——创建一个"节点"类(继承 Node),在节点中创建发布者或订阅者,然后进入事件循环等待消息。如果你对这个模式还不熟悉,建议先阅读 ROS2 官方 Python 教程

5.1 示例一:订阅机器人状态(Python)

目标:连接 Go2,实时读取并打印机器人的位置和速度信息。这是最基础的交互——"先学会听,再学会说"。

import rclpy
from rclpy.node import Node
from unitree_go.msg import SportModeState


class Go2StateSubscriber(Node):
    def __init__(self):
        super().__init__('go2_state_subscriber')
        # 创建一个订阅者:
        #   - SportModeState:消息类型(Go2 运动状态数据结构)
        #   - 'rt/lf/sportmodestate':话题名称(Go2 固定发布状态到这个话题)
        #   - self.state_callback:每收到一条消息就调用这个函数
        #   - 10:消息队列深度(最多缓存 10 条未处理的消息)
        self.subscription = self.create_subscription(
            SportModeState,
            'rt/lf/sportmodestate',
            self.state_callback,
            10
        )

    def state_callback(self, msg):
        # 每当 Go2 发布一条状态消息,这个函数就会被自动调用
        # msg 就是收到的 SportModeState 消息
        self.get_logger().info(
            f'位置: x={msg.position[0]:.3f}, y={msg.position[1]:.3f}, z={msg.position[2]:.3f}\n'
            f'速度: vx={msg.velocity[0]:.3f}, vy={msg.velocity[1]:.3f}'
        )


def main():
    rclpy.init()                         # 初始化 ROS2 运行时
    node = Go2StateSubscriber()          # 创建我们的节点
    rclpy.spin(node)                     # 进入事件循环,持续等待和处理消息
    node.destroy_node()                  # 退出时清理节点
    rclpy.shutdown()                     # 关闭 ROS2 运行时


if __name__ == '__main__':
    main()

代码流程解读

graph LR
    A[Go2 机器人] -->|"持续发布 SportModeState 消息"| B["话题: rt/lf/sportmodestate"]
    B -->|"自动触发"| C["state_callback()"]
    C --> D["打印位置和速度"]
  1. 程序启动后,rclpy.spin() 让节点进入无限循环,等待消息
  2. Go2 持续以约 50Hz 的频率向 rt/lf/sportmodestate 话题发布状态消息
  3. 每收到一条消息,state_callback 被自动调用,打印出机器人的 3D 位置和速度

运行方式

# 终端中依次执行
export ROS_DOMAIN_ID=0
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
source ~/unitree_ros2/install/setup.bash
python3 go2_state_subscriber.py

如果一切正常,你应该能看到类似以下的输出不断刷新:

[INFO] 位置: x=0.000, y=0.000, z=0.320
       速度: vx=0.000, vy=0.000

排查提示:如果运行后没有任何输出,检查以下几点:

  1. Go2 是否已开机且正常运行?
  2. PC 和 Go2 是否在同一网络?(ping 192.168.123.161 是否通?)
  3. 环境变量是否正确设置?(echo $RMW_IMPLEMENTATION 应该输出 rmw_cyclonedds_cpp
  4. 可以用 ros2 topic list 命令查看是否能发现 Go2 的话题

5.2 示例二:发布高层运动指令(Python)

目标:通过 Sport Mode API 让 Go2 以 0.3 m/s 的速度向前行走。这是"学会说话"——向机器人发送控制指令。

import json
import rclpy
from rclpy.node import Node
from unitree_api.msg import Request


class Go2SportController(Node):
    def __init__(self):
        super().__init__('go2_sport_controller')
        # 创建发布者:向 sport/request 话题发送控制指令
        self.publisher = self.create_publisher(
            Request,
            'rt/api/sport/request',
            10
        )
        # 创建定时器:每 0.1 秒(10Hz)执行一次 timer_callback
        # Sport Mode 需要持续收到指令才会持续运动,停止发送后机器人会停下来
        self.timer = self.create_timer(0.1, self.timer_callback)
        self.cmd_id = 0

    def build_request(self, api_id, parameter=None):
        """构建 Sport Mode API 请求消息。
        Sport Mode 的指令通过 Request 消息发送,包含:
        - api_id:指令编号,不同编号对应不同动作
        - parameter:JSON 格式的参数字符串
        """
        msg = Request()
        msg.header.identity.api_id = api_id
        msg.header.identity.id = self.cmd_id
        self.cmd_id += 1
        if parameter:
            msg.parameter = json.dumps(parameter)
        return msg

    def timer_callback(self):
        # api_id=1008 对应 Move 指令
        # x: 前进/后退速度 (m/s),正值前进,负值后退
        # y: 侧移速度 (m/s),正值左移,负值右移
        # z: 旋转角速度 (rad/s),正值左转,负值右转
        param = {
            "x": 0.3,    # 前进 0.3 m/s(约 1 km/h,非常慢的步行速度)
            "y": 0.0,    # 不侧移
            "z": 0.0     # 不旋转
        }
        msg = self.build_request(api_id=1008, parameter=param)
        self.publisher.publish(msg)
        self.get_logger().info('发送前进指令: vx=0.3 m/s')


def main():
    rclpy.init()
    node = Go2SportController()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

代码流程解读

graph LR
    A["定时器 (10Hz)"] -->|"每 0.1 秒触发"| B["timer_callback()"]
    B --> C["构建 Move 指令 (vx=0.3)"]
    C --> D["发布到 rt/api/sport/request"]
    D --> E["Go2 内部平衡控制器执行运动"]

关键点

  • 为什么要用定时器持续发送? Sport Mode 的 Move 指令类似遥控器——你按住"前进"按钮机器人才走,松开就停。程序需要持续发送 Move 指令,机器人才会持续运动。
  • 为什么 api_id 是 1008? 每个高层动作都有一个唯一的数字编号。1008 是 Move 指令的编号。这些编号定义在 Unitree SDK 的文档中。
  • 速度单位:x/y 的单位是 m/s,z 的单位是 rad/s。0.3 m/s 大约是一个人悠闲散步速度的 1/5,非常安全。

安全提示:首次运行时建议使用非常小的速度(如 0.1 m/s),确保机器人在可控范围内。按 Ctrl+C 可以终止程序,机器人会自动停下。

5.3 示例三:低层关节控制基础(Python)

目标:在低层控制模式下,让 Go2 维持站立姿势。这是最接近强化学习策略部署的控制方式——直接控制每个关节的角度。

前提:运行此示例前,必须先通过 MotionSwitcherClient 将机器人切换到低层控制模式(参见 4.4 节)。

import rclpy
from rclpy.node import Node
from unitree_go.msg import LowCmd, LowState
import numpy as np


# Go2 默认站立时的关节角度(弧度)
# 这组角度可以让机器人维持基本的站立姿态
# 每条腿 3 个值:[髋关节侧摆, 髋关节前摆, 膝关节]
DEFAULT_STAND_ANGLES = [
     0.0,  0.8, -1.6,   # 前左腿 (FL): 不侧摆, 髋向前弯 0.8rad, 膝向后弯 1.6rad
     0.0,  0.8, -1.6,   # 前右腿 (FR): 与前左腿对称
     0.0,  1.0, -1.6,   # 后左腿 (RL): 髋弯曲角度略大
     0.0,  1.0, -1.6    # 后右腿 (RR): 与后左腿对称
]


class Go2LowLevelController(Node):
    def __init__(self):
        super().__init__('go2_low_level_controller')

        # 订阅低层状态:持续接收关节角度、速度、IMU 等数据
        self.state_sub = self.create_subscription(
            LowState, '/lowstate', self.state_callback, 10
        )

        # 发布低层指令:向 12 个关节电机发送控制命令
        self.cmd_pub = self.create_publisher(LowCmd, '/lowcmd', 10)

        # 创建定时器,控制频率 50Hz(每 20ms 执行一次控制循环)
        # 频率选择很重要:太低会导致控制不稳定,太高会增加通讯负担
        # 50Hz 是一个安全的起始频率
        self.timer = self.create_timer(0.02, self.control_loop)

        self.current_state = None

    def state_callback(self, msg):
        # 保存最新的机器人状态,供控制循环使用
        self.current_state = msg

    def control_loop(self):
        # 等待收到第一条状态消息后再开始控制
        # 这很重要——在不知道机器人当前状态时盲目发送指令是危险的
        if self.current_state is None:
            return

        cmd = LowCmd()

        # 为 12 个关节分别设置目标
        for i in range(12):
            cmd.motor_cmd[i].q = DEFAULT_STAND_ANGLES[i]  # 目标角度
            cmd.motor_cmd[i].dq = 0.0                      # 目标速度 = 0(希望关节静止在目标位置)
            cmd.motor_cmd[i].tau = 0.0                      # 不施加额外前馈力矩
            cmd.motor_cmd[i].kp = 30.0                      # 位置增益:适中的刚度
            cmd.motor_cmd[i].kd = 1.0                       # 速度增益:轻微阻尼

        # 发布指令——12 个关节的指令打包在一条消息中同时发出
        self.cmd_pub.publish(cmd)

        # 打印当前关节 0(前左腿髋侧摆)的角度,用于观察控制效果
        if self.current_state.motor_state:
            q0 = self.current_state.motor_state[0].q
            self.get_logger().info(f'关节0 当前角度: {q0:.4f} rad')


def main():
    rclpy.init()
    node = Go2LowLevelController()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

代码流程解读

graph TD
    A["Go2 发布 /lowstate"] --> B["state_callback 保存状态"]
    C["定时器 50Hz"] --> D["control_loop()"]
    D --> E["构建 LowCmd: 12 个关节的 q, kp, kd"]
    E --> F["发布到 /lowcmd"]
    F --> G["Go2 电机执行 PD 控制"]
    G --> A

这是一个闭环控制系统

  1. Go2 持续发布当前关节状态 → 你的程序收到并保存
  2. 你的程序以 50Hz 频率发送目标角度 → Go2 电机执行
  3. 电机移动后状态更新 → 回到第 1 步

理解站立角度:为什么 DEFAULT_STAND_ANGLES 是这些值?想象你从侧面看 Go2 的一条腿:

       髋关节 (侧摆=0, 前摆=0.8)
        │
        │  ← 大腿向前倾斜约 46°
        │
       膝关节 (弯曲=-1.6)
        │
        │  ← 小腿向后弯曲约 92°
        │
       脚掌着地

这个姿态让机器人的重心稳定地落在四只脚围成的支撑面内,是一个天然稳定的站立姿态。

安全提示

  • 首次测试务必悬空机器人(用支架撑起),避免错误指令导致机器人猛烈动作
  • Kp 和 Kd 值从小开始调(如 kp=20, kd=0.5),观察关节响应后再逐渐增大
  • 如果关节高频抖动(嗡嗡声),立即停止程序——说明 kp 过大或控制频率有问题
  • 始终在程序中保留紧急停止机制(例如检测到关节角度异常时自动切入 Damp 模式)

6. Isaac Lab 强化学习训练

本章介绍如何在 NVIDIA Isaac Lab 仿真平台上,为 Go2 训练强化学习(RL)运动策略。即使你之前没有强化学习的背景,本章也会从基本概念讲起。

6.1 为什么需要强化学习?

传统的机器人运动控制依赖人工设计的控制器(比如 Sport Mode 就是宇树工程师精心设计的)。这种方法虽然可靠,但有两个局限:

  1. 设计成本高:让四足机器人稳定行走的控制器非常复杂,需要深厚的动力学和控制理论功底
  2. 泛化能力差:针对平地设计的控制器可能在草地、碎石路上表现不佳

强化学习(Reinforcement Learning, RL) 提供了另一种思路:让机器人在仿真环境中"自己学走路"。核心思想是:

  • 给机器人一个目标(比如"按照指令速度前进")
  • 让它在仿真中反复试错——随机移动关节,观察结果
  • 做得好就给奖励,做得差就给惩罚
  • 经过数百万次尝试后,机器人自动学会了稳定、高效的运动策略
graph LR
    A["策略网络 (神经网络)"] -->|"输出动作: 12个关节角度"| B["仿真环境中的 Go2"]
    B -->|"返回状态: 关节/IMU/速度"| A
    B -->|"计算奖励"| C["RL 算法 (PPO)"]
    C -->|"更新网络权重"| A

6.2 为什么在仿真中训练?

在真实机器人上做 RL 训练是不现实的:

  • 时间:训练需要数百万次交互,在真实中需要几个月甚至几年
  • 安全:训练初期策略是随机的,机器人会做出各种危险动作
  • 成本:频繁摔倒会损坏机器人

在仿真中训练则完全不同:

  • 速度:GPU 并行仿真可以同时运行数千个 Go2,一小时训练量相当于真实中几年的经验
  • 安全:摔倒了重置就好,没有硬件损耗
  • 可控:可以自由设置地形、摩擦力、外力扰动等条件

这就是 Isaac Lab 的价值——它提供了一个高性能的 GPU 加速仿真平台,专门为机器人 RL 训练设计。

6.3 Isaac Lab 简介

NVIDIA Isaac Lab 是基于 Isaac Sim 构建的机器人学习统一框架。它提供了:

  • GPU 并行仿真:可同时运行数千个 Go2 实例,所有物理计算在 GPU 上完成
  • 预置机器人模型:包含 Go2 的 URDF/USD 模型,开箱即用
  • RL 算法集成:与 RSL-RL、Stable-Baselines3、rl_games 等主流库无缝对接
  • Sim-to-Real 支持:域随机化、地形生成等工具帮助缩小仿真-现实差距

6.4 系统要求

需求最低配置
操作系统Ubuntu 22.04 / Windows 11
内存32 GB RAM
GPU 显存16 GB(推荐 RTX 3080 及以上)
NVIDIA 驱动580.65+ (Linux) / 580.88+ (Windows)
Isaac Sim5.1.0 或更高版本
Python3.11(与 Isaac Sim 5.x 匹配)

6.5 安装 Isaac Lab

步骤一:安装 Isaac Sim

推荐使用 pip 安装方式(以 Isaac Sim 5.x 为例):

# 创建虚拟环境(Python 版本必须与 Isaac Sim 要求匹配)
conda create -n isaaclab python=3.11 -y
conda activate isaaclab

# 安装 Isaac Sim(具体版本请参考官方文档)
pip install isaacsim

注意:Isaac Sim 体积较大,首次安装需要较长时间。具体安装步骤请以 Isaac Lab 官方文档 为准。

步骤二:安装 Isaac Lab

# 克隆 Isaac Lab 仓库
git clone https://github.com/isaac-sim/IsaacLab.git
cd IsaacLab

# 安装 Isaac Lab(使用脚本方式)
./isaaclab.sh --install

步骤三:验证安装

# 运行一个简单的仿真场景验证
./isaaclab.sh -p scripts/tutorials/00_sim/create_empty.py

6.6 使用 unitree_rl_lab 进行训练

Unitree 官方提供了 unitree_rl_lab 仓库,基于 Isaac Lab 封装了针对 Unitree 机器人的强化学习训练管线。它帮你预配置好了 Go2 的机器人模型、环境参数、奖励函数等,让你可以专注于训练本身。

克隆与配置

git clone https://github.com/unitreerobotics/unitree_rl_lab.git
cd unitree_rl_lab
pip install -e .

-e 表示"可编辑模式"安装。这样你修改代码后不需要重新安装就能生效,方便调试奖励函数等参数。

可用训练环境

环境名称说明难度
Isaac-Velocity-Flat-Unitree-Go2-v0平坦地形上的速度跟踪任务入门(建议先用这个)
Isaac-Velocity-Rough-Unitree-Go2-v0崎岖地形(台阶、斜坡、碎石)上的速度跟踪进阶(Sim-to-Real 必备)

两个环境的目标都是让 Go2 学会跟踪给定的速度指令——你发出"以 0.5 m/s 速度前进、同时以 0.2 rad/s 左转"的指令,机器人要能稳定地执行。

Flat 环境适合验证训练流程和调试参数;Rough 环境训练出的策略鲁棒性更强,更适合部署到真实世界。

6.7 训练流程

什么是 PPO?

PPO(Proximal Policy Optimization,近端策略优化) 是目前机器人 RL 中最常用的算法。你不需要完全理解其数学原理,只需要知道:

  • PPO 是一种策略梯度算法:它直接优化策略网络("看到什么状态 → 做什么动作"的映射)
  • 它通过限制每次更新的幅度来保证训练稳定性(不会因为一次大更新把之前学到的东西毁掉)
  • 它支持并行采样:非常适合 Isaac Lab 的大规模并行仿真

训练的核心循环:

graph TD
    A["1. 采样: 4096 个 Go2 同时在仿真中运行,收集状态-动作-奖励数据"] --> B["2. 计算优势函数: 评估每个动作比平均水平好多少"]
    B --> C["3. 更新策略网络: 让好的动作更可能被选择"]
    C --> D["4. 更新价值网络: 更准确地预测未来累积奖励"]
    D --> E{"达到最大迭代次数?"}
    E -->|否| A
    E -->|是| F["训练完成,保存策略"]

启动训练

使用 PPO 算法训练 Go2 在崎岖地形上行走:

# 使用 Isaac Lab 的训练脚本
./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py \
    --task Isaac-Velocity-Rough-Unitree-Go2-v0 \
    --num_envs 4096 \
    --max_iterations 20000 \
    --headless

关键参数说明:

参数说明建议值
--task训练任务名称先用 Flat,效果好了再换 Rough
--num_envs并行环境数量(越多训练越快,但需要更多显存)16GB 显存: 2048; 24GB: 4096
--max_iterations最大训练迭代次数Flat: 5000 即可; Rough: 15000-20000
--headless无头模式运行(不渲染画面,提高训练速度)训练时始终建议加上
--seed随机种子,用于复现实验可选,如 42

训练时间参考:使用 RTX 4090、4096 个并行环境,训练 10000 个迭代大约需要 30-60 分钟。较低端的 GPU 可能需要数小时。

训练过程监控(TensorBoard)

训练启动后,可以用 TensorBoard 实时监控训练进展。TensorBoard 是一个网页端的可视化工具,它会把训练过程中的各项指标绘制成曲线图。

# 在另一个终端中运行(训练的同时打开)
tensorboard --logdir logs/rsl_rl/

在浏览器中打开 http://localhost:6006,关注以下关键指标:

指标含义正常趋势异常情况
Episode Reward每个 episode 的累计奖励逐渐上升并趋于平稳一直在 0 附近不涨:奖励函数可能有问题
Episode Length每个 episode 的步数逐渐增长一直很短:机器人很快就摔倒了
Policy Loss策略网络的损失逐渐收敛到较小值发散(越来越大):学习率可能过高
Value Loss价值网络的损失逐渐收敛不收敛:可能需要更多训练步数

什么是 Episode? 一个 episode 是从机器人初始化到终止条件触发之间的一段连续仿真。终止条件通常是:机器人摔倒(机身接触地面)或达到最大步数。训练初期机器人很快就摔倒(episode 很短),随着策略改进,它能走得越来越远(episode 越来越长)。

策略推理/回放

训练完成后,可以可视化训练结果:

# 使用预训练检查点进行推理
./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/play.py \
    --task Isaac-Velocity-Rough-Unitree-Go2-v0 \
    --num_envs 32

你也可以使用预训练的模型快速验证:

./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/play.py \
    --task Isaac-Velocity-Rough-Unitree-Go2-v0 \
    --num_envs 32 \
    --use_pretrained_checkpoint

6.8 观测空间与动作空间

理解 RL 的输入输出是设计和调试策略的基础。用一个类比来说明:策略网络就像一个"大脑",观测空间是它的"眼睛和耳朵"(输入),动作空间是它的"手脚"(输出)。

观测空间(Observation Space)——策略网络看到什么?

策略网络在每个控制步(通常每 20ms 一次)接收以下信息作为输入:

观测量维度说明来源
基座角速度3机身绕 roll/pitch/yaw 轴的旋转速度IMU 陀螺仪
重力投影向量3重力方向在机身坐标系下的投影由 IMU 姿态计算
指令速度3期望的前进速度 vx、侧移速度 vy、旋转速度 vyaw外部输入
关节角度12当前 12 个关节的角度(相对默认站立姿态的偏移)电机编码器
关节速度12当前 12 个关节的角速度电机编码器
上一步动作12上一个控制步策略输出的动作自身历史

总计约 45 维的观测向量。

为什么需要"重力投影向量"? 它告诉策略网络"机身现在是正的还是歪的"。如果机器人前倾,重力向量在机身坐标系下会偏向前方;如果左倾,则偏向左方。策略可以根据这个信息调整步态来恢复平衡。

为什么需要"上一步动作"? 这帮助策略网络输出平滑的动作序列。如果不提供这个信息,网络可能在相邻两步输出差异很大的动作,导致关节剧烈抖动。

崎岖地形额外观测:Rough 环境通常还会加入高度扫描(Height Scan) 信息——在机器人脚周围采样若干个点的地面高度,让策略能"看到"前方的地形(台阶、坑洞等)。这会额外增加约 100-200 维观测。

动作空间(Action Space)——策略网络输出什么?

动作维度说明
关节角度偏移12相对于默认站立姿态的关节角度增量

策略网络每一步输出 12 个浮点数,表示每个关节相对于默认站立角度的偏移量。最终发送给电机的目标角度按如下方式计算:

其中 action_scale(动作缩放因子)通常设为 0.25。为什么不直接让网络输出目标角度?原因有二:

  1. 安全限制:缩放因子限制了关节的运动范围,避免策略在训练初期输出极端角度损坏电机
  2. 学习效率:网络输出的范围被归一化到较小区间(通常 [-1, 1]),更容易学习

完整的数据流

graph LR

奖励函数设计要点

奖励函数是 RL 训练中最重要的设计决策——它定义了"什么是好的行为"。一个好的奖励函数需要平衡多个目标:既要走得快,又要走得稳,还要省电。

Go2 运动训练常用的奖励项:

奖励项作用权重方向直觉解释
线速度跟踪鼓励跟踪指令速度正奖励(+)"走得越接近目标速度越好"
角速度跟踪鼓励跟踪指令角速度正奖励(+)"转弯转得越准越好"
关节加速度惩罚避免关节突变抖动负奖励(-)"关节不要突然猛转"
力矩惩罚降低能耗负奖励(-)"用最小的力量完成动作"
脚部离地时间奖励鼓励抬脚走路正奖励(+)"走路要抬脚,不要拖地"
基座姿态惩罚保持身体水平负奖励(-)"身体不要歪"
动作平滑度惩罚相邻步动作差异过大负奖励(-)"动作要连贯,不要一步一个样"
碰撞惩罚惩罚机身非脚部位接触地面负奖励(-)"除了脚,身体其他部分不能碰到地面"

调参经验:奖励函数的权重调整是一个反复试错的过程。常见问题包括:

  • 机器人不走路只站着?→ 增大速度跟踪奖励的权重
  • 机器人走得很抖?→ 增大动作平滑度和关节加速度惩罚的权重
  • 机器人拖着脚走?→ 增大脚部离地时间奖励的权重
  • 机器人走得像螃蟹(侧着走)?→ 检查速度跟踪奖励是否正确区分了前进和侧移

6.9 训练流程总览

graph TD
    A[定义环境配置] --> B[配置奖励函数]
    B --> C[设定观测与动作空间]
    C --> D[启动 PPO 训练]
    D --> E[TensorBoard 监控]
    E --> F{奖励收敛?}
    F -->|否| G[调整超参数/奖励权重]
    G --> D
    F -->|是| H[可视化策略回放]
    H --> I[导出 ONNX 模型]
    I --> J[Sim-to-Real 部署]

7. Sim-to-Real 部署

训练好的策略需要部署到真实 Go2 机器人上才能发挥价值。这个过程称为 Sim-to-Real(仿真到现实) 迁移——它是整个流程中最激动人心也最有挑战性的一步。

7.1 理解 Sim-to-Real Gap

为什么不能直接把仿真中训练好的策略放到真机上用?因为仿真和现实之间存在不可避免的差异,称为 Sim-to-Real Gap(仿真-现实鸿沟)

差异来源仿真中现实中
地面摩擦力均匀、可精确设定随材质、湿度变化
电机响应理想化,瞬时响应有延迟、非线性、发热降性能
机身质量/惯量精确数值可能与 CAD 模型有偏差
传感器噪声可选择是否添加始终存在
外部扰动可控风、不平地面、碰撞等不可预测

如果策略在训练时只见过一种"完美"的仿真环境,它就像一个只在室内跑步机上练跑的人——到了户外凹凸不平的路面上就会摔倒。解决办法就是域随机化(Domain Randomization):训练时故意让环境参数在一个范围内随机变化,迫使策略学会适应各种条件。后面 7.5 节会详细介绍。

7.2 策略导出

训练完成后,需要将 PyTorch 模型导出为 ONNX 格式。ONNX(Open Neural Network Exchange)是一种通用的神经网络模型格式,可以在不同平台和框架上高效运行推理,不依赖 PyTorch 环境。

import torch

# 加载训练好的策略
policy = torch.load("logs/rsl_rl/model_20000.pt")
policy.eval()

# 构造示例输入(维度与观测空间一致)
dummy_input = torch.randn(1, obs_dim)

# 导出为 ONNX
torch.onnx.export(
    policy,
    dummy_input,
    "go2_policy.onnx",
    opset_version=11,
    input_names=["observations"],
    output_names=["actions"]
)

7.3 部署工作空间配置

推荐使用 go2_rl_ws 工作空间来部署策略。它是一个预配置好的 ROS2 Humble 工作空间,封装了从加载 ONNX 模型到发送低层控制指令的完整流程:

# 克隆部署工作空间
git clone https://github.com/eppl-erau-db/go2_rl_ws.git
cd go2_rl_ws

# 安装依赖
pip install -r requirements.txt

# 编译 ROS2 包
source /opt/ros/humble/setup.bash
colcon build
source install/setup.bash

将导出的 ONNX 模型放到工作空间中的指定目录(通常是 models/ 文件夹)。

7.4 部署流程

graph TD
    A[PC 通过以太网连接 Go2] --> B[切换到低层控制模式]
    B --> C[加载 ONNX 策略]
    C --> D[订阅 /lowstate 获取机器人状态]
    D --> E[构造观测向量]
    E --> F[ONNX 推理得到动作]
    F --> G[计算目标关节角度]
    G --> H[发布 /lowcmd]
    H --> D

完整的部署步骤:

  1. 连接机器人:通过以太网连接 PC 和 Go2(部署时必须用以太网,WiFi 延迟太高)
  2. 切换到低层模式:调用 StandDown() → 使用 MotionSwitcherClient 切换(参见 4.4 节)
  3. 挂绳保护:首次部署时务必用挂绳悬挂机器人,防止策略失败导致摔落
  4. 启动策略推理节点
export ROS_DOMAIN_ID=0
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
source install/setup.bash

# 启动推理节点(具体命令视工作空间而定)
ros2 run go2_rl_deploy policy_runner --ros-args \
    -p model_path:=go2_policy.onnx \
    -p action_scale:=0.25

部署时的关键注意事项

  • action_scale 必须与训练时一致:如果训练时用 0.25,部署时也必须是 0.25,否则关节会输出过大或过小的角度
  • 控制频率必须匹配:如果训练时仿真步长对应 50Hz 控制,部署时也要以 50Hz 发送指令
  • 观测量必须对齐:部署时构造的观测向量必须与训练时的维度、顺序、归一化方式完全一致。特别注意,训练时如果用了高度扫描(Height Scan),但真机上无法获取等效数据,需要在训练时就排除这个观测

7.5 Sim-to-Real 常见问题与解决方案

Sim-to-Real 迁移通常不会一次成功,这是正常的。以下是常见问题和解决办法:

问题原因解决方案
机器人拖着脚走路仿真中地面摩擦力设置偏低训练时增大地面摩擦力,增加「脚部离地时间」奖励权重
策略在真机上不稳定仿真与现实存在动力学差异使用域随机化(Domain Randomization):随机化摩擦力、机身质量、电机力矩等参数
关节抖动严重动作缩放因子或 Kp/Kd 不匹配调整 action_scale,确保与训练时一致(通常 0.25)
机器人侧翻重心建模不准确在训练中随机化重心偏移
控制频率不匹配仿真和部署的控制频率不一致确保部署控制频率与训练一致(通常 50Hz / 200Hz 物理步)

域随机化(Domain Randomization)详解

域随机化的核心思想是:如果策略在训练时见过各种各样的环境条件,那么真实世界的条件大概率落在它见过的范围内

graph TD
    A["训练环境 1: 摩擦力=0.5, 质量=14kg"] --> E["策略学会适应各种条件"]
    B["训练环境 2: 摩擦力=1.5, 质量=16kg"] --> E
    C["训练环境 3: 摩擦力=0.8, 质量=15kg + 外力扰动"] --> E
    D["...数千个不同配置的环境..."] --> E
    E --> F["部署到真实世界: 策略能自适应"]

在 Isaac Lab 的环境配置中添加随机化参数:

randomization = {
    "friction": {
        "range": [0.5, 2.0],       # 地面摩擦力系数在 0.5~2.0 之间随机
        "operation": "scaling"      # 缩放模式:默认值 × 随机系数
    },
    "base_mass": {
        "range": [-1.0, 3.0],      # 机身额外增减 -1kg ~ +3kg 的质量
        "operation": "additive"     # 加法模式:默认值 + 随机偏移
    },
    "motor_strength": {
        "range": [0.8, 1.2],       # 电机出力在 80%~120% 之间波动
        "operation": "scaling"      # 模拟电机性能差异和老化
    },
    "push_robots": {
        "interval_s": 15,           # 每隔 15 秒给机器人施加一次随机外力推击
        "max_velocity": 1.0         # 推击产生的最大速度变化 1.0 m/s
    }
}

每个随机化参数的作用:

  • 摩擦力随机化:模拟不同地面材质(瓷砖、水泥、草地、地毯等),这是 Sim-to-Real 中最关键的随机化参数
  • 质量随机化:模拟 CAD 模型与实际的误差,以及机器人背负不同载荷的情况
  • 电机力矩随机化:模拟电机性能差异、温度变化导致的出力波动
  • 随机外力推击:模拟意外碰撞和外部扰动,训练策略的恢复能力

实践建议:域随机化的范围不宜过大也不宜过小。过小则覆盖不到真实条件;过大则训练难度暴增,策略可能学不出来。建议从较小范围开始,逐步扩大并观察训练曲线和真机效果。


8. 参考资源与社区

8.1 官方仓库

仓库说明链接
unitree_ros2ROS2 官方支持包GitHub
unitree_sdk2C++ SDKGitHub
unitree_rl_labIsaac Lab 强化学习训练GitHub
unitree_rl_gymIsaac Gym 强化学习训练GitHub
unitree_ros2_to_realSim-to-Real 部署GitHub
go2_rl_wsRL 策略部署工作空间GitHub

8.2 文档与教程

资源链接
Isaac Lab 官方文档isaac-sim.github.io/IsaacLab
ROS2 Humble 文档docs.ros.org/en/humble
Unitree Go2 社区文档unitree-go2-robot.github.io
ROS2 Essentials (Go2)j3soon.github.io/ros2-essentials/go2-ws
Go2 Low-Level 控制教程ric.engineering

8.3 社区资源

  • GitHub Discussions:在各个仓库的 Issues 和 Discussions 区提问是获取帮助的最佳方式
  • ROS Discoursediscourse.ros.org 上有大量 ROS2 相关讨论
  • Unitree 官方论坛:宇树科技的官方技术支持渠道