从程序中学习EKF-SLAM(一)

2023-05-16

在一次课程的结课作业上,作业要求复写一个EKF-SLAM系统,我从中学到了好多知识。作为一个典型轻量级slam系统,这个小项目应该特别适合于slam系统入门,可以了解到经典卡尔曼滤波器在slam系统中的应用以及一个slam系统的重要组成部分。下面,我将以原理+代码形式来介绍这个project,如有问题,欢迎讨论;如有错误,欢迎指出。

参考博客:EKF-SLAM原理

它的原代码是用matlab写的,已经上传GitHub:ekf_slm_inMATLAB
它的演示视频也已经上传YouTube:ekf_slmVideo_inMATLAB
我的代码会放在GitHub上,请看最后一篇博客,下面的图片是显示效果。
在这里插入图片描述

一、整体框架

1.代码框架

首先从matlab的系统来说,它的核心分成了两个部分,这两个部分在C++工程中将会形成两个类。

在C++工程中,其中一个就是slam仿真器类,负责完成两个任务:
(1)计算car的真实位姿,收集观测的路标信息;
(2)对系统运行状态进行显示(在RVIZ中显示),协调。
在这里插入图片描述

另一个类就是核心了,扩展卡尔曼滤波器类,它负责两个非常重要的任务:
(1)估计位姿;
(2)更新位姿。
在这里插入图片描述

上面的两张图片里的内容是由各个子函数名构成的,这个类分别管理各自功能所属的子函数。

2.小车car的结构

下图是移动小车car在两个相邻时刻的位姿,其中 θ 1 {\theta}_1 θ1是两相邻时刻移动机器人绕圆弧运动的角度, θ 3 {\theta}_3 θ3是两相邻时刻移动机器航向角(朝向角head)的变化量。 l l l是左右轮之间的间距, d d d是右轮比左轮多走的距离。 r r r 是移动机器人圆弧运动的半径。

在这里插入图片描述

在本系统中,car的位姿是指 ( x , y , θ ) (x, y, \theta) (x,y,θ),它由两个控制量 ( V n , G n ) (V_n, G_n) (Vn,Gn)控制。其中,各个表示含义为:
(1) V n V_n Vn : car的前进线速度大小,在系统中是预先设定好的,此处是3m/s(这个在后面的配置文件里会说明);
(2) G n G_n Gn : car的转向角,用于控制car转向,它由据下一个新的路标点远近情况来决定。
那么,小车的位姿模型可以列成三个等式,如下所示:
在这里插入图片描述
其中, d t dt dt是系统采样时间,此处是0.025秒。

二、程序主流程

1.准备

原matlab程序里有一个参数文件和一个数据文件,这里就统一用两个yaml文件来管理。首先,参数文件应该是最简单的,将原程序中的参数复制粘贴即可:

#%%% Configuration file
#%%% Permits various adjustments to parameters of the SLAM algorithm.
#%%% See ekfslam_sim.m for more information

#% control parameters
V: 3 #; % m/s
MAXG: rad(30*pi/180) #; % radians, maximum steering angle (-MAXG < g < MAXG)
RATEG: rad(20*pi/180) #; % rad/s, maximum rate of change in steer angle
WHEELBASE: 4 #; % metres, vehicle wheel-base
DT_CONTROLS: 0.025 #; % seconds, time interval between control signals控制信号之间的时间间隔

#% control noises
sigmaV: 0.3 #; % m/s
sigmaG: 0.03
#sigmaG: rad(3.0*pi/180) # ; % radians
#Q: [sigmaV^2 0; 0 sigmaG^2] #;

#% observation parameters
MAX_RANGE: 30.0 #; % metres
#DT_OBSERVE: 8*DT_CONTROLS #; % seconds, time interval between observations观测信号之间的间隔

#% observation noises
sigmaR: 0.1 #; % metres
sigmaB: rad(1.0*pi/180) #; % radians
#R: [sigmaR^2 0; 0 sigmaB^2] #;

#% data association innovation gates (Mahalanobis distances)
GATE_REJECT: 4.0 #; % maximum distance for association
GATE_AUGMENT: 25.0 #; % minimum distance for creation of new feature
#GATE_AUGMENT: 15.0 
#% For 2-D observation:
#%   - common gates are: 1-sigma (1.0), 2-sigma (4.0), 3-sigma (9.0), 4-sigma (16.0)
#%   - percent probability mass is: 1-sigma bounds 40%, 2-sigma 86%, 3-sigma 99%, 4-sigma 99.9%.

