高翔【自动驾驶与机器人中的SLAM技术】学习笔记(十)高翔书中的细节:参考链接;卫星导航;ESKF

news2024/11/16 9:42:17

一、 参考链接

我认真查找了好多地方:结果在最后一页。

作者GITHUB链接如下:
https://github.com/gaoxiang12/slam_in_autonomous_driving

全书所有参考链接 :如下

1 https://www.sae.org/standards/content/j3016_202104
2 http://www.evinchina.com/articleshow-217.html
3 Arcgis: https://www.arcgis.com/
4 Autoware: https://github.com/autowarefoundation/autoware
5 工具地址见:https://github.com/hobu/mgrs
6 ÀEPFL雕像数据集:https://lgg.epfl.ch/statues_dataset.php
7 https://github.com/HKUST-Aerial-Robotics/A-LOAM
8 https://github.com/wh200720041/floam



二、卫星导航

全球卫星导航系统(Global Navigation Satellite System,GNSS),简称卫星导航。

GNSS 通过测量自身与地球周围各卫星的距离来确定自身的位置,而与卫星的距离主要是通过测量时间间隔来确定的。一个卫星信号从卫星上发出时,带有一个发送时间。而GNSS接收机接收到它时,又有一个接收时间。比较接收时间与卫星发送时间,就能估算各卫星离我们的距离。而各种 GNSS 和测量方法的主要差异,就是如何减少这个时间测量的误差。从这种角度来看,GNSS本质上可以看成一种高精度的授时系统

目前,世界范围内,我们可以接收到的卫星信号主要来自四个系统:

  • 美国的全球定位系统Global Positioning System,GPS )、
  • 中国的北斗卫星导航系统(Beidou Navigation Satellite System,BDS)、
  • 俄罗斯的格洛纳斯系统(GLONASS)、
  • 欧盟的伽利略系统(GALILEO)。

对于自动驾驶车辆来说,最常用的卫星定位技术包括以下几种。

  1. 单点 GNSS 定位,即传统的米级精度卫星定位。这种定位方式价格低廉,应用广泛。大多数手机、车机等终端都具备单点卫星定位能力。在普通车辆的道路级导航中,单点定位的精度足以让驾驶人员辨认出车辆位于哪条道路,但在多条道路并排(车道级)时,它的精度又往往不足以区分车辆是在高速路上还是在辅路上,或者是在主路上还是在匝道上。
  2. RTK 定位。由于卫星定位信号在传输过程中可能产生误差,人们发展了差分定位技术,即通过地面上的一个已知精确位置的基站与车辆通信,校正车辆卫星接收机的信号。差分定位又可进一步分为位置伪距载波相位差分定位。其中最广泛使用的,是基于载波相位差分的 RTK 技术。RTK 通过与一个或多个基站进行通信,可以实时地获取校正后的卫星导航位置。

自动驾驶通常需要车道级导航而非道路级导航。车道级导航可以指出车辆位于道路当中哪个车道,比道路级导航更稳定。

世界坐标系

  • 地理坐标系:经纬度
  • UTM坐标系:米制坐标

GNSS代码:src/common/gnss.h

//
// Created by xiang on 2022/1/4.
//

#ifndef SLAM_IN_AUTO_DRIVING_GNSS_H
#define SLAM_IN_AUTO_DRIVING_GNSS_H

#include "common/eigen_types.h"
#include "common/message_def.h"

namespace sad {

/// GNSS状态位信息
/// 通常由GNSS厂商提供,这里使用千寻提供的状态位
enum class GpsStatusType {
    GNSS_FLOAT_SOLUTION = 5,         // 浮点解(cm到dm之间)
    GNSS_FIXED_SOLUTION = 4,         // 固定解(cm级)
    GNSS_PSEUDO_SOLUTION = 2,        // 伪距差分解(分米级)
    GNSS_SINGLE_POINT_SOLUTION = 1,  // 单点解(10m级)
    GNSS_NOT_EXIST = 0,              // GPS无信号
    GNSS_OTHER = -1,                 // 其他
};

/// UTM 坐标
struct UTMCoordinate {
    UTMCoordinate() = default;
    explicit UTMCoordinate(int zone, const Vec2d& xy = Vec2d::Zero(), bool north = true)
        : zone_(zone), xy_(xy), north_(north) {}

