本章目录

MuJoCo 的计算流程与状态更新

前面几章已经建立了姿态更新、空间惯量、雅可比和树形动力学。给定配置与速度,可以求出质量矩阵和偏置力;给定外力,可以进一步求出加速度。

这一章把这些计算连成一次仿真。先在当前状态上建立几何、速度和力,再求加速度,最后由积分器推进状态。看清每一步读写哪些数据,便能理解 MuJoCo 的主要调用顺序。

数学约定

配置记为 q,广义速度为 v,广义加速度为 a=\dot v。质量矩阵为 M,偏置力为 c。本章手算模型采用两个直线移动自由度,长度、速度、力分别使用米、米每秒和牛顿。源码依据本地 MuJoCo 3.10.0。

01

模型、状态与派生量

给定某一时刻的关节角和速度,程序还要计算每个刚体的位置、惯量以及接触关系,才能得到加速度。首先分清哪些量描述当前状态、哪些量由状态推得,后面的计算顺序才有明确的输入与输出。

一个长方体的质量、形状和连接方式决定了它采用怎样的动力学方程;它当前位于哪里、运动多快,则决定这一次求值的结果。MuJoCo 用 mjModel 保存编译后的模型参数,用 mjData 保存一份运行实例。同一个模型可以配合多份数据,分别从不同状态出发。

mjData 中既有随时间积分的状态,也有控制输入和已经算出的中间量。三类数据会在同一数组集合中出现,使用时按它们的计算来源区分:

字段含义长度或产生方式
qpos配置 qn_q,由积分或用户设置
qvel广义速度 vn_v,由积分或用户设置
act执行器内部激活 zn_a,由自身动力学推进
ctrl执行器控制 un_u,由控制器写入
qfrc_applied、xfrc_applied用户广义力、刚体笛卡尔力与力矩输入;后者通过雅可比回传
xpos、xmat、M、contact世界位姿、质量矩阵、接触记录位置阶段产生
qacc、act_dot加速度与激活导数本次动力学求值的结果

含球关节时,四个四元数分量对应三个独立角速度分量。一个自由关节对 qpos 占七项,对 qvel 占六项。因此配置更新要按关节类型进行:标量关节使用加法,姿态使用第三章的四元数增量。

\dot q=N(q)v,\qquad \dot v=a,\qquad \dot z=g(q,v,z,u).
(9.1)

N(q) 表示把广义速度转成配置表示导数的映射。标量移动关节对应单位映射;球关节对应 \dot q_{\rm quat}=q_{\rm quat}(0,\boldsymbol\omega_b)/2。实际配置积分由 mj_integratePos 按这一几何关系完成。

可以把当前状态理解为下一步求值的起点。例如,给一个自由刚体设置新的位置和四元数后,qpos 已经记录这一改变;碰撞包围盒、世界惯量和接触点还需要根据新配置重新计算。相同地,只改变控制时,几何可能仍可复用,而执行器力和加速度需要更新。数据之间的这种依赖关系决定了后面的计算顺序。

保存与恢复状态

上述变量构成常见刚体和执行器的主要状态。3.10.0 的 mjSTATE_PHYSICS 还包含用于延迟执行器、传感器的 history;完整物理状态再包括 time 和 plugin_state。精确恢复一次运行时,还要保存控制、施加力、等式开关与求解器热启动等输入。mjSTATE_INTEGRATION 将这些积分所需字段一起纳入。

int nstate = mj_stateSize(m, mjSTATE_INTEGRATION);
// buffer 已分配 nstate 个 mjtNum
mj_getState(m, d, buffer, mjSTATE_INTEGRATION);
// 稍后恢复这一份状态
mj_setState(m, d, buffer, mjSTATE_INTEGRATION);
mj_forward(m, d);

最后一次调用使几何、力和加速度与恢复的状态对应。若控制回调已安装,它也会在这次求值中运行。状态分类与接口说明见本地 simulation.rst 的 State components。

02

一次动力学求值的五个阶段

连续时间动力学要回答的是:在给定状态和输入下,此刻的速度将怎样改变?MuJoCo 的 mj_forward 依次完成五组计算,主结果为 qacc 和 act_dot。整个过程保持当前仿真时间。

父子位姿沿用式(6.2)的递推,质量矩阵描述式(7.2)中的加速度系数。两者由当前配置确定;速度项随后使用当前位置与当前速度,力和加速度再使用这些已经建立的量。

