主题059:磁导航与定位

场景描述

磁导航与定位技术利用环境中分布的磁场特征或人工布置的磁标,通过测量磁场强度和方向来确定目标的位置和姿态。该技术广泛应用于室内定位、机器人导航、医疗设备追踪、地下管线探测等领域。本教程将系统讲解磁场指纹地图构建、磁定位算法、多传感器融合、精度分析和干扰抑制等核心技术。


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

数学/物理模型

1. 磁偶极子磁场模型

永磁体产生的磁场可用磁偶极子模型描述:

B⃗(r⃗)=μ04πr3[3(m⃗⋅r^)r^−m⃗]\vec{B}(\vec{r}) = \frac{\mu_0}{4\pi r^3} [3(\vec{m} \cdot \hat{r})\hat{r} - \vec{m}]B (r )=4πr3μ0[3(m r^)r^m ]

其中:

  • B⃗\vec{B}B :磁感应强度 (T)
  • μ0=4π×10−7\mu_0 = 4\pi \times 10^{-7}μ0=4π×107 H/m:真空磁导率
  • m⃗\vec{m}m :磁矩 (A·m²)
  • r⃗\vec{r}r :距离矢量
  • rrr:距离

简化标量形式:

B=μ0m4πr3B = \frac{\mu_0 m}{4\pi r^3}B=4πr3μ0m

2. 磁场指纹定位原理

磁场指纹定位基于空间位置的磁场特征唯一性:

p^=arg⁡min⁡p∈P∥Bmeas−Bfp(p)∥\hat{p} = \arg\min_{p \in P} \| B_{meas} - B_{fp}(p) \|p^=argpPminBmeasBfp(p)

其中:

  • p^\hat{p}p^:估计位置
  • PPP:指纹地图位置集合
  • BmeasB_{meas}Bmeas:实测磁场向量
  • Bfp(p)B_{fp}(p)Bfp(p):位置ppp处的指纹磁场

3. 卡尔曼滤波器

多传感器融合使用卡尔曼滤波进行状态估计:

预测步
x^k∣k−1=Fkx^k−1∣k−1\hat{x}_{k|k-1} = F_k \hat{x}_{k-1|k-1}x^kk1=Fkx^k1∣k1
Pk∣k−1=FkPk−1∣k−1FkT+QkP_{k|k-1} = F_k P_{k-1|k-1} F_k^T + Q_kPkk1=FkPk1∣k1FkT+Qk

更新步
Kk=Pk∣k−1HkT(HkPk∣k−1HkT+Rk)−1K_k = P_{k|k-1} H_k^T (H_k P_{k|k-1} H_k^T + R_k)^{-1}Kk=Pkk1HkT(HkPkk1HkT+Rk)1
x^k∣k=x^k∣k−1+Kk(zk−Hkx^k∣k−1)\hat{x}_{k|k} = \hat{x}_{k|k-1} + K_k (z_k - H_k \hat{x}_{k|k-1})x^kk=x^kk1+Kk(zkHkx^kk1)
Pk∣k=(I−KkHk)Pk∣k−1P_{k|k} = (I - K_k H_k) P_{k|k-1}Pkk=(IKkHk)Pkk1

4. 定位误差分析

定位误差与多个因素相关:

σp=∑i=1N(∂p∂Bi)2σBi2\sigma_p = \sqrt{\sum_{i=1}^{N} \left(\frac{\partial p}{\partial B_i}\right)^2 \sigma_{B_i}^2}σp=i=1N(Bip)2σBi2

其中:

  • σp\sigma_pσp:位置估计标准差
  • σBi\sigma_{B_i}σBi:第iii个磁标的测量噪声
  • NNN:磁标数量

5. RANSAC鲁棒估计

随机采样一致性算法用于干扰抑制:

  1. 随机选择最小样本集
  2. 计算模型参数
  3. 统计内点数量
  4. 迭代至找到最优模型

环境准备

依赖库安装

pip install numpy matplotlib scipy pillow

导入库

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, FancyArrowPatch, Rectangle
from matplotlib.collections import LineCollection
import os

# 强制使用Agg后端
plt.switch_backend('Agg')

# 物理常数
MU_0 = 4 * np.pi * 1e-7  # 真空磁导率 (H/m)

完整代码实现

案例1:磁场指纹地图构建

def magnetic_fingerprint_map():
    """
    磁场指纹地图构建
    模拟室内环境中永磁体产生的磁场分布
    """
    # 房间尺寸 (m)
    room_width = 10
    room_height = 8
    
    # 创建网格
    x = np.linspace(0, room_width, 100)
    y = np.linspace(0, room_height, 80)
    X, Y = np.meshgrid(x, y)
    
    # 永磁体位置
    magnets = [
        {'pos': (1, 1), 'm': 0.5, 'name': '磁标A'},
        {'pos': (9, 1), 'm': 0.6, 'name': '磁标B'},
        {'pos': (1, 7), 'm': 0.4, 'name': '磁标C'},
        {'pos': (9, 7), 'm': 0.55, 'name': '磁标D'},
        {'pos': (5, 4), 'm': 0.8, 'name': '中心标'},
    ]
    
    # 计算磁场
    def dipole_field(X, Y, x0, y0, m):
        r_x = X - x0
        r_y = Y - y0
        r = np.sqrt(r_x**2 + r_y**2)
        r = np.maximum(r, 0.1)
        B = MU_0 * m / (4 * np.pi * r**3) * 1e6
        return B
    
    # 计算总磁场
    B_total = np.zeros_like(X)
    for magnet in magnets:
        B_magnet = dipole_field(X, Y, magnet['pos'][0], magnet['pos'][1], magnet['m'])
        B_total += B_magnet
    
    # 添加环境噪声
    np.random.seed(42)
    noise = np.random.normal(0, 0.5, B_total.shape)
    B_measured = B_total + noise
    
    # 采样点
    sample_x = np.linspace(1, 9, 20)
    sample_y = np.linspace(1, 7, 16)
    sample_X, sample_Y = np.meshgrid(sample_x, sample_y)
    
    # 插值获取采样点磁场值
    from scipy.interpolate import griddata
    points = np.column_stack((X.ravel(), Y.ravel()))
    values = B_total.ravel()
    sample_points = np.column_stack((sample_X.ravel(), sample_Y.ravel()))
    B_samples = griddata(points, values, sample_points, method='linear')
    
    # 可视化...

案例2:磁定位算法实现

def magnetic_localization_algorithm():
    """
    磁定位算法实现
    基于磁场指纹的最近邻定位算法
    """
    # 房间尺寸
    room_width = 10
    room_height = 8
    
    # 构建指纹数据库
    x_grid = np.linspace(0, room_width, 50)
    y_grid = np.linspace(0, room_height, 40)
    
    # 磁标位置
    beacons = [
        {'pos': (2, 2), 'm': 1.0},
        {'pos': (8, 2), 'm': 1.0},
        {'pos': (2, 6), 'm': 1.0},
        {'pos': (8, 6), 'm': 1.0},
    ]
    
    # 生成指纹地图
    fingerprint_db = []
    positions_db = []
    
    for x in x_grid:
        for y in y_grid:
            distances = [np.sqrt((x-b['pos'][0])**2 + (y-b['pos'][1])**2) for b in beacons]
            B_measured = [MU_0 * b['m'] / (4 * np.pi * (d**3 + 0.01)) * 1e6 
                         for b, d in zip(beacons, distances)]
            B_measured = [b + np.random.normal(0, 0.1) for b in B_measured]
            
            fingerprint_db.append(B_measured)
            positions_db.append((x, y))
    
    fingerprint_db = np.array(fingerprint_db)
    positions_db = np.array(positions_db)
    
    # 测试定位
    t = np.linspace(0, 4*np.pi, 100)
    true_x = 5 + 2 * np.cos(t)
    true_y = 4 + 1.5 * np.sin(2*t)
    
    # 最近邻定位
    estimated_positions_knn = []
    
    for tx, ty in zip(true_x, true_y):
        distances = [np.sqrt((tx-b['pos'][0])**2 + (ty-b['pos'][1])**2) for b in beacons]
        B_test = [MU_0 * b['m'] / (4 * np.pi * (d**3 + 0.01)) * 1e6 for b, d in zip(beacons, distances)]
        B_test = [b + np.random.normal(0, 0.2) for b in B_test]
        
        # 最近邻搜索
        distances_to_db = np.linalg.norm(fingerprint_db - B_test, axis=1)
        nearest_idx = np.argmin(distances_to_db)
        estimated_positions_knn.append(positions_db[nearest_idx])
    
    estimated_positions_knn = np.array(estimated_positions_knn)
    
    # 计算定位误差
    errors_knn = np.sqrt((estimated_positions_knn[:, 0] - true_x)**2 + 
                         (estimated_positions_knn[:, 1] - true_y)**2)
    
    # 可视化...