    int zone_ = 0;              // utm 区域
    Vec2d xy_ = Vec2d::Zero();  // utm xy
    double z_ = 0;              // z 高度(直接来自于gps)
    bool north_ = true;         // 是否在北半球
};

/// 一个GNSS读数结构
struct GNSS {
    GNSS() = default;
    GNSS(double unix_time, int status, const Vec3d& lat_lon_alt, double heading, bool heading_valid)
        : unix_time_(unix_time), lat_lon_alt_(lat_lon_alt), heading_(heading), heading_valid_(heading_valid) {
        status_ = GpsStatusType(status);
    }

    /// 从ros的NavSatFix进行转换
    /// NOTE 这个只有位置信息而没有朝向信息,UTM坐标请从ch3的代码进行转换
    GNSS(sensor_msgs::NavSatFix::Ptr msg) {
        unix_time_ = msg->header.stamp.toSec();
        // 状态位
        if (int(msg->status.status) >= int(sensor_msgs::NavSatStatus::STATUS_FIX)) {
            status_ = GpsStatusType::GNSS_FIXED_SOLUTION;
        } else {
            status_ = GpsStatusType::GNSS_OTHER;
        }
        // 经纬度
        lat_lon_alt_ << msg->latitude, msg->longitude, msg->altitude;
    }

    double unix_time_ = 0;                                  // unix系统时间
    GpsStatusType status_ = GpsStatusType::GNSS_NOT_EXIST;  // GNSS 状态位
    Vec3d lat_lon_alt_ = Vec3d::Zero();                     // 经度、纬度、高度,前二者单位为度
    double heading_ = 0.0;                                  // 双天线读到的方位角,单位为度
    bool heading_valid_ = false;                            // 方位角是否有效

    UTMCoordinate utm_;       // UTM 坐标(区域之类的也在内)
    bool utm_valid_ = false;  // UTM 坐标是否已经计算(若经纬度给出错误数值,此处也为false)

    SE3 utm_pose_;  // 用于后处理的6DoF Pose
};

}  // namespace sad

using GNSSPtr = std::shared_ptr<sad::GNSS>;

#endif  // SLAM_IN_AUTO_DRIVING_GNSS_H

给出了GNSS的状态枚举;以及UTM坐标的数据结构和GNSS的读数数据结构。

其中GNSS的数据结构与ros中的NavSatStatus消息可以转换。

注意GNSS中,有几个属性字段:一个是通过utm_convert转换之后的坐标。另一个是基于UTM坐标的6自由度pose。

antenna:天线

//
// Created by xiang on 2022/1/4.
//

#include <glog/logging.h>
#include <iomanip>
#include <memory>

#include "common/gnss.h"
#include "common/io_utils.h"
#include "tools/ui/pangolin_window.h"
#include "utm_convert.h"

DEFINE_string(txt_path, "./data/ch3/10.txt", "数据文件路径");

// 以下参数仅针对本书提供的数据
DEFINE_double(antenna_angle, 12.06, "RTK天线安装偏角(角度)");
DEFINE_double(antenna_pox_x, -0.17, "RTK天线安装偏移X");
DEFINE_double(antenna_pox_y, -0.20, "RTK天线安装偏移Y");
DEFINE_bool(with_ui, true, "是否显示图形界面");

/**
 * 本程序演示如何处理GNSS数据
 * 我们将GNSS原始读数处理成能够进行后续处理的6自由度Pose
 * 需要处理UTM转换、RTK天线外参、坐标系转换三个步骤
 *
 * 我们将结果保存在文件中,然后用python脚本进行可视化
 */

