机器人分层控制 · 架构讲解

分层机器人:层与层之间,
到底是什么关系?

一台真正部署运行的机器人,身上同时跑着十几套算法:最高层切换任务,上层在不同决策阶段间选择, 中层算轨迹位姿,底层驱动电机,还有一圈永远在盯着的安全规约。它们运行在差好几个数量级的频率上。 自然的问题是:这些层之间到底是什么关系?上层凭什么、在什么时候、以什么方式影响下层?

具体一点:当我们说「上层给下层一个参考」,这个参考到底是个什么东西——它的数学是什么、代码是什么? 上层会不会突然把下层「kill 掉」、切换到另一层?我怎么知道此刻该跑上层还是下层,难道只是「轮到它了」? 控制权又是在什么时候、依据什么,从一套控制器交到另一套的?这篇就把这些问题逐一拆开, 再讲清楚交接怎么做才不出岔子,最后用腿足机器人、飞控、航天器三个真实控制栈看它们怎么落地。 预设读者:会基本控制、微积分、线性代数。

深度讲解 · 6 个交互演示 · 最后更新 2026-05-27

先给一句话答案

层之间不是轮流、不是谁 kill 谁——它们同时在跑。下层一直在算「发给电机的命令」, 上层一直在算「给下层的目标(参考)」,两者干的不是同一件事,所以不存在「现在跑哪个」。上层影响下层有四种方式 (设参考 / 设约束 / 选模式 / 抢占),其中只有最后一种「抢占」才是真正的夺权。控制权何时交接由四类触发决定 (时间 / 事件 / 值 / 安全)——每一类下面都单独讲、配代码与经典工作。

§1并发,不是轮流

先纠正一个最常见的误解。分层不像接力赛——不是上层跑一段、交棒、停下,再换下层跑。 机器人的各层是同时、持续地运行,每层跑在自己的频率上。最底层的电机电流环从不停,它一直在以上万赫兹算「该给电机多少电流」; 最高层的任务规划也从不停,它每秒重新盘算一次大局。它们干的根本不是同一件事,所以「现在该跑上层还是下层」是个伪问题——两个都在跑。

谁在算什么(这是理解全篇的关键)

下层一直在算「发给执行器的命令」$u$(电机力矩/电流)。上层一直在算「给下层的目标」,也就是参考 $r$。 上层的输出正是下层的输入目标。所以上层不会「kill」下层——它只是不断地改下层要追的目标。 频率自上而下递增:上层慢(想得多、看得远),下层快(反应快、贴着电机)。

最朴素的例子是级联控制:一个伺服里套着位置环、速度环、电流环,三个环同时在跑。外层位置环不去碰电机,它只算一个 「期望速度」交给速度环;速度环再算一个「期望力矩」交给电流环;只有最内的电流环真正驱动 PWM。外环慢、内环快:

$$ v_{\text{ref}} = K_p^{\text{pos}}(x_d - x), \qquad \tau_{\text{ref}} = K_p^{\text{vel}}(v_{\text{ref}} - v), \qquad f_{\text{pos}} < f_{\text{vel}} < f_{\text{cur}} $$

推广到整台机器人:第 $k$ 层从上方收到参考 $r_k$,以频率 $f_k$ 运行,把它细化成给下层的参考 $r_{k+1}$;最底层第 $n$ 层产出执行器命令 $u$。向上则汇报状态与事件。

$$ r_{k+1} = \pi_k(x,\, r_k), \qquad u = \pi_n(x,\, r_n), \qquad f_1 < f_2 < \cdots < f_n $$
骨架图 · 各层并发,参考下传、事件上传,越下越快
任务 / 使命0.1–1 Hz · 规划/状态机 · 视野 秒–分钟 决策 / 行为1–10 Hz · FSM / 行为树 运动 / 轨迹10–100 Hz · MPC / 局部规划 全身 / 控制100–1000 Hz · WBC-QP / 操作空间 伺服 / 电机1–20 kHz · PID / 电流环 物理本体 plant 参考 r ↓ 事件 ↑

全篇的地图。每个方框是一个一直在跑的循环,频率自上而下递增。橙箭头是参考下传(上层给下层设目标), 青箭头是事件/状态上传(下层把结果与异常报上去)。第一部讲这两条箭头到底是什么;第二部讲交接的时机;第三部看经典架构;第四部讲交接的工程;第五部看真实系统。

为什么内环要比外环快?工程铁律:内环带宽约为外环的 5–10 倍。够快时外环可以把内环近似当成「瞬间完成」, 外环设计大大简化,而且扰动会在传到上层之前就被快内环吃掉。下面的演示就是这一点。

Demo 1 · 嵌套控制环:内环快,扰动到不了上层
位置环与速度环同时在跑——内环每个外环周期跑很多次(这就是「并发」而非「轮流」)。 每隔约 1.9 秒注入一次速度扰动(红竖线)。速率比拉到 1×:内外环一样慢,扰动一路漏到位置。拉到 15–20×:快内环在慢外环回头看之前就把扰动压平。

§2什么是"参考"

既然上层影响下层的主要方式是「给一个参考」,那就得把参考讲清楚。一句话:参考 $r$ 是相邻两层之间的接口变量,是下层的「期望值 / 目标」。 上层的输出就是下层的参考;下层的全部职责,就是让现实去逼近这个参考。

关键在于:参考的「类型」随层而变,越往下越具体、维度越低、越贴近物理。所谓「分解」,就是把抽象参考一层层翻译成具体参考:

参考的抽象阶梯 · 同一个目标,逐层翻译得越来越具体
任务层的参考:一个符号 / 目标位姿「把杯子放到桌上」 → T_goal ∈ SE(3) 运动层的参考:一条期望轨迹末端在未来 1s 的位姿序列 x_des(t) 控制层的参考:期望关节力矩 / 地面力q̈_des, 或地面反作用力 f* 伺服层的参考:期望电流(安培)i_des → PWM 占空比 翻译 / 分解 翻译 / 分解 翻译 / 分解 越往下:越具体、维度越低、离电机越近

按「参考是个什么形状」,可分成五类——前两类最常见:

参考的类型是什么下层拿它干嘛例子
设定点 setpoint一个要保持的目标值(常数)把误差 $r-y$ 调到 0期望速度 $v_{\text{ref}}$
轨迹 trajectory时间索引的参考 $r(t)$,一条要跟的曲线跟踪整条曲线期望末端轨迹 $x_{\text{des}}(t)$
目标 / 代价 objective一个要最小化的代价,不是单点自己优化出动作MPC 把"期望力"编码成跟踪项
约束集 constraint一个"不许出去"的可行域在域内自由,只是不越界安全包络、keep-out
模式 / 符号 mode一个离散选择切到被指定的行为"现在跑抓取技能"

参考的数学:它就是下层自己那个控制问题里的「期望值」。下层要么用一条反馈律把误差消掉,要么把参考当成优化目标去最小化:

$$ u = \kappa(x,\, r)\quad(\text{e.g. } u = K(r - y)), \qquad u = \operatorname*{arg\,min}_{u}\ \lVert y(x,u) - r\rVert^2 + \lambda\lVert u\rVert^2 $$

参考的代码:下面这段把「上层何时动作」也一并讲清楚了——上层在它自己的时钟上周期性地刷新参考, 下层则远快于它、持续地用当前保持着的那个参考(零阶保持 ZOH)。所谓「上层做了一次动作」,具体就是「刷新了一次参考」。

# 上层(慢) 产出参考 r;下层(快) 用 r。两者并发,r 在更新之间保持不变(ZOH)
r = upper.initial_reference()             # 当前生效的参考
while True:                               # 下层主循环:每 dt 一次,远快于上层
    y = sense()
    u = K @ (r - y)                       # 参考进入误差: desired - actual
    actuate(u)                            # 只有下层真正驱动执行器
    if clock.tick(period=T_upper):        # "轮到上层了"——它的时钟到点
        r = upper.update(state)           # 刷新参考 = 上层的一次"动作"

§3上层决定下层的四种方式

「设参考」是最主要、但不是唯一的耦合方式。把上层影响下层的全部手段摊开,一共四种。前三种里上层都没有「杀掉」下层—— 下层始终在算执行器命令,上层只是改它的目标 / 边界 / 模式;只有第四种「抢占」才是真正把控制权从一套控制器抢到另一套。

耦合方式上层给下层的是下层还自由吗是"夺权"吗例子
设参考 reference一个目标值 / 轨迹否,必须追踪它否(下层一直在跑)级联、MPC→WBC、制导→控制
设约束 constraint一个可行域 / 边界是,域内自由安全包络、参考调速器、CBF
选模式 mode一个离散选择 / 激活哪个技能否,按所选模式否(命令下层切,非夺权)3T executive、行为树、自驾仪选模式
抢占 preemption直接接管执行器(唯一真正夺权)subsumption 抑制、Simplex 安全切换
回答「上层会不会突然 kill 掉下层」

正常情况下不会。设参考 / 设约束 / 选模式,下层都一直活着、一直在算执行器命令,上层只是改它追的东西。 真正的「夺权」只发生在抢占这一种,而且它通常是同一层级上两套控制器之间的切换(比如「性能控制器」和「安全控制器」都在控制层,安全的把性能的换下来), 或由优先级最高的安全层发起——不是「上层 kill 下层」,而是「更该信的那套把不该信的那套顶替掉」。

知道了上层通过哪四种方式影响下层(§3),还剩一个问题:每一次「上层动作」或「控制权交接」,到底在什么时候、依据什么发生?答案有四类触发。 你之前的猜测「是不是只是轮到它了」——对时间触发确实是,但另外三类不是。前三类(时间 / 事件 / 安全)直觉上好懂,唯独值触发最微妙、也最常用在机器人的运动/行为仲裁上,所以单独展开。先用一张图对比最常见的两类:

两种最常见的触发 · 时间触发(周期刷参考) vs 事件触发(条件变真)
时间触发:上层每个周期刷新参考 r(阶梯/ZOH),下层持续追踪 ↑ 刷新↑ 刷新↑ 刷新↑ 刷新 橙=参考 r(阶梯,每 1/f 刷一次) · 青=下层输出 y(一直在追) 事件触发:信号越过卫式阈值的那一刻才切换 阈值 g(x) ← 这一刻切换

§4时间触发:到点就轮到它

判据:纯时钟驱动,第 $i$ 个任务每 $\Delta t_i = 1/f_i$ 秒跑一次,与状态无关。这就是你猜的「轮到它了」——但请注意它适用于这类周期刷新,不是所有交接都这样。 确定性来自时间表,不来自数据。一个直接推论是零阶保持(ZOH):在两次刷新之间命令保持不变,$u(t)=u(t_k),\ t\in[t_k,t_{k+1})$——这是每个数字控制回路的根本假设。

经典工作与工程:

  • 循环执行体(cyclic executive,Baker & Shaw 1988):一张静态调度表把「大周期」切成若干「小周期」,每个时隙固定跑哪些任务。运行时没有调度器、不抢占,只按帧计数查表——极其确定、易认证,但僵硬难改。
  • 时间触发架构(TTA,Kopetz):所有计算/通信/采样都由一个全局同步时钟驱动,节点按预分配的 TDMA 时隙发送。完全先验确定,因此能过 DO-178C / ISO 26262 认证。部署:FlexRay(汽车底盘/动力)、TTEthernet(NASA Orion 飞船航电)、TTP(A380 子系统)。
  • 速率单调调度(RMS,Liu & Layland 1973):周期越短优先级越高;可调度的充分利用率界 $U=\sum_i C_i/T_i \le n(2^{1/n}-1) \to \ln 2 \approx 0.693$。
  • ros2_control:controller-manager 跑一个固定速率(常见 1 kHz)的 read → update → write 回路——活生生的时间触发控制。
# 固定速率回路(ros2_control 风格):决策是"到点了",与状态无关
T = 1.0 / 1000.0                  # 1 kHz 周期
next_t = now()
while running:
    x = read_state()
    u = controller.update(x, T)   # 每周期更新一次
    write_command(u)
    next_t += T
    sleep_until(next_t)           # 用绝对截止时刻,避免漂移

§5事件触发:条件成真才切