#% waypoint proximity
AT_WAYPOINT: 1.0 #; % metres, distance from current waypoint at which to switch to next waypoint
NUMBER_LOOPS: 2 #; % number of loops through the waypoint list

#% switches
SWITCH_CONTROL_NOISE: 1 #; % if 0, velocity and gamma are perfect
SWITCH_SENSOR_NOISE: 1 #; % if 0, measurements are perfect
SWITCH_INFLATE_NOISE: 0 #; % if 1, the estimated Q and R are inflated (ie, add stabilising noise)
SWITCH_HEADING_KNOWN: 0 #; % if 1, the vehicle heading is observed directly at each iteration
SWITCH_ASSOCIATION_KNOWN: 0 #; % if 1, associations are given, if 0, they are estimated using gates
SWITCH_BATCH_UPDATE: 1 #; % if 1, process scan in batch, if 0, process sequentially
SWITCH_SEED_RANDOM: 0 #; % if not 0, seed the randn() with its value at beginning of simulation (for repeatability)
SWITCH_USE_IEKF: 0 #; % if 1, use iterated EKF for updates, if 0, use normal EKF

而这里的数据文件的读取有一点复杂,需要注意一下。它的yaml写法如下:

#保存路标点landmarks和路径点waypoints
#set of landmarks
lm: [[2.9922, -25.7009], [32.8988, -33.1776], [24.7991, -68.3801], [75.2664, -65.5763], [73.7087, -35.6698],
       [98.6308, 3.8941],  [49.4097, 26.9470], [80.2508, 59.9688], [54.7056, 88.6293], [13.2726, 80.8411],
       [-16.3224, 49.0654], [-65.8551, 83.6449], [-96.3847, 60.5919], [-76.1335, 36.2928], [-87.3505, -21.3396],
       [-103.5498, -32.2430], [-92.9579, -77.7259], [-55.8863, -55.9190], [-35.3255, -19.1589], [-56.1978, 16.3551],
       [-7.2882, 19.7819], [25.7336, 3.2710], [-19.5610, 80.1444], [-41.7506, 46.2325], [25.4021, 26.8543],
       [91.3870, 28.2385], [-13.7216, -79.0338], [-52.8454, -92.1833], [-86.1298, 15.0890], [-127.0053, 22.7018],
       [-25.9843, 4.7078], [56.3508, -17.4388], [51.6793, -83.1863], [21.31446, -96.3358], [48.1757, 57.9979]]

#set of waypoints
wp: [[12.6496, -41.588], [44.7368, -54.9844], [85.5467, -45.0156], [93.6464, -17.2897], [64.9860, 5.7632],
       [71.8396, 31.6199], [71.2165, 70.5607], [33.5218, 76.4798], [12.9611, 51.5576], [-32.5218, 67.4455],
       [-74.8894, 69.0031], [-97.3193, 41.9003], [-107.5997, 6.3863], [-86.4159, -25.7009], [-83.9237, -64.0187],
       [-39.3754, -81.4642], [-17.5685, -51.5576]]

程序里用一种特殊的数据类型进行转换,这里就只贴出landmark的坐标读取程序了。使用方法看懂了之后其实非常简单,照着学,学着用就行了。

//获取路标点以及行驶点
double dimension=1.0;
XmlRpc::XmlRpcValue landmarks_xmlrpc;
XmlRpc::XmlRpcValue waypoints_xmlrpc;
//获取参数的值  
handle.getParam("/ekf_simulator/lm", landmarks_xmlrpc);
handle.getParam("/ekf_simulator/wp", waypoints_xmlrpc);

if (landmarks_xmlrpc.getType() == XmlRpc::XmlRpcValue::TypeArray)
{
	landmarks_size = landmarks_xmlrpc.size();
	std::cout<<"We obtained "<<landmarks_size<<" landmark points......"<<std::endl;
	landmarks.resize(2,  landmarks_size);
	landmarks.setZero(2,  landmarks_size);
	for(int i=0 ; i < landmarks_size ; i++)
	{
		XmlRpc::XmlRpcValue point = landmarks_xmlrpc[i];
		landmarks(0, i)=double(point[0])/dimension;
		landmarks(1, i)=double(point[1])/dimension;
	}
	ROS_INFO("The landmarks data is recorded !");
}else ROS_ERROR("The dimension of landmarks arry is wrong !");

