学习记录-自动驾驶与机器人中的SLAM技术

以下所有内容均为高翔大神所注的《自动驾驶与机器人中的SLAM技术》中的内容

融合导航

1. EKF和优化的关系

2. 组合导航eskf中的预测部分,主要是F矩阵的构建

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 对 v
    F.template block<3, 3>(3, 6) = -R_.matrix() * SO3::hat(imu.acce_ - ba_) * dt;  // v对theta
    F.template block<3, 3>(3, 12) = -R_.matrix() * dt;                             // v 对 ba
    F.template block<3, 3>(3, 15) = Mat3T::Identity() * dt;                        // v 对 g
    F.template block<3, 3>(6, 6) = SO3::exp(-(imu.gyro_ - bg_) * dt).matrix();     // theta 对 theta
    F.template block<3, 3>(6, 9) = -Mat3T::Identity() * dt;                        // theta 对 bg

    // mean and cov prediction
    dx_ = F * dx_;  // 这行其实没必要算,dx_在重置之后应该为零,因此这步可以跳过,但F需要参与Cov部分计算,所以保留
    cov_ = F * cov_.eval() * F.transpose() + Q_;
    current_time_ = imu.timestamp_;
    return true;
}

3. 以下是速度量测,主要是H矩阵的构建

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

    // 卡尔曼增益
    Eigen::Matrix<S, 18, 3> K = cov_ * H.transpose() * (H * cov_ * H.transpose() + odom_noise_).inverse();

    // 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_);//v_是状态递推后的速度

    // update cov
    cov_ = (Mat18T::Identity() - K * H) * cov_;

    UpdateAndReset();
    return true;
}

4. 以下是GPS的量测,主要任然是H矩阵的构建

template <typename S>
bool ESKF<S>::ObserveGps(const GNSS& gnss) {
    /// GNSS 观测的修正
    assert(gnss.unix_time_ >= current_time_);

    if (first_gnss_) {
        R_ = gnss.utm_pose_.so3();
        p_ = gnss.utm_pose_.translation();
        first_gnss_ = false;
        current_time_ = gnss.unix_time_;
        return true;
    }

    assert(gnss.heading_valid_);
    ObserveSE3(gnss.utm_pose_, options_.gnss_pos_noise_, options_.gnss_ang_noise_);
    current_time_ = gnss.unix_time_;

    return true;
}

template <typename S>
bool ESKF<S>::ObserveSE3(const SE3& pose, double trans_noise, double ang_noise) {
    /// 既有旋转,也有平移
    /// 观测状态变量中的p, R,H为6x18,其余为零
    Eigen::Matrix<S, 6, 18> H = Eigen::Matrix<S, 6, 18>::Zero();
    H.template block<3, 3>(0, 0) = Mat3T::Identity();  // P部分
    H.template block<3, 3>(3, 6) = Mat3T::Identity();  // R部分(3.66)

    // 卡尔曼增益和更新过程
    Vec6d noise_vec;
    noise_vec << trans_noise, trans_noise, trans_noise, ang_noise, ang_noise, ang_noise;

    Mat6d V = noise_vec.asDiagonal();
    Eigen::Matrix<S, 18, 6> K = cov_ * H.transpose() * (H * cov_ * H.transpose() + V).inverse();

    // 更新x和cov
    Vec6d 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_;

    UpdateAndReset();
    return true;
}

IMU预积分

  • 书中有IMU预积分所有的公式推导,包括了预积分计算公式,预积分相较于状态量的雅克比矩阵公式,误差传播公式等等

/**
 * IMU 预积分器
 *
 * 调用Integrate来插入新的IMU读数,然后通过Get函数得到预积分的值
 * 雅可比也可以通过本类获得,可用于构建g2o的边类
 */
class IMUPreintegration {
   public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
    /// 参数配置项
    /// 初始的零偏需要设置,其他可以不改
    struct Options {
        Options() {}
        Vec3d init_bg_ = Vec3d::Zero();  // 初始零偏
        Vec3d init_ba_ = Vec3d::Zero();  // 初始零偏
        double noise_gyro_ = 1e-2;       // 陀螺噪声,标准差
        double noise_acce_ = 1e-1;       // 加计噪声,标准差
    };

    IMUPreintegration(Options options = Options());

    /**
     * 插入新的IMU数据
     * @param imu   imu 读数
     * @param dt    时间差
     */
    void Integrate(const IMU &imu, double dt);

