想象你跟一位武术师父学一套“推山掌”。师父完整打了一遍,你拿手机录了视频。可光有视频没用——换一个场地,对手站位不同,或者出掌目标从左边变成右边,那串“像素级”的录像就僵住了。真正的功夫,不是背下每一帧画格,而是记住一套“心法”:怎么起势、怎么发力、怎么收住。DMP(Dynamic Movement Primitives,动态运动基元)就是给机器人写的“心法口诀”。它不存轨迹本身,而是把动作拆成三个合作伙伴:一个从1衰减到0的“节拍器”,一个保证最后精准到目标的“弹簧振子”,以及一堆记录师父个人习惯的“发力小铃铛”。只要有心法在手,哪怕目标换了、速度变了、场地歪了,机器人照样能打出一套形不似而神似的好拳。
掌握 DMP,等于掌握机器人学习动作的“通用语言”:既能从示教中提取特征,又能一键泛化到新任务,是模仿学习、机器人技能复用、人机协作方向的核心基本功。
可视化演示
可视化展示DMP轨迹生成、吸引子动力学收敛与示教学习泛化过程,交互调节参数体验动态运动基元的运动生成能力。
本节配有交互动效(进入视口后自动加载,约 3KB)。
DMP轨迹生成过程 动态系统从起点平滑收敛到目标点生成轨迹 强迫函数塑造轨迹形状实现灵活运动 dζ/dt = α_z(β(g-y)-ζ) 二阶动态系统生成平滑轨迹
DMP 灵魂心法
录像的问题是:它把时间钉死在每一帧上。三秒打完的拳,换成一秒,人的动作就显得完全不对;靶子从左边移到右边,录像就彻底失效。DMP 的理念是:不要记录“每一帧长什么样”,而是记录“每一步怎么去”。师父打拳时,身体里有三种力量在协作:一个内部节拍器告诉你现在到哪一步;一组肌肉弹簧让拳头无论如何都能收在目标上;还有一堆源自习惯的小动作,让拳风带弧、有韵味。
DMP 把这三者写进一个二阶微分方程,让轨迹不再是一串离散点,而是一个由动力学决定的连续函数。机器人在执行时,每一次控制循环都根据当前状态计算下一步该往哪走,而不是照着查表找点。这样做的好处是:即使中途目标变了、速度变了、甚至受到意外扰动,机器人也不会崩掉,因为物理规律会把它重新拉回来。
核心方程长这样:
录像会过时,心法不会。DMP 把师父的招式变成一套“规则”,机器人拿到规则后,无论舞台怎么变,都能临场发挥。这也是为什么模仿学习领域把 DMP 当作动作编码的“母语”。
- 时间尺度
\tau决定整段动作被压缩还是拉伸,相当于把同一套拳用二倍速或慢放播放,而动作结构不变。 - 位置误差的牵引力
\beta_q (g - q)是弹簧力,状态离目标越远,牵引越大,保证最终能到达目标。 - 速度阻尼
- \tau \dot{q}是阻尼项,抑制震荡,避免控制系统越拉越跑方向,最后顺畅停在目标点。 - 风格塑造力
f(s)是强迫项,由相位s驱动,负责在基础弹簧轨迹上叠加师父特有的动作弧线,是 DMP 的灵魂。
双时钟建模
师父常说:“先听锣鼓,再动身子。”DMP 也有两套时钟接力工作。第一套是“节拍器”,叫规范系统(canonical system),它唯一的工作是输出一个从 1 均匀衰减到 0 的相位变量 s。第二套是“身体系统”,叫变换系统(transformation system),它接收节拍,驱动关节位置 q 和速度 z 的变化。两个系统分工明确:一个负责“现在打到第几段”,一个负责“这一下到底怎么发”。
规范系统的数学特别简单,是一个一阶衰减:
变换系统则由两个方程组成,一个是速度变量 z 的演化,另一个是位置随时间的更新:
这就像音乐会里,定音鼓手只负责数拍子,弦乐手负责奏旋律,两者各司其职又严格同步。规范系统让 DMP 天然拥有时间上的“时标不变性”,不管多快多慢,动作的节奏感都保持连贯。
- 相位节拍器
s从 1 出发指数衰减到 0,衰减速度由\alpha_s控制,相当于演出倒计时,时间越往后越接近“尾声”。 - 中间速度变量
z是广义速度,先把弹簧和强迫项算成力的效果,再通过积分变成位置变化,让运动轨迹平滑连续。 - 弹簧-阻尼模型
\alpha_q ( \beta_q (g - q) - z )把“当前位置与目标之差”转成加速度,相当于弹簧加阻尼,保证不震荡、不飞出去。 - 两个时钟的耦合变换系统里出现的
s强迫项,让身体动作严格跟着节拍走,节拍走到零,动作也刚好完成。
强迫项基函数
师父的“小习惯”不是一条平滑曲线那么简单,它是由许多个局部发力点组成的。比如出掌中途会有一个短暂的抖腕,收势前会有一次轻轻的回带——这些细节各自发生在特定的时间节奏上。DMP 用高斯基函数来模拟这些“局部发力点”:每个基函数像一个小铃铛,只在它对应的相位附近响,别的时候不打扰。
所有铃铛的加权和,再除以铃铛声叠加的总响度,就形成了强迫项:
这套设计很妙:师父的习惯只在特定的节拍点上“借力”,等动作接近尾声,所有铃铛自动哑火,剩下的弹簧系统稳稳地把手收到目标处。所以 DMP 生成的轨迹结束后不会乱颤。
w_i 和“位置” c_i;所有铃铛同时响,谁在节拍点上谁的声音大,最后合成一句完整的“风格唱腔”。- 高斯局部响应
\psi_i(s)只在中心c_i附近有明显值,离中心越远越趋近于 0,对应“这个动作私藏在小节拍片段”。 - 加权与归一化分子把所有基函数按权重叠加,分母做归一化,让输出不受基函数数量影响,变成平滑的加权平均。
- 相位收缩因子外层的
s保证强迫项随动作收尾趋近于 0,否则拳到最后还在“抖腕”,会停不稳。 - 基函数形状参数
h_i控制铃铛的宽窄,c_i控制铃铛出现的相位时刻;一宽一窄,动作的“笔画”就有粗有细。
LWR 参数学习
想学会师父的发力点,最快的方法就是看师父完整打一遍,然后记住每个节拍点应该出多少力。机器人的“看”是采集演示轨迹:位置、速度、加速度随时间的变化。接下来要做的是“反推”出强迫项在每一个相位时刻应该长什么样,再反解出每个基函数的权重。
这个反解用的是局部加权回归(LWR),它让每个基函数只负责自己附近的相位数据,互不干扰,每个权重都有闭式解:
LWR 的计算非常快,不需要梯度下降、不需要神经网络,一次矩阵运算就能学完。这也让 DMP 特别适合“一次示教、立刻部署”的工业场景,老师傅演示一遍,机械臂马上学会。
- 目标强迫项从演示数据反推需要的总发力,公式是
\tau^2 \ddot{q}_{demo} - \alpha_q ( \beta_q (g - q_{demo}) - \tau \dot{q}_{demo})。 - 局部加权分子
\psi_i(s_t) f_{\text{target}}(s_t)表示只在第i个铃铛响的时候采集发力数据,别处的声音不干扰这个铃铛的音量。 - 归一化分母除以所有响度之和,是把采样密度带来的偏差消掉,让每个采样时刻的贡献被均衡化。
- 权重意义
w_i就是第i个基函数的发力大小,学完后整段演示的“神韵”就被压缩进了这几个权重里。
目标泛化能力
掌握心法后,师父让你换个方向出掌,靶子从左边移到右边。你不需要重新学一遍拳,只需要把“目标”从 g 改成 g_{\text{new}},弹簧系统自然会把轨迹引向新目标,而师父那些发力小铃铛一个都不用改。这正是 DMP 被称为“运动基元”的原因:形状与目标解耦,目标变了,神韵还在。
新目标下,变换系统变成:
这种能力是“硬编码轨迹”永远给不了的:换目标必须重新录演示,而 DMP 一条基元就能覆盖一条轨迹带。机械臂分拣、避障绕行、人机协作中的动态调整,全靠这个特性。
- 目标参数替换原本方程里的
g换成g_{\text{new}},强迫项权重不变,所以动作风格保持不变。 - 目标牵引再分配弹簧力方向的改变会把整个积分轨迹的“流场”重新拉伸,机器人自动走出一条绕到新目标的新曲线。
- 时间缩放与形状解耦改变
\tau只影响动作快慢,不影响目标牵引与强迫项的相对关系,因此节奏变了,动作形状骨架不变。 - 收敛性质保持当
q接近g_{\text{new}},弹簧力趋近于 0,强迫项也因s \to 0而消失,系统天然收敛到新目标。
节律与避障
单招练完了,接着练连续套路。离散 DMP 的节拍器从 1 衰减到 0,只适合“打一拳、停一下”。但走路、游泳、划桨是无限循环动作,需要把节拍器改成循环报数的闹钟,让相位永远在 0 和 1 之间打转,这就是节律 DMP。此外,真正上场时总会有障碍物,DMP 可以在变换系统上叠加一个“势场排斥项”,让轨迹临时绕开障碍,再回到原计划。
节律 DMP 与避障调制的组合可以写成:
这一扩展让 DMP 从“单招生成器”升级为“套招引擎”。多段 DMP 还可以像积木一样拼接,先做一次下蹲,再做一个出拳,中间用接力相位串联,构成了机器人的完整行为库。
o 时,一个“看不见的斥力”把轨迹推开,绕过障碍后再回归正轨。- 周期相位
\tau \dot{s} = 1表示相位匀速前进,配合周期基函数就能生成循环往复的节律运动。 - 周期参考轨迹
m是周期运动的中线或参考点,通常是振荡中心,弹簧项围绕它来回收缩。 - 节律强迫项
f_{\text{rhythm}}(s)用周期化基函数构建,同一个相位点循环出现时产生完全相同的发力。 - 斥力调制
- \kappa (q - o) / \|q - o\|^3在离障碍越近时越强,把轨迹弹开;\kappa决定多“怕”障碍。
工程落地调参
再好的心法也需要排练。实际部署 DMP 时,最常遇到的问题不是“学不会”,而是“学过了头”或者“表达不出来”。基函数太少,师父的细节表现不出来;基函数太多,演示时手臂抖动的噪声也被当成风格学进去。所以工程调参是一门平衡术。
评估 DMP 复现好坏,最简单实用的指标是跟踪误差:
工程上还有一个经验值:基函数数量通常选 10~20 个,带宽与相邻基函数中心距接近。先快速演示,看 RMSE 是否达标,再观察轨迹抖动情况,最后在真实设备上做安全限幅。调好一次,这套心法就能复制到无数台机器上。
- 逐帧误差
q_t - q_t^*是同一时刻机器人位置与演示位置的差值,对所有时刻求平方和。 - 平均化除以总时长
T,防止采样多寡影响误差量级,得到“平均帧偏差”。 - 开方还原开根号让单位回到长度量纲,方便和实际位置误差做直观比较,类似“平均偏离几厘米”。
- 调参导向RMSE 太大时优先检查基函数数量与带宽;RMSE 很小时,再结合轨迹平滑度判断是否过拟合。
AI 应用场景
工业分拣机械臂轨迹复用
阶段:模仿学习 / 示教编程
解决的问题:传统示教器编程需要工程师逐点手动移动机械臂,遇到传送带物品位置变化就得重新示教。DMP 允许工人直接拉着机械臂演示一次抓取动作,算法把动作学成运动基元,之后目标位置变化时只要改一个目标参数,轨迹自动泛化。
具体案例:某汽车零部件产线上,机械臂需要从托盘抓取小零件放上装配线。工程师带动机械臂演示三种摆放角度的抓取轨迹,DMP 学习后,摄像机实时识别零件偏移量,对应设置 g_new,机械臂即使面对数十种倾斜角度,也能保持同样稳定的抓取姿态。
双足机器人的节律步行控制
阶段:运动控制 / 技能生成
解决的问题:双足机器人需要无休止地重复迈步,离散 DMP 无法胜任。节律 DMP 把相位改成周期性循环,学习人类步态的摇摆曲线,再叠加避障调制项,机器人就能在不平地面或遇到障碍时自然调整步幅。
具体案例:某高校实验室采集了人在跑步机上行走的运动数据,用节律 DMP 编码髋关节和膝关节的角度振荡。当机器人准备踩上低矮台阶时,新增的斥力调制让膝盖多抬 3 厘米,越障后步态平滑恢复,体感几乎与人无异。
康复外骨骼的柔顺辅助
阶段:人机交互 / 柔顺控制
解决的问题:康复训练需要机器人既懂标准动作,又懂得照顾患者差异。DMP 与柔顺控制结合,从治疗师演示中学习理想运动轨迹,同时根据患者的发力反馈实时微调目标点,让机器在人机协作中既不生硬也不伤到人。
具体案例:上肢康复外骨骼中,治疗师带着患者手臂缓慢画一次喝水动作,DMP 完成学习。患者在机器人助力下重复练习时,传感器检测到手臂力量不足,目标点自动向身体内侧偏移,始终保证动作在安全范围内完成。
Python 演示
import numpy as np class DiscreteDMP: """极简一维 DMP:学习一段演示轨迹,并泛化到新目标。""" def __init__(self, n_basis=10, alpha_s=25.0, alpha_q=25.0, beta_q=6.25): self.n = n_basis self.a_s = alpha_s self.a_q = alpha_q self.b_q = beta_q self.centers = np.linspace(0.0, 1.0, n_basis) self.widths = np.full(n_basis, 10.0) self.w = np.zeros(n_basis) self.q0 = 0.0 def _basis(self, s): return np.exp(-self.widths * (s - self.centers) ** 2) def _force(self, s): psi = self._basis(s) return (np.dot(psi, self.w) / (np.sum(psi) + 1e-10)) * s def learn(self, q_demo, dt): T = len(q_demo) tau = T * dt qd = np.gradient(q_demo, dt) qdd = np.gradient(qd, dt) t = np.arange(T) * dt / tau s = np.exp(-self.a_s * t) g0 = q_demo[-1] f_target = tau * tau * qdd - self.a_q * (self.b_q * (g0 - q_demo) - tau * qd) psi = self._basis(s) self.w = (psi.T @ f_target) / (np.sum(psi, axis=0) + 1e-10) self.q0 = q_demo[0] def plan(self, g_new, tau=1.0, dt=0.01, T=200): s = 1.0 z = 0.0 q = self.q0 qs = [q] for _ in range(T): dz = (self.a_q * (self.b_q * (g_new - q) - z) + self._force(s)) / tau * dt dq = z / tau * dt z += dz q += dq s += -self.a_s * s / tau * dt qs.append(q) return np.array(qs) def main(): dt = 0.01 t = np.arange(0, 1.0, dt) demo = 0.3 + 0.5 * np.tanh((t - 0.5) / 0.1) dmp = DiscreteDMP() dmp.learn(demo, dt) traj_new = dmp.plan(g_new=1.5, dt=dt) print("新目标轨迹起始点:", traj_new[0]) print("新目标轨迹结束点:", traj_new[-1]) if __name__ == "__main__": main()
self.w = np.zeros(n_basis)初始化基函数权重,相当于把所有“发力小铃铛”先设为静音。t = np.arange(T) * dt / tau构造归一化时间轴,把真实时间换成内部节拍,保证学习不受演示时长影响。f_target = tau * tau * qdd - self.a_q * (...)从演示数据反推目标强迫项,挖出师父发力细节。psi = self._basis(s)计算每个高斯基函数在相位点上的响应,让每个铃铛只认自己的节拍。self.w = (psi.T @ f_target) / (np.sum(psi, axis=0) + 1e-10)局部加权回归闭式解,一步算完所有权重。self.q0 = q_demo[0]记录起点,推理时从演示起点出发,保持动作的起手式。dz = (self.a_q * (self.b_q * (g_new - q) - z) + self._force(s)) / tau * dt对变换系统做欧拉积分,弹簧与强迫项共同决定下一时刻速度。s += -self.a_s * s / tau * dt相位按一阶系统衰减到 0,节拍器走完,动作自然收住。
常见误区
误区DMP 本质上是把演示轨迹用函数曲线拟合出来,那我直接用多项式或者样条拟合不就成了?何必整一套微分方程?
正解DMP 不是对轨迹的静态拟合,而是一个闭环动力学系统。它把“轨迹形状”编码在强迫项里,把“任务目标”编码在弹簧项里。遇到临时扰动或中途改目标,DMP 能在线重新规划,而多项式拟合只能输出一条固定曲线,改一个点就要重新整条拟合。
为何常见:因为大家刚接触时最容易先画轨迹图,看到曲线相似就误以为是在做函数逼近,忽略了背后的控制闭环属性。
误区我多放一些高斯基函数,总能更精确地逼近演示轨迹吧?设它个 50 个,总比 10 个强。
正解基函数增加会带来两个问题:过拟合演示中的传感器噪声;相邻基函数重叠加剧,导致权重解不稳定,轨迹出现高频抖动。通常 10~20 个基函数就足够描述常见动作,真正该调的是中心分布和宽度,而不是拼命堆数量。
为何常见:受神经网络“参数越多拟合越强”的思维影响,容易忽略 DMP 是低维运动基元,追求稀疏表达才是它的设计初衷。
误区机器人要换个方向抓东西,应该重新让师傅演示一遍,或者把原轨迹坐标平移。
正解DMP 的最大威力是“形状”与“目标”解耦。学习阶段只需一次演示,把风格压缩进权重;推理阶段更换 g 值即可,强迫项和基函数权重完全不用变。简单平移在二维场景看似可行,一旦目标远近、高度同时变化,平移就会破坏动作结构,DMP 则能保持完整动力学特性。
为何常见:接触示教轨迹时,头脑中自然把动作看成坐标系里的几何形状,忽略了 DMP 是面向任务的动力学模型。
误区DMP 是从起点走到目标点然后停下来,像走路这种循环动作应该用别的算法。
正解离散 DMP 确实面向点到点运动,但同样框架下改造规范系统为周期相位,就得到节律 DMP,可以稳定生成行走、游泳、划桨等循环运动。它的基函数和权重学习逻辑完全一致,只是相位 s 不再衰减到 0,而是周期性回到初始值。
为何常见:大多数 DMP 教程用“抓取”“挥臂”举例,确实都是单次动作,容易忽略扩展类型。
面试题
Q1. DMP 中规范系统的核心作用是什么?
- A.提供随时间衰减的相位信号← 正确答案
- B.直接计算关节力矩输出
- C.代替传感器采集数据
- D.保存整套演示轨迹
答案:A。规范系统输出相位变量 s,从 1 衰减到 0,负责给变换系统“打节拍”。它不是控制器、不是传感器、更不存储轨迹,所以只能选 A。
Q2. 变换系统在结构上最接近以下哪种物理模型?
- A.电磁铁吸合模型
- B.弹簧-阻尼质量系统← 正确答案
- C.流体湍流模型
- D.刚体碰撞模型
答案:B。变换系统中的 \beta_q (g - q) 是弹簧力,- \tau \dot{q} 是阻尼力,两者叠加产生平滑收敛行为,本质就是弹簧-阻尼质量系统。其他模型不包含目标牵引和振动抑制。
Q3. 强迫项 f(s) 外层乘一个相位变量 s,主要目的是什么?
- A.加快收敛速度
- B.增加基函数数量
- C.保证末端力消失← 正确答案
- D.让目标点移动
答案:C。当相位 s 趋近于 0 时,强迫项整体归零,确保动作收尾时由纯弹簧-阻尼系统完成稳定收敛,不会在目标点附近出现残抖。它与速度、基函数数量、目标位置均无关。
Q4. 学习 DMP 基函数权重时最常用的是哪种方法?
- A.遗传算法
- B.反向传播
- C.蒙特卡洛采样
- D.局部加权回归← 正确答案
答案:D。DMP 的权重对每个基函数来说是闭式解,用局部加权回归(LWR)按高斯响应加权平均即可,速度极快。深度学习式反向传播不是必需,遗传和采样更是低效。
Q5. 时间缩放参数 \tau 增大后,动作会发生什么变化?
- A.动作整体变慢← 正确答案
- B.动作整体变快
- C.动作目标点改变
- D.动作形状完全改变
答案:A。\tau 把真实的物理时间和相位节拍做尺度换算,\tau 越大,同样的相位变化对应延续更长时间,所以动作变慢。形状和目标由权重和 g 决定,不受 \tau 影响。
Q6. 换一个距离原目标很远的新目标时,DMP 依然能泛化,根本原因是什么?
- A.物理系统必须立即重建
- B.变换系统含目标吸引子项← 正确答案
- C.重新初始化所有权重
- D.相位衰减速度自动改变
答案:B。变换系统里 \beta_q (g - q) 是随着当前位置与目标之差动态变化的目标吸引子。只要目标项存在,任何位置都会被平滑拉向新目标,不需要重学权重或改变相位。
Q7. 下列哪些属于 DMP 的泛化能力表现?
- A.目标点从左边改到右边← 正确答案
- B.动作整体变快或变慢← 正确答案
- C.重新采集一组演示数据
- D.起始点不变但轨迹尺度变化← 正确答案
答案:ABD。DMP 允许换目标、调速度、改尺度,都只需修改 g、\tau 和初始条件,强迫项权重不变。重新采集演示数据属于重新学习,不是泛化。
Q8. 节律 DMP 与离散 DMP 最本质的区别是什么?
- A.节律 DMP 不需要微分方程
- B.节律 DMP 相位周期循环不衰减← 正确答案
- C.节律 DMP 不能生成圆弧轨迹
- D.节律 DMP 只能用于双足机器人
答案:B。离散 DMP 相位 s 单调衰减到 0,节律 DMP 则让相位循环往返,从而产生周期性运动。两者同样用微分方程和基函数,也适合各种机器人平台。
Q9. 在 DMP 中引入避障调制项的常见做法有哪些?
- A.在加速度上叠加势场排斥项← 正确答案
- B.把相位信号强制归零
- C.将障碍位置引入变换系统← 正确答案
- D.删除全部强迫项权重
答案:AC。避障调制通常在变换系统右侧加速度方程中叠加斥力势场项,障碍物位置 o 作为一个新输入进入系统动力学。归零相位和删除权重都会破坏原始动作,不可取。
Q10. 基函数宽度过大时,对 DMP 学习效果最直接的影响是什么?
- A.局部细节被过度平均← 正确答案
- B.收敛速度反而变快
- C.权重学习变成确定性
- D.目标点自动偏移
答案:A。宽度过大导致每个基函数影响范围过宽,多个铃铛同时含糊地响应一个节拍,局部发力细节被互相平均掉,轨迹显得“绵软无力”。收敛速度、确定性和目标点都不会因此改变。