int main(int argc, char** argv) {
    google::InitGoogleLogging(argv[0]);
    FLAGS_stderrthreshold = google::INFO;
    FLAGS_colorlogtostderr = true;
    google::ParseCommandLineFlags(&argc, &argv, true);

    if (fLS::FLAGS_txt_path.empty()) {
        return -1;
    }

    sad::TxtIO io(fLS::FLAGS_txt_path);

    std::ofstream fout("./data/ch3/gnss_output.txt");
    Vec2d antenna_pos(FLAGS_antenna_pox_x, FLAGS_antenna_pox_y);

    auto save_result = [](std::ofstream& fout, double timestamp, const SE3& pose) {
        auto save_vec3 = [](std::ofstream& fout, const Vec3d& v) { fout << v[0] << " " << v[1] << " " << v[2] << " "; };
        auto save_quat = [](std::ofstream& fout, const Quatd& q) {
            fout << q.w() << " " << q.x() << " " << q.y() << " " << q.z() << " ";
        };

        fout << std::setprecision(18) << timestamp << " " << std::setprecision(9);
        save_vec3(fout, pose.translation());
        save_quat(fout, pose.unit_quaternion());
        fout << std::endl;
    };

    std::shared_ptr<sad::ui::PangolinWindow> ui = nullptr;
    if (FLAGS_with_ui) {
        ui = std::make_shared<sad::ui::PangolinWindow>();
        ui->Init();
    }

    bool first_gnss_set = false;
    Vec3d origin = Vec3d::Zero();
    io.SetGNSSProcessFunc([&](const sad::GNSS& gnss) {
          sad::GNSS gnss_out = gnss;
          if (sad::ConvertGps2UTM(gnss_out, antenna_pos, FLAGS_antenna_angle)) {
              if (!first_gnss_set) {
                  origin = gnss_out.utm_pose_.translation();
                  first_gnss_set = true;
              }

              /// 减掉一个原点
              gnss_out.utm_pose_.translation() -= origin;

              save_result(fout, gnss_out.unix_time_, gnss_out.utm_pose_);

              if (ui) {
                  ui->UpdateNavState(
                      sad::NavStated(gnss_out.unix_time_, gnss_out.utm_pose_.so3(), gnss_out.utm_pose_.translation()));
                  usleep(1e3);
              }
          }
      }).Go();

    if (ui) {
        while (!ui->ShouldQuit()) {
            usleep(1e5);
        }
        ui->Quit();
    }

    return 0;
}

正如代码注释中所写的:将txt中的GNSS数据读取,然后转换成UTM数据,基于天线外参,坐标转换为6自由度的pose。将pose记录gnss_out。最后用gnss_out转换成导航状态量NavStated类。

此处是从txt读取数据,真实应用更可能订阅某个ROS话题节点。



三、误差状态卡尔曼滤波器(Error State Kalman Filter,ESKF)

RTK 设备为我们提供了一个不太稳定的位姿观测源。我们可以将它视为定位滤波器的一种观测。本节将RTK与IMU 结合,使用拓展卡尔曼滤波器形成传统的组合导航算法,以供后续的算法对比。严格来说,笔者向读者介绍的是误差状态卡尔曼滤波器(Error State Kalman Filter,ESKF)。ESKF 的应用十分广泛,从GINS组合导航到视觉SLAM[58-60]、外参自标定[61-62]等任务中都有应用。

这里提到了一个外参自动标定

  • Visual-inertial sensor fusion: Localization, mapping and sensor-to-sensor self-calibration. 
  • A kalman flter-based algorithm for imu-camera calibration:Observability analysis and performance evaluation.

1、ESKF的数学推导演变

之前是把IMU视为观测模型。现在我们IMU视为运动模型,并把GNSS观测视为观测模型,推导整个滤波器。