阶段已有信息主要结果
位置 mj_fwdPosition配置、模型与几何输入世界位姿、惯量、质量矩阵及分解、碰撞、约束雅可比、传动
速度 mj_fwdVelocity位置阶段结果、广义速度刚体速度、传动速度、被动力、偏置力、约束参考量
执行器 mj_fwdActuation控制、激活、传动长度与速度激活导数、标量执行器力、执行器广义力
平滑加速度 mj_fwdAcceleration已知力与质量矩阵分解总平滑广义力、平滑加速度
约束 mj_fwdConstraint平滑加速度、约束模型与雅可比约束力、最终加速度

位置阶段先沿刚体树计算世界位姿,继而组装质量矩阵并分解;碰撞查询建立接触记录,约束装配确定本次的行数和雅可比。执行器传动也在这里求出长度和力臂。速度阶段沿已有结构计算运动量,并用广义加速度取零时的递归 Newton–Euler 计算求出 c(q,v)。

由配置建立本次计算的几何

位置阶段承接第六、八章的递推。父刚体的世界位姿与当前关节变换相乘,得到子刚体位姿;刚体惯量由各自的质心和主轴变换到计算坐标,再沿树形成 M(q)。质量矩阵完成分解后,后续每次给定一列力,都可以通过前代、回代求出一列加速度。一个状态中的平滑加速度求解与约束方向求解共用这一分解。

接触几何也依赖配置。两个表面距离多远、法向指向哪里,先由碰撞查询确定;接着将接触点速度投影到法向和切向,形成相应雅可比行。接触对发生变化时,约束行数也随之改变。由此可见,固定的关节自由度数与本次求值中的约束行数分别由模型拓扑和当前几何决定。

由速度建立偏置与被动力

速度阶段使用刚才得到的几何,将 v 传到各刚体和执行器。以一条标量关节为例,阻尼产生 -dv,弹簧产生 -k(q-q_*);这里 q_* 是弹簧的参考位置。这些力可以在约束求解之前确定。偏置力 c(q,v) 则包括重力、向心和科里奥利效应:把广义加速度设为零,运行第八章的递归 Newton–Euler 计算,就得到维持该瞬时零加速度所需的广义力。

例如,单摆取角度 \theta、杆长 l、末端质量 m,有 M=ml^2、c=mgl\sin\theta。位置改变后,世界位置和重力矩需要重算;只改变角速度时,几何保持原值,而阻尼力随之改变。在多连杆中,质量矩阵也通常随配置变化,偏置力中还会出现速度的二次项。

位置、速度和加速度传感器分别在相应结果准备好后求值。控制回调位于速度阶段之后,因而可以读取当前的位姿和速度,用它们生成随后所需的控制输入。

第三至五节分别建立传动、质量求解和约束修正的关系。第六节把同一组力依次代入,并用两质点图展示每一步怎样改变后续计算。

各阶段会读取此前留下的数组。以质量矩阵为例,改变配置后,下一次求解就要先重新组装和分解。第八节将据此说明阶段复用的条件。

03

传动、控制与执行器内部状态

考虑一根连接机构的绳索。关节转动会改变绳长;绳索拉力也会沿同一几何关系产生关节力矩。关节、绳索等常用长度传动用标量 l_i(q) 描述,将长度对配置运动的瞬时变化排列成矩阵 P:

\dot l=Pv,\qquad \tau_a=P^{\mathsf T}f.
(9.2)

f 收集各执行器的标量力,\tau_a 是它们合成的广义力。MuJoCo 将传动的这些系数保存为稀疏的 actuator_moment,长度和速度分别保存在 actuator_length、actuator_velocity。

读矩阵 P 时,每一行对应一路执行器,每一列对应一个广义速度。某个系数表示:该自由度以单位速度运动时,这一路传动长度以多快的速度变化。对铰链,系数中会含力臂;对直接驱动的移动关节,它可以是无量纲传动比。因此传动把“长度变化”和“广义力方向”联系起来,矩阵转置由功率相等确定。

推导长度变化与功率守恒\dot l=Pv,\quad\tau_a=P^{\mathsf T}f

先取全部配置都是标量的情形。链式法则给出

\dot l_i=\sum_j\frac{\partial l_i}{\partial q_j}\dot q_j=\sum_j P_{ij}v_j.

含四元数时,使用式(9.1),相应系数为 P=(\partial l/\partial q)N(q),仍然具有 n_v 列。执行器的总功率等于标量力乘传动速度:

\mathcal P=f^{\mathsf T}\dot l=f^{\mathsf T}Pv=(P^{\mathsf T}f)^{\mathsf T}v.

另一方面,广义力的功率为 \tau_a^{\mathsf T}v。对每个可能的瞬时速度都相等,便有 \tau_a=P^{\mathsf T}f。也可从任意虚位移的做功相等得到同一结果。

例题:两路传动的功率

P=\begin{bmatrix}1&0\\1/2&1\end{bmatrix},\quad v=\begin{bmatrix}0.6\\-0.2\end{bmatrix}\mathrm{m/s},\quad f=\begin{bmatrix}4\\-2\end{bmatrix}\mathrm N.

第一路速度为 1(0.6)+0(-0.2)=0.6,第二路为 (1/2)(0.6)-0.2=0.1,单位均为米每秒。广义力两项分别为 4+(1/2)(-2)=3 与 -2,单位均为牛顿。两侧功率分别为

4(0.6)-2(0.1)=2.2\,\mathrm W,\qquad 3(0.6)+(-2)(-0.2)=2.2\,\mathrm W.
思考执行器力为负,是否就表示它正在从机构取走能量?

功率等于力与传动速度的乘积。若力和速度同时为负,乘积为正,执行器仍向机构输入能量。例如 (-2\,\mathrm N)(-0.1\,\mathrm{m/s})=0.2\,\mathrm W。判断能量流向需要同时检查力和速度。

实验 01速度映射与力的回传

分别改变两种广义速度和两路执行器力,核对执行器侧与机构侧的功率。正、负功率分别对应向机构输入能量和从机构取出能量。

在图中令 v_1=1,v_2=-1/2,第二路传动速度变为零。它仍可对两个质点施力,但这一瞬间两项功率互相抵消。第一路速度保持为一,其功率直接等于第一路标量力。这个状态能同时核对 P 的行含义和 P^{\mathsf T} 的力回传方式。

力的生成

直接力控制可以令 f_i=k_i u_i。位置伺服则根据目标与当前传动长度之差产生力,例如 f_i=k_p(u_i-l_i)-k_d\dot l_i。这时 ctrl 保存目标长度,其单位随传动定义。控制输入的物理含义由执行器模型给定。

启用限幅后,执行器先按控制范围取得有效输入,再计算力,并按执行器力范围限制输出;关节级的执行器总力限制在广义力合成后应用。一个区间的限幅可写为 \operatorname{clip}(x,l,h)=\min(h,\max(l,x))。MuJoCo 的黏附等特殊传动直接从接触几何构造广义力方向,其 actuator_moment 按该传动定义读取。

带内部状态的执行器先由控制驱动激活,再由激活产生力。一阶滤波是一个简单例子:

\dot z=\frac{u-z}{\tau},\qquad f=kz,\qquad \tau>0.
(9.3)

这里 \tau 是时间常数,控制和激活具有相同单位。即使控制瞬间变化,激活也要沿自身的微分方程演化。MuJoCo 分别用 act 和 act_dot 保存当前激活及其导数。

在一段时间内保持控制恒定,激活与目标的差按指数衰减:

z(t+h)=u+(z(t)-u)e^{-h/\tau}.

经过一个时间常数,剩余误差约为原来的 e^{-1}\approx0.368。控制从零阶跃到一、初始激活为零时,这就对应激活上升到约 0.632。时间常数因而给出了可以从图上直接辨认的响应快慢。

推导恒定控制下的激活解析更新z(t+h)=u+(z(t)-u)e^{-h/\tau}

在一步内令控制恒定。记误差 e=z-u,则 \dot e=-e/\tau。乘以积分因子:

\frac{\mathrm d}{\mathrm dt}\left(e^{t/\tau}e(t)\right)=e^{t/\tau}\left(\dot e+\frac e\tau\right)=0.

括号中的量保持常数,因此经过步长 h:

e(t+h)=e(t)e^{-h/\tau},\qquad z(t+h)=u+(z(t)-u)e^{-h/\tau}.

在 MuJoCo 中,filterexact 采用这一恒定输入解析更新。Euler 近似则为 z(t+h)=z(t)+h(u-z(t))/\tau。普通力计算使用当前激活;启用 actearly 时,执行器会根据预测的下一激活计算力。

实验 02控制阶跃与激活动态

比较连续解析曲线和 Euler 离散点。改变时间常数可观察响应快慢;改变步长可观察离散误差。

