← 返回文章库

不用强化学习,四足机器人怎么走路?读 unitree_guide 的控制实现

最后更新 2026-08-23
⏱ 约 31 分钟 🟡 涉接线/强电
你将学到
  • 从 unitree_guide 的目录结构反推出一套经典四足控制器的完整分层
  • 搞懂有限状态机、步态相位、摆动腿轨迹与支撑腿力分配、状态估计这几块各自在解决什么问题
  • 说清经典控制路线和强化学习路线各自的代价与好处,知道自己该先学哪个

现在聊足式机器人,话题几乎默认从强化学习开始:在仿真里跑几千个并行环境,调奖励函数,导出一个策略网络,扔到真机上跑。这条路确实有效,但它容易造成一个错觉——好像四足机器人是最近几年才学会走路的。

不是。强化学习进场之前,机器狗已经能小跑、能爬坡、能被踹一脚之后稳住。靠的是一整套经典控制方法:状态机管模式,步态发生器管节奏,运动学管腿的位置,状态估计器管「我在哪、我多快」,平衡控制器管力怎么分到四条腿上。这些东西一个都不能少,而且每一块都是人一行行设计出来的。

unitree_guide 就是这条路线的一份完整开源实现。它的价值不在于跑得多好——README 自己都说了它只是给初学者的基础控制器——而在于它把这套骨架摆得特别整齐,整齐到你光看目录名就能把控制框图画出来。

先把边界说清楚:这篇文章基于公开仓库的 README、目录结构和文件命名来写,没有真机验证,也没有跑过仿真。所有关于「这个模块在干什么」的判断,来自文件名和它在目录里的位置,我会明确区分哪些是仓库白纸黑字写的、哪些是我从结构上的解读。

这个仓库是干什么的

README 第一句就交代了定位:它是宇树四足机器人的开源控制项目,同时也是《四足机器人控制算法——建模、控制与实践》这本书的配套软件项目

「配套教材」这四个字很关键,它解释了这个仓库后面所有的设计取向。一个面向生产的控制器会做很多让代码变难读的事:性能优化、边界条件堆叠、各种历史包袱。而一个配套教学的实现,首要目标是让人看懂——模块要能一一对应到书里的章节,命名要直白,层次要浅,能拆开单独讲的绝不揉在一起。

README 里推荐的运行环境是 Ubuntu 18.04 + ROS melodic,依赖三个 ROS 包放在同一个工作空间里编译(以官方仓库当前文档为准)。用 catkin_make 编译,roslaunch 起 Gazebo 仿真,然后另开一个终端跑控制器可执行文件 junior_ctrl

junior_ctrl 这个名字我停了一下。junior——初级。这是作者在给你划范围:这份代码是入门用的。

README 结尾还有一句更直接的:这个项目提供的是给初学者的基础四足控制器,想要更好的性能,需要额外的参数调优或者更进阶的方法(README 里举的例子是 MPC 之类)。也就是说,仓库本身并不包含 MPC 实现——这一点必须说清楚,别看到「经典控制」就默认里面躺着一个模型预测控制器。它给你的是比 MPC 更基础、也更容易读懂的那一层骨架。

按 README 的说法,控制器启动后机器人趴在地上,按键盘 2Passive(初始状态)切到 FixedStand,再按 4 切到 Trotting,然后用 w a s d 控制平移、j l 控制旋转,空格键停下站住。

这段操作说明本身就是一份状态机说明书。我们从这儿切进去。

顶层:三个包,各管一段

仓库根目录下是三个并列的 ROS 包:

unitree_actuator_sdk/   —— 电机通信 SDK
unitree_guide/          —— 控制器本体(本文主角)
unitree_move_base/      —— 导航相关配置与仿真世界

两端是配套设施,中间是正戏。

unitree_actuator_sdk/ 里是 motor_ctrl.hmotor_msg.hLSerial.h,以及针对 Linux 32/64、ARM 32/64、Win64 各编译一份的动态库,外加一个 ChangeID_Tool(改电机 ID 的小工具,Linux 和 Windows 版本都有)。这一层的存在提醒你一件容易被忽略的事:足式控制的最底下,是一根串口线和一套电机协议。控制律再漂亮,最后也得变成一帧一帧发给关节电机的报文。

unitree_move_base/ 里全是 YAML 和 launch:costmap_common_params.yamlglobal_costmap_params.yamllocal_costmap_params.yamlbase_local_planner_params.yamlpointCloud_to_laserScan_params.yaml,加上 indoor.worldsmallRoom.world 两个 Gazebo 世界。这是 ROS 导航栈那一套:全局代价地图、局部代价地图、局部规划器,还有把点云降维成激光扫描的转换。

