十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

INS惯性导航作业解算:四元数姿态更新与Python实现

INS惯性导航作业解算:四元数姿态更新与Python实现 简介这是一份基于MATLAB的INS惯性导航算法学习包面向导航工程、航空航天及机器人领域的学生和工程师帮助理解捷联惯性导航系统SINS从IMU数据采集、姿态解算到位置速度推算的完整流程。压缩包共15个文件以9个m脚本为主涵盖四元数运算、方向余弦矩阵DCM、姿态更新及速度位置积分等核心模块另有5个asv自动备份文件和1个mat数据文件整体仅14KB轻量紧凑可直接在MATLAB中运行无需额外输入数据。已有385人学习下载。通过这份作业代码学习者可以深入掌握惯性导航的基本原理体会加速度计与陀螺仪数据如何经过积分、坐标变换和卡尔曼滤波形成连续导航解并学习传感器误差建模与补偿思路。典型处理流程包括数据预处理、姿态解算、速度位置更新以及滤波校正等环节适合在课程作业或入门实践中边运行边调试帮助理解各算法步骤的输入输出关系。对正在完成导航课程作业或入门惯性导航算法开发的读者是一份值得对照学习的紧凑资料。1. 拿到 INS 惯性导航作业先看懂它在解算什么很多人在打开一个 INS 惯性导航作业包时第一反应是去翻算法文件找所谓的“核心公式”。但真正浪费时间的往往不是算法本身而是坐标系、单位、数据排列顺序这些看起来不起眼的东西。惯性导航算法说穿了就是把三轴陀螺仪的角速度积成姿态把三轴加速度计的比力变换到导航系后积成速度再积出位置。三条积分链加在一起就是一套完整的纯惯导解算。这个标题里反复出现的“INS”“惯性导航”“导航作业”指向的正是这条主线。无论代码里用的是欧拉角、四元数还是方向余弦矩阵解算的物理过程都一样用 IMU 的输出推算出载体在导航坐标系下的 attitude、velocity、position。这类作业适合两类人一类是把课程理论转成可运行代码的学生另一类是刚接触组合导航、想先把纯惯导这一环跑通的工程师。后者尤其要留意很多组合导航里的坑其实都埋在纯惯导这一层。2. 惯性导航算法主流程姿态、速度、位置三条更新链2.1 先明确 INS 解算要用的坐标系和符号约定与普通导航不同惯性导航计算里每一条数据都带着坐标系属性混用一次后面的积分结果全错。做作业之前先把三个东西写死载体系body记为 b 系、导航系navigation通常取当地水平坐标系记为 n 系、以及 IMU 数据文件里的轴序。b 系一般按“右前上”或“前右下”定义加速度计和陀螺仪的三轴必须与 b 系三轴严格对应。n 系在作业场景里就是东北天或北东地。这里有个关键点重力向量在这两个坐标系里的符号恰好相反。东北天下 g 取[0, 0, -9.8]北东地下 g 取[0, 0, 9.8]。不要背公式直接把坐标系画出来再决定代码里 g 的符号。提示文件里数据轴序可能是gx, gy, gz, ax, ay, az也可能是ax, ay, az, gx, gy, gz。查不到说明时优先用静止段数据判断静止时加速度计模长应接近 9.8陀螺仪输出应接近零偏。这一步比读任何文档都快。2.2 姿态更新四元数比欧拉角更适合算法实现姿态更新是整个 INS 解算的最前端。陀螺仪输出的是载体坐标系下的角速度需要把它转成姿态变化率。常用做法是四元数更新状态量为四元数q [q0, q1, q2, q3]更新方程为q_dot 0.5 * omega_4x4 * q其中omega_4x4由陀螺仪角速度组成。离散化可以用一阶毕卡近似也可以直接用指数映射。作业场景下一阶毕卡在 100 Hz 到 200 Hz 下精度足够还省去大量矩阵运算。选择四元数而不是欧拉角的原因很实际欧拉角在俯仰角接近 90 度时会出现万向锁方程退化而四元数只有 4 个分量、1 个约束更新后做一次归一化就能保持正交性。代价是姿态角和四元数之间的转换要写两个小函数但这两个函数在各份代码里都是固定套路可以直接当工具函数用。归一化是整个姿态更新里唯一不能省的一步。四元数不归一化等效于姿态矩阵不再是正交矩阵后面的坐标变换会把比力的方向带偏速度误差会成倍往上累。建议每个更新周期做一次。2.3 速度更新比力方程里最容易漏掉的重力补偿加速度计测的是比力不是常规意义上的加速度。静止放在桌面上时加速度计输出的是[0, 0, 9.8]或[0, 0, -9.8]看坐标约定而不是零。所以速度更新里最核心的一步是扣除重力影响v_new v_prev (C_b^n * f_b g^n) * dt这里f_b是加速度计输出的比力C_b^n是由四元数换算出的姿态矩阵。注意符号如果 n 系采用东北天g^n是[0, 0, -9.8]式子里要用加号用减号反而把重力加回去了。除了重力严格推导里还有两项科里奥利项由地球自转引起和向心项载体在地球表面运动引起。作业场景的短时、低速载体下这两项的量级远小于积分误差直接省略问题不大。但如果你的作业动态范围较大、速度在几十米每秒以上建议保留科里奥利项v_new (-2 * w_en - w_ie) x v_prev * dt这一项在组合导航阶段会成为常客趁作业时就把它留好后面扩展会顺很多。2.4 位置更新与整条解算链的误差来源位置更新最简单直接把速度在导航系下做梯形积分pos_new pos_prev 0.5 * (v_prev v_new) * dt用前后两个时刻速度的平均值比单点速度多一步运算但对高频噪声有抑制作用。位置更新层的误差来源极其单一它只取决于速度误差的累计。也就是说姿态误差和比力误差最终都会汇入速度误差再翻倍进入位置。因此观察 INS 解算结果时不要直接盯位置曲线。把姿态、速度、位置三条曲线按层打印出来看误差是在哪一层开始发散的。这是惯性导航算法调错的基本功姿态层误差导致速度层漂移速度层漂移又放大位置层发散问题越靠前对结果的影响越致命。3. 用 Python 复现 6 自由度 INS 解算的最小实现3.1 数据格式预设与变量表写代码之前先按最常见的数据格式做一份假设文件为文本格式每行 7 个数依次是时间戳、三轴陀螺、三轴加速度单位分别为秒、rad/s、m/s^2。若你手里的作业包数据排列不同调整解析那一行即可解算核心不需要改。变量含义单位t时间戳通常不均匀sgx, gy, gz载体系三轴角速度rad/sfx, fy, fz载体系三轴比力m/s^2C_b^n载体系到导航系的姿态矩阵无量纲q姿态四元数无量纲对于时间戳不均匀的数据先做插值或按平均间隔重采样。这里手动改成dt 0.01即假设 100 Hz。绝大部分作业实验台架的采样率是均匀的直接取中间时间差并检查最小值、最大值差多少就能判断。3.2 姿态、速度、位置更新的可分步验证代码完整的解算流程拆成四个函数逐个可以独立测试。先看四元数乘法与向量旋转import numpy as np def quat_mul(p, q): # 四元数乘法Hamilton 约定 p0, p1, p2, p3 p q0, q1, q2, q3 q return np.array([ p0*q0 - p1*q1 - p2*q2 - p3*q3, p0*q1 p1*q0 p2*q3 - p3*q2, p0*q2 - p1*q3 p2*q0 p3*q1, p0*q3 p1*q2 - p2*q1 p3*q0 ]) def rotate_vector(q, v): # 用四元数把向量从 b 系转到 n 系等价于 C_b^n * v qv np.concatenate([[0.0], v]) q_conj np.array([q[0], -q[1], -q[2], -q[3]]) return quat_mul(quat_mul(q, qv), q_conj)[1:]这段代码的要点是四元数乘法顺序不能换先q * qv再乘共轭错一个顺序结果就是双倍角度。Hamilton 约定下共轭取负虚部归一化在外部做。然后是姿态更新。作业场景用简化毕卡近似即可def attitude_update(q, gyro, dt): # gyro: [gx, gy, gz]单位 rad/s wx, wy, wz gyro norm np.sqrt(wx*wx wy*wy wz*wz) if norm 1e-12: return q / np.linalg.norm(q) # 一阶毕卡近似适合 dtheta 小于 0.01 rad 的工况 dtheta norm * dt q_delta np.array([ np.cos(dtheta / 2), wx / norm * np.sin(dtheta / 2), wy / norm * np.sin(dtheta / 2), wz / norm * np.sin(dtheta / 2) ]) q_new quat_mul(q, q_delta) return q_new / np.linalg.norm(q_new)这里q_delta相当于一个从 b 系到当前姿态的四元数增量。把角度增量归一化保证即使姿态角变化较大也不会因为小角度近似失效而出错。注意这里用的是左乘连续更新时顺序保持一致即可。速度与位置更新的实现则要简单得多def velocity_update(v_prev, q, acc, dt, gnp.array([0, 0, -9.8])): # acc 为加速度计比力需先转到导航系 f_n rotate_vector(q, acc) v_new v_prev (f_n g) * dt return v_new def position_update(pos_prev, v_prev, v_new, dt): return pos_prev 0.5 * (v_prev v_new) * dtvelocity_update里的g符号是这套代码里最容易出错的点。上面默认 n 系是东北天所以重力项取[0, 0, -9.8]并在公式里加。如果你的作业声明使用北东地就改成g np.array([0, 0, 9.8])同时把初始位置和初始速度的符号习惯一起翻过来。3.3 把解算流程串起来的运行逻辑与判读主循环结构没有技术难度但初始化最容易丢项def ins_solution(imu_data, dt, init_quat): # imu_data: N x 6 矩阵按 [gx, gy, gz, fx, fy, fz] 排列 q init_quat / np.linalg.norm(init_quat) v np.zeros(3) pos np.zeros(3) traj [] for row in imu_data: gyro row[:3] acc row[3:] q attitude_update(q, gyro, dt) v_new velocity_update(v, q, acc, dt) pos position_update(pos, v, v_new, dt) v v_new traj.append(pos.copy()) return np.array(traj)注意三个细节初始四元数必须归一化每次循环都保存位置副本避免后面对pos的修改污染历史值v必须在pos更新完之后再覆盖否则position_update就少了一个中间速度。运行验证时先看静止段位置应基本不动速度在零点附近小幅游走。再看纯直线运动段位置轨迹应当是一条平滑曲线不能出现明显折返。如果第二步就不对先检查velocity_update里的重力符号再看陀螺仪三轴方向是否与 b 系约定一致。4. 惯性导航作业调参要点初始对准、标定参数与验证轨迹4.1 初始对准决定几分钟内结果的好坏纯惯导是积分系统初始姿态错 0.1 度一分钟后的位置误差可能在几十上百米量级。初始对准的常见做法分两步水平对准和方位对准。水平对准直接用静止时刻加速度计输出的重力方向反推横滚角、俯仰角方位对准则有讲究。大多数作业不会给高精度陀螺也没有外部航向参考方位角一般直接设 0 或用磁力计粗测。这时要知道一个关键结论方位角误差不像水平姿态误差那样直接通过重力耦合进速度它表现为一条缓慢增长的横向漂移短时间内不容易发现。所以作业验收时如果只跑 1 到 2 分钟方位角给个大概值影响有限如果跑 5 分钟以上方位误差就主导整个位置误差。从静止数据里算初始横滚与俯仰的代码很固定def initial_attitude(acc): # acc 为静止段加速度均值东北天下测得的比力约等于 -g # 所以静止时 acc 约等于 [0, 0, 9.8] ax, ay, az acc roll np.arctan2(ay, az) pitch np.arctan2(-ax, np.sqrt(ay*ay az*az)) return roll, pitch # 单位 rad这里不要用欧拉角的加减去凑直接按方向余弦矩阵的定义解唯一的角度即可。算完再回头用roll, pitch构造初始四元数就能保证水平姿态的初值精度在零点几度以内。4.2 标定参数里值得动手调的三个量作业包里 IMU 数据通常已被标定过但残留误差依然存在。三个量对解算影响最大陀螺零偏、加速度计零偏、比例因子误差。参数典型量级对解算的影响陀螺零偏0.01~0.1 deg/s姿态漂移进而引起速度二次增长加表零偏1~10 mg速度线性漂移位置二次发散比例因子误差0.1%~1%动态段误差速度越大越明显调参顺序也是这个顺序。先用静止段数据求陀螺零偏直接取静止时三轴输出的均值解算时减去。加速度计零偏的标定需要多位置翻转作业场景通常没有条件做用静止段的平均模长与 9.8 的偏差做个粗略补偿即可。比例因子误差对静止数据不可观察只能通过直线运动段的距离偏差估算。提示千万不要用陀螺零偏直接补偿加速度计零偏。这两者作用的物理通道不同混在一起调参数结果只是把姿态误差和速度误差做了交换位置误差比之前更大。4.3 用静止段和直线运动段验证解算轨迹解算完不要直接画一整条轨迹先分段验证。一般实验数据会包含“静止 → 前进 → 停止 → 转弯 → 前进 → 停止”几个阶段每一段都有独立的验证标准。把速度输出画出来看静止段速度是否在零附近匀速段速度波动是否在可接受范围。这段代码只做一件事却最能定位问题# 静止段速度 RMS 应小于 0.05 m/s static_idx data[:, 0] t_after_align static_vel_error np.sqrt(np.mean(velocity[static_idx]**2, axis1)) print(静止段速度RMS:, static_vel_error)如果静止段速度 RMS 在零点几米每秒以上先查姿态更新有没有每步归一化再查零偏补偿有没有生效。如果直线段位置误差与速度积分误差不匹配再检查速度更新里是不是漏了重力补偿。这个金字塔式的排查顺序比盯着整条轨迹找原因高效得多。5. 用反向时间解算检验惯性导航代码的坐标轴符号5.1 反向解算的原理正反轨迹应该对称惯性导航解算对时间不可逆正向跑一遍再把数据倒序跑一遍两条轨迹在固定时间段内应当关于起点对称。原因在于解算方程里的角速度、比力都是带方向的矢量反着积分等于把时间倒流物理含义上轨迹应当回到原点附近。这个特性可以用来定位一个非常隐蔽的坑IMU 数据某一根轴的符号接反。正向解算时符号反了的轴会让载体轨迹朝错误方向弯曲但位置依然连续肉眼很难判断是哪根轴。反向解算时符号反了的轴会让轨迹反向弯曲一次正反轨迹的末端偏差会比正常情况大一倍以上。利用这个特性还给作业验收多了一种手段不管标定参数是否完美只要正反轨迹在短时段内基本对称就能确认数据通道和坐标轴方向没有大问题如果不对称优先检查陀螺和加表的三轴极性再检查四元数乘法顺序。5.2 检验步骤与判据实际执行时不需要写额外算法把数据文件倒序即可。Linux 下直接用tac或awk倒序Windows 下用 Python 读入后反转数组# 正向解算 python ins_solution.py --input imu_data.txt --output traj_forward.txt # 反向解算把时间戳和量测列整体倒序 awk NR1{first$0} {lines[NR]$0} END{for(iNR;i1;i--) print lines[i]} imu_data.txt imu_data_rev.txt python ins_solution.py --input imu_data_rev.txt --output traj_reverse.txt注意反向解算不是简单地把时间改成负数而是把整段数据沿时间轴镜像时间戳要从 0 重新计否则四元数更新里用dt计算角度增量时负时间会让角度增量的符号反过来结果全乱。判据取法正向轨迹和反向轨迹各自取同一时间索引处的位置点计算逐点距离。两者之间的距离应该近似为两倍的同时刻位置误差且随时间缓慢增长。若某个时间点距离突跳说明该时刻的姿态更新或数据符号有突变。更简单粗暴的办法是看末端点正向轨迹的终点和反向轨迹的终点应当落在一个小邻域内作业数据质量好时能到几十厘米以内差一些也不该超过几米。这一招在后面接卡尔曼滤波做组合导航时同样用得上。IMU 的流动标定、零速修正、以及航向观测进来之前先用反向解算把纯惯导数据管道验干净能省掉一整轮排错时间。纯惯性导航代码的常见问题里十个有八个出在符号和坐标约定上而不是滤波或积分算法本身。本文还有配套的精品资源点击获取
返回列表