固定时间常数并减小步长,橙色离散点逐渐靠近绿色解析曲线。Euler 每一步沿当前斜率前进;激活上升时,实际斜率持续减小,所以在图中的正步长范围内,Euler 点通常位于解析曲线上方。这个误差会通过 f=kz 继续影响力,随后进入质量方程。

04

从已知力求平滑加速度

第七、八章得到的广义动力学方程可以写为

Ma+c=\tau_p+\tau_a+\tau_u+\tau_x+J^{\mathsf T}\lambda.
(9.4)

\tau_p 为弹簧、阻尼等被动力,\tau_a 为执行器广义力,\tau_u 为直接施加的用户广义力。用户对刚体施加的笛卡尔力和力矩,经相应雅可比转置投影后组成 \tau_x。最后一项收集尚待求解的约束力。

将已知各项相加,得到平滑广义力,再解一次质量矩阵方程:

\tau_0=\tau_p+\tau_a+\tau_u+\tau_x-c,\qquad Ma_u=\tau_0.
(9.5)

a_u 称为平滑加速度,对应 qacc_smooth。实际求值使用已经分解的质量矩阵,通过回代求解。第八章给出的稀疏分解可以在这一位置直接接入。

为便于计算,式(9.4)的四类已知力在求和后只保留一个合力记号 \tau_0。后面的推导只需记住 Ma_u=\tau_0;各数组名称用于最后的源码对照。这里的“平滑”表示约束修正之前的运动响应。接触法向、摩擦和关节限位等力,将在下一阶段依照各自的力学条件共同确定。

若外力作用在刚体上的某一点,点速度雅可比 J_x 将广义速度变成该点速度;自由力矩对应角速度雅可比 J_\omega。它们对已知力的贡献为 \tau_x=J_x^{\mathsf T}\boldsymbol F+J_\omega^{\mathsf T}\boldsymbol T。各雅可比与力都采用同一个世界坐标系,才能直接按下式核对功率。

推导笛卡尔点力与广义力\tau_x=J_x^{\mathsf T}\boldsymbol F+J_\omega^{\mathsf T}\boldsymbol T

若一个作用点的世界速度为 \dot{\boldsymbol x}=J_xv,世界力为 \boldsymbol F,则

\boldsymbol F^{\mathsf T}\dot{\boldsymbol x}=\boldsymbol F^{\mathsf T}J_xv=(J_x^{\mathsf T}\boldsymbol F)^{\mathsf T}v.

因此其广义力为 J_x^{\mathsf T}\boldsymbol F。另有自由力矩 \boldsymbol T 时,加上角速度雅可比对应的 J_\omega^{\mathsf T}\boldsymbol T。同一功率关系同时说明了执行器传动和外力投影的转置结构。

在源码中,qfrc_passive - qfrc_bias + qfrc_applied + qfrc_actuator 先组成 qfrc_smooth;mj_xfrcAccumulate 再加入 xfrc_applied 的投影,随后由 mj_solveLD 求得 qacc_smooth。

05

约束力与加速度修正

两条滑轨上的质点若由保持相对位移的理想连接件约束,就需要 q_2-q_1=d。对时间求导,它要求两者速度相同;再求导,要求两者加速度相同。约束力承担了将自由运动改为共同运动的作用。

a=a_u+M^{-1}J^{\mathsf T}\lambda.
(9.6)

这个式子由式(9.4)减去式(9.5)得到。J^{\mathsf T}\lambda 是约束广义力,质量矩阵的求解将它转成加速度修正。

一条理想等式约束可以分成“运动要求”与“力的响应”两部分。运动要求写为 Ja=b;b 给出几何曲率要求的加速度。单位约束力经过质量矩阵后产生一列加速度,记为 y。将它再投影回约束方向,得到标量 A:

My=J^{\mathsf T},\qquad A=Jy,\qquad \lambda=\frac{b-Ja_u}{A},\qquad a=a_u+y\lambda.

分子是约束方向上尚缺少的加速度,分母是单位约束力能造成的加速度;两者相除就是需要施加的力。这个顺序把嵌套乘积拆为一次质量求解、一次投影和一次标量除法,每一步都有明确的量纲。

推导单行等式约束的求解\lambda=(b-Ja_u)/A

设标量约束为 r(q)=0,配置使用标量坐标。第一次求导得到 \dot r=Jv,其中 J=\partial r/\partial q。第二次求导:

\ddot r=Ja+\dot Jv.

当约束位置和速度都准确时,保持约束需要 \ddot r=0。令 b=-\dot Jv,要求便写成 Ja=b。代入加速度修正:

Ja_u+JM^{-1}J^{\mathsf T}\lambda=b.

对于一条独立约束,记标量 A=JM^{-1}J^{\mathsf T}。先解 My=J^{\mathsf T},再取 A=Jy,得到

\lambda=\frac{b-Ja_u}{A},\qquad a=a_u+y\lambda.

质量矩阵正定且约束行非零时,A>0。多个独立约束把同一推导变成约束空间中的线性系统;实际通过质量矩阵回代组成所需乘积。

以两质点的相对加速度为例,J=(-1,1)。正的 \lambda 对第一个质点施负向力,对第二个施正向力:

y=\begin{bmatrix}-1/m_1\\1/m_2\end{bmatrix},\qquad A=\frac1{m_1}+\frac1{m_2}.

同样大小的一对力使两者各改变一份加速度,因此它们对相对加速度的贡献相加。由正质量直接看出 A>0。若已有 a_{u1}=a_{u2} 且目标为零,相对运动无需修正,公式自动给出零约束力。

MuJoCo 把接触、关节限位、等式与摩擦等约束装配成行,并结合软约束和摩擦模型求解。约束阶段计算 efc_b = J*qacc_smooth - efc_aref,将当前平滑运动与参考目标作比较;最后产生 efc_force、qfrc_constraint 和 qacc。第十一至十四章将分别建立这些参考量和力的条件。这里的理想等式算例用于展示质量求解与力回传的完整关系。

06

贯穿五阶段的两质点算例

取两个独立的直线移动坐标。在本次状态上,质量、传动与力为

M=\begin{bmatrix}2&0\\0&1\end{bmatrix}\mathrm{kg},\quad P=\begin{bmatrix}1&0\\1/2&1\end{bmatrix},\quad f=\begin{bmatrix}4\\-2\end{bmatrix}\mathrm N.
\tau_p=\begin{bmatrix}1\\-1\end{bmatrix}\mathrm N,\quad c=\begin{bmatrix}2\\3\end{bmatrix}\mathrm N,\quad \tau_u=\begin{bmatrix}0\\4\end{bmatrix}\mathrm N,\quad \tau_x=\boldsymbol0.

常量偏置可以由线性势能 V(q)=2q_1+3q_2 对应的恒定载荷产生,系数单位为牛顿;被动力与用户力采用上述本次取值。约束为 q_2-q_1=0,因此 J=(-1,1),且 \dot J=0。

这组数据的完整结果为:传动后的执行器力 (3,-2)^{\mathsf T}\,\mathrm N,平滑加速度 (1,-2)^{\mathsf T}\,\mathrm{m/s^2},约束力大小 2\,\mathrm N,最终两者加速度均为零。下面按同一顺序展开每个中间量;图中的每个阶段都对应其中的一步。

计算执行器、平滑运动和等式力逐项计算\lambda=2\,\mathrm N,\quad a=\boldsymbol0

1. 传动力。转置后相乘:

\tau_a=\begin{bmatrix}1&1/2\\0&1\end{bmatrix}\begin{bmatrix}4\\-2\end{bmatrix}=\begin{bmatrix}3\\-2\end{bmatrix}\mathrm N.

2. 平滑加速度。先合力,再按两个质量分别求解:

\tau_0=\begin{bmatrix}1\\-1\end{bmatrix}+\begin{bmatrix}3\\-2\end{bmatrix}+\begin{bmatrix}0\\4\end{bmatrix}-\begin{bmatrix}2\\3\end{bmatrix}=\begin{bmatrix}2\\-2\end{bmatrix}\mathrm N,\qquad a_u=\begin{bmatrix}1\\-2\end{bmatrix}\mathrm{m/s^2}.

3. 约束空间。单位约束力对应的加速度方向为

y=M^{-1}J^{\mathsf T}=\begin{bmatrix}-1/2\\1\end{bmatrix}\mathrm{kg^{-1}},\qquad A=Jy=3/2\,\mathrm{kg^{-1}},\qquad Ja_u=-3\,\mathrm{m/s^2}.

目标 b=0 给出 \lambda=3/(3/2)=2\,\mathrm N。因此