判据:当一个状态谓词 $g(x)$ 变真时才动;事件之间时钟无关紧要。$g$ 可以是「到达子目标 $\lVert x-x_{\text{goal}}\rVert<\epsilon$」「检测到接触 $\hat f>F_{\text{th}}$」「误差超阈」「电量低」等。 FSM 的转移、行为树的 condition 节点(每 tick 重判),都是这类。

控制理论里有一个专门的分支把它形式化:事件触发控制(event-triggered control,Tabuada 2007)。把控制 $u=K\hat x$ 锁在上次采样的状态 $\hat x$ 上,定义测量误差 $e=\hat x - x$, 只在误差涨到与状态成比例的阈值时才重新采样、重算控制:

$$ \lVert e\rVert \ge \sigma\,\lVert x\rVert, \qquad \sigma \in (0,1) $$

每次触发把 $e$ 清零。这保证 Lyapunov 函数持续下降(系统稳定),同时把「周期执行」换成「按需执行」,省下大量算力与通信——网络化控制、多智能体一致性里特别有用。 它还有个预测变种自触发控制(self-triggered,Anta & Tabuada 2010):每次更新时预先算出下一次更新时刻 $t_{k+1}=t_k+\tau(x(t_k))$,连持续监测都省了。

# Tabuada 事件触发:只在 ‖e‖ ≥ σ‖x‖ 时才重算控制,其余时刻 ZOH 保持
sigma = 0.2                       # 设计常数 ∈ (0,1)
x_hat = read_state()              # 上次采样的状态
u = K @ x_hat                     # 保持的控制
while running:
    x = read_state()
    e = x_hat - x                 # 自上次更新以来的测量误差
    if norm(e) >= sigma * norm(x):   # 触发条件被破坏
        x_hat = x                    # 重新采样 → e 清零
        u = K @ x_hat                # 重算控制
    write_command(u)              # 否则:保持上一个 u(ZOH)

其余事件源还有:硬件中断(编码器边沿)、看门狗超时 / deadline 错过(当成故障事件,见 §16)、以及 ROS action 的 goal/feedback/preemption——都是异步的事件触发。

§6值触发:给候选打分,选最优的那个(cost)

这是最微妙、也是机器人在运动/行为层最常用的触发。它不靠离散事件,而是每个周期给一组候选动作/轨迹打一个标量代价(或效用),取最优的那个

$$ a^{\star} = \operatorname*{arg\,min}_{a\in A}\ \sum_i w_i\, c_i(a) \qquad\Big(\equiv \operatorname*{arg\,max}_{a\in A}\ \textstyle\sum_i w_i v_i(a),\ \ v_i=-c_i\Big) $$

(右边是等价的效用 / 投票形式,令 $v_i=-c_i$ 即可。)

妙处在于:没有 if 语句。被选中的动作随代价地形变化而自动翻转——障碍一出现,绕行轨迹的代价跌破直行轨迹,机器人就自己绕开了,看起来像「触发了避障」,其实只是 argmin 的结果变了。

经典例子:ROS 的 DWA 局部规划器

这正是很多人记忆里那套「每一步选 cost 最低的行动」的设计。Dynamic Window Approach(Fox, Burgard, Thrun 1997;ROS dwa_local_planner)在动态窗口 (一个周期内由加减速可达的 $(v,\omega)$ 集合)里采样,对每个样本前向仿真一小段轨迹,按三项加权代价打分,取最低:

$$ \operatorname*{arg\,min}_{(v,\omega)}\ \Big[\, w_{\text{path}}\,d_{\text{path}} + w_{\text{goal}}\,d_{\text{goal}} + w_{\text{occ}}\,c_{\text{obs}} \,\Big] $$

$d_{\text{path}}$ 是轨迹末端到全局路径的距离,$d_{\text{goal}}$ 是到局部目标的距离,$c_{\text{obs}}$ 是沿途查 costmap 的最大障碍代价(撞了即 $\infty$,直接淘汰)。 ROS 的经典配置权重是 path_distance_bias=32.0goal_distance_bias=24.0occdist_scale=0.01(脚注:包里 .cfg 的裸默认是 0.6/0.8/0.01,而教程/TurtleBot 配置用 32/24/0.01——后者是大家实际在用的)。 其中 $c_{\text{obs}}$ 来自 costmap 的膨胀层,从致命障碍向外指数衰减 $\text{cost}(d)=252\cdot\exp(-k\,(d-r_{\text{inscribed}}))$(系数 $252=253-1$)。

# DWA:采样动态窗口里的 (v, ω),前向仿真打分,取 cost 最低(ROS 经典 32/24/0.01)
def dwa_pick(state, candidates, costmap):
    B_PATH, B_GOAL, B_OCC = 32.0, 24.0, 0.01
    best, best_cost = None, float("inf")
    for v, w in candidates:                        # 动态窗口内的速度对
        traj = rollout(state, v, w)                # 前向仿真一小段轨迹
        cost = (B_PATH * dist_to_path(traj, state.path)
              + B_GOAL * dist_to_goal(traj, state.goal)
              + B_OCC  * max_obstacle_cost(traj, costmap))   # 撞了即 inf 淘汰
        if cost < best_cost: best_cost, best = cost, (v, w)
    return best                                    # argmin:选 cost 最低
Demo 2 · DWA 轨迹打分(值触发)
每条弧是一个采样的 $(v,\omega)$ 前向仿真,颜色按总代价(绿低→红高),最粗的绿弧是被选中的 argmin。 调大 goal_bias 机器人变得「目标饥渴」贴着障碍走;调大 occdist_scale 它变胆小绕更远;点「移动障碍」看最优弧自动改变——这就是值触发:调权重=调性格,没有一行 if。

值触发的一大家族

把候选、代价、权重换成不同的东西,就得到一整族工程方法——它们本质都是「打分取最优」:

方法候选是什么代价 / 选择部署
DWB(Nav2)动态窗口 $(v,\omega)$可插拔 critic 的加权和(PathAlign/GoalAlign/Obstacle…)取 minROS2 Nav2
TEB(Rösmann)整条带(位姿+时间间隔)多目标加权最小二乘(时间+离障+运动学),g2o 优化ROS 局部规划
MPC未来一段控制序列滚动时域 argmin 阶段代价 s.t. 约束,执行第一步AV、腿足、无人机
MPPI(Williams)$K$ 条带噪控制序列按 $w_k\propto e^{-S(\tau_k)/\lambda}$ 软加权(软 argmin)AutoRally、Nav2 MPPI
Frenet 采样(Werling 2010)横/纵多项式候选轨迹代价 = jerk积分 + 时间 + 终端偏差,取 min自动驾驶(Apollo EM 同理)
DAMN(Rosenblatt)离散转向命令行为投票加权和 $V(c)=\sum w_i v_i(c)$ 取 argmaxCMU Navlab
势场 / motor schema(连续)运动方向代价场负梯度 / 加权向量和(有局部极小)机械臂避障、早期移动机器人
加权 QP(软优先级)关节加速度$\min\sum w_i\lVert J_i\ddot q+\dot J_i\dot q-\ddot x_i^*\rVert^2$,权重决定冲突时谁赢人形/四足 WBC
抓取选择候选抓取位姿抓取质量(force-closure ε-度量 / Dex-Net / GraspNet 打分) 取 argmax分拣/拣选产线
落脚点选择候选落脚点地形代价图(坡度/粗糙/可达)取 minANYmal / 四足
效用 AI(IAUS)候选行为各行为按效用曲线打分取 argmax商业游戏 AI
值触发 vs 硬优先级(一个要点)

值触发是「软」的:用权重把多个目标揉进一个标量代价,谁权重大谁在冲突里赢,低优先目标会优雅退化而非被清零。 与之相对的是 §10 / 控制层的硬优先级(零空间投影、严格分层 QP):高优先任务一分不让,低优先只在其零空间里做。 两者都在「协调多个目标」,但一个用打分排序、一个用线性代数投影。

再往外,RL 的贪心策略 $a^\star=\operatorname*{arg\,max}_a Q(s,a)$、anytime 规划(A*/D* Lite 最小化 $g+h$、代价变了就重规划)、交叉熵法 CEM(采样-保留低代价精英-重拟合分布)都属于同一家族:定义标量代价、重打分、取最优。 值触发是机器人在运动/行为层仲裁竞争选项最通用的方式。

§7安全触发:快越界才接管

判据:当状态即将离开安全集时才触发——不是时钟、不是一般 guard,而是「即将违约」这个特殊谓词。它通常发起一次抢占(§3)。几种形态:

  • Simplex(Sha 2001):当一步前瞻的状态要离开可恢复集时切到安全控制器,判据 $x^{+}=f(x,u_{\text{complex}})\notin\mathcal R$。
  • 控制屏障函数 CBF(Ames 等 2019):QP 滤波器只在屏障约束 $L_f h + L_g h\,u + \alpha(h)\ge 0$ 将被违反、即 $h(x)\to 0$ 时才介入。
  • HJ 可达性(Bansal/Tomlin):在后向可达不安全集的边界 $V(x)=0$ 处触发「最不保守」的安全控制。
  • 运行时保障 RTA:一个可信监视器盯着不可信(常是学出来的)控制器,即将违约就 monitor-and-switch(NASA/AFRL,ASTM F3269 标准)。
# 安全触发:一步前瞻,若预测状态要出可恢复集就接管(Simplex / RTA 形态)
def safety_trigger(x, u_complex, dt):
    x_next = predict(x, u_complex, dt)      # 一步前瞻
    return x_next not in R                  # R = 可恢复 / 安全集
# CBF 变体:当屏障约束将被违反(h(x) → 0)时介入
# HJ 变体:当 V(x) ≤ 0(到达后向可达边界)时介入

这一类「该切」之后,前向夺权与反向交还的完整工程在 §15(Simplex)讲。下面先把所有切换「切得干净」的工程补齐。

§8Subsumption:抢占式耦合的极致

Brooks 1986(AI Memo 864)的反应式分层,是 §3 里「抢占」这种耦合的纯粹形态。 它按能力切层(0 避碰、1 漫游、2 探索……),每层是一张增广有限状态机(AFSM)网络,把传感直接连到动作。 协调靠两种节点接在导线上:抑制器(suppressor)在一段时间内用高层信号替换低层模块的输入线; 压制器(inhibitor)在一段时间内掐断低层模块的输出线。所以「explore」要拐弯时,把信号灌到「wander」的输入线上盖过它; 而最底层的「avoid」始终在跑、随时能压制一切——高层 subsume(接管)低层,低层永不下线,作安全兜底。这里没有中央世界模型,「世界就是它自己最好的模型」。

Subsumption 接线 · 抑制(S) 替换输入、压制(I) 掐断输出
传感 avoid (L0)始终运行 wander (L1) explore (L2) S I 执行器 S = 高层信号替换低层输入(抑制时间内) · I = 掐断输出

§9三层架构 3T:「选模式」耦合的范本

Gat 1998(On Three-Layer Architectures)按内部状态的性质切三层: Controller(无状态反应技能,传感器速率,每次迭代必须有界时间,且要「能认错地失败」cognizant failure)、 Deliberator(规划/搜索,时间不设上限)、以及夹在中间、最容易被忽视的 Sequencer / executive。 executive 正是 §3 里「选模式」耦合的执行者:它做条件式排序(RAPs / ESL / PRS 这类语言), 按当前情境激活 / 停用一组 controller 里的技能、给它们填参数、监视成功/失败并处理意外,且每个决策都必须在有界时间内给出。 它对 deliberator 的使用是异步的:「在调用规划器到它出结果这段时间里,可以发生好几次技能切换」——慢规划永远不坐进快回路,executive 拿到结果再择机并入。 「为什么恰好三层」:状态只有无 / 关于过去 / 关于未来三种。

§10RCS / 4D-RCS:「设参考」逐层分解的极致

Albus 在 NIST 做了几十年的 RCS,及其无人车版 4D-RCS(NISTIR 6910), 是 §2「参考逐层分解」这件事做得最完整的范例——而且真在美军 Demo III 自主车队和 NIST 工厂里跑过。 整摞由结构相同的控制节点堆成,每个节点都含同样的四件套:SP(感知处理:把约 10 个子事件归并成 1 个往上送)、 WM(世界模型:预测、回答查询)、VJ(价值判断:算代价/收益)、 BG(行为生成:把上层给的一条任务命令分解成约 10 条子命令发给下层,每隔自己周期的约 1/10 重规划一次)。 于是命令向下走是「参考的分解」,事件向上走是「聚合」,相邻层的时空尺度天然差约 10 倍——这正是 §2 那张抽象阶梯的工业化版本。