注意它和控制器的分工:导航负责「往哪走」,控制器负责「怎么走」。move_base 输出的是速度指令,控制器把速度指令变成十二个关节的力矩。这两件事在概念上、代码上、甚至包的边界上都被彻底分开了。这个切分是足式机器人软件栈的一条主缝,值得记住。

回到 unitree_guide/,它的 include/ 下面是这几个目录:

include/FSM/          —— 有限状态机
include/Gait/         —— 步态生成
include/control/      —— 控制核心
include/common/       —— 机器人模型与数学工具
include/interface/    —— 输入输出接口
include/message/      —— 底层收发消息定义
include/thirdParty/   —— 第三方库

src/ 下面是完全对称的目录结构,加一个 main.cpp

这七个目录基本就是一张控制框图。下面逐块拆。

FSM:为什么状态机是足式控制的标配

include/FSM/ 下面十个头文件,src/FSM/ 下面十个对应的实现:

FSM.h                 —— 状态机本体
FSMState.h            —— 状态基类
State_Passive.h       —— 被动(趴下/失能)
State_FixedStand.h    —— 固定站立
State_FreeStand.h     —— 自由站立
State_Trotting.h      —— 小跑
State_BalanceTest.h   —— 平衡测试
State_SwingTest.h     —— 摆动测试
State_StepTest.h      —— 踏步测试
State_move_base.h     —— 对接导航栈

一个 FSM 加一个 FSMState 基类,剩下八个是具体状态。这个组织方式是教科书式的状态模式:基类定一套接口(大概率是进入、执行、退出、以及决定下一个状态是谁),每个状态各自实现自己的那份控制律。

为什么足式机器人非要有状态机?

因为「走路」根本不是一个连续的控制问题,而是若干个彼此不兼容的控制问题拼起来的。

机器人趴在地上时,关节应该是软的、不出力的,你甚至希望它被人推得动——这是 Passive。要站起来时,控制目标是把关节角度平滑地插值到一组预设的站立角度,这是一个纯粹的位置跟踪问题——这是 FixedStand。站稳之后想让身体前后左右倾一倾、高低升降一下,这时候控制目标从关节空间转到了身体位姿空间,需要反解四条腿的关节角——这是 FreeStand。而一旦开始小跑,四条腿被分成了两组,一组在地上撑着、一组在空中甩着,两组腿的控制目标完全相反——这是 Trotting。

四种模式,四套控制律,用的物理量都不一样。硬塞进一个函数里写 if-else,代码三个月后就没人敢动了。

状态机做的第二件事更重要:管切换的安全性。README 里那个操作序列——先 24——不是随便定的顺序。你不能从趴在地上直接进入小跑,因为小跑的控制律假设机器人已经站着、四条腿已经受力、状态估计已经收敛。中间那个 FixedStand 是必经的过渡态。状态机的价值就是把「哪些切换是合法的」这件事变成显式的、可审查的代码,而不是散落在各处的隐含假设。

还有一个细节值得停一秒:三个 Test 状态。State_SwingTest 测摆动腿,State_StepTest 测踏步,State_BalanceTest 测平衡控制。

这三个状态在真正跑起来的机器人上没有任何用处,它们纯粹是调试脚手架。它们的存在说明作者很清楚一件事:足式控制器的每一块都必须能被单独验证。你不可能一上来就调 Trotting——那里面同时跑着步态、摆动轨迹、状态估计、力分配四个东西,出了问题你根本不知道该怪谁。正确的做法是先用 SwingTest 单独确认摆动腿的轨迹和逆运动学是对的,再用 BalanceTest 单独确认力分配是对的,最后才把它们合起来。

把可调试性做进架构里,这是这份教学代码里最值得抄的一个决定。

Gait:把「走路的节奏」变成三个文件

include/Gait/ 只有三个文件,但这三个名字信息量很大:

WaveGenerator.h   —— 波形发生器
GaitGenerator.h   —— 步态生成器
FeetEndCal.h      —— 足端(落脚点)计算

我不去猜作者内部的具体实现,但从命名和分层看,这是一条很清晰的流水线:相位 → 落脚点 → 足端轨迹

相位:步态的本质是一组周期信号

先讲原理,这块是通用知识,不依赖具体仓库。

四足步态的定义,说到底就是回答三个问题:

第一,一个周期多长。 步态周期 T 决定了腿多久迈一次。

第二,一条腿在周期里有多少比例踩在地上。 这个比例叫占空比(duty factor)。占空比大于 0.5,意味着大部分时间腿都在地上,同一时刻至少有多条腿支撑,这类步态稳定但慢,典型的就是 walk。占空比等于或小于 0.5,腿在空中的时间和在地上的时间相当甚至更多,速度快但对控制要求高,trot 就属于这一类。

第三,四条腿之间的相位怎么错开。 每条腿有一个相位偏移量。trot(对角小跑)是左前和右后同相、右前和左后同相,两组差半个周期——所以你看机器狗小跑时,永远是对角线上的两条腿同时抬起同时落地。walk 则是四条腿依次相差四分之一周期,任何时刻都有三条腿在地上。

所以「换一种步态」在代码里往往不是换算法,而是换一组参数:周期、占空比、四个相位偏移。这就是为什么这里叫 WaveGenerator(波形发生器)而不是 TrotController ——它生成的是一组周期性的相位信号,具体是什么步态由参数决定。

从这组周期信号里,每一条腿在每一个控制周期都能得到两个东西:我现在是支撑相还是摆动相,以及我在当前相里走了百分之多少。这两个量往下传,就是整个控制器的节拍器。

关于步态的更细致展开——包括为什么 trot 是四足机器人最常用的中速步态、相位差和稳定裕度的关系——可以看足式步态基础那篇,这里不重复。

落脚点:FeetEndCal 在算什么

FeetEndCal 这个名字直译是「足端计算」。摆动腿要落到哪里,是四足控制里一个专门的子问题,而且它的答案并不显然。

直觉上你会觉得「往前走就把脚往前放呗」。但实际上落脚点决定的不只是位置,还有下一步的速度。脚落得比身体重心投影靠前,会产生减速的效果;落得靠后,会加速。所以落脚点其实是速度控制的主要手段之一——你想让机器人保持某个速度,就得根据当前速度和期望速度的差,去调整每一步落在哪。

这是经典足式控制里一个非常「人设计出来」的环节:有明确的物理含义,有可以推导的公式,参数可以一个一个调,出了问题可以画图看。后面对照强化学习时,这一点会再提。

摆动腿与支撑腿:四足控制的核心二分

有了相位,就有了这个控制器最重要的一次分叉。每个控制周期,四条腿被分成两拨,走两条完全不同的处理路径。

摆动相的腿,是位置问题。 这条腿不接触地面,不承担重量,你要做的是让它的足端沿着一条平滑的轨迹从起点飞到落脚点——抬起、跨越、落下。轨迹要平滑(否则关节会剧烈抖动),落地瞬间的速度要接近零(否则会砸地并弹起来)。算出足端的期望位置之后,用逆运动学反解出三个关节角,再让关节去跟踪这个角度。这条路径的本质是位置跟踪,而位置跟踪的最常见实现就是 PD 控制——如果你对比例微分这套东西还不熟,PID 控制那篇是绕不过去的前置。

支撑相的腿,是力的问题。 这条腿踩在地上,你没法「移动」它——它被地面约束住了。你能做的是控制它推地的力。而这个力的目的不是让这条腿动,是让身体动。

这个二分是理解四足控制的钥匙。摆动腿在解「腿该去哪」,支撑腿在解「身体该怎么动」。两者同时进行,靠相位信号切换。一条腿在一秒钟内可能来回切换好几次身份。

control:状态估计与力分配

include/control/ 四个文件,是整个控制器的中枢:

ControlFrame.h     —— 控制框架(主循环)
CtrlComponents.h   —— 控制组件的集合
Estimator.h        —— 状态估计器
BalanceCtrl.h      —— 平衡控制器

ControlFrameCtrlComponents 是骨架:一个是每个控制周期都会被调用的主循环,一个是把各个模块(接口、状态估计、机器人模型、步态、状态机等)打包在一起传来传去的容器。这种「组件包」的写法在控制程序里很常见,好处是各模块之间通过一个共享的结构体交换数据,不用互相持有对方的指针。

真正有内容的是另外两个。

Estimator:机器人并不知道自己在哪

这是初学者最容易低估的一块。

想一想:机器人身上有什么传感器?IMU 给你角速度和加速度,关节编码器给你每个关节的角度和速度。就这些。

没有任何一个传感器直接告诉你「身体现在的位置和速度」。