状态估计问题嘛,先明确状态变量。

状态变量为:

\mathbf{x} = [\mathbf{p}, \mathbf{v},\mathbf{R},\mathbf{b}_g, \mathbf{b}_a, \mathbf{g}]^\top

所有变量都默认取下标()_{WB},其中p为平移,v为速度,R为旋转,\mathbf{b}_g,\mathbf{b}_a为零偏,g为重力。

将IMU测量值带入公式3.1。状态变量在连续时间下的运动方程为公式3.22:

公式3.1:

公式3.22

直白点说:搞不定了,或者说不好搞了。得想个其他法子。

于是,能否避免直接使用x和P来表达状态的均值协方差,推导运动和观察方程呢?能否使用原先卡尔曼滤波器中的更新量来推导这两个方程?

说直白点:状态量用x和P来表示均值和协方差,来推导运动方程和观察方程这个方式有困难推不动了,改用更新量来重新推导运动方程和观察方程。

回忆卡尔曼滤波器中的观测部分。咱们把运动方程和观察方程,以及“黄金五公式”一起回忆下。

在卡尔曼滤波器观测部分,我们看到在用卡尔曼增益更新状态时,(C和H看做等效)。

\boldsymbol{x}_k=\boldsymbol{x}_{k,\mathrm{pred}}+\boldsymbol{K}_k\underbrace{(\boldsymbol{z}_k-\boldsymbol{H}_k\boldsymbol{x}_{k,\mathrm{pred}})}_\text{Updates Value}.

之前提到这个公式有大用,基于此可以做一些操作了。

在流形意义下,右侧的更新量(Updates Value)(CSDN富文本编辑模式下,公式不支持中文,此处用了Updates Value)。原文如下:

应是位于切空间中的矢量,中间的加法应为流形与切空间指数映射的广义加法。但也可以将更新量(或者称为误差状态)视为滤波器的状态变量来推导运动和观测模型

这就引出了误差状态卡尔曼滤波器

到这意思就是说,对于我们的状态估计问题,我们要估计的状态变为了误差状态

这个更新量:其实就是观测结果与递推预测结果的差异

  • A有一个数据,B有一个数据,真实数据应该是多少。
  • 这俩数据之间的差异,通过其协方差(可信度)来分配更倾向于A还是B。

这个之前理解KF时提及过。如何分配这个差异。

  • 矩阵H是从预测空间转换到观测空间的变换矩阵
  • Hx就将递推预测的数据转换到观测空间
  • 与观测数据进行比较。获取差异。

更进一步,不光是平移和旋转,把所有的状态都用误差状态来表达,这就是典型ESKF的做法。
ESKF是许多传统的、现代的系统里都广泛使用的状态估计方法,既可以作为组合导航的滤波器,也可以用来实现LIO、VIO 等复杂系统。

相比于传统KF,ESKF的优点可以总结如下:

  1. 在旋转的处理上,ESKF的状态变量可以采用最小化的参数表达,也就是使用三维变量来表达旋转的增量。该变量位于切空间中,而切空间是一个矢量空间。传统KF需要用到四元数(4维)或者更高维的变量来表达状态(旋转矩阵,9维),要不就得采用带有奇异性的表达方式(欧拉角)。
  2. ESKF总是在原点附近,离奇异点较远,数值方面更稳定并且不会产生离工作点太远而导致线性化近似不够的问题。
  3. ESKF的状态量为小量,其二阶变量相对来说可以忽略。同时,大多数雅可比矩阵在小量
    情况下变得非常简单,甚至可以用单位阵代替
  4. 误差状态的运动学相比原状态变量更小(小量的运动学),因此可以把更新部分归入原状
    态变量中。

个人理解哈:只考虑扰动模型的情况下,有各种好处。直白点:累计误差不会在更新中因累计放大而影响彼此。只考虑观察和预测的扰动模型即可。


