
简介现代机器人学Modern Robotics: Mechanics, Planning, and Control的课后习题答案面向机器人专业学生、工程师与自学者旨在帮助读者系统掌握机械、运动规划与控制等核心知识。题解覆盖第2章至第13章从运动学与自由度计算出发逐步深入到机构分析、动力学、路径规划和控制理论比如对多关节上肢机构的自由度、约束条件、关节运动关系等均给出了逐步推导和算例便于对照原教材查漏补缺。资源包共1个PDF文件大小13.97MB内容紧凑适合电子设备阅读或打印学习。目前已吸引1245人学习浏览可见其在机器人学习群体中的实用价值。读者通过研读这些习题解答不仅能验证自己的解题思路还能将机械设计、运动学与控制策略串联起来为实际机器人建模和控制系统设计打下扎实基础。1. “现代机器人学 课后习题答案”搜出来的东西为什么多数是坑“现代机器人学 课后习题答案”这个关键词背后是一批正在啃《Modern Robotics: Mechanics, Planning, and Control》的人。你以为能搜到一份完整答案 PDF实际搜到的多是零散截图、过时文档和互相对不上的数字。这本书的作者团队没有公开过标准答案簿他们把大量数值计算题直接做成了可判分的编程作业判分系统就是答案本身。所以更可靠的路径是把配套代码库跑起来自己算出答案、自己验证正确性。这篇文章就把这条路径拆开讲从环境搭建到三类高频题目的可复现代码再到提交前容易踩的坑。适合正在读这本书、卡在课后数值题上的人也适合想搞懂 POE 公式和库函数怎么配合的人。2. 为什么这本书不给你标准答案POE 表述与课程判分的设计逻辑这本书和传统机器人学教材最大的区别是它不用 DH 参数表建立运动学而是用螺旋轴加指数积POE公式。课后习题里的计算题答案不是某个表达式而是一串有物理意义的数值。这些数值要你自己编程求出才符合这门课的训练目标。理解了这套设计逻辑你就明白为什么“搜答案”天然走不通而自己跑代码才是正路。2.1 课后题目的真实形态理论题之外全是数值题《现代机器人学》每章后面的习题分成两类。一类是理论题需要推导公式、证明性质这类题的答案在网上能找到不少讨论因为它是纸笔功夫。另一类是数值计算题题目给出一组机械臂参数螺旋轴、零位形、质量参数让你求末端位形、雅可比矩阵、关节力矩、动力学矩阵等具体数值。这类题目没有官方答案它们的“标准答案”只存在于课程作业的判分系统里。判分系统的逻辑很简单你提交题目要求的答案数据它计算你的答案和它的真值之间的误差误差小于一个阈值就算通过。也就是说你不需要写完整解题过程只要提交的数字对系统就认为你会做。这带来一个很实际的结论网上流传的那些“答案合集”很多是从课程不同期次的作业里抄出来的随机参数和你的题目参数根本对不上。与其花时间辨认二手答案不如用官方配套库把当前题目算出来。2.2 指数积公式为什么题目里给的是 Slist 而不是 DH 表这本书用螺旋轴描述每个关节的运动。一个旋转关节的螺旋轴是一个六维向量前三维是旋转轴单位方向后三维是线速度方向由旋转轴和轴线上某一点叉乘得到。整条机械臂的运动学参数就是一组螺旋轴组成的矩阵 Slist加上机械臂零位时末端坐标系相对基座坐标系的位形矩阵 M。给定关节角度向量末端位形由指数积公式算出来。这套表述和 DH 参数有个关键差异DH 表把每个关节的坐标系强绑定在连杆上参数含义繁琐螺旋轴则直接在基座坐标系里描述运动旋量不需要为每个关节建立中间坐标系。对编程求解来说POE 公式的代码实现极其规整就是一个矩阵指数乘以另一个矩阵指数循环连乘。这也是作者不提供标准答案的原因之一数值题本来就是让你自己写代码调用这些公式答案只是程序运行的输出。课后题里常见的参数形式有两种给空间螺旋轴 Slist或给物体螺旋轴 Blist。Slist 对应的末端位形是空间形式的 FKinSpaceBlist 对应物体形式的 FKinBody。两者算出来的最终位形矩阵 T 是一样的但中间过程不同。很多人在做题时搞混这两个函数提交答案差之毫厘这个坑我放到第 4 章专门讲。2.3 官方配套代码库一个 py 文件就能跑起来这本书的官方配套库在课程里以一套子程序的形式提供Python 版本就是一个名为 ModernRobotics_py_subroutines.py 的文件。它不依赖任何第三方机器人框架只需要 numpy。这个文件里包括了矩阵指数与对数、正运动学、雅可比、动力学、轨迹规划等所有核心函数。你在作业里用到的大概率就是这些函数判分系统本身也用同一套函数。我先演示一个最小自检导入库、构造一个两连杆平面臂、在零位调用正运动学确认输出等于题目给出的 M 矩阵。import numpy as np from ModernRobotics_py_subroutines import FKinSpace # 两连杆平面臂两个旋转关节旋转轴都沿 z 方向 M np.array([ [1, 0, 0, 0.3], # 零位时末端在 (0.3, 0, 0) [0, 1, 0, 0.0], [0, 0, 1, 0.0], [0, 0, 0, 1.0] ]) # 关节1轴线过原点关节2轴线过 (0.3, 0, 0)方向也是 z Slist np.array([ [0, 0, 1, 0, 0, 0], # 关节1的运动旋量 [0, 0, 1, 0, -0.3, 0] # 关节2的运动旋量 ]).T thetalist np.array([0.0, 0.0]) T FKinSpace(M, Slist, thetalist) print(T)这段代码的逻辑FKinSpace 输入零位位形 M、螺旋轴组 Slist、关节角向量 thetalist输出末端位形 T。当所有关节角为 0 时机械臂处于零位T 应该严格等于 M。这个自检能确认 Slist 的线速度分量方向没写反。Slist 里每个螺旋轴的三个线速度分量由旋转轴向量和轴线上一点的叉乘加负号得到方向写反是新手最高频的错误。跑完这段如果输出和 M 一致说明库环境和参数方向都没问题。这个子程序文件里我常用的核心函数大概这么几个后面做题基本围着它们转函数输入要点输出对应章节FKinSpace / FKinBodyM、螺旋轴组、关节角末端位形 T第4章 正运动学JacobianSpace / JacobianBody螺旋轴组、关节角6×n 雅可比第5章 速度学MatrixExp6 / MatrixLog6se(3) 六维向量 / 4×4 矩阵矩阵指数 / 对数第3章 刚体运动InverseDynamics关节角、速度、加速度、重力等关节力矩向量第8章 动力学MassMatrix关节角、惯性参数n×n 质量矩阵第8章 动力学这套函数的命名和返回格式在各版本间有过调整。你手里如果是课程提供的版本函数签名一般没问题如果是几年前的旧文件注意 FKinSpace 的参数顺序可能是反的。我统一按课程版本的签名写第 4 章避坑里会再提这个兼容性问题。3. 用 Python 把课后计算题算出数值答案三类题的完整代码明确了库函数之后剩下的事就是把题目参数填进对应函数。这一章我用一组结构常见的六关节机械臂参数演示完整流程。你做题时把原题给的实际 Slist、M 矩阵、惯性参数替换进代码函数一行都不用改。先说明下面这组参数是我自造的结构等价参数目的是把步骤跑通课程作业里的参数是随机化的直接抄网上别人的数值没有意义自己替换才是对的。3.1 正运动学题从 Slist 和 M 求末端位形课后第 4 章的计算题最常见的问法是已知各个关节的螺旋轴和末端零位形 M给定一组关节角求末端位形 T。我自造的六关节臂参数如下长度单位米。关节旋转轴方向轴线通过点关节类型1(0, 0, 1)(0, 0, 0)旋转2(0, 1, 0)(0, 0, 0.3)旋转3(0, 1, 0)(0, 0, 0.6)旋转4(1, 0, 0)(0, 0, 0.6)旋转5(0, 1, 0)(0, 0, 0.6)旋转6(0, 0, 1)(0, 0, 0.6)旋转这个结构前三关节决定腕部位置后三关节是交于一点的球腕。每个旋转螺旋轴的前三维就是旋转轴方向后三维由“负的旋转轴方向 叉乘 轴线通过点”得到。写成代码import numpy as np from ModernRobotics_py_subroutines import FKinSpace # 末端零位形姿态为单位阵位置 (0.3, 0, 0.9) M np.array([ [1, 0, 0, 0.3], [0, 1, 0, 0.0], [0, 0, 1, 0.9], [0, 0, 0, 1.0] ]) # 每个螺旋轴前三维是 omega后三维是 v Slist np.array([ [0, 0, 1, 0, 0, 0 ], # 关节1 [0, 1, 0, -0.3, 0, 0 ], # 关节2 [0, 1, 0, -0.6, 0, 0 ], # 关节3 [1, 0, 0, 0, -0.6, 0 ], # 关节4 [0, 1, 0, -0.6, 0, 0 ], # 关节5 [0, 0, 1, 0, 0, 0 ] # 关节6 ]).T thetalist np.array([0.2, -0.4, 0.3, 0.1, 0.5, -0.2]) T FKinSpace(M, Slist, thetalist) print(末端位形 T) print(np.round(T, 6))关节 4 的螺旋轴后三维为什么是 (0, -0.6, 0)因为旋转轴方向 (1, 0, 0) 叉乘通过点 (0, 0, 0.6) 得到 (0, -0.6, 0)再取负号就是 (0, -0.6, 0) 的相反数这里需要算一下(1,0,0) × (0,0,0.6) (00.6-00, 00-10.6, 10-00) (0, -0.6, 0)。螺旋轴的 v 分量是负叉乘也就是 (0, 0.6, 0)。但这个细节很容易写错最稳的验证方式就是我在 2.3 节里说的零位自检把 thetalist 全部置 0T 必须等于 M。如果不等说明至少有一个 v 分量方向反了。做完这步把 T 里的旋转矩阵和平移向量抄进作业系统。旋转矩阵九个数加平移三个数一共 12 个数值。提交前建议保留 6 位小数因为判分系统通常按 1e-3 到 1e-4 的容差判定保留位数太少可能被误判。3.2 速度学题求空间雅可比并用有限差分给它打分课后第 5 章的计算题常让你求某个位形下的空间雅可比矩阵。直接用 JacobianSpace 函数就能得到解析雅可比但你别只把它当黑匣子用。我一般会额外写一个数值雅可比做交叉验证两边对得上这个答案才敢提交。import numpy as np from ModernRobotics_py_subroutines import ( FKinSpace, JacobianSpace, MatrixLog6, se3ToVec, TransInv ) # 沿用 3.1 的 M 和 Slist M np.array([ [1, 0, 0, 0.3], [0, 1, 0, 0.0], [0, 0, 1, 0.9], [0, 0, 0, 1.0] ]) Slist np.array([ [0, 0, 1, 0, 0, 0 ], [0, 1, 0, -0.3, 0, 0 ], [0, 1, 0, -0.6, 0, 0 ], [1, 0, 0, 0, -0.6, 0 ], [0, 1, 0, -0.6, 0, 0 ], [0, 0, 1, 0, 0, 0 ] ]).T thetalist np.array([0.2, -0.4, 0.3, 0.1, 0.5, -0.2]) # 解析空间雅可比 J_ana JacobianSpace(Slist, thetalist) def numerical_jacobian_space(M, Slist, theta, h1e-6): n len(theta) J_num np.zeros((6, n)) for i in range(n): e np.zeros(n) e[i] h T_plus FKinSpace(M, Slist, theta e) T_minus FKinSpace(M, Slist, theta - e) # 相对位形的对数坐标近似等于 2h * V_s T_rel T_plus TransInv(T_minus) J_num[:, i] se3ToVec(MatrixLog6(T_rel)) / (2 * h) return J_num J_num numerical_jacobian_space(M, Slist, thetalist) err np.linalg.norm(J_num - J_ana, fro) print(解析雅可比与数值雅可比 Frobenius 误差, err)这段代码的中心思想雅可比矩阵的第 i 列就是第 i 个关节轴对末端速度的贡献。数值上让第 i 个关节角加一个小步长正运动学位形随之改变相邻两个位形之间的相对运动旋量除以步长就是这一列的估计。我用了中心差分而不是前向差分因为中心差分的精度是步长的平方量级前向差分只有线性量级。两个结果之间的 Frobenius 误差在 1e-5 以内都算正常。如果误差到了一两个数量级以上优先检查 Slist 的 v 分量方向它是运动学里最容易出错的地方。确认雅可比正确后作业里如果要求某一列或某个元素直接取对应值提交即可。3.3 动力学题逆动力学算力矩再用库的自洽性验证第 8 章的数值题通常是给定质量参数、关节运动轨迹求驱动力矩。我用一个二连杆平面臂来说明因为参数少、结论直观。这里的关键不是手推公式而是让库函数和我自己调用的方式互相验证。import numpy as np from ModernRobotics_py_subroutines import ( InverseDynamics, MassMatrix ) # 两连杆平面臂杆长 0.3m质量 1kg质心在杆中点 L 0.3 m 1.0 Izz m * L**2 / 12 # 细杆绕质心的转动惯量 # Mlist零位下相邻坐标系的变换这里把每个杆的坐标系原点放在质心 M01 np.array([[1, 0, 0, L/2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) M12 np.array([[1, 0, 0, L/2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) M23 np.eye(4) Mlist [M01, M12, M23] # Glist空间惯性矩阵参考点在质心且主轴对齐时的块对角形式 G1 np.diag([0, 0, Izz, m, m, 0]) # 杆绕 z 轴旋转 G2 np.diag([0, 0, Izz, m, m, 0]) Glist [G1, G2] # 螺旋轴两个关节都绕 z 方向 Slist np.array([ [0, 0, 1, 0, 0, 0], [0, 0, 1, 0, -L, 0] ]).T thetalist np.array([0.3, -0.5]) dthetalist np.array([0.5, 0.2]) ddthetalist np.array([1.0, 0.5]) g np.array([0, 0, -9.81]) # 重力沿 -z与 xy 平面垂直不做功 Ftip np.zeros(6) # 逆动力学求关节力矩 tau InverseDynamics(thetalist, dthetalist, ddthetalist, g, Ftip, Mlist, Glist, Slist) print(关节力矩, tau) # 自洽性验证速度、重力全部置零加速度只留第一关节 dd np.array([1.0, 0.0]) tau_test InverseDynamics(thetalist, np.zeros(2), dd, np.zeros(3), Ftip, Mlist, Glist, Slist) M_matrix MassMatrix(thetalist, Mlist, Glist, Slist) print(M dd 的第一种结果, M_matrix dd) print(InverseDynamics 直接结果, tau_test)这段代码最后几行做的是库函数之间的自洽性验证当速度为零、重力为零、末端力为零时动力学方程退化为 τ M(q) · ddq那么 InverseDynamics 的输出应该严格等于 MassMatrix 的输出乘以角加速度向量。如果这个等号不成立说明 Mlist、Glist 或 Slist 里有一个参数输错了。这种方法的好处是不需要自己手推公式就能排查掉绝大部分参数输入错误。动力学的数值题在作业里占分不低但它的难点恰恰在参数准备Glist 每个连杆六个自由度质心位置、惯性矩都要换算成空间惯性矩阵的形式。课程题目里一般会直接给你 Glist你只要保证顺序和 Mlist 对应一致就行。如果题目只给了质量、质心位置和惯性张量需要先手工组装成 6×6 矩阵这一步我建议你对照书上第 8 章的定义逐项确认组装错一项后面全错。4. 课后求解避坑指南最容易翻车的五个点数值题交了四五次都过不了多半不是数学问题而是版本、约定和容差问题。我把这些年见过的翻车现场汇总成五条每条都是现象、原因、解决的结构照着排查能省下大量时间。4.1 网上下载的“全题答案”为什么对不上不少读者会先找来一份参考答案做对照结果发现对方的数值和自己在作业里拿到的参数完全对不上。这不是算错而是这门课的数值参数针对不同期次会做随机化处理螺旋轴的通过点、零位位形、质量参数都可能在同一道题的不同版本里有微小差异。网上流传的答案截图往往是某一期作业的真值换一期就失效了。解决方法是只把网上的答案当思路参考最终以你本地库算出来的结果为准。参数换掉之后流程完全一样但数字必须自己算。4.2 FKinSpace 和 FKinBody 用混位姿差出一个坐标系变换题目如果给的是空间螺旋轴 Slist应该用 FKinSpace给的是物体螺旋轴 Blist应该用 FKinBody。混用之后相同关节角算出来的末端位形一般不会直接报错但数值会差一个刚体变换。现象是旋转矩阵每项都对不上平移分量错得离谱而且怎么检查 Slist 都检查不出错。原因很简单你按空间轴的数值去调用了物体轴版本的函数。解决办法是第一时间确认题目参数前面的大写字母是 S 还是 B。如果题目故意给了一组 Blist 却要求你用空间雅可比先通过公式把 Blist 转换成 SlistSlist 的每一列等于伴随矩阵 Ad(T) 作用在 Blist 对应列上其中 T 是零位形 M。这个转换关系在书上第 5 章有推导编程实现时要注意伴随矩阵的旋转部分和平移部分的组合方式。4.3 矩阵对数在转角接近 π 时不唯一有一类课后题是给定末端位形反求关节角或运动旋量中间要调 MatrixLog6 取矩阵对数。当旋转角度接近 π 时对数坐标里的旋转轴方向会出现符号歧义旋转 180 度和旋转负 180 度对应同一个旋转矩阵但螺旋轴的符号会相反。现象是在本地算出来的某一项是负值但你认为正值才对提交后系统判定误差很大。原因就是矩阵对数主值的选取不唯一。解决方法是特别留意旋转角度接近 π 时的输出手动归一化把转角限制在 (0, π) 范围并将旋转轴方向调整为与题目参考方向一致。这类题不多但一旦碰上卡住的时间通常以小时计。4.4 数值雅可比的步长选错怎么验都差一截在 3.2 节我有意用数值雅可比做交叉验证但步长选不好也会得到误导性结果。现象是解析雅可比和数值雅可比在小数点后第三位就对不上误差停在 1e-2 量级不下去。原因有两个方向步长太大中心差分的截断误差占主导步长太小浮点舍入误差占主导。解决方法是把步长定在 1e-6 附近这是中心差分的一个经验甜点区。另外要注意你的机械臂尺寸是米还是毫米级别如果尺寸极小或极大步长也要等比缩放。我一般会先试 1e-6如果误差不是 1e-5 量级就把步长调到 1e-7 和 1e-5 各跑一次看误差随步长的变化是否呈现典型趋势。4.5 本地库版本与判分环境的 API 不兼容这是最隐蔽的一个坑。现象是你的代码逻辑完全正确本地输出也自洽但提交到课程系统后返回的全是红叉。原因很可能是你本地用的子程序文件是某个旧版二次分发版本函数签名或返回格式和判分环境不一致。常见差异包括FKinSpace 的参数顺序从 (M, Slist, thetalist) 变成了 (Slist, M, thetalist)MatrixLog6 返回的是矩阵还是六维向量se3ToVec 是否要求输入先做一次转置。解决方法是固定使用课程页面发布的子程序文件不要用 pip 里第三方打包的替代品。更保险的做法是提交前先用 2.3 节那个零位自检脚本跑一遍如果输出不符合预期说明你的库版本和判分环境之间存在可疑差异优先换源。5. 提交前对答案的正确姿势随机回归加可视化数值答案算出来了提交前到底怎么确认它“像真的”我的习惯是做两件事随机采样回归和三维骨架可视化。前者抓参数输入错误后者抓物理层面的荒谬结果。import numpy as np from ModernRobotics_py_subroutines import FKinSpace M np.array([ [1, 0, 0, 0.3], [0, 1, 0, 0.0], [0, 0, 1, 0.9], [0, 0, 0, 1.0] ]) Slist np.array([ [0, 0, 1, 0, 0, 0 ], [0, 1, 0, -0.3, 0, 0 ], [0, 1, 0, -0.6, 0, 0 ], [1, 0, 0, 0, -0.6, 0 ], [0, 1, 0, -0.6, 0, 0 ], [0, 0, 1, 0, 0, 0 ] ]).T rng np.random.default_rng(42) max_err 0.0 for _ in range(100): theta rng.uniform(-1.5, 1.5, 6) T FKinSpace(M, Slist, theta) R T[:3, :3] err_det abs(np.linalg.det(R) - 1.0) err_trans abs(T[3, 3] - 1.0) max_err max(max_err, err_det, err_trans) print(100 组随机关节角下的最大结构误差, max_err)这段代码随机生成 100 组关节角每组都调用正运动学然后检查旋转矩阵的行列式是否严格等于 1、齐次矩阵最后一个元素是否严格等于 1。这两条是刚体变换的数学性质任何一组违反都说明 Slist 或 M 里存在结构性问题。误差量级应该在浮点精度附近。如果你换了自己的题目参数后这里报错别急着提交先把参数表逐行对一遍。可视化验证我也强烈建议做matplotlib 十几行就能画出连杆骨架。把正运动学输出的一系列关节位置点按顺序连线然后观察末端位置是否落在机械臂可达空间内、相邻连杆长度是否和输入一致。这一眼能看出参数是否输反了方向。我自己的一个习惯是所有数值题算完先画图再提交。有一次一个旋转轴方向输反数值自检全过但画出来的构型明显像手臂在反关节运动立刻抓住问题。最后讲个我自己的教训。早先贪快做过一次直接从网上找答案提交的事结果对方给的是另一期随机参数的旧答案被系统打回不算还浪费了一天排查自己的代码。从那以后我定了个规矩所有课后数值题一律用官方库本地算随机回归加可视化双验证最后再看一眼精度位数才提交。这个流程跑顺之后一遍通过率极高比下载十份“答案”都可靠。希望帮到你。本文还有配套的精品资源点击获取