这不是小问题,因为整个平衡控制都建立在「我知道自己的重心在哪、速度多少」之上。落脚点计算需要当前速度,平衡控制需要重心位置和姿态。这些量必须估计出来。

经典做法是把几路信息融合起来:

  • IMU 的加速度积分一次得到速度、两次得到位置。但加速度计有零偏,积分会漂移,时间稍长就完全不能用了。IMU 的姿态(滚转、俯仰)倒是相对可靠。
  • 关节编码器 + 运动学:如果你知道某条腿现在踩在地上没有滑动,那么这条腿的足端在世界坐标系里是不动的。已知关节角,正运动学能算出足端相对身体的位置,反过来就得到了身体相对这个「不动点」的位置和速度。这条路的精度依赖一个前提:这只脚真的没滑
  • 足端接触判断:所以还得知道哪条腿现在真正踩实了。相位信号能给一个预期值(现在应该是支撑相),但预期和现实会有偏差——地面比预想的低,脚就会晚一点着地。

把这几路信息按各自的可信度融合起来,得到一个身体状态的最优估计,这就是 Estimator 干的事。经典实现通常是卡尔曼滤波一类的方法。

这里面有个循环依赖很有意思:状态估计依赖接触判断,而接触判断又受控制器自己的步态相位影响。机器人是靠「我以为我踩在地上」来推断「我在哪」的。这个假设一旦被打破——比如踩空了、踩到了会滑的地面——估计就会出错,而估计出错会让控制器做出错误的动作,进一步恶化状态。足式机器人在光滑地面上摔得特别难看,很大一部分原因在这里。

状态估计这块内容展开讲能写一整篇,本卷里有专门的足式状态估计那篇,这里点到为止。

BalanceCtrl:把身体的期望变成四条腿的力

平衡控制的基本思路,可以用一句话概括:先想清楚身体需要什么,再把它分配到腿上。

具体分两步。

第一步,把身体当成一个刚体,问「要让它达到期望状态,需要施加什么样的合力和合力矩」。期望状态包括重心的位置和速度、身体的姿态和角速度。当前状态由 Estimator 给出。两者的偏差,通过一组 PD 律,转换成需要的合力(让重心加速到期望速度)和合力矩(把身体姿态摆正)。到这一步为止,其实和一个自平衡小车的思路没有本质区别——只是从二维变成了三维。

第二步是四足特有的:把这一组合力合力矩,分配到当前处于支撑相的那几条腿上。

这一步为什么难?因为它通常没有唯一解。三条腿支撑时,你需要决定每条腿各出多大的三维力,一共九个未知数,而约束只有六个(三个力分量加三个力矩分量)。解不唯一,意味着要在众多可行解里挑一个「最好的」。

而且这些力不能随便挑,有硬性的物理约束:

  • 腿只能推不能拉。脚和地面之间没有胶水,法向力必须是压力,不能是拉力。
  • 摩擦锥约束。切向力不能超过法向力乘以摩擦系数,否则脚会滑。这个约束在数学上是一个锥形区域,工程上常近似成一个多面锥。
  • 力矩上限。关节电机能输出的力矩是有限的,算出来的力必须是电机真能给出来的。

「在一堆线性约束下,找一组让某个二次代价最小的解」——这正好是二次规划(Quadratic Programming, QP)的标准形式。

而仓库里恰好躺着一个 QP 求解器:include/thirdParty/quadProgpp/QuadProg++.hhArray.hh,以及 src/quadProgpp/ 下的对应实现。这是这个仓库里除了绘图库之外唯一的第三方依赖。

一个只有几十个文件的教学控制器,专门引入了一个二次规划求解器——从工程角度看,这个依赖出现在这里,指向的就是支撑腿的力分配。我不去替作者下结论说它一定只用在这一处,但把力分配写成带约束的优化问题,是这类控制器里非常成熟的做法。

另外那个第三方依赖是 thirdParty/matplotlibcpp.h,配合 common/PyPlot.h 使用。这是让 C++ 程序直接调 Python 的 matplotlib 画图。一个控制器里内置绘图能力,同样是教学取向的证据:调控制器八成的时间在看曲线。期望值和实际值画在一起,你才知道是超调了还是滞后了。

common 与 interface:模型层和边界层

include/common/ 是被所有模块共用的基础设施:

unitreeRobot.h     —— 整机模型
unitreeLeg.h       —— 单腿模型
mathTypes.h        —— 数学类型定义
mathTools.h        —— 数学工具
LowPassFilter.h    —— 低通滤波器
enumClass.h        —— 枚举定义
timeMarker.h       —— 计时
PyPlot.h           —— 绘图