RCS 节点 · 命令(参考)下分解(≈10)、事件上聚合(≈10)、视野≈10×周期
一个 RCS 控制节点 SP 感知处理 WM 世界模型 VJ 价值判断 BG 行为生成 上层参考 1 条 下层:约 10 条子参考 上层:聚合后的事件 下层:约 10 个子事件
4D-RCS 层级(单车)规划视野重规划周期地图范围/分辨率
Servo 伺服50 ms5 ms
Primitive 基元500 ms50 ms
Subsystem 子系统(含避障)5 s500 ms~500 m / 4 m
Vehicle 整车战术1 min5 s
Section / Platoon 多车10 min – 小时1 – 5 min2–10 km / 30 m

到这里,「层之间是什么关系、何时依据什么交接」已经清楚了。但决定了「该切」之后,切的那一瞬间还得做干净——否则命令会跳、模式会抖、安全切换会反复横跳。这是第四部。

§11无扰切换 bumpless:切控制器时怎么不跳

这是「交接」里最经典的工程细节。控制器 A 正在控、控制器 B 待命,某一刻要从 A 切到 B。 问题:B 的内部状态(尤其是积分器)是陈旧的——它没在控,积分项停在某个老值。切过去的瞬间,B 的输出 $u_B = K_p e + I_B$ 和 A 刚才的输出 $u_A$ 对不上, 执行器命令瞬间跳一下。这一跳轻则颠簸,重则触发限位、打坏齿轮、激起振荡。

为什么这是难点

机器人的执行器是有惯量、有限幅的物理设备,一个阶跃式的命令跳变会激起高频暂态、可能瞬间饱和。 切换在控制系统里无处不在(手动↔自动、自驾仪换模式、性能↔安全、摆动↔触地),每一次切换都是一个潜在的冲击源,所以「无扰」是安全攸关的基本功。

标准解法:让待命的控制器一直「跟踪」实际施加的命令,这样切换时它的输出早已对齐。 做法是在待命控制器的积分器上加一条回算(back-calculation)项——也就是 anti-windup 里那条「跟踪」反馈:

$$ u_c = K_p\,e + I, \qquad \dot I = K_i\,e + K_t\,\big(u_{\text{applied}} - u_c\big) $$

当这个控制器没在控时,$u_{\text{applied}}$ 是另一个控制器实际施加的命令;$K_t(u_{\text{applied}}-u_c)$ 这一项把它的积分器拽到「使自己输出 = 实际命令」的值上。 切换那一刻,$u_c \approx u_{\text{applied}}$,命令连续。工业上还有两个等价做法:手动→自动切换时直接反算积分项 $I \leftarrow u^- - K_p e$;以及用 增量式(velocity-form)PID——它输出 $\Delta u$ 而非 $u$,天生没有「绝对积分状态」要对齐,白送无扰切换。

# 带回算跟踪的 PI:待命时跟踪实际施加的 u,切换处输出连续(无扰)
def pi_update(c, e, u_applied, dt, active):
    Kp, Ki, Kt = c["Kp"], c["Ki"], c["Kt"]
    u_self = Kp * e + c["I"]                       # 本控制器"想"输出的
    if active:
        c["I"] += Ki * e * dt                      # 在控:正常积分
        return u_self
    else:
        # 待命:回算项把积分器拽向"使自己输出=实际命令",对齐到切换那一刻
        c["I"] += (Ki * e + Kt * (u_applied - u_self)) * dt
        return u_self                              # ≈ u_applied,切过去不跳
Demo 3 · 无扰切换 bumpless transfer
两个 PI 控制器轮流控同一个一阶对象,设定值 r 缓慢变化,每 2.6 秒切换一次(竖线)。 关掉 bumpless:待命控制器积分器停在老值,切换处命令 u(橙线)明显跳一下,被控量 y 跟着抖。 打开 bumpless:待命控制器一直回算跟踪实际 u,切换处命令连续,y 平滑。看读数里的 Δu。

§12防抖:迟滞带 + 驻留时间

第二个交接难点:抖动(chattering)。如果切换判据是「信号过阈值就切」,而信号正好在阈值附近徘徊(带噪声),系统就会在两个模式间疯狂来回切。两个标准武器:

迟滞带(hysteresis)——升和降用不同阈值:高于 $\theta + b/2$ 才切上去,低于 $\theta - b/2$ 才切下来,中间「保持」。就是施密特触发器/恒温器的原理:

$$ \text{mode} = \begin{cases} \text{ON}, & s > \theta + b/2 \\ \text{OFF}, & s < \theta - b/2 \\ \text{hold}, & \text{otherwise} \end{cases} $$

驻留时间(dwell time)——切换后强制至少停留 $\tau_d$ 才允许再切。它还有更深的理由:在切换系统理论里,即使每个模式单独都稳定,切太快也可能整体发散。 Hespanha–Morse 的平均驻留时间定理给出稳定的充分条件——只要切换次数 $N_\sigma(t)$ 不太密:

$$ N_\sigma(t) \le N_0 + \frac{t}{\tau_a}, \qquad \tau_a > \frac{\ln \mu}{\lambda_0} $$

其中 $\mu$ 是切换时 Lyapunov 函数的跳变因子、$\lambda_0$ 是每个模式的衰减率。直觉:每次切换都「亏」一点能量,你得给系统足够时间把它赚回来;切得太密,亏空来不及补,就垮了。

# 迟滞带 + 最小驻留时间:消除阈值附近的抖动
def hysteresis_dwell(state, s, theta, band, dwell, since_switch):
    if since_switch < dwell:                       # 驻留期内:锁定,不准切
        return state, since_switch
    if not state and s > theta + band/2:           # 升:高阈值
        return True, 0.0
    if state and s < theta - band/2:               # 降:低阈值(迟滞)
        return False, 0.0
    return state, since_switch                      # 否则保持
Demo 4 · 迟滞 + 驻留防抖
带噪声的信号穿越阈值驱动一个二态开关。两个滑块都拉到 0:开关在阈值附近疯狂抖动(读数里切换频率很高)。加宽迟滞带、或加上驻留时间:抖动消失,只剩干净的切换。

