机器人学入门:从旋转矩阵到DH参数的机械臂运动学详解
做了两年机器视觉和自动化集成之后我最怕被客户问的问题不是相机标定而是机械臂本身你们那个工具坐标系的四点法标定底层原理是什么机械臂在奇异点附近为什么会突然抖一下这两个问题当场把我问住了。于是才有了这份《机器人学导论学习笔记》。它是我从零开始系统啃教材的完整记录目标是让每一条数学公式都落在可运行的代码和真实的调试场景里不搞“背完就忘”的那一套。这篇先写学习路线规划、三维空间刚体变换以及机械臂建模最核心的DH参数法。适合正在入门机器人学的学生也适合像我这样半路出家的自动化工程师参考。如果你只想快速“调包”跑通正解可以直接跳到第4章但如果你想弄懂坐标系变换的本质我建议从第2章顺着读因为后面所有逆解、动力学的内容都会反复用到这里面的矩阵运算习惯。1. 为什么我决定系统啃一遍机器人学——三个被现实逼出来的理由1.1 做了两年代码才知道机器人工程师的护城河不在ROS我入行做的是机器视觉引导日常就是标定、抓取、定位代码写了一堆ROS跑得也算熟。可有一次现场调试时工程师直接问我“机械臂从A点走到B点路径规划时为什么要插值关节空间和笛卡尔空间插值出来的轨迹有什么本质区别”我答得支支吾吾最后只能说“上位机里是这么设的”。这件事让我意识到光会调用官方的运动学接口不叫会机器人。真正拉开差距的是能不能从数学上回答“机械臂是怎么知道自己在哪的”这个问题。机器人学的核心不是某个软件框架而是一套描述空间运动、建立连杆模型、求解关节角度的数学语言。不懂这套语言换个品牌机械臂、换套SDK你还是得重新学一遍。1.2 学习资料怎么选教材为主视频为辅确定了学习方向接下来就是选资料。市面上的课程和书籍五花八门我踩了不少回头路之后形成了一套组合策略。一本体系完整的教材我选的是国内高校用得最多的那本经典教材从头到尾啃重点看刚体变换、正逆运动学、雅可比、动力学这几章。一个可视化程度高的开源库我用的是matplotlib加numpy自己画坐标轴能做三维旋转和机械臂连杆图就够用了。后面进阶可以考虑一些工业级仿真平台。一两套高校公开课视频视频不是为了替代教材而是为了在卡壳的时候换个老师换个说法帮我把概念“翻译”过来。我不建议一上来就看零散的短视频那只能缓解焦虑不能搭建体系。真正的学习路径应该是教材给骨架代码给肌肉现场项目给灵魂。1.3 给自己立下的三条学习军规为了更好地“持续更新”这套笔记我给自己定了三条规矩每学完一个公式必须用numpy写成可运行的代码在Jupyter里亲手跑一遍不然等于没学。每篇笔记必须回答一个“为什么”为什么旋转矩阵是正交的为什么DH表能唯一描述连杆这些“为什么”才是学习的富矿。每章结尾要留一个“坑位”写下我还不确定、准备下一阶段解决的点作为下一篇笔记的引子。这样做的效果很明显。以前我看变换矩阵就是“四个角上的数”现在我能闭着眼写出它的结构以前看机械臂是“一堆铁”现在我能一眼看出它的每个关节是转动副还是移动副。这就是体系化学习的价值。2. 数学地基旋转矩阵不是矩阵是说话方式2.1 为什么第一个拦路虎是坐标系机器人学里几乎所有问题都绕不开坐标系。机械臂末端在哪里不是问它“离我多远”而是问它在“哪个坐标系下”、以谁的视角来看。这就是机器人学最基本的世界观你永远先要问一句这个位置和姿态是相对于谁描述的。你可以这样理解坐标系就像一把尺子我在北京和在上海量同一个目的地读数是不同的但物理位置完全一致。机器人身上每个连杆都自带一把“尺子”也就是局部坐标系而我们要做的事情就是不断在不同尺子之间换算。这个换算的数学工具就是旋转矩阵和齐次变换矩阵。理解了这一点后面的内容就顺了你学的不是矩阵而是一种“翻译工具”把A坐标系里的一句话翻译成B坐标系里的一句等价表述。2.2 三个基本旋转矩阵的手写与代码实现绕x、y、z三个轴的纯旋转是机器人学的“乘法口诀”。它们的矩阵形式并不复杂但我强烈建议你亲手推一遍而不是背公式。绕z轴旋转的角度记为θ旋转矩阵长这样R_z(θ) [ cosθ -sinθ 0 ] [ sinθ cosθ 0 ] [ 0 0 1 ]绕x轴和绕y轴的矩阵构造方式类似只是对应行和列的位置不同。用numpy实现我习惯写三个小函数方便后面反复调用import numpy as np def rot_x(theta): c, s np.cos(theta), np.sin(theta) return np.array([ [1, 0, 0], [0, c, -s], [0, s, c] ]) def rot_y(theta): c, s np.cos(theta), np.sin(theta) return np.array([ [ c, 0, s], [ 0, 1, 0], [-s, 0, c] ]) def rot_z(theta): c, s np.cos(theta), np.sin(theta) return np.array([ [c, -s, 0], [s, c, 0], [0, 0, 1] ])注意这里的正负号位置决定了旋转方向如果你发现自己算出来的结果和直觉相反多半是右手定则的符号没对齐。我一开始就在rot_y上栽了跟头调试了半小时才发现是sin项的符号写反了。2.3 旋转矩阵的四个关键性质掌握了基本构造方法更重要的是理解旋转矩阵的性质因为后面推导逆运动学时会反复用到每一列都是单位向量相当于局部坐标系的三个轴在参考坐标系里的“方向余弦”。矩阵是正交的即R^T * R I所有列向量两两垂直且模长为1。行列式等于1它表示这是一个本征旋转不会翻转空间。逆矩阵等于转置矩阵R^-1 R^T这是后面最常用的“白嫖”技巧。我特别想强调最后一点。求逆向运动学时如果你要把一个末端坐标系的描述变换回基座坐标系直接对变换矩阵求逆当然可以但数字上容易慢语义上也不够直观。利用转置的性质一个姿态矩阵的逆就是一个转置速度和清爽程度完全不一样。还有一点要注意多个旋转矩阵相乘的顺序不能乱。绕固定轴旋转相当于左边的矩阵不断“叠”上去绕自身轴旋转右边的矩阵作用在最新坐标系上。这个区别我在下一节展开说因为它是新手最容易踩的隐藏坑。3. 从旋转到位姿刚体变换的完整描述3.1 齐次变换矩阵的动机把旋转和平移放进一个框纯旋转只能描述姿态但机械臂末端不光有姿态还有位置。假如坐标系A的原点到坐标系B的原点有一个偏移向量p那么描述B系相对A系的完整关系就要同时记录旋转R和平移p。于是人们发明了齐次变换矩阵它是一个4x4的结构T [ R p ] [ 0 1 ]其中左上角3x3是旋转矩阵右上角3x1是平移向量底行固定为[0, 0, 0, 1]。用4维齐次坐标表示三维点好处有两个一是旋转和平移可以用同一个矩阵乘法完成不需要分开算两次二是多个连杆的位姿关系可以通过矩阵连乘一层层传递下去这是机械臂运动学的基本计算框架。我记得第一次看到这个4x4矩阵时觉得底行很“碍眼”。后来才明白这其实是把仿射变换统一成线性变换的一种数学技巧。就像用“借位”让减法变成加法一样齐次坐标让平移也拥有了“矩阵乘”的资格。3.2 变换矩阵的结构复现一行代码看穿所有信息拿到一个已知的4x4变换矩阵怎么快速读懂它我推荐一个拆解习惯把左上角3x3旋转矩阵的每一列分别命名为主轴n、滑轴s、接近轴a这就是很多教材里说的“n-s-a”表示法。第一列n末端坐标系的x轴在参考坐标系下的方向第二列s末端坐标系的y轴在参考坐标系下的方向第三列a末端坐标系的z轴在参考坐标系下的方向第四列p末端坐标系原点在参考坐标系下的位置这套命名来自抓取场景a轴总是朝“接近”物体的方向s轴是“滑动”方向n轴是“法向”方向。理解了这套语义你看任何工业机器人给的位姿矩阵就能在脑中直接“看见”它的空间形态。这个习惯对调试坐标系偏移非常有帮助。比如客户说“工具坐标系转了一下”你去看矩阵的前三列方向余弦马上就知道它绕哪个轴转了、转了多少而不是只盯着数值发呆。3.3 求逆变换的快捷方法转置平移不用算逆实际开发中我们经常需要描述一个相对变换的“反向”已知末端系相对基座系是T求基座系相对末端系是多少如果直接对4x4矩阵求逆也可以但数值求解不仅慢还会累积浮点误差。利用旋转矩阵的正交性这里有一个非常优雅的快捷方式T^-1 [ R^T -R^T * p ] [ 0 1 ]思路是旋转部分直接用转置平移部分多加一个负号但负号不能直接放在p上而是要把p先经过R^T变换再取负。用代码实现如下def inverse_transform(T): R T[:3, :3] p T[:3, 3] inv_R R.T inv_p -inv_R p T_inv np.eye(4) T_inv[:3, :3] inv_R T_inv[:3, 3] inv_p return T_inv这个函数的正确性本质上就是齐次变换矩阵的逆运算规则先把姿态换一个“说法”再把平移量搬到对方的坐标系里重新表达。用熟悉了以后矩阵求逆这件事在机器人学里基本就变成“转置加平移负号”了。4. 机械臂运动学的关键一步DH参数法4.1 DH参数到底在干什么当你面对一台六轴机械臂光看实物是不可能直接写出正运动学方程的。我们需要一种“把机械臂翻译成一张表”的方法这张表上的每一行对应一个关节每一行由四个数字组成这就是DH参数。DH参数包含四个量a连杆长度相邻两关节轴之间的公垂线长度一般是沿当前x轴方向的距离alpha连杆扭转角相邻两关节轴之间的夹角绕当前x轴旋转d连杆偏距沿当前z轴方向的距离theta关节角绕当前z轴旋转的角度对于转动关节theta是变量对于移动关节d是变量。剩下两个参数由机械结构决定是固定的。通过这套建模方式任何复杂的机械臂结构都能拆成一串“绕z转、沿x平移、绕x转”的标准动作。我记得第一次看到这张表时完全懵了为什么是这四个量为什么顺序这么别扭后来自己动手建模一次才明白DH参数的实质是让每个连杆坐标系之间的变换被拆成四次足够简单的运动每一次都只绕一个轴或沿一个轴运动这样数学推导就极大简化了。4.2 从一个DH表写出变换矩阵的通用模板给定某一行DH参数[a, alpha, d, theta]从上一个坐标系到当前坐标系的齐次变换矩阵可以统一写成def dh_matrix(a, alpha, d, theta): ct, st np.cos(theta), np.sin(theta) ca, sa np.cos(alpha), np.sin(alpha) return np.array([ [ct, -st*ca, st*sa, a*ct], [st, ct*ca, -ct*sa, a*st], [0, sa, ca, d], [0, 0, 0, 1] ])只要把每个关节的四个参数填进这个模板再把所有矩阵连乘就得到了末端坐标系相对基座坐标系的总变换矩阵。整个过程就是一种“搭积木”每一块积木一模一样只是参数不同。4.3 二自由度机械臂的正运动学实例纸上谈兵不如来点实的。我们用上面的函数算一个平面两连杆机械臂的末端位置。假设连杆长度分别是l1 1.0、l2 0.8关节一角度45度关节二角度负30度l1, l2 1.0, 0.8 theta1 np.radians(45) theta2 np.radians(-30) T01 dh_matrix(l1, 0.0, 0.0, theta1) T12 dh_matrix(l2, 0.0, 0.0, theta2) T02 T01 T12 pos T02[:3, 3] print(末端位置:, pos) x l1*np.cos(theta1) l2*np.cos(theta1theta2) y l1*np.sin(theta1) l2*np.sin(theta1theta2) print(几何法验证: x , x, y , y)你会发现矩阵连乘的结果和高中几何算出来的平面坐标完全一致。这个验证很值得亲手做一次它能让你真正相信“DH表加矩阵乘法”这套流程是可靠的后面扩展到六轴时你的信心就是从这种小实验里攒出来的。我把这个例子打印出来贴在工位挡板上。每次调机械臂的时候看到这张表都会提醒自己任何复杂的六轴运动学本质上不过是这张表的延长。4.4 从正解到可视化让自己看见坐标系只算数字还不够直观。我建议你在Jupyter里画一下基座坐标系、关节一坐标系、关节二坐标系以及末端工具坐标系的坐标轴能看到三个坐标系层层套嵌、随关节角度变化而平移旋转的效果。用matplotlib的quiver画箭头即可核心代码不复杂def draw_frame(ax, T, length0.3, label): origin T[:3, 3] axes T[:3, :3] * length colors [r, g, b] labels [x, y, z] for i in range(3): ax.quiver(origin[0], origin[1], origin[2], axes[i, 0], axes[i, 1], axes[i, 2], colorcolors[i], arrow_length_ratio0.1)三个坐标轴的箭头代表末端所在的参考方向看到它随theta1、theta2变化而转动时你对“姿态”的直觉会变得非常具体。这在后面学逆运动学时能帮你快速理解多解现象同一个末端位置可以有肘部朝上和肘部朝下两种姿态。5. 学习路上踩过的坑和认真对比过的资料5.1 最大的坑绕固定轴和绕自身轴的“左乘右乘”陷阱很多人在旋转矩阵这一章就劝退了大部分原因是没弄清楚左乘和右乘的区别。我以前也在这上面吃过大亏。如果每次旋转都绕“世界坐标系”的固定轴进行新矩阵要写在左边也就是R R_z * R_y * R_x。如果每次旋转都绕“当前已变换的坐标系”自身轴进行新矩阵要写在右边也就是R R_x * R_y * R_z。这两种写法最后得到的结果通常不一样。我一开始做欧拉角分解时总是习惯性把所有矩阵从右往左乘结果算出来的姿态永远和示教器上的读数对不上。后来把一堆打印出来的矩阵一条条核对才发现问题就出在“绕固定轴”和“绕自身轴”的顺序反了。建议你现在就在代码里做个实验拿三个角度去算两种顺序的乘积然后把得到的矩阵打印出来对比一下。你大概率会发现它们差得很远。这个坑越早踩后面学逆解越顺利。5.2 不同教材和资料里DH坐标系建立的差异这里必须要提醒一下市面上关于DH参数存在两种主流约定一种是标准DH一种是改进型DH。它们的共同点是都叫DH但坐标系的附着位置和参数定义有细微差别尤其是alpha和d的解释不完全一样。我一开始混合看教材和公开课结果用A教材的公式去对B课程的例题算出来的矩阵怎么都对不上。后来才意识到自己下意识混用了两种约定。我的建议是入门阶段只盯住一种约定用到底不要同时开两本不同约定的教材。等把正运动学完全吃透了再回头看看另一种约定是怎么一回事你会发现它们的差异其实只是在坐标系的建立习惯上核心思想一致。5.3 学习工具与环境的取舍我最终选定的学习环境是Jupyter Lab numpy matplotlib的组合轻量、免费、能边写边画。以下是我摸索出的分工numpy负责所有矩阵运算它是运动学计算的主力。matplotlib负责把坐标轴、机构简图画出来直观反馈公式是否正确。Jupyter Lab方便把笔记和代码揉在一起真正做到“文档即实验”。不建议一开始就去折腾大型仿真软件。那种重型工具的建模流程本身就有学习成本很容易让你迷失在软件操作里反而不利于理解运动学本质。先用最简单的工具把数学吃透等需要做仿真验证时再去上重型工具收益要高得多。我还有个习惯每个函数旁边都写一段验证注释输入一组已知角度手动算一遍预期结果再用程序跑一遍比对。这种“自查式笔记”虽然写起来慢但效果扎实。5.4 笔记、代码、仿真三条腿走路的意义“持续更新”这四个字重点不在更新而在“可持续”。我发现光是看书抄公式第二天就忘了光是抄别人的代码调出来也不会讲原理。真正能留下来的学习是笔记、代码、仿真三者互为验证的。笔记负责记录推导逻辑代码负责验证正确性仿真负责建立空间直觉。这三样缺一个学起来都很容易断片。比如我写逆运动学笔记时如果只推导公式没有在仿真里拖动关节角观察末端位置变化我对多解和奇异点的理解就会停留在字面上。所以后续每篇笔记我都准备按这个三件套来写。这也算是给自己持续更新立的一个框架。6. 后续更新规划留给逆解、雅可比和动力学的坑位6.1 逆运动学从末端位姿反推关节角的几种路线正运动学是从关节角求位姿逆运动学刚好反过来。六轴机械臂的逆解比二轴的复杂很多但它才是实际应用里最常用的计算。我目前理解的解法分两条线解析法和数值法。解析法通过几何关系直接求出所有可行解速度快、可重复但推导复杂数值法用迭代逼近通用性强但存在初值敏感的问题容易收敛到局部解。下一篇笔记我计划重点整理解析法中比较经典的“腕部解耦”思路即把六轴分成前三轴定位置、后三轴定姿态的结构来求解。对于球腕结构这种方法能极大简化计算。到时候我会把六轴球腕机械臂的逆解算法完整跑一遍并用仿真验证是否还原初始关节角。6.2 微分运动学与雅可比矩阵让“速度”成为可计算的量关节空间和笛卡尔空间的速度映射关系靠的就是雅可比矩阵。它对标定、奇异点分析、力控制都有影响。我目前的朴素理解是雅可比矩阵描述了每个关节速度对末端线速度和角速度的贡献。当这个矩阵的秩下降时机械臂就会进入奇异位形末端在某些方向上失去运动能力这就是为什么奇异点附近机械臂会“抖”。这块内容我希望通过数值微分来验证解析雅可比的计算结果再做一个简单的二连杆奇异点可视化实验把“秩下降”这个抽象概念变成一眼可见的现象。6.3 动力学拉格朗日还是牛顿-欧拉动力学是机器人学的深水区。拉格朗日法从能量角度出发推导过程清晰优雅牛顿-欧拉法从受力分析出发递推计算效率更高更适合实时控制。我的计划是先吃透拉格朗日法对二连杆模型的应用把动能、势能、广义力的推导一步步写清楚再用数值仿真跟现有代码的结果做对比。这一步如果想深入可能还要补一些矩阵微积分的基础我已经把它列进后续几篇笔记的“前置技能”清单。6.4 和我一样在自学的人可以怎么安排节奏最后结合我自己的节奏给你一个可以直接抄的路线图第1-2周搞透刚体变换每天写三个旋转矩阵抄写齐次变换矩阵的逆运算。第3-4周建立一至两个连杆的DH表熟练写正运动学代码。第5-6周做逆运动学至少实现一种解析解和一种数值解法。第7-8周接触雅可比矩阵把奇异点的几何意义弄明白。后面的时间按需进入动力学、轨迹规划、控制相关章节。一方面要把公式变成代码跑起来另一方面要时刻问自己“这个变量到底在描述哪个坐标系的事情”。只要这两个习惯保持住机器人学这门课真的不难啃。我现在再看客户现场那个“四点法标定工具坐标系”脑子里终于不是一片空白了。它本质上就是在求解一个齐次变换矩阵拿四个不共面的参考点做约束把工具末端相对法兰坐标系的位置和姿态标出来。这个道理没想通之前做一百遍标定也是程式化操作想通之后连机械臂突然“抽风”的方向都能预判了。这篇笔记先写到这。下一篇我会动手做逆运动学到时候把六轴球腕的求解步骤和仿真验证一起放上来继续补上“从正解到逆解”这段最关键的桥梁。