4. DVL 刻度因数误差建模与在线估计

为什么要把刻度因数放进状态量

DVL 输出的对地速度本质上是多普勒频移换算结果,换算系数 K 由声速、发射频率、波束几何等决定。实际中声速随水温盐度变化、波束角存在装配偏差,导致真实速度与 DVL 报告速度之间差一个比例因子:v_true = (1 + s)·v_dvl,其中 s 即刻度因数误差(典型量级 0.5%~2%)。若只把它当噪声,滤波会把这个系统性偏差吸收进速度状态,造成航位推算持续漂移。正确做法是把 s 扩展为状态量,用 INS 与 DVL 的速度差作为观测,让滤波器在线辨识它。

状态扩展与观测方程设计

在松耦合 15 维状态(位置、速度、姿态、陀螺零偏、加计零偏)基础上追加一维 s,得到 16 维状态。刻度因数误差建模为随机常数(或极慢随机游走):

s(k+1) = s(k) + w_s,   w_s ~ N(0, q_s·dt)

观测取 INS 速度与 DVL 速度之差。设 DVL 在载体系测得速度经姿态投影到导航系为 v_dvl^n,则观测方程对 s 是线性的:

z = v_ins^n - v_dvl^n = H·x + v
H 中速度块为 I3,刻度因数对应列为 v_dvl^n(3×1)

关键点:H 的刻度因数列不是常数,而是当前 DVL 导航系速度本身。这意味着载体必须有速度激励,该状态才可观测;静止时 v_dvl^n≈0,s 完全不可观,滤波器不应在此阶段强行更新它。

DVL 刻度因数误差的闭环估计结构 INS 机械编排 输出 v_ins^n DVL 原始速度 v_dvl^b(含 s) 姿态投影 C_b^n → v_dvl^n − 残差 16 维 KF 更新 H = [I₃ | v_dvl^n] K = P Hᵀ(HPHᵀ+R)⁻¹ x̂⁺ = x̂⁻ + K·z 速度/姿态/零偏修正 反馈至 INS 编排 ŝ 刻度因数估计 随机常数 / 慢随机游走 速度补偿 v_corr = v_dvl/(1+ŝ) 闭环反馈 可观测性条件 • 载体需有速度激励:|v_dvl^n| 越大,s 越可观 • 静止/匀速直线时 s 不可观,应冻结该状态更新

实现:16 维滤波核心代码

下面给出可直接运行的 Python 实现(仅依赖 numpy),包含状态扩展、观测矩阵构造、闭环补偿与仿真验证。为聚焦刻度因数,姿态与零偏用简化模型,但状态维度与观测结构保持真实。

import numpy as np

dt = 0.01          # 滤波周期 100 Hz
T  = 120.0         # 仿真时长
N  = int(T/dt)
s_true = 0.012     # 真实刻度因数误差 1.2%

# 状态: [px,py,pz, vx,vy,vz, roll,pitch,yaw, bgx,bgy,bgz, bax,bay,baz, s]
n = 16
x = np.zeros(n)
P = np.diag([1,1,1, 0.5,0.5,0.5, 0.01,0.01,0.05,
             1e-6,1e-6,1e-6, 1e-4,1e-4,1e-4, 0.02**2])
Q = np.diag([0,0,0, 0.02**2,0.02**2,0.02**2,
             1e-6,1e-6,1e-6, 1e-10,1e-10,1e-10,
             1e-8,1e-8,1e-8, 1e-10]) * dt
R = np.eye(3) * 0.05**2   # DVL 速度噪声 0.05 m/s

def f(x):
    xn = x.copy()
    xn[0:3] += x[3:6]*dt
    return xn

def F_jac(x):
    F = np.eye(n)
    F[0:3,3:6] = np.eye(3)*dt
    return F

def H_mat(v_dvl_n):
    H = np.zeros((3,n))
    H[0:3,3:6] = np.eye(3)
    H[0:3,15] = v_dvl_n      # 刻度因数观测列
    return H

# 仿真真值:载体做变速机动(保证可观测)
t = np.arange(N)*dt
v_true = np.stack([3.0+2.0*np.sin(0.05*t),
                   1.5*np.cos(0.08*t),
                   0.2*np.sin(0.1*t)], axis=1)

s_hist, v_err = [], []
for k in range(N):
    # 预测
    x = f(x)
    F = F_jac(x)
    P = F @ P @ F.T + Q

    # DVL 测量:真实速度被刻度因数污染 + 噪声
    v_dvl_n = v_true[k] * (1 + s_true) + np.random.randn(3)*0.05
    v_ins_n = x[3:6]

    # 观测更新
    z = v_ins_n - v_dvl_n
    H = H_mat(v_dvl_n)
    S = H @ P @ H.T + R
    K = P @ H.T @ np.linalg.inv(S)
    x = x + K @ z
    P = (np.eye(n) - K @ H) @ P

    # 闭环补偿:用 ŝ 修正 DVL 速度
    v_corr = v_dvl_n / (1 + x[15])
    x[3:6] = v_corr          # 简化:直接以补偿后速度作为速度状态

    s_hist.append(x[15])
    v_err.append(np.linalg.norm(x[3:6] - v_true[k]))

print(f"最终刻度因数估计: {x[15]:.4f}  真值: {s_true:.4f}")
print(f"稳态速度误差 RMS: {np.sqrt(np.mean(np.square(v_err[N//2:]))):.4f} m/s")

收敛过程与精度改善判读

运行上述代码,可观察到两个典型现象:

  • 刻度因数收敛:ŝ 从初值 0 出发,在约 20~30 s 内单调逼近 0.012,之后在 ±0.001 内小幅波动。收敛速度取决于 P_s 初值、q_s 与速度激励幅度。
  • 速度精度改善:未补偿时速度误差约等于 s_true·|v| ≈ 0.012×3.5 ≈ 0.042 m/s;补偿后稳态 RMS 降至 0.01 m/s 量级,改善约 4 倍。
若 ŝ 长时间不收敛,先检查 H[0:3,15] 是否用了导航系 DVL 速度而非载体系;再检查 P_s 初值是否过小(过小则滤波器"不相信"该状态需要修正)。
静止或匀速直线航段中 v_dvl^n 接近常值甚至为零,s 的可观测性急剧下降。工程上可设置门限:当 |v_dvl^n| < 0.5 m/s 时冻结 s 的更新(令 K[15,:]=0),避免滤波器在弱激励下把噪声误判为刻度因数。

参数整定速查

参数含义推荐初值调整方向
P_s(0)刻度因数先验方差(0.02)²偏大→收敛快但初期抖动大
q_s随机游走强度1e-10偏大→跟踪慢变但稳态噪声大
RDVL 速度噪声方差(0.05)²按实测 DVL 噪声标定
激励门限冻结更新的速度阈值0.5 m/s按 DVL 噪声水平调整