§13增益调度:把「切」做成「连续地变」

有时候根本不想「切」,而想连续地从一套控制器过渡到另一套。增益调度(gain scheduling)就是这招,也是飞控的看家本领: 在若干工作点(不同空速、马赫数、关节角、负载)各设计一套线性控制器,再按一个慢变的调度变量 $\sigma$ 在它们之间插值

$$ K(\sigma) = \sum_i w_i(\sigma)\,K_i, \qquad K = (1-\beta)\,K_i + \beta\,K_{i+1} $$

(右式是在相邻两个工作点之间线性插值。)关键工程注意点:(1) 「冻结时间」假设——调度变量必须比闭环动态慢得多,控制器才能近似认为自己工作在一个准静态的工作点上; (2) 隐藏耦合(hidden coupling)——调度变量随状态变化会引入设计时没考虑的项,是经典坑;(3) 调度变量要选慢变、可测、能代表工况的量。 飞机从起飞到巡航,动压 $\bar q = \tfrac12 \rho V^2$ 变好几个量级,全靠增益调度把一套套线性律平滑接起来。

§14混杂自动机 / 监督器:把所有切换收进一个框架

前面这些切换可以统一成一个混杂自动机(hybrid automaton):系统由若干模式组成,每个模式有自己的连续动态; 模式之间有卫式条件(guard)触发跳转,跳转时有复位(reset)规定连续状态怎么重新初始化。坐在这些连续控制器之上的,是一个监督器 / 模式管理器,它只管「现在该哪个模式当家」。

前面几部其实都是它的零件

guard 就是 §4–§7 的「何时交接」的判据;reset 就是 §11 的无扰切换(复位积分器让命令连续);选哪个 mode 就是 §3 的「选模式」耦合。 而给 guard 配上迟滞带和驻留时间(§12),是为了防止 Zeno 现象——无穷多次切换挤在有限时间里,把系统锁死。一句话:监督器 = 「何时切(§4–7)+ 切哪个(§3)+ 切得干净(§11/§12)」。

操作类机器人里还有一个漂亮的「选择式」切换:混合力/位控制(Raibert & Craig 1981)。用一个对角的选择矩阵 $S$,逐任务空间方向决定这一维控位置还是控力——插孔时 $xy$ 控位置、$z$ 控力:

$$ \tau = J^\top \big[\, S\,f_{\text{pos}} + (I - S)\,f_{\text{force}} \,\big] $$

§15Simplex 安全切换:什么时候夺权,什么时候敢交还

最关键的一种抢占:安全。你想用一个高性能但没法验证的复杂控制器(可能是学出来的、可能有 bug),又必须保证不出事。 Lui Sha 的 Simplex 架构IEEE Software 2001)给出三件套:高性能复杂控制器、简单且形式验证过的安全控制器、以及一个决策模块

决策模块的核心是一个可恢复集 $\mathcal R$——从这些状态出发,安全控制器仍能把系统永远保持在约束内。它常取安全控制器稳定域的一个 Lyapunov 子水平集

$$ \mathcal{R} = \{\,x : x^\top P x \le c\,\} $$

前向切换(复杂→安全)用 §7 的安全触发:一步前瞻预测复杂控制器这一步会把状态带到 $x^{+}$,若 $x^{+}$ 要离开 $\mathcal R$ 就夺权刹车。 真正微妙、也最少被讲的是反向切换(安全→复杂):什么时候敢把控制权还回去?不能一回到 $\mathcal R$ 就还——那会在边界上反复横跳(正是 §12 的抖动)。 得等状态深入到一个更靠里的内区 $\mathcal R_{\text{in}}$、并保持一段驻留时间,才交还:

$$ u = \begin{cases} u_{\text{complex}}, & x^{+}(u_{\text{complex}}) \in \mathcal{R} \\ u_{\text{safe}}, & \text{otherwise} \end{cases} \qquad \text{hand back iff}\ \ x \in \mathcal{R}_{\text{in}} \subset \mathcal{R}\ \text{and dwell} > \tau_d $$
# Simplex 决策模块:前向夺权 + 带迟滞/驻留的反向交还
def simplex_decision(x, u_complex, model, dt, S):
    x_next = model.predict(x, u_complex, dt)        # 一步前瞻
    if S["mode"] == "complex":
        if not in_recoverable(x_next, margin=0.0):  # 要出可恢复集
            S["mode"], S["dwell"] = "safe", 0.0     # 前向:夺权
    else:  # safe
        if in_recoverable(x, margin=BIG):           # 深入内区 R_in(更大裕度)
            S["dwell"] += dt
            if S["dwell"] > TAU_D:                   # 且保持足够久
                S["mode"] = "complex"                # 反向:才敢交还
        else:
            S["dwell"] = 0.0
    return u_complex if S["mode"] == "complex" else safety_controller(x)
Demo 5 · Simplex 前向 + 反向切换
小车在「home」和墙边「目标」之间往返。下方状态空间 $(x,v)$:青色阴影是可恢复集,橙虚线是其边界 $\mathcal R$,青虚线是更靠里的交还内区 $\mathcal R_{\text{in}}$。 蓝=性能控制器、黄=安全控制器。冲向墙时一碰橙边界前向切到刹车;退回内区并保持一会儿才反向交还。无论激进度多大,右上角 SAFE 灯永远绿

§16失效切换:看门狗 → 安全态、降级链

最后一种交接是出错时的。Simplex 假设状态可测、模型可信;但传感器会坏、回路会错过 deadline、算力会爆。工程上的标准做法是一条降级链(graceful degradation)

  • 看门狗(watchdog):一个独立计时器,控制回路每个周期「喂」它一次;一旦回路卡死、错过 deadline,看门狗超时触发,立刻切到安全态(保持位姿、刹车、急停)。这是「实时回路」与「安全」之间的硬连接,也是 §5 事件触发的一种。
  • 分级控制律降级:空客电传飞控就是教科书例子——传感器/计算逐步失效时,控制律从 Normal Law(带包络保护)降到 AlternateDirect、最后 Mechanical Backup,每一级都比上一级简单、保护更少、但更可信。
  • 安全模式(safe mode):航天器的终极兜底——一旦姿态/健康异常,自主切到「太阳指向安全模式」,只保证活着(对日充电、稳住姿态、等地面指令),放弃任务。它就是 Simplex 安全控制器在系统级的化身。

