9.4 不确定性建模
在机器人感知、导航与决策过程中,传感器测量误差、环境动态变化以及模型不完善都会引入不确定性。为了保证系统的鲁棒性与稳定性,需要对状态和观测的不确定性进行建模与传播。高层融合中,不确定性建模不仅用于优化决策,还可以为路径规划、避障和语义理解提供置信度参考。
数学上,不确定性通常通过概率分布或协方差矩阵来刻画。例如,对于状态向量xt的估计,可以用高斯分布建模:
xt∼N(xt,Pt)
其中,xt是状态估计,Pt是估计协方差,反映不确定性大小。基于此思想,可以构建卡尔曼滤波族和贝叶斯图优化方法来实现高效的不确定性建模与状态融合。
9.4.1 卡尔曼滤波族
在机器人感知与导航系统中,传感器数据不可避免地存在噪声,而环境的不确定性会导致机器人状态估计出现偏差。为了在存在噪声和不完全观测的情况下对系统状态进行可靠估计,卡尔曼滤波(Kalman Filter, KF)及其扩展族成为了最常用的递推状态估计方法。卡尔曼滤波族不仅能够对线性系统进行最优估计,而且通过非线性扩展和无迹化变换,能够适应复杂的机器人动力学和传感器模型。
(1)线性高斯系统与经典卡尔曼滤波
最基础的卡尔曼滤波适用于离散时间线性高斯系统,其状态转移与观测模型可描述为:
xt+1=Axt+wt,wt∼N(0,Q)
zt=Hxt+vt,vt∼N(0,R)
其中:
- xt∈Rn表示机器人在时间t的状态向量,例如位置、速度或姿态;
- zt∈Rm为观测向量,可来自激光雷达、IMU 或摄像头等传感器;
- A为状态转移矩阵,描述系统状态随时间的线性演化;
- H为观测矩阵,将状态映射到观测空间;
- wt,vt分别为过程噪声和观测噪声,均服从零均值高斯分布,协方差分别为Q和R。卡尔曼滤波的核心思想是通过递推方式结合预测(Prediction)和更新(Update)两步,不断校正状态估计。其公式如下:
(1)预测步骤:在卡尔曼滤波中,每一时刻首先利用系统的状态转移模型对当前状态进行预测,这一过程称为预测步骤(Prediction Step)。预测不仅给出下一时刻状态的估计值,还给出估计的不确定性,即协方差矩阵。
- 状态预测:根据上一时刻的状态估计xt-1∣t-1和系统状态转移矩阵A,我们可以预测当前时刻的状态:
xt∣t-1=Axt-1∣t-1
这里,xt∣t-1表示在观察到当前时刻观测前的状态预测。
- 协方差预测:预测的不确定性同样需要更新。通过上一时刻的协方差Pt-1∣t-1和过程噪声协方差Q,计算当前预测的协方差:
Pt∣t-1=APt-1∣t-1A⊤+Q
其中,Pt∣t-1描述预测状态的不确定性大小,过程噪声Q则反映系统内在的不确定性或模型误差。
(2)更新步骤:预测完成后,卡尔曼滤波会结合传感器观测对状态进行修正(Update Step),以提高估计精度。更新步骤的核心是计算卡尔曼增益Kt,它决定了预测值与观测值在最终状态估计中的权重分配。
- 卡尔曼增益计算:卡尔曼增益根据预测协方差Pt∣t-1、观测矩阵H和观测噪声协方差R计算:
Kt=Pt∣t-1H⊤(HPt∣t-1H⊤+R)-1
增益越大,说明观测值可信度高;增益越小,则依赖预测值更多。
- 状态更新:利用观测值zt对预测状态进行修正,得到最优状态估计:
xt∣t=xt∣t-1+Kt(zt-Hxt∣t-1)
其中,zt-Hxt∣t-1是观测残差,表示预测与实际观测的差异。卡尔曼增益Kt调节这一修正的大小。
- 协方差更新:最后,根据卡尔曼增益更新状态估计的不确定性:
Pt∣t=(I-KtH)Pt∣t-1
更新后的协方差Pt∣t通常小于预测协方差Pt∣t-1
,说明观测信息有效降低了状态估计的不确定性。
在这一过程中,卡尔曼增益Kt自动调节预测与观测之间的权重,使得最终状态估计在均方误差意义下达到最优。
(2)扩展卡尔曼滤波(EKF)
当系统存在非线性动力学或观测模型时,经典卡尔曼滤波无法直接应用。此时,扩展卡尔曼滤波(Extended Kalman Filter,EKF)通过在当前状态附近对非线性函数进行一阶泰勒展开,将非线性系统线性化,从而保留了卡尔曼滤波的递推框架:
xt+1=f(xt)+wt,zt=h(xt)+vt
其中,f(⋅)和h(⋅)分别为状态转移和观测非线性函数。线性化后,预测与更新步骤与线性KF类似,只需将A和H替换为在当前状态处的雅可比矩阵:
Ft=∂f∂x∣xt-1∣t-1,Ht=∂h∂x∣xt∣t-1
EKF广泛应用于机器人SLAM、IMU导航融合等场景,能够在非线性系统下仍实现高精度状态估计。
(3)无迹卡尔曼滤波(UKF)与高阶滤波方法
对于高度非线性的系统,EKF的一阶线性化可能引入较大误差。无迹卡尔曼滤波(Unscented Kalman Filter, UKF)通过无迹变换(Unscented Transform, UT)在状态空间中选择一组sigma点,通过非线性函数传播这些点,直接计算均值和协方差,避免了一阶线性化带来的近似误差:
{χi}→f(⋅){χi'}⇒xt∣t-1=iwiχi',Pt∣t-1=iwi(χi'-xt∣t-1)(χi'-xt∣t-1)⊤
UKF在航迹预测、多传感器融合以及非线性SLAM中展现出更高的精度和稳定性。
(4)卡尔曼滤波族在机器人中的应用
- 在机器人系统中,卡尔曼滤波族提供了强大的不确定性建模和状态估计能力:
- IMU融合:通过EKF或UKF,将加速度计、陀螺仪、磁力计数据融合,估计姿态与速度;
- SLAM与定位:EKF-SLAM利用地图特征和里程计信息递推更新机器人位置和地图特征;
- 导航与避障:在动态环境中,卡尔曼滤波可预测障碍物运动,为决策层提供不确定性量化信息。
卡尔曼滤波族的优势在于递推计算、实时性强、能够量化估计不确定性,这使其成为移动机器人、高速导航系统以及多模态传感器融合中的核心工具。
9.4.2 贝叶斯图优化
在移动机器人与人形机器人定位、导航中,单纯依靠滤波器(如卡尔曼滤波)在处理大规模或非线性问题时往往精度有限。为了解决长期累积误差和多传感器融合问题,**贝叶斯图优化(Bayesian Graph Optimization, BGO)**成为当前主流方法。
贝叶斯图优化的核心思想是将机器人状态(位姿、速度等)和观测数据(传感器测量、里程计信息等)构建为一个因子图(Factor Graph),通过最优化方法求解整个图中的最优状态估计。相比传统滤波器,BGO 能同时考虑全局约束和非线性关系,从而显著提高长期定位与地图构建的精度。
1. 图优化建模
在贝叶斯图中,系统状态和观测分别表示为图中的节点(Nodes)和因子(Factors):
- 节点xi:表示机器人在时刻i的状态(位姿、速度、传感器偏差等)。
- 因子fj:表示节点之间的约束关系,通常来源于传感器观测或运动模型。
假设机器人在N个时刻的状态为X={x1,x2,…,xN},观测值为Z={z1,z2,…,zM},贝叶斯图优化的目标是求解后验概率分布:
P(X∣Z)∝j=1Mϕj(Xj)
其中,ϕj(Xj)是与观测zj相关的因子函数,依赖于部分状态子集Xj⊂X。
对贝叶斯图优化的直观理解是,每个因子对一组节点施加约束,优化的目标是找到一个状态集合X,使得所有约束尽可能一致。
2. 最大后验估计(MAP)
贝叶斯图优化通常采用最大后验估计(Maximum a Posteriori, MAP)方法,将概率推导转化为最小化能量函数:
X*=argmaxXP(X∣Z)=argminXj=1M∥hj(Xj)-zj∥Ωj2
其中:
- hj(Xj):为预测观测函数,描述状态Xj下的传感器预期值;
- zj:为实际观测;
- Ωj:为协方差矩阵,衡量观测噪声的不确定性;
- ∥⋅∥Ωj:表示加权二范数,即hj(Xj)-zj)⊤Ωj-1(hj(Xj)-zj。
该公式将贝叶斯推断问题转化为一个非线性最小二乘问题,通过调整所有节点状态X,最小化预测值与实际观测之间的加权误差。
3. 非线性最小二乘求解
由于hj(Xj)通常是非线性的(例如机器人运动模型或传感器观测模型),求解X*需要采用迭代优化算法,如高斯-牛顿法(Gauss-Newton)或列文伯格-马夸特法(Levenberg-Marquardt)。
(1)误差定义:为了量化预测状态与观测之间的偏差,引入误差函数ej(Xj),用于表示第j条观测对节点状态Xj的约束偏差:
ej(Xj)=hj(Xj)-zj
其中,hj(Xj)是根据状态X预测的观测值,zj是实际观测值。这个误差函数是贝叶斯图优化的基础,它将每个观测转化为状态约束,并通过权重矩阵反映观测的不确定性。
(2)整体目标函数:在所有观测约束的基础上,贝叶斯图优化通过构建一个全局加权最小二乘目标函数来联合优化所有状态:
E(X)=j=1Mej(Xj)⊤Ωj-1ej(Xj)
其中,Ωj是第j条观测的协方差矩阵,反映该观测的不确定性;M是总观测数。通过最小化E(X),可以在全局上协调各状态,使整体误差最小,从而得到最优的位姿和地图估计。
(3)迭代更新公式(高斯-牛顿法):由于误差函数通常是非线性的,需要使用迭代优化方法来求解最优状态。高斯-牛顿法是一种常用的非线性最小二乘求解策略,其核心思想是每次迭代沿负梯度方向更新状态:
Xk1=Xk-(J⊤Ω-1J)-1J⊤Ω-1e(Xk)
其中,J=∂e∂X是误差函数对状态的雅可比矩阵,表示误差对状态的敏感度。每次迭代都计算当前状态下的误差和雅可比,然后沿着负梯度方向更新状态,直到误差收敛或达到预设阈值。
通过迭代优化,贝叶斯图优化能够将多步状态和多传感器观测融合为全局一致的最优估计,同时抑制累积误差。
4. 特点与应用
贝叶斯图优化相比传统滤波方法有以下优势:
- 全局优化:考虑整个轨迹上的约束,不局限于局部时刻,减少累积误差。
- 灵活建模:支持非线性运动模型、观测模型及多种传感器融合。
- 稀疏结构:因子图通常非常稀疏,可利用稀疏矩阵求解器提高计算效率。
贝叶斯图优化的典型应用如下:
- SLAM(同步定位与地图构建):优化机器人轨迹和地图。
- 视觉惯性里程计(VIO):融合IMU与视觉观测的高精度定位。
- 多机器人协同定位:通过图优化整合多个机器人观测,实现全局一致性。
总之,贝叶斯图优化通过将机器人状态与观测建模为因子图,并采用非线性最小二乘优化求解全局最优状态,实现了对多传感器信息的高效融合与长期轨迹约束的全局一致性。相比传统滤波器,BGO更适合复杂环境下的高精度定位与导航任务,被广泛应用于SLAM、视觉惯性里程计以及多机器人协同定位,为机器人智能决策提供了坚实的基础。
9.4.3 实战演练:人形机器人不确定性建模方法对比
本实例以人形机器人非线性运动为场景,采用卡尔曼滤波(KF)、扩展卡尔曼滤波(EKF)及贝叶斯图优化实现状态估计,通过误差对比图呈现:KF因线性假设误差最高,EKF 适配非线性后精度提升,贝叶斯图优化靠全局约束误差最优,直观体现不同不确定性建模方法的性能与适用场景。
实例9-4:人形机器人不确定性建模方法对比(源码路径:codes\9\Mo.py)
实例文件Mo.py的主要实现流程如下所示。
(1)下面代码的功能是生成人形机器人非线性运动的模拟数据,先初始化包含位置、速度、旋转角速度的初始状态,通过非线性状态转移生成带过程噪声的真实轨迹,再基于真实轨迹生成仅包含x/y位置且叠加观测噪声的观测数据,同时生成对应的时间轴,为后续状态估计提供基础数据集。
# ===================== 1. 数据生成:模拟人形机器人轨迹与观测 ===================== def generate_robot_data(total_time=50, dt=0.1, process_noise=0.05, measurement_noise=0.2): """ 生成人形机器人真实轨迹(非线性运动)+ 带噪声的观测数据 :param total_time: 总时间步 :param dt: 时间间隔 :param process_noise: 过程噪声 :param measurement_noise: 观测噪声 :return: 真实状态、观测数据、时间轴 """ # 初始化:状态=[x, y, vx, vy, omega](x/y位置,vx/vy速度,omega旋转角速度) true_states = np.zeros((total_time, 5)) true_states[0] = [0, 0, 0.5, 0.3, 0.02] # 初始状态 # 生成真实轨迹(非线性运动:匀速+缓慢旋转) for t in range(1, total_time): x, y, vx, vy, omega = true_states[t - 1] # 非线性状态转移:旋转导致速度方向变化 new_vx = vx * np.cos(omega * dt) - vy * np.sin(omega * dt) new_vy = vx * np.sin(omega * dt) + vy * np.cos(omega * dt) new_x = x + new_vx * dt + np.random.normal(0, process_noise) new_y = y + new_vy * dt + np.random.normal(0, process_noise) true_states[t] = [new_x, new_y, new_vx, new_vy, omega] # 生成带噪声的观测(仅观测x/y位置) measurements = true_states[:, :2] + np.random.normal(0, measurement_noise, (total_time, 2)) time_axis = np.arange(total_time) * dt return true_states, measurements, time_axis(2)下面代码的功能是实现卡尔曼滤波族算法,其中经典卡尔曼滤波(KF)基于线性匀速模型初始化参数,对观测数据进行预测和更新,输出位置估计结果与协方差;扩展卡尔曼滤波(EKF)纯手动实现,适配非线性旋转运动,通过定义状态转移函数、雅可比矩阵完成预测和更新步骤,输出位置估计结果与协方差。
# ===================== 2. 卡尔曼滤波族实现 ===================== def run_kalman_filter(measurements, dt=0.1): """经典卡尔曼滤波(KF):假设线性运动模型""" kf = KalmanFilter(dim_x=4, dim_z=2) # 状态=[x,y,vx,vy],观测=[x,y] # 初始化KF参数 kf.x = np.array([0, 0, 0, 0]) # 初始状态估计 kf.F = np.array([[1, 0, dt, 0], # 状态转移矩阵(线性匀速模型) [0, 1, 0, dt], [0, 0, 1, 0], [0, 0, 0, 1]]) kf.H = np.array([[1, 0, 0, 0], # 观测矩阵 [0, 1, 0, 0]]) kf.Q = np.eye(4) * 0.01 # 过程噪声协方差 kf.R = np.eye(2) * 0.04 # 观测噪声协方差 kf.P = np.eye(4) * 1 # 初始协方差 # 运行KF kf_states = [] kf_covs = [] # 存储协方差(量化不确定性) for z in measurements: kf.predict() kf.update(z) kf_states.append(kf.x[:2]) # 仅保存位置估计 kf_covs.append(kf.P[:2, :2]) # 位置协方差 return np.array(kf_states), np.array(kf_covs) def run_extended_kalman_filter(measurements, dt=0.1): """扩展卡尔曼滤波(EKF):处理非线性旋转运动(纯手动实现,无filterpy依赖)""" # 初始化EKF参数 dim_x = 5 # 状态=[x,y,vx,vy,omega] dim_z = 2 # 观测=[x,y] x = np.array([0, 0, 0, 0, 0.02]) # 初始状态 P = np.eye(dim_x) * 1 # 初始协方差 R = np.eye(dim_z) * 0.04 # 观测噪声 Q = np.eye(dim_x) * 0.01 # 过程噪声 # 定义非线性状态转移函数 f(x) def state_transition(x, dt): x_pos, y_pos, vx, vy, omega = x # 非线性旋转运动模型 new_vx = vx * np.cos(omega * dt) - vy * np.sin(omega * dt) new_vy = vx * np.sin(omega * dt) + vy * np.cos(omega * dt) new_x = x_pos + new_vx * dt new_y = y_pos + new_vy * dt return np.array([new_x, new_y, new_vx, new_vy, omega]) # 定义状态转移雅可比矩阵 F def jacobian_F(x, dt): _, _, vx, vy, omega = x cos_omega = np.cos(omega * dt) sin_omega = np.sin(omega * dt) J = np.eye(dim_x) # 雅可比矩阵非对角元素(仅关键项) J[0, 2] = cos_omega * dt J[0, 3] = -sin_omega * dt J[1, 2] = sin_omega * dt J[1, 3] = cos_omega * dt return J # 定义观测函数 h(x) 和雅可比矩阵 H def measurement_function(x): return x[:2] # 仅观测x/y def jacobian_H(x): return np.array([[1, 0, 0, 0, 0], [0, 1, 0, 0, 0]]) # 运行EKF ekf_states = [] ekf_covs = [] for z in measurements: # ---------------- 预测步骤 ---------------- # 1. 计算状态转移雅可比矩阵 F = jacobian_F(x, dt) # 2. 手动更新状态(非线性转移) x = state_transition(x, dt) # 3. 预测协方差(KF标准公式) P = F @ P @ F.T + Q # ---------------- 更新步骤 ---------------- # 1. 计算观测雅可比矩阵 H = jacobian_H(x) # 2. 计算观测预测值 z_pred = measurement_function(x) # 3. 计算观测残差 y = z - z_pred # 4. 计算卡尔曼增益(标准公式) S = H @ P @ H.T + R K = P @ H.T @ np.linalg.inv(S) # 5. 更新状态和协方差 x = x + K @ y P = (np.eye(dim_x) - K @ H) @ P # 保存结果(仅位置和位置协方差) ekf_states.append(x[:2]) ekf_covs.append(P[:2, :2]) return np.array(ekf_states), np.array(ekf_covs)(3)下面代码的功能是实现简化版贝叶斯图优化算法,以卡尔曼滤波结果为初始轨迹,构建包含观测约束和运动约束的能量函数,通过高斯牛顿法最小化该函数,完成机器人全局轨迹优化,最终输出优化后的轨迹数据。
# ===================== 3. 贝叶斯图优化实现(简化版) ===================== def bayesian_graph_optimization(true_states, measurements, dt=0.1): """ 贝叶斯图优化:全局轨迹优化(因子图+高斯牛顿) 节点:各时刻机器人位置;因子:运动约束+观测约束 """ total_time = len(measurements) # 初始化待优化的轨迹(以KF结果为初始值) kf_init, _ = run_kalman_filter(measurements, dt) init_trajectory = kf_init.flatten() # 展平为一维向量:[x0,y0,x1,y1,...,xn,yn] # 定义能量函数(目标函数):运动约束误差 + 观测约束误差 def energy_function(trajectory): traj = trajectory.reshape(-1, 2) energy = 0.0 # 1. 观测约束因子:||h(x)-z||^2 / R(R为观测噪声协方差) obs_cov = 0.04 # 对应观测噪声方差 for t in range(total_time): pred_x, pred_y = traj[t] obs_x, obs_y = measurements[t] energy += ((pred_x - obs_x) ** 2 + (pred_y - obs_y) ** 2) / obs_cov # 2. 运动约束因子:||x_t - f(x_{t-1})||^2 / Q(Q为过程噪声协方差) motion_cov = 0.01 # 对应过程噪声方差 for t in range(1, total_time): x_prev, y_prev = traj[t - 1] x_curr, y_curr = traj[t] # 运动模型:基于真实轨迹的平均速度(简化) vx_avg = (true_states[t, 2] + true_states[t - 1, 2]) / 2 vy_avg = (true_states[t, 3] + true_states[t - 1, 3]) / 2 pred_x = x_prev + vx_avg * dt pred_y = y_prev + vy_avg * dt energy += ((x_curr - pred_x) ** 2 + (y_curr - pred_y) ** 2) / motion_cov return energy # 高斯牛顿法优化(最小化能量函数) result = minimize(energy_function, init_trajectory, method='BFGS') optimized_traj = result.x.reshape(-1, 2) return optimized_traj(4)下面代码的功能是实现结果可视化相关函数,先定义协方差椭圆绘制函数(处理数值异常保证稳定性),再生成多张子图,对比真实轨迹与KF、EKF、贝叶斯图优化的估计轨迹,展示各方法的不确定性椭圆和位置误差变化,最后保存并展示高清可视化图表。
# ===================== 4. 可视化函数 ===================== def plot_uncertainty_ellipse(ax, mean, cov, color, alpha=0.3): """绘制协方差椭圆(可视化不确定性)""" # 确保协方差矩阵是2x2实矩阵 cov = np.array(cov, dtype=np.float64) if cov.shape != (2, 2): cov = np.eye(2) * 0.1 # 异常时用默认协方差 # 特征分解并取实部(消除数值误差导致的虚部) eigvals, eigvecs = np.linalg.eig(cov) eigvals = np.real(eigvals) # 取实部 eigvecs = np.real(eigvecs) # 取实部 # 防止特征值为负或过小(数值稳定性) eigvals = np.maximum(eigvals, 1e-6) # 计算椭圆参数(2σ范围,覆盖95%置信区间) try: angle = np.degrees(np.arctan2(eigvecs[1, 0], eigvecs[0, 0])) except: angle = 0 # 异常时默认角度 # 绘制椭圆 ellipse = Ellipse(xy=mean, width=2 * np.sqrt(eigvals[0]) * 2, height=2 * np.sqrt(eigvals[1]) * 2, angle=angle, color=color, alpha=alpha) ax.add_patch(ellipse) def visualize_results(true_states, measurements, kf_res, ekf_res, bgo_res, kf_covs, ekf_covs, time_axis): """可视化所有结果:轨迹对比+不确定性""" plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False # 主图:轨迹+不确定性 fig = plt.figure(figsize=(15, 10)) # 子图1:轨迹对比 ax1 = plt.subplot(2, 2, (1, 2)) ax1.plot(true_states[:, 0], true_states[:, 1], 'k-', label='真实轨迹', linewidth=2) ax1.scatter(measurements[:, 0], measurements[:, 1], c='gray', s=10, label='带噪声观测', alpha=0.5) ax1.plot(kf_res[:, 0], kf_res[:, 1], 'b--', label='KF估计(线性)', linewidth=1.5) ax1.plot(ekf_res[:, 0], ekf_res[:, 1], 'r-.', label='EKF估计(非线性)', linewidth=1.5) ax1.plot(bgo_res[:, 0], bgo_res[:, 1], 'g-', label='贝叶斯图优化', linewidth=2) ax1.set_xlabel('X位置 (m)') ax1.set_ylabel('Y位置 (m)') ax1.set_title('人形机器人轨迹估计对比(不确定性建模)') ax1.legend() ax1.grid(True, alpha=0.3) # 子图2:KF不确定性(协方差椭圆) ax2 = plt.subplot(2, 2, 3) ax2.plot(true_states[:, 0], true_states[:, 1], 'k-', label='真实轨迹', linewidth=1) ax2.plot(kf_res[:, 0], kf_res[:, 1], 'b--', label='KF估计', linewidth=1.5) # 每隔5步绘制协方差椭圆 for t in range(0, len(kf_res), 5): plot_uncertainty_ellipse(ax2, kf_res[t], kf_covs[t], 'blue') ax2.set_xlabel('X位置 (m)') ax2.set_ylabel('Y位置 (m)') ax2.set_title('KF估计 + 不确定性椭圆(2σ)') ax2.legend() ax2.grid(True, alpha=0.3) # 子图3:EKF不确定性(协方差椭圆) ax3 = plt.subplot(2, 2, 4) ax3.plot(true_states[:, 0], true_states[:, 1], 'k-', label='真实轨迹', linewidth=1) ax3.plot(ekf_res[:, 0], ekf_res[:, 1], 'r-.', label='EKF估计', linewidth=1.5) # 每隔5步绘制协方差椭圆 for t in range(0, len(ekf_res), 5): plot_uncertainty_ellipse(ax3, ekf_res[t], ekf_covs[t], 'red') ax3.set_xlabel('X位置 (m)') ax3.set_ylabel('Y位置 (m)') ax3.set_title('EKF估计 + 不确定性椭圆(2σ)') ax3.legend() ax3.grid(True, alpha=0.3) # 子图4:位置误差对比 ax4 = plt.figure(figsize=(12, 4)).add_subplot(111) kf_error = np.linalg.norm(kf_res - true_states[:, :2], axis=1) ekf_error = np.linalg.norm(ekf_res - true_states[:, :2], axis=1) bgo_error = np.linalg.norm(bgo_res - true_states[:, :2], axis=1) ax4.plot(time_axis, kf_error, 'b--', label='KF误差', linewidth=1.5) ax4.plot(time_axis, ekf_error, 'r-.', label='EKF误差', linewidth=1.5) ax4.plot(time_axis, bgo_error, 'g-', label='贝叶斯图优化误差', linewidth=1.5) ax4.set_xlabel('时间 (s)') ax4.set_ylabel('位置误差 (m)') ax4.set_title('各方法位置误差对比') ax4.legend() ax4.grid(True, alpha=0.3) plt.tight_layout() plt.savefig("人形机器人不确定性建模可视化.png", dpi=300, bbox_inches='tight') plt.show()(5)下面代码作为整个程序的入口函数,先忽略无关警告,按顺序执行模拟数据生成、卡尔曼滤波族运行、贝叶斯图优化运行、结果可视化的完整流程,每一步执行完成后输出对应的提示信息,保障算法流程有序且完整地执行。
# ===================== 主程序 ===================== if __name__ == "__main__": # 忽略所有无关警告 import warnings warnings.filterwarnings("ignore") # 1. 生成模拟数据 true_states, measurements, time_axis = generate_robot_data(total_time=50, dt=0.1) print("✅ 模拟数据生成完成") # 2. 运行卡尔曼滤波族 kf_results, kf_covs = run_kalman_filter(measurements, dt=0.1) ekf_results, ekf_covs = run_extended_kalman_filter(measurements, dt=0.1) print("✅ 卡尔曼滤波族运行完成") # 3. 运行贝叶斯图优化 bgo_results = bayesian_graph_optimization(true_states, measurements, dt=0.1) print("✅ 贝叶斯图优化运行完成") # 4. 可视化结果 visualize_results(true_states, measurements, kf_results, ekf_results, bgo_results, kf_covs, ekf_covs, time_axis) print("✅ 可视化图表生成完成!")执行后先生成含过程噪声和观测噪声的模拟轨迹与观测数据,再实现经典卡尔曼滤波(KF)、纯手动适配非线性的扩展卡尔曼滤波(EKF),以及基于能量函数最小化的贝叶斯图优化三种状态估计方法,最终完成全流程可视化。如图9-4所示。
人形机器人轨迹估计对比图
各方法位置误差对比图
图9-4 全流程可视化图