基于ESKF的IMU姿态融合【附MATLAB代码】

2023-05-16

目录

  • 0 前言
  • 1 什么是ESKF
  • 2 系统方程
    • 2.1 状态变量
    • 2.2 imu的测量值
    • 2.3 预测方程及雅克比矩阵
    • 2.4 测量方程及雅克比矩阵
  • 3 kalman filter loop计算
  • 4 Show me the code
  • 5 代码下载链接

0 前言

在很多工程应用里都需要获得物体的姿态信息,而通过imu估计姿态应该最广。
仅使用imu获取姿态信息有很多算法,本文讨论基于ESKF(Error-State Kalman Filter)的算法。
本文偏向于算法的工程应用,因此省去了公式推导,但是会列出必不可少的核心公式。

1 什么是ESKF

ESKF(Error-State Kalman Filter)看起来很吓唬人,其实它和一般的KF、EKF没有什么本质的区别,只是状态量的选取不同罢了。
以位姿估计为例,普通的kalman filter的状态量(名义状态量)一般都是位置p、速度v、四元数q,而ESKF的状态量(误差状态量)是上述状态量的误差,如位置的误差δp、速度误差δv、姿态误差δq,而kalman得5个核心公式是没有区别的。
为了方便阅读,kalman的核心公式扔在这里。
image.png

优点(不翻译了)
image.png

2 系统方程

2.1 状态变量

1)真实状态量:
x t = [ q t ω b t ] x_t= \begin{bmatrix} q_t \\ \omega_{bt} \end{bmatrix} xt=[qtωbt] q t q_t qt为真实四元数, ω b t \omega_{bt} ωbt为陀螺仪的真实bias。

2)状态量(名义状态量):
x = [ q ω b ] x= \begin{bmatrix} q \\ \omega_b \end{bmatrix} x=[qωb] q q q为四元数, ω b \omega_{b} ωb为陀螺仪的bias。

3)误差状态量:
δ x = [ δ q δ ω b ] \delta x= \begin{bmatrix} \delta q \\ \delta\omega_b \end{bmatrix} δx=[δqδωb] δ q \delta q δq δ ω b \delta\omega_b δωb分别真实状态量和名义状态量的误差。

那么他们之间有这样的关系:
x t = x ⊕ δ x = [ q × δ q ω b + δ ω b ] x_t = x\oplus\delta x= \begin{bmatrix} q\times \delta q \\ \omega_b + \delta\omega_b \end{bmatrix} xt=xδx=[q×δqωb+δωb]
其中, δ q \delta q δq δ θ \delta\theta δθ的关系为: δ q = e δ θ / 2 \delta q=e^{\delta\theta/2} δq=eδθ/2,因为有

q ≜ E x p ( ϕ u ) = e ϕ u / 2 = c o s ( ϕ / 2 ) + u s i n ( ϕ / 2 ) = [ c o s ( ϕ / 2 ) u s i n ( ϕ / 2 ) ] q\triangleq Exp(\phi \boldsymbol{u}) =e^{\phi \boldsymbol{u}/2} = cos (\phi/2) + \boldsymbol{u}sin(\phi/2) = \begin{bmatrix} cos(\phi/2) \\ \boldsymbol{u}sin(\phi/2) \end{bmatrix} qExp(ϕu)=eϕu/2=cos(ϕ/2)+usin(ϕ/2)=[cos(ϕ/2)usin(ϕ/2)]

也就是常用的轴角公式,由于 δ θ \delta\theta δθ只需要3个变量表示,所以误差状态量又可以表示为更简洁的形式:
δ x = [ δ θ δ ω b ] \delta x= \begin{bmatrix} \delta \theta \\ \delta\omega_b \end{bmatrix} δx=[δθδωb]

2.2 imu的测量值

1)加速度计
a t = a m + a n a_t=a_m+a_n at=am+an
其中 a t a_t at为真值, a m a_m am为测量值, a n a_n an为测量噪声。

