拆解 PSINS 第 0 篇:读码地基——数据结构、六大约定、主循环骨架¶
拆解 PSINS 系列的"地基"篇。正式逐行拆解前,先把读懂 PSINS 代码所需的全部"通用前置"一次性立起来:代码长什么样、数据结构怎么存、有哪些反直觉的约定、主循环怎么转。读完它,再看 P1–P4 和后续逐行注释篇,不需要任何额外上下文。
本篇也是 Mini-INS 参考实现库(见 00 内化蓝图)的文档入口——文中的数据结构速查表,就是库里
data_classes.m的字段定义来源。
一、代码地图:demo 是"薄壳",肉在 base/¶
PSINS 的源码分两层,读懂这个结构就成功了一半:
PSINS/
├── demos/ # 薄壳:每个 demo 只"点菜",几乎不干活(19~40 行)
│ test_SINS_trj.m / test_SINS.m / test_DR.m / test_SINS_DR.m / test_SINS_GPS_153.m …
└── base/ # 肉:库函数,按职能分目录
├── base1/ # 轨迹与机械编排:trjsegment / trjsimu / insupdate / cnscl / earth / qupdt2 …
├── base2/ # 框架与误差:inspure / avperrset / avpadderr / poserrset / avpcmpplot …
├── kf/ # 卡尔曼:kfinit / kfupdate / kffk / kffeedback / kfplot …
├── imu/ # 传感器误差:imuerrset / imuadderr / odsimu …
└── plot/ # 画图闭包:insplot / imuplot / myfigure …
demo 的正确读法 = 把它当"数据流入口":每一行调用都是一扇门,推开门(读库函数)才是拆解。三个薄壳消费同一份数据母版:
| demo | 行数 | 调用 | 消费 |
|---|---|---|---|
test_SINS_trj.m | 31 | trjsegment/trjsimu | 无(造数据母版 trj10ms.mat) |
test_SINS.m | 19 | inspure(内含 insupdate) | 母版 |
test_DR.m | 29 | odsimu/drinit/drupdate | 母版 |
test_SINS_DR.m | 40 | etm/kffk/kfupdate/kffeedback | 母版 |
二、六大约定(全系列统一字典)¶
PSINS 有 6 个反直觉的约定,每一个都踩过坑。先记熟,读代码时处处用到:
| # | 约定 | 内容 | 踩坑后果(如果不注意) |
|---|---|---|---|
| 1 | global glv 全局量 | 每个函数第一行几乎都是 global glv,地球模型常量全在 glvs.m 装的 glv 里(glv.Re/glv.wie/glv.g0/glv.deg…) | 单独拎一段代码出来跑会报"glv 未定义"——PSINS 代码不能脱离全局量单独读 |
| 2 | att 顺序 [pitch;roll;yaw] | 姿态向量是 [俯仰; 横滚; 航向](欧拉角顺序 p-r-y) | 和很多输出习惯(yaw-pitch-roll)不同,对拍前要先重排 |
| 3 | pos 顺序 [lat;lon;h],弧度+米混用 | 纬度/经度是弧度,高度是米 | poserrset 的位置误差要按 /Re 转弧度(P4 坑 3 填错直接发散 NaN) |
| 4 | MATLAB 列主序 | 矩阵按列存储,M(2) 是第 (2,1) 格,不是 (1,2) | 照搬 etm.m 线性索引时读反符号(P4 坑 2)→ 滤波器归因反了 |
| 5 | rad 单位贯穿 | 角速度/角度全用弧度,换算靠 glv.deg/glv.min | inst=[3;60;6]*glv.min 这类写法,漏乘 glv.min 单位差 60 倍 |
| 6 | avp 大列向量 [att;vn;pos] | 导航状态拼成 9×1(或 12×1 含初始)列向量,ins.avp 是它的便捷形式 | 结构体 ⇄ 列向量互转时维数对不上 |
📌 第 0 篇的重点提醒:约定 2/3/4 是"坐标系与存储"层面,约定 1/5/6 是"代码风格"层面。前三个影响数学,后三个影响读码——它们共同构成了"PSINS 风味"。
三、数据结构速查表(本篇最大交付)¶
⚠️ 字段名/顺序以实际 PSINS 源码为准,本表是阅读速查(若与你的 PSINS 版本有出入,以源码为准)。这是 Mini-INS 库
data_classes.m的设计蓝本。
3.1 trj(trjsimu 产出,数据母版)¶
| 字段 | 维度 | 含义 |
|---|---|---|
avp0 | 12×1 | 初始状态 [att(3); vn(3); pos(3)] |
avp | N×10 | 每拍真值:[att(3) vn(3) pos(3) t](att rad、vn m/s、pos 弧度+米) |
imu | N×7 | 每拍 IMU 增量:[wm(3) vm(3) t](增量,rad / m/s,不是速率) |
od | N×2 | 每拍里程增量:[S t](P3/P4 用,dS 单位 m) |
ts | 1×1 | 采样间隔(0.01 s = 100 Hz) |
len | 1×1 | 总拍数(96600) |
3.2 ins(insinit 建立,解算状态)¶
| 字段 | 维度 | 含义 | 谁读写 |
|---|---|---|---|
qnb | 4×1 | 姿态四元数(体→导航,[w;x;y;z]) | insupdate 姿态更新 |
vn | 3×1 | 速度 [vE;vN;vU](ENU) | 速度更新 |
pos | 3×1 | 位置 [lat;lon;h] | 位置更新 |
eb / db | 3×1 | 陀螺 / 加计零偏(rad/s、m/s²) | 标定步 |
wib | 3×1 | 陀螺测量角速度(含地球自转) | 姿态更新 |
wnb | 3×1 | 体轴相对导航系角速度 | 姿态更新 |
eth | struct | 地球参数:RMh/RNh/wnie/wnin/gcc | 每一步 |
Kg / Ka | 3×3 | 刻度 / 安装误差阵(单位阵 = 无误差) | 标定步 |
t | 1×1 | 当前时间 | 循环 |
3.3 kf(kfinit 建立,卡尔曼状态)¶
| 字段 | 维度 | 含义 |
|---|---|---|
Phikk_1 | n×n | 离散状态转移阵(每步由 kffk 刷新) |
Ft | n×n | 连续状态阵(etm.m 生成) |
Hk | m×n | 量测阵 |
Rk | m×m | 量测噪声协方差 |
Pk | n×n | 状态协方差 |
xk | n×1 | 状态估计(误差状态,如 22 维) |
Qk / Gk | — | 过程噪声(离散/连续) |
3.4 dr(drinit 建立,航位推算状态)¶
| 字段 | 维度 | 含义 |
|---|---|---|
qnb | 4×1 | 姿态四元数(P4 里直接抄 ins.qnb) |
pos | 3×1 | 位置 |
vn | 3×1 | 速度(DR 由里程 dS 推速度) |
Kg | 1×1 | 里程计刻度系数(≈1) |
att | 2×1 | 安装角(俯仰/航向,P4 的 dinst) |
3.5 一句话记忆¶
trj= 剧本(真值 + 读数),ins= 演员(解算状态),kf= 教练(误差修正),dr= 另一个演员(独立定位链)。- 所有"状态"都是列向量,所有"时间序列"都是矩阵(每行一拍,最后一列 t)。
四、主循环骨架:P2 / P3 / P4 其实同构¶
四个 demo 的主循环是同一个骨架换零件:
for k = 1:nn:len-nn+1 % 每 nn 拍(双子样 nn=2)推进一次
wvm = imu(k:k1, 1:6); % 取本双子样的角增量 + 速度增量
[解算核心] % ← 换零件的位置(下表)
...
end
| demo | 解算核心(每双子样) | 附加动作 | 输出 |
|---|---|---|---|
test_SINS.m | insupdate(ins, wvm) | 高度阻尼 'Z'(钉 trj.bh) | 纯惯导 avp |
test_DR.m | drupdate(dr, wvm(:,1:3), dS) | 无 | DR 位置 |
test_SINS_DR.m | insupdate → dr.qnb=ins.qnb → drupdate → kffk → kfupdate | 每 10 拍(0.1 s)量测 + kffeedback 'v' 速度闭环 | 组合 avp |
读法:P2 的循环是"SINS 单机",P4 的循环是"SINS + DR + KF 三机"——多出来的每一步都是往骨架里加零件。理解了这个骨架,任何 PSINS demo 拿到手都能 10 秒定位它在哪个零件上做了文章。
五、三本"字典"的分工¶
| 文档 | 管什么 | 什么时候翻 |
|---|---|---|
| 本篇(00 读码地基) | 代码约定 + 数据结构 + 主循环 | 看不懂任何一行 PSINS 代码时 |
| 00 内化蓝图 | Mini-INS 库的路线图 + 写作规范 | 想知道"拆完怎么变成自己的库"时 |
| 附录 · 绘图函数与 MATLAB 惯用法 | MATLAB 语法词典(nargin/subplot/interp1…) | 看不懂某句 MATLAB 语法时 |
一句话:迷路了先回这三处——约定不懂回本篇,语法不懂回附录,方向不懂回蓝图。
六、自测题¶
trj.imu每行 7 列,前 6 列是什么?为什么叫"增量"而不是"速率"?ins.qnb和dr.qnb在 P4 里是什么关系?为什么?- 为什么
poserrset的位置误差要按/Re转弧度? - MATLAB 列主序下,
M(2)是矩阵的哪个元素?这和 P4 坑 2 有什么关系? test_SINS.m和test_SINS_DR.m的主循环,每双子样各做了几步?差在哪?
📐 参考答案
[wm(3) vm(3)]= 角增量(rad)+ 速度增量(m/s),是每个采样间隔内积出来的量;陀螺/加计输出本来就是增量(脉冲/积分结果),注入误差、标定补偿都要乘 ts。- P4 里
dr.qnb = ins.qnb(DR 姿态硬抄 SINS)——车辆 DR 没有独立航向源,陀螺是唯一方向源,抄 SINS 保证 DR 方向一致;KF 只靠位置差修正。 - 因为
ins.pos的 lat/lon 单位是弧度(约定 3),位置误差若用米表示必须先除地球半径转弧度,否则数量级差 6×10⁶ 倍直接发散。 M(2)= 第 (2,1) 格(第 2 行第 1 列)。etm.m用线性索引填 F 矩阵时,读错行/列顺序会把符号写反(如 Mva 变 -askew),滤波器归因反转 → 组合发散。test_SINS:1 步(insupdate)+ 高度阻尼;test_SINS_DR:4 步(insupdate → 锚 DR → drupdate → kf 预测)+ 每 0.1 s 量测反馈。多出的 3 步都是往骨架里加 DR 和 KF 零件。