J^{\mathsf T}\lambda=\begin{bmatrix}-2\\2\end{bmatrix}\mathrm N,\qquad a=\begin{bmatrix}1\\-2\end{bmatrix}+\begin{bmatrix}-1\\2\end{bmatrix}=\boldsymbol0.

把约束力加回平滑合力,两个自由度的净力都为零。若初始速度同为 0.2\,\mathrm{m/s},它们保持共同匀速运动,理想连接保持原长度。

下面的实验 03 和配套内核使用这组数据。关闭等式约束后,两者分别按 1 与 -2\,\mathrm{m/s^2} 加速;打开约束后,求解器通过一对等大反向的力使加速度相同。

实验 03两质点的受力与约束修正

选择计算阶段,核对下方累计记录与当前受力。到约束阶段后,拖动“约束力施加比例”,观察相对加速度如何从自由值变化到零;改变执行器力,比较共同加速度与约束力的变化。

初始数据下,质点一的平滑合力为正,质点二为负,两者会相向加速。约束施加负向和正向各 2\,\mathrm N 的力,恰好抵消这两项。把施加比例记为 \alpha,图中的中间加速度为 a(\alpha)=a_u+\alpha y\lambda,相对加速度为 Ja(\alpha)=-3(1-\alpha)\,\mathrm{m/s^2}。滑块展示同一个约束修正的组成过程;完整约束解位于 \alpha=1。

再把两路执行器力改为 (2,2)^{\mathsf T}\,\mathrm N,两个质点的平滑加速度变为 1 与 2\,\mathrm{m/s^2},共同加速度为 4/3\,\mathrm{m/s^2}。约束使它们共同运动,系统整体是否加速则由总外力决定。连接件的一对内力在求整体合力时互相抵消。

07

积分与数据的时间层

完成一次动力学求值后,积分器把状态推进到下一时刻。对本章标量坐标模型,半隐式 Euler 先更新速度,再用新速度更新位置:

v_{k+1}=v_k+ha_k,\qquad q_{k+1}=q_k+hv_{k+1},\qquad t_{k+1}=t_k+h.
(9.7)

含姿态时,第二步调用配置积分,用新角速度构造四元数增量。有状态执行器还需要推进激活;其离散公式由所选激活动力学决定。MuJoCo 的 Euler 分支也包含关节阻尼的隐式处理,因此具体更新所用加速度可能经过阻尼求解修正。第十五章推导这些积分器。

积分后的缓存时间层

以本章没有隐式阻尼修正的单步示例为例,先在 t_k 求位姿、力与加速度,再把 qpos、qvel 和 time 推进到 t_{k+1}。这时配置已经改变,先前计算的世界位姿与接触仍保存着旧计算结果。

调用 mj_step 后需要立即读取新状态上的派生量时,可以再调用 mj_forward。它保持新状态的时间,同时计算与新配置、速度对应的几何、力和加速度。经典四阶 Runge–Kutta 方法(Runge–Kutta method of order four, RK4)会在一步内对多个临时状态求值,其返回缓存可能来自内部阶段;统一的判据是:积分之后,所需派生量应在最终状态上重新计算。

例题:新位置与旧几何缓存

关闭前例的约束,取 q_k=(0,0)^{\mathsf T}\,\mathrm m、v_k=(0.2,0.2)^{\mathsf T}\,\mathrm{m/s}、h=0.1\,\mathrm s。一步后

v_{k+1}=\begin{bmatrix}0.3\\0\end{bmatrix}\mathrm{m/s},\qquad q_{k+1}=\begin{bmatrix}0.03\\0\end{bmatrix}\mathrm m.

如果几何位置缓存仍为 (0,0)^{\mathsf T}\,\mathrm m,它记录的就是本步积分前的求值。重新计算位置阶段,第一质点的缓存才会变为 0.03\,\mathrm m。

实验 04状态推进与几何缓存

依次观察求值、速度更新、配置更新和重新求值。暂停在第三阶段,比较新配置与仍保存旧值的几何缓存。

在第三阶段,绿色圆点已经移动,橙色方框仍停在旧位置;进入第四阶段后,方框追上新配置,但仿真时间保持原值。两次变化分别对应积分和重新求值。由此可以判断一个读数的时间含义:配置由积分推进,几何缓存由相应计算阶段刷新。

08

调用接口与阶段复用

本地 engine_forward.c 第 1920 行起实现 mj_forwardSkip。它按位置、速度、执行器、平滑加速度、约束的次序调用各阶段。mj_step 先运行完整前向计算,再根据 opt.integrator 选择积分器。

