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 这些,叫惯性积,表示质量分布不对称的程度。

你想想看,一个连杆如果形状规整,比如圆柱体,那惯性积基本为零。但机器人连杆往往奇形怪状——有电机、有减速器、有走线孔——惯性积就不能忽略了。

实际项目中怎么处理?

我一般用两种方法:

  1. CAD 软件导出:SolidWorks 或 Inventor 可以直接算惯性张量,精度够用。
  2. 实验辨识:如果 CAD 模型不准,就做参数辨识实验。我做过一次,用最小二乘法拟合,效果还不错。

4.4 牛顿-欧拉递推算法

好了,平动和转动都讲完了,怎么把它们串起来?

牛顿-欧拉法分两步:

  • 向外递推(从基座到末端):计算每个连杆的角速度、角加速度、线加速度
  • 向内递推(从末端到基座):计算每个关节的力和力矩

我画了一张流程图,帮你理清这个逻辑:

牛顿-欧拉递推算法流程 第一阶段:向外递推(基座 → 末端) ① 初始化基座角速度 ω₀ = 0,角加速度 α₀ = 0 ② 对 i = 1 到 n: ω_i = R_i^{i-1} * ω_{i-1} + θ̇_i * z_i α_i = R_i^{i-1} * α_{i-1} + θ̈_i * z_i + ω_i × (θ̇_i * z_i) 第二阶段:向内递推(末端 → 基座) ③ 对 i = n 到 1: F_i = m_i * a_ci τ_i = I_i * α_i + ω_i × (I_i * ω_i) f_i = R_{i+1}^i * f_{i+1} + F_i 最终输出:各关节驱动力矩 τ_i

这张图你看明白了吗?向外递推算运动学量,向内递推算动力学量。两个方向一结合,关节力矩就出来了。

代码实现小技巧:

我习惯用 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,转动用欧拉方程。但真正落地的时候,细节决定成败。

我个人建议,刚开始做动力学控制的朋友,先用仿真环境把递推算法跑通,再上真机。我在实验室里至少跑了上百次仿真,才敢把代码烧到控制器里。

嗯,今天就到这儿。记住:惯性张量不是标量,叉积项不能丢,坐标系要统一。这三点记住了,动力学这块你就稳了。


专注资料整理