2)陀螺仪
ω t = ω m + ω n \omega_t = \omega_m + \omega_n ωt=ωm+ωn
其中 ω t \omega_t ωt为真值, ω m \omega_m ωm为测量值, ω n \omega_n ωn为测量噪声。

2.3 预测方程及雅克比矩阵

这里我们是用陀螺仪的数据对姿态进行预测。
对于名义状态量,有:
{ q ˙ = 1 2 q ⊗ ( ω m − ω b ) ω ˙ b = 0 \left \{ \begin{array}{ll} \dot{q} = \frac{1}{2}q \otimes(\omega_m - \omega_b) \\ \dot{\omega}_b = 0 \end{array} \right. {q˙=21q(ωmωb)ω˙b=0

对于误差状态量,有:
{ δ θ ˙ = − [ ω m − ω b ] × δ θ − δ ω b − ω n δ ω b ˙ = ω ω \left \{ \begin{array}{ll} \dot{\delta \theta} = -[\omega_m - \omega_b]_{\times}{\delta \theta} - \delta{\omega_b} - \omega_n \\ \dot{\delta \omega _b} = \omega_{\omega} \\ \end{array} \right. {δθ˙=[ωmωb]×δθδωbωnδωb˙=ωω
上式中,
ω n \omega_n ωn为陀螺仪数据噪声, ω ω \omega_{\omega} ωω为陀螺仪零偏的噪声(仔细品味一下,象征陀螺仪零偏的稳定性); [ ∙ ] × [\bullet]_{\times} []×为反对称矩阵。

这里,我们把误差状态的系统方程的一般形式表示为:
δ x ← f ( x , δ x , u m , i ) = F x ( x , u m ) ⋅ δ x + F i ⋅ i \delta x \gets f(x,\delta x, u_m, i) = F_x(x, u_m) \cdot \delta x + F_i \cdot i δxf(x,δx,um,i)=Fx(x,um)δx+Fii
上式中, u m u_m um为系统的输入, i i i为噪声。

那么其预测方程可以写为:
δ x ^ ← F x ( x , u m ) ⋅ δ x ^ \hat{\delta x} \gets F_x(x, u_m) \cdot \hat{\delta x} δx^Fx(x,um)δx^
P ← F x P F x T + F i Q i F i T P \gets F_xPF^T_x + F_iQ_iF^T_i PFxPFxT+FiQiFiT
其中:
F x = ∂ f ∂ δ x ∣ x , u m = [ R T { ( ω m − ω b ) Δ t } − I Δ t 0 I ] F_x = \frac{\partial f}{\partial \delta x} \lvert_{x,u_m} = \begin{bmatrix} R^T\{{(\omega_m-\omega_b)\Delta t}\} & -I\Delta t \\ 0 & I \end{bmatrix} Fx=δxfx,um=[RT{(ωmωb)Δt}0IΔtI]

上式中, R { u } = I + s i n ϕ [ u ] × + ( 1 − c o s ϕ ) [ u ] × 2 R\{\boldsymbol{u}\} = I + sin \phi[\boldsymbol{u}]_{\times} + (1-cos \phi)[\boldsymbol{u}]^2_{\times} R{u}=I+sinϕ[u]×+(1cosϕ)[u]×2,有 [ u ] × 2 = u u T − I [\boldsymbol{u}]^2_{\times} = \boldsymbol{u}\boldsymbol{u}^T-I [u]×2=uuTI
F i = ∂ f ∂ i ∣ x , u m = [ I 0 0 I ] F_i = \frac{\partial f}{\partial i} \lvert_{x,u_m}= \begin{bmatrix} I & 0 \\ 0&I \end{bmatrix} Fi=ifx,um=[I00I]

Q i = [ σ ω n 2 Δ t 2 I 0 0 σ ω ω 2 Δ t I ] Q_i = \begin{bmatrix} \sigma^2_{\omega_n}\Delta t^2 I & 0 \\ 0 & \sigma^2_{\omega_{\omega}}\Delta t I \end{bmatrix} Qi=[σωn2Δt2I00σωω2ΔtI]

需要注意的是, δ x \delta x δx不需要做预测,因为每次初始化 δ x \delta x δx都为0,所以预测值肯定也是0。
有人会问,它会一直是0吗,经过几次运算迭代一直是0?
如果有这样的疑问,那就说明还没有从常规kalman filter的模式中走出来。ESKF每次迭代后,都会将最优估计得到的 δ x \delta x δx叠加到 x x x中,所以对于下一时刻的开始,认为 x x x是没有误差的,也就是认为 δ x \delta x δx为0了。

总结,在kalman预测这一步,每一轮新的kalman迭代,需要做的事情是:
1)对名义状态量进行预测;
3)计算雅克比矩阵
2)对误差状态量进行预测;( δ x \delta x δx的值是0,可以不计算,但是不能认为它没有意义)

2.4 测量方程及雅克比矩阵

观测方程可以表示为下面的形式
y = h ( x t ) + v y=h(x_t)+v y=h(xt)+v
这里使用imu的加速度进行姿态的校正,假设的前提是没有机动加速度,只有重力加速度,所以有
y = a m = − R t T g + a n y=a_m=-R^T_t \boldsymbol{g} + a_n y=am=RtTg+an,其中 R t T R_t^T RtT是通过 q t q_t qt得到的旋转矩阵的转置。
image.png

带入 R T R^T RT,可以把上式化简为:
y = h ( x t ) + v = − R t T g + a n = − R t T [ 0 0 g ] + a n = [ 2 ( q x q z − q w q y ) 2 ( q y q z + q w q x ) q w 2 − q x 2 − q y 2 + q z 2 ] ( − g ) + a n \begin{aligned} y & =h(x_t)+v \\ & = -R^T_t \boldsymbol{g} + a_n \\ & = -R^T_t \begin{bmatrix} 0\\0\\g \end{bmatrix}+ a_n =\begin{bmatrix} 2(q_xq_z-q_wq_y) \\ 2(q_yq_z+q_wqx) \\ q^2_w -q^2_x-q^2_y+q^2_z \end{bmatrix} (-g) + a_n \end{aligned} y=h(xt)+v=RtTg+an=RtT00g+an=2(qxqzqwqy)2(qyqz+qwqx)qw2qx2qy2+qz2(g)+an

然后将 h ( x t ) h(x_t) h(xt) δ x \delta x δx求偏导,即
H ≜ ∂ h ∂ δ x ∣ x = ∂ h ∂ x t ∣ x ∂ x t ∂ δ x ∣ x = H x X δ x H \triangleq \frac{\partial h}{\partial \delta x}|_x = \frac{\partial h}{\partial x_t}|_x \frac{\partial x_t}{\partial \delta x}|_x = H_x X_{\delta x} Hδxhx=xthxδxxtx=HxXδx

H x = ∂ h ∂ δ x ∣ x = [ − 2 q y 2 q z − 2 q w 2 q x 2 q x 2 q w 2 q z 2 q y 2 q w − 2 q x − 2 q y 2 q z ] ( − g ) H_x = \frac{\partial h}{\partial \delta x}|_x = \begin{bmatrix} -2q_y & 2q_z & -2q_w & 2q_x \\ 2q_x & 2q_w & 2q_z & 2q_y \\ 2q_w & -2q_x & -2q_y & 2q_z \end{bmatrix} (-g) Hx=δxhx=2qy2qx2qw2qz2qw2qx2qw2qz2qy2qx2qy2qz(g)

X δ x = [ ∂ ( q ⊗ δ q ) ∂ δ θ 0 0 ∂ ( ω b + δ ω b ) ∂ δ ω b ] = [ Q δ θ 0 0 I ] X_{\delta x} = \begin{bmatrix} \frac{\partial(q \otimes \delta q)}{\partial \delta \theta} & 0 \\ 0 & \frac{\partial(\omega_b + \delta \omega _b)}{\partial \delta \omega _b} \end{bmatrix} = \begin{bmatrix} Q_{\delta_\theta} & 0 \\ 0 & I \end{bmatrix} Xδx=[δθ(qδq)00δωb(ωb+δωb)]=[Qδθ00I]

Q δ θ = 1 2 [ − q x − q y − q z q w − q z q y q z q w − q x − q y q x q w ] Q_{\delta \theta} = \frac{1}{2} \begin{bmatrix} -q_x & -q_y & -q_z \\ q_w & -q_z &q_y \\ q_z & q_w & -q_x \\ -q_y & q_x & q_w \end{bmatrix} Qδθ=21qxqwqzqyqyqzqwqxqzqyqxqw

3 kalman filter loop计算

上面的公式太多了,看得人头皮发麻,那就赶紧进入算法实践环节,接下来就可以按照Kalman Filter的“套路”计算了,大致的流程如下。

image.png

上图中的每一个公式或者变量都可以在前文中找到定义和说明,你要做的就是按照上述的流程进行计算就行。

4 Show me the code

有人说,没有代码代码,还是太抽象,好吧,这里奉上MATLAB脚本和仿真数据。

代码文件的结构如下。
image.png
​仿真结果如下。
image.png

%% 使用IMU的数据,基于ESKF的方法估计姿态
clc;clear;close;

%% 添加路径,加载数据
addpath('ESKF')
load('imu_log_data.mat');
mean_dt = mean(diff(imu.t));

%% 调用算法,记录数据
eskf.t = imu.t;
[eskf.quat, eskf.gyb, eskf.cov] = eskf_imu(imu.t, imu.gyr, imu.acc);
[eskf.yaw, eskf.pitch, eskf.roll] = quat2angle(eskf.quat,'ZYX');

%% 画图
figure('Name', 'euler in degree');
h1 = subplot(3,1,1);
plot(eskf.t, rad2deg(eskf.roll), 'r');
grid minor;title('roll')

h2 = subplot(3,1,2);
plot(eskf.t, rad2deg(eskf.pitch), 'r');
grid minor;title('pitch')

h3 = subplot(3,1,3);
plot(eskf.t, rad2deg(eskf.yaw), 'r');
grid minor;title('yaw')
linkaxes([h1 h2 h3],'x');

function [state_quat, state_gyb, state_cov] = eskf_imu(t, gyr, acc)

%% 参数初始化
quat = quaternion(1,0,0,0);
init_quat_var = power(4, 2);
init_gyb_var = power(1e-3, 2);

% 初始化P矩阵
P(1:3,1:3) = init_quat_var * eye(3);
P(4:6,4:6) = init_gyb_var * eye(3);
acc_meas_noise_var = power(2, 2); % 加速度计噪声方差
R = eye(3) * acc_meas_noise_var;
gyr_noise = power(1e-2, 2);
gyr_drift_noise = power(1e-3, 2);
gyb = zeros(1,3);

%% 申请内存,可以加快仿真速度
len = length(gyr);
state_quat = zeros(len,4);
state_gyb = zeros(len,3);
state_cov = zeros(len,6);

%% 循环计算
for index = 1:len
    if (index == 1)
        dt = saturate_user(t(2) - t(1),0.001,0.05);
    else
        dt = saturate_user(t(index) - t(index-1),0.001,0.05);
    end
    
    %                ---- 预测 -----                 %
    % 预测姿态角
    d_angle = (gyr(index,:) - gyb) * dt;
    dq = quaternion(d_angle, 'rotvec'); % 将旋转向量转化为四元数
    new_quat = quat * dq;
    quat = new_quat.normalize;
    
    % 预测协方差
    P = ESKF_predict_P(dt, P, gyr_noise, gyr_drift_noise);
    
    %                ---- 更新 -----                 %
    y = ESKF_predict_acc(quat); %
    H = ESKF_jacH(quat);    
    S = H * P * H' + R;
    K = P * H' / S;
    P = P - K * H * P;
    innov = K * (acc(index,:) - y)';
    dq = quaternion(innov(1:3)', 'rotvec');
    new_quat = quat * dq;
    quat = new_quat.normalize;
    
    gyb = gyb + innov(4:6)';
    
    state_quat(index,:) = quat.compact;
    state_gyb(index,:) = gyb;
    state_cov(index,:) =  diag(P)';
end

end

function out = saturate_user(in, min, max)
if (in < min)
    out = min;
elseif (in > max)
    out = max;
else
    out = in;
end
end

function new_P = ESKF_predict_P(dt, P, gyro_noise, gyr_drift_noise)

Fx = eye(6);
Fx(1:3,4:6) = -eye(3) * dt;
Fi = eye(6);
Qi(1:3, 1:3) = eye(3) * gyro_noise * dt * dt;
Qi(4:6, 4:6) = eye(3) * gyr_drift_noise * dt;

new_P = Fx * P * transpose(Fx) + Fi * Qi * transpose(Fi);
end

function pred_meas = ESKF_predict_acc(quat)

[qw, qx, qy, qz] = parts(quat);
gravity = 9.80665;
pred_meas(1) = -gravity * 2 * (qx * qz - qw * qy);
pred_meas(2) = -gravity * 2 * (qw * qx + qy * qz);
pred_meas(3) = -gravity * (qw * qw - qx * qx - qy * qy + qz * qz);

end

function H = ESKF_jacH(quat)

[qw, qx, qy, qz] = parts(quat);

Hx = zeros(3,7);
Xdx = zeros(7,6);

Hx(1:3,1:4) = -9.80665 * 2 * [  -qy,  qz,  -qw,  qx;...
                            qx, qw,  qz,  qy;...
                             qw, -qx, -qy,  qz];
Xdx(1:4,1:3) = 0.5 * [-qx, -qy, -qz;...
                       qw, -qz, qy;...
                       qz, qw, -qx;...
                      -qy, qx, qw];
Xdx(5:7,4:6) = eye(3);              

H = Hx * Xdx;

end

如果还有什么不清楚,欢迎评论区交流。

5 代码下载链接

可以在这里下载。链接: 点击下载。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于ESKF的IMU姿态融合【附MATLAB代码】 的相关文章

随机推荐

  • openMP学习笔记之一 -- 杂记

    1 使用libffi启动执行 xff0c ffi全称Foreign Function Interface xff0c 参考https www cnblogs com findumars p 4882620 html的介绍 xff0c 2 在
  • Vue系列之—Vue-router详解

    目录 一 简介 1 1 单页面应用 1 2 路由管理器 二 起步 2 1 动态路由匹配 2 2 路由组件传参 2 3 嵌套路由 声明式 编程式导航 命名路由 命名视图 重定向和别名 三 进阶 导航守卫 全局的守卫 路由独享的守卫 一 简介
  • vscode 代码格式化及快捷键

    VSCode 代码格式化 快捷键 On Windows Shift 43 Alt 43 F On Mac Shift 43 Option 43 F On Ubuntu Ctrl 43 Shift 43 I 代码格式化 vscode默认启用了
  • ORB-SLAM2添加稠密建图线程

    注 xff1a 本篇文章只是对高翔博士稠密点云代码进行的简述 xff0c 内容主要包括的是在ORB SLAM2基础上怎么添加稠密建图线程 xff0c 并未对高翔博士代码进行改动 本文章仅用于自己学习记录 xff0c 若有侵权麻烦私聊联系删除
  • ubuntu挂载sd卡到分区目录+修改docker镜像存储位置

    ubuntu挂载sd卡到分区目录 43 修改docker镜像存储位置 一 挂载SD卡到 data 1 查看Linux硬盘信息 lsblk 或 fdisk l lsblk 新的硬盘 xff0c 最好删除之前的分区 xff0c 再新建分区 de
  • Ubuntu虚拟机安装EDA工具:VCS+Verdi+dve2018方法教程

    上个月刚完成Ubuntu虚拟机的安装 xff0c 本教程的基础是你已经安装好了Ubuntu的虚拟机 xff0c 最好是和笔者版本接近的Ubuntu xff0c 具体安装方法已在之前的文章中介绍过了 xff1a https blog csdn
  • 基于deepstream-test3添加跟踪插件和4类sinkType输出(包括rtsp)

    基于deepstream test3添加目标跟踪插件和4类sinkType输出 xff08 包括rtsp输入输出 xff09 deepstream C C 43 43 deepstream官方示例没有给出一个单管道的多类输入和4类sinkT
  • 国外学位论文查找

    转自 xff1a http blog chinaunix net uid 20517852 id 1936377 html 国外博士论文下载的网站 xff0c 不知道以前发过没有 http search ohiolink edu etd i
  • DirectShow播放器(LAVFilter + EVR)开发例子

    LAVFilter是一套著名的DirectShow插件 xff0c 包括Demux xff0c Video Decoder xff0c AudioDecoder xff0c 播放文件所需要的几个重要插件都包含进去了 xff0c 并且支持播放
  • 关于最新版的keil5不能正常调试或者调试过程自动停止的解决方法

    适用范围 1 在进入debug的功能中提示J link is defective xff0c 大概意思就是最新版的J LINK驱动跟正在用的硬件不匹配 xff0c 要你更换驱动或者更换硬件 xff08 其实是使用盗版的J LINK会出现的问
  • C++11变参模板的参数包

    Parameter pack 原文来自这里 A template parameter pack is a template parameter that accepts zero or more template arguments non
  • Linux socket 关闭场景

    测试环境 root 64 centos192 168 1 12 cat etc system release CentOS release 6 9 Final 工具 xff1a 服务器 192 168 1 12 ipython Python
  • QGroundControl地面站二次开发环境搭建(win+linux+android)

    更新时间 xff1a 2017 6 19 大家好 xff0c 我是learn xff0c 下面主要介绍一下QGroundControl地面站的环境搭建 网上也有好多教程 xff0c 我就不再麻烦了 xff0c 补充一下好了 http blo
  • std::vector用法

    vector 是C 43 43 标准模板库中的部分内容 xff0c 它是一个多功能的 xff0c 能够操作多种数据结构和算法的模板类和函数库 vector之所以被认为是一个容器 xff0c 是因为它能够像容器一样存放各种类型的对象 xff0
  • Linux串口(serial、uart)驱动程序设计

    一 核心数据结构 串口驱动有3个核心数据结构 xff0c 它们都定义在 lt include linux serial core h gt 1 uart driver uart driver包含了串口设备名 串口驱动名 主次设备号 串口控制
  • Xshell 5 评估过期,需要采购,不能使用的解决办法

    Xshell 5 当然 xff0c 现在我们可以直接撸 Xshell 6 了 卸载原来的 Xshell 5进入 Xshell 5 官网 xff1a https www netsarang com页面上点导航栏的 Free Licence x
  • mapreduce编程(一)-二次排序

    mr自带的例子中的源码SecondarySort xff0c 我重新写了一下 xff0c 基本没变 这个例子中定义的map和reduce如下 xff0c 关键是它对输入输出类型的定义 xff1a xff08 java泛型编程 xff09 p
  • Android apk执行shell脚本 工具类

    在做Android应用时 xff0c 经常需要执行shell脚本 xff0c 以快速实现某些功能 xff1b 在Android应用程序中执行shell脚本可以省去一大堆繁琐的代码 xff0c 还可以避免不必要的错误 xff1b 比如 xff
  • Python最强装逼神技!微信远程控制电脑,想让你电脑关机就关机!

    今天带给大家一个非常有意思的 python 程序 xff0c 基于 itchat 实现微信控制电脑 你可以通过在微信发送命令 xff0c 来拍摄当前电脑的使用者 xff0c 然后图片会发送到你的微信上 甚至你可以发送命令来远程关闭电脑 程序
  • 基于ESKF的IMU姿态融合【附MATLAB代码】

    目录 0 前言1 什么是ESKF2 系统方程2 1 状态变量2 2 imu的测量值2 3 预测方程及雅克比矩阵2 4 测量方程及雅克比矩阵 3 kalman filter loop计算4 Show me the code5 代码下载链接 0