    /**
     * 从某个起始点开始预测积分之后的状态
     * @param start 起始时时刻状态
     * @return  预测的状态
     */
    NavStated Predict(const NavStated &start, const Vec3d &grav = Vec3d(0, 0, -9.81)) const;

    /// 获取修正之后的观测量,bias可以与预积分时期的不同,会有一阶修正
    SO3 GetDeltaRotation(const Vec3d &bg);
    Vec3d GetDeltaVelocity(const Vec3d &bg, const Vec3d &ba);
    Vec3d GetDeltaPosition(const Vec3d &bg, const Vec3d &ba);

   public:
    double dt_ = 0;                          // 整体预积分时间
    Mat9d cov_ = Mat9d::Zero();              // 累计噪声矩阵
    Mat6d noise_gyro_acce_ = Mat6d::Zero();  // 测量噪声矩阵

    // 零偏
    Vec3d bg_ = Vec3d::Zero();
    Vec3d ba_ = Vec3d::Zero();

    // 预积分观测量
    SO3 dR_;
    Vec3d dv_ = Vec3d::Zero();
    Vec3d dp_ = Vec3d::Zero();

    // 雅可比矩阵
    Mat3d dR_dbg_ = Mat3d::Zero();
    Mat3d dV_dbg_ = Mat3d::Zero();
    Mat3d dV_dba_ = Mat3d::Zero();
    Mat3d dP_dbg_ = Mat3d::Zero();
    Mat3d dP_dba_ = Mat3d::Zero();
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/327032.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于杂交PSO算法的风光储微网日前优化调度(MATLAB实现)

微网中包含&#xff1a;风电、光伏、储能、微型燃气轮机&#xff0c;以最小化电网购电成本、光伏风机的维护成本、蓄电池充放电维护成本、燃气轮机运行成本及污染气体治理成本为目标&#xff0c;综合考虑&#xff1a;功率平衡约束、燃气轮机爬坡约束、电网交换功率约束、储能装…

Elasticsearch_8.11.4_kibana_8.11.4_metricbeat_8.11.4安装及本地部署_ELK日志部署

文章目录 Elasticsearch_8.11.4_kibana_8.11.4_metricbeat_8.11.4安装及本地部署_ELK日志部署分布式引擎Elasticsearch_8.11.4安装及本地部署系统环境要求1 Windows 安装 Elasticsearch下载完成后进行解压,进入 bin 目录,找到elasticsearch.bat脚本文件执行一键启动.启动都选允…

【Python学习】Python学习15-模块

目录 【Python学习】Python学习15-模块 前言创建语法引入模块from…import 语句from…import* 语句搜索路径PYTHONPATH 变量-*- coding: UTF-8 -*-导入模块现在可以调用模块里包含的函数了PYTHONPATH 变量命名空间和作用域dir()函数globals() 和 locals() 函数reload() 函数Py…

ROS2学习笔记一:安装及测试

目录 前言 1 ROS2安装与卸载 1.1 安装虚拟机 1.2 ROS2 humble安装 2 ROS2测试 2.1 topic测试 2.2 小海龟测试 2.3 RQT可视化 2.4 占用空间 前言 ROS2的前身是ROS&#xff0c;ROS即机器人操作系统&#xff08;Robot Operating System&#xff09;,ROS为了“提高机器人…

原生js实现拖拽效果

<!DOCTYPE html> <html> <head> <style> #mydiv { width: 200px; height: 200px; background-color: red; position: absolute; cursor: move; } </style> | </head> <body> <div id"mydiv">拖拽我…

基于java web的机票管理系统设计与实现设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

机器学习扩散模型简介

一、说明 扩散模型的迅速崛起是过去几年机器学习领域最大的发展之一。在这本易于理解的指南中了解您需要了解的有关扩散模型的所有信息。 扩散模型是生成模型&#xff0c;在过去几年中越来越受欢迎&#xff0c;这是有充分理由的。仅在 2020 年代发布的几篇开创性论文就向世界…

socket.io分房间交流

基本详情看这里 Socket.IO 是一个库,可以在客户端和服务器之间实现 低延迟, 双向 和 基于事件的 通信. 效果展示 安装依赖 // 后端插件安装 npm i socket.io -S // 前端插件安装 npm i socket.io-client -S 前端搭建及逻辑 <script setup> import { ref, onMounted…

机器学习 | 卷积神经网络

机器学习 | 卷积神经网络 实验目的 采用任意一种课程中介绍过的或者其它卷积神经网络模型&#xff08;例如LeNet-5、AlexNet等&#xff09;用于解决某种媒体类型的模式识别问题。 实验内容 卷积神经网络可以基于现有框架如TensorFlow、Pytorch或者Mindspore等构建&#xff…

Vue2脚手架配置教程IDEA配置VUE

5.12.3 Vue Cli 文档地址: https://cli.vuejs.org/zh/ IDEA 打开项目&#xff0c;运行项目

React 原理

函数式编程 纯函数 reducer 必须是一个纯函数&#xff0c;即没有副作用的函数&#xff0c;不修改输入值&#xff0c;相同的输入一定会有相同的输出不可变值 state 必须是不可变值&#xff0c;否则在 shouldComponentUpdate 中无法拿到更新前的值&#xff0c;无法做性能优化操作…

Linux网络服务部署yum仓库

目录 一、网络文件 1.1.存储类型 1.2.FTP 文件传输协议 1.3.传输模式 二、内网搭建yum仓库 一、网络文件 1.1.存储类型 直连式存储&#xff1a;Direct-Attached Storage&#xff0c;简称DAS 存储区域网络&#xff1a;Storage Area Network&#xff0c;简称SAN&#xff0…

服务拆分及远程调用

分布式架构都离不开服务的拆分&#xff0c;微服务也是一样。 1.微服务拆分 不同微服务&#xff0c;不要重复开发相同业务 微服务数据独立&#xff0c;不要访问其它微服务的数据库 微服务可以将自己的业务暴露为接口&#xff0c;供其它微服务调用 2.远程调用 以前时&#xf…

Halcon提取亚像素轮廓edges_sub_pix算子

Halcon提取亚像素轮廓edges_sub_pix算子 最常用的提取亚像素轮廓的算子是edges_sub_pix算子&#xff0c;该算子同样提供了大量的提取方法&#xff0c;只需要在Filter 参数中设置方法的名字&#xff0c;就可以完成边缘的提取。该算子的输入是灰度图像&#xff0c;输出是XLD轮廓…

BurpSuite超详细安装教程-功能概述-配置-使用教程---(附下载链接)

一、介绍 BurpSuite是渗透测试、漏洞挖掘以及Web应用程序测试的最佳工具之一&#xff0c;是一款用于攻击web 应用程序的集成攻击测试平台&#xff0c;可以进行抓包、重放、爆破&#xff0c;包含许多工具&#xff0c;能处理对应的HTTP消息、持久性、认证、代理、日志、警报。 …

使用scipy处理图片——滚动图片

大纲 常规模式constant和grid-constant 交换模式wrap和grid-wrap 镜像reflect、mirror和grid-mirror 最近值nearest 代码 在《使用numpy处理图片——滚动图片》一文中&#xff0c;我们介绍了numpy的roll方法&#xff0c;它只能让超出区域的元素回到被移动的区域中&#xff0c;如…

图像提取大师:轻松从指定时长中获取某帧的图片,视频剪辑方法

在数字媒体时代&#xff0c;视频和图像已成为生活中不可或缺的部分。要从视频中提取某一帧作为图片&#xff0c;或者在视频剪辑时要采用其他的方法来达到需求的效果。下面来看云炫AI智剪如何轻松地从指定时长的视频中获取某帧的图片&#xff0c;视频剪辑的新方法。 视频中按指定…

Spring Cloud中的提供者与消费者

在服务调用关系中&#xff0c;会有两个不同的角色&#xff1a; 服务提供者&#xff1a;一次业务中&#xff0c;被其它微服务调用的服务。&#xff08;提供接口给其它微服务&#xff09; 服务消费者&#xff1a;一次业务中&#xff0c;调用其它微服务的服务。&#xff08;调用…

【竞技宝】DOTA2梦幻联赛 G2.iG让一追二击败Bright晋级败决!

北京时间2024年1月16日&#xff0c;DOTA2梦幻联赛S22中国区预选赛继续进行&#xff0c;本日首场比赛迎来G2.IG对阵Bright。本场比赛双方前两局战至1-1平&#xff0c;决胜局G2.iG monet的虚空在中期连续放出两个完美团战帮助G2.iG奠定胜势&#xff0c;最终G2.iG让一追二击败Brig…

Java 基础 - 06 List 之 Stack 以及List的相关总结

Java的栈&#xff0c;算是我们在Java中常见的一种数据结构&#xff0c;他遵循先进后出的原则&#xff08;Last-In-First-Out&#xff0c;LIFO&#xff09;的原则&#xff0c;在Java中&#xff0c;Stack是通过继承自Vector类实现的。 如上图所示&#xff0c;我们的stack继承自Ve…