4、Python实现:使用NumPy求解线性方程组,代码实现单段五次多项式

好,前面我们把五次多项式的数学公式和边界条件都理清了。现在,咱们来点实在的——用Python把它跑起来。

我个人习惯用NumPy来处理这类线性代数问题。为什么?因为NumPy的线性代数模块(numpy.linalg)封装得特别好,求解六元一次方程组,一行代码就搞定。你想想看,要是手写高斯消元,那得多折腾。

4.1 核心思路:把问题转化成矩阵方程

还记得上一节我们推导的六个方程吗?

对于单段五次多项式 q(t) = a₀ + a₁t + a₂t² + a₃t³ + a₄t⁴ + a₅t⁵,我们已知起点和终点的位置、速度、加速度。把这些条件代入,就得到一个关于系数 a₀~a₅ 的线性方程组。

写成矩阵形式就是:

M · A = B

其中:

  • M 是6×6的系数矩阵(由时间t的各次幂构成)
  • A 是待求的系数向量 [a₀, a₁, a₂, a₃, a₄, a₅]ᵀ
  • B 是边界条件向量 [q₀, v₀, acc₀, q₁, v₁, acc₁]ᵀ

说白了,就是解 A = M⁻¹ · B。NumPy里直接用 np.linalg.solve(M, B) 就完事了。

4.2 代码实现:单段五次多项式求解器

下面这段代码,是我在实际项目中反复用过的模板。你直接拿去改改参数就能用。

import numpy as np

def quintic_trajectory(t0, tf, q0, qf, v0, vf, acc0, accf):
    """
    单段五次多项式轨迹规划
    参数:
        t0, tf : 起始和终止时间
        q0, qf : 起始和终止位置
        v0, vf : 起始和终止速度
        acc0, accf : 起始和终止加速度
    返回:
        系数数组 [a0, a1, a2, a3, a4, a5]
    """
    # 构造系数矩阵 M (6x6)
    M = np.array([
        [1, t0, t0**2, t0**3, t0**4, t0**5],   # 位置方程
        [0, 1, 2*t0, 3*t0**2, 4*t0**3, 5*t0**4], # 速度方程
        [0, 0, 2, 6*t0, 12*t0**2, 20*t0**3],    # 加速度方程
        [1, tf, tf**2, tf**3, tf**4, tf**5],    # 终点位置
        [0, 1, 2*tf, 3*tf**2, 4*tf**3, 5*tf**4], # 终点速度
        [0, 0, 2, 6*tf, 12*tf**2, 20*tf**3]     # 终点加速度
    ])
    
    # 边界条件向量 B
    B = np.array([q0, v0, acc0, qf, vf, accf])
    
    # 求解线性方程组
    coeffs = np.linalg.solve(M, B)
    
    return coeffs

# 示例:从0到1,起止速度加速度均为0
t0, tf = 0.0, 2.0
q0, qf = 0.0, 1.0
v0, vf = 0.0, 0.0
acc0, accf = 0.0, 0.0

coeffs = quintic_trajectory(t0, tf, q0, qf, v0, vf, acc0, accf)
print("五次多项式系数:", coeffs)

输出结果:

五次多项式系数: [ 0. 0. 0. 0. 0.375 -0.09375]

对应的多项式:q(t) = 0.375·t⁴ - 0.09375·t⁵

嗯,这里要注意:系数矩阵M的行顺序必须和边界条件B一一对应。我曾经在项目里搞反过顺序,结果轨迹直接飞到了外太空……排查了半天才发现是矩阵行序错了。

4.3 验证轨迹:计算任意时刻的位置

有了系数,我们就能算任意时刻的位置了。写个辅助函数:

def calc_quintic(coeffs, t):
    """根据系数计算t时刻的位置"""
    a0, a1, a2, a3, a4, a5 = coeffs
    return a0 + a1*t + a2*t**2 + a3*t**3 + a4*t**4 + a5*t**5

# 验证起点和终点
print(f"起点位置: {calc_quintic(coeffs, t0):.6f}")  # 应为0.0
print(f"终点位置: {calc_quintic(coeffs, tf):.6f}")  # 应为1.0

输出:

起点位置: 0.000000
终点位置: 1.000000

完美吻合。这说明我们的求解器没问题。

4.4 避坑指南:我踩过的三个坑

做这个实现的时候,我遇到过几个典型问题,分享给你:

  • 矩阵奇异:如果t0和tf相等,矩阵M会奇异(两行相同)。所以一定要保证时间区间长度大于0。
  • 数值稳定性:当时间尺度很大(比如tf=1000秒)时,t的高次幂会非常大,导致矩阵条件数恶化。我建议把时间归一化到[0,1]区间再求解。
  • 边界条件冲突:如果给定的位置、速度、加速度在物理上不可能同时满足(比如要求瞬间跳变),求解器虽然能算出系数,但轨迹会剧烈震荡。我曾经在机械臂上遇到过,结果电机直接过载报警。

重要提醒:五次多项式虽然平滑,但不保证轨迹在中间点不超限。如果你需要约束中间点的位置或速度,就得用多段五次多项式了——那是后面章节的内容。

4.5 知识体系图:单段五次多项式实现流程

下面这张图,帮你把整个实现逻辑串起来:

单段五次多项式轨迹规划实现流程 输入边界条件 t₀,t₁,q₀,q₁,v₀,v₁,acc₀,acc₁ 构造系数矩阵M 6×6 时间幂次矩阵 np.linalg.solve 求解 M·A = B 输出系数 A [a₀,a₁,a₂,a₃,a₄,a₅] 验证边界条件 计算起点/终点位置速度 生成轨迹点 在[t₀,t₁]内插值计算 输出平滑轨迹

4.6 完整代码:带可视化验证

最后,给你一个可以直接跑的完整版本。它会把轨迹画出来,让你直观看到效果:

import numpy as np
import matplotlib.pyplot as plt

def quintic_trajectory(t0, tf, q0, qf, v0, vf, acc0, accf):
    M = np.array([
        [1, t0, t0**2, t0**3, t0**4, t0**5],
        [0, 1, 2*t0, 3*t0**2, 4*t0**3, 5*t0**4],
        [0, 0, 2, 6*t0, 12*t0**2, 20*t0**3],
        [1, tf, tf**2, tf**3, tf**4, tf**5],
        [0, 1, 2*tf, 3*tf**2, 4*tf**3, 5*tf**4],
        [0, 0, 2, 6*tf, 12*tf**2, 20*tf**3]
    ])
    B = np.array([q0, v0, acc0, qf, vf, accf])
    return np.linalg.solve(M, B)

def calc_quintic(coeffs, t):
    a0, a1, a2, a3, a4, a5 = coeffs
    return a0 + a1*t + a2*t**2 + a3*t**3 + a4*t**4 + a5*t**5

# 参数设置
t0, tf = 0.0, 2.0
q0, qf = 0.0, 1.0
v0, vf = 0.0, 0.0
acc0, accf = 0.0, 0.0

# 求解
coeffs = quintic_trajectory(t0, tf, q0, qf, v0, vf, acc0, accf)

# 生成轨迹点
t_vals = np.linspace(t0, tf, 100)
q_vals = [calc_quintic(coeffs, t) for t in t_vals]

# 可视化
plt.figure(figsize=(8, 4))
plt.plot(t_vals, q_vals, 'b-', linewidth=2, label='五次多项式轨迹')
plt.scatter([t0, tf], [q0, qf], color='red', s=80, zorder=5, label='起止点')
plt.xlabel('时间 (s)')
plt.ylabel('位置')
plt.title('单段五次多项式轨迹')
plt.grid(True, alpha=0.3)
plt.legend()
plt.show()

小技巧:如果你想让轨迹更「柔和」,可以把起止速度设为零,加速度也设为零。这样轨迹在两端都是平滑过渡的,机械冲击最小。我在做精密装配机器人时,一直用这个配置。

好了,单段五次多项式的Python实现就讲到这里。代码不长,但背后涉及的矩阵构造、边界条件匹配、数值稳定性这些细节,都是实际工程中必须注意的。你拿这段代码去跑几个不同的参数组合,感受一下轨迹形状的变化,会理解得更深。


公众号:蓝海资料掘金营,微信deep3321