parent
0c1a2f8eee
commit
cbd8df9bdd
@ -0,0 +1,427 @@
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from IMM_Filter import IMMFilter
|
||||
from KalmanFilter2D import KalmanFilter2D
|
||||
from CTModelEKF import CTModelEKF
|
||||
# 设置中文字体
|
||||
plt.rcParams['font.sans-serif'] = ['SimHei']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
|
||||
def generate_trajectory(n_points=100, trajectory_type='circular', noise_std=0.1,
|
||||
outlier_ratio=0.0, outlier_magnitude=10.0):
|
||||
"""
|
||||
生成[x,y]轨迹数据
|
||||
|
||||
参数:
|
||||
n_points: 轨迹点数
|
||||
trajectory_type: 轨迹类型 ('linear', 'circular', 'figure8', 'maneuver')
|
||||
noise_std: 噪声标准差
|
||||
outlier_ratio: 野值比例 (0.0-1.0)
|
||||
outlier_magnitude: 野值幅度倍数
|
||||
|
||||
返回:
|
||||
true_positions: 真实轨迹 (n_points, 2)
|
||||
noisy_measurements: 带噪声的观测 (n_points, 2)
|
||||
outlier_indices: 野值点的索引
|
||||
"""
|
||||
t = np.linspace(0, 10, n_points)
|
||||
|
||||
if trajectory_type == 'linear':
|
||||
# 直线运动
|
||||
x_true = 2 * t + 10
|
||||
y_true = 1.5 * t + 5
|
||||
elif trajectory_type == 'circular':
|
||||
# 圆形轨迹
|
||||
radius = 20
|
||||
x_true = radius * np.cos(0.5 * t) + 50
|
||||
y_true = radius * np.sin(0.5 * t) + 50
|
||||
elif trajectory_type == 'figure8':
|
||||
# 8字形轨迹
|
||||
x_true = 20 * np.sin(t) + 50
|
||||
y_true = 20 * np.sin(2 * t) + 50
|
||||
elif trajectory_type == 'maneuver':
|
||||
# 机动轨迹(包含转弯)
|
||||
x_true = np.zeros_like(t)
|
||||
y_true = np.zeros_like(t)
|
||||
for i, time in enumerate(t):
|
||||
if time < 3:
|
||||
# 直线运动
|
||||
x_true[i] = 5 * time
|
||||
y_true[i] = 2 * time
|
||||
elif time < 6:
|
||||
# 转弯
|
||||
angle = np.pi * (time - 3) / 3
|
||||
x_true[i] = 15 + 10 * np.cos(angle)
|
||||
y_true[i] = 6 + 10 * np.sin(angle)
|
||||
else:
|
||||
# 再次直线运动
|
||||
x_true[i] = 5 + 3 * (time - 6)
|
||||
y_true[i] = 16 + 1 * (time - 6)
|
||||
elif trajectory_type == 'my':
|
||||
x_true = np.zeros_like(t)
|
||||
y_true = np.zeros_like(t)
|
||||
for i, time in enumerate(t):
|
||||
if time < 3:
|
||||
x_true[i] = 5 * time
|
||||
y_true[i] = 2 * time
|
||||
elif time < 6:
|
||||
angle = np.pi * (time - 3) / 15
|
||||
x_true[i] = 15 + 2 * np.cos(angle)
|
||||
y_true[i] = 6 + 2 * np.sin(angle)
|
||||
|
||||
|
||||
else:
|
||||
raise ValueError(f"未知的轨迹类型: {trajectory_type}")
|
||||
|
||||
# 真实位置
|
||||
true_positions = np.column_stack([x_true, y_true])
|
||||
|
||||
# 添加测量噪声
|
||||
noise = np.random.normal(0, noise_std, (n_points, 2))
|
||||
noisy_measurements = true_positions + noise
|
||||
|
||||
# 添加野值(跳点)
|
||||
outlier_indices = []
|
||||
if outlier_ratio > 0:
|
||||
n_outliers = int(n_points * outlier_ratio)
|
||||
outlier_indices = np.random.choice(n_points, n_outliers, replace=False)
|
||||
|
||||
for idx in outlier_indices:
|
||||
# 生成随机方向的野值
|
||||
outlier_direction = np.random.uniform(0, 2*np.pi)
|
||||
outlier_distance = outlier_magnitude * noise_std
|
||||
outlier_offset = outlier_distance * np.array([np.cos(outlier_direction),
|
||||
np.sin(outlier_direction)])
|
||||
noisy_measurements[idx] += outlier_offset
|
||||
|
||||
return true_positions, noisy_measurements, outlier_indices
|
||||
|
||||
def test_imm_filter(trajectory_type='maneuver', noise_std=0.5, n_points=300,
|
||||
outlier_ratio=0.0, outlier_magnitude=10.0):
|
||||
"""
|
||||
测试IMM滤波器
|
||||
|
||||
参数:
|
||||
trajectory_type: 轨迹类型
|
||||
noise_std: 测量噪声标准差
|
||||
n_points: 轨迹点数
|
||||
outlier_ratio: 野值比例
|
||||
outlier_magnitude: 野值幅度倍数
|
||||
"""
|
||||
# 时间步长
|
||||
dt = 0.01
|
||||
|
||||
# 生成轨迹
|
||||
true_positions, measurements, outlier_indices = generate_trajectory(
|
||||
n_points, trajectory_type, noise_std, outlier_ratio, outlier_magnitude)
|
||||
|
||||
# 初始化两个卡尔曼滤波器
|
||||
# 平滑模型(低过程噪声)- 适合匀速运动
|
||||
kf_smooth = KalmanFilter2D(dt, process_noise_std=0.1, measurement_noise_std=noise_std)
|
||||
# 机动模型(高过程噪声)- 适合机动运动
|
||||
kf_maneuver = KalmanFilter2D(dt, process_noise_std=10, measurement_noise_std=noise_std)
|
||||
|
||||
# 协调转弯模型 - 适合转弯运动
|
||||
# 增加过程噪声和角速度噪声以提高鲁棒性,防止发散
|
||||
ct_model = CTModelEKF(dt, process_noise_std=1.0, measurement_noise_std=noise_std, omega_noise_std=10.0)
|
||||
|
||||
# 模型转移矩阵: 假设有95%概率保持原模型
|
||||
M = np.array([[0.95, 0.05],
|
||||
[0.05, 0.95]])
|
||||
mu_init = np.array([0.5, 0.5]) # 初始模型概率
|
||||
|
||||
# 初始化每个滤波器的状态
|
||||
# 使用前两个测量点估计初始速度
|
||||
if len(measurements) > 1:
|
||||
initial_vx = (measurements[1, 0] - measurements[0, 0]) / dt
|
||||
initial_vy = (measurements[1, 1] - measurements[0, 1]) / dt
|
||||
else:
|
||||
initial_vx, initial_vy = 0, 0
|
||||
|
||||
initial_state_4d = np.array([[measurements[0, 0]], [measurements[0, 1]], [initial_vx], [initial_vy]])
|
||||
initial_state_5d = np.array([[measurements[0, 0]], [measurements[0, 1]], [initial_vx], [initial_vy], [0.01]]) # 给CT模型一个初始角速度猜测
|
||||
|
||||
kf_smooth.x = initial_state_4d.copy()
|
||||
kf_maneuver.x = initial_state_4d.copy()
|
||||
ct_model.x = initial_state_5d.copy()
|
||||
|
||||
# 创建IMM滤波器 - 使用改进后的单滤波器兼容性
|
||||
|
||||
imm = IMMFilter([kf_smooth,kf_maneuver],M,mu_init)
|
||||
# 存储结果
|
||||
estimated_positions = []
|
||||
estimated_velocities = []
|
||||
model_probabilities = []
|
||||
individual_model_positions = [[] for _ in range(len(imm.filters))] # 存储每个模型的独立估计
|
||||
|
||||
# 运行滤波器
|
||||
for i in range(len(measurements)):
|
||||
# 执行完整的滤波步骤(预测+更新)
|
||||
z = measurements[i].reshape(-1, 1)
|
||||
x, P, mu, velocity = imm.step(z)
|
||||
|
||||
# 保存IMM融合结果
|
||||
estimated_positions.append([x[0, 0], x[1, 0]])
|
||||
estimated_velocities.append([velocity[0], velocity[1]])
|
||||
model_probabilities.append(mu.copy())
|
||||
|
||||
# 保存每个模型的独立估计结果
|
||||
for j, filter_obj in enumerate(imm.filters):
|
||||
individual_model_positions[j].append([filter_obj.x[0, 0], filter_obj.x[1, 0]])
|
||||
|
||||
estimated_positions = np.array(estimated_positions)
|
||||
estimated_velocities = np.array(estimated_velocities)
|
||||
model_probabilities = np.array(model_probabilities)
|
||||
|
||||
# 计算误差
|
||||
position_errors = np.linalg.norm(true_positions - estimated_positions, axis=1)
|
||||
rmse = np.sqrt(np.mean(position_errors**2))
|
||||
|
||||
print(f"轨迹类型: {trajectory_type}")
|
||||
print(f"测量噪声标准差: {noise_std}")
|
||||
if outlier_ratio > 0:
|
||||
print(f"野值比例: {outlier_ratio:.1%}, 野值数量: {len(outlier_indices)}")
|
||||
print(f"野值幅度: {outlier_magnitude}倍噪声标准差")
|
||||
print(f"位置估计RMSE: {rmse:.3f}")
|
||||
|
||||
# 动态输出模型概率(根据实际滤波器类型确定名称)
|
||||
n_models = model_probabilities.shape[1]
|
||||
|
||||
# 根据实际传入的滤波器类型确定模型名称
|
||||
model_names = []
|
||||
for filter_obj in imm.filters:
|
||||
if isinstance(filter_obj, CTModelEKF):
|
||||
model_names.append('CT模型')
|
||||
elif isinstance(filter_obj, KalmanFilter2D):
|
||||
# 根据过程噪声区分平滑模型和机动模型
|
||||
if filter_obj.process_noise_std <= 0.1:
|
||||
model_names.append('平滑模型')
|
||||
else:
|
||||
model_names.append('机动模型')
|
||||
else:
|
||||
model_names.append(f'{type(filter_obj).__name__}')
|
||||
|
||||
for i in range(n_models):
|
||||
model_name = model_names[i] if i < len(model_names) else f'模型{i+1}'
|
||||
print(f"平均模型概率 - {model_name}: {np.mean(model_probabilities[:, i]):.3f}")
|
||||
|
||||
if n_models == 1:
|
||||
print("注意: 当前使用单个滤波器模式")
|
||||
|
||||
# 绘图
|
||||
fig = plt.figure(figsize=(15, 10))
|
||||
|
||||
# 子图1: 轨迹对比(交互式)
|
||||
ax1 = plt.subplot(2, 2, 1)
|
||||
plt.plot(true_positions[:, 0], true_positions[:, 1], 'g-', linewidth=2, label='真实轨迹')
|
||||
plt.scatter(measurements[:, 0], measurements[:, 1], c='r', s=10, alpha=0.5, label='噪声观测')
|
||||
# 标出野值点
|
||||
if len(outlier_indices) > 0:
|
||||
plt.scatter(measurements[outlier_indices, 0], measurements[outlier_indices, 1],
|
||||
c='orange', s=50, marker='x', linewidth=2, label='野值点')
|
||||
plt.plot(estimated_positions[:, 0], estimated_positions[:, 1], 'b-', linewidth=2, label='IMM估计')
|
||||
plt.xlabel('X位置')
|
||||
plt.ylabel('Y位置')
|
||||
title = f'{trajectory_type}轨迹跟踪结果'
|
||||
if outlier_ratio > 0:
|
||||
title += f' (野值{outlier_ratio:.1%})'
|
||||
plt.title(title + '\n')
|
||||
plt.legend()
|
||||
plt.grid(True)
|
||||
|
||||
|
||||
# 子图2: 位置误差
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.plot(position_errors, 'r-', linewidth=1)
|
||||
plt.xlabel('时间步')
|
||||
plt.ylabel('位置误差')
|
||||
plt.title(f'位置估计误差 (RMSE={rmse:.3f})')
|
||||
plt.grid(True)
|
||||
|
||||
# 子图3: 模型概率(动态适应模型数量)
|
||||
plt.subplot(2, 2, 3)
|
||||
|
||||
# 动态绘制模型概率曲线
|
||||
n_models = model_probabilities.shape[1]
|
||||
colors = ['b-', 'r-', 'g-', 'm-', 'c-', 'y-'] # 支持更多模型的颜色
|
||||
|
||||
# 使用与输出部分相同的模型名称逻辑
|
||||
for i in range(n_models):
|
||||
model_name = model_names[i] if i < len(model_names) else f'模型{i+1}'
|
||||
color = colors[i] if i < len(colors) else f'C{i}-'
|
||||
plt.plot(model_probabilities[:, i], color, linewidth=2, label=model_name)
|
||||
|
||||
plt.xlabel('时间步')
|
||||
plt.ylabel('模型概率')
|
||||
if n_models == 1:
|
||||
plt.title('模型概率变化 (单模型)')
|
||||
else:
|
||||
plt.title('模型概率变化')
|
||||
plt.legend()
|
||||
plt.grid(True)
|
||||
|
||||
# 子图4: 滤波速度
|
||||
plt.subplot(2, 2, 4)
|
||||
|
||||
# 计算真实速度(用于对比)
|
||||
true_velocities = np.zeros((len(true_positions), 2))
|
||||
for i in range(1, len(true_positions)):
|
||||
true_velocities[i, 0] = (true_positions[i, 0] - true_positions[i-1, 0]) / dt
|
||||
true_velocities[i, 1] = (true_positions[i, 1] - true_positions[i-1, 1]) / dt
|
||||
|
||||
# 绘制速度曲线
|
||||
time_steps = np.arange(len(estimated_velocities))
|
||||
plt.plot(time_steps, true_velocities[:, 0], 'g--', linewidth=1, alpha=0.7, label='真实Vx')
|
||||
plt.plot(time_steps, true_velocities[:, 1], 'g:', linewidth=1, alpha=0.7, label='真实Vy')
|
||||
plt.plot(time_steps, estimated_velocities[:, 0], 'b-', linewidth=2, label='估计Vx')
|
||||
plt.plot(time_steps, estimated_velocities[:, 1], 'r-', linewidth=2, label='估计Vy')
|
||||
|
||||
plt.xlabel('时间步')
|
||||
plt.ylabel('速度')
|
||||
plt.title('X和Y方向滤波速度')
|
||||
plt.legend()
|
||||
plt.grid(True)
|
||||
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
# 转换个体模型位置为numpy数组
|
||||
individual_model_positions = [np.array(pos) for pos in individual_model_positions]
|
||||
|
||||
# 创建新的独立图形:显示各个模型的独立滤波结果
|
||||
fig2 = plt.figure(figsize=(12, 8))
|
||||
plt.plot(true_positions[:, 0], true_positions[:, 1], 'g-', linewidth=3, label='真实轨迹', alpha=0.8)
|
||||
plt.scatter(measurements[:, 0], measurements[:, 1], c='gray', s=8, alpha=0.3, label='噪声观测')
|
||||
|
||||
# 标出野值点
|
||||
if len(outlier_indices) > 0:
|
||||
plt.scatter(measurements[outlier_indices, 0], measurements[outlier_indices, 1],
|
||||
c='orange', s=50, marker='x', linewidth=2, label='野值点')
|
||||
|
||||
# 绘制每个模型的独立估计轨迹
|
||||
colors = ['blue', 'red', 'purple', 'brown', 'pink', 'cyan']
|
||||
linestyles = ['-', '--', '-.', ':', '-', '--']
|
||||
|
||||
for i, (pos, filter_obj) in enumerate(zip(individual_model_positions, imm.filters)):
|
||||
model_name = model_names[i] if i < len(model_names) else f'模型{i+1}'
|
||||
color = colors[i] if i < len(colors) else f'C{i}'
|
||||
linestyle = linestyles[i] if i < len(linestyles) else '-'
|
||||
|
||||
plt.plot(pos[:, 0], pos[:, 1], color=color, linestyle=linestyle,
|
||||
linewidth=2, label=f'{model_name}独立估计', alpha=0.7)
|
||||
|
||||
# 绘制IMM融合结果
|
||||
plt.plot(estimated_positions[:, 0], estimated_positions[:, 1], 'k-',
|
||||
linewidth=2, label='IMM融合结果', alpha=0.9)
|
||||
|
||||
plt.xlabel('X位置')
|
||||
plt.ylabel('Y位置')
|
||||
title = f'{trajectory_type}轨迹 - 各模型独立滤波结果对比'
|
||||
if outlier_ratio > 0:
|
||||
title += f' (野值{outlier_ratio:.1%})'
|
||||
plt.title(title)
|
||||
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
|
||||
plt.grid(True, alpha=0.3)
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
return rmse, model_probabilities, estimated_velocities
|
||||
|
||||
|
||||
|
||||
def test_one_filter(trajectory_type='maneuver', noise_std=0.5, n_points=300,
|
||||
outlier_ratio=0.0, outlier_magnitude=10.0):
|
||||
# 时间步长
|
||||
dt = 0.01
|
||||
|
||||
# 生成轨迹
|
||||
true_positions, measurements, outlier_indices = generate_trajectory(
|
||||
n_points, trajectory_type, noise_std, outlier_ratio, outlier_magnitude)
|
||||
|
||||
|
||||
# 初始化每个滤波器的状态
|
||||
# 使用前两个测量点估计初始速度
|
||||
if len(measurements) > 1:
|
||||
initial_vx = (measurements[1, 0] - measurements[0, 0]) / dt
|
||||
initial_vy = (measurements[1, 1] - measurements[0, 1]) / dt
|
||||
else:
|
||||
initial_vx, initial_vy = 0, 0
|
||||
|
||||
|
||||
initial_state_4d = np.array([[measurements[0, 0]], [measurements[0, 1]], [initial_vx], [initial_vy]])
|
||||
initial_state_5d = np.array([[measurements[0, 0]], [measurements[0, 1]], [initial_vx], [initial_vy], [0.0001]]) # 给CT模型一个初始角速度猜测
|
||||
|
||||
|
||||
# 平滑模型(低过程噪声)- 适合匀速运动
|
||||
filter = KalmanFilter2D(dt, process_noise_std=8, measurement_noise_std=noise_std)
|
||||
#filter = CTModelEKF(dt, process_noise_std=1.0, measurement_noise_std=noise_std, omega_noise_std=10.0)
|
||||
|
||||
filter.x = initial_state_4d.copy()
|
||||
|
||||
|
||||
# 存储结果
|
||||
estimated_positions = []
|
||||
estimated_velocities = []
|
||||
|
||||
# 运行滤波器
|
||||
for i in range(len(measurements)):
|
||||
# 执行完整的滤波步骤(预测+更新)
|
||||
z = measurements[i].reshape(-1, 1)
|
||||
x, P, mu = filter.step(z)
|
||||
# 保存
|
||||
estimated_positions.append([x[0, 0], x[1, 0]])
|
||||
#estimated_velocities.append([velocity[0], velocity[1]])
|
||||
|
||||
estimated_positions = np.array(estimated_positions)
|
||||
estimated_velocities = np.array(estimated_velocities)
|
||||
|
||||
# 计算误差
|
||||
position_errors = np.linalg.norm(true_positions - estimated_positions, axis=1)
|
||||
rmse = np.sqrt(np.mean(position_errors**2))
|
||||
|
||||
# 绘图
|
||||
fig = plt.figure(figsize=(15, 10))
|
||||
|
||||
# 子图1: 轨迹对比(交互式)
|
||||
ax1 = plt.subplot(2, 1, 1)
|
||||
plt.plot(true_positions[:, 0], true_positions[:, 1], 'g-', linewidth=2, label='真实轨迹')
|
||||
plt.scatter(measurements[:, 0], measurements[:, 1], c='r', s=10, alpha=0.5, label='噪声观测')
|
||||
# 标出野值点
|
||||
if len(outlier_indices) > 0:
|
||||
plt.scatter(measurements[outlier_indices, 0], measurements[outlier_indices, 1],
|
||||
c='orange', s=50, marker='x', linewidth=2, label='野值点')
|
||||
plt.plot(estimated_positions[:, 0], estimated_positions[:, 1], 'b-', linewidth=2, label='Filter估计')
|
||||
plt.xlabel('X位置')
|
||||
plt.ylabel('Y位置')
|
||||
title = f'{trajectory_type}轨迹跟踪结果'
|
||||
if outlier_ratio > 0:
|
||||
title += f' (野值{outlier_ratio:.1%})'
|
||||
plt.title(title + '\n')
|
||||
plt.legend()
|
||||
plt.grid(True)
|
||||
|
||||
|
||||
# 子图2: 位置误差
|
||||
plt.subplot(2, 1, 2)
|
||||
plt.plot(position_errors, 'r-', linewidth=1)
|
||||
plt.xlabel('时间步')
|
||||
plt.ylabel('位置误差')
|
||||
plt.title(f'位置估计误差 (RMSE={rmse:.3f})')
|
||||
plt.grid(True)
|
||||
|
||||
plt.show()
|
||||
|
||||
return rmse,1.0, estimated_velocities
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
trajectory_types = ['linear', 'circular', 'figure8', 'maneuver','my']
|
||||
|
||||
#rmse, model_probs, velocities = test_imm_filter('my', noise_std = 0.1, n_points=300,outlier_ratio=0.01,outlier_magnitude=20)
|
||||
|
||||
test_one_filter('my', noise_std = 0.1, n_points=300,outlier_ratio=0.01,outlier_magnitude=1)
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in new issue