SAD notes

2023-10-26 07:15
文章标签 notes sad

本文主要是介绍SAD notes,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ESKF 总结

prediction

更新误差先验
在这里插入图片描述

F F F通过3.42来算
在这里插入图片描述
得到
在这里插入图片描述
这里有点绕的一点是: 误差状态的 F F F牵涉到名义状态, 而名义状态又需要在时间上推进更新

其中, F中的名义状态的推进通过公式3.41得到,

(名义状态不考虑误差, 这一点从3.41d, 3.41e可以看出, 误差状态只考虑误差, 真实状态为名义+误差)

在这里插入图片描述代码的实现

template <typename S>
bool ESKF<S>::Predict(const IMU& imu) {assert(imu.timestamp_ >= current_time_);double dt = imu.timestamp_ - current_time_;if (dt > (5 * options_.imu_dt_) || dt < 0) {// 时间间隔不对,可能是第一个IMU数据,没有历史信息LOG(INFO) << "skip this imu because dt_ = " << dt;current_time_ = imu.timestamp_;return false;}// nominal state 递推VecT new_p = p_ + v_ * dt + 0.5 * (R_ * (imu.acce_ - ba_)) * dt * dt + 0.5 * g_ * dt * dt;VecT new_v = v_ + R_ * (imu.acce_ - ba_) * dt + g_ * dt;SO3 new_R = R_ * SO3::exp((imu.gyro_ - bg_) * dt);R_ = new_R;v_ = new_v;p_ = new_p;// 其余状态维度不变// error state 递推// 计算运动过程雅可比矩阵 F,见(3.47)// F实际上是稀疏矩阵,也可以不用矩阵形式进行相乘而是写成散装形式,这里为了教学方便,使用矩阵形式Mat18T F = Mat18T::Identity();                                                 // 主对角线F.template block<3, 3>(0, 3) = Mat3T::Identity() * dt;                         // p 对 vF.template block<3, 3>(3, 6) = -R_.matrix() * SO3::hat(imu.acce_ - ba_) * dt;  // v对thetaF.template block<3, 3>(3, 12) = -R_.matrix() * dt;                             // v 对 baF.template block<3, 3>(3, 15) = Mat3T::Identity() * dt;                        // v 对 gF.template block<3, 3>(6, 6) = SO3::exp(-(imu.gyro_ - bg_) * dt).matrix();     // theta 对 thetaF.template block<3, 3>(6, 9) = -Mat3T::Identity() * dt;                        // theta 对 bg// mean and cov predictiondx_ = F * dx_;  // 这行其实没必要算,dx_在重置之后应该为零,因此这步可以跳过,但F需要参与Cov部分计算,所以保留cov_ = F * cov_.eval() * F.transpose() + Q_; current_time_ = imu.timestamp_;return true;
}

Q Q Q的算法

注意, 在离散情况下
η v = η a Δ t η θ = η g Δ t η g = η b g Δ t η a = η b a Δ t \begin{aligned} &\eta_v = \eta_a \Delta t\\ &\eta_ \theta = \eta_g \Delta t \\ &\eta_g = \eta_{bg} \Delta t\\ &\eta_ a = \eta_{ba} \Delta t \\ \end{aligned} ηv=ηaΔtηθ=ηgΔtηg=ηbgΔtηa=ηbaΔt
可以根据3.40将3.42进行一阶泰勒展开
在这里插入图片描述

代码中的实现

        double ev = options.acce_var_;double et = options.gyro_var_;double eg = options.bias_gyro_var_;double ea = options.bias_acce_var_;double ev2 = ev;  // * ev;   // tj : 为什么没平方? Q里面应该是方差double et2 = et;  // * et;double eg2 = eg;  // * eg;double ea2 = ea;  // * ea;// 设置过程噪声Q_.diagonal() << 0, 0, 0, ev2, ev2, ev2, et2, et2, et2, eg2, eg2, eg2, ea2, ea2, ea2, 0, 0, 0;

其中options的定义

    struct Options {Options() = default;/// IMU 测量与零偏参数double imu_dt_ = 0.01;  // IMU测量间隔// NOTE IMU噪声项都为离散时间,不需要再乘dt,可以由初始化器指定IMU噪声double gyro_var_ = 1e-5;       // 陀螺测量标准差double acce_var_ = 1e-2;       // 加计测量标准差double bias_gyro_var_ = 1e-6;  // 陀螺零偏游走标准差double bias_acce_var_ = 1e-4;  // 加计零偏游走标准差

correction

更新误差后验

在这里插入图片描述

H H H是观测值对误差状态变量的jacob

先看GNSS, 它观测值有位移和旋转, 根据3.66和3.70可得 H H H,
在这里插入图片描述

GNSS对旋转的观测是总体的旋转, 然而名义上的 R R R是知道的, 所以我们可以将观测值变以成对于旋转误差状态的直接观测, 这样一来, 它对自己求导就为 I I I

同理, GNSS对位移的观测是总体的位移, 然后名义上的 p p p是知道, 所以我们将对总体位移的观测转到对于误差位移的直接观测

    Eigen::Matrix<S, 6, 18> H = Eigen::Matrix<S, 6, 18>::Zero();H.template block<3, 3>(0, 0) = Mat3T::Identity();  // P部分 (3.70)H.template block<3, 3>(3, 6) = Mat3T::Identity();  // R部分(3.66)

然后求 V V V

 // 卡尔曼增益和更新过程Vec6d noise_vec;noise_vec << trans_noise, trans_noise, trans_noise, ang_noise, ang_noise, ang_noise;Mat6d V = noise_vec.asDiagonal();

其中trans_noise, ang_noise在option中定义

        /// RTK 观测参数double gnss_pos_noise_ = 0.1;                   // GNSS位置噪声double gnss_height_noise_ = 0.1;                // GNSS高度噪声double gnss_ang_noise_ = 1.0 * math::kDEG2RAD;  // GNSS旋转噪声

然后更新后验3.51b, 3.51d

    // 更新x和covVec6d innov = Vec6d::Zero();innov.template head<3>() = (pose.translation() - p_);          // 平移部分innov.template tail<3>() = (R_.inverse() * pose.so3()).log();  // 旋转部分(3.67)dx_ = K * innov;cov_ = (Mat18T::Identity() - K * H) * cov_;

有了误差状态, 就可以更新名义状态3.51c

   /// 更新名义状态变量,重置error statevoid UpdateAndReset() {p_ += dx_.template block<3, 1>(0, 0);v_ += dx_.template block<3, 1>(3, 0);R_ = R_ * SO3::exp(dx_.template block<3, 1>(6, 0));if (options_.update_bias_gyro_) {bg_ += dx_.template block<3, 1>(9, 0);}if (options_.update_bias_acce_) {ba_ += dx_.template block<3, 1>(12, 0);}g_ += dx_.template block<3, 1>(15, 0);ProjectCov();dx_.setZero();}

注意 这里有一步需要投影, 参考3.63

在这里插入图片描述

/// 对P阵进行投影,参考式(3.63)
void ProjectCov() {Mat18T J = Mat18T::Identity();J.template block<3, 3>(6, 6) = Mat3T::Identity() - 0.5 * SO3::hat(dx_.template block<3, 1>(6, 0));cov_ = J * cov_ * J.transpose();
}

再看odom, 它观测值只有速度

首先求H, 注意这里观测值为本地速度, 但是名义变量 R R R这时是知道的, 所以可以把观测值从本地速度转化到世界速度, 然后世界速度对世界速度求导就为 I I I

在这里插入图片描述

// odom 修正以及雅可比
// 使用三维的轮速观测,H为3x18,大部分为零
Eigen::Matrix<S, 3, 18> H = Eigen::Matrix<S, 3, 18>::Zero();
H.template block<3, 3>(0, 3) = Mat3T::Identity();

然后算V

// 设置里程计噪声
double o2 = options_.odom_var_ * options_.odom_var_;
odom_noise_.diagonal() << o2, o2, o2;

option里面的定义

/// 里程计参数
double odom_var_ = 0.5;
double odom_span_ = 0.1;        // 里程计测量间隔
double wheel_radius_ = 0.155;   // 轮子半径
double circle_pulse_ = 1024.0;  // 编码器每圈脉冲数

然后更新后验

本地速度观测值的算法参见 3.76

// velocity obs
double velo_l = options_.wheel_radius_ * odom.left_pulse_ / options_.circle_pulse_ * 2 * M_PI / options_.odom_span_;
double velo_r =options_.wheel_radius_ * odom.right_pulse_ / options_.circle_pulse_ * 2 * M_PI / options_.odom_span_;
double average_vel = 0.5 * (velo_l + velo_r);VecT vel_odom(average_vel, 0.0, 0.0);
VecT vel_world = R_ * vel_odom;dx_ = K * (vel_world - v_);// update cov
cov_ = (Mat18T::Identity() - K * H) * cov_;
UpdateAndReset();

这篇关于SAD notes的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/285485

相关文章

【Hello Python World】Class Notes of Week 2

列表 (3.14 update) 1.数组下标错误会抛出异常(与C++不同)2.一个list里可以有不同的数据结构3.插入方法4.删除方法 第一种方法:pop(),有返回值第二种方法:del,没有返回值第三种方法:remove(),没有返回值,而且会抛出异常 5.操作方法 用sort()和sorted()从小到大排序翻转列表reverse()求长度len() 6.列表的数据处理 求和su

Kevin‘s notes about Qt---Episode 3 在界面中修改程序参数Demo

Demo 效果 实现一个加法器,在输入框中分别填入a和b的值,点击“calculate”按钮,在sum处显示a+b的结果。 整体结构 我自己先写了一个模板用于测试从文本框获取输入数据,整个工程的结构如下: 说明: func_myself.h和func_myself.cpp是我自己创建的头文件和源文件,用于定义sum函数;CMakeLists.txt文件不用另外添加语句,只需要在

Kevin‘s notes about Qt---Episode 4 多次点击按钮反复执行机械臂动作

原先版本 SDK 1.2.1 - 2020-04-02 HMI 界面 4.0.6.135135 控制器 1.7.5 升级程序 0.6.4 伺服版本 不详(似乎也并不重要) 前言 在之前的工作中,这个问题并没有得到有效解决,情况如下: 在之前版本的程序中,每次动作执行完后需要关闭Qt界面,下一次想要运行机械臂程序时,就算没有改动程序,也需要再次点击编译运行,可以正常运行

对,一个按钮将Outlook联系人导入Notes

大家好,才是真的好。 outlook的邮件和联系人导入到Notes里面真心很容易,为了证明这一点,我们今天先来看看如何一键导入Outlook联系人到Notes里面。 对,你没看错,是一键导入。 因为,我们采用的是最强大的代码方式。 怎么说,为了证明效果,我们先看看Notes里面现在是没有联系人的。 而Oultook里面是有很多: 为此,我们在Notes客户机中,创建一个按钮

再次优化Notes启动速度!

大家好,才是真的好。 作为Notes客户机的重度爱好和使用者,很多人对Notes客户机的启动和运行速度表示了一定的质疑。 以前我们提到过,影响Notes运行性能是多方面的,比如操作系统上的杀毒软件或者windows自带的defender会扫描Notes文件,造成启动和运行变慢,请参看《Notes启动快如闪电!》 今天我们介绍另外的影响因素,简单来说就是Java。 一般来说,Notes有两种

使用Notes客户机高效工作

大家好,才是真的好。 年纪越大,发现每天时间越不够用。突然想到一个好办法,找相关书看,学习一下高效工作和生活管理。 刚好,就看到一本《每天节省2小时》,2013年出版,作者是肯尼斯·齐格勒。其中谈到一些高效方法,用的工具是Lotus Notes和Outlook,这挺让人意外。 该书第6章《控制并有效使用电子邮件》,居然详细地描述了使用邮件客户端的方式和原则。 该篇的开头箴言是:如果你希望成

Notes客户机开启事务日志

大家好,才是真的好。 了解过事务日志的人都知道——等等,你还不知道事务日志? 那我们先介绍一下,简单来说,Domino事务日志是捕获数据库更改并将其写入的记录,然后等服务器不繁忙或按计划更新到磁盘上的Notes库。 很像数据库缓存的工作机制,对吧?这是Domino R5(1999年发布)推出的新功能。 Domino服务器上建议启用事务日志,大部分情况下,可以提升Domino服务器性能,极大

Keras Notes: Keras安装与简介

reference: http://blog.csdn.net/mmc2015/article/details/50976776 先安装上再说: sudo pipinstall keras 或者手动安装: 下载:Git clone git://github.com/fchollet/keras.git 传到相应机器上 安装:cd to the Keras fol

维修笔记(Notes on Diagnosing and Fixing Everything Electronic)

前言心得 检修步骤 实录 电磁炉卫星机顶盒 卫星机顶盒机顶盒遥控器 数码播放器 参考资料 前言 本人维修的一点经验记录。 维修的意义是什么呢?”How to Diagnose and Fix Everything Electronic - by Michael Jay Geier”(book_how)这本书中给出了参考: It might be easier, but

Android Notes

maven 版本发布 1、小于 AGP7+ 使用 maven 插件 apply plugin: 'maven'uploadArchives {repositories {mavenDeployer {pom.groupId = GROUP_IDpom.artifactId = ARTIFACT_IDpom.version = VERSION//正式版本repository(url: R