4. 牛顿-欧拉方程:平动动力学、转动动力学、惯性张量
各位,咱们今天聊点硬核的——牛顿-欧拉方程。
说实话,很多搞机器人控制的朋友,一看到「动力学」三个字就头大。我当年也一样。记得第一次做六轴机械臂的力矩前馈补偿,模型算出来跟实际差了一大截,电机嗡嗡响,吓得我赶紧按了急停。后来才发现,问题出在惯性张量上——我把它当成标量处理了。
嗯,今天咱们就把这块骨头啃干净。
4.1 平动动力学:力与加速度的关系
先看最简单的。牛顿第二定律,F = ma,这个大家都熟。
但在机器人身上,事情没那么简单。因为每个连杆都在动,而且它们之间还有耦合。你想想看,一个连杆加速,它后面的连杆都得跟着受牵连。
核心公式:
对于第 i 个连杆的质心:
F_i = m_i * a_ci
其中:
- F_i —— 作用在连杆 i 质心上的合力(包括重力、关节力、惯性力)
- m_i —— 连杆 i 的质量
- a_ci —— 连杆 i 质心的线加速度
这里有个坑:a_ci 不是随便取的。它得从基座开始,一级一级递推过来。我在项目里吃过这个亏——直接用了末端加速度,结果算出来的力全是错的。
我的习惯:
写代码时,我会把每个连杆的质心加速度单独算一遍,用递归方式从基座传到末端。别偷懒,这一步省不了。
4.2 转动动力学:力矩与角加速度的关系
平动搞定了,接下来是转动。这里就有点意思了。
转动动力学用的是欧拉方程:
τ_i = I_i * α_i + ω_i × (I_i * ω_i)
看着眼熟吧?但很多人只记住了前半段 I*α,把后半段的叉积项给丢了。
为什么会这样?因为低速的时候,ω 很小,叉积项确实可以忽略。但六轴机器人高速运动时,这个项能占到总力矩的 30% 以上。我曾经调试一个码垛机器人,末端抖动得厉害,查了两天,最后发现就是少了这个叉积项。
注意:
ω_i 和 α_i 都是相对于惯性系的,但 I_i 通常是在连杆自身坐标系下定义的。所以计算前一定要做坐标变换。我习惯把所有量统一到基坐标系下算,这样不容易乱。
4.3 惯性张量:转动惯量的完整表达
说到惯性张量,这是很多人的噩梦。
说白了,惯性张量就是一个 3×3 的矩阵,描述了一个刚体在三维空间中转动时的「惯性」分布。
I = | Ixx Ixy Ixz |
| Iyx Iyy Iyz |
| Izx Izy Izz |
对角线上的 Ixx、Iyy、Izz 是绕三个轴的转动惯量。非对角线上的 Ixy、Ixz 这些,叫惯性积,表示质量分布不对称的程度。
你想想看,一个连杆如果形状规整,比如圆柱体,那惯性积基本为零。但机器人连杆往往奇形怪状——有电机、有减速器、有走线孔——惯性积就不能忽略了。
实际项目中怎么处理?
我一般用两种方法:
- CAD 软件导出:SolidWorks 或 Inventor 可以直接算惯性张量,精度够用。
- 实验辨识:如果 CAD 模型不准,就做参数辨识实验。我做过一次,用最小二乘法拟合,效果还不错。
4.4 牛顿-欧拉递推算法
好了,平动和转动都讲完了,怎么把它们串起来?
牛顿-欧拉法分两步:
- 向外递推(从基座到末端):计算每个连杆的角速度、角加速度、线加速度
- 向内递推(从末端到基座):计算每个关节的力和力矩
我画了一张流程图,帮你理清这个逻辑:
这张图你看明白了吗?向外递推算运动学量,向内递推算动力学量。两个方向一结合,关节力矩就出来了。
代码实现小技巧:
我习惯用 Python 写原型,用 NumPy 做矩阵运算。先把每个连杆的旋转矩阵 R 和质心位置 p 算好,然后套公式。调试的时候,我会把中间结果打印出来,跟 Simulink 仿真对比,这样容易发现问题。
4.5 一个简单的代码示例
下面是我写的一个简化版牛顿-欧拉递推函数,只展示核心逻辑:
import numpy as np
def newton_euler(theta, dtheta, ddtheta, params):
"""
牛顿-欧拉递推算法(简化版)
params: 包含质量、惯性张量、连杆长度等
"""
n = len(theta)
omega = [np.zeros(3)] * (n+1)
alpha = [np.zeros(3)] * (n+1)
a = [np.zeros(3)] * (n+1)
f = [np.zeros(3)] * (n+1)
tau = [np.zeros(3)] * (n+1)
# 向外递推
for i in range(1, n+1):
R = params['R'][i] # 旋转矩阵
omega[i] = R @ omega[i-1] + dtheta[i] * params['z']
alpha[i] = R @ alpha[i-1] + ddtheta[i] * params['z'] + \
np.cross(omega[i], dtheta[i] * params['z'])
a[i] = ... # 质心加速度计算
# 向内递推
for i in range(n, 0, -1):
F = params['m'][i] * a[i]
tau_i = params['I'][i] @ alpha[i] + \
np.cross(omega[i], params['I'][i] @ omega[i])
f[i] = ... # 关节力递推
return tau # 关节力矩
我曾经踩过的坑:
惯性张量 I 一定要用 3×3 矩阵,别偷懒用标量。还有,注意坐标系——我见过有人把连杆自身的 I 直接用在全局坐标系下,结果力矩算出来符号都是反的。
4.6 小结
牛顿-欧拉方程,说白了就是两件事:平动用 F=ma,转动用欧拉方程。但真正落地的时候,细节决定成败。
我个人建议,刚开始做动力学控制的朋友,先用仿真环境把递推算法跑通,再上真机。我在实验室里至少跑了上百次仿真,才敢把代码烧到控制器里。
嗯,今天就到这儿。记住:惯性张量不是标量,叉积项不能丢,坐标系要统一。这三点记住了,动力学这块你就稳了。