FEATURED · 精选文章

基于MATLAB的行人航位推算(PDR)实现:步态检测与航向估计

发布时间 / 2026/9/20 14:21:40
来源 / 创域科博编辑部
栏目 / 资讯中心
基于MATLAB的行人航位推算(PDR)实现:步态检测与航向估计 简介这套基于MATLAB的PDR-master项目实现行人航位推算Pedestrian Dead Reckoning面向惯性导航、室内定位与传感器数据融合学习者。解决GPS信号受限时借助加速度计与陀螺仪推算行人步态、方向与轨迹的问题。压缩包共286个文件含205个m脚本、43个txt说明、16个csv数据、15个kml轨迹及mcf、prj等配置大小约6.97MB已有876人学习浏览。项目覆盖数据读取、预处理、步态检测、航向估计到位置更新全流程csv、kml便于直接运行验证与轨迹比对m文件带分步注释。整体结构清晰源码、数据、文档分离既可用于课程实践也可作为二次开发基础。 干过PDR项目的人应该都体会过那种明明每一步都测出来了轨迹却越走越歪的憋屈感。我去年在MATLAB里搭行人航位推算PDRPedestrian Dead Reckoning系统时就花了不少时间在步态检测和航向漂移的调参上。累积误差这事儿是惯性导航绕不开的坎但PDR和传统的惯性导航系统INS思路还不太一样——INS是硬积分几十秒就飘得没法看PDR利用步态的周期性做离散推算相当于每隔一两秒就把误差清零一次所以只要步态检测稳、步长模型准、航向不跑偏纯靠低成本MEMS传感器走出像样的轨迹是完全可行的。这套东西放到MATLAB里做验证比在嵌入式板上调要舒服太多。我在项目里用到的核心模块有四个步态检测、步长估计、航向估算、位置更新本文把我在实际工程里的实现思路和踩坑记录整理出来给正要入手或正在做PDR定位的同行一个参考。1. PDR到底是什么值得用MATLAB搭一套完整工程PDR的全称是Pedestrian Dead Reckoning直译过来就是行人航位推算。它不依赖任何外部基站和卫星信号只靠佩戴者身上的惯性测量单元IMU里的加速度计、陀螺仪数据去推算佩戴者走了多少步、每一步走了多远、朝哪个方向走不断累加成一条二维或三维轨迹。和WiFi指纹、蓝牙信标、UWB这类设施定位相比PDR最大的优势是自主性强——在隧道、地下车库、大型商场这种信号复杂的环境里它是少数能持续输出相对位置的方案。但PDR也有它的天生缺点它是一个递推过程每一步的误差都会向后累积所以如果步态检测频繁误判、步长估计偏差大、航向角度有轻微漂移最后画出来的轨迹就会像喝醉了酒的人走出的路线。这也是为什么很多人一开始用MATLAB跑PDR时第一步就是先找一份开源的PDR-master代码跑通再去改自己的数据——因为调PDR的难度不在代码量而在参数之间的耦合关系。我在这个项目里主要做的是离线数据处理读取IMU原始数据文件经过滤波、步态分割、参数估计最后在figure窗口里画出完整的行人轨迹。MATLAB在这类场景里的优势很明显一是矩阵化操作让批量数据处理非常高效几十万行的传感器数据几行代码就能处理完二是自带信号处理工具箱和绘图函数步态检测、轨迹可视化、误差评估这些环节不需要额外引第三方库三是调试时可以直接在命令行看中间变量的变化发现问题能立刻定位到具体函数和参数。2. 步态检测算法怎么搭从传感器数据到步数事件步态检测是整个PDR系统的地基。如果步数都数不对后面步长估计再准都没有意义。步态检测的核心思路是利用人在行走过程中加速度的周期性变化每走一步身体会经历一次向上加速再向下减速的循环这个循环会在加速度数据上表现为一个波峰和一个波谷。2.1 加速度预处理先把重力分量和噪声处理干净我拿到的原始数据是三轴加速度计和陀螺仪采样频率通常在50Hz到100Hz之间。第一步先对原始信号做两件事去重力和低通滤波。去重力这一步很关键。静止状态下加速度计测量的是重力加速度大约1g走路时垂直方向上的加速度会在1g上下波动。如果把整个加速度信号直接拿去做峰值检测重力分量会淹没步态的周期性特征。通常的做法是用高通滤波器把低频的重力分量滤掉或者用姿态角把加速度从载体坐标系旋转到导航坐标系再单独取出水平或垂直方向的分量。我在实测中发现用二阶巴特沃斯高通滤波器截止频率设在0.5Hz左右可以很好地剥离重力分量保留步态频率范围内的信号。fs 100; % 采样率 [b, a] butter(2, 0.5 / (fs / 2), high); acc_hp filtfilt(b, a, acc_raw);这里用filtfilt而不是filter是因为filtfilt是零相位滤波不会引入相位偏移步态波峰的位置不会因为滤波而改变。这一点在峰值检测时非常重要如果相位偏移了步态事件的时间戳就会不对最终影响步长估计和位置更新的对齐。2.2 峰值检测与步数判定三轴合成量的抓取逻辑预处理之后计算三轴加速度的合成幅度acc_mag sqrt(acc_hp(:, 1).^2 acc_hp(:, 2).^2 acc_hp(:, 3).^2);然后利用积累量检测步态波峰。MATLAB自带的findpeaks函数很好用它的优势在于可以设置MinPeakHeight、MinPeakDistance、MinPeakProminence三个关键参数这两个弹性参数能把误检率压下去。[peaks_locs, peaks_mag] findpeaks(acc_mag, ... MinPeakHeight, 0.8, ... MinPeakDistance, round(0.3 * fs));MinPeakHeight的作用是过滤掉幅度过小的噪声峰值。MinPeakDistance的值特别关键它表示两个波峰之间最小的样本间隔正常人步频在1到2.5Hz之间最快可能达到两步每秒所以我一般设置为0.3秒确保检测到的都是合理步态事件而不是同一步的多次抖动。这一步在实际调试时坑不少。慢走和快走的步频差异很大老人和小孩的步频也不同如果MinPeakDistance设太大快走时就会漏检设太小慢走时的微小抖动会被误判成一步。我开始时在写字楼走廊里测50米距离走了大概60步检测出来85个步态事件后来把MinPeakProminence提高了并且加了一个小窗口的峰值平滑处理才把误检压下来。2.3 零速修正ZUPT为什么这个机制能防止误差雪崩纯靠步数检测虽然能算出行走距离但位置更新的过程中每个步长估计的微小偏差会逐步累积走几百米就偏得离谱。这时候就需要利用行人行走的一个天然特性每一步脚着地的那一瞬脚相对于地面的速度是零。这个时刻在步态周期里叫做站立相利用这个零速时刻对速度进行修正的方法就是零速修正ZUPT。ZUPT的检测原理是站立相时加速度计输出的方差特别小因为脚不动了加速度几乎只剩重力而摆动相时加速度方差会明显增大。所以可以用滑动窗口内的加速度方差或加速度模值的方差来判断当前是否处于站立相并以此为触发条件告诉系统此刻累积的速度应当重置为零。window_len round(0.1 * fs); % 滑动窗口大小 var_acc movvar(acc_mag, window_len); is_stance var_acc stance_threshold;在MATLAB里用movvar滑动方差函数一行就能拿到每个时刻的局部方差。实测下来零点阈值设在0.15到0.25倍重力加速度平方之间比较合适但这个阈值受佩戴位置影响很大。挂在腰上时方差值偏大绑在脚踝上时站立相特征特别明显方差很小阈值可以放宽到0.3。设置阈值时宁可多检测一些站立相也不要漏检因为漏检一次就等于丢掉一次误差清零机会后面位置的偏差会直接叠加。3. 步长估计模型选哪个从固定步长到Weinberg模型步态检测解决的是什么时候走完一步步长估计解决的是这一步走了多少米。这是PDR系统里另一个直接决定距离精度的环节。最偷懒的做法是设定一个固定步长比如每步0.7米简单粗暴但对不同身高、不同行走姿态的人来说误差很大实际中并不推荐。3.1 动态步长模型的三个经典数学表达动态步长模型的基本思想是步长不是固定的而是和步频、加速度幅度密切相关。走得越快步子越大加速度波动也越大。目前工程上用得比较多的是三个模型Weinberg模型、Kim模型和Scarlett模型。Weinberg模型利用一步周期内垂直加速度的最大值和最小值来估算步长公式为L K * (acc_max - acc_min)^(1/4)其中acc_max和acc_min分别是该步垂直加速度的峰值和谷值K是事先标定好的系数。这个模型的好处是抗干扰能力相对较强因为它用到了单步内的幅度信息对佩戴位置差异有一定容忍度。Kim模型则是基于步频的模型认为步长与步频近似成正比公式类似L A * step_freq B适合匀速行走的场景但步频变化较大时效果变差。Scarlett模型结合了加速度方差和步频两个因素公式更复杂但精度提升有限。我在实际项目中用的是Weinberg模型原因是它对手机放在口袋走路、手持走路、放在包里走路这几种状态下的表现都比较稳定。下面是一段典型的MATLAB计算逻辑基于detect_step得到的每一对波峰波谷位置来计算步长step_len zeros(length(step_events) - 1, 1); for n 1:length(step_events) - 1 range step_events(n):step_events(n 1); acc_seg acc_vertical(range); acc_max max(acc_seg); acc_min min(acc_seg); step_len(n) K * (acc_max - acc_min)^0.25; end这里要注意acc_vertical是经过高通滤波后的垂直方向加速度分量不要直接用原始三轴模值。我用原始模值试过效果远不如用垂直分量稳定因为摆动时水平方向的加速度分量会干扰最大值和最小值的提取。3.2 步长系数的标定流程和实测效果Weinberg模型里的K值需要提前标定。最常见的办法是让人携带设备走一段已知长度的距离比如30米或50米同时记录步数反推K的值。公式很容易推导已知总距离D和步数N平均步长是D/N而每一步的垂直加速度波动幅度可以算出来K (D/N) / mean((acc_max - acc_min)^0.25)。我一般会标定三组K慢走一组、正常走一组、快走一组使用时根据实时步频选择最接近的一组系数或者对三组K做线性插值。实测下来正常步行速度下Weinberg模型配合标定好的K值50米距离的累计误差能控制在3到5米以内比固定步长法的10米以上误差好得多。标定时有一个容易被忽略的点必须让起始点和结束点是完整的站立相否则第一步和最后一步的不完整步态会干扰平均步长的计算。我在标定时会在起点和终点原地踏步两三秒让步态检测稳定然后截取中间完整的步态数据来反推K。4. 航向角估算陀螺仪积分与磁力计融合的关键点如果说步态检测决定系统能不能跑航向估算就是决定轨迹方向准不准的头号瓶颈。PDR位置更新本质上是一个积分过程x(i1) x(i) L_i * cos(yaw_i) y(i1) y(i) L_i * sin(yaw_i)如果每一步的航向角偏了哪怕1度走走100步之后方向偏差就有好几十米。航向角也是PDR系统里最难调的部分因为陀螺仪积分会漂移磁力计又容易受环境干扰。4.1 陀螺仪积分航向的漂移来源与静止校准三轴陀螺仪提供了角速度数据对z轴角速度积分就能得到航向角变化量yaw_gyro cumtrapz(t, gyro_z);这个方法短时间精度很高但陀螺仪本身存在零偏bias即使静止不动积分也会让航向角不断偏移。更麻烦的是MEMS陀螺仪的零偏会随温度和环境改变而缓慢变化所以光靠开机时校准一次是不够的。我的做法是每次进PDR主循环前取前200个静止状态的陀螺仪z轴均值作为零偏估计减掉gyro_bias mean(gyro_z(1:200)); gyro_z_corrected gyro_z - gyro_bias;如果行走过程中能检测到较长时间的静止比如停下来看手机也可以随时更新零偏估计。这种运行中的零偏修正对抑制长时漂移非常有效。4.2 磁力计航向与互补滤波参数alpha怎么调磁力计可以直接给出绝对航向角原理是地球磁场方向在当地是基本固定的通过x和y轴磁力计读数可以算出设备朝向相对磁北的角度mag_yaw atan2(mag_y, mag_x);磁力计的优点是长期不漂移但缺点是瞬时噪声大尤其在室内钢筋、电梯电机、钢制设备都会扭曲磁场读数会忽大忽小。所以在PDR工程里我不会只用磁力计而是把陀螺仪和磁力计做互补滤波。互补滤波的思路很朴素短时间里相信陀螺仪因为短时积分准长时间里相信磁力计因为绝对参考不会漂移。用一阶互补滤波写的离散形式alpha 0.95; yaw_filtered(n) alpha * (yaw_filtered(n - 1) gyro_z(n) * dt) ... (1 - alpha) * mag_yaw(n);alpha表示对陀螺仪的信任程度一般取0.93到0.98之间。alpha太小磁力计的噪声会被明显引入航向角轨迹会有高频抖动alpha太大磁力计对漂移的抑制能力变弱长距离下航向角还是会慢慢飘。调试的时候建议取0.95试起观察轨迹是否在长直道上有缓慢的弧线偏差。4.3 初始航向对准与坐标系变换的处理PDR输出的轨迹是相对轨迹不是绝对坐标轨迹。系统启动时必须有一个初始航向否则画出来的轨迹是从原点出发朝向任意方向的。最简单的做法是读取设备静止时磁力计的均值用atan2算出初始方位角作为yaw_0然后把后面每一步的航向角都转换成相对于初始航向的变化量这样画出来的轨迹就从出发方向开始展开方向上的观感会好很多。还有一个细节IMU数据默认是载体坐标系device frame而定位结果需要在导航坐标系navigation frame下描述。如果设备贴有不同方向横屏、竖屏、倒置需要先做坐标系对齐。大部分PDR开源代码默认z轴朝上x轴朝前但在实际的手机或穿戴设备里这个前提经常不成立所以需要一组欧拉角把载体坐标系转到导航坐标系。我用的方法是先用加速度计估计俯仰角和横滚角再拿磁力计和陀螺仪做航向解算完整姿态估计可以用MATLAB的ahrsfilter函数省事且效果不错。5. MATLAB工程模块划分与核心代码骨架一个可以真正用于测试和二次开发的MATLAB PDR工程绝不是把算法堆在一起就完事了清晰的结构才能让你快速换数据集、调参数、做误差分析。我在项目里把工程分成了五个模块每个模块职责单一改动互不影响。5.1 工程文件组织和数据接口设计我的推荐文件结构是PDR_project/ ├── data/ │ └── imu_dataset.mat ├── lib/ │ ├── load_imu_data.m │ ├── preprocess_signal.m │ ├── detect_steps.m │ ├── estimate_heading.m │ ├── update_position.m │ └── plot_trajectory.m ├── config/ │ └── pdr_config.m └── main_pdr.mpdr_config.m集中管理所有参数采样率、滤波截止频率、步态阈值、Weinberg系数K等调试时的参数全改这一个文件避免在代码里到处找参数。load_imu_data.m负责读取原始数据把加速度、陀螺仪、磁力计按x、y、z三轴存成结构体并统一到双精度浮点防止后续计算出现精度问题。这个数据接口统一了之后不管是读CSV还是读二进制文件都只改这一个函数。5.2 位置更新的核心循环位置更新的循环在MATLAB里一定要预分配数组避免循环里动态扩张数组导致内存不断重新分配、速度大幅下降。核心逻辑如下pos zeros(length(step_lengths), 2); yaw_rad deg2rad(yaw_filtered(step_indices)); for k 2:length(step_lengths) pos(k, 1) pos(k - 1, 1) step_lengths(k) * cos(yaw_rad(k)); pos(k, 2) pos(k - 1, 2) step_lengths(k) * sin(yaw_rad(k)); end我一开始用过逐点更新位置而不是逐步行更新结果发现计算量大很多精度却没提升。原因很简单步态周期内设备会随着身体摆动产生小幅度位移逐点积分反而引入了身体摆动的高频噪声。按步更新相当于把每一步的位移浓缩成了一个矢量这就是PDR比纯惯性推算更稳的原因。5.3 轨迹绘图与误差评估的常用套路轨迹可视化用plot函数最直接figure; plot(pos(:, 1), pos(:, 2), b-o, LineWidth, 1.5); axis equal; grid on; xlabel(X / m); ylabel(Y / m); title(PDR轨迹);axis equal这一步不能省否则MATLAB默认会拉伸坐标轴直角拐弯看起来像钝角方向偏差很容易被视觉掩盖。如果想和真实轨迹对比用quiver画出每步的方向向量会更直观。误差评估方面我习惯计算轨迹终点与真实终点的距离误差end-point error以及逐点位置误差的均方根RMSE。如果拿到的是有真实位置标签的数据可以先把真实轨迹和估计轨迹对齐到同一个起点然后逐点计算误差rmse_pos sqrt(mean(sum((pos_est - pos_true).^2, 2)));这个指标能直观地告诉你系统在整个路段上的平均漂移水平。如果RMSE很大但终点误差小很可能是中间某段方向偏了后面又偏回来了这时就要回去查那一段的航向估计而不是只看终点。6. 跑数据时最容易踩的坑和调试顺序PDR这东西理论看懂了和实际跑通是两回事我在项目里来回踩过的坑比算法细节本身更值得说。6.1 传感器位置、采样率和参数耦合的三个典型问题第一传感器佩戴位置不一致导致的跨越性差异。同一套参数设备放在兜里和绑在脚踝上的检测结果完全不是一个等级。脚踝处0.1秒一个站立相信号非常清晰腰间则因为身体摆动抵消了部分加速度变化站立相特征不那么明显。最好的办法是固定一种佩戴方式调好参数换方式就重新标定不要指望一套参数走天下。第二采样率低导致步态检测频率分辨率不够。有些设备输出只有20Hz每秒才20个采样点一步大约1秒一个步态周期只有20个点滑动方差窗口都没法取得太短检测出来的步态位置误差很大。我建议采样率低于50Hz的数据在步态检测前先做插值或者直接放弃波峰之间的精细判断改用步频计数器的粗略估计。第三参数耦合问题。步态检测阈值影响步数步数影响步长估计系数KK又影响位置更新距离一连串互相纠缠。调试时必须控制变量先把步态检测参数调到步数误差在2%以内再标定步长K最后才调航向滤波的alpha。如果一开始就急着调alpha轨迹看起来方向对了但距离误差其实已经被步长偏差污染了反而分不清到底是哪一环出了问题。6.2 我推荐的调试路径从静态到复杂路线的四步走我的调试顺序是固定的每次换数据集都这样走一遍静态测试设备静止放置60秒观察步态检测是否有虚报步数、航向角是否有明显漂移。这一步能快速暴露零偏校准是否到位、阈值是否太低。直线测试在走廊走50米直线回到起点观察轨迹能否贴在一条直线上。这是验证航向估计和步长估计配合是否正常的最高效方法。如果直线都走不直多半是磁力计被干扰或陀螺仪零偏没去掉。矩形路线测试沿矩形操场走一圈看四条边是否平行、四个角是否接近直角。这个测试能单独暴露航向漂移的程度因为矩形路线对累计方向误差非常敏感。复杂路线测试模拟真实使用场景多拐弯、有停顿、有快慢变化。最后计算终点误差和RMSE作为系统整体精度的最终指标。实测下来一个参数调好的MATLAB PDR系统在100米左右的室内走廊中终点误差在5到10米范围内属于正常水平如果跑到20米以上基本就是航向或步长估计里有某个环节出了问题不要盲目加滤波或调gain先把四个测试步骤逐项跑一遍定位是哪个模块坏了再动手修。最后再分享一个调试时的实用小技巧把中间过程产品化地绘制出来比如步态事件在加速度波形上标圈、航向角随时间变化的曲线、每一步的步长柱状图。有时候肉眼看轨迹看不出问题但看中间变量一眼就能发现——比如航向角在某一段突然跳变了几十度那基本就是磁力计被干扰了比如步长序列里突然出现一个两倍于正常值的尖峰那大概率是波峰波谷错位了。把这些画图代码留在工程里每次调试能省一半的时间。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