在ESKF中,通常把原状态

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在线相亲系统:新时代的婚恋观与传统习俗的碰撞

随着互联网技术的发展&#xff0c;相亲交友平台已成为年轻人寻找伴侣的新方式。这些平台不仅改变了人们的社交习惯&#xff0c;也反映了当代婚恋观与传统习俗之间的碰撞与融合。开发h17711347205本文将探讨在线相亲系统是如何在尊重传统的基础上&#xff0c;为现代年轻人提供更…

年龄性别与手势识别系统源码分享

年龄性别与手势识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comput…

Docekrfile和docker compose编写指南及注意事项

Dockerfile 基础语法 我们通过编写dockerfile,将每一层要做的事情使用语法固定下来&#xff0c;之后运行指令就可以通过docker来制作自己的镜像了。 构建镜像的指令&#xff1a;docker build /path -t imageName:tag 注意&#xff0c;docker build后的path必须是dockerfile…

什么是文件完整性监控(FIM)

组织经常使用基于文件的系统来组织、存储和管理信息。文件完整性监控&#xff08;FIM&#xff09;是一种用于监控和验证文件和系统完整性的技术&#xff0c;识别用户并提醒用户对文件、文件夹和配置进行未经授权或意外的变更是 FIM 的主要目标&#xff0c;有助于保护关键数据和…

贝锐洋葱头浏览器随时随地访问教务系统,轻松搞定选课

教育网的“拥堵”早已是老生常谈&#xff0c;学生数量庞大、上网时间集中、带宽有限&#xff0c;导致网络速度慢。尤其是从外部网络访问教育网时&#xff0c;更是因为跨运营商的缘故变得缓慢。 而学校内网也是类似的情况&#xff0c;课余时间和上课时间的网络使用情况差别巨大…

【Python报错已解决】TypeError: unhashable type: ‘numpy.ndarray‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

【API安全】crAPI靶场全解

目录 BOLA Vulnerabilities Challenge 1 - Access details of another user’s vehicle Challenge 2 - Access mechanic reports of other users Broken User Authentication Challenge 3 - Reset the password of a different user Excessive Data Exposure Challenge …

深度解析APP软件开发:构建卷轴式分销系统的实践探索

在移动互联网的浪潮中&#xff0c;APP软件的开发与应用不断推动着商业模式的创新与发展。其中&#xff0c;卷轴模式分销系统作为一种新兴的营销手段&#xff0c;正逐渐受到市场的青睐。作为程序员&#xff0c;深入探索并实践这一模式的系统开发&#xff0c;不仅是对技术能力的挑…

深入理解函数【JavaScript】

在 JavaScript 中&#xff0c;函数作为一种重要的基本结构&#xff0c;承载着编程中的许多关键概念。下面是与函数相关的内容介绍&#xff1a; 1. 函数定义 JavaScript 中有多种方式定义函数&#xff1a; 1.1 函数声明&#xff08;Function Declaration&#xff09; functi…

温州大麓青年音乐节即将开唱,37组音乐人国庆齐聚共谱华章

金秋十月&#xff0c;当丰收的季节与音乐的旋律相遇&#xff0c;温州将迎来一场前所未有的文化盛事。2024年10月1日至4日&#xff0c;温州大麓青年音乐节将在瓯海盛大举行。不仅是一场音乐的狂欢&#xff0c;更是一次多元文化的碰撞与融合。本届音乐节邀请了37组以上的知名音乐…

WEB3.0是什么?

WEB3.0是什么&#xff1f; web3 之前用户创作分享内容&#xff0c;内容信息用户信息被运营平台的科技公司控制&#xff0c;比如某云&#xff0c;某信&#xff0c;某音&#xff0c;用户对直接的数据没有真正的所有权&#xff0c;web2平台拥有绝对控制权&#xff0c;想禁用你的账…

