4. 端到端模仿学习
任务目标
基于车辆运动学自行车模型,实现一个离散时间线性MPC控制器,使其能够跟踪直线与圆弧参考轨迹。通过调节预测时域 N,观察跟踪精度与计算开销的变化,并验证控制器在给定初始误差下的收敛能力。
核心原理与实现要点
采用运动学自行车模型作为预测模型,状态量为 [x, y, yaw],控制量为 [v, delta](速度与前轮转角)。在参考轨迹点处对非线性模型进行线性化,得到线性时变预测模型,进而构建二次规划(QP)问题求解最优控制序列。MPC核心为:在每个控制周期,基于当前状态求解未来N步的最优控制序列,仅将第一组控制量作用于系统,下一周期滚动优化。
完整实现代码
import numpy as np
from scipy.optimize import minimize
# 车辆运动学模型参数
L = 2.5 # 轴距(m)
def kinematic_bicycle(x, u, dt):
"""离散运动学自行车模型"""
yaw = x[2]
v = u[0]
delta = u[1]
x_next = np.array([
x[0] + v * np.cos(yaw) * dt,
x[1] + v * np.sin(yaw) * dt,
yaw + v / L * np.tan(delta) * dt
])
return x_next
def mpc_control(x0, ref_traj, N, dt, v_ref, delta_max=np.radians(30)):
"""
线性MPC控制器
x0: 当前状态 [x, y, yaw]
ref_traj: 参考轨迹点集 (N+1个点), 每个点为 [x, y, yaw]
N: 预测时域
dt: 采样时间
v_ref: 参考速度
"""
n_state = 3
n_control = 2
# 线性化参考点(取当前状态作为线性化点)
x_lin = x0
u_lin = np.array([v_ref, 0.0])
# 状态矩阵离散化(前向欧拉)
A = np.eye(n_state)
A[0, 2] = -v_ref * np.sin(x_lin[2]) * dt
A[1, 2] = v_ref * np.cos(x_lin[2]) * dt
B = np.zeros((n_state, n_control))
B[0, 0] = np.cos(x_lin[2]) * dt
B[1, 0] = np.sin(x_lin[2]) * dt
B[2, 1] = v_ref / (L * np.cos(u_lin[1])**2) * dt
# 构建QP问题(使用scipy.optimize.minimize求解)
# 决策变量: [v_0..v_{N-1}, delta_0..delta_{N-1}]
# 以及预测状态 x_1..x_N(通过约束隐式表达)
def cost(u_flat):
u_seq = u_flat.reshape(N, n_control)
x_pred = x0.copy()
cost = 0.0
for k in range(N):
# 线性化预测
x_pred = A @ (x_pred - x_lin) + B @ u_seq[k] + x_lin
# 跟踪误差代价
ref_k = ref_traj[k+1]
err = x_pred - ref_k
cost += err @ np.diag([10.0, 10.0, 1.0]) @ err
# 控制量代价
cost += u_seq[k] @ np.diag([1.0, 10.0]) @ u_seq[k]
# 控制增量代价(平滑性)
if k > 0:
du = u_seq[k] - u_seq[k-1]
cost += du @ np.diag([0.1, 0.1]) @ du
return cost
# 约束:控制量边界
bounds = [(-2.0, 2.0), (-delta_max, delta_max)] * N
# 初始猜测:恒速直行
u_init = np.zeros((N, n_control))
u_init[:, 0] = v_ref
result = minimize(cost, u_init.flatten(), method='SLSQP', bounds=bounds)
u_opt = result.x.reshape(N, n_control)
return u_opt[0] # 返回第一组控制量
def simulate_tracking(ref_traj, x0, N, dt, steps):
"""闭环仿真:MPC滚动优化"""
x = x0.copy()
traj = [x.copy()]
for i in range(steps):
# 截取从当前时刻起的N+1个参考点
idx = min(i, len(ref_traj) - N - 1)
ref_seg = ref_traj[idx:idx+N+1]
u = mpc_control(x, ref_seg, N, dt, v_ref=2.0)
x = kinematic_bicycle(x, u, dt)
traj.append(x.copy())
return np.array(traj)
# ============ 轨迹生成与验证 ============
def generate_straight_line(length=30.0, dt=0.1):
"""生成直线参考轨迹(沿x轴)"""
t = np.arange(0, length, 2.0*dt)
ref = np.zeros((len(t), 3))
ref[:, 0] = t * 2.0 # x坐标
ref[:, 2] = 0.0 # yaw=0
return ref
def generate_circle(radius=10.0, dt=0.1):
"""生成圆弧参考轨迹"""
t = np.arange(0, 2*np.pi, dt)
ref = np.zeros((len(t), 3))
ref[:, 0] = radius * np.cos(t)
ref[:, 1] = radius * np.sin(t)
ref[:, 2] = t + np.pi/2 # 切向角
return ref
# 测试:直线跟踪
print("=== 直线跟踪测试 (N=10) ===")
ref_line = generate_straight_line()
x0_line = np.array([0.0, 1.0, 0.1]) # 初始横向偏差1m,航向偏差0.1rad
traj_line = simulate_tracking(ref_line, x0_line, N=10, dt=0.1, steps=100)
final_err_line = np.linalg.norm(traj_line[-1][:2] - ref_line[min(100, len(ref_line)-1)][:2])
print(f"终点横向位置误差: {final_err_line:.4f} m")
# 测试:圆弧跟踪
print("\n=== 圆弧跟踪测试 (N=15) ===")
ref_circle = generate_circle()
x0_circle = np.array([10.0, 0.5, np.pi/2]) # 初始径向偏差0.5m
traj_circle = simulate_tracking(ref_circle, x0_circle, N=15, dt=0.1, steps=200)
final_err_circle = np.linalg.norm(traj_circle[-1][:2] - ref_circle[min(200, len(ref_circle)-1)][:2])
print(f"终点径向位置误差: {final_err_circle:.4f} m")
# 预测时域影响对比
print("\n=== 预测时域N对跟踪误差的影响(直线) ===")
for N_test in [5, 10, 20]:
traj_test = simulate_tracking(ref_line, x0_line, N=N_test, dt=0.1, steps=100)
err = np.linalg.norm(traj_test[-1][:2] - ref_line[min(100, len(ref_line)-1)][:2])
print(f"N={N_test}: 终点误差 = {err:.4f} m")
运行方式与依赖
- 依赖:Python 3.8+,NumPy,SciPy(
pip install numpy scipy) - 运行:将代码保存为
mpc_basic.py,执行python mpc_basic.py
验证方法与判定标准
- 直线跟踪:初始横向偏差1m、航向偏差0.1rad,仿真10秒后终点横向位置误差应小于0.1m,证明MPC能消除初始误差并稳定跟踪。
- 圆弧跟踪:初始径向偏差0.5m,仿真20秒后终点径向误差应小于0.2m,验证曲率路径下的跟踪能力。
- 预测时域对比:增大
N(如从5增至20)通常会减小稳态跟踪误差,但单步求解时间增加。观察输出误差随N的变化趋势是否符合预期。
常见问题
- 求解失败:若
minimize返回异常,可检查参考轨迹是否连续、控制量边界是否过紧,或适当增大maxiter参数。 - 模型失配:线性化点取当前状态而非参考点,在大曲率路径下可能引入误差,可通过减小
dt缓解。 - 实时性:本实现使用SLSQP求解器,
N较大时计算耗时增加,实际应用中可改用OSQP等专用QP求解器。