简介:PDR-master是一套基于MATLAB实现的行人航位推算(PDR)算法资源,面向室内定位、惯性导航与可穿戴追踪方向的研究者或工程师,解决无GPS环境下利用加速度计与陀螺仪推算行人步态、方向及轨迹的问题。资源包共286个文件,大小约6.97MB,以205个m脚本为核心,另有16个csv传感器数据集、43个txt说明文档、15个kml轨迹文件等,便于对照代码理解数据格式与定位结果。通过阅读和运行源码,可掌握步频检测、航向估计、卡尔曼滤波等关键环节,并能用附带数据验证和优化定位精度,kml轨迹可导入地图查看效果。目前已有876人学习下载,适合具备一定MATLAB和滤波基础、希望快速上手PDR的开发者参考。 去年做室内人员定位项目的时候,我接触到一个很有意思的开源项目——PDR-master_matlab_pdr惯性导航_PDR。简单说,这是一套用MATLAB实现的行人航位推算算法,英文叫Pedestrian Dead Reckoning,缩写正好就是PDR。做室内定位、人员轨迹追踪、应急救援、商场导航这类场景的同学,对这个词应该不陌生。
这套方案的核心思路是:在没有GPS的室内环境下,利用手机或穿戴设备上的惯性传感器——加速度计、陀螺仪、磁力计——来推算人走了多少步、朝什么方向走,从而实时绘出运动轨迹。它解决的问题非常直接:进了商场、地铁站、地下车库,手机信号漂移严重甚至完全没信号,怎么知道人走到哪了?靠惯性导航推算就是一条性价比很高的技术路线。
这套MATLAB工程比我在实际项目中遇到的很多商用SDK可读性高得多,代码逻辑很清晰,适合拿来学习,也适合改造成自己的定位原型。这篇文章我就从项目结构、核心算法、实操复现到工程踩坑,把你动手实现PDR时该知道的细节一次讲透。
1. 项目结构拆解:PDR方案为什么这么设计
1.1 先搞清楚PDR和传统惯导的定位区别
在讲代码之前,我建议你先理解一个根本性的问题:为什么行人导航不用传统的惯导捷联解算?
传统惯性导航是把加速度积分两次得到位移,把陀螺仪积分得到姿态。这个方案在飞行器、导弹上没什么问题,但放在人身上就完全不行——消费级MEMS传感器本身的零偏和噪声很大,加上行人走路是高频振荡运动,加速度积分两次之后误差会像滚雪球一样膨胀。只要几秒,位移就不知道飘到哪里去了,这就是所谓的"积分漂移"。
PDR换了个思路:它不去精细积分每一毫秒的位移,而是把行人运动当作"一步一个脚印"的离散过程。算法只需要识别"走了一步",估计"这一步多长",再判断"这一步朝哪个方向",最后累加位置。这样做的好处是,误差不会随时间无限累积,而是在每一步的粒度上可控。PDR-master这套MATLAB项目采用的就是这个Step-and-Heading模式,这也是目前行业内做行人定位沿用最广的框架。
1.2 系统级框架:四个模块接力配合
这套MATLAB PDR工程的核心链路分为四块:步态检测、步长估计、航向估计、位置更新。
- 步态检测:从加速度计数据中识别出每一步,输出步伐事件。
- 步长估计:根据每一步的加速度特征估算步长,把"步"换算成"米"。
- 航向估计:通过陀螺仪和磁力计计算行人的朝向角度。
- 位置更新:以起始点为原点,把步长分解到x、y方向,累积出轨迹。
项目先在MATLAB里读入IMU原始数据,经过时间对齐、滤波预处理,再分别送进这四个模块处理,最终用plot绘制出二维轨迹。这个框架的好处是模块之间低耦合,你可以在MATLAB里单独调一个模块的参数,不影响其他部分,非常方便做算法迭代试验。
2. 核心算法模块深入解析
2.1 步态检测:怎么从加速度信号里数出每一步
步态检测是整个PDR里最容易踩坑、也最影响体验的一环。步数数错,后面全崩。
常见的方案有三种:峰值检测法、过零检测法、自相关分析法。PDR-master里主要用的是峰值检测,这也是工程上最直观、运算量最小的方法。原理很简单:人在走路时,垂直方向的加速度会呈现周期性的波动,每迈一步就会产生一个明显的波峰和波谷。
不过直接对原始加速度做峰值检测是行不通的。因为上下楼的冲击、手臂摆动、手机晃动,都会产生伪峰。实际处理时要先做两步预处理:去重力直流分量和低通滤波。我自己惯用的做法是取三轴加速度的模值sqrt(ax^2+ay^2+az^2),再做滑动窗口均值平滑,或者用一个截止频率5Hz左右的低通滤波器把高频抖动滤掉。这样得到的波形干净很多,峰值检测的成功率会显著提升。
峰值检测有几个关键参数需要调:最小峰值阈值(比如1.2倍重力加速度)、相邻步的最小时间间隔(一般取0.3~0.5秒,防止一次迈步被识别成两步)、以及峰值的动态更新策略。其中最小时间间隔特别重要,否则你在颠簸路面跑起来,波形高频率震荡就能把步数刷到天上去。
2.2 步长估计:为什么步长不是固定值
数出步数之后,下一步是把步数换算成距离。很多新手会直接设一个固定步长:0.7米一步。这个做法在直线匀速走时还行,但只要速度变化就完全失效——你慢走和快走、小步走和大步走,步长差异很大。
工程上常用的步长模型有两类。一类是基于步频的线性模型,步长和步频成正相关;另一类是基于加速度幅值的模型,最经典的是Weinberg模型:
step_length = k * (acc_max - acc_min)^(1/4)
其中acc_max和acc_min是这一步周期内加速度模值的最大值和最小值,k是一个需要标定的比例系数。这个模型的物理含义是:步子迈得越重、身体起伏越大,通常步长也越长。PDR-master项目里用的就是类似的幅值模型,关键就是k值的标定。
标定k的方法其实不复杂:找一段已知长度的直线路径,比如30米,按你的正常步速走一遍,记录算法识别出的步数,然后用真实距离除以估算的步长之和,反推k。最好是快走、慢走、正常走各来一遍,取平均值,这样模型在不同速度下都有一定的鲁棒性。
2.3 航向估计:PDR准确度的最大变量
如果说步态检测决定了步数准不准,那航向估计就直接决定轨迹往哪个方向画。PDR项目中航向输出的是航向角,一般是0~360度或者-180~180度,以正北或起始方向为0。
航向来源主要有两个:磁力计和陀螺仪。磁力计能给出绝对航向,但它极其容易受环境中的铁磁物质干扰——室内钢筋结构、电脑主机、金属门框都会让它突然跳变。陀螺仪积分航向很平稳,但它有零偏,积分时间长了会持续漂移。
所以工程上最稳妥的思路是互补滤波:短时间以陀螺仪积分为主,长时间用磁力计修正漂移。用公式表示就是:
yaw = alpha * (yaw_gyro + gyro_z * dt) + (1 - alpha) * yaw_mag
其中alpha一般取0.95左右,代表更信任陀螺仪短期的变化量。这个思路和PDR-master里实现的航向模块是一致的。如果你在室内跑出来的轨迹是"越走越偏",八成不是步长问题,而是航向在什么时候被磁力计给带偏了,这个我们在后面的问题排查章节会细说。
2.4 位置更新:把一步分解到地图坐标上
拿到步长和航向后,位置更新其实就是一个简单的三角函数累加:
pos_x = pos_x + step_length * sin(deg2rad(yaw)); pos_y = pos_y + step_length * cos(deg2rad(yaw));这里坐标系最好提前约定:0度是正北,顺时针转;或者0度是起始朝向。两种约定都可以,但一定要在代码里保持一致,否则画出来的轨迹可能和实际轨迹呈镜像关系。PDR-master代码里用的是相对起始朝向的累计,你看代码时留意一下这个细节。
3. 实操复现:从数据集到轨迹可视化
3.1 环境准备与数据采集
这个项目用MATLAB跑,我建议版本在R2019b以上,用到了信号处理相关的基础函数和绘图接口。如果你手头没有MATLAB,用GNU Octave也能运行大部分代码,个别工具箱函数需要手动替换,但核心算法不受影响。
数据从哪来?有两条路:一是项目自带的测试数据文件,直接load进来跑;二是用你自己的手机采数据——Android手机装一个"Sensor Record"或者"Phyphox"App,把加速度计、陀螺仪、磁力计的原始数据录成CSV。录制时注意采样率,50Hz以上最好,太低会导致步态波形失真,步数识别率下降。我自己一般用100Hz采,数据量不大,但处理起来波形细节保留完整。
数据格式建议统一成这样三列一组,时间戳单独一列:
timestamp, acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z, mag_x, mag_y, mag_z3.2 数据预处理:滤波与去直流偏置
拿到原始数据的第一步不是直接跑算法,而是先观察数据波形。用plot把加速度模值画出来看看,是不是有明显的周期性波动。如果波形毛毛躁躁的,先做低通滤波。
以下是用MATLAB做Butterworth低通滤波的示例:
fc = 5; % 截止频率5Hz,行人步行主要能量都在这个频段内 fs = 100; % 采样率,按你实际数据修改 [b, a] = butter(4, fc/(fs/2), 'low'); acc_filtered = filtfilt(b, a, acc_raw);注意这里用了filtfilt而不是filter,因为filtfilt是零相位滤波,不会引入相位延迟。在步态检测里面,相位延迟意味着波形整体右移,会让步态事件的时间戳偏晚,直接干扰步长计算的时间窗口。这个细节很多人不知道,等你在图上对比一下滤波前后的峰值位置就明白了。
3.3 步态检测核心代码逻辑
这是PDR-master里最核心的步态检测逻辑。我列举一个简化但能跑通的关键代码段:
% 输入: acc_mag 为滤波后的加速度模值 % 参数 peak_threshold = 9.8 * 1.2; % 峰值阈值,大于1.2g才算是步态波峰 min_step_interval = 0.4; % 两步之间的最小时间间隔,单位秒 fs = 100; % 采样率 step_indices = []; last_step_idx = -inf; for i = 2 : length(acc_mag) - 1 % 峰值检测:当前点大于阈值,且大于前后相邻点 if acc_mag(i) > peak_threshold && ... acc_mag(i) > acc_mag(i-1) && ... acc_mag(i) >= acc_mag(i+1) % 检查与前一步的时间间隔 if (i - last_step_idx) / fs >= min_step_interval step_indices(end+1) = i; last_step_idx = i; end end end step_count = length(step_indices);这个代码看起来简单,但实际项目里有两处容易踩坑。第一,阈值是动态的还是固定的?固定阈值在平路好用,但在上下楼梯时波形幅值差异很大,建议按时间窗口动态更新:比如取最近2秒波形最大值的一定比例作为当前峰值阈值。第二,相邻步的最小时间间隔,如果设得太短,走路时鞋跟触地的二次震动会被误判成新的一步;设得太长,快走时又会漏步。0.4秒大约是每秒2.5步的节奏上限,超过这个就很可能是误检。
3.4 航向解算与轨迹绘制
航向解算建议用互补滤波。初始化时取前50个磁力计样本的平均方向作为初始朝向,然后利用陀螺仪的z轴角速度积分增量航向:
% 陀螺仪积分 yaw_gyro = yaw_prev + gyro_z * dt; % 磁力计瞬时航向 yaw_mag = atan2(mag_y, mag_x); % 按实际轴向定义转换 % 互补融合 yaw = alpha * yaw_gyro + (1 - alpha) * yaw_mag;最终把每一步的步长和航向累积到坐标上,画出来:
pos_x = cumsum(step_length .* sind(yaw_at_steps)); pos_y = cumsum(step_length .* cosd(yaw_at_steps)); plot(pos_x, pos_y, 'b-', 'LineWidth', 1.5); axis equal; grid on;axis equal必须写,否则x和y轴的比例不一致,正方形路径会被画成长方形,视觉判断会严重误导你对算法精度的判断。这也是新手特别容易忽略的绘图细节。
4. 常见问题与排查技巧实录
4.1 步数统计不准:多计与漏计的常见原因
下面列几个我在调PDR-master项目时遇到的高频问题,基本覆盖了90%的定位不准场景,整理成速查表:
| 现象 | 典型原因 | 排查方向 |
|---|---|---|
| 步数明显偏多 | 地面颠簸、抖动造成的伪峰 | 提高峰值阈值,增加最小步间隔限制,加强低通滤波 |
| 步数偏少 | 阈值过高,步态波形幅值偏小 | 降低阈值,改用动态阈值,检查滤波是否过度平滑 |
| 静止时还在计数 | 传感器噪声被当成波峰 | 增加零速检测(静止时加速度方差很小),方差低于阈值则禁检 |
| 轨迹方向偏移 | 磁力计受环境干扰 | 观察航向曲线是否有跳变点,对磁力计异常段改用纯陀螺积分 |
| 轨迹整体旋转 | 初始航向设置错误 | 检查0度基准的定义,确认磁力计轴方向和地图坐标轴对应关系 |
4.2 航向漂移问题:磁力计干扰识别
磁力计在室内几乎必然受干扰,但不是说不能用,而是要懂得"信一段、丢一段"。我的做法是计算磁力计模值sqrt(mag_x^2+mag_y^2+mag_z^2),正常情况下这个模值应该接近当地地磁总强度,波动不大。一旦发现模值显著偏离正常范围,或者跳变剧烈,就判定当前处于磁干扰环境,把互补滤波的权重临时改成更偏信托螺仪,等模值恢复正常后再重新信任磁力计。
这个方法在商场、办公室环境中实测很有效。你在调PDR-master时,可以先把磁力计模值画出来看一看,通常那些"跳楼式"的毛刺出现的位置,就是轨迹开始偏航的位置。
4.3 校准步长系数k的实操心得
步长系数k的标定误差会在长距离时被放大。你走100米,每步长误差3厘米,累计下来就是一两米的偏差。我的建议是标定时走一条带折返的路线,比如25米直线走到头,转身走回来,这样等于标定了两趟,还能验证算法的对称性——好的步长模型来回的步数应该几乎一致。
另外,不同的人走路习惯差异很大:有人步频高、步幅小,有人步频低、步幅大。如果系统要服务多个用户,最好在程序里做一个"用户步态自学习"功能:让用户先走一段已知距离,自动反推k值,再进入正常推算模式。PDR-master里k值是写死的,实际产品化时需要改成可配置参数。
4.4 采样率和实时性问题
这个MATLAB项目是离线处理的,加载一整段数据文件然后跑完,得到完整轨迹。但工程上PDR通常是流式处理的:传感器数据一来,就要在当前步结束时立刻输出一个新的位置点。
做在线处理时要注意两点。第一,窗口式处理——不要等所有数据到齐再算,而是维护一个滑动窗口(比如2秒),步态检测在窗口内完成,检测到一步就立刻更新位置。第二,算法复杂度——峰值检测和互补滤波都是O(n)级别的计算,在C语言或嵌入式设备上毫无压力,MATLAB里做实时仿真也可以借助drawnow逐帧刷新轨迹图,只是帧率不要太快,否则绘图本身反而成为瓶颈。
5. 项目扩展:从单惯导到融合定位
5.1 PDR的固有缺陷与解决思路
任何基于PDR的方案都有一个绕不过去的缺陷:相对定位,没有绝对坐标。它在短时间内精度不错,但走久了累计误差还是不可避免。你走500米之后,终点误差可能达到10米甚至更多,而且你无法判断到底是在哪一步开始偏的。
解决这个问题的思路非常明确:给它一个"绝对参考"。常见做法有几种:
- PDR + 地图约束:预先建立室内地图,PDR轨迹碰到墙壁就修正,或者用地板走向识别(走廊直线、拐角90度)来修正航向。
- PDR + 蓝牙/WiFi指纹定位:定期用指纹定位得到的绝对位置去校正PDR的累积位置。
- PDR + 粒子滤波:把PDR的步长和航向作为运动模型,把地图信息和信号指纹作为观测模型,做贝叶斯滤波,这是目前室内融合定位精度最高的工程方案之一。
在MATLAB里,你可以在PDR-master基础上加一个粒子滤波模块。粒子滤波的思路不复杂:初始化N个粒子,每个粒子代表一个可能的位置,步态事件发生时所有粒子按同样的步长和航向移动,然后根据观测模型给粒子赋权重,最后重采样。这套流程在MATLAB里实现大概两三百行代码,就能把定位精度提升一个档次。
5.2 MATLAB工具箱与后续开发建议
跑这个项目主要的依赖是MATLAB基础环境和信号处理工具箱(Signal Processing Toolbox)。butter、filtfilt都在里面。如果没装工具箱,也可以自己写一个简单的移动平均滤波器替代,平滑效果略差一些,但步态检测基本可用。
最后给你一条过来人的建议:不要只看代码,一定要自己采集几组不同场景的数据去跑。我踩过最大的坑就是——用仿真数据调试时一切正常,换到真实手机数据之后,步态检测和航向估计各种失灵。真实数据的噪声、丢帧、异常跳变,是仿真数据永远模拟不出来的。把这三类场景的数据各采一组:平路直行、弯道环绕、上下楼梯,再配合你调好参数,这套PDR才算真正能用。
另外,如果你想把算法迁移到嵌入式平台,建议把MATLAB代码里的滤波和步态检测部分用C语音重写,配合MPU6050或BMI160这类六轴传感器,做一个腕带式或腰带式的硬件原型,就是一套完整的低成本室内定位方案了。MATLAB阶段把算法逻辑验证清楚,后面移植就是体力活了。
本文还有配套的精品资源,点击获取