unitreeLegunitreeRobot 这一对分层很讲究:单腿负责这条腿自己的正逆运动学和雅可比,整机负责把四条腿组装起来、处理身体坐标系和腿坐标系之间的换算。运动学是可以按腿复用的,几何参数变了换个参数就行,这种分法让同一套控制代码能适配不同尺寸的机型。

LowPassFilter 单独成文件,说明滤波在这个系统里是常规操作而不是补丁。IMU 数据、估计出来的速度、关节速度,都是典型需要滤波的量——原始信号里的高频噪声一旦进了微分项,输出会直接抖成一团。

enumClass.h 里大概率放着状态机的状态枚举、腿的编号、步态类型这些。集中定义枚举是个小事,但它避免了魔法数字散落各处。

include/interface/include/message/ 是这个控制器和外部世界的边界:

interface/IOInterface.h      —— 输入输出抽象基类
interface/IOROS.h            —— 走 ROS/Gazebo 仿真
interface/IOSDK.h            —— 走真机 SDK
interface/CmdPanel.h         —— 指令输入抽象
interface/KeyBoard.h         —— 键盘输入
interface/WirelessHandle.h   —— 无线手柄输入
message/LowlevelCmd.h        —— 下发的底层指令
message/LowlevelState.h      —— 读回的底层状态
message/unitree_joystick.h   —— 手柄数据结构

这里是两组「抽象基类 + 多个实现」,模式一模一样。

IOInterface 这一组解决的是仿真和真机的切换。控制器的核心逻辑只跟 IOInterface 打交道,只管「发一个 LowlevelCmd、收一个 LowlevelState」。至于这些数据是发给 Gazebo 还是发给真实的机器人,由具体挑哪个实现决定。这个设计的收益极大:你在仿真里调好的控制器,理论上换一行实例化代码就能上真机。

CmdPanel 这一组解决的是指令来源的切换:键盘、无线手柄,或者从导航栈来(对应 State_move_base)。控制器不关心速度指令是谁给的。

顺带一提,仓库的 library/ 下面并存着三个版本的 unitree_legged_sdk(3.2、3.4、3.8.0),各自带一份头文件和预编译库。这是 SDK 一代目的接口,和现在的 unitree_sdk2 不是一回事——两代 SDK 的差别本身是个话题,sdk2 架构那篇里拆过新一代的分层。

把整张图拼起来

现在可以把这个控制器的数据流画出来了:

        键盘 / 手柄 / move_base
                  │  速度指令
                  ▼
        ┌──────────────────┐
        │   FSM 状态机      │  Passive → FixedStand → Trotting …
        └────────┬─────────┘
                 │ 当前状态决定用哪套控制律
                 ▼
   ┌─────────────────────────────┐
   │  WaveGenerator  相位信号     │
   │        ↓                    │
   │  GaitGenerator  支撑/摆动划分 │
   │        ↓                    │
   │  FeetEndCal     落脚点        │
   └──────┬───────────────┬──────┘
          │摆动腿          │支撑腿
          ▼                ▼
   足端轨迹 → 逆运动学   BalanceCtrl
   (unitreeLeg)        合力合力矩 → QP 分配
          │                │
          └───────┬────────┘
                  ▼
            LowlevelCmd(十二个关节)
                  │
             IOInterface
            ┌─────┴─────┐
          IOROS       IOSDK
        (Gazebo)    (真机)
                  │
            LowlevelState 回读
                  │
               Estimator ──→ 身体位置/速度(回灌给上面各环节)

每一个方框,都对应目录里的一个文件。整套系统里没有一个环节是黑箱:相位怎么算的、落脚点公式是什么、力怎么分配的、估计器融合了哪几路信号,全都写在代码里,可以逐行读、逐个参数调。

和强化学习路线的对照

同一家公司的 unitree_rl_gym 走的是完全不同的路。它的 README 把流程写成四步:TrainPlaySim2SimSim2Real——在仿真环境里让机器人和环境交互,找到一个让设计好的奖励最大化的策略;用 Play 验证策略;换一个仿真器再验一遍,确保策略没有过度依赖某个仿真器的特性;最后部署到真机。

它的目录结构也完全不同:legged_gym/envs/ 下面按机型放环境和配置,legged_gym/scripts/ 下是 train.pyplay.pydeploy/ 下是部署代码和预训练模型。你在里面找不到 Gait,找不到 BalanceCtrl,找不到 FeetEndCal