为什么说3-8岁的行为塑造奠定孩子的一生?

亲爱的爸爸妈妈&#xff1a; 育儿征程中会有无数问题在脑海中闪烁。孩子成长的每一步&#xff0c;都紧紧牵动着我们的心弦。 实际上&#xff0c;成长的关键在于塑造孩子五个至关重要的能力&#xff0c;分别是专注力、思维力、沟通力、抗逆力和行动力。 数业智能心大陆基于自研 …

CJEval:一个基于中国初中考试的多样化考试问题数据集

2024-09-26&#xff0c;由腾讯YouTu Lab和北京大学联合发布的CJEval&#xff0c;是一个基于中国初中生考试数据的评估基准&#xff0c;用于测试和分析大型语言模型&#xff08;LLMs&#xff09;在教育任务中的表现&#xff0c;从而提高在线教育平台的智能化水平。 一、背景&…

(done) 声音信号处理基础知识(11) (Complex Numbers for Audio Signal Processing)

参考&#xff1a;https://www.youtube.com/watch?vDgF4m0AWCgA&t1047s 似乎是因为信号处理需要使用复数&#xff0c;作者花了一节课介绍复数 据油管主所说&#xff0c;声学信号处理中引入复数的原因是&#xff1a;快速完成部分计算 这里的例子是&#xff0c;当我们做傅里…

Python库matplotlib之一

Python库matplotlib之一 plot函数使用列表或数组画线绘图的格式 matplotlib.pyplot 是使 matplotlib 像 MATLAB 一样工作的函数集合。每个 pyplot 函数都会对图形进行一些更改&#xff1a;例如&#xff0c; 创建图形在图形中创建绘图区域在绘图区域中绘制一些线条用标签装饰绘…

Redis --- 第一讲 --- 分布式简单介绍

一、认识Redis 定义变量&#xff0c;不就是在内存中存储数据吗&#xff1f;Redis是在分布式系统中&#xff0c;才能发挥威力的。如果只是单机程序。直接通过变量存储数据的方式势必使用Redis更优的选择。由于进程的的隔离性。进程之间通过网络通信&#xff0c;就能共享数据。Re…

keepalived+nginx实现高可用的案例详解(主主模式)

文章目录 前言keepalived主备模式和主主模式有什么区别1. 主备模式&#xff08;Master-Backup Mode&#xff09;2. 主主模式&#xff08;Active-Active Mode 或 Dual Master Mode&#xff09;主备模式 vs 主主模式 的区别总结&#xff1a; 环境案例实现具体步骤ngx1ngx2验证 前…

【教学类-56-05】数感训练——数字05(指定数字出现次数,速度快)

背景需求&#xff1a; 昨天有客户订购“阿拉伯数字的数感训练” 我查看文件夹&#xff0c;发现前期没有生成过0-50的数字&#xff08;只研究了学号数感训练的学具&#xff09; 赶紧用之前写过的代码生成了一份 【教学类-56-01】数感训练——数字01&#xff08;同样的数字涂色…

猫头虎分享已解决Bug:npm warn old lockfile Could not fetch metadata for yallist@3.1.1

&#x1f42f;猫头虎分享已解决Bug&#xff1a;npm warn old lockfile Could not fetch metadata for yallist3.1.1 今天有粉丝问猫哥&#xff1a;“&#x1f42f;猫头虎&#xff0c;我在使用 npm 安装依赖时遇到了一个错误提示 Could not fetch metadata for yallist3.1.1&am…

凿岩机械液压比例多路阀控制器

工程机械应用的BEUEC比例放大器控制比例多路阀主要应用于以下几大类设备中&#xff1a; 1. 挖掘机械&#xff1a;包括挖掘机、挖掘装载机、挖掘船等&#xff0c;主要用于挖掘土壤、煤和矿石等物料。 2. 铲土运输机械&#xff1a;如推土机、铲运机、装载机等&#xff0c;主要用…