其中,需要注意的就只有三条语句(步骤就是两步,一是取出每一个点,二是之后进行数据类型的强制转换即可):

XmlRpc::XmlRpcValue point = landmarks_xmlrpc[i];
landmarks(0, i)=double(point[0])/dimension;
landmarks(1, i)=double(point[1])/dimension;

2.主程序

这是用C++写的run函数,就是整个系统的主流程。

void slam_simulator::run()
{//main loop
	//% stored data for off-line
	ekf_cal->initialise_store();
	while((iwp !=-1)&&(ros::ok()))
	{
		//compute true data
		compute_steering();
		//perform loops: if final waypoint reached, go back to first
		if((iwp==-1)&&(number_loops>1))
		{
			iwp=0;
			number_loops=number_loops-1;
		}
		ekf_cal->vehicle_model(velocity, G, dt);
		ekf_cal->add_control_noise(velocity, G, switch_control_noise);	//默认添加噪声
		ekf_cal->predict(dt);
		//ekf_cal->ukf_predict(dt);

		//if heading known, observe heading
		//ekf_cal->observe_heading(switch_heading_know);			//默认此函数不用,转向未知

		//EKF update step
		dtsum= dtsum + dt;
		if(dtsum>=dt_observe)
		{
			dtsum=0;
			get_observations();
			ekf_cal->add_observation_noise(switch_sensor_noise);
			if(switch_association_know == 1)
				ekf_cal->data_associate_known(ftag_visible);			//已知数据关联,路标观测顺序已知
			else ekf_cal->data_associate(gate_reject, gate_augment);		//默认进程

			if(switch_use_Iekf == 1)
				ekf_cal->update_iekf(5);
			else ekf_cal->update(switch_batch_update);				//默认进程
			//ekf_cal->ukf_update(switch_batch_update);

		}
		
		//offline data store
		ekf_cal->store_data();
		viz_state();
		viz_dataAssociation();
		odometry_publisher();
		viz_robotpose();
		//r.sleep();
	}
}

后面我将会以程序的调用顺序,分别讲解每一个子函数,然后解释其中的原理,这样来学习ukf。

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

