自动驾驶感知入门:用Python手把手实现CTRV模型与EKF/UKF滤波(附代码避坑)

张开发
2026/4/16 20:46:44 15 分钟阅读

分享文章

自动驾驶感知入门:用Python手把手实现CTRV模型与EKF/UKF滤波(附代码避坑)
自动驾驶感知实战CTRV运动模型与EKF/UKF的Python实现指南在自动驾驶系统的感知模块中目标跟踪的准确性直接影响着路径规划与决策的质量。当我们面对城市道路中频繁变道、加减速的车辆时传统的匀速(CV)模型往往力不从心。本文将带您从零实现CTRV(恒定转率和速度)模型并配以EKF(扩展卡尔曼滤波)和UKF(无迹卡尔曼滤波)两种非线性滤波方法通过可运行的Python代码展示如何处理转弯目标的跟踪难题。1. 环境准备与基础概念1.1 必要工具链配置推荐使用Python 3.8环境主要依赖库包括pip install numpy matplotlib scipy numdifftools sympy关键库的作用说明numdifftools用于数值计算雅可比矩阵避免手动推导复杂公式sympy符号运算库辅助理解状态方程推导过程matplotlib可视化跟踪结果与误差分析1.2 运动模型选择原则不同运动模型的适用场景对比模型类型状态变量适用场景计算复杂度CV[x, y, vx, vy]高速公路直行低CA[x, y, vx, vy, ax, ay]城市道路匀加速中CTRV[x, y, v, θ, ω]弯道行驶高提示CTRV模型中的θ表示偏航角yawω表示偏航角速度yaw rate这种参数化方式更符合车辆的实际运动特性。2. CTRV模型核心实现2.1 状态方程推导CTRV模型假设目标以恒定角速度ω和速度v运动其连续时间状态方程为dx/dt v*cos(θ) dy/dt v*sin(θ) dv/dt 0 dθ/dt ω dω/dt 0对应的离散形式Δt时间步长实现代码def ctrv_predict(state, dt): x, y, v, theta, omega state if abs(omega) 1e-5: # 处理ω≈0的数值稳定性问题 new_x x v*np.cos(theta)*dt new_y y v*np.sin(theta)*dt return np.array([new_x, new_y, v, theta, omega]) new_theta theta omega*dt new_x x (v/omega)*(np.sin(new_theta) - np.sin(theta)) new_y y (v/omega)*(np.cos(theta) - np.cos(new_theta)) return np.array([new_x, new_y, v, new_theta, omega])2.2 过程噪声建模CTRV模型需要考虑两类过程噪声线性加速度噪声σₐ²角加速度噪声σω̇²噪声协方差矩阵Q的构造方法def build_process_noise(sigma_a, sigma_omega_dot, dt): Q np.zeros((5,5)) Q[0,0] 0.25*dt**4 * sigma_a**2 Q[1,1] 0.25*dt**4 * sigma_a**2 Q[0,2] 0.5*dt**3 * sigma_a**2 Q[2,0] Q[0,2] Q[1,2] Q[0,2] Q[2,1] Q[0,2] Q[2,2] dt**2 * sigma_a**2 Q[3,3] dt**2 * sigma_omega_dot**2 Q[3,4] 0.5*dt**2 * sigma_omega_dot**2 Q[4,3] Q[3,4] Q[4,4] sigma_omega_dot**2 return Q3. EKF实现关键步骤3.1 雅可比矩阵计算使用numdifftools自动计算雅可比矩阵避免手动推导错误import numdifftools as nd def compute_jacobian(state, dt): def f(x): return ctrv_predict(x, dt) J nd.Jacobian(f)(state) return J3.2 预测与更新流程完整的EKF实现框架class EKF_CTRV: def __init__(self, initial_state, initial_cov): self.state initial_state self.cov initial_cov def predict(self, dt, Q): # 状态预测 self.state ctrv_predict(self.state, dt) # 协方差预测 F compute_jacobian(self.state, dt) self.cov F self.cov F.T Q def update(self, z, R, H): # 计算卡尔曼增益 S H self.cov H.T R K self.cov H.T np.linalg.inv(S) # 状态更新 y z - H self.state self.state self.state K y # 协方差更新 I np.eye(len(self.state)) self.cov (I - K H) self.cov4. UKF实现技巧4.1 Sigma点生成策略采用对称采样策略生成Sigma点def generate_sigma_points(state, cov, lambda_3-n): n len(state) sigma_points np.zeros((2*n1, n)) U np.linalg.cholesky((nlambda_)*cov) sigma_points[0] state for i in range(n): sigma_points[i1] state U[i] sigma_points[ni1] state - U[i] return sigma_points4.2 权重计算与统计量恢复Sigma点权重分配方案def compute_weights(n, lambda_): w_m np.zeros(2*n1) w_c np.zeros(2*n1) w_m[0] lambda_ / (n lambda_) w_c[0] w_m[0] (1 - alpha**2 beta) for i in range(1, 2*n1): w_m[i] 1 / (2*(n lambda_)) w_c[i] w_m[i] return w_m, w_c5. 实际应用中的挑战与解决方案5.1 数值稳定性处理常见问题及解决方法协方差矩阵非正定使用np.linalg.eigh进行特征分解后重建添加小的对角扰动项保持正定性角速度接近零时的数值问题设置阈值判断ω≈0的情况切换为CV模型近似计算def ensure_positive_definite(P): eigenvalues, eigenvectors np.linalg.eigh(P) eigenvalues[eigenvalues 0] 1e-6 return eigenvectors np.diag(eigenvalues) eigenvectors.T5.2 多传感器融合策略激光雷达与毫米波雷达的测量融合方案传感器测量内容更新频率精度特点激光雷达[x, y]10Hz位置精度高(厘米级)毫米波[ρ, φ, ρ̇]20Hz速度测量更准确实现异构传感器更新的代码逻辑def update_lidar(ekf, z_lidar, R_lidar): H np.array([[1,0,0,0,0], [0,1,0,0,0]]) ekf.update(z_lidar, R_lidar, H) def update_radar(ukf, z_radar, R_radar): # 雷达测量模型非线性需要特殊处理 sigma_points generate_sigma_points(ukf.state, ukf.cov) z_points np.array([radar_measurement_model(sp) for sp in sigma_points]) z_pred np.sum(w_m[:, None] * z_points, axis0) # 计算测量协方差和互相关 P_zz np.sum(w_c[:, None, None] * (z_points - z_pred)[:, :, None] (z_points - z_pred)[:, None, :], axis0) R_radar P_xz np.sum(w_c[:, None, None] * (sigma_points - ukf.state)[:, :, None] (z_points - z_pred)[:, None, :], axis0) # 计算卡尔曼增益 K P_xz np.linalg.inv(P_zz) # 状态更新 ukf.state ukf.state K (z_radar - z_pred) ukf.cov ukf.cov - K P_zz K.T6. 性能评估与可视化6.1 跟踪误差指标计算常用的评估指标实现def compute_rmse(estimates, ground_truth): return np.sqrt(np.mean((estimates - ground_truth)**2, axis0)) def compute_anees(estimates, covariances, ground_truth): errors estimates - ground_truth return np.mean([e.T np.linalg.inv(P) e for e, P in zip(errors, covariances)])6.2 结果可视化技巧使用Matplotlib绘制跟踪轨迹对比def plot_results(true_states, ekf_states, ukf_states): plt.figure(figsize(12,6)) plt.plot(true_states[:,0], true_states[:,1], k-, labelGround Truth) plt.plot(ekf_states[:,0], ekf_states[:,1], r--, labelEKF) plt.plot(ukf_states[:,0], ukf_states[:,1], b-., labelUKF) plt.xlabel(X position (m)) plt.ylabel(Y position (m)) plt.legend() plt.grid(True) plt.title(Tracking Performance Comparison)7. 工程实践中的经验分享在真实项目中部署CTRV模型时有几点特别值得注意参数调优顺序先调整过程噪声参数(Q矩阵)再调整测量噪声参数(R矩阵)最后考虑初始状态不确定性(P0矩阵)计算效率优化对雅可比矩阵计算使用缓存机制并行化Sigma点传播过程针对嵌入式平台进行定点数优化模型切换策略 当检测到长时间ω≈0时可自动切换到CV模型降低计算量当检测到明显转弯时再切换回CTRV模型。这种混合策略在实际工程中能显著提升系统效率。def model_switching_logic(state, omega_threshold0.01): _, _, _, _, omega state if abs(omega) omega_threshold: return CV else: return CTRV

更多文章