这就是这次对照最直观的一处:unitree_guide 里那一整套精心设计的模块,在 RL 路线里被一个策略网络整体替代了。观测进去,关节指令出来,中间的相位、落脚点、力分配都不再显式存在——它们(如果存在的话)以某种形式隐含在网络权重里。

两条路各自的代价与好处,我尽量客观摆开:

经典控制路线

  • 每一步都是人设计的,每个中间量都有明确的物理含义。速度不对,你可以去看落脚点计算;姿态抖,你可以去看 PD 增益;打滑,你可以去看摩擦锥约束。问题可以被定位到具体的环节。
  • 可以做稳定性分析,可以推导边界条件,可以论证在什么条件下系统一定不会发散。这在需要安全论证的场合有实际价值。
  • 调试手段丰富:仓库里那三个 Test 状态、内置的绘图能力,都是这条路线的典型配置。
  • 代价是设计成本高。每一块都要人去建模、推导、实现、调参。想加一种新步态、想适应一种新地形,往往意味着改公式。
  • 更关键的代价是对未建模情况适应差。整套推导建立在一堆假设上:地面是刚性的、脚不打滑、模型参数准确、接触状态判断正确。现实一旦跳出这些假设——碎石、软泥、突然的外力——性能就会下降,而且下降的方式经常不优雅。

强化学习路线

  • 端到端学出来,不需要人去设计步态和力分配。对复杂地形的适应性是这条路线被广泛采用的主要原因:训练时在仿真里铺上各种随机地形和随机扰动,学出来的策略往往能处理设计者根本没预想过的情况。
  • 加新能力的方式是改奖励和改训练环境,不是改公式。
  • 代价是不可解释。策略网络输出一个奇怪的动作,你没有一个「中间变量」可以去查。你只能改奖励再训一遍。
  • 高度依赖仿真质量。策略在仿真里学到的东西能不能迁移到真机,取决于仿真和现实的差距有多大。unitree_rl_gym 的流程里专门放了一步 Sim2Sim,就是为了检查策略是不是过度贴合了某一个仿真器的特性。这一步本身就说明了这条路线的软肋在哪。
  • 奖励设计是门手艺。奖励项之间互相牵制,权重改一点结果可能天差地别,而反馈周期是以小时甚至天计的训练。这部分经验很难写成公式传授。

我不打算给这两条路线排名次,因为它们解决问题的方式不同,付出的代价也在不同的地方。真实的工程里,两者并不是互斥的——RL 策略也需要状态估计提供观测,也需要一个外层的状态机来管「什么时候允许策略接管、什么时候必须切回安全状态」。unitree_guide 里那个 State_Passive 存在的理由,在任何路线下都成立。

RL 路线的完整拆解在unitree_rl_gym 那篇里,两条路线的正面对比另有专门一篇展开。

读完这个仓库,你应该带走三件事

第一,控制器的目录结构就是控制框图。 FSM、Gait、control、common、interface——五个目录,五个层次,数据从指令输入流到关节力矩输出。以后你打开任何一个陌生的机器人控制项目,先把目录列出来,多半能直接读出它的架构。

第二,摆动腿和支撑腿的二分,是四足控制的分水岭。 摆动腿是位置问题,走轨迹规划加逆运动学;支撑腿是力的问题,走合力合力矩分配加带约束优化。相位信号决定谁是谁。理解了这一刀,四足控制里大半的设计选择都有了解释。

第三,两条路都值得看,但顺序有讲究。 先读经典实现,你会知道「走路」这件事到底由哪些子问题组成——落脚点为什么影响速度、状态估计为什么会漂、力为什么不能随便分。带着这些问题去看强化学习路线,你才能看懂那个策略网络到底替你解决了什么、又把什么变成了黑箱。反过来,如果一上来就是 train.py --task=go2,跑通了也只是跑通了。

如果你手上还没有真机,unitree_guide 配的 Gazebo 仿真是个门槛很低的入口——一个 ROS 工作空间、一次 catkin_make,就能把上面这些模块全都跑在眼前。至于更完整的学习路径,可以顺着这一卷往下走,或者先回机器人与具身智能卷把电机、PID、运动学这些地基补齐。

📄 来源 / 自校链接

本文为公开资料整理,非亲测。关键参数与代码请结合实物与下列官方来源验证。

内容有错、看不懂、或想看下一期?告诉我们 →

本文为公开资料的学习整理,非亲测。涉接线/花钱/合规的步骤请结合实物与官方最新资料验证,风险自负。见免责声明