在当前观测之后设置控制

mj_step1 完成位置和速度相关计算,让控制器读取这些新结果。随后可以写入控制和外力,再由 mj_step2 完成力、加速度与积分:

mj_step1(m, d);
// 使用本次已计算的位姿与速度,写入 ctrl 或施加力
set_control(m, d);
mj_step2(m, d);

在两次调用之间保持配置、速度和模型一致,使前两阶段结果继续有效。已安装的 mjcb_control 会在 mj_step1 末尾调用一次,随后手动写入的控制会成为 mj_step2 的输入。

本地第 2062 行的分支表明:implicit 和 implicitfast 在 mj_step2 中使用隐式积分,其余选项使用 Euler;RK4 选项在这一路径下也进入 Euler。使用 RK4 并使反馈控制随内部状态更新时,将控制律放入 mjcb_control,由完整 mj_step 在各求值阶段调用。

说明RK4 中的反馈求值依赖

令全部连续状态为 y,反馈为 u=\pi(t,y),组成闭环导数 F(t,y)=f(t,y,\pi(t,y))。经典 RK4 的中间量为

\begin{aligned}k_1&=F(t,y),\\k_2&=F(t+h/2,y+hk_1/2),\\k_3&=F(t+h/2,y+hk_2/2),\\k_4&=F(t+h,y+hk_3),\\y_{k+1}&=y+\frac h6(k_1+2k_2+2k_3+k_4). \end{aligned}

每个阶段都在自己的状态上计算控制,才能近似这个闭环微分方程。若整步有意使用一次采样后保持的控制 u_k,则各阶段评估 f(t_i,y_i,u_k),得到的是该采样控制下的运动。含四元数的中间配置使用相应的姿态增量实现;上式展示控制求值的依赖关系。

复用已经计算好的阶段

研究同一配置附近的控制响应时,可以保留质量矩阵和雅可比。mj_forwardSkip 的参数指定已经有效的前段结果:

选项保留结果继续执行
mjSTAGE_NONE从头计算全部阶段
mjSTAGE_POS当前位置阶段的结果速度与后续阶段
mjSTAGE_VEL当前位置、速度阶段的结果控制回调、执行器、平滑加速度、约束

例如,先在一个状态上完整求值,只修改 ctrl,再用 mjSTAGE_VEL 重新求加速度;改变速度而保持位置阶段的全部输入时,可从速度阶段继续。复用的判断依据是缓存的全部依赖保持一致,其中包括模型、运动捕捉位姿、约束启用状态、休眠状态与相关回调输入。每次正常时间推进之后,通常从完整前向计算开始。

最后一个参数 skipsensor 控制是否重算传感器。选择跳过时,传感器数组保留已有值。控制回调仍按函数中的位置调用,因此比较不同控制样本时,要让回调与手动设置输入采用一致的策略。

官方说明见前向动力学与阶段复用、数据一致性。具体版本行为以本章链接的本地 3.10.0 源码为准。

09

实现与练习

pipeline.py 将两质点模型写成 Model 与 Data,实现传动、质量求解、单行理想等式、阶段缓存和半隐式更新。其 forward 保持当前状态,step 推进状态并保留本步派生量;缓存键检查展示复用所需条件。激活函数单独实现恒定输入解析更新。

from pipeline import Model, Data, forward, step
m, d = Model(), Data()
forward(m, d)
print(d.cache["smooth"], d.cache["acceleration"])
step(m, d, 0.1, constrained=False)
print(d.q, d.cache["position"])
forward(m, d)
print(d.q, d.cache["position"])

在笔记目录运行独立核对,检查手算结果、传动功率、约束动力学残差、阶段复用及时间层:

python3 examples/check_pipeline.py

练习 1 · 状态维数

模型含一个自由关节、两个铰链和一个球关节;三个执行器中有一个采用一维激活滤波。求 n_q,n_v,n_u,n_a,并解释激活与控制的区别。

答案按关节与执行器分别计数
n_q=7+2+4=13,\quad n_v=6+2+3=11,\quad n_u=3,\quad n_a=1.

控制由用户或控制器设置。滤波激活保存历史控制作用形成的内部状态,需要根据导数推进;另两个执行器直接根据当前控制产生力。

练习 2 · 传动方向与功率

使用式(9.2)的两路传动,取 v=(1,-1/2)^{\mathsf T}\,\mathrm{m/s} 和 f=(2,6)^{\mathsf T}\,\mathrm N。求传动速度、广义力和功率。

