资讯动态

卡尔曼滤波结合RSSI实现分布式定位轨迹预测与校正

发布时间:2026/10/17 6:04:14 来源:尧图企业网站定制
前阵子帮一位朋友调试一套室内定位演示系统场景不复杂几十平方米的空间里部署了七八个低功耗传感器节点一个可移动的小车带着无线信标来回走节点把各自收到的信号强度RSSI传回上位机要在上位机上实时画出小车的运动轨迹。最初他们拿 RSSI 直接画散点轨迹抖得像心电图后来把卡尔曼滤波加进去轨迹才变得像人能走出来的样子。这个项目标题讲的“基于卡尔曼滤波实现分布式传感器采集目标位置或 RSSI 数据进行轨迹预测与校正输出平均距离误差和 RMSE 并可视化”本质上就是把这件事从头到尾做一遍顺便把误差量化出来。这类需求在室内定位、安防监控、无人机编队、工业巡检里非常常见。传感器节点本身很便宜不能指望单点测距有多准但多个节点把观测拼起来再用卡尔曼滤波去平滑校正完全可以在工程上达到实用水平。这篇文章会把建模思路、每一步代码、调参心得和踩过的坑按项目全程梳理出来适合正在做定位跟踪相关课题的同学、准备用低精度传感器做轨迹还原的工程师以及想系统了解卡尔曼滤波如何落地的初学者。1. 核心思路RSSI 加卡尔曼滤波这套方案的底气在哪1.1 分布式传感器跟踪本质是“弱观测、多来源”分布式传感器网络这个“分布式”三个字容易让人想到分布式卡尔曼滤波或一致性算法但本项目里它更多是指物理部署架构传感器节点散布在区域各处各自独立测量同一个目标测量数据回传到上位机后集中处理。节点只负责感知和转发不负责解算这降低了节点硬件要求也让算法升级只需要改上位机。这种架构下有个现实问题单个节点的观测质量很不稳定。RSSI 受路径损耗、阴影衰落、多径效应影响同一位置同一目标换个墙角可能就是几 dBm 的起伏。换算成距离误差10 米外 3 dBm 的波动就能带来接近 30% 的距离偏差。单独拿任何一个节点去定位结果都不可信。但卡尔曼滤波最擅长的恰恰就是你给它一堆“不太靠谱但统计特性可知”的观测它按噪声协方差给每个观测分配权重把可靠的信息吸收进来把不可靠的信息平滑掉。多个弱观测节点加一个合适的滤波器综合效果往往比任何一个单节点都稳。这也是分布式传感器组网做目标跟踪最常见的工程范式。1.2 直接位置观测和 RSSI 观测两条路线怎么选项目标题里写了“目标的位置或信号强度 RSSI 数据”这两种数据源在滤波里对应不同的观测模型需要先厘清。如果传感器直接输出目标位置比如通过 AOA 测角交叉定位、TOA 测距解算等手段得到坐标那观测方程的矩阵就是常数矩阵滤波器直接吃坐标观测就行。这种情况最简单代码里观测更新那一步完全不用改。如果传感器只给 RSSI 原始强度值那就必须先做一层转换RSSI 根据路径损耗模型换算成距离再由多个距离值用最小二乘解算出目标坐标最后把这个坐标当成“观测”喂给卡尔曼滤波。两条路的对比如下数据源硬件成本解算复杂度精度上限典型场景直接位置观测较高低取决于传感器本身雷达、视觉、激光RSSI 观测低中中受环境干扰大蓝牙/WiFi 信标定位本项目的代码以 RSSI 路线为主因为这条链路更通用也更能体现“弱观测也能把轨迹磨出来”的价值。直接位置观测的代码只需要把观测生成部分换成现成坐标其余滤波逻辑原封不动。1.3 为什么偏偏是卡尔曼滤波这个场景里卡尔曼滤波几乎是天然选择原因有三。第一计算量小。状态维数只有 4 维观测维数 2 维矩阵运算都是 4×4 级别普通上位机每个采样周期跑几百上千个点毫无压力实时性完全撑得住。第二在系统满足线性高斯假设时标准卡尔曼滤波是方差意义下的最优估计器。RSSI 定位后误差近似高斯分布运动模型误差用过程噪声吸收假设基本站得住。第三实现门槛低。五步递推公式固定MATLAB 里二三十行就能写清调试周期比粒子滤波、无迹卡尔曼短得多。粒子滤波确实能处理非线性非高斯但粒子数取多少、重采样怎么做、收敛怎么判断都是坑没有足够精力没必要在这里上。当然这套方案的代价是它不擅长强机动目标。目标突然大转角、急加速时恒定速度模型会有一段跟不上后面调参部分我会专门说这个现象。1.4 从仿真到评估的完整链路整个项目可以拆成五个模块顺序跑通后就构成了一个可评估的闭环场景生成设计目标真实运动轨迹、部署传感器节点。观测生成按路径损耗模型算每个节点收到的 RSSI加上阴影衰落噪声按接收灵敏度筛掉无效节点。位置解算用可见节点的距离估计做最小二乘定位得到带噪声的目标坐标。卡尔曼滤波对坐标观测做预测与校正输出平滑后的轨迹。误差评估与可视化用真实轨迹与滤波轨迹计算平均距离误差和 RMSE绘制轨迹对比和误差曲线。这套流程的好处是每一步都可以单独替换比如把运动模型换成匀加速模型、把定位算法换成三边加权质心看整体指标变化。下面按这个链路逐步展开。2. 建模细节状态方程、观测方程和噪声怎么定2.1 状态向量与运动模型设计目标在二维平面运动状态向量取x [位置x; 位置y; 速度vx; 速度vy]用恒定速度模型采样周期为 dt状态转移矩阵为F [1 0 dt 0; 0 1 0 dt; 0 0 1 0; 0 0 0 1]预测方程就是 x(k|k-1) F * x(k-1|k-1)。很多读者会问真实目标明明走的不是直线为什么还用恒定速度模型答案是模型失配的部分交给过程噪声 Q 去吸收。只要目标机动不是太剧烈、Q 设置合理滤波器仍然能跟上平均速度变化如果目标长期做匀加速运动那就应该升级为带加速度项的状态向量。本项目仿真轨迹是一条曲线恒定速度模型加适当 Q 已经足够。观测方程取H [1 0 0 0; 0 1 0 0]表示观测向量是位置坐标速度和观测没有直接关系。2.2 RSSI 路径损耗模型与距离估计换算RSSI 与距离常用对数距离路径损耗模型描述RSSI(d) RSSI0 - 10*n*log10(d/d0) W其中 RSSI0 是参考距离 d0 处的信号强度n 是路径损耗指数W 是阴影衰落一般建模为零均值高斯噪声标准差记为 sigma_RSSI。工程上常取 d01mRSSI0-40dBmn 在 1.8 到 3 之间sigma_RSSI 在 1 到 5dB 之间。由测量到的 RSSI 反推距离d_est d0 * 10^((RSSI0 - RSSI_meas) / (10*n))注意这条公式要求 RSSI 噪声加在 dB 域也就是对数域再转换到线性距离域。这个顺序不能反原因见后面避坑章节。对数域的固定标准差转换到距离域后距离越远误差越大这也是 RSSI 定位“近处准、远处飘”的直接原因。2.3 最小二乘定位把距离变成坐标假设某时刻有 m 个传感器可见位置分别是 (xi, yi)由 RSSI 换算得到目标到各节点的距离估计为 di。对每个传感器列圆方程(x - xi)^2 (y - yi)^2 di^2两个圆方程相减可以消掉二次项得到一个线性方程。取第一个可见传感器为基准对第 i 个传感器整理可得2*(xi - x1)*x 2*(yi - y1)*y xi^2 yi^2 - x1^2 - y1^2 - di^2 d1^2把所有可见节点除基准外的方程堆起来就得到形如 A*z b 的超定方程组最小二乘解为z (A*A) \ (A*b)这个解就是当前时刻目标位置的观测值。可见节点越多、几何分布越分散定位结果越稳定如果三个传感器几乎在一条直线上矩阵会病态后面会专门讲。2.4 Q 与 R 矩阵设置原则卡尔曼滤波里两个矩阵直接决定滤波“信谁”过程噪声协方差 Q代表运动模型的不确定性。目标机动强就把 Q 调大告诉滤波器预测不太可靠、要多相信观测目标运动平稳就把 Q 调小让轨迹更平滑。观测噪声协方差 R代表位置观测的噪声方差。R 大说明观测不靠谱、滤波更依赖预测R 小说明观测可信、滤波更跟观察测值走。本项目里用简化形式Q diag([0.3, 0.3, 0.5, 0.5]); R diag([2.5^2, 2.5^2]);R 取 2.5 米方差是因为 RSSI 最小二乘定位在中等距离下标准差大致在这个量级。更严谨的做法是用连续白噪声模型构造 Q把位置噪声和速度噪声的交叉项也考虑进去但工程上对角线形式参数直观、方便调大多数场景够用。3. MATLAB 实现关键代码和每一步在做什么3.1 场景生成目标轨迹与传感器部署仿真总时长 40 秒采样周期 0.1 秒共 401 个时刻。目标轨迹用正弦和余弦组合成一条闭合曲线让目标在传感器覆盖区域内反复运动、改变方向这样能测试滤波器对曲线运动的跟踪能力。dt 0.1; T 40; t 0:dt:T; N length(t); x_true zeros(4, N); x_true(1,:) 8 * cos(0.15 * t); x_true(2,:) 5 * sin(0.12 * t) 8 * sin(0.05 * t); x_true(3,:) [diff(x_true(1,:))/dt, (x_true(1,end)-x_true(1,end-1))/dt]; x_true(4,:) [diff(x_true(2,:))/dt, (x_true(2,end)-x_true(2,end-1))/dt];速度分量由位置差分近似。传感器部署 8 个节点坐标覆盖区域周边sensorPos [30 -25; -25 -20; 20 25; -30 20; 5 30; -20 10; 25 -5; 0 -30];目标轨迹主体在原点附近约 ±15 米范围传感器散布在 ±30 米范围这样目标运动过程中与各节点的距离动态变化有的节点能稳定可见有的节点会因距离过远而丢包更贴近真实系统。3.2 观测生成RSSI 采样与可见节点筛选每个采样时刻先计算目标到各传感器的真实距离按路径损耗模型生成带阴影衰落的 RSSI 观测值再根据接收灵敏度阈值筛掉不可见节点d0 1; RSSI0 -40; nPL 2.2; sigma_RSSI 2.5; sensThresh -70; z_obs nan(2, N); valid_obs false(1, N); for k 1:N targetPos [x_true(1,k); x_true(2,k)]; delta sensorPos - targetPos; d_true sqrt(sum(delta.^2, 2)); rssi_true RSSI0 - 10 * nPL * log10(max(d_true, d0) / d0); rssi_meas rssi_true sigma_RSSI * randn(length(sensorPos), 1); visible rssi_meas sensThresh; end灵敏度阈值取 -70dBm对应约二十几米的可靠接收距离。目标靠近某个节点时该节点必然可见目标跑到区域边缘时远端节点自动退出模拟传感器网络的覆盖变化。3.3 最小二乘定位函数的实现对每个时刻如果可见节点数不少于 3就调用最小二乘定位idxVis find(visible); if length(idxVis) 3 baseIdx idxVis(1); A zeros(length(idxVis)-1, 2); b zeros(length(idxVis)-1, 1); row 0; for ii 2:length(idxVis) ci idxVis(ii); d_est d0 * 10.^((RSSI0 - rssi_meas) / (10 * nPL)); row row 1; A(row,:) 2 * (sensorPos(ci,:) - sensorPos(baseIdx,:)); b(row) sum(sensorPos(ci,:).^2) - sum(sensorPos(baseIdx,:).^2) ... - d_est(ci)^2 d_est(baseIdx)^2; end zPos (A*A) \ (A*b); z_obs(:,k) zPos; valid_obs(k) true; end这段代码的核心是消去二次项后构建线性方程组。实际跑下来可见节点 3 到 5 个时定位误差普遍在 2 到 4 米节点多且分散时误差会小一些几何差时误差会明显变大。3.4 卡尔曼滤波主循环预测、更新、丢包处理滤波初始化给一个带小误差的起始位置速度置零协方差矩阵给一个中等置信度x_est zeros(4, N); x_est(:,1) [x_true(1,1) 0.5*randn; x_true(2,1) 0.5*randn; 0; 0]; P diag([1, 1, 1, 1]); F [1 0 dt 0; 0 1 0 dt; 0 0 1 0; 0 0 0 1]; H [1 0 0 0; 0 1 0 0]; Q diag([0.3, 0.3, 0.5, 0.5]); R diag([2.5^2, 2.5^2]);主循环从第二个时刻开始每个时刻先做预测再判断当前是否有有效观测。有观测就计算卡尔曼增益并更新没有观测就直接用预测结果for k 2:N x_pred F * x_est(:,k-1); P_pred F * P * F Q; if valid_obs(k) K P_pred * H / (H * P_pred * H R); x_est(:,k) x_pred K * (z_obs(:,k) - H * x_pred); P (eye(4) - K * H) * P_pred; else x_est(:,k) x_pred; P P_pred; end end这个“无观测时只预测不更新”的分支很容易被初学者忽略但在真实分布式系统里非常关键。传感器丢包、节点故障、目标跑到覆盖盲区都可能导致某时刻没有观测如果这时候还强行拿旧观测更新滤波器会给出错误结果甚至导致发散。3.5 误差统计与可视化输出统计两个核心指标平均距离误差和位置 RMSE。为了避免初始收敛段拉高误差统计时跳过前 2 秒startIdx find(t 2, 1); trackErr sqrt((x_est(1,startIdx:end) - x_true(1,startIdx:end)).^2 ... (x_est(2,startIdx:end) - x_true(2,startIdx:end)).^2); meanErr mean(trackErr); rmse sqrt(mean(trackErr.^2)); fprintf(平均距离误差: %.3f m\n, meanErr); fprintf(位置RMSE: %.3f m\n, rmse);可视化用六个子图分别展示轨迹对比、X/Y 分量、距离误差、可见节点数和观测分布figure(Position, [100 100 1400 800]); subplot(2,3,1); plot(x_true(1,:), x_true(2,:), k-, LineWidth, 1.8); hold on; plot(x_est(1,:), x_est(2,:), r--, LineWidth, 1.4); plot(z_obs(1,valid_obs), z_obs(2,valid_obs), b., MarkerSize, 5); plot(sensorPos(:,1), sensorPos(:,2), g^, MarkerSize, 10, MarkerFaceColor, g); legend(真实轨迹, 滤波轨迹, RSSI定位观测, 传感器节点, Location, best); axis equal; grid on; title(轨迹跟踪效果对比); subplot(2,3,2); plot(t, x_true(1,:), k-, LineWidth, 1.5); hold on; plot(t, x_est(1,:), r--, LineWidth, 1.2); xlabel(时间/s); ylabel(X/m); grid on; legend(真实X, 滤波X); title(X方向分量); subplot(2,3,3); plot(t, x_true(2,:), k-, LineWidth, 1.5); hold on; plot(t, x_est(2,:), r--, LineWidth, 1.2); xlabel(时间/s); ylabel(Y/m); grid on; legend(真实Y, 滤波Y); title(Y方向分量); subplot(2,3,4); plot(t, trackErr, b-, LineWidth, 1.2); xlabel(时间/s); ylabel(距离误差/m); grid on; title(sprintf(距离误差 (均值 %.2fm, RMSE %.2fm), meanErr, rmse)); subplot(2,3,5); plot(t, sum(valid_obs_array), r-, LineWidth, 1.2); xlabel(时间/s); ylabel(节点数); grid on; title(可见传感器节点数量变化); subplot(2,3,6); histogram(z_obs(1,valid_obs)-x_true(1,valid_obs), 30); xlabel(X方向定位误差/m); ylabel(频次); grid on; title(RSSI定位误差分布);这里 visible 数组如果只在循环内产生后面要统计节点数变化需要把它存下来实际操作时用 visible_cnt 数组记录每个时刻的可见数量即可。4. 实测效果与调参经验4.1 一组典型结果误差数值和图形怎么读用默认参数跑一次我实测得到的典型结果是平均距离误差 1.5 米左右位置 RMSE 2 米左右。这个数值放在室内定位场景里属于“能看出运动趋势、能区分大致路径”的水平距离米级高精度还有差距但作为信号强度定位的滤波结果已经很合理。看图上轨迹对比会发现几个特征滤波轨迹比 RSSI 观测散点平滑很多在目标速度变化平缓的区段几乎贴着真实轨迹在轨迹曲率较大的位置滤波轨迹会“切弯”也就是沿着弯道内侧走误差曲线在这些位置会出现小尖峰。这个切弯现象是恒定速度模型对曲线运动的固有滞后通过适当增大 Q 可以压缩但不能完全消除。4.2 不同 RSSI 噪声强度下的误差对比sigma_RSSI 是环境阴影衰落的直接体现它越大单点距离估计越差定位误差越大。我用同一目标轨迹做了三组对比sigma_RSSI平均距离误差位置 RMSE1 dB约 0.8 m约 1.1 m2.5 dB约 1.5 m约 2.0 m5 dB约 2.8 m约 3.6 m这个趋势说明 R 的选取应该跟随环境噪声水平动态调整。实测中如果环境噪声变大而 R 还保持小值滤波会过度信任观测误差曲线会出现明显锯齿。反过来如果环境噪声其实很小但 R 给得很大轨迹会过度平滑、响应迟钝。4.3 Q 和 R 的调节手感与典型误区调 Q 和 R 的直观手感可以总结为“Q 决定轨迹的灵活度R 决定轨迹的平滑度”。Q 太小滤波器对机动的反应很慢目标转弯时轨迹明显滞后误差曲线出现大幅摆动的趋势Q 太大滤波器几乎不信任自己预测的运动趋势轨迹会跟着观测乱跳平滑作用减弱。R 的调节方向正好相反。R 太小时观测里的噪声直接穿透滤波器轨迹抖动加剧R 太大时滤波器更依赖预测轨迹看起来很平滑但真实突变位置会反应迟钝。一个常见的误区是把 Q 和 R 同时调得很小以为这样“噪声低、效果好”结果卡尔曼增益既不相信预测又不相信观测滤波结果变得很飘忽。实际调试时一般固定 R 按物理估计然后只调 Q看轨迹的灵活度是否匹配目标机动。也可以用模拟退火等简单方式扫参数但人工观察误差曲线往往更直接。4.4 观测丢包场景下滤波的韧性这套仿真里灵敏度阈值设成 -70dBm 后可见节点数并不恒定。目标跑向区域远端时个别节点会低于阈值可见节点数可能从 6 个掉到 4 个极少数时刻可能出现少于 3 个可见节点此时没有观测滤波退化为纯预测。我特意统计过丢包时刻的误差变化连续一两秒无观测时由于目标运动相对规律预测轨迹仍然能保持在真实轨迹附近误差增加约半米重新获得观测后滤波器能在几个周期内把误差拉回来。这说明卡尔曼滤波的预测能力在分布式系统中是很有价值的——就算某个时刻观测全部丢失系统仍然能给出一段时间的位置估计而不是直接黑屏。5. 常见坑与排查速查5.1 RSSI 噪声必须加在 dB 域别加在线性域这是最容易被带偏的点。正确的观测生成方式是在 dBm 域加高斯噪声再转换成距离。有人直接在真实距离上加高斯噪声模拟 RSSI 测量误差相当于把整个误差模型搞反了。从数学上看dB 域的固定标准差在线性域对应的是乘性对数正态噪声远处误差大、近处误差小。如果在线性域加固定标准差的高斯噪声近处可能变成负距离远处误差又体现不出来整个定位链路都会失真。RSSI 转距离的公式决定了两者的换算关系完全不同。5.2 最小二乘矩阵奇异或病态怎么办最小二乘定位的 A 矩阵如果接近奇异通常是因为可见传感器几乎共线或者基准传感器和其他传感器离得太近。这时 (A*A) 的行列式接近零求逆结果会非常离谱可能算出几十米外的错误坐标。工程上有两个简单对策一是可见节点多时把所有可见节点轮流作为基准取误差最小的组合二是对定位结果加一个合理性检查如果位置解离所有传感器太远或速度突变异常就丢弃该观测本时刻按无观测处理。后一种方法实现成本低、见效快在真实系统里非常实用。5.3 滤波器发散的三大症状与对策卡尔曼滤波发散最常见的三个症状是轨迹在某几个点出现大幅跳变然后又很快拉回多半是 R 给得太小观测噪声穿透了滤波器。轨迹整体偏离真实轨迹更新基本不起作用多半是 R 给得太大滤波器太依赖预测。误差持续增长不收敛考虑是不是 Q 太小无法覆盖运动模型失配或者状态矩阵写错了导致 P 计算异常。排查时可以先把 P 的每一个时刻输出画出来如果协方差数值异常膨胀或者卡尔曼增益趋近于零基本可以锁定问题出在噪声矩阵的设置上。也可以把真实观测单独画出来和滤波结果对比先确认定位观测本身不是发散的再谈滤波调参。5.4 误差指标统计时别忘了观测丢失段平均距离误差和 RMSE 的统计要覆盖所有滤波输出时刻包括无观测的预测段。有些实现只统计有观测时刻的误差这会高估滤波器性能因为丢包时预测段误差通常更大丢掉这些样本显然不公平。真正交付项目时建议把无观测段的误差单独统计出来和正常时段并排展示。这样既能体现卡尔曼滤波的预测能力也能直观反映系统对覆盖盲区的容忍度。这也是向非技术角色解释“系统什么时候会变差”的最好方式。5.5 代码调试层面的几个小建议第一在跑整条链路前先把真实轨迹画出来确认轨迹范围和传感器部署区域匹配。我调试初期就遇到过目标轨迹跑出传感器覆盖范围的情况结果后面每个时刻都几乎无观测一排查发现是轨迹幅度设置太大。第二每一步都留输出存档。RSSI 观测、距离估计、最小二乘定位结果、滤波结果分别存成变量出了问题可以定位到具体环节不用从头猜。第三随机种子固定下来。RSSI 噪声是随机的不固定种子每次跑的结果都不一样调参时很难判断改参数的效果是真实改善还是随机波动。在代码开头固定 rng等参数调完再放开随机性验证稳定性是最省事的做法。如果把这套代码继续往深做可以考虑几个方向把运动模型升级为匀加速模型或交互多模型来应对更复杂的机动目标在观测端引入加权融合按节点距离质量动态调整每个节点的权重也可以把 RSSI 原值直接作为非线性观测接入无迹卡尔曼滤波省掉最小二乘定位这一层减少误差传播。每条路都有各自的适用场景但核心的“弱观测多来源加滤波平滑”这套思路几乎所有分布式跟踪问题都能复用上。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