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

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

作者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)。
对于自动驾驶车辆来说,最常用的卫星定位技术包括以下几种。
- 单点 GNSS 定位,即传统的米级精度卫星定位。这种定位方式价格低廉,应用广泛。大多数手机、车机等终端都具备单点卫星定位能力。在普通车辆的道路级导航中,单点定位的精度足以让驾驶人员辨认出车辆位于哪条道路,但在多条道路并排(车道级)时,它的精度又往往不足以区分车辆是在高速路上还是在辅路上,或者是在主路上还是在匝道上。
- 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 xydouble 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 sadusing 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观测视为观测模型,推导整个滤波器。
状态估计问题嘛,先明确状态变量。
状态变量为:
所有变量都默认取下标,其中p为平移,v为速度,R为旋转,
为零偏,g为重力。
将IMU测量值带入公式3.1。状态变量在连续时间下的运动方程为公式3.22:
公式3.1:

公式3.22


直白点说:搞不定了,或者说不好搞了。得想个其他法子。
于是,能否避免直接使用x和P来表达状态的均值和协方差,推导运动和观察方程呢?能否使用原先卡尔曼滤波器中的更新量来推导这两个方程?
说直白点:状态量用x和P来表示均值和协方差,来推导运动方程和观察方程这个方式有困难推不动了,改用更新量来重新推导运动方程和观察方程。
回忆卡尔曼滤波器中的观测部分。咱们把运动方程和观察方程,以及“黄金五公式”一起回忆下。

在卡尔曼滤波器观测部分,我们看到在用卡尔曼增益更新状态时,(C和H看做等效)。
之前提到这个公式有大用,基于此可以做一些操作了。
在流形意义下,右侧的更新量(Updates Value)(CSDN富文本编辑模式下,公式不支持中文,此处用了Updates Value)。原文如下:

应是位于切空间中的矢量,中间的加法应为流形与切空间指数映射的广义加法。但也可以将更新量(或者称为误差状态)视为滤波器的状态变量,来推导运动和观测模型。
这就引出了误差状态卡尔曼滤波器。
到这意思就是说,对于我们的状态估计问题,我们要估计的状态变为了误差状态。
这个更新量:其实就是观测结果与递推预测结果的差异。
- A有一个数据,B有一个数据,真实数据应该是多少。
- 这俩数据之间的差异,通过其协方差(可信度)来分配更倾向于A还是B。
这个之前理解KF时提及过。如何分配这个差异。
- 矩阵H是从预测空间转换到观测空间的变换矩阵。
- Hx就将递推预测的数据转换到观测空间,
- 与观测数据进行比较。获取差异。
更进一步,不光是平移和旋转,把所有的状态都用误差状态来表达,这就是典型ESKF的做法。
ESKF是许多传统的、现代的系统里都广泛使用的状态估计方法,既可以作为组合导航的滤波器,也可以用来实现LIO、VIO 等复杂系统。
相比于传统KF,ESKF的优点可以总结如下:
- 在旋转的处理上,ESKF的状态变量可以采用最小化的参数表达,也就是使用三维变量来表达旋转的增量。该变量位于切空间中,而切空间是一个矢量空间。传统KF需要用到四元数(4维)或者更高维的变量来表达状态(旋转矩阵,9维),要不就得采用带有奇异性的表达方式(欧拉角)。
- ESKF总是在原点附近,离奇异点较远,数值方面更稳定,并且不会产生离工作点太远而导致线性化近似不够的问题。
- ESKF的状态量为小量,其二阶变量相对来说可以忽略。同时,大多数雅可比矩阵在小量
情况下变得非常简单,甚至可以用单位阵代替。 - 误差状态的运动学相比原状态变量更小(小量的运动学),因此可以把更新部分归入原状
态变量中。
个人理解哈:只考虑扰动模型的情况下,有各种好处。直白点:累计误差不会在更新中因累计放大而影响彼此。只考虑观察和预测的扰动模型即可。
在ESKF中,通常把原状态变量称为名义状态变量(Nominal State),把ESKF里的状态变量称为误差状态变量(Error State)。
<相关文章:
高翔【自动驾驶与机器人中的SLAM技术】学习笔记(十)高翔书中的细节:参考链接;卫星导航;ESKF
一、 参考链接 我认真查找了好多地方:结果在最后一页。 作者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.htm…...
【在Python中爬取网页信息并存储】
在Python中爬取网页信息并存储的过程通常涉及几个关键步骤:发送HTTP请求、解析HTML内容、提取所需数据,以及将数据存储到适当的格式中(如文本文件、CSV文件、数据库等)。以下是一个更详细的指南,包括示例代码ÿ…...
ESP32 Bluedroid 篇(1)—— ibeacon 广播
前言 前面我们已经了解了 ESP32 的 BLE 整体架构,现在我们开始实际学习一下Bluedroid 从机篇的广播和扫描。本文将会以 ble_ibeacon demo 为例子进行讲解,需要注意的一点是。ibeacon 分为两个部分,一个是作为广播者,一个是作为观…...
【通配符】粗浅学习
1 背景说明 首先要注意,通配符中的符号和正则表达式中的特殊符号具备不同的匹配意义,例如:*在正则表达式中表示里面是指匹配前面的子表达式0次或者多次,而在通配符领域则是表示代表0个到无穷个任意字符。 此外,要注意…...
Spring MVC 常用注解
目录 基础概念 常用注解介绍 基础概念 1、MVC :代表一种软件架构设计思想,通俗的理解:客户端发送请求到后台服务器的Controller(C),控制器调用Model(M)来处理业务逻辑,处理完成后,返回处理后的数据到Vie…...
水泵模块(5V STM32)
目录 一、介绍 二、传感器原理 1.尺寸介绍 2.继电器控制水泵电路原理图 三、程序设计 main.c文件 bump.h文件 bump.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 水泵模块(bump)通常是指用于液体输送系统的组件,它负责将水或其他流体从低处提…...
需求6:如何写一个后端接口?
这两天一直在对之前做的工作做梳理总结,不过前两天我都是在总结一些bug的问题。尽管有些bug问题我还没写文章,但是,我今天不得不先停下对bug的总结了。因为在国庆之后,我需要自己开发一个IT资产管理的功能,这个功能需要…...
《Linux从小白到高手》理论篇(五):文件权限控制及文件操作相关的命令
本篇介绍Linux文件权限控制及文件操作相关的命令,看完本文,有关Linux文件权限控制及文件操作相关的常用命令你就掌握了99%了。 文件权限 在介绍文件权限之前先来复习下Linux的文件类型,始终记住那句话:Linux系统下,一…...
异常场景分析
优质博文:IT-BLOG-CN 为了防止黑客从前台异常信息,对系统进行攻击。同时,为了提高用户体验,我们都会都抛出的异常进行拦截处理。 一、异常处理类 Java把异常当做是破坏正常流程的一个事件,当事件发生后,…...
Leetcode: 0001-0010题速览
Leetcode: 0001-0010题速览 本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解 遵从开源协议为知识共享 版权归属-相同方式…...
计算机的错误计算(一百一十二)
摘要 计算机的错误计算(六十三)与(六十八)以及(六十九)分别探讨了大数与 附近数以及 附近数 的余切函数的计算精度问题。本节讨论余切序列(即迭代 )的计算精度问题。 余切序列是指…...
C++基础(7)——STL简介及string类
目录 1.STL简介 1.1什么是 1.2STL的历史版本 1.3STL的六大组件 编辑 1.4有用的网址 2.string类 2.1string的多种定义方式 2.2string的插入 2.2.1尾插(push_back) 2.2.2insert插入 2.3拼接(append) 2.4删除 2.4.1尾…...
配置Nginx以支持通过HTTPS回源到CDN
要配置Nginx以支持通过HTTPS回源到CDN,你需要确保Nginx已正确配置SSL,并且能够处理来自CDN的HTTPS请求。以下是一个简化的Nginx配置示例,它配置了SSL并设置了代理服务器参数以回源到CDN: server {listen 443 ssl;server_name you…...
yolov10+strongsort的目标跟踪实现
此次yolov10deepsort不论是准确率还是稳定性,再次超越了之前的yolodeepsort系列。 yolov10介绍——实时端到端物体检测 YOLOv10 是清华大学研究人员在 UltralyticsPython 清华大学的研究人员在 YOLOv10软件包的基础上,引入了一种新的实时目标检测…...
C# 字符与字符串
本课要点: 1、字符类Char的使用 2、字符串类String的使用 3、可变字符串****StringBuilder 4、常见错误 一 何时用到字符与字符串 问题: 输出C#**课考试最高分:**98.5 输出最高分学生姓名:张三 输出最高分学生性别&#x…...
在Ubuntu 16.04上使用LEMP安装WordPress的方法
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 WordPress 是互联网上最流行的 CMS(内容管理系统)。它允许您在 MySQL 后端和 PHP 处理的基础上轻松设置灵…...
显示器放大后,大漠识图识色坐标偏移解决方法
原因分析: 显示器分辨率较高,DPI设置放大125% or 150% or 200%,游戏打开时也会默认会根据显示器的放大比例自行放大,但是大漠综合管理工具抓图不会放大; 解决方法: 1、大漠综合管理…...
C++容器之list基本使用
目录 前言 一、list的介绍? 二、使用 1.list的构造 2.list iterator的使用 3.list capacity 🥇 empty 🥇size 4.list element access 🥇 front 🥇 back 5.list modifiers 🥇 push_front 🥇 po…...
Redis-哨兵
概念 Redis Sentinel 相关名词解释 注意: 哨兵机制不负责存储数据,只是对其它的redis-server进程起到监控的作用哨兵节点,也会搞一个集合,防止一个挂了 ⼈⼯恢复主节点故障 用户监控: 实际开发中,对于服务器后端开发,监控程序,是很重要的 服务器长期运行,总会有一些意外,…...
Pikachu-Sql-Inject - 基于时间的盲注
基于时间的盲注: 就是前端的基于time 的盲注,什么错误信息都看不到,但是还可以通过特定的输入,判断后台的执行时间,从而确定注入。 mysql 里函数sleep() 是延时的意思,sleep(10)就是数据库延时10 秒返回内…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
企业大模型服务合规指南:深度解析备案与登记制度
伴随AI技术的爆炸式发展,尤其是大模型(LLM)在各行各业的深度应用和整合,企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者,还是积极拥抱AI转型的传统企业,在面向公众…...
算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...
相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...
Java中HashMap底层原理深度解析:从数据结构到红黑树优化
一、HashMap概述与核心特性 HashMap作为Java集合框架中最常用的数据结构之一,是基于哈希表的Map接口非同步实现。它允许使用null键和null值(但只能有一个null键),并且不保证映射顺序的恒久不变。与Hashtable相比,Hash…...