§17腿足机器人:gait FSM → 凸 MPC → WBC

把前面所有概念拼起来,看一个真实、开源、跑得很好的机器人控制栈:MIT Cheetah 的腿足控制 (Di Carlo 2018 凸 MPC + Kim 2019 WBIC)。 腿足是个欠驱动的混杂系统:只能通过触地的脚对世界施力,而哪些脚触地是离散变化的。先按本文的词汇点明它的关系:

层间接口参考是什么(§2)耦合方式(§3)触发(§4–7)
步态调度器 → MPC接触调度 $c_i(k)$ + 期望体速设参考 + 选模式(stance/swing)时间 + 接触事件
MPC → WBC期望地面力 $f^*$ + 期望体姿设参考(目标/代价,值触发)时间(~30–50 Hz 刷新)
WBC → 关节期望力矩 $\tau_{\text{ff}}$ + $q_{\text{des}}$设参考时间(~1 kHz)
腿足控制栈 · 越下越快,接触调度是贯穿的契约
步态调度器 / FSM出"接触调度" + 期望体速 · 相位运算(随 1kHz tick) 凸 MPC(单刚体模型)解地面反作用力 · 视野~10步/0.5s · ~30–50 Hz WBC / WBIC(全身 QP)调和 MPC 力 + 摆动腿跟踪 · ~1 kHz 关节 / 电机力矩τ = τ_ff + Kp(q_des−q) + Kd(q̇_des−q̇) · 数十 kHz 接触调度 c_i(k) 状态估计 ↑ 每层往下传"参考/期望力",往上传"状态/接触估计"——接触调度是上下都认的同一份契约

为什么这样分层:MPC 解一个带摩擦锥约束的凸 QP,几毫秒一次,只能跑到几十 Hz、且用粗糙的单刚体模型——它的价值是预view(未来 0.5 秒力该怎么分配); WBC 没有预view、但在完整全身模型上 1 kHz 跟踪——它的价值是realize(把那些力在真实动力学上实现、跟踪摆动脚)。二者按频率拆开、各取所长。MPC 本身就是一个值触发器(§6),大致长这样(接触调度通过「摆动脚出力=0」这个约束进入优化):

$$ \min_{x,\,f}\ \sum_{k=0}^{N-1} \lVert x_{k+1}-x_{k+1}^{\text{ref}}\rVert_{Q}^{2} + \lVert f_k\rVert_{R}^{2} \quad\text{s.t.}\ \ x_{k+1}=A_k x_k + B_k f_k,\ \ |f_{i,xy}|\le \mu f_{i,z},\ \ f_i = 0\ \text{if } c_i=0 $$

步态调度器给每条腿一个相位 $\varphi_i$,决定它 stance(触地)还是 swing(摆动)——这是一个纯事件触发的接触切换:

$$ \varphi_i(t) = \big(t/T + \theta_i\big) \bmod 1, \qquad c_i = \mathbb{1}\big[\varphi_i < d\big] $$

不同步态只是不同的相位偏移 $\theta_i$ 和占空比 $d$。摆动脚落哪用 Raibert 落脚点启发式(落点 = 髋投影 + 半个支撑期的位移 + 速度反馈修正):

$$ p_{\text{foot}} = p_{\text{hip}} + \frac{T_{\text{stance}}}{2}\,v + k\,(v - v_{\text{des}}) $$
为什么纯按时间切接触是错的

纯时间表假设地面正好在预测位置——它从来不在。脚提前触地(上坡、碎石),还当它在摆动就往地里砸;脚延迟触地(下坡、缺口),机器人以为这条腿在支撑、开始加载,结果腿下是空的。 所以时间触发必须叠加事件触发:从动力学反推地面力 $\hat f$ 过阈值、或接触传感器、或概率接触估计。提前触地就把腿立刻锁为 stance(相位前跳),延迟就让它保持「探地」的 guarded 摆动直到真的碰到。

# 步态调度器:相位(时间触发) + 接触检测(事件触发) 共同决定接触状态
def gait_scheduler(legs, t, T, duty, offset, grf_est, dt):
    for i in legs:
        phase = (t / T + offset[i]) % 1.0
        scheduled = "STANCE" if phase < duty[i] else "SWING"
        detected  = grf_est[i] > F_THRESH                  # 事件:从动力学反推接触
        if legs[i] == "SWING" and (scheduled == "STANCE" or
                                   (detected and phase > EARLY_OK)):
            legs[i] = "STANCE"                             # 提前/正常触地 -> 锁支撑
        elif legs[i] == "STANCE" and scheduled == "SWING":
            legs[i] = "SWING"                              # 抬腿
        elif legs[i] == "STANCE" and not detected:
            pass                                           # 延迟触地:保持探地,别发力
    return [1 if legs[i] == "STANCE" else 0 for i in legs] # 接触表喂给 MPC

swing→stance 那一刻怎么不冲击(§11 的精神用在这里)?落脚点选得让脚落地时相对速度≈0;MPC 里这条腿的力约束从「=0」放开成摩擦锥,于是力从 0 渐增而非跳变; WBC 把摆动跟踪任务的优先级降下来、把力任务提上去。靠「零速触地 + 零起步力 + 滚动重解」把暂态压住。

Demo 6 · 步态调度器(接触切换)
四条腿(FR/FL/RR/RL)按相位在 stance(实心条/触地)和 swing(空心条/摆动)之间切换,右侧是滚动的接触调度。 点「换步态」在 trot / walk / bound / pace 间切换——它们只是不同的相位偏移和占空比。左侧俯视图里实心脚=当前触地。

§18飞控 GNC:分层切换的工业标杆

