资讯动态

捷联惯导最优平滑算法:RTS原理、实现与工程实践

发布时间:2026/8/14 10:57:52 来源:尧图企业网站定制
1. 项目概述从“事后诸葛亮”到数据价值的极致挖掘在惯性导航尤其是捷联惯导系统SINS的工程实践中我们常常面临一个经典矛盾实时性与精度难以兼得。实时滤波如卡尔曼滤波能在线给出最优估计但其精度受限于当前及过去时刻的观测信息而事后处理虽然可以调用全部数据但若只是简单地对滤波结果进行平均或拟合又无法从本质上超越滤波器的性能极限。这就引出了一个关键问题如何利用一段完整数据轨迹的全部信息过去、现在、未来来对轨迹中任意一点的状态进行最优估计这就是“最优平滑算法”要解决的核心问题。它不是一个独立的滤波器而是一种基于已有滤波结果和全部量测数据的“后处理”或“再处理”技术。你可以把它理解为导航数据处理领域的“终极复盘”——当一场飞行或航行任务结束后我们拿到了从起飞到降落的完整惯性测量单元IMU数据和外部辅助观测数据如GNSS最优平滑算法能让我们回过头以“上帝视角”重新审视整个过程中的载体姿态、速度和位置其精度理论上将高于任何时刻的实时滤波结果。“平滑”之所以吸引人是因为它在许多高精度事后处理场景中不可或缺。例如航空测绘、重力测量、组合导航系统的事后精度评定、测试轨迹的高精度重建乃至为其他传感器如激光雷达、相机提供事后优化后的高精度位姿基准。它挖掘了数据中尚未被实时利用的“未来信息”的价值。本次我们就深入拆解捷联惯导系统中最优平滑算法的原理、主流实现方案并分享从理论推导到代码实现的实战经验与避坑指南。2. 核心思路解析前向滤波与后向平滑的融合之道最优平滑算法的核心思想并不复杂但实现起来需要清晰的逻辑。其基本前提是我们已经完成了一次前向的从初始时刻到最终时刻卡尔曼滤波过程。这个滤波过程为我们保存了两类关键数据一是各个时刻的状态估计值及其误差协方差阵即滤波结果二是滤波过程中计算出的中间量如状态预测值、预测协方差、滤波增益等。平滑算法的目标是利用从平滑时刻之后的所有未来观测信息来修正该时刻的滤波估计值。主流的最优平滑算法主要有三种固定点平滑、固定滞后平滑和固定区间平滑。在捷联惯导的事后处理中固定区间平滑应用最为广泛因为它旨在对整个时间区间内所有时刻的状态进行平滑典型代表就是Rauch-Tung-Striebel (RTS) 平滑器。2.1 RTS平滑器的运作机理RTS平滑器是一种“前向滤波后向平滑”的两遍算法。它的流程可以清晰地分为两个阶段第一阶段标准前向卡尔曼滤波这一阶段就是常规的捷联惯导/GNSS组合卡尔曼滤波过程。我们从初始时刻k0开始随着时间推进依次执行预测和更新步骤直到终点时刻kN。在这个过程中我们必须完整地保存每一个时间步k的以下数据x_k_k基于到k时刻为止所有观测信息得到的状态估计滤波结果。P_k_k上述状态估计对应的误差协方差矩阵。x_k_k-1从k-1时刻预测到k时刻的状态预测值。P_k_k-1状态预测值的误差协方差矩阵。 这些数据是后续平滑的基础缺少任何一项都无法进行RTS平滑。第二阶段后向递归平滑这是平滑算法的精髓。我们从终点时刻N开始倒着向初始时刻0递归计算。初始条件就是前向滤波的最终结果平滑值x_N^s x_N_N平滑误差协方差P_N^s P_N_N。 对于任意时刻k (从 N-1 到 0)RTS平滑的递归公式为平滑增益矩阵C_k:C_k P_k_k * F_k^T * (P_{k1}_k)^{-1}这里F_k是系统从k时刻到k1时刻的状态转移矩阵。这个增益决定了如何用k1时刻的平滑信息来修正k时刻的滤波信息。平滑状态估计x_k^s:x_k^s x_k_k C_k * (x_{k1}^s - x_{k1}_k)该公式直观地解释了平滑的本质k时刻的平滑值等于该时刻的滤波值加上一个修正项。修正项是平滑增益乘以“k1时刻平滑值与预测值之差”。这个差值包含了k1时刻之后所有未来观测信息带来的改进通过增益矩阵C_k和状态转移关系F_k回溯影响到k时刻。平滑误差协方差P_k^s:P_k^s P_k_k C_k * (P_{k1}^s - P_{k1}_k) * C_k^T它描述了平滑后状态估计的不确定度。一个重要的结论是P_k^s P_k_k即平滑误差协方差阵不大于滤波误差协方差阵这从理论上证明了平滑精度不低于滤波精度。注意后向平滑阶段本身不涉及重新处理观测数据z_k。它完全是在操作前向滤波阶段保存下来的状态估计序列{x_k_k, P_k_k, x_{k1}_k, P_{k1}_k}。因此前向滤波的准确性是平滑结果的基础。2.2 为何选择RTS平滑与其他方案的对比除了RTS还有基于两滤波器或最大似然估计的平滑方法。选择RTS的主要原因在于其计算效率和实现简洁性。计算效率RTS平滑只需要在完成前向滤波后进行一次额外的后向递归遍历计算量约为O(N * n^3)n为状态维数对于事后处理是可接受的。实现简洁其公式清晰编程实现容易只需在标准卡尔曼滤波代码基础上增加数据存储和后向递归循环即可。数值稳定性通过精心处理矩阵求逆如使用Cholesky分解或直接求解线性系统和协方差矩阵的对称性保持RTS算法可以做得非常稳定。相比之下其他方法可能涉及更复杂的推导或更大的计算负担。因此在工程实践中RTS固定区间平滑器是捷联惯导最优平滑的“标配”选择。3. 关键实现细节与实战陷阱剖析理解了原理下一步就是实现。这里有几个细节处理不好轻则结果不佳重则算法发散。3.1 前向滤波数据的存储策略这是实现平滑的第一个挑战。对于长时间、高频率的导航任务例如2小时、100Hz的数据状态向量维度可能是15维以上位置、速度、姿态误差、陀螺/加速度计零偏等加上协方差矩阵数据量非常庞大。方案对比与选择全内存存储最直接的方法。为x_k_k,P_k_k,x_k_k-1,P_k_k-1分别开辟N1个数组。对于P矩阵由于是对称阵可以只存储上三角或下三角部分以节省空间。此方案访问速度最快但内存消耗大。例如状态维数n16协方差矩阵存储为完整矩阵双精度8字节数据点N7200002小时*100Hz则总内存需求约为(n n*n) * 8字节 * N * 2滤波和平滑两组≈ (16256)*8*720000*2 ≈ 3.1 GB。这在许多嵌入式或资源受限的后处理环境中可能无法承受。缓存到文件将前向滤波的中间结果实时写入文件如二进制文件或高效的HDF5格式。后向平滑时再从文件尾部反向读取。这解决了内存问题但引入了大量的I/O操作可能显著降低处理速度。折中方案——滑动窗口与稀疏存储对于超长数据可以采用“分段平滑”或“滑动窗口平滑”。例如将总数据分成重叠的段落对每段进行RTS平滑再拼接。此外协方差矩阵P在滤波稳定后往往呈现特定的稀疏结构例如不同状态量之间的相关性较弱可以考虑使用压缩存储格式。实操建议对于中等规模数据如N50万优先采用全内存存储并优化P矩阵的存储为向量化的上三角部分。这需要在滤波和平滑的每个步骤中编写专门的矩阵-向量运算函数来处理这种压缩格式的P矩阵。虽然增加了编码复杂度但能极大节约内存。一个16维状态的协方差矩阵完整存储需256个元素只存上三角则需136个元素节省了近47%的空间。3.2 平滑增益矩阵C_k的计算与求逆稳定性公式C_k P_k_k * F_k^T * (P_{k1}_k)^{-1}中涉及对预测协方差矩阵P_{k1}_k的求逆。这是一个潜在的风险点。风险在滤波过程中由于数值舍入误差理论上应保持正定对称的P_{k1}_k矩阵可能失去正定性导致求逆失败或结果异常。解决方案绝对避免直接调用inv()函数。应采用更稳健的数值方法Cholesky分解法由于P_{k1}_k应是正定对称阵可以对其进行Cholesky分解P L * L^T其中L是下三角矩阵。那么求P^{-1} * b这里b F_k * P_k_k的问题就转化为求解两个三角线性系统先解L * y b再解L^T * x y则x P^{-1} * b。这种方法速度快、数值稳定。直接求解线性系统将计算C_k看作是求解线性矩阵方程P_{k1}_k * C_k^T (P_k_k * F_k^T)^T。可以利用高效的线性系统求解器如基于LU分解的求解器来解出C_k^T再转置。许多数值计算库如Eigen, NumPy的solve()函数内部会采用最优分解方式比显式求逆更稳定。代码片段示意 (Python/NumPy风格)# 假设 P_k_k, F_k, P_kp1_k 均已定义 # 计算中间量A P_k_k.dot(F_k.T) A P_k_k F_k.T # 采用求解线性系统的方式计算 C_k^T # 解方程P_kp1_k * X A.T C_T np.linalg.solve(P_kp1_k, A.T) # 使用solve而非inv C_k C_T.T这段代码中np.linalg.solve会自动选择稳定的算法来求解是更安全的选择。3.3 状态转移矩阵F_k的准确获取在捷联惯导误差模型中状态转移矩阵F_k通常是时变的它与载体当时的姿态、角速度、比力有关。在前向滤波时每一步都会计算当前的F_k用于预测步骤。关键点为了后向平滑必须在进行前向滤波的同时将每一步计算出的F_k矩阵也保存下来。因为后向递归公式中需要用到它。丢失了F_k序列平滑将无法进行。实操心得F_k的维度是n x n存储它也会占用大量内存。可以分析其结构它通常包含大量常数0、1和时变元素。如果内存紧张可以考虑只存储生成F_k所需的关键时变参数如姿态四元数、比力测量值在后向平滑时根据这些参数重新实时计算F_k。但这会以增加计算量为代价换取内存节省。4. 完整算法实现流程与代码框架下面我们以一个简化的SINS/GNSS紧组合模型为例勾勒出包含RTS平滑的完整算法框架。假设状态向量包含位置、速度、姿态误差、陀螺零偏、加表零偏。4.1 前向滤波与数据存储import numpy as np from scipy.linalg import cholesky, solve_triangular class SINSGNSSFilter: def __init__(self, initial_state, initial_covariance): self.x initial_state # 状态向量 self.P initial_covariance # 误差协方差矩阵 self.n len(initial_state) # 为平滑准备存储容器 self.states_filtered [] # 存储 x_k_k self.covs_filtered [] # 存储 P_k_k (向量化上三角) self.states_predicted [] # 存储 x_k1_k self.covs_predicted [] # 存储 P_k1_k (向量化上三角) self.F_matrices [] # 存储 F_k def save_for_smoothing(self, x_filt, P_filt, x_pred, P_pred, F): 保存当前时刻的滤波、预测结果和状态转移矩阵 self.states_filtered.append(x_filt.copy()) # 将对称矩阵P_filt存储为上三角向量 self.covs_filtered.append(self._matrix_to_upper_tri_vec(P_filt)) self.states_predicted.append(x_pred.copy()) self.covs_predicted.append(self._matrix_to_upper_tri_vec(P_pred)) self.F_matrices.append(F.copy()) def _matrix_to_upper_tri_vec(self, mat): 提取对称矩阵的上三角部分包含对角线并向量化 n mat.shape[0] indices np.triu_indices(n) return mat[indices] def _upper_tri_vec_to_matrix(self, vec): 将向量化的上三角部分恢复为对称矩阵 n int(np.sqrt(2 * len(vec) 0.25) - 0.5) # 从向量长度反推矩阵维度 mat np.zeros((n, n)) indices np.triu_indices(n) mat[indices] vec # 复制上三角到下三角以保持对称 mat mat mat.T - np.diag(np.diag(mat)) return mat def forward_filter(self, imu_data, gnss_data): 前向滤波主循环伪代码框架 for k in range(len(imu_data)): # 1. 系统传播基于IMU F_k self._compute_state_transition_matrix(imu_data[k]) # ... 执行卡尔曼滤波预测步骤得到 x_pred, P_pred ... x_pred, P_pred self._predict(F_k, ...) # 2. 量测更新基于GNSS if gnss_data_available_at_k: # ... 执行卡尔曼滤波更新步骤得到 x_filt, P_filt ... x_filt, P_filt self._update(x_pred, P_pred, gnss_data[k], ...) else: x_filt, x_pred.copy() P_filt P_pred.copy() # 3. 保存当前时刻数据以备平滑 self.save_for_smoothing(x_filt, P_filt, x_pred, P_pred, F_k) # 4. 为下一时刻准备 self.x x_filt self.P P_filt这个框架的关键在于save_for_smoothing方法它确保了所有必要的历史数据都被保留了下来。4.2 后向RTS平滑实现def rts_smoother(self): 执行RTS固定区间平滑 N len(self.states_filtered) - 1 # 时间终点索引 # 初始化平滑结果容器 states_smoothed [None] * (N 1) covs_smoothed [None] * (N 1) # 终点条件平滑值等于滤波值 states_smoothed[N] self.states_filtered[N].copy() covs_smoothed[N] self.covs_filtered[N].copy() # 后向递归 for k in range(N-1, -1, -1): # 从 N-1 到 0 # 从向量恢复矩阵 P_k_k self._upper_tri_vec_to_matrix(self.covs_filtered[k]) P_kp1_k self._upper_tri_vec_to_matrix(self.covs_predicted[k]) # 注意索引对应关系 F_k self.F_matrices[k] # 计算平滑增益 C_k # C_k P_k_k * F_k^T * (P_kp1_k)^{-1} # 采用解线性系统的方法解 P_kp1_k * X (P_k_k * F_k^T).T A P_k_k F_k.T # 使用Cholesky分解求解更稳定 L cholesky(P_kp1_k, lowerTrue) # P_kp1_k L * L^T # 解 L * y A.T y solve_triangular(L, A.T, lowerTrue) # 解 L^T * X y C_T solve_triangular(L.T, y, lowerFalse) C_k C_T.T # 计算平滑状态 # x_k^s x_k_k C_k (x_{k1}^s - x_{k1}_k) x_k_k self.states_filtered[k] x_kp1_s states_smoothed[k1] x_kp1_k self.states_predicted[k] states_smoothed[k] x_k_k C_k (x_kp1_s - x_kp1_k) # 计算平滑误差协方差 # P_k^s P_k_k C_k (P_{k1}^s - P_{k1}_k) C_k^T P_kp1_s self._upper_tri_vec_to_matrix(covs_smoothed[k1]) P_correction P_kp1_s - P_kp1_k cov_update C_k P_correction C_k.T P_k_s P_k_k cov_update # 确保对称性 P_k_s 0.5 * (P_k_s P_k_s.T) covs_smoothed[k] self._matrix_to_upper_tri_vec(P_k_s) return states_smoothed, covs_smoothed后向递归是平滑的核心。注意循环是倒序的并且每一步都严格依赖k1时刻的平滑结果。使用Cholesky分解求解增益矩阵C_k是保证数值稳定的关键步骤。5. 典型问题排查与性能优化经验在实际应用中即使算法实现正确也可能遇到各种问题。以下是一些常见坑点及其解决方案。5.1 平滑结果反而比滤波结果差这是最令人困惑的问题之一。可能的原因有前向滤波本身不佳或已发散平滑无法挽救一个糟糕的滤波基础。如果前向滤波由于模型错误、噪声设置不当或异常值处理不好而导致估计误差很大平滑只是在“优化”一个错误的基础。务必首先确保前向滤波结果本身是合理、收敛的。数据存储或索引错误这是最常见的编程错误。确保在保存和读取x_k_k,x_{k1}_k,P_k_k,P_{k1}_k,F_k时时间索引k完全对应。一个有效的调试方法是对一段非常短的数据如10个点进行手动计算并与程序输出逐行对比。状态转移矩阵F_k不匹配后向平滑使用的F_k必须与前向滤波计算该矩阵时使用的状态/IMU数据完全一致。如果滤波时F_k是时变且依赖于x_k_k在误差方程线性化点那么保存的F_k序列必须准确。如果滤波使用的是基于标称轨迹的F_k即忽略状态反馈则一致性更容易保证。数值问题特别是P_{k1}_k矩阵失去正定性导致求逆或Cholesky分解失败。除了使用更稳定的求解器还可以在滤波步骤中加入协方差矩阵的对称化和正则化如添加一个极小的单位矩阵倍数epsilon * I来强制其正定。5.2 内存与计算速度瓶颈对于超长航时数据分段平滑将整个时间轴分成若干有重叠的段落。对每段独立进行“前向滤波后向平滑”在重叠区对结果进行加权融合。这能有效控制单次处理的数据量。降低存储精度在精度允许的情况下将双精度浮点数float64改为单精度float32内存占用和计算量几乎减半。并行化如果采用分段平滑各段之间的处理是独立的可以很容易地利用多核CPU进行并行计算大幅提升整体处理速度。使用高效数值库如NumPy、EigenC、CuPyGPU等利用其高度优化的矩阵运算。5.3 如何验证平滑算法的正确性蒙特卡洛仿真这是最可靠的方法。生成一条已知真实状态的轨迹和对应的IMU/GNSS仿真数据。分别运行前向滤波和包含平滑的后处理比较滤波误差和平滑误差的统计特性如RMS。理论上平滑误差的RMS应小于等于滤波误差。一致性检查对于RTS平滑平滑后的状态序列应满足系统动力学方程。你可以用平滑后的状态x_k^s和保存的F_k计算x_k^s与F_k * x_{k-1}^s的差异这个差异应该很小在数值误差范围内。协方差检查检查平滑误差协方差P_k^s是否始终小于等于滤波误差协方差P_k_k在矩阵正定意义下。同时可以计算平滑新息序列理论上也应为零均值白噪声。5.4 平滑算法对初始条件敏感吗RTS平滑的递归从终点N开始其初始值就是前向滤波的终点估计x_N_N。因此平滑结果对终点的滤波精度是敏感的。如果滤波在终点附近由于观测质量差等原因导致估计变差这个误差会通过后向递归影响到前面时刻的平滑结果尤其是靠近终点的时间点。为了减轻这种影响可以确保在轨迹的终点也有良好的观测数据如GNSS信号。另一种思路是使用“前向-后向”平滑即先从头到尾滤波再从尾到头平滑然后再从头到尾用平滑后的值作为初始条件进行一次滤波迭代几次但这种方法计算量较大。6. 拓展应用更复杂的模型与融合框架基础的RTS平滑适用于线性高斯系统。而实际的捷联惯导系统是非线性的。如何处理非线性扩展卡尔曼滤波平滑最直接的方法。前向滤波使用EKF保存线性化后的状态转移矩阵F_k和误差协方差。后向平滑仍然使用标准的RTS线性公式只是其中的F_k是EKF线性化得到的雅可比矩阵。这是工程中最常用的方法。无迹卡尔曼滤波平滑前向滤波使用UKF。后向平滑面临挑战因为UKF没有显式的F_k矩阵。一种近似方法是使用UKF滤波过程中产生的sigma点来近似计算状态转移的统计特性从而推导出一个等效的平滑增益。另一种思路是采用基于采样的平滑算法。图优化与因子图这是现代SLAM和导航领域的主流后优化方法。它将所有状态位置、姿态等和观测IMU预积分、GNSS位置等建模为一个概率图通过优化整个图的代价函数来一次性得到所有状态的最优估计。这种方法本质上是一种批处理的最大后验估计其平滑效果通常优于基于EKF的两遍RTS平滑尤其对于非线性强、闭环回环的场景但计算复杂度也更高。在资源允许的情况下对于事后处理将基于IMU预积分的因子图优化作为平滑工具正在成为高精度轨迹重建的新趋势。它能够更自然地处理非线性并且方便地融合多种异构观测数据。

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

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

免费获取报价