正运动学:DH 参数与 PoEForward Kinematics: DH vs. PoE
给定关节角,每一个连杆坐标系在世界里的哪里?机器人界有两门通用语言:几十年来写在厂商手册里的 Denavit–Hartenberg (DH) 参数表,和 5.10 节学过的指数积公式 (PoE)。本课把它们放在同一台 UR5 上对比:一个 DH 连杆是哪四步动作,两种写法为什么算出同一个结果,DH 在哪里会出问题,以及基座、工具和标定怎么处理。 Given the joint angles, where is every link frame in the world? Robotics has two common languages for this: the Denavit–Hartenberg (DH) tables that vendor manuals have used for decades, and the product of exponentials (PoE) of Lesson 5.10. This lesson puts them side by side on one UR5: the four moves behind a DH link, why both give the same answer, where DH breaks, and how base frames, tool frames and calibration are handled.
1为什么机器人需要它Why robots need this
控制器每个周期要知道末端在哪里;逆运动学要反复调用它;碰撞检测要把每一节连杆放进世界;仿真器、可视化、手眼标定都先问同一个问题:给定 \(\boldsymbol\theta\),每个坐标系在哪?这就是正运动学 (FK)。5.10 节已经给出一个干净的答案:指数积公式 \(T(\boldsymbol\theta)=\Exp(S_1\theta_1)\cdots\Exp(S_n\theta_n)M\)。
可是你在现实中拿到的资料,往往是另一种形式。Universal Robots 和 Franka 在官方文档里公布的是 DH 参数表;教科书(Craig、Spong、Siciliano)、很多老代码、不少标定软件说的也是 DH。反过来,Pinocchio、Drake、Gazebo 这类现代库和仿真器读的是 URDF,那是第三种写法。要做机器人,你必须能读懂这三种语言,并且知道它们怎样互相翻译。
本课的路线:先用四步动作拆开一个 DH 连杆(FIG 1);再把真实的 UR5 用 DH 乘一遍、用 PoE 乘一遍,看两个结果在 \(10^{-15}\) 内重合,并学会从 DH 表机械地写出 \(S_i\) 和 \(M\)(FIG 2);然后看 DH 在轴平行或相交时为什么会出问题(FIG 3);最后讨论基座与工具坐标系怎么改(FIG 4),以及为什么 FK 里一毫米的参数误差会变成末端的几毫米误差,机器人怎样标定它(FIG 5)。
A controller needs to know where the end effector is every cycle; inverse kinematics calls FK over and over; collision checking places every link in the world; simulators, visualisation and hand-eye calibration all start with the same question: given \(\boldsymbol\theta\), where is each frame? That is forward kinematics (FK). Lesson 5.10 already gave a clean answer, the product of exponentials \(T(\boldsymbol\theta)=\Exp(S_1\theta_1)\cdots\Exp(S_n\theta_n)M\).
But the documents you receive in practice are often in another form. Universal Robots and Franka publish DH parameter tables in their official documentation; the textbooks (Craig, Spong, Siciliano), a lot of legacy code and many calibration tools speak DH too. Modern libraries and simulators such as Pinocchio, Drake and Gazebo, on the other hand, read URDF, which is a third way of writing the same thing. To work in robotics you must read all three and know how to translate between them.
The route of this lesson: first take one DH link apart into four moves (FIG 1); then multiply out a real UR5 once with DH and once with PoE, watch the results agree to \(10^{-15}\), and learn to write \(S_i\) and \(M\) mechanically from a DH table (FIG 2); then see why DH misbehaves when joint axes become parallel or intersect (FIG 3); then change base and tool frames (FIG 4); finally see how a millimetre of parameter error in FK becomes several millimetres at the tool, and how robots are calibrated (FIG 5).
2直觉:一个 DH 连杆 = 四步动作Intuition: one DH link = four moves
一般来说,两个坐标系之间的位姿有 6 个自由度。DH 的想法是:既然我们可以自己选坐标系,就选得巧一点,让相邻两个坐标系之间只需要 4 个数。办法是规定三件事:关节 \(i\) 的转轴取作 \(z_{i-1}\);\(x_i\) 垂直于 \(z_{i-1}\);\(x_i\) 与 \(z_{i-1}\) 相交。满足这两条垂直和相交的约束后,从坐标系 \(\{i-1\}\) 到 \(\{i\}\) 恰好可以用下面四步依次完成(每一步都是绕着或沿着刚刚到达的坐标系的轴):
- 绕 \(z_{i-1}\) 转 \(\theta_i\);
- 沿 \(z_{i-1}\) 平移 \(d_i\);
- 沿新的 \(x\) 轴(即 \(x_i\),也就是公共法线)平移 \(a_i\);
- 绕 \(x_i\) 转 \(\alpha_i\),把 \(z_{i-1}\) 扭到 \(z_i\)。
这里 \(\theta_i\)(转动关节)或 \(d_i\)(移动关节)是关节变量,其余三个数是连杆本身的几何常数。下面的实验让你调这四个数,并且一步一步地播放。紫色虚线是两根关节轴:第一根穿过 \(\{i-1\}\) 的原点沿 \(z_{i-1}\),第二根穿过 \(\{i\}\) 的原点沿 \(z_i\)。红色线段是沿 \(x\) 的平移 \(a\),蓝色线段是沿 \(z\) 的平移 \(d\)。顶部的开关可以切换到 Craig 的改进 DH:同样四个数,顺序换了,得到另一个矩阵。
In general the pose between two frames has six degrees of freedom. The idea of DH is: since we are free to choose the frames, choose them cleverly so that two neighbouring frames differ by only four numbers. The recipe has three rules: the axis of joint \(i\) is taken as \(z_{i-1}\); \(x_i\) is perpendicular to \(z_{i-1}\); and \(x_i\) intersects \(z_{i-1}\). Under these perpendicularity and intersection constraints, getting from \(\{i-1\}\) to \(\{i\}\) takes exactly four moves, each one about or along an axis of the frame you have just arrived at:
- rotate about \(z_{i-1}\) by \(\theta_i\);
- slide along \(z_{i-1}\) by \(d_i\);
- slide along the new \(x\) axis (that is \(x_i\), the common normal) by \(a_i\);
- rotate about \(x_i\) by \(\alpha_i\), twisting \(z_{i-1}\) into \(z_i\).
\(\theta_i\) (revolute joint) or \(d_i\) (prismatic joint) is the joint variable; the other three numbers are geometric constants of the link. The lab below lets you set the four numbers and play the moves one by one. The dashed purple lines are the two joint axes: the first through the origin of \(\{i-1\}\) along \(z_{i-1}\), the second through the origin of \(\{i\}\) along \(z_i\). Red segments are the slide \(a\) along \(x\), blue segments the slide \(d\) along \(z\). The switch at the top changes to Craig's modified DH: the same four numbers in a different order, which gives a different matrix.
- 点"播放四步",看完整个过程。哪两步让原点移动?哪两步只转动坐标系?Press "Play the 4 moves" and watch it through. Which two moves shift the origin? Which two only rotate the frame?
- 选标准 DH,设 \(\theta=0\)、\(a=0\)、\(\alpha=90^\circ\)(UR5 的第 1 个连杆就是这样)。新的 \(z_i\)(蓝色轴)指向哪个方向?把读数里的矩阵第三列和你的判断对一下。Choose standard DH and set \(\theta=0\), \(a=0\), \(\alpha=90^\circ\) (this is exactly UR5's link 1). Which way does the new \(z_i\) (the blue axis) point? Check the third column of the matrix in the readout against your guess.
- 在 \(a>0.3\)、\(|\alpha|>30^\circ\) 时,来回切换标准 DH 和改进 DH。同样的四个数,终点坐标系是一样的吗?With \(a>0.3\) and \(|\alpha|>30^\circ\), toggle between standard and modified DH. With the same four numbers, is the destination frame the same?
3动手玩:同一台 UR5,两种描述Play: one UR5, two descriptions
UR5 是最常见的协作臂之一,它的标准 DH 参数是 Universal Robots 官方公布的(单位米与弧度):
The UR5 is one of the most common collaborative arms, and its standard DH parameters are published by Universal Robots (metres and radians):
| i | \(\theta_i\) | \(d_i\) (m) | \(a_i\) (m) | \(\alpha_i\) |
|---|
每一行的 \(\theta_i\) 就是第 \(i\) 个关节的角度(零偏为 0)。把六个 \(A_i(\theta_i)\) 依次相乘,就得到法兰盘(第 6 个坐标系)在基座里的位姿。这是教科书的做法,也是 UR 官方文档给你的做法。
PoE 要的却是零位时的六根关节轴 \(S_i\) 和零位末端位姿 \(M\)。怎么办?答案藏在 DH 的构造里:关节 \(i\) 的转轴就是 \(z_{i-1}\),所以只要把 \(\theta=0\) 时各个坐标系的位姿算出来,第 \(i\) 根轴的方向就是坐标系 \(\{i-1\}\) 的 \(z\) 轴,轴上一点就是它的原点:\(\boldsymbol\omega_i = R_{0,i-1}\hat{\mathbf z}\),\(\mathbf q_i=\mathbf p_{0,i-1}\),\(S_i=(\boldsymbol\omega_i,\,-\boldsymbol\omega_i\times\mathbf q_i)\),而 \(M\) 就是零位时的第 6 个坐标系。第 5 节会证明这是严格成立的。FIG 2 同时用两种方法算同一台机械臂,右上角读数是两个 \(4\times4\) 矩阵之差的范数。
The \(\theta_i\) in each row is the angle of joint \(i\) (zero offset 0). Multiply the six \(A_i(\theta_i)\) in order and you get the pose of the flange (frame 6) in the base. That is the textbook recipe, and what the UR documentation hands you.
PoE, however, asks for the six screw axes \(S_i\) at home and the home end-effector pose \(M\). What now? The answer is hidden in the construction of DH: the axis of joint \(i\) is \(z_{i-1}\), so once you compute the frame poses at \(\theta=0\), the direction of axis \(i\) is the \(z\) axis of frame \(\{i-1\}\) and a point on it is that frame's origin: \(\boldsymbol\omega_i = R_{0,i-1}\hat{\mathbf z}\), \(\mathbf q_i=\mathbf p_{0,i-1}\), \(S_i=(\boldsymbol\omega_i,\,-\boldsymbol\omega_i\times\mathbf q_i)\), and \(M\) is simply frame 6 at home. Section 5 proves that this is exact. FIG 2 computes the same arm both ways; the readout shows the norm of the difference between the two \(4\times4\) matrices.
| i | \(\boldsymbol\omega_i\) | \(\mathbf q_i\) | \(\mathbf v_i=-\boldsymbol\omega_i\times\mathbf q_i\) |
|---|
上表由 DH 表在 \(\theta=0\) 处自动算出(把鼠标移到表的某一行,FIG 2 会高亮对应的关节)。注意 \(\boldsymbol\omega_i\) 全是 \(\pm\) 坐标轴方向,因为 UR5 的所有关节轴都平行或垂直于基座的坐标轴。This table is computed from the DH table at \(\theta=0\) (hover a row and FIG 2 highlights that joint). All the \(\boldsymbol\omega_i\) are along coordinate axes, because every joint axis of the UR5 is parallel or perpendicular to the base axes at zero.
- 点"零位"。工具位置是 \((-0.817,\,-0.191,\,-0.005)\)。这三个数各自来自 DH 表里的哪些参数?(提示:\(a_2+a_3\)、\(d_4+d_6\)、\(d_1-d_5\)。)Press "Zero". The tool position is \((-0.817,\,-0.191,\,-0.005)\). Which DH parameters produce each of the three numbers? (Hint: \(a_2+a_3\), \(d_4+d_6\), \(d_1-d_5\).)
- 只把 \(\theta_1\) 设成 \(90^\circ\),其余为 0。把零位的 \((-0.817,-0.191)\) 绕 \(z\) 转 \(90^\circ\) 得到 \((0.191,-0.817)\)。读数一致吗?为什么 \(z\) 不变?Set only \(\theta_1=90^\circ\) and the rest to 0. Rotating the home position \((-0.817,-0.191)\) by \(90^\circ\) about \(z\) gives \((0.191,-0.817)\). Does the readout agree? Why does \(z\) not change?
- 让至少三个关节都偏离 \(30^\circ\) 以上,再用"DH 坐标系"和"PoE 轴"两个模式看。两种算法的差有多大?它会随姿态变大吗?Move at least three joints more than \(30^\circ\) from zero, then look at both the "DH frames" and "PoE axes" modes. How large is the difference between the two algorithms? Does it grow with the posture?
DH 和 PoE 不是两种不同的运动学,而是同一个乘积的两种分组方式。DH 把每个关节的转动和它后面的固定几何捆在一起:\(\mathrm{Rot}_z(\theta_1)\hat A_1\cdot\mathrm{Rot}_z(\theta_2)\hat A_2\cdots\);PoE 把固定几何全部挪到最右边,剩下的转动被"共轭"成了世界系里的螺旋轴 \(\Exp(S_i\theta_i)\)。从 DH 到 PoE 只是做了一次代数整理,没有近似。
DH and PoE are not two different kinematics; they are two groupings of the same product. DH bundles each joint rotation with the fixed geometry after it: \(\mathrm{Rot}_z(\theta_1)\hat A_1\cdot\mathrm{Rot}_z(\theta_2)\hat A_2\cdots\); PoE moves all the fixed geometry to the far right, and the rotations that remain are "conjugated" into screw axes \(\Exp(S_i\theta_i)\) written in the world frame. Going from DH to PoE is a piece of algebra, with no approximation.
4再看两面:DH 的软肋与坐标系的记账Two more views: where DH breaks, and frame bookkeeping
DH 在哪里出问题Where DH breaks
DH 的四个参数由相邻两根关节轴之间的公共法线决定。两根空间中的直线一般有唯一的公共法线,可是有两种特殊情形:
- 两轴平行:公共法线有无穷多条(沿着轴滑动都是),\(d_i\) 没有唯一值,是一个自由参数。习惯做法是挑一个让别的参数为 0 的位置(UR5 的关节 2、3、4 轴互相平行,所以 \(d_2=d_3=0\))。
- 两轴相交:公共法线长度 \(a_i=0\),位置确定(过交点),但方向只能取垂直于两轴的平面法线,\(\pm\) 都可以:这是一个离散的符号约定,选哪个会翻转 \(\alpha_i\) 的符号并让 \(\theta\) 差 \(\pi\)。
更麻烦的是"差一点平行"。真实的机械臂不可能做到严格平行:装配误差让轴有零点几度的倾斜,此时公共法线是唯一的,可它的落脚点会飞到很远,\(d_i\) 变成 \(10\) 米、\(100\) 米,下一个连杆的 \(d_{i+1}\) 也被迫取一个大数来补偿,乘积仍然正确,但这两个参数已经没有物理含义,也无法稳定地辨识。FIG 3 把一根轴慢慢掰到与另一根平行,同时画出 DH 参数和螺旋轴 \(S\) 的各个分量。
The four DH parameters are determined by the common normal between two consecutive joint axes. Two lines in space generally have one common normal, but there are two special cases:
- Parallel axes: there are infinitely many common normals (slide along the axes), so \(d_i\) has no unique value; it is a free parameter. The usual habit is to pick the position that zeroes some other parameter (joints 2, 3, 4 of the UR5 are mutually parallel, so \(d_2=d_3=0\)).
- Intersecting axes: the common normal has length \(a_i=0\) and a definite position (through the intersection), but its direction is only fixed up to the normal of the plane spanned by the two axes, and both signs are allowed: this is a discrete sign convention, and flipping it flips the sign of \(\alpha_i\) and shifts \(\theta\) by \(\pi\).
Worse is "almost parallel". A real arm is never exactly parallel: assembly error tilts the axes by a fraction of a degree. The common normal is then unique, but its foot flies far away: \(d_i\) becomes 10 m, 100 m, and \(d_{i+1}\) is forced to a large value to compensate. The product is still right, but these two parameters have lost their physical meaning and can no longer be identified stably. FIG 3 tilts one axis slowly towards parallel and plots both the DH parameters and the components of the screw axis \(S\).
- 选"近平行",把 \(|\varepsilon|\) 拉到 \(0.1^\circ\) 以下(或点"趋于 0")。\(d\) 变成多少?公共法线现在在哪里?右图的曲线有任何异常吗?Choose "near parallel" and push \(|\varepsilon|\) below \(0.1^\circ\) (or press "Drive to 0"). What does \(d\) become? Where is the common normal now? Does anything strange happen in the right-hand curves?
- 选"近相交",把 \(g\) 从正数慢慢拖到负数,穿过 0。左图里哪些参数发生了跳变?物理上轴 B 动了多少?Choose "near intersecting" and drag \(g\) slowly from positive to negative, across 0. Which parameters jump in the left panel? How much did axis B actually move physically?
坐标系的记账:基座与工具Frame bookkeeping: base and tool
现实中的机械臂总有两个"额外"的坐标系:基座系(机械臂装在工作台上,相对世界系 \(\{W\}\) 有位姿 \(T_{WB}\))和工具系(法兰盘上装了夹爪,工具中心点 TCP 相对法兰有固定位姿 \(T_{\rm tool}\))。换一个夹爪,或者把机械臂挪个位置,FK 怎么改?
两种写法的改法并不一样,这正是 PoE 方便的地方:改工具只动 \(M\)(\(M\mapsto M\,T_{\rm tool}\)),所有 \(S_i\) 原样不动;改基座要动所有 \(S_i\)(\(S_i\mapsto\operatorname{Ad}_{T_{WB}}S_i\),\(M\mapsto T_{WB}M\))。DH 里则是在乘积的左边或右边多乘一个固定矩阵,\(A_i\) 不变。FIG 4 同时用两种方式算,并显示哪些量变了。
A real arm always has two "extra" frames: the base frame (the arm is bolted to a table, at a pose \(T_{WB}\) relative to the world frame \(\{W\}\)) and the tool frame (a gripper is mounted on the flange, and the tool centre point TCP has a fixed pose \(T_{\rm tool}\) relative to it). When you swap the gripper or move the arm, how must FK change?
The two languages do it differently, and this is where PoE is convenient: changing the tool touches only \(M\) (\(M\mapsto M\,T_{\rm tool}\)) and every \(S_i\) stays as it is; changing the base touches every \(S_i\) (\(S_i\mapsto\operatorname{Ad}_{T_{WB}}S_i\), \(M\mapsto T_{WB}M\)). In DH you multiply one more fixed matrix on the left or on the right of the chain and leave the \(A_i\) alone. FIG 4 computes both ways and shows what changed.
- 选"改工具",把 TCP 沿法兰 \(z\) 伸出 \(0.15\) m 以上。\(S_1,\dots,S_6\) 变了吗?\(M\) 变了多少?Choose "change tool" and extend the TCP more than \(0.15\) m along the flange \(z\). Did \(S_1,\dots,S_6\) change? By how much did \(M\) change?
- 选"改基座",把基座绕世界 \(z\) 轴转超过 \(20^\circ\)。哪些 \(S_i\) 变了?有没有哪个 \(\|\Delta S_i\|\) 恰好为 0?为什么?(想一想 \(S_1\) 的轴方向。)Choose "move base" and yaw the base more than \(20^\circ\) about the world \(z\) axis. Which \(S_i\) changed? Is any \(\|\Delta S_i\|\) exactly zero? Why? (Think about the direction of axis 1.)
5再看数学The math
提示:把鼠标悬停在公式中带底色的符号上,FIG 1 或 FIG 2 中对应的元素会被高亮。Tip: hover the highlighted symbols in the formulas; the matching element in FIG 1 or FIG 2 lights up.
标准 DH 矩阵The standard DH matrix
三条规则(轴 \(z_{i-1}\) 是关节 \(i\) 的转轴;\(x_i\perp z_{i-1}\);\(x_i\) 与 \(z_{i-1}\) 相交)确定后,四个参数的含义是:\(\theta_i\) 是绕 \(z_{i-1}\) 从 \(x_{i-1}\) 转到 \(x_i\) 的角;\(d_i\) 是沿 \(z_{i-1}\) 从 \(\{i-1\}\) 的原点到 \(x_i\) 与 \(z_{i-1}\) 交点的距离;\(a_i\) 是沿 \(x_i\) 从交点到 \(\{i\}\) 原点的距离,也就是两轴之间公共法线的长度;\(\alpha_i\) 是绕 \(x_i\) 从 \(z_{i-1}\) 转到 \(z_i\) 的角。把四步按"每一步都在当前坐标系里"的规则(右乘)连起来:
With the three rules (axis \(z_{i-1}\) is the axis of joint \(i\); \(x_i\perp z_{i-1}\); \(x_i\) intersects \(z_{i-1}\)) in place, the parameters mean: \(\theta_i\) is the angle about \(z_{i-1}\) from \(x_{i-1}\) to \(x_i\); \(d_i\) is the distance along \(z_{i-1}\) from the origin of \(\{i-1\}\) to the point where \(x_i\) meets \(z_{i-1}\); \(a_i\) is the distance along \(x_i\) from that point to the origin of \(\{i\}\), the length of the common normal between the two axes; and \(\alpha_i\) is the angle about \(x_i\) from \(z_{i-1}\) to \(z_i\). Chaining the four moves with the rule "each move is in the current frame" (right multiplication) gives
读这个矩阵:最后一列是 \(\{i\}\) 的原点在 \(\{i-1\}\) 里的位置,\((a_i\cos\theta_i,\ a_i\sin\theta_i,\ d_i)\),先升 \(d_i\),再朝转过 \(\theta_i\) 的方向走 \(a_i\);左上的 \(3\times3\) 是 \(R_z(\theta_i)R_x(\alpha_i)\)。整台机械臂是 \(T_{0n}(\boldsymbol\theta)=A_1(\theta_1)A_2(\theta_2)\cdots A_n(\theta_n)\),每个因子相对前一个坐标系,所以全部右乘。这与 PoE 相反:PoE 的指数全部相对固定的世界系,所以左乘(5.10 节)。
Reading the matrix: the last column is the position of the origin of \(\{i\}\) in \(\{i-1\}\), \((a_i\cos\theta_i,\ a_i\sin\theta_i,\ d_i)\): go up \(d_i\), then walk \(a_i\) in the direction turned by \(\theta_i\); the upper-left \(3\times3\) block is \(R_z(\theta_i)R_x(\alpha_i)\). The whole arm is \(T_{0n}(\boldsymbol\theta)=A_1(\theta_1)A_2(\theta_2)\cdots A_n(\theta_n)\), each factor relative to the previous frame, so all multiplications are on the right. PoE is the opposite: its exponentials are all relative to the fixed world frame, hence on the left (Lesson 5.10).
改进 DH(Craig)Modified DH (Craig)
Craig 的版本把坐标系 \(\{i\}\) 放在关节 \(i\) 的轴上(\(z_i\) 是关节 \(i\) 的转轴),并把连杆参数 \(a_{i-1},\alpha_{i-1}\) 放在关节之前:
Craig's version puts frame \(\{i\}\) on the axis of joint \(i\) (\(z_i\) is the axis of joint \(i\)) and puts the link parameters \(a_{i-1},\alpha_{i-1}\) before the joint:
两个版本描述的是同一类机械臂,但每个连杆的坐标系放法不同,表格的数值与下标都对不上。标准 DH 表里第 \(i\) 行的 \((a_i,\alpha_i)\) 描述 \(z_{i-1}\to z_i\),改进 DH 表里第 \(i\) 行的 \((a_{i-1},\alpha_{i-1})\) 也是描述 \(z_{i-1}\to z_i\),只是挂在了第 \(i\) 个关节的名下。这个下标错位是两个版本混用时最常见的 bug(误区 1)。Franka 公布的是改进 DH,UR 是标准 DH。
The two versions describe the same class of arms but place the link frames differently, so neither the numbers nor the indices of the tables match. In a standard DH table, the \((a_i,\alpha_i)\) in row \(i\) describe \(z_{i-1}\to z_i\); in a modified DH table the \((a_{i-1},\alpha_{i-1})\) in row \(i\) describe the same \(z_{i-1}\to z_i\), but are filed under joint \(i\). This index shift is the most common bug when mixing the two (Pitfall 1). Franka publishes modified DH; UR publishes standard DH.
从 DH 到 PoE:一次代数整理From DH to PoE: a piece of algebra
把标准 DH 的每个因子拆成"关节转动 × 固定几何":\(A_i(\theta_i)=\mathrm{Rot}_z(\theta_i)\,\hat A_i\),其中 \(\hat A_i=\mathrm{Trans}_z(d_i)\mathrm{Trans}_x(a_i)\mathrm{Rot}_x(\alpha_i)\) 是常数。记 \(P_i=\hat A_1\cdots\hat A_i\),它正是 \(\theta=0\) 时坐标系 \(\{i\}\) 的位姿 \(T_{0i}(0)\),\(P_0=I\)。\(\mathrm{Rot}_z(\theta)=\Exp(Z\theta)\),\(Z=(0,0,1;\,0,0,0)\)。利用 5.8 节的 \(T\,\Exp(\xi)\,T^{-1}=\Exp(\operatorname{Ad}_T\xi)\),把常数因子一个一个挪到右边:
Split each standard-DH factor into "joint rotation × fixed geometry": \(A_i(\theta_i)=\mathrm{Rot}_z(\theta_i)\,\hat A_i\), where \(\hat A_i=\mathrm{Trans}_z(d_i)\mathrm{Trans}_x(a_i)\mathrm{Rot}_x(\alpha_i)\) is constant. Let \(P_i=\hat A_1\cdots\hat A_i\); this is exactly the pose \(T_{0i}(0)\) of frame \(\{i\}\) at \(\theta=0\), with \(P_0=I\). Also \(\mathrm{Rot}_z(\theta)=\Exp(Z\theta)\) with \(Z=(0,0,1;\,0,0,0)\). Using \(T\,\Exp(\xi)\,T^{-1}=\Exp(\operatorname{Ad}_T\xi)\) from Lesson 5.8, move the constant factors to the right one at a time:
于是得到一份可以直接写成代码的转换算法:
This is a conversion algorithm you can code up directly:
用 \(\operatorname{Ad}_{(R,\mathbf p)}(\boldsymbol\omega,\mathbf v)=(R\boldsymbol\omega,\ \mathbf p\times R\boldsymbol\omega+R\mathbf v)\) 验证 \(\mathbf p\times\boldsymbol\omega=-\boldsymbol\omega\times\mathbf p\),与 5.10 节的 \(\mathbf v=-\boldsymbol\omega\times\mathbf q\) 一致。移动关节则取 \(Z=(0,0,0;\,0,0,1)\)。如果表里有零偏 \(\theta_i=q_i+\delta_i\),把 \(\mathrm{Rot}_z(\delta_i)\) 并进 \(\hat A_i\) 即可,\(q_i\) 就是编码器读数。改进 DH 的推导完全一样,只是转轴在坐标系 \(\{i\}\) 上而不是 \(\{i-1\}\):\(S_i=\operatorname{Ad}_{P_i}Z\),其中 \(P_i\) 此时是改进 DH 里零位时 \(\{i\}\) 的位姿。反过来,从 \(S_i\)、\(M\) 回到 DH 参数要先求相邻两轴的公共法线(FIG 3 的内容)并选定符号约定,结果不唯一。
Check with \(\operatorname{Ad}_{(R,\mathbf p)}(\boldsymbol\omega,\mathbf v)=(R\boldsymbol\omega,\ \mathbf p\times R\boldsymbol\omega+R\mathbf v)\): the moment part is \(\mathbf p\times\boldsymbol\omega=-\boldsymbol\omega\times\mathbf p\), matching \(\mathbf v=-\boldsymbol\omega\times\mathbf q\) of Lesson 5.10. For a prismatic joint use \(Z=(0,0,0;\,0,0,1)\). If the table has a zero offset \(\theta_i=q_i+\delta_i\), absorb \(\mathrm{Rot}_z(\delta_i)\) into \(\hat A_i\), and \(q_i\) is the encoder reading. The derivation for modified DH is identical, except that the axis lies on frame \(\{i\}\) instead of \(\{i-1\}\): \(S_i=\operatorname{Ad}_{P_i}Z\), where \(P_i\) is now the pose of \(\{i\}\) at home in the modified convention. Going back from \(S_i\), \(M\) to DH parameters requires the common normals of neighbouring axes (the content of FIG 3) and a sign convention, and the result is not unique.
第三种语言:URDFThe third language: URDF
URDF(ROS 与大多数仿真器使用的机器人描述文件)给每个关节两样东西:从父连杆坐标系到关节坐标系的固定变换 origin xyz rpy,记作 \(O_i\in\SE\);和一个转轴 axis,单位向量 \(\hat{\mathbf a}_i\),在关节坐标系里表示。关节转 \(q_i\) 之后,子连杆相对父连杆的位姿是 \(O_i\,\Exp\bigl((\hat{\mathbf a}_i;\mathbf 0)\,q_i\bigr)\)。整条链是
URDF (the robot description file used by ROS and most simulators) gives each joint two things: a fixed transform from the parent link frame to the joint frame, origin xyz rpy, call it \(O_i\in\SE\); and an axis, a unit vector \(\hat{\mathbf a}_i\) expressed in the joint frame. After the joint turns by \(q_i\) the child link sits at \(O_i\,\Exp\bigl((\hat{\mathbf a}_i;\mathbf 0)\,q_i\bigr)\) relative to the parent. The whole chain is
其中 \(Z_i=(\hat{\mathbf a}_i;\mathbf 0)\)。这与改进 DH 的结构完全一样(先固定几何,再转关节),只是固定变换用完整的 6 个数 \((x,y,z,r,p,y)\) 而不是靠约定压缩到 4 个,所以不需要任何坐标系放置规则,也没有平行轴的歧义。用同一段推导就能转成 PoE。
with \(Z_i=(\hat{\mathbf a}_i;\mathbf 0)\). This has exactly the structure of modified DH (fixed geometry first, then the joint turns), except that the fixed transform uses all six numbers \((x,y,z,r,p,y)\) instead of squeezing them to four by convention, so no frame-placement rule is needed and there is no parallel-axis ambiguity. The same derivation converts it to PoE.
参数个数Counting parameters
空间中一条直线有 4 个自由度。螺旋轴 \(S=(\boldsymbol\omega,\mathbf v)\) 有 6 个数,但转动关节满足两个约束 \(\|\boldsymbol\omega\|=1\) 和 \(\boldsymbol\omega\cdot\mathbf v=0\),所以独立的只有 4 个(这就是 Plücker 坐标)。一行 DH 也是 4 个数,其中 \(\theta\) 的零偏同样要在标定里估计。所以对 \(n\) 个转动关节,两种写法的"自由度"相当:前人的经典结论是,带任意工具位姿的最小连续模型需要 \(4n+6\) 个参数(6R 臂是 30 个)。差别不在个数,而在参数化是否连续:螺旋轴随几何光滑变化,DH 在平行/相交处没有定义,在附近则病态。PoE 要额外多记一个 \(M\),但它同时也确定了关节零位。
A line in space has four degrees of freedom. A screw axis \(S=(\boldsymbol\omega,\mathbf v)\) has six numbers, but a revolute joint satisfies two constraints, \(\|\boldsymbol\omega\|=1\) and \(\boldsymbol\omega\cdot\mathbf v=0\), leaving four independent numbers (these are Plücker coordinates). A DH row is also four numbers, one of which (the zero offset of \(\theta\)) must likewise be estimated in calibration. So for \(n\) revolute joints both languages carry a comparable number of degrees of freedom: the classical result is that a minimal continuous model with an arbitrary tool pose needs \(4n+6\) parameters (30 for a 6R arm). The difference is not the count but whether the parametrisation is continuous: screw axes vary smoothly with the geometry, whereas DH is undefined at parallel or intersecting axes and ill-conditioned near them. PoE also carries the extra \(M\), which at the same time fixes the joint zeros.
标准 DH:\(\mathrm{Rot}_z\,\mathrm{Trans}_z\,\mathrm{Trans}_x\,\mathrm{Rot}_x\),每行 4 个数,靠建系约定省掉两个自由度。URDF / 改进 DH:固定变换在前、关节转动在后,URDF 用满 6 个数。PoE:把所有固定变换挪到最右边,关节变成世界系中的螺旋轴。三者算的是同一个 \(T(\boldsymbol\theta)\);选哪种取决于你手里的资料和你要做的事:读厂商手册用 DH,读仿真和 ROS 用 URDF,推导速度、标定和理论分析用 PoE。
Standard DH: \(\mathrm{Rot}_z\,\mathrm{Trans}_z\,\mathrm{Trans}_x\,\mathrm{Rot}_x\), four numbers per row, saving two degrees of freedom by a frame-placement convention. URDF / modified DH: fixed transform first, joint rotation after; URDF uses all six numbers. PoE: move every fixed transform to the far right and the joints become screw axes in the world frame. All three compute the same \(T(\boldsymbol\theta)\); which one you use depends on the documents in your hand and what you want to do: DH to read vendor manuals, URDF for simulation and ROS, PoE for derivations of velocity, calibration and theory.
6机器人中的它:运动学标定In robotics: kinematic calibration
FK 是整个机器人栈的地基,所有后面的东西(IK、规划、视觉伺服、学习)都假设 \(T(\boldsymbol\theta)\) 是对的。但是 DH 表里的 \(a_i,d_i\) 是设计值。真实机械臂的连杆有加工公差,轴有装配倾斜,减速器和零点有偏差。结果是:重复定位精度可以很好(同一点来回走,误差零点几毫米以内),但绝对定位精度(命令到某个笛卡尔位置,真的到了没有)可能差几毫米甚至更多。精密作业(装配、钻孔、与相机配准)需要运动学标定:用激光跟踪仪或相机测量末端在很多姿态下的真实位置,再反推出每个连杆参数的修正值。
它是一个最小二乘问题(Gauss–Newton,6.6 节;非线性最小二乘,6.9 节):未知数是参数修正量 \(\delta\mathbf p\),残差是"测得的末端位置 − FK 预测",雅可比是 FK 对参数的导数。FIG 5 做一个最小的版本:你给 UR5 悄悄加入几毫米的制造误差(隐藏的"真机"),先看这点误差怎样变成末端的偏差,再用 20 个测量姿态让 Gauss–Newton 把误差找回来。
FK is the foundation of the whole robot stack: IK, planning, visual servoing and learning all assume that \(T(\boldsymbol\theta)\) is right. But the \(a_i,d_i\) in a DH table are design values. A real arm has machining tolerances in its links, tilt in the assembly of its axes, and errors in its gearboxes and zeros. The result: repeatability can be excellent (going back to the same point is accurate to a fraction of a millimetre), yet absolute accuracy (commanding a Cartesian position and actually arriving there) can be off by several millimetres or more. Precision tasks (assembly, drilling, registration to a camera) need kinematic calibration: measure the true end-effector position in many postures with a laser tracker or a camera, then solve for corrections to each link parameter.
This is a least-squares problem (Gauss–Newton, Lesson 6.6; nonlinear least squares, Lesson 6.9): the unknowns are the parameter corrections \(\delta\mathbf p\), the residuals are "measured tool position − FK prediction", and the Jacobian is the derivative of FK with respect to the parameters. FIG 5 is a minimal version: you secretly add a few millimetres of manufacturing error to the UR5 (the hidden "real robot"), see how this error turns into tool deviation, and then let Gauss–Newton recover the errors from 20 measured postures.
- 只把 \(\delta a_2\) 设成 \(5\) mm(其余为 0)。工具偏差是多少?换几个姿态看看:偏差总是正好 5 mm,为什么?(提示:连杆长度误差只是把它下游的所有坐标系沿 \(x_2\) 平移 \(\delta a_2\)。)对比下一项的零点误差,它的影响随姿态变化。Set only \(\delta a_2\) to \(5\) mm (the rest 0). How large is the tool deviation? Try several postures: it is always exactly 5 mm. Why? (Hint: a link-length error just shifts every downstream frame by \(\delta a_2\) along \(x_2\).) Compare with the zero-offset error in the next task, whose effect changes with the posture.
- 只给关节 2 一个 \(0.3^\circ\) 的零点误差。末端偏差有多大?(\(0.3^\circ\approx0.0052\) rad,乘以关节 2 到工具的距离,约 \(0.8\) m。)Give joint 2 only a \(0.3^\circ\) zero-offset error. How large is the tool deviation? (\(0.3^\circ\approx0.0052\) rad, times the distance from joint 2 to the tool, about \(0.8\) m.)
- 设定几项误差,点"标定"。残差平方根 (RMS) 经过几次迭代降到多少?加上 \(0.5\) mm 的测量噪声再试一次:标定后的验证误差还能到 0 吗?Set a few errors and press "Calibrate". After how many iterations does the RMS residual drop, and to what? Add \(0.5\) mm of measurement noise and try again: can the validation error still reach 0 after calibration?
厂商标定。UR 机械臂出厂时每台都做过运动学标定,修正值(相对标准 DH 的 \(\delta a,\delta d,\delta\alpha,\delta\theta\))存在控制器里,ROS 的 ur_calibration 可以把它导出来给 URDF 使用。ROS / tf。robot_state_publisher 读 URDF 和 joint_states,每个周期做一遍 FK,把所有连杆的位姿发布成 tf 树。库。Pinocchio、KDL、MoveIt 的 RobotModel 与 Drake 都从 URDF 构造运动树,用"固定变换 × 关节变换"递推 FK,一台 6 自由度臂的 FK 只要大约一微秒量级,所以规划器每秒可以调用百万次。学习。可微分 FK 层(如 PyTorch 的 pytorch_kinematics)把 \(\boldsymbol\theta\mapsto T(\boldsymbol\theta)\) 做成网络里的一层,使末端位姿、关键点(夹爪指尖、肘部)的损失可以直接对关节角求导,用于模仿学习的损失、逆运动学网络和人体到机器人的动作重定向。标定学。PoE 形式的参数在辨识时没有平行轴的奇点,所以被用于运动学标定(Okamura & Park, 1996)。
Factory calibration. Every UR arm is kinematically calibrated at the factory; the corrections (\(\delta a,\delta d,\delta\alpha,\delta\theta\) relative to the nominal DH) are stored in the controller, and ROS's ur_calibration can export them for use in a URDF. ROS / tf. robot_state_publisher reads the URDF and joint_states, runs FK every cycle and publishes all link poses as the tf tree. Libraries. Pinocchio, KDL, MoveIt's RobotModel and Drake build a kinematic tree from the URDF and evaluate FK by the recursion "fixed transform × joint transform"; for a 6-DOF arm FK takes on the order of a microsecond, so a planner can call it millions of times per second. Learning. Differentiable FK layers (for example PyTorch's pytorch_kinematics) make \(\boldsymbol\theta\mapsto T(\boldsymbol\theta)\) a layer inside a network, so losses on the end-effector pose or on keypoints (fingertips, elbow) are differentiable with respect to the joint angles; this is used in imitation-learning losses, IK networks and human-to-robot motion retargeting. Calibration research. PoE parameters have no parallel-axis singularity during identification, which is why they are used for kinematic calibration (Okamura & Park, 1996).
| 标准 DHStandard DH | 改进 DHModified DH | URDF | PoE | |
|---|---|---|---|---|
| 每个关节的数据Data per joint | \((\theta,d,a,\alpha)\) | \((\alpha_{i-1},a_{i-1},\theta,d)\) | \(O_i\)(6 个数)+ 轴向量\(O_i\) (6 numbers) + axis vector | \(S_i\)(6 个数,4 个自由度)\(S_i\) (6 numbers, 4 dof) |
| 需要的坐标系Frames needed | 每连杆一个,按规则放one per link, placed by rule | 每连杆一个,按规则放one per link, placed by rule | 每连杆一个,随意放one per link, any placement | 只有世界系和 \(M\)only world frame and \(M\) |
| 平行轴Parallel axes | \(d\) 自由;邻近病态\(d\) free; ill-conditioned nearby | 同左(加 \(\beta\) 修正)same (fix by adding \(\beta\)) | 无问题no problem | 无问题no problem |
| 改工具 / 改基座Change tool / base | 右 / 左乘一个矩阵multiply one matrix on the right / left | 同左same | 加一个固定关节 / 改根连杆add a fixed joint / root link | 只改 \(M\) / 所有 \(S_i\) 与 \(M\)only \(M\) / all \(S_i\) and \(M\) |
| 常见于Typically found in | UR 文档、Spong、Siciliano、很多老代码UR documentation, Spong, Siciliano, a lot of legacy code | Franka 文档、CraigFranka documentation, Craig | ROS、Gazebo、Pinocchio、Drake(MuJoCo 的 MJCF 结构类似)ROS, Gazebo, Pinocchio, Drake (MuJoCo's MJCF is similar) | Modern Robotics、标定论文Modern Robotics, calibration papers |
| 哪里最方便Where it shines | 紧凑的参数表,手算compact tables, hand computation | 同左,关节坐标系贴着关节same, frame sits on the joint | 带几何、惯量的完整模型full models with geometry and inertia | 雅可比、标定、理论推导Jacobians, calibration, theory |
7常见误区Common pitfalls
把 Franka 的表(改进 DH)塞进标准 DH 的公式,程序照样跑,输出的也是一个合法的 \(4\times4\) 位姿,只是不对。两者的差别有两层:乘法顺序(\(\mathrm{Rot}_z\mathrm{Trans}_z\mathrm{Trans}_x\mathrm{Rot}_x\) 对 \(\mathrm{Rot}_x\mathrm{Trans}_x\mathrm{Rot}_z\mathrm{Trans}_z\))和 下标(\(a_i\) 对 \(a_{i-1}\))。不会有任何异常,旋转矩阵仍然正交,位置的量级也合理,所以不容易一眼看出来。防范办法:先问文档用的是哪种约定;然后在至少两个非零位形下,和厂商给出的 FK 值(或仿真器)对一遍。
Feed Franka's table (modified DH) into the standard-DH formula and the program runs and returns a plausible-looking \(4\times4\) pose, which is wrong. The two differ in two ways: the multiplication order (\(\mathrm{Rot}_z\mathrm{Trans}_z\mathrm{Trans}_x\mathrm{Rot}_x\) versus \(\mathrm{Rot}_x\mathrm{Trans}_x\mathrm{Rot}_z\mathrm{Trans}_z\)) and the indices (\(a_i\) versus \(a_{i-1}\)). No exception is raised, the rotation matrix is still orthogonal and the position has a sensible magnitude, so it is hard to notice by eye. Defence: first ask which convention the documentation uses; then compare against the vendor's FK values (or a simulator) in at least two non-zero configurations.
UR5 的 \(\boldsymbol\theta=0\) 是手臂向 \(-x\) 方向水平伸直;有的机器人把"直立"当零位;很多 DH 表里写着 \(\theta_2=q_2-90^\circ\) 这类零偏,需要并进 \(\hat A_i\)(第 5 节)。基座系也不统一:ROS 的 ur_description 里 base_link 与 UR 控制器文档里的 base 差一个绕 \(z\) 的 \(\pi\) 旋转,两边的 FK 位置 \(x,y\) 正好差一个符号。症状:手算对了,放到机器人上镜像或偏转 \(180^\circ\)。
The UR5's \(\boldsymbol\theta=0\) has the arm stretched horizontally along \(-x\); some robots call "upright" the zero pose; many DH tables contain zero offsets such as \(\theta_2=q_2-90^\circ\), which must be absorbed into \(\hat A_i\) (Section 5). Base frames are not uniform either: in ROS's ur_description, base_link differs from the base of the UR controller documentation by a rotation of \(\pi\) about \(z\), so the \(x,y\) of the FK position differ by a sign. Symptom: the hand calculation is right, and on the robot the result is mirrored or turned by \(180^\circ\).
\(\alpha_i\) 的方向是绕 \(x_i\) 用右手定则从 \(z_{i-1}\) 转到 \(z_i\)。\(\alpha=+90^\circ\) 和 \(-90^\circ\) 让 \(z_i\) 指向相反的方向,是两台不同的手臂。UR5 的表里 \(\alpha_4=+\pi/2\) 而 \(\alpha_5=-\pi/2\),抄错一个符号,腕部就扭向另一边。另外,UR 的表是弧度;把 90 当弧度传给 sin/cos,得到的是一个完全不相关的角(\(90\) rad \(\approx 5157^\circ\),去掉整圈后约 \(117^\circ\)):程序不会报错,只是 FK 全错。FIG 1 里把 \(\alpha\) 从 \(+90^\circ\) 翻成 \(-90^\circ\),看 \(z_i\) 指向哪个方向。
\(\alpha_i\) is measured about \(x_i\) by the right-hand rule from \(z_{i-1}\) to \(z_i\). \(\alpha=+90^\circ\) and \(-90^\circ\) point \(z_i\) in opposite directions and are two different arms. In the UR5 table \(\alpha_4=+\pi/2\) but \(\alpha_5=-\pi/2\); copy one sign wrongly and the wrist twists the other way. Also, UR's table is in radians; pass 90 as radians to sin/cos and you get an unrelated angle (\(90\) rad is about \(5157^\circ\), which is \(117^\circ\) after removing whole turns): no error is raised, FK is simply wrong. In FIG 1 flip \(\alpha\) from \(+90^\circ\) to \(-90^\circ\) and see where \(z_i\) points.
DH 的因子是"相对前一个坐标系",T = A1 @ A2 @ ... @ A6;PoE 空间形式的因子是"相对世界系",T = E1 @ E2 @ ... @ E6 @ M,其中 \(M\) 在最右边。把 DH 的 \(\hat A_i\) 转成 \(S_i\) 时漏掉共轭,或者把 \(M\) 放到左边,在 \(\boldsymbol\theta=0\) 处都对(所有 \(\Exp\) 是单位阵),非零位形才露馅(5.10 节误区 2)。所以任何 FK 实现都要有一个非零位形的单元测试,FIG 2 读数里的两者之差就是这样的测试。
DH factors are "relative to the previous frame": T = A1 @ A2 @ ... @ A6; PoE space-form factors are "relative to the world": T = E1 @ E2 @ ... @ E6 @ M, with \(M\) on the far right. Forget the conjugation when converting \(\hat A_i\) into \(S_i\), or put \(M\) on the left, and the result is still right at \(\boldsymbol\theta=0\) (every \(\Exp\) is the identity); it fails only in non-zero configurations (Pitfall 2 of Lesson 5.10). So every FK implementation needs a unit test at a non-zero configuration; the difference in the FIG 2 readout is exactly such a test.
UR5 的 \(a_2=-0.425\) 为负,并不是"连杆长度是负的",只是 \(x_2\) 朝了相反的方向;平行轴之间的 \(d\) 可以随便挪动;近平行时 \(d\) 是一个大数(FIG 3)。这些数是建系约定的产物,不是你能用尺子量的东西。做标定时,用普通 DH 去估计近平行轴的参数会病态(Hayati & Mirmirani 提出过给平行轴多加一个绕 \(y\) 的转角 \(\beta\));PoE 或 URDF 的参数没有这个问题。
The UR5's \(a_2=-0.425\) is negative not because "the link length is negative" but because \(x_2\) points the other way; the \(d\) between parallel axes can be slid at will; for near-parallel axes \(d\) is a large number (FIG 3). These numbers are products of the frame-placement convention, not things you can measure with a ruler. In calibration, estimating the parameters of near-parallel axes with plain DH is ill-conditioned (Hayati & Mirmirani proposed adding one more rotation \(\beta\) about \(y\) for parallel axes); PoE or URDF parameters do not have this problem.
8小练习Check yourself
9延伸阅读Further reading
- J. Denavit & R. S. Hartenberg, "A kinematic notation for lower-pair mechanisms based on matrices", ASME Journal of Applied Mechanics 22(2):215–221, 1955 — DH 参数的原始论文the original paper on DH parameters
- J. J. Craig, Introduction to Robotics: Mechanics and Control, Ch. 3 — 改进 DH 的约定,建系步骤有大量例题the modified-DH convention, with many worked frame-assignment examples
- K. Lynch & F. Park, Modern Robotics, Ch. 4 (Forward Kinematics) and Appendix C — PoE、URDF,以及 DH 参数与 PoE 的关系PoE, URDF, and the relation between DH parameters and PoE
- M. W. Spong, S. Hutchinson, M. Vidyasagar, Robot Modeling and Control, Ch. 3 — 标准 DH 的建系规则(本课的约定)the standard-DH frame-assignment rules (the convention used in this lesson)
- B. Siciliano, L. Sciavicco, L. Villani, G. Oriolo, Robotics: Modelling, Planning and Control, Ch. 2 — 运动学与 DH 的完整处理a complete treatment of kinematics and DH
- S. Hayati & M. Mirmirani, "Improving the absolute positioning accuracy of robot manipulators", Journal of Robotic Systems 2(4):397–413, 1985 — 指出平行轴会让 DH 标定病态,并提出多一个转角的修正points out that parallel axes make DH calibration ill-conditioned and proposes the extra-rotation fix
- F. C. Park, "Computational aspects of the product-of-exponentials formula for robot manipulators", IEEE Transactions on Automatic Control 39(3):643–647, 1994 — PoE 在 FK 与雅可比计算上的效率computational efficiency of PoE for FK and Jacobians
- A. M. Okamura & F. C. Park, "Kinematic calibration using the product of exponentials formula", Robotica 14(4):415–421, 1996 — 用 PoE 做标定calibration with PoE
- R. Murray, Z. Li, S. Sastry, A Mathematical Introduction to Robotic Manipulation, Ch. 3 — 螺旋理论下的正运动学forward kinematics via screw theory