从程序中学习EKF-SLAM(一) 的相关文章

  • SLAM方法汇总

    原文 http blog csdn net smartxxyx article details 53068855 目录 SLAM概述 SLAM一般处理流程包括track和map两部分 所谓的track是用来估计相机的位姿 也叫front e
  • slam数学基础——最小二乘

    一 前言 1 最小二乘是一类特殊形式的最优化求解问题 相比于其他优化问题 求解方式比较简洁 2 最小二乘被广泛应用于各种实际问题的建模 在求解实际工程问题中有着广泛的应用 例如 slam 中随处可见最小二乘的声影 二 线性最小二乘法 1 预
  • SLAM入门

    SLAM定义 SLAM Simultaneous localization and mapping 同时定位 我在哪里 与建图 我周围有什么 当某种移动设备 汽车 扫地机 手机 无人机 机器人 从一个未知环境的未知地点出发 在运动过程中 通
  • 各向异性(anisotropic)浅提

    文章目录 各向异性 anisotropic 定义 哪种物体具有各向异性反射 什么导致各向异性反射 总结 各向异性 anisotropic 定义 它指一种存在方向依赖性 这意味着在不同的方向不同的特性 相对于该属性各向同性 当沿不同轴测量时
  • 【SLAM】卡尔曼滤波(Kalman Filter)

    卡尔曼滤波 Kalman filter 一种利用线性系统状态方程 通过系统输入输出观测数据 对系统状态进行最优估计的算法 由于观测数据中包括系统中的噪声和干扰的影响 所以最优估计也可看作是滤波过程 卡尔曼滤波器的原理解释如下 首先 我们先要
  • Sophus使用记录

    sophus库是一个基于Eigen的C 李群李代数库 可以用来方便地进行李群李代数的运算 头文件 主要用到以下两个头文件 include
  • rtabmap安装与使用

    参考 ubuntu18 04安装Rtabmap 具体详细步骤 教你手把手运行基于ZED的rtab map ZED入门 利用RTAB MAP做SLAM ubuntu16 04 ROS Kinetic rtabmap 源码 非ros版本 安装运
  • 从零开始一起学习SLAM(9)不推公式,如何真正理解对极约束?

    文章目录 对极几何基本概念 如何得到极线方程 作业 此文发于公众号 计算机视觉life 原文链接 从零开始一起学习SLAM 不推公式 如何真正理解对极约束 自从小白向师兄学习了李群李代数和相机成像模型的基本原理后 感觉书上的内容没那么难了
  • 视觉SLAM漫谈(二):图优化理论与g2o的使用

    视觉SLAM漫谈 二 图优化理论与g2o的使用 1 前言以及回顾 各位朋友 自从上一篇 视觉SLAM漫谈 写成以来已经有一段时间了 我收到几位热心读者的邮件 有的希望我介绍一下当前视觉SLAM程序的实用程度 更多的人希望了解一下前文提到的g
  • 单目视觉里程记代码

    在Github上发现了一个简单的单目vo 有接近500星 链接如下 https github com avisingh599 mono vo 这个单目里程计主要依靠opencv实现 提取fast角点并进行光流跟踪 然后求取本质矩阵并恢复两帧
  • 【SLAM】libQGLViewer:VS 2019 + Qt 5.14.2 + Win 10 配置

    libQGLViewer 2 7 2 VS 2019 Qt 5 14 2 Win 10 配置 注意 这次配置没有完全成功 编译25个成功 一个失败 失败的是 qglviewerplugin qglviewerplugin 是一个可选控件 不
  • 图像匹配算法

    图像匹配算法分为3类 基于灰度的匹配算法 基于特征的匹配算法 基于关系的匹配算法 1 基于灰度的模板匹配算法 模板匹配 Blocking Matching 是根据已知模板图像到另一幅图像中寻找与模板图像相似的子图像 基于灰度的匹配算法也称作
  • 关于GPS、惯导、视觉里程计的几个定义

    1 首先写几个定义 惯性导航系统 Inertial Navigation System INS 全球定位卫星系统 Global Navigation Satellite System GNSS GNSS 包括全球定位系统 Global Po
  • Sophus安装踩坑

    装SLAM十四讲第二版提供的Sophus Eigen版本3 4 0 报错 home ch 下载 Sophus 13fb3288311485dc94e3226b69c9b59cd06ff94e test core test so2 cpp 9
  • SLAM-hector_slam 简介与使用

    hector slam功能包使用高斯牛顿方法 不需要里程计数据 只根据激光信息便可构建地图 所以他的总体框架如下 hector slam功能包 hector slam的核心节点是hector mapping 它订阅 scan 话题以获取SL
  • 高斯牛顿法求非线性最小二乘的步骤和c++代码实现

    slam图优化的本质是一个非线性优化问题 Gauss Newton求解步骤 1 线性化误差函数 2 构建线性系统 3 求解线性系统 4 更新解 并不断迭代直至收敛 一个简单的代码实现 一维参数xy 高维变为对应的矩阵即可 include
  • lego-LOAM跑自己的数据包无法显示全局点云地图解决(速腾聚创RS-LiDAR-16 雷达 )---SLAM不学无术小问题

    LeGo LOAM跑自己的数据包无法显示全局地图问题 注意 本文笔者使用环境 Ubuntu18 04 ROS melodic 版本 背景 3D SLAM新手 在看到了各种狂拽炫酷的3D点云图的之后决定亲自上手一试 首先当然的是最为经典的LO
  • SLAM练习题(十一)—— G2O实战

    SLAM 学习笔记 写在前面的话 算是一点小小的感悟吧 估计位姿的方法有线性方法和非线性方法 线性方法就是特征点法中的2D 2D的对极约束 3D 2D的PnP问题 非线性方法有BA优化 它将位姿的估计问题转换成了一个误差关于优化量的最小二乘
  • LIO-SAM运行自己数据包遇到的问题解决--SLAM不学无数术小问题

    LIO SAM 成功适配自己数据集 注意本文测试环境 Ubuntu18 04 ROS melodic版本 笔者用到的硬件以简单参数 激光雷达 速腾聚创16线激光雷达 RS Lidar 16 IMU 超核电子CH110型 9轴惯导 使用频率1
  • 3.Open3D教程——点云数据操作

    点云数据 本教程阐述了基本的点云用法 随需要的文件链接 1 显示点云 import open3d as o3d import numpy as np print Load a ply point cloud print it and ren

随机推荐