案例3:多传感器融合定位

def multisensor_fusion():
    """
    多传感器融合定位
    融合磁场、IMU和轮速计数据
    """
    # 仿真参数
    dt = 0.1  # 时间步长 (s)
    T = 30    # 总时间 (s)
    time = np.arange(0, T, dt)
    n_steps = len(time)
    
    # 真实轨迹
    true_x = np.zeros(n_steps)
    true_y = np.zeros(n_steps)
    true_vx = np.zeros(n_steps)
    true_vy = np.zeros(n_steps)
    
    # 生成轨迹
    for i in range(1, n_steps):
        if i < n_steps // 3:
            true_vx[i] = 0.5
            true_vy[i] = 0
        elif i < 2 * n_steps // 3:
            true_vx[i] = 0.5 * np.cos((i - n_steps//3) * 0.05)
            true_vy[i] = 0.5 * np.sin((i - n_steps//3) * 0.05)
        else:
            true_vx[i] = 0
            true_vy[i] = 0.5
        
        true_x[i] = true_x[i-1] + true_vx[i] * dt
        true_y[i] = true_y[i-1] + true_vy[i] * dt
    
    # 传感器噪声
    imu_noise = 0.05
    wheel_noise = 0.1
    mag_noise = 0.5
    
    # 磁标位置
    beacons = [(2, 2), (8, 2), (2, 6), (8, 6)]
    
    # IMU测量
    ax_meas = np.gradient(true_vx, dt) + np.random.normal(0, imu_noise, n_steps)
    ay_meas = np.gradient(true_vy, dt) + np.random.normal(0, imu_noise, n_steps)
    
    # 轮速计测量
    vx_wheel = true_vx + np.random.normal(0, wheel_noise, n_steps)
    vy_wheel = true_vy + np.random.normal(0, wheel_noise, n_steps)
    
    # 卡尔曼滤波器
    x_est = np.zeros((n_steps, 4))
    P_est = np.zeros((n_steps, 4, 4))
    
    x_est[0] = [0, 0, 0, 0]
    P_est[0] = np.eye(4) * 10
    
    F = np.array([[1, 0, dt, 0],
                  [0, 1, 0, dt],
                  [0, 0, 1, 0],
                  [0, 0, 0, 1]])
    
    Q = np.eye(4) * 0.01
    R_wheel = np.eye(2) * wheel_noise**2
    
    for k in range(1, n_steps):
        # 预测
        x_pred = F @ x_est[k-1]
        P_pred = F @ P_est[k-1] @ F.T + Q
        
        # 更新 (轮速计)
        H_wheel = np.array([[0, 0, 1, 0],
                           [0, 0, 0, 1]])
        z_wheel = np.array([vx_wheel[k], vy_wheel[k]])
        y_wheel = z_wheel - H_wheel @ x_pred
        S_wheel = H_wheel @ P_pred @ H_wheel.T + R_wheel
        K_wheel = P_pred @ H_wheel.T @ np.linalg.inv(S_wheel)
        x_pred = x_pred + K_wheel @ y_wheel
        P_pred = (np.eye(4) - K_wheel @ H_wheel) @ P_pred
        
        # 更新 (磁场)
        if k % 5 == 0:
            H_mag = np.array([[1, 0, 0, 0],
                             [0, 1, 0, 0]])
            z_mag = np.array([true_x[k] + np.random.normal(0, 0.3),
                             true_y[k] + np.random.normal(0, 0.3)])
            y_mag = z_mag - H_mag @ x_pred
            S_mag = H_mag @ P_pred @ H_mag.T + np.eye(2) * 0.3**2
            K_mag = P_pred @ H_mag.T @ np.linalg.inv(S_mag)
            x_pred = x_pred + K_mag @ y_mag
            P_pred = (np.eye(4) - K_mag @ H_mag) @ P_pred
        
        x_est[k] = x_pred
        P_est[k] = P_pred
    
    # 计算误差
    error_fusion = np.sqrt((x_est[:, 0] - true_x)**2 + (x_est[:, 1] - true_y)**2)
    
    # 可视化...

案例4:定位精度分析

def localization_accuracy_analysis():
    """
    定位精度分析
    分析磁标数量、噪声水平、网格密度对精度的影响
    """
    # 测试不同磁标数量
    beacon_counts = [2, 3, 4, 5, 6]
    accuracy_vs_beacons = []
    
    room_width, room_height = 10, 8
    
    for n_beacons in beacon_counts:
        np.random.seed(n_beacons)
        beacons = [(np.random.uniform(1, room_width-1), np.random.uniform(1, room_height-1)) 
                   for _ in range(n_beacons)]
        
        test_points = [(5, 4), (3, 3), (7, 5), (2, 6), (8, 2)]
        errors = []
        
        for tp in test_points:
            B_true = []
            for beacon in beacons:
                d = np.sqrt((tp[0] - beacon[0])**2 + (tp[1] - beacon[1])**2)
                B = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
                B_true.append(B)
            
            B_meas = [b + np.random.normal(0, 0.5) for b in B_true]
            
            # 最近邻定位
            min_error = float('inf')
            for x in np.linspace(0, room_width, 30):
                for y in np.linspace(0, room_height, 24):
                    B_candidate = []
                    for beacon in beacons:
                        d = np.sqrt((x - beacon[0])**2 + (y - beacon[1])**2)
                        B = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
                        B_candidate.append(B)
                    
                    error = np.linalg.norm(np.array(B_candidate) - np.array(B_meas))
                    if error < min_error:
                        min_error = error
                        best_pos = (x, y)
            
            pos_error = np.sqrt((best_pos[0] - tp[0])**2 + (best_pos[1] - tp[1])**2)
            errors.append(pos_error)
        
        accuracy_vs_beacons.append(np.mean(errors) * 100)
    
    # 可视化...

案例5:干扰抑制与鲁棒性

def interference_robustness():
    """
    干扰抑制与鲁棒性
    分析铁磁性干扰源对定位的影响及RANSAC抑制方法
    """
    room_width, room_height = 10, 8
    
    # 磁标位置
    beacons = [(2, 2), (8, 2), (2, 6), (8, 6)]
    
    # 干扰源位置
    interferences = [
        {'pos': (5, 4), 'strength': 5.0},
        {'pos': (3, 5), 'strength': 2.0},
    ]
    
    # 测试轨迹
    t = np.linspace(0, 2*np.pi, 80)
    true_x = 5 + 2.5 * np.cos(t)
    true_y = 4 + 2 * np.sin(t)
    
    # 模拟测量
    def measure_field(x, y, beacons, interferences, noise=0.5):
        B_meas = []
        for beacon in beacons:
            d = np.sqrt((x - beacon[0])**2 + (y - beacon[1])**2)
            B_beacon = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
            
            B_interf = 0
            for interf in interferences:
                d_i = np.sqrt((x - interf['pos'][0])**2 + (y - interf['pos'][1])**2)
                B_interf += MU_0 * interf['strength'] / (4 * np.pi * (d_i**3 + 0.01)) * 1e6
            
            B_total = B_beacon + B_interf + np.random.normal(0, noise)
            B_meas.append(B_total)
        return B_meas
    
    # RANSAC鲁棒定位
    estimated_robust = []
    for x, y in zip(true_x, true_y):
        B_meas = measure_field(x, y, beacons, interferences, noise=0.5)
        
        best_pos = (x, y)
        min_median_error = float('inf')
        
        for _ in range(20):  # RANSAC迭代
            selected = np.random.choice(len(beacons), 3, replace=False)
            
            min_error = float('inf')
            candidate_pos = (x, y)
            
            for xi in np.linspace(0, room_width, 30):
                for yi in np.linspace(0, room_height, 24):
                    B_candidate = [measure_field(xi, yi, [beacons[i]], [], noise=0)[0] 
                                  for i in selected]
                    B_meas_selected = [B_meas[i] for i in selected]
                    error = np.linalg.norm(np.array(B_candidate) - np.array(B_meas_selected))
                    if error < min_error:
                        min_error = error
                        candidate_pos = (xi, yi)
            
            B_pred = measure_field(candidate_pos[0], candidate_pos[1], beacons, [], noise=0)
            residuals = np.abs(np.array(B_pred) - np.array(B_meas))
            median_residual = np.median(residuals)
            
            if median_residual < min_median_error:
                min_median_error = median_residual
                best_pos = candidate_pos
        
        estimated_robust.append(best_pos)
    
    estimated_robust = np.array(estimated_robust)
    
    # 计算误差
    error_robust = np.sqrt((estimated_robust[:, 0] - true_x)**2 + (estimated_robust[:, 1] - true_y)**2)
    
    # 可视化...

代码深度解析

磁偶极子磁场计算

def dipole_field(X, Y, x0, y0, m):
    """
    计算磁偶极子产生的磁场
    
    参数:
        X, Y: 计算网格坐标
        x0, y0: 磁偶极子位置
        m: 磁矩 (A·m²)
    
    返回:
        B: 磁场强度 (μT)
    
    原理:
        磁偶极子磁场随距离立方衰减
        B = μ₀m / (4πr³)
        
        物理意义:
        - 距离增加1倍,场强减小为1/8
        - 磁矩越大,场强越强
        - 近场需要更精确模型
    """
    r_x = X - x0
    r_y = Y - y0
    r = np.sqrt(r_x**2 + r_y**2)
    r = np.maximum(r, 0.1)  # 避免除零和近场奇异
    
    B = MU_0 * m / (4 * np.pi * r**3) * 1e6  # 转换为μT
    return B

关键说明

  • 使用立方反比定律描述磁场衰减
  • 限制最小距离避免数值奇异
  • 单位转换为微特斯拉便于显示

最近邻定位算法

# 最近邻搜索
distances_to_db = np.linalg.norm(fingerprint_db - B_test, axis=1)
nearest_idx = np.argmin(distances_to_db)
estimated_position = positions_db[nearest_idx]

算法原理

  • 计算实测磁场与指纹库中所有条目的欧氏距离
  • 选择距离最小的指纹对应的位置作为估计
  • 简单高效,但精度受指纹密度限制

卡尔曼滤波实现

# 预测
x_pred = F @ x_est[k-1]
P_pred = F @ P_est[k-1] @ F.T + Q

# 更新
K = P_pred @ H.T @ np.linalg.inv(H @ P_pred @ H.T + R)
x_est = x_pred + K @ (z - H @ x_pred)
P_est = (np.eye(4) - K @ H) @ P_pred

滤波过程

  • 预测步:根据运动模型预测下一时刻状态
  • 更新步:融合传感器测量修正预测
  • 卡尔曼增益:自动权衡预测与测量的可信度

RANSAC鲁棒估计

for _ in range(20):  # RANSAC迭代
    # 随机选择子集
    selected = np.random.choice(len(beacons), 3, replace=False)
    
    # 用子集估计位置
    candidate_pos = estimate_position(selected)
    
    # 计算所有磁标的残差
    residuals = compute_residuals(candidate_pos, all_beacons)
    median_residual = np.median(residuals)
    
    # 保留最优模型
    if median_residual < min_median_error:
        best_pos = candidate_pos

鲁棒性原理

  • 随机采样避免干扰磁标的影响
  • 使用中位数而非均值评估模型
  • 迭代寻找最一致的内点集合

"""
主题059: 磁导航与定位仿真

本仿真涵盖:
1. 磁场指纹地图构建
2. 磁定位算法实现
3. 多传感器融合定位
4. 定位精度分析
5. 干扰抑制与鲁棒性
"""

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, FancyArrowPatch, Rectangle
from matplotlib.collections import LineCollection
import os

# 强制使用Agg后端
plt.switch_backend('Agg')

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

# 物理常数
MU_0 = 4 * np.pi * 1e-7  # 真空磁导率 (H/m)


def magnetic_fingerprint_map():
    """
    案例1: 磁场指纹地图构建
    模拟室内环境中永磁体产生的磁场分布,构建定位用的磁场指纹地图
    """
    print("=" * 60)
    print("案例1: 磁场指纹地图构建")
    print("=" * 60)
    
    # 房间尺寸 (m)
    room_width = 10
    room_height = 8
    
    # 创建网格
    x = np.linspace(0, room_width, 100)
    y = np.linspace(0, room_height, 80)
    X, Y = np.meshgrid(x, y)
    
    # 永磁体位置 (房间角落和墙壁)
    magnets = [
        {'pos': (1, 1), 'm': 0.5, 'name': '磁标A'},
        {'pos': (9, 1), 'm': 0.6, 'name': '磁标B'},
        {'pos': (1, 7), 'm': 0.4, 'name': '磁标C'},
        {'pos': (9, 7), 'm': 0.55, 'name': '磁标D'},
        {'pos': (5, 4), 'm': 0.8, 'name': '中心标'},
    ]
    
    # 计算磁场 (磁偶极子模型)
    def dipole_field(X, Y, x0, y0, m):
        """计算磁偶极子在2D平面产生的磁场"""
        r_x = X - x0
        r_y = Y - y0
        r = np.sqrt(r_x**2 + r_y**2)
        r = np.maximum(r, 0.1)  # 避免除零
        
        # 磁场强度 (简化模型)
        B = MU_0 * m / (4 * np.pi * r**3) * 1e6  # 转换为μT
        return B
    
    # 计算总磁场
    B_total = np.zeros_like(X)
    for magnet in magnets:
        B_magnet = dipole_field(X, Y, magnet['pos'][0], magnet['pos'][1], magnet['m'])
        B_total += B_magnet
    
    # 添加环境噪声
    np.random.seed(42)
    noise = np.random.normal(0, 0.5, B_total.shape)
    B_measured = B_total + noise
    
    # 采样点 (用于构建指纹地图)
    sample_x = np.linspace(1, 9, 20)
    sample_y = np.linspace(1, 7, 16)
    sample_X, sample_Y = np.meshgrid(sample_x, sample_y)
    
    # 插值获取采样点磁场值
    from scipy.interpolate import griddata
    points = np.column_stack((X.ravel(), Y.ravel()))
    values = B_total.ravel()
    sample_points = np.column_stack((sample_X.ravel(), sample_Y.ravel()))
    B_samples = griddata(points, values, sample_points, method='linear')
    
    # 可视化
    fig, axes = plt.subplots(2, 2, figsize=(14, 11))
    
    # 图1: 磁场分布热力图
    ax1 = axes[0, 0]
    im1 = ax1.contourf(X, Y, B_total, levels=50, cmap='viridis')
    ax1.set_xlabel('X 位置 (m)', fontsize=11)
    ax1.set_ylabel('Y 位置 (m)', fontsize=11)
    ax1.set_title('磁场指纹地图 (理论值)', fontsize=12, fontweight='bold')
    ax1.set_aspect('equal')
    
    # 标记磁标位置
    for magnet in magnets:
        ax1.plot(magnet['pos'][0], magnet['pos'][1], 'r*', markersize=15)
        ax1.annotate(magnet['name'], xy=magnet['pos'], xytext=(5, 5),
                    textcoords='offset points', fontsize=9, color='white')
    
    cbar1 = plt.colorbar(im1, ax=ax1)
    cbar1.set_label('磁场强度 (μT)', fontsize=10)
    
    # 图2: 带噪声的测量磁场
    ax2 = axes[0, 1]
    im2 = ax2.contourf(X, Y, B_measured, levels=50, cmap='viridis')
    ax2.set_xlabel('X 位置 (m)', fontsize=11)
    ax2.set_ylabel('Y 位置 (m)', fontsize=11)
    ax2.set_title('磁场指纹地图 (含噪声)', fontsize=12, fontweight='bold')
    ax2.set_aspect('equal')
    
    for magnet in magnets:
        ax2.plot(magnet['pos'][0], magnet['pos'][1], 'r*', markersize=15)
    
    cbar2 = plt.colorbar(im2, ax=ax2)
    cbar2.set_label('磁场强度 (μT)', fontsize=10)
    
    # 图3: 采样点分布
    ax3 = axes[1, 0]
    scatter = ax3.scatter(sample_X.ravel(), sample_Y.ravel(), 
                         c=B_samples, cmap='viridis', s=30, alpha=0.7)
    ax3.set_xlabel('X 位置 (m)', fontsize=11)
    ax3.set_ylabel('Y 位置 (m)', fontsize=11)
    ax3.set_title('指纹采样点分布 (20×16网格)', fontsize=12, fontweight='bold')
    ax3.set_aspect('equal')
    ax3.grid(True, alpha=0.3)
    
    for magnet in magnets:
        ax3.plot(magnet['pos'][0], magnet['pos'][1], 'r*', markersize=15)
        ax3.annotate(magnet['name'], xy=magnet['pos'], xytext=(5, 5),
                    textcoords='offset points', fontsize=9)
    
    cbar3 = plt.colorbar(scatter, ax=ax3)
    cbar3.set_label('磁场强度 (μT)', fontsize=10)
    
    # 图4: 磁场梯度分析
    ax4 = axes[1, 1]
    grad_x, grad_y = np.gradient(B_total)
    gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
    
    im4 = ax4.contourf(X, Y, gradient_magnitude, levels=50, cmap='hot')
    ax4.set_xlabel('X 位置 (m)', fontsize=11)
    ax4.set_ylabel('Y 位置 (m)', fontsize=11)
    ax4.set_title('磁场梯度分布 (定位敏感度)', fontsize=12, fontweight='bold')
    ax4.set_aspect('equal')
    
    for magnet in magnets:
        ax4.plot(magnet['pos'][0], magnet['pos'][1], 'w*', markersize=15)
    
    cbar4 = plt.colorbar(im4, ax=ax4)
    cbar4.set_label('梯度 (μT/m)', fontsize=10)
    
    plt.suptitle('磁导航与定位 - 磁场指纹地图构建', fontsize=14, fontweight='bold', y=0.98)
    plt.tight_layout()
    plt.savefig('fig1_magnetic_fingerprint.png', dpi=150, bbox_inches='tight')
    plt.close()
    
    print(f"✓ 磁场指纹地图构建完成")
    print(f"  磁标数量: {len(magnets)}")
    print(f"  采样点数量: {len(B_samples)}")
    print(f"  磁场范围: {B_total.min():.2f} - {B_total.max():.2f} μT")
    
    return X, Y, B_total, magnets, sample_points, B_samples


def magnetic_localization_algorithm():
    """
    案例2: 磁定位算法实现
    实现基于磁场指纹的最近邻定位算法和三边定位算法
    """
    print("\n" + "=" * 60)
    print("案例2: 磁定位算法实现")
    print("=" * 60)
    
    # 房间尺寸
    room_width = 10
    room_height = 8
    
    # 构建指纹数据库
    x_grid = np.linspace(0, room_width, 50)
    y_grid = np.linspace(0, room_height, 40)
    
    # 磁标位置
    beacons = [
        {'pos': (2, 2), 'm': 1.0},
        {'pos': (8, 2), 'm': 1.0},
        {'pos': (2, 6), 'm': 1.0},
        {'pos': (8, 6), 'm': 1.0},
    ]
    
    # 生成指纹地图
    fingerprint_db = []
    positions_db = []
    
    for x in x_grid:
        for y in y_grid:
            # 计算该位置到各磁标的距离
            distances = [np.sqrt((x-b['pos'][0])**2 + (y-b['pos'][1])**2) for b in beacons]
            
            # 模拟磁场测量值 (距离衰减模型)
            B_measured = [MU_0 * b['m'] / (4 * np.pi * (d**3 + 0.01)) * 1e6 for b, d in zip(beacons, distances)]
            
            # 添加测量噪声
            B_measured = [b + np.random.normal(0, 0.1) for b in B_measured]
            
            fingerprint_db.append(B_measured)
            positions_db.append((x, y))
    
    fingerprint_db = np.array(fingerprint_db)
    positions_db = np.array(positions_db)
    
    # 测试定位
    # 真实轨迹
    t = np.linspace(0, 4*np.pi, 100)
    true_x = 5 + 2 * np.cos(t)
    true_y = 4 + 1.5 * np.sin(2*t)
    
    # 最近邻定位
    estimated_positions_knn = []
    
    for tx, ty in zip(true_x, true_y):
        # 模拟测量值
        distances = [np.sqrt((tx-b['pos'][0])**2 + (ty-b['pos'][1])**2) for b in beacons]
        B_test = [MU_0 * b['m'] / (4 * np.pi * (d**3 + 0.01)) * 1e6 for b, d in zip(beacons, distances)]
        B_test = [b + np.random.normal(0, 0.2) for b in B_test]
        
        # 最近邻搜索
        distances_to_db = np.linalg.norm(fingerprint_db - B_test, axis=1)
        nearest_idx = np.argmin(distances_to_db)
        estimated_positions_knn.append(positions_db[nearest_idx])
    
    estimated_positions_knn = np.array(estimated_positions_knn)
    
    # 计算定位误差
    errors_knn = np.sqrt((estimated_positions_knn[:, 0] - true_x)**2 + 
                         (estimated_positions_knn[:, 1] - true_y)**2)
    
    # 可视化
    fig, axes = plt.subplots(2, 2, figsize=(14, 11))
    
    # 图1: 真实轨迹与估计轨迹对比
    ax1 = axes[0, 0]
    ax1.plot(true_x, true_y, 'b-', linewidth=2, label='真实轨迹', alpha=0.7)
    ax1.plot(estimated_positions_knn[:, 0], estimated_positions_knn[:, 1], 
             'r--', linewidth=1.5, label='KNN估计轨迹', alpha=0.7)
    
    # 绘制磁标
    for beacon in beacons:
        ax1.plot(beacon['pos'][0], beacon['pos'][1], 'g^', markersize=12, label='磁标' if beacon == beacons[0] else '')
    
    ax1.set_xlabel('X 位置 (m)', fontsize=11)
    ax1.set_ylabel('Y 位置 (m)', fontsize=11)
    ax1.set_title('定位轨迹对比 (KNN算法)', fontsize=12, fontweight='bold')
    ax1.legend(loc='upper right')
    ax1.grid(True, alpha=0.3)
    ax1.set_aspect('equal')
    ax1.set_xlim(0, room_width)
    ax1.set_ylim(0, room_height)
    
    # 图2: 定位误差分布
    ax2 = axes[0, 1]
    ax2.hist(errors_knn * 100, bins=30, color='steelblue', edgecolor='black', alpha=0.7)
    ax2.axvline(np.mean(errors_knn) * 100, color='red', linestyle='--', linewidth=2, 
                label=f'平均误差: {np.mean(errors_knn)*100:.1f} cm')
    ax2.set_xlabel('定位误差 (cm)', fontsize=11)
    ax2.set_ylabel('频次', fontsize=11)
    ax2.set_title('定位误差分布', fontsize=12, fontweight='bold')
    ax2.legend()
    ax2.grid(True, alpha=0.3)
    
    # 图3: 误差随时间变化
    ax3 = axes[1, 0]
    time_steps = np.arange(len(errors_knn))
    ax3.plot(time_steps, errors_knn * 100, 'b-', linewidth=1, alpha=0.7)
    ax3.axhline(np.mean(errors_knn) * 100, color='red', linestyle='--', linewidth=2, 
                label=f'平均误差: {np.mean(errors_knn)*100:.1f} cm')
    ax3.fill_between(time_steps, 0, errors_knn * 100, alpha=0.3)
    ax3.set_xlabel('时间步', fontsize=11)
    ax3.set_ylabel('定位误差 (cm)', fontsize=11)
    ax3.set_title('定位误差随时间变化', fontsize=12, fontweight='bold')
    ax3.legend()
    ax3.grid(True, alpha=0.3)
    
    # 图4: CDF误差分布
    ax4 = axes[1, 1]
    sorted_errors = np.sort(errors_knn * 100)
    cdf = np.arange(1, len(sorted_errors) + 1) / len(sorted_errors) * 100
    ax4.plot(sorted_errors, cdf, 'b-', linewidth=2)
    ax4.axvline(np.percentile(errors_knn * 100, 50), color='green', linestyle='--', 
                label=f'中位数: {np.percentile(errors_knn*100, 50):.1f} cm')
    ax4.axvline(np.percentile(errors_knn * 100, 90), color='orange', linestyle='--',
                label=f'90%分位: {np.percentile(errors_knn*100, 90):.1f} cm')
    ax4.axvline(np.percentile(errors_knn * 100, 95), color='red', linestyle='--',
                label=f'95%分位: {np.percentile(errors_knn*100, 95):.1f} cm')
    ax4.set_xlabel('定位误差 (cm)', fontsize=11)
    ax4.set_ylabel('累积概率 (%)', fontsize=11)
    ax4.set_title('定位误差累积分布 (CDF)', fontsize=12, fontweight='bold')
    ax4.legend()
    ax4.grid(True, alpha=0.3)
    
    plt.suptitle('磁导航与定位 - 定位算法性能分析', fontsize=14, fontweight='bold', y=0.98)
    plt.tight_layout()
    plt.savefig('fig2_localization_algorithm.png', dpi=150, bbox_inches='tight')
    plt.close()
    
    print(f"✓ 定位算法实现完成")
    print(f"  平均定位误差: {np.mean(errors_knn)*100:.1f} cm")
    print(f"  最大定位误差: {np.max(errors_knn)*100:.1f} cm")
    print(f"  95%置信区间: {np.percentile(errors_knn*100, 95):.1f} cm")
    
    return true_x, true_y, estimated_positions_knn, errors_knn


def multisensor_fusion():
    """
    案例3: 多传感器融合定位
    融合磁场、惯性传感器(IMU)和轮速计数据,提高定位精度
    """
    print("\n" + "=" * 60)
    print("案例3: 多传感器融合定位")
    print("=" * 60)
    
    # 仿真参数
    dt = 0.1  # 时间步长 (s)
    T = 30    # 总时间 (s)
    time = np.arange(0, T, dt)
    n_steps = len(time)
    
    # 真实轨迹 (直线运动 + 转弯)
    true_x = np.zeros(n_steps)
    true_y = np.zeros(n_steps)
    true_vx = np.zeros(n_steps)
    true_vy = np.zeros(n_steps)
    
    # 生成轨迹
    for i in range(1, n_steps):
        if i < n_steps // 3:
            true_vx[i] = 0.5  # m/s
            true_vy[i] = 0
        elif i < 2 * n_steps // 3:
            true_vx[i] = 0.5 * np.cos((i - n_steps//3) * 0.05)
            true_vy[i] = 0.5 * np.sin((i - n_steps//3) * 0.05)
        else:
            true_vx[i] = 0
            true_vy[i] = 0.5
        
        true_x[i] = true_x[i-1] + true_vx[i] * dt
        true_y[i] = true_y[i-1] + true_vy[i] * dt
    
    # 传感器噪声参数
    imu_noise = 0.05  # m/s²
    wheel_noise = 0.1  # m/s
    mag_noise = 0.5   # μT
    
    # 磁标位置
    beacons = [(2, 2), (8, 2), (2, 6), (8, 6)]
    
    # IMU测量 (加速度)
    ax_meas = np.gradient(true_vx, dt) + np.random.normal(0, imu_noise, n_steps)
    ay_meas = np.gradient(true_vy, dt) + np.random.normal(0, imu_noise, n_steps)
    
    # 轮速计测量
    vx_wheel = true_vx + np.random.normal(0, wheel_noise, n_steps)
    vy_wheel = true_vy + np.random.normal(0, wheel_noise, n_steps)
    
    # 磁场测量
    B_meas = []
    for i in range(n_steps):
        B_i = []
        for beacon in beacons:
            d = np.sqrt((true_x[i] - beacon[0])**2 + (true_y[i] - beacon[1])**2)
            B = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
            B_i.append(B + np.random.normal(0, mag_noise))
        B_meas.append(B_i)
    B_meas = np.array(B_meas)
    
    # 卡尔曼滤波器实现 (简化版)
    # 状态: [x, y, vx, vy]
    x_est = np.zeros((n_steps, 4))
    P_est = np.zeros((n_steps, 4, 4))
    
    # 初始化
    x_est[0] = [0, 0, 0, 0]
    P_est[0] = np.eye(4) * 10
    
    # 状态转移矩阵
    F = np.array([[1, 0, dt, 0],
                  [0, 1, 0, dt],
                  [0, 0, 1, 0],
                  [0, 0, 0, 1]])
    
    # 过程噪声
    Q = np.eye(4) * 0.01
    
    # 测量噪声
    R_mag = np.eye(4) * mag_noise**2
    R_wheel = np.eye(2) * wheel_noise**2
    
    # 卡尔曼滤波迭代
    for k in range(1, n_steps):
        # 预测
        x_pred = F @ x_est[k-1]
        P_pred = F @ P_est[k-1] @ F.T + Q
        
        # 更新 (轮速计)
        H_wheel = np.array([[0, 0, 1, 0],
                           [0, 0, 0, 1]])
        z_wheel = np.array([vx_wheel[k], vy_wheel[k]])
        y_wheel = z_wheel - H_wheel @ x_pred
        S_wheel = H_wheel @ P_pred @ H_wheel.T + R_wheel
        K_wheel = P_pred @ H_wheel.T @ np.linalg.inv(S_wheel)
        x_pred = x_pred + K_wheel @ y_wheel
        P_pred = (np.eye(4) - K_wheel @ H_wheel) @ P_pred
        
        # 更新 (磁场 - 简化为位置观测)
        # 使用磁场指纹进行位置修正
        if k % 5 == 0:  # 每5步进行一次磁场更新
            # 简化的磁场位置观测
            H_mag = np.array([[1, 0, 0, 0],
                             [0, 1, 0, 0]])
            # 从磁场反推位置 (简化处理)
            z_mag = np.array([true_x[k] + np.random.normal(0, 0.3),
                             true_y[k] + np.random.normal(0, 0.3)])
            y_mag = z_mag - H_mag @ x_pred
            S_mag = H_mag @ P_pred @ H_mag.T + np.eye(2) * 0.3**2
            K_mag = P_pred @ H_mag.T @ np.linalg.inv(S_mag)
            x_pred = x_pred + K_mag @ y_mag
            P_pred = (np.eye(4) - K_mag @ H_mag) @ P_pred
        
        x_est[k] = x_pred
        P_est[k] = P_pred
    
    # 仅使用轮速计的航位推算
    x_dead = np.zeros((n_steps, 2))
    x_dead[0] = [0, 0]
    for k in range(1, n_steps):
        x_dead[k, 0] = x_dead[k-1, 0] + vx_wheel[k] * dt
        x_dead[k, 1] = x_dead[k-1, 1] + vy_wheel[k] * dt
    
    # 计算误差
    error_fusion = np.sqrt((x_est[:, 0] - true_x)**2 + (x_est[:, 1] - true_y)**2)
    error_dead = np.sqrt((x_dead[:, 0] - true_x)**2 + (x_dead[:, 1] - true_y)**2)
    
    # 可视化
    fig, axes = plt.subplots(2, 2, figsize=(14, 11))
    
    # 图1: 轨迹对比
    ax1 = axes[0, 0]
    ax1.plot(true_x, true_y, 'g-', linewidth=2, label='真实轨迹', alpha=0.8)
    ax1.plot(x_dead[:, 0], x_dead[:, 1], 'r--', linewidth=1.5, label='航位推算', alpha=0.7)
    ax1.plot(x_est[:, 0], x_est[:, 1], 'b-.', linewidth=1.5, label='融合定位', alpha=0.7)
    
    # 标记磁标
    for beacon in beacons:
        ax1.plot(beacon[0], beacon[1], 'k^', markersize=10)
    
    ax1.set_xlabel('X 位置 (m)', fontsize=11)
    ax1.set_ylabel('Y 位置 (m)', fontsize=11)
    ax1.set_title('多传感器融合定位轨迹', fontsize=12, fontweight='bold')
    ax1.legend()
    ax1.grid(True, alpha=0.3)
    ax1.set_aspect('equal')
    
    # 图2: 误差对比
    ax2 = axes[0, 1]
    ax2.plot(time, error_dead * 100, 'r-', linewidth=1.5, label='航位推算误差', alpha=0.7)
    ax2.plot(time, error_fusion * 100, 'b-', linewidth=1.5, label='融合定位误差', alpha=0.7)
    ax2.fill_between(time, 0, error_dead * 100, alpha=0.2, color='red')
    ax2.fill_between(time, 0, error_fusion * 100, alpha=0.2, color='blue')
    ax2.set_xlabel('时间 (s)', fontsize=11)
    ax2.set_ylabel('定位误差 (cm)', fontsize=11)
    ax2.set_title('定位误差对比', fontsize=12, fontweight='bold')
    ax2.legend()
    ax2.grid(True, alpha=0.3)
    
    # 图3: 速度估计
    ax3 = axes[1, 0]
    ax3.plot(time, true_vx, 'g-', linewidth=2, label='真实Vx', alpha=0.8)
    ax3.plot(time, vx_wheel, 'r--', linewidth=1, label='轮速计Vx', alpha=0.6)
    ax3.plot(time, x_est[:, 2], 'b-.', linewidth=1.5, label='估计Vx', alpha=0.7)
    ax3.set_xlabel('时间 (s)', fontsize=11)
    ax3.set_ylabel('速度 (m/s)', fontsize=11)
    ax3.set_title('X方向速度估计', fontsize=12, fontweight='bold')
    ax3.legend()
    ax3.grid(True, alpha=0.3)
    
    # 图4: 误差统计
    ax4 = axes[1, 1]
    methods = ['航位推算', '融合定位']
    mean_errors = [np.mean(error_dead) * 100, np.mean(error_fusion) * 100]
    max_errors = [np.max(error_dead) * 100, np.max(error_fusion) * 100]
    
    x_pos = np.arange(len(methods))
    width = 0.35
    
    bars1 = ax4.bar(x_pos - width/2, mean_errors, width, label='平均误差', color='steelblue')
    bars2 = ax4.bar(x_pos + width/2, max_errors, width, label='最大误差', color='coral')
    
    ax4.set_ylabel('误差 (cm)', fontsize=11)
    ax4.set_title('定位误差统计对比', fontsize=12, fontweight='bold')
    ax4.set_xticks(x_pos)
    ax4.set_xticklabels(methods)
    ax4.legend()
    ax4.grid(True, alpha=0.3, axis='y')
    
    # 添加数值标签
    for bar in bars1:
        height = bar.get_height()
        ax4.annotate(f'{height:.1f}',
                    xy=(bar.get_x() + bar.get_width() / 2, height),
                    xytext=(0, 3),
                    textcoords="offset points",
                    ha='center', va='bottom', fontsize=9)
    for bar in bars2:
        height = bar.get_height()
        ax4.annotate(f'{height:.1f}',
                    xy=(bar.get_x() + bar.get_width() / 2, height),
                    xytext=(0, 3),
                    textcoords="offset points",
                    ha='center', va='bottom', fontsize=9)
    
    plt.suptitle('磁导航与定位 - 多传感器融合', fontsize=14, fontweight='bold', y=0.98)
    plt.tight_layout()
    plt.savefig('fig3_multisensor_fusion.png', dpi=150, bbox_inches='tight')
    plt.close()
    
    print(f"✓ 多传感器融合定位完成")
    print(f"  航位推算平均误差: {np.mean(error_dead)*100:.1f} cm")
    print(f"  融合定位平均误差: {np.mean(error_fusion)*100:.1f} cm")
    print(f"  精度提升: {(1 - np.mean(error_fusion)/np.mean(error_dead))*100:.1f}%")
    
    return time, true_x, true_y, x_est, error_fusion, error_dead


def localization_accuracy_analysis():
    """
    案例4: 定位精度分析
    分析不同因素对定位精度的影响
    """
    print("\n" + "=" * 60)
    print("案例4: 定位精度分析")
    print("=" * 60)
    
    # 测试不同磁标数量对精度的影响
    beacon_counts = [2, 3, 4, 5, 6]
    accuracy_vs_beacons = []
    
    room_width, room_height = 10, 8
    
    for n_beacons in beacon_counts:
        # 随机放置磁标
        np.random.seed(n_beacons)
        beacons = [(np.random.uniform(1, room_width-1), np.random.uniform(1, room_height-1)) 
                   for _ in range(n_beacons)]
        
        # 测试点
        test_points = [(5, 4), (3, 3), (7, 5), (2, 6), (8, 2)]
        errors = []
        
        for tp in test_points:
            # 真实磁场
            B_true = []
            for beacon in beacons:
                d = np.sqrt((tp[0] - beacon[0])**2 + (tp[1] - beacon[1])**2)
                B = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
                B_true.append(B)
            
            # 添加噪声的测量值
            B_meas = [b + np.random.normal(0, 0.5) for b in B_true]
            
            # 最近邻定位 (简化)
            min_error = float('inf')
            for x in np.linspace(0, room_width, 30):
                for y in np.linspace(0, room_height, 24):
                    B_candidate = []
                    for beacon in beacons:
                        d = np.sqrt((x - beacon[0])**2 + (y - beacon[1])**2)
                        B = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
                        B_candidate.append(B)
                    
                    error = np.linalg.norm(np.array(B_candidate) - np.array(B_meas))
                    if error < min_error:
                        min_error = error
                        best_pos = (x, y)
            
            pos_error = np.sqrt((best_pos[0] - tp[0])**2 + (best_pos[1] - tp[1])**2)
            errors.append(pos_error)
        
        accuracy_vs_beacons.append(np.mean(errors) * 100)  # cm
    
    # 测试不同噪声水平对精度的影响
    noise_levels = [0.1, 0.3, 0.5, 1.0, 2.0, 5.0]  # μT
    accuracy_vs_noise = []
    
    beacons_fixed = [(2, 2), (8, 2), (2, 6), (8, 6)]
    test_point = (5, 4)
    
    for noise in noise_levels:
        errors = []
        for _ in range(50):  # 多次测量取平均
            # 真实磁场
            B_true = []
            for beacon in beacons_fixed:
                d = np.sqrt((test_point[0] - beacon[0])**2 + (test_point[1] - beacon[1])**2)
                B = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
                B_true.append(B)
            
            # 添加噪声
            B_meas = [b + np.random.normal(0, noise) for b in B_true]
            
            # 最近邻定位
            min_error = float('inf')
            for x in np.linspace(3, 7, 20):
                for y in np.linspace(2, 6, 16):
                    B_candidate = []
                    for beacon in beacons_fixed:
                        d = np.sqrt((x - beacon[0])**2 + (y - beacon[1])**2)
                        B = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
                        B_candidate.append(B)
                    
                    error = np.linalg.norm(np.array(B_candidate) - np.array(B_meas))
                    if error < min_error:
                        min_error = error
                        best_pos = (x, y)
            
            pos_error = np.sqrt((best_pos[0] - test_point[0])**2 + (best_pos[1] - test_point[1])**2)
            errors.append(pos_error)
        
        accuracy_vs_noise.append(np.mean(errors) * 100)  # cm
    
    # 测试不同网格密度对精度的影响
    grid_densities = [10, 20, 30, 40, 50]  # 每维采样点数
    accuracy_vs_grid = []
    
    for density in grid_densities:
        # 构建指纹地图
        x_grid = np.linspace(0, room_width, density)
        y_grid = np.linspace(0, room_height, int(density * 0.8))
        
        fingerprint_db = []
        positions_db = []
        
        for x in x_grid:
            for y in y_grid:
                B_measured = []
                for beacon in beacons_fixed:
                    d = np.sqrt((x - beacon[0])**2 + (y - beacon[1])**2)
                    B = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
                    B_measured.append(B + np.random.normal(0, 0.5))
                fingerprint_db.append(B_measured)
                positions_db.append((x, y))
        
        fingerprint_db = np.array(fingerprint_db)
        positions_db = np.array(positions_db)
        
        # 测试定位
        B_test = []
        for beacon in beacons_fixed:
            d = np.sqrt((test_point[0] - beacon[0])**2 + (test_point[1] - beacon[1])**2)
            B = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
            B_test.append(B + np.random.normal(0, 0.5))
        
        distances = np.linalg.norm(fingerprint_db - B_test, axis=1)
        nearest_idx = np.argmin(distances)
        estimated_pos = positions_db[nearest_idx]
        
        error = np.sqrt((estimated_pos[0] - test_point[0])**2 + 
                       (estimated_pos[1] - test_point[1])**2)
        accuracy_vs_grid.append(error * 100)
    
    # 可视化
    fig, axes = plt.subplots(2, 2, figsize=(14, 11))
    
    # 图1: 磁标数量 vs 精度
    ax1 = axes[0, 0]
    ax1.plot(beacon_counts, accuracy_vs_beacons, 'bo-', linewidth=2, markersize=8)
    ax1.set_xlabel('磁标数量', fontsize=11)
    ax1.set_ylabel('平均定位误差 (cm)', fontsize=11)
    ax1.set_title('定位精度 vs 磁标数量', fontsize=12, fontweight='bold')
    ax1.grid(True, alpha=0.3)
    
    # 图2: 噪声水平 vs 精度
    ax2 = axes[0, 1]
    ax2.semilogy(noise_levels, accuracy_vs_noise, 'ro-', linewidth=2, markersize=8)
    ax2.set_xlabel('磁场测量噪声 (μT)', fontsize=11)
    ax2.set_ylabel('平均定位误差 (cm)', fontsize=11)
    ax2.set_title('定位精度 vs 测量噪声', fontsize=12, fontweight='bold')
    ax2.grid(True, alpha=0.3)
    
    # 图3: 网格密度 vs 精度
    ax3 = axes[1, 0]
    ax3.plot(grid_densities, accuracy_vs_grid, 'go-', linewidth=2, markersize=8)
    ax3.set_xlabel('网格密度 (每维点数)', fontsize=11)
    ax3.set_ylabel('定位误差 (cm)', fontsize=11)
    ax3.set_title('定位精度 vs 指纹地图密度', fontsize=12, fontweight='bold')
    ax3.grid(True, alpha=0.3)
    
    # 图4: 综合精度热力图
    ax4 = axes[1, 1]
    
    # 生成不同位置的精度分布
    x_test = np.linspace(1, 9, 40)
    y_test = np.linspace(1, 7, 32)
    X_test, Y_test = np.meshgrid(x_test, y_test)
    
    accuracy_map = np.zeros_like(X_test)
    
    for i in range(X_test.shape[0]):
        for j in range(X_test.shape[1]):
            pos = (X_test[i, j], Y_test[i, j])
            
            # 计算到最近磁标的距离
            min_dist = min([np.sqrt((pos[0] - b[0])**2 + (pos[1] - b[1])**2) 
                           for b in beacons_fixed])
            
            # 精度与距离相关 (简化模型)
            accuracy_map[i, j] = 5 + 20 * min_dist + np.random.normal(0, 2)
    
    im = ax4.contourf(X_test, Y_test, accuracy_map, levels=30, cmap='RdYlGn_r')
    
    # 标记磁标
    for beacon in beacons_fixed:
        ax4.plot(beacon[0], beacon[1], 'w*', markersize=15, markeredgecolor='black')
    
    ax4.set_xlabel('X 位置 (m)', fontsize=11)
    ax4.set_ylabel('Y 位置 (m)', fontsize=11)
    ax4.set_title('定位精度空间分布', fontsize=12, fontweight='bold')
    ax4.set_aspect('equal')
    
    cbar = plt.colorbar(im, ax=ax4)
    cbar.set_label('定位误差 (cm)', fontsize=10)
    
    plt.suptitle('磁导航与定位 - 定位精度分析', fontsize=14, fontweight='bold', y=0.98)
    plt.tight_layout()
    plt.savefig('fig4_accuracy_analysis.png', dpi=150, bbox_inches='tight')
    plt.close()
    
    print(f"✓ 定位精度分析完成")
    print(f"  最优磁标数量: {beacon_counts[np.argmin(accuracy_vs_beacons)]} (误差: {min(accuracy_vs_beacons):.1f} cm)")
    print(f"  噪声敏感度: 噪声每增加1μT, 误差增加约{(accuracy_vs_noise[-1]-accuracy_vs_noise[0])/(noise_levels[-1]-noise_levels[0]):.1f} cm")
    
    return beacon_counts, accuracy_vs_beacons, noise_levels, accuracy_vs_noise


def interference_robustness():
    """
    案例5: 干扰抑制与鲁棒性
    分析铁磁性干扰源对定位的影响及抑制方法
    """
    print("\n" + "=" * 60)
    print("案例5: 干扰抑制与鲁棒性")
    print("=" * 60)
    
    # 场景设置
    room_width, room_height = 10, 8
    
    # 磁标位置
    beacons = [(2, 2), (8, 2), (2, 6), (8, 6)]
    
    # 干扰源位置 (铁磁性物体)
    interferences = [
        {'pos': (5, 4), 'strength': 5.0},  # 强干扰
        {'pos': (3, 5), 'strength': 2.0},  # 中等干扰
    ]
    
    # 测试轨迹
    t = np.linspace(0, 2*np.pi, 80)
    true_x = 5 + 2.5 * np.cos(t)
    true_y = 4 + 2 * np.sin(t)
    
    # 模拟测量 (含干扰)
    def measure_field(x, y, beacons, interferences, noise=0.5):
        """模拟磁场测量"""
        B_meas = []
        
        for beacon in beacons:
            # 磁标贡献
            d = np.sqrt((x - beacon[0])**2 + (y - beacon[1])**2)
            B_beacon = MU_0 * 1.0 / (4 * np.pi * (d**3 + 0.01)) * 1e6
            
            # 干扰源贡献 (简化模型)
            B_interf = 0
            for interf in interferences:
                d_i = np.sqrt((x - interf['pos'][0])**2 + (y - interf['pos'][1])**2)
                B_interf += MU_0 * interf['strength'] / (4 * np.pi * (d_i**3 + 0.01)) * 1e6
            
            B_total = B_beacon + B_interf + np.random.normal(0, noise)
            B_meas.append(B_total)
        
        return B_meas
    
    # 无干扰情况下的定位
    estimated_clean = []
    for x, y in zip(true_x, true_y):
        B_meas = measure_field(x, y, beacons, [], noise=0.5)
        
        # 最近邻定位
        min_error = float('inf')
        best_pos = (x, y)
        
        for xi in np.linspace(0, room_width, 40):
            for yi in np.linspace(0, room_height, 32):
                B_candidate = measure_field(xi, yi, beacons, [], noise=0)
                error = np.linalg.norm(np.array(B_candidate) - np.array(B_meas))
                if error < min_error:
                    min_error = error
                    best_pos = (xi, yi)
        
        estimated_clean.append(best_pos)
    
    estimated_clean = np.array(estimated_clean)
    
    # 有干扰但无抑制
    estimated_noisy = []
    for x, y in zip(true_x, true_y):
        B_meas = measure_field(x, y, beacons, interferences, noise=0.5)
        
        min_error = float('inf')
        best_pos = (x, y)
        
        for xi in np.linspace(0, room_width, 40):
            for yi in np.linspace(0, room_height, 32):
                B_candidate = measure_field(xi, yi, beacons, [], noise=0)
                error = np.linalg.norm(np.array(B_candidate) - np.array(B_meas))
                if error < min_error:
                    min_error = error
                    best_pos = (xi, yi)
        
        estimated_noisy.append(best_pos)
    
    estimated_noisy = np.array(estimated_noisy)
    
    # 有干扰 + RANSAC鲁棒定位
    estimated_robust = []
    for x, y in zip(true_x, true_y):
        B_meas = measure_field(x, y, beacons, interferences, noise=0.5)
        
        # RANSAC: 随机采样一致性
        best_pos = (x, y)
        min_median_error = float('inf')
        
        for _ in range(20):  # RANSAC迭代
            # 随机选择3个磁标
            selected = np.random.choice(len(beacons), 3, replace=False)
            
            # 使用选中的磁标定位
            min_error = float('inf')
            candidate_pos = (x, y)
            
            for xi in np.linspace(0, room_width, 30):
                for yi in np.linspace(0, room_height, 24):
                    B_candidate = [measure_field(xi, yi, [beacons[i]], [], noise=0)[0] 
                                  for i in selected]
                    B_meas_selected = [B_meas[i] for i in selected]
                    error = np.linalg.norm(np.array(B_candidate) - np.array(B_meas_selected))
                    if error < min_error:
                        min_error = error
                        candidate_pos = (xi, yi)
            
            # 计算所有磁标的残差
            B_pred = measure_field(candidate_pos[0], candidate_pos[1], beacons, [], noise=0)
            residuals = np.abs(np.array(B_pred) - np.array(B_meas))
            median_residual = np.median(residuals)
            
            if median_residual < min_median_error:
                min_median_error = median_residual
                best_pos = candidate_pos
        
        estimated_robust.append(best_pos)
    
    estimated_robust = np.array(estimated_robust)
    
    # 计算误差
    error_clean = np.sqrt((estimated_clean[:, 0] - true_x)**2 + (estimated_clean[:, 1] - true_y)**2)
    error_noisy = np.sqrt((estimated_noisy[:, 0] - true_x)**2 + (estimated_noisy[:, 1] - true_y)**2)
    error_robust = np.sqrt((estimated_robust[:, 0] - true_x)**2 + (estimated_robust[:, 1] - true_y)**2)
    
    # 可视化
    fig, axes = plt.subplots(2, 2, figsize=(14, 11))
    
    # 图1: 干扰环境示意
    ax1 = axes[0, 0]
    
    # 绘制磁场分布 (含干扰)
    x_grid = np.linspace(0, room_width, 80)
    y_grid = np.linspace(0, room_height, 64)
    X_grid, Y_grid = np.meshgrid(x_grid, y_grid)
    
    B_field = np.zeros_like(X_grid)
    for i in range(X_grid.shape[0]):
        for j in range(X_grid.shape[1]):
            B_field[i, j] = sum(measure_field(X_grid[i,j], Y_grid[i,j], beacons, interferences, noise=0))
    
    im1 = ax1.contourf(X_grid, Y_grid, B_field, levels=40, cmap='viridis')
    
    # 标记磁标
    for beacon in beacons:
        ax1.plot(beacon[0], beacon[1], 'g^', markersize=12, label='磁标' if beacon == beacons[0] else '')
    
    # 标记干扰源
    for i, interf in enumerate(interferences):
        circle = Circle(interf['pos'], 0.3, color='red', alpha=0.5)
        ax1.add_patch(circle)
        ax1.annotate(f'干扰{i+1}', xy=interf['pos'], xytext=(5, 5),
                    textcoords='offset points', fontsize=9, color='white')
    
    ax1.plot(true_x, true_y, 'w--', linewidth=2, label='真实轨迹')
    ax1.set_xlabel('X 位置 (m)', fontsize=11)
    ax1.set_ylabel('Y 位置 (m)', fontsize=11)
    ax1.set_title('干扰环境下的磁场分布', fontsize=12, fontweight='bold')
    ax1.legend(loc='upper right')
    ax1.set_aspect('equal')
    
    cbar1 = plt.colorbar(im1, ax=ax1)
    cbar1.set_label('总磁场 (μT)', fontsize=10)
    
    # 图2: 轨迹对比
    ax2 = axes[0, 1]
    ax2.plot(true_x, true_y, 'g-', linewidth=2, label='真实轨迹', alpha=0.8)
    ax2.plot(estimated_clean[:, 0], estimated_clean[:, 1], 'b--', 
             linewidth=1.5, label='无干扰', alpha=0.7)
    ax2.plot(estimated_noisy[:, 0], estimated_noisy[:, 1], 'r--', 
             linewidth=1.5, label='有干扰(无抑制)', alpha=0.7)
    ax2.plot(estimated_robust[:, 0], estimated_robust[:, 1], 'm-.', 
             linewidth=1.5, label='有干扰(RANSAC)', alpha=0.7)
    
    # 标记干扰源
    for interf in interferences:
        circle = Circle(interf['pos'], 0.3, color='red', alpha=0.3)
        ax2.add_patch(circle)
    
    ax2.set_xlabel('X 位置 (m)', fontsize=11)
    ax2.set_ylabel('Y 位置 (m)', fontsize=11)
    ax2.set_title('干扰抑制效果对比', fontsize=12, fontweight='bold')
    ax2.legend()
    ax2.grid(True, alpha=0.3)
    ax2.set_aspect('equal')
    
    # 图3: 误差对比
    ax3 = axes[1, 0]
    steps = np.arange(len(error_clean))
    ax3.plot(steps, error_clean * 100, 'b-', linewidth=1.5, label='无干扰', alpha=0.7)
    ax3.plot(steps, error_noisy * 100, 'r-', linewidth=1.5, label='有干扰(无抑制)', alpha=0.7)
    ax3.plot(steps, error_robust * 100, 'm-', linewidth=1.5, label='有干扰(RANSAC)', alpha=0.7)
    ax3.fill_between(steps, 0, error_noisy * 100, alpha=0.2, color='red')
    ax3.fill_between(steps, 0, error_robust * 100, alpha=0.2, color='magenta')
    ax3.set_xlabel('轨迹点', fontsize=11)
    ax3.set_ylabel('定位误差 (cm)', fontsize=11)
    ax3.set_title('定位误差对比', fontsize=12, fontweight='bold')
    ax3.legend()
    ax3.grid(True, alpha=0.3)
    
    # 图4: 误差统计
    ax4 = axes[1, 1]
    methods = ['无干扰', '有干扰\n(无抑制)', '有干扰\n(RANSAC)']
    mean_errors = [np.mean(error_clean) * 100, np.mean(error_noisy) * 100, np.mean(error_robust) * 100]
    max_errors = [np.max(error_clean) * 100, np.max(error_noisy) * 100, np.max(error_robust) * 100]
    
    x_pos = np.arange(len(methods))
    width = 0.35
    
    bars1 = ax4.bar(x_pos - width/2, mean_errors, width, label='平均误差', color='steelblue')
    bars2 = ax4.bar(x_pos + width/2, max_errors, width, label='最大误差', color='coral')
    
    ax4.set_ylabel('误差 (cm)', fontsize=11)
    ax4.set_title('干扰抑制效果统计', fontsize=12, fontweight='bold')
    ax4.set_xticks(x_pos)
    ax4.set_xticklabels(methods)
    ax4.legend()
    ax4.grid(True, alpha=0.3, axis='y')
    
    # 添加数值标签
    for bar in bars1:
        height = bar.get_height()
        ax4.annotate(f'{height:.1f}',
                    xy=(bar.get_x() + bar.get_width() / 2, height),
                    xytext=(0, 3),
                    textcoords="offset points",
                    ha='center', va='bottom', fontsize=9)
    for bar in bars2:
        height = bar.get_height()
        ax4.annotate(f'{height:.1f}',
                    xy=(bar.get_x() + bar.get_width() / 2, height),
                    xytext=(0, 3),
                    textcoords="offset points",
                    ha='center', va='bottom', fontsize=9)
    
    plt.suptitle('磁导航与定位 - 干扰抑制与鲁棒性', fontsize=14, fontweight='bold', y=0.98)
    plt.tight_layout()
    plt.savefig('fig5_interference_robustness.png', dpi=150, bbox_inches='tight')
    plt.close()
    
    print(f"✓ 干扰抑制分析完成")
    print(f"  无干扰平均误差: {np.mean(error_clean)*100:.1f} cm")
    print(f"  有干扰(无抑制)平均误差: {np.mean(error_noisy)*100:.1f} cm")
    print(f"  有干扰(RANSAC)平均误差: {np.mean(error_robust)*100:.1f} cm")
    print(f"  RANSAC改善效果: {(1 - np.mean(error_robust)/np.mean(error_noisy))*100:.1f}%")
    
    return true_x, true_y, error_clean, error_noisy, error_robust


def main():
    """主函数:运行所有仿真案例"""
    print("=" * 70)
    print("磁导航与定位仿真")
    print("=" * 70)
    
    # 案例1: 磁场指纹地图构建
    X, Y, B_total, magnets, sample_points, B_samples = magnetic_fingerprint_map()
    
    # 案例2: 磁定位算法实现
    true_x, true_y, estimated_positions_knn, errors_knn = magnetic_localization_algorithm()
    
    # 案例3: 多传感器融合定位
    time, true_x_ms, true_y_ms, x_est, error_fusion, error_dead = multisensor_fusion()
    
    # 案例4: 定位精度分析
    beacon_counts, accuracy_vs_beacons, noise_levels, accuracy_vs_noise = localization_accuracy_analysis()
    
    # 案例5: 干扰抑制与鲁棒性
    true_x_int, true_y_int, error_clean, error_noisy, error_robust = interference_robustness()
    
    print("\n" + "=" * 70)
    print("所有仿真案例已完成!")
    print("=" * 70)
    print("\n生成的文件:")
    print("  - fig1_magnetic_fingerprint.png: 磁场指纹地图构建")
    print("  - fig2_localization_algorithm.png: 磁定位算法实现")
    print("  - fig3_multisensor_fusion.png: 多传感器融合定位")
    print("  - fig4_accuracy_analysis.png: 定位精度分析")
    print("  - fig5_interference_robustness.png: 干扰抑制与鲁棒性")


if __name__ == "__main__":
    main()

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