答案零传动速度的一路
Pv=\begin{bmatrix}1\\0\end{bmatrix}\mathrm{m/s},\quad P^{\mathsf T}f=\begin{bmatrix}5\\6\end{bmatrix}\mathrm N.

执行器侧功率为 2(1)+6(0)=2\,\mathrm W;广义功率为 5(1)+6(-1/2)=2\,\mathrm W。第二路执行器在这一瞬时运动上速度为零,其两个广义力分量的功率相互抵消。

练习 3 · 激活响应

初始激活为零,控制阶跃到一,时间常数 0.05\,\mathrm s。求经过 0.01\,\mathrm s 的解析激活与一步 Euler 激活。若力增益为 10\,\mathrm N,求解析激活对应的力。

答案解析积分与离散近似
z=1-e^{-0.01/0.05}\approx0.181269,\qquad z_E=0+0.01/0.05=0.2.

解析激活产生 f=10z\approx1.81269\,\mathrm N。Euler 一步将激活增加得更多;减小步长后可用实验 02 检查两者接近。

练习 4 · 平滑力与约束力

保留第六节全部参数,把执行器力改为 (2,2)^{\mathsf T}\,\mathrm N。仍要求 a_2-a_1=0。求平滑加速度、约束力和最终加速度。

答案共同加速的两个质点
P^{\mathsf T}f=(3,2)^{\mathsf T},\quad \tau_0=(2,2)^{\mathsf T}\,\mathrm N,\quad a_u=(1,2)^{\mathsf T}\,\mathrm{m/s^2}.

相对加速度为 Ja_u=1,故 \lambda=-1/(3/2)=-2/3\,\mathrm N。代回:

a=\begin{bmatrix}1\\2\end{bmatrix}+\begin{bmatrix}-1/2\\1\end{bmatrix}\left(-\frac23\right)=\begin{bmatrix}4/3\\4/3\end{bmatrix}\mathrm{m/s^2}.

总外力为 4\,\mathrm N,总质量为 3\,\mathrm{kg},共同加速度也可由整体 Newton 方程核对。

练习 5 · 时间层

第七节例题改用 h=0.05\,\mathrm s。求新配置和速度,说明教学内核 step 返回后 cache["position"] 的值,以及怎样刷新。

答案先速度、后位置
v_{k+1}=(0.25,0.10)^{\mathsf T}\,\mathrm{m/s},\qquad q_{k+1}=(0.0125,0.005)^{\mathsf T}\,\mathrm m.

缓存仍为旧配置对应的 (0,0)^{\mathsf T}。调用 forward 后,缓存和新配置一致,仿真时间仍为 0.05\,\mathrm s。

练习 6 · 选择阶段复用

已完整计算某状态。依次考虑三次独立修改:只改控制、只改速度、改变配置。在其他相关输入固定的条件下,各自从哪个阶段继续?

答案按依赖选择起点

只改控制时保留位置与速度结果,选 mjSTAGE_VEL;只改速度时保留位置结果,选 mjSTAGE_POS;改变配置时选 mjSTAGE_NONE。若控制回调会重写控制,还要使每次输入与该回调的策略一致。复用传感器与否由最后一个参数单独指定。

练习 7 · 约束求解残差

写出两个可以独立核对第五节解的残差,并说明各自检查什么。用第六节数值计算它们。

答案运动条件与力平衡
r_c=Ja-b,\qquad r_d=M(a-a_u)-J^{\mathsf T}\lambda.

第一项检查约束加速度,第二项检查约束力造成的加速度修正。第六节中 a=0,b=0,所以 r_c=0;第二项为 (-2,2)^{\mathsf T}-(-2,2)^{\mathsf T}=0。配套检查脚本另在一般正定二阶质量矩阵上同时检验这两个残差。

练习 8 · RK4 与控制

希望每个 RK4 内部阶段都采用当前状态的反馈控制。说明应使用的调用方式;再解释整步固定控制对应怎样的模型。

答案反馈求值与采样保持

将反馈函数安装为 mjcb_control,选择 RK4 并调用 mj_step。内部求值会用各阶段的配置、速度和时间计算控制。整步固定 u_k 时,积分的是采样保持输入下的动力学;两种控制策略各有明确含义。3.10.0 的 mj_step1/mj_step2 路径在 RK4 选项下采用 Euler。