3、单段轨迹求解:已知起点和终点的位置、速度、加速度,求解系数。

好,咱们直接进入正题。

上一章我们聊了五次多项式的数学形式,说白了就是那个 s(t) = a0 + a1*t + a2*t² + a3*t³ + a4*t⁴ + a5*t⁵。六个系数,六个未知数,对吧?

那怎么把这六个系数求出来?

答案就在边界条件里。

你想想看,一条轨迹从起点到终点,我们不光要知道它从哪出发、到哪结束,还得知道它出发时的速度和加速度,以及到达时的速度和加速度。这就是六个条件——正好对应六个系数。

3.1 边界条件的数学表达

假设我们规划的时间区间是 [0, T]。起点时刻 t=0,终点时刻 t=T

那么六个边界条件可以写成:

时刻 位置 速度 加速度
起点 (t=0) s₀ v₀ a₀
终点 (t=T) s₁ v₁ a₁

嗯,这里要注意:s₀v₀a₀ 是已知的起点状态,s₁v₁a₁ 是已知的终点状态。这些值通常由上层规划器给出,或者由你手动设定。

t=0 代入五次多项式及其一阶、二阶导数,我们立刻得到:

s(0) = a0 = s₀
s'(0) = a1 = v₀
s''(0) = 2*a2 = a₀  →  a2 = a₀ / 2

看到了吗?前三个系数直接由起点条件确定,根本不用算。我个人习惯先把这三个系数写下来,剩下的三个才是重头戏。

3.2 求解剩余三个系数

t=T 代入,得到终点条件:

s(T) = a0 + a1*T + a2*T² + a3*T³ + a4*T⁴ + a5*T⁵ = s₁
s'(T) = a1 + 2*a2*T + 3*a3*T² + 4*a4*T³ + 5*a5*T⁴ = v₁
s''(T) = 2*a2 + 6*a3*T + 12*a4*T² + 20*a5*T³ = a₁

把已知的 a0a1a2 代入,整理后得到一个关于 a3a4a5 的线性方程组。

写成矩阵形式更清晰:

[ T³   T⁴   T⁵ ]   [ a3 ]   [ s₁ - (a0 + a1*T + a2*T²) ]
[ 3T²  4T³  5T⁴ ] * [ a4 ] = [ v₁ - (a1 + 2*a2*T)       ]
[ 6T  12T² 20T³ ]   [ a5 ]   [ a₁ - 2*a2                ]

这个3x3矩阵是可逆的(只要T>0),直接求逆或者用克莱姆法则都能解。不过在实际工程中,我建议直接用数值方法——比如NumPy的 linalg.solve,又快又稳。

核心结论:五次多项式轨迹的六个系数,前三个由起点条件直接给出,后三个通过求解一个3x3线性方程组得到。整个过程是解析的,没有迭代,计算量极小。

3.3 Python代码实现

光说不练假把式。咱们直接上代码。

import numpy as np

def quintic_trajectory_coeffs(s0, v0, a0, s1, v1, a1, T):
    """
    计算五次多项式轨迹的系数
    
    参数:
        s0, v0, a0: 起点位置、速度、加速度
        s1, v1, a1: 终点位置、速度、加速度
        T: 总时间
    
    返回:
        coeffs: [a0, a1, a2, a3, a4, a5]
    """
    # 前三个系数直接赋值
    a0_val = s0
    a1_val = v0
    a2_val = a0 / 2.0
    
    # 构建线性方程组
    A = np.array([
        [T**3, T**4, T**5],
        [3*T**2, 4*T**3, 5*T**4],
        [6*T, 12*T**2, 20*T**3]
    ])
    
    b = np.array([
        s1 - (a0_val + a1_val*T + a2_val*T**2),
        v1 - (a1_val + 2*a2_val*T),
        a1 - 2*a2_val
    ])
    
    # 求解后三个系数
    a3_val, a4_val, a5_val = np.linalg.solve(A, b)
    
    return [a0_val, a1_val, a2_val, a3_val, a4_val, a5_val]

# 举个实际例子
# 从位置0出发,静止,加速度0
# 到位置10,静止,加速度0
# 用时2秒
coeffs = quintic_trajectory_coeffs(0, 0, 0, 10, 0, 0, 2)
print("系数:", coeffs)
# 输出: [0, 0, 0, 10.0, -15.0, 6.0]