航空飞控把本文几乎每个概念都用上了。它的层叫 制导-导航-控制(Guidance-Navigation-Control)制导(外环,慢,1–10 Hz:由任务目标生成期望轨迹/姿态,这是给下层的参考)→ 导航(状态估计)→ 控制(内环,快,50–1000 Hz:跟踪制导给的参考)。和级联控制同一个套路——快内环 + 慢外环,且 $\omega_{\text{in}} \gtrsim 5\,\omega_{\text{out}}$。

  • 自驾仪模式切换(高度保持、航向保持、进近、复飞)是 §3 的「选模式」+ §5 的事件触发(飞行员/FMS 触发);切模式时用同步(synchronization)把积分器初始化好让舵面不跳,正是 §11:$I(t_0) = (u^- - K_p e)/K_i$。
  • 增益调度跨整个飞行包线(按动压/马赫/高度),正是 §13。
  • 包络保护是 §15 的系统级 Simplex——飞行员/自驾仪即将超限时抢占。空客 Normal→Alternate→Direct 律降级则是 §16。
  • 一个血的教训是模式混淆:飞行员不知道自驾仪此刻在哪个模式,是已知的人因事故根源——所以才有醒目的飞行模式通报(FMA)。交接不仅要对机器无扰,还要对人「可见」。

§19航天器自主:Remote Agent (DS1) 的三层与故障恢复

1999 年 NASA 在 Deep Space 1 上飞的 Remote Agent,是史上第一次让 AI 分层自主真的在轨控制一艘飞船 (Muscettola/Nayak/Pell/Williams, AIJ 1998)。它是一个三层架构,把「故障恢复」做成了核心:

角色它给下层的参考 / 它的触发
PS 规划/调度器从目标生成一段时间内有弹性的计划参考 = 计划(一串带时窗的目标)
EXEC 智能执行器(ESL)执行计划、分解任务、管资源、监视、重试选模式 + 事件触发(监视到失败)
MIR(Livingstone)模式识别与恢复从传感器推断部件健康/模式,建议恢复基于模型诊断 → 触发重构/重规划

故障恢复怎么交接:MIR 用一个部件模型做基于模型的诊断——发现异常就推断「哪个部件处于什么模式」,并建议重构;EXEC 据此切到恢复程序,或在无法本地处理时请求 PS 重新规划。 这是「事件触发的模式切换」在自主系统级的体现:诊断 → 重构 → 必要时重规划。最终兜底是飞船的太阳指向安全模式(§16)。 Remote Agent 在轨还暴露了一个著名的并发死锁 bug(两个线程的竞态,地面诊断后上传补丁修复)——一个关于「分层执行器里并发有多难」的真实教训(细节据飞行后复盘,引用前建议核对原始报告)。

§20总结,与还没解决的

一句话把全篇收起来

层与层不是轮流、不互相 kill,而是并发:下层一直在算执行器命令,上层一直在给下层设参考(一个随层变得越来越具体的目标值)。 上层影响下层有四种方式——设参考 / 设约束 / 选模式 / 抢占,只有抢占是真夺权;每一次交接由四类触发决定——时间(到点了)、 事件(条件成真)、(打分取最优,cost 最低的赢)、安全(即将越界)。决定了「该切」之后,再用无扰切换、迟滞+驻留、增益调度、可恢复集+反向交还把那一瞬间做干净。 腿足、飞控、航天器三个真实栈,讲的都是同一件事。

  • 层界怎么画:RCS/3T 给了按时间尺度/状态切的判据,但一个新机器人该切几层、界画在哪,仍靠工程经验。
  • 跨层端到端的保证:单点有无扰切换、单模式有平均驻留时间定理,但「穿过五层、从传感到电机」的端到端时序与稳定性,缺统一形式化。
  • 学习式控制器的可验证交接:把复杂控制器换成学出来的策略很有前景,但它进不了 Simplex 的「可恢复集证明」——这正是它暂时只能当复杂控制器、底下必须垫经典安全控制器的原因。
  • 语义级安全约束:CBF/包络保护守得住几何与运动学约束,但「先救人再灭火」这类语义规约怎么进安全层,还没有干净答案。

§A名词表

术语解释
参考 (reference)相邻两层的接口变量,下层的期望值;类型随层变(符号→位姿→关节→电流)。
四种耦合上层影响下层的方式:设参考 / 设约束 / 选模式 / 抢占(只有抢占是夺权)。
四种触发何时交接:时间(周期) / 事件(卫式条件) / 值(打分 argmin) / 安全(出可恢复集)。
时间触发纯时钟驱动;cyclic executive / TTA / RMS / ros2_control 的固定速率回路。
事件触发谓词变真才动;Tabuada 事件触发控制 ‖e‖≥σ‖x‖;FSM/BT guard。
值触发 (cost)每周期给候选打分取最优 argmin Σwᵢcᵢ;DWA/MPPI/MPC/Frenet/加权QP/抓取打分…。
安全触发即将越界才接管;Simplex(x⁺∉R)/CBF(h→0)/HJ 可达/RTA。
零阶保持 (ZOH)参考在上层两次刷新之间保持不变,下层持续用它。
级联 / 嵌套控制 (cascade)外环算参考、内环跟参考;内环比外环快约 5–10 倍。
DWA / DWB对动态窗口里的 (v,ω) 轨迹按加权 cost 取最低;ROS 经典 32/24/0.01。
MPPI采样 K 条控制序列按 exp(−cost/λ) 软加权(软 argmin)。
Subsumption / 3T / RCS抢占耦合 / 选模式(executive) / 设参考逐层分解。
无扰切换 (bumpless)切控制器时用回算让待命控制器跟踪实际命令,命令连续不跳。
迟滞 / 驻留时间升降用不同阈值、切后强制最小停留;防抖动;平均驻留时间定理保证稳定。
增益调度在多个工作点设计线性控制器并按慢变调度变量插值(连续切换)。
Simplex / 可恢复集复杂+安全控制器+决策模块;近边界前向夺权、深入内区+驻留才反向交还。
看门狗 / 安全模式回路错过 deadline 即超时切安全态;航天器终极兜底只保证活着。
凸 MPC + WBC腿足栈:慢 MPC 预view 出力 + 快 WBC 全身实现。
GNC / Remote Agent制导-导航-控制;DS1 飞船三层自主 PS/EXEC/MIR。

Made for learning. 勘误、补遗欢迎 issue。最后更新 2026-05-27。

内容基于公开论文 / 技术报告 / 官方代码与文档。交互演示是 Canvas2D 教学玩具,参数不代表真实系统性能。 个别史料(如 DS1 死锁细节)据飞行后复盘,逐字引用前请核对原始报告。