gmapping An Incomplete Scan Matching Tutorial

本文档提供了一种基于最大似然估计的简单激光雷达地图构建方法。首先介绍了基本概念及符号定义,随后详细阐述了如何根据已知里程计数据构建占用网格地图,并提出了一种基于梯度下降的简单扫描匹配算法。

gmapping 说明文档:

   源自:  https://svn.openslam.org/data/svn/gmapping/trunk/docs/scanmatcher.tex



letex在线编译  

\documentclass[a4paper]{article}
\begin{document}
\title {An Incomplete Scan Matching Tutorial}
\author{Giorgio Grisetti}
\maketitle
In this document we provide an explanation of the basic concepts for developing a
simple maximum likelihood mapper for laser data.
We first introduce the notation, then we describe how to compute an occupancy grid map, given a known set od poses. Subsequantly we present a simple gradient based scan matchgin algorithm.

\section{Notation}
\begin{itemize}
\item $x_t$: robot pose at time $t$.
\item $z_t=\underbrace{(z^1_t,\cdots,z^n_t)}_\mathrm{beams}$: 
scan taken at time $t$.
\item $u_t$: odometry movement which brings the robot from $x_{t-1}$ to $x_{t}$, $t$.
\item $m_t=f_t(x,y)\rightarrow[0,1]$ occupancy grid map which can be seen as a function that maps each point in the probability of being occupied.
\end{itemize}

\section{The Scan Matching Problem}
A scan matching algorithm works in two steps:
\begin{itemize}
\item it determines the most likely pose, given the actual measurements and the previously build best map:
\begin{equation}
\hat x_t = \mathop{argmax}_{x_t}(p(x_t|z_t, \hat x_{t-1}, m_{t-1}, u_t))
\nonumber
\end{equation}
\item it computes the next step map given the previously built one, the corrected pose and the range reading:
\begin{equation}
p(m_t|m_{t-1}, \hat x_t, \hat z_t)
\nonumber
\end{equation}
\end{itemize}
In the following we present two simple approaches for performing these two steps.

\section{Frequancy Based Occupancy grids}
An occupancy grid is a discrete world representation. It describes the world as a matrix, whose cells represents the probability of being occupied. The cells are considered independent.
Here we present a simple algorithm for updating an occupancy grid, based on a frequentist approach.

For each cell of the map $m^{x,y}$ we keep two numbers: the number of times that cell has been visited $v^{x,y}$, and the number of times that cell has been found occupied $b^{x,y}$.

Let $m_{t-1}$ be the map at the previous time step, $x_t$ the robot pose at time $t$ and $z_t=(z^1_t,\cdots,z^n_t)$ the reading.
We first consider the range reading translated according to the current robot pose
\begin{equation}
\hat z_t=(\hat z^1_t,\cdots, \hat z^n_t)=\hat x_t \oplus z_t
\end{equation}
Each beam $z^i_t$ can be described with its endpoint $\hat p^i_t$.
The cells in the map spanned by such a beam lies on the segment $(\hat x_t, \hat p^i_t)$. All the cells wich are before th endpoint are detected as free, while the endpoint is occupied.

The we can incrementally update the cells of the map for each beam, in the following way:
\begin{equation}
m^{x,y}_t=(b^{x,y}_t,v^{x,y}_t)=
\left\{
\begin{array}{cc}
(b^{x,y}_t+1,v^{x,y}_t+1) & \mathrm{if\;occupied}\\
(b^{x,y}_t,v^{x,y}_t+1) & \mathrm{if\;free}
\end{array}
\right.
\end{equation}
The probability that a cell is pccupied is
\begin{equation}
p(m^{x,y}_t)=\frac{b^{x,y}_t}{v^{x,y}_t}
\end{equation}

\newpage
\section{A simple gradient descent scan matcher}
Here we describe the simple scamnmatcher you find in this software bundle.
The basic principle of this algorithm is to perform a gradinent descent search,
on a score function $s(x,z,m)$ of the pose, th reading and the map.
The overall algorithm works as follows:
\begin{itemize}
\item $bestPose=x_\mathrm{init}$
\item $bestScore=s(bestPose,z,m)$
\item $searchStep=initialSearchStep$
\item $iterations=0$
\item while (!$iterations<maxIterations$)
	\begin{itemize}
	\item $maxMoveScore=bestScore$
	\item $bestMovePose=bestPose$
	\item for $move$ in ($Backward, Forward, Left, Right, RotateLeft, RotatrRight$)
		\begin{itemize}
		\item  $testPose=computePose(bestPose, move)$
		\item  $score=s(testPose,z,m)$
		\item if ($maxMoveScore<score$)
			\begin{itemize}
			\item $maxMoveScore=score$
			\item $bestMovePose=testPose$
			\end{itemize}
		\end{itemize}
	\item if ($bestScore<maxMoveScore$)
		\begin{itemize}
		\item $bestScore<maxMoveScore$
		\item $bestPose=bestMovePose$
		\end{itemize}
	\item else 
		\begin{itemize}
		\item $searchStep=searchStep/2$
		\item $iterations++$
		\end{itemize}
	\end{itemize}
\end{itemize}

Here a critical issue is how to compute the score function.
A simple approach is to consider a score function as  the sum of the score of the single beams score.
\begin{equation}
s(x,z,m)=\sum_i s(x,z^i,m)
\nonumber
\end{equation}
Our choice is to use a simple endpoint based model. Given a pose $x$ and a reading we consider individually the single beams $z^i$. 
\begin{itemize}
\item We compute the cell of the map in which the beam endpoint falls. This can be done by translating the endpoint according to $x$ as  $\hat z^i= x \oplus z^i$
\item We compute the cell of the map in which th endpoint falls.
\item We found the busy cell $\hat m^{x,y}$ in the map which is closest to $\hat z^i$.
\item We compute the score as a function which decreases with the increase of the distance among $\hat z^i$ and $(x,y)^T$. The squared distance $d^2$ can be computed as $d^2=(\hat z^i- (x,y)^T))^T\cdot(\hat z^i- (x,y)^T))$
\begin{equation}
s(x,z^i,m) = e^{d^2/\sigma}
\nonumber
\end{equation}
\end{itemize}
As an additional optimization for each map cell $m^{x,y}$ we can consider as its center the center of mass of the points falling in that cell.
\end{document}