这段代码我用了很多次,基本没出过问题。不过有一次我在项目中遇到一个坑——T传成了0,结果矩阵奇异,直接报错。所以后来我加了个判断:

注意:总时间T必须大于0。如果T=0,矩阵不可逆,物理上也没意义——瞬间移动?不存在的。

3.4 一个完整的可视化示例

咱们用上面求出的系数,生成一条完整的轨迹看看。

import matplotlib.pyplot as plt

def evaluate_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

def evaluate_velocity(coeffs, t):
    """计算速度"""
    a1, a2, a3, a4, a5 = coeffs[1], coeffs[2], coeffs[3], coeffs[4], coeffs[5]
    return a1 + 2*a2*t + 3*a3*t**2 + 4*a4*t**3 + 5*a5*t**4

def evaluate_acceleration(coeffs, t):
    """计算加速度"""
    a2, a3, a4, a5 = coeffs[2], coeffs[3], coeffs[4], coeffs[5]
    return 2*a2 + 6*a3*t + 12*a4*t**2 + 20*a5*t**3

# 生成时间序列
T = 2.0
t_vals = np.linspace(0, T, 100)

# 计算轨迹
pos = [evaluate_quintic(coeffs, t) for t in t_vals]
vel = [evaluate_velocity(coeffs, t) for t in t_vals]
acc = [evaluate_acceleration(coeffs, t) for t in t_vals]

# 绘制
plt.figure(figsize=(10, 8))
plt.subplot(3, 1, 1)
plt.plot(t_vals, pos, 'b-', linewidth=2)
plt.ylabel('位置')
plt.grid(True)

plt.subplot(3, 1, 2)
plt.plot(t_vals, vel, 'g-', linewidth=2)
plt.ylabel('速度')
plt.grid(True)

plt.subplot(3, 1, 3)
plt.plot(t_vals, acc, 'r-', linewidth=2)
plt.ylabel('加速度')
plt.xlabel('时间 (s)')
plt.grid(True)

plt.tight_layout()
plt.show()

运行这段代码,你会看到位置曲线从0平滑上升到10,速度从0开始、中间达到峰值、最后归零,加速度也是连续变化的。这就是五次多项式的魅力——全程平滑,没有突变。

小技巧:如果你只需要位置轨迹,可以只保存系数。但如果你还需要速度和加速度反馈,建议把三个评估函数都封装好,随调随用。我曾经在调试时频繁切换查看位置和速度,后来干脆写了个类,一劳永逸。

3.5 知识体系总览

为了让你对整个求解过程有个直观印象,我画了一张流程图:

五次多项式轨迹求解流程 输入边界条件 s₀, v₀, a₀, s₁, v₁, a₁, T 步骤1:直接赋值 a₀=s₀, a₁=v₀, a₂=a₀/2 步骤2:构建矩阵 A·x = b 步骤3:求解方程组 a₃, a₄, a₅ 合并六个系数 a₀ ~ a₅ 输出轨迹 位置、速度、加速度随时间变化 应用:机器人关节插补、相机轨迹规划

这张图把整个求解过程串起来了。你跟着箭头走一遍:输入条件 → 前三个系数直接拿 → 后三个系数解方程 → 合并 → 输出轨迹。逻辑很清晰,对吧?

3.6 避坑指南

最后,分享几个我踩过的坑:

  • 时间单位要统一。 我曾经把T设成毫秒,但位置单位是米,结果算出来的速度大得离谱。后来统一用秒,问题解决。
  • 数值稳定性。 当T很小(比如0.001秒)时,矩阵中的T³、T⁴、T⁵会非常小,可能导致数值误差。我建议T至少大于0.01秒,否则考虑用其他方法。
  • 边界条件要合理。 如果起点和终点的速度、加速度相差太大,五次多项式可能会产生很大的中间值。我曾经遇到过加速度峰值超过电机极限的情况,后来加了约束检查才搞定。

一句话总结:五次多项式单段轨迹求解,就是六个方程解六个未知数。前三个直接拿,后三个解矩阵。代码实现不超过20行,但边界条件的合理性需要你根据实际场景判断。

好了,这一节就到这里。代码你拿去就能用,但记得根据你的实际参数调一调T和边界条件。嗯,动手试试吧。