在使用 `QImage` 时遇到的 **“incomplete type” 编译错误**,通常是因为编译器无法识别 `QImage` 类型的完整定义,导致无法正确进行变量声明或操作。这类问题在 C++ 项目中尤其常见,尤其是在使用了前向声明(forward declaration)或未包含必要头文件的情况下。 ### 常见原因及解决方案 #### 1. 缺少头文件 确保在使用 `QImage` 的源文件中包含了 `<QImage>` 头文件。这是最常见的修复方式之一。如果未正确包含头文件,编译器将无法识别 `QImage` 的完整类型定义,从而导致“incomplete type”错误。 ```cpp #include <QImage> ``` #### 2. 不正确的前向声明 如果在头文件中使用了前向声明,例如: ```cpp class QImage; ``` 而没有在对应的 `.cpp` 文件中包含完整的头文件,则在使用 `QImage` 对象时(如定义变量、调用成员函数等),会触发“incomplete type”错误。应避免对 `QImage` 使用前向声明,或确保在实际使用前包含完整定义。 #### 3. 指针与引用的使用 如果仅使用 `QImage` 的指针或引用,理论上可以通过前向声明完成。但一旦涉及对象构造、调用成员函数或访问成员变量,就必须包含完整的头文件。 例如,以下代码可以仅使用前向声明: ```cpp class QImage; void processImage(QImage* image); // 合法:仅涉及指针声明 ``` 但以下代码则必须包含头文件: ```cpp QImage image; // 需要完整类型定义 image.load("image.png"); ``` #### 4. 构建配置问题 有时项目构建配置不正确也会导致此类错误。例如: - 未正确链接 Qt 的图像模块(`QT += gui` 或 `QT += widgets`)。 - 使用了错误的编译器或 Qt 版本。 - 构建系统缓存未清理,导致旧的头文件未被更新。 确保 `.pro` 文件中包含以下内容(根据使用情况): ```qmake QT += core gui widgets ``` #### 5. 命名空间或宏定义冲突 在某些情况下,项目中可能定义了与 `QImage` 相关的宏或命名空间别名,导致类型定义不一致。检查是否在全局或局部作用域中存在类似以下代码: ```cpp #define QImage MyImage ``` 或者: ```cpp using MyImage = QImage; ``` 这些可能会干扰编译器对 `QImage` 的解析。 #### 6. 使用了不兼容的 Qt 模块 `QImage` 属于 `QtGui` 模块。如果项目未正确引入该模块,即使包含了头文件也可能导致编译错误。确保在 `.pro` 文件中包含: ```qmake QT += gui ``` ### 示例修复代码 假设在 `.h` 文件中有如下声明: ```cpp #ifndef MYCLASS_H #define MYCLASS_H class QImage; // 错误的前向声明 class MyClass { public: void setImage(QImage image); // 错误:使用不完整类型 private: QImage m_image; // 错误:成员变量使用不完整类型 }; #endif // MYCLASS_H ``` 修复方式是在 `.cpp` 文件中包含完整的头文件: ```cpp #include "myclass.h" #include <QImage> void MyClass::setImage(QImage image) { m_image = image; } ``` 同时修改头文件,避免前向声明: ```cpp #ifndef MYCLASS_H #define MYCLASS_H #include <QImage> class MyClass { public: void setImage(QImage image); private: QImage m_image; }; #endif // MYCLASS_H ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值