首页 > Udacity机器人软件工程师课程笔记(三十三) - 蒙特卡洛定位算法(MCL)

Udacity机器人软件工程师课程笔记(三十三) - 蒙特卡洛定位算法(MCL)

一、概述

之前的文章介绍过卡尔曼滤波算法进行定位,我们知道kalman算法适合用于线性的高斯分布的状态环境中,我们也介绍了EKF,来解决在非高斯和非线性环境下的机器人定位算法。但是他们在现实应用中存在计算量,内存消耗上不是很高效。这就引出了MCL算法。

粒子滤波很粗浅的说就是一开始在地图空间很均匀的撒一把粒子,然后通过获取机器人的motion来移动粒子,比如机器人向前移动了一米,所有的粒子也就向前移动一米,不管现在这个粒子的位置对不对。使用每个粒子所处位置模拟一个传感器信息跟观察到的传感器信息(一般是激光)作对比,从而赋给每个粒子一个概率。之后根据生成的概率来重新生成粒子,概率越高的生成的概率越大。这样的迭代之后,所有的粒子会慢慢地收敛到一起,机器人的确切位置也就被推算出来了

MCL的计算步骤:

  1. 随机产生一堆粒子:粒子可以有位置、方向和/或任何其他需要估计的状态变量。每一个都有一个权值(概率),表明它与系统的实际状态匹配的可能性有多大。用相同的权重初始化每个变量。
  2. 预测粒子的下一个状态:根据真实系统行为的预测来移动粒子。
  3. 更新:根据测量结果更新粒子的权重。与测量值密切匹配的粒子的权重要高于与测量值不太匹配的粒子。
  4. 重新采样:抛弃高度不可能的粒子,代之以更可能的粒子的副本。
  5. 计算估计值:可选地,计算粒子集的加权平均值和协方差来得到状态估计。

粒子滤波的基本步骤为上面所述的5步,其本质是使用一组有限的加权随机样本(粒子)来近似表征任意状态的后验概率密度。粒子滤波的优势在于对复杂问题的求解上,比如高度的非线性、非高斯动态系统的状态递推估计或概率推理问题。

此部分转自知乎专栏

二、MCL算法

在这里插入图片描述

蒙特卡罗定位算法的伪代码如上图所示,其由两个主要部分组成 由两个for循环表示。第一个部分是运动和传感器更新,第二个是重采样过程。

若给出一张环境地图,MCL的目标是确定由可信度代表的机器人姿态。在每次迭代中 算法都采用之前的可信度作为启动命令 ,传感器测量作为输入。

最初,可信度是通过随机生成m个粒子获得的。然后 ,在第一个for循环中,假设的状态是在机器人移动时计算出来的。接下来 ,用新的传感器测量值计算粒子的权重,然后更新每一个粒子的状态。

在MCL的第二个循环中进行重采样,在这里 具有高概率的粒子存活下来并在下一次迭代中被重新绘制出来,低概率粒子则被抛弃。

最后 算法输出新的可信度,然后启动新的迭代,通过读取新的传感器测量值来实现下一个运动。

三、C++代码实现

以下是基础代码,我们需要在mian中完成相关程序

#define _USE_MATH_DEFINES
//#include "src/matplotlibcpp.h"//Graph Library
#include 
#include 
#include 
#include 
#include  // throw errors
#include  //C++ 11 Random Numbers//namespace plt = matplotlibcpp;
using namespace std;// Landmarks
double landmarks[8][2] = {  {  20.0, 20.0 }, {  20.0, 80.0 }, {  20.0, 50.0 },{  50.0, 20.0 }, {  50.0, 80.0 }, {  80.0, 80.0 },{  80.0, 20.0 }, {  80.0, 50.0 } };// Map size in meters
double world_size = 100.0;// Random Generators
random_device rd;
mt19937 gen(rd());// Global Functions
double mod(double first_term, double second_term);
double gen_real_random();class Robot { 
public:Robot(){ // Constructorx = gen_real_random() * world_size; // robot's x coordinatey = gen_real_random() * world_size; // robot's y coordinateorient = gen_real_random() * 2.0 * M_PI; // robot's orientationforward_noise = 0.0; //noise of the forward movementturn_noise = 0.0; //noise of the turnsense_noise = 0.0; //noise of the sensing}void set(double new_x, double new_y, double new_orient){ // Set robot new position and orientationif (new_x < 0 || new_x >= world_size)throw std::invalid_argument("X coordinate out of bound");if (new_y < 0 || new_y >= world_size)throw std::invalid_argument("Y coordinate out of bound");if (new_orient < 0 || new_orient >= 2 * M_PI)throw std::invalid_argument("Orientation must be in [0..2pi]");x = new_x;y = new_y;orient = new_orient;}void set_noise(double new_forward_noise, double new_turn_noise, double new_sense_noise){ // Simulate noise, often useful in particle filtersforward_noise = new_forward_noise;turn_noise = new_turn_noise;sense_noise = new_sense_noise;}vector<double> sense(){ // Measure the distances from the robot toward the landmarksvector<double> z(sizeof(landmarks) / sizeof(landmarks[0]));double dist;for (int i = 0; i < sizeof(landmarks) / sizeof(landmarks[0]); i++) { dist = sqrt(pow((x - landmarks[i][0]), 2) + pow((y - landmarks[i][1]), 2));dist += gen_gauss_random(0.0, sense_noise);z[i] = dist;}return z;}Robot move(double turn, double forward){ if (forward < 0)throw std::invalid_argument("Robot cannot move backward");// turn, and add randomness to the turning commandorient = orient + turn + gen_gauss_random(0.0, turn_noise);orient = mod(orient, 2 * M_PI);// move, and add randomness to the motion commanddouble dist = forward + gen_gauss_random(0.0, forward_noise);x = x + (cos(orient) * dist);y = y + (sin(orient) * dist);// cyclic truncatex = mod(x, world_size);y = mod(y, world_size);// set particleRobot res;res.set(x, y, orient);res.set_noise(forward_noise, turn_noise, sense_noise);return res;}string show_pose(){ // Returns the robot current position and orientation in a string formatreturn "[x=" + to_string(x) + " y=" + to_string(y) + " orient=" + to_string(orient) + "]";}string read_sensors(){ // Returns all the distances from the robot toward the landmarksvector<double> z = sense();string readings = "[";for (int i = 0; i < z.size(); i++) { readings += to_string(z[i]) + " ";}readings[readings.size() - 1] = ']';return readings;}double measurement_prob(vector<double> measurement){ // Calculates how likely a measurement should bedouble prob = 1.0;double dist;for (int i = 0; i < sizeof(landmarks) / sizeof(landmarks[0]); i++) { dist = sqrt(pow((x - landmarks[i][0]), 2) + pow((y - landmarks[i][1]), 2));prob *= gaussian(dist, sense_noise, measurement[i]);}return prob;}double x, y, orient; //robot posesdouble forward_noise, turn_noise, sense_noise; //robot noisesprivate:double gen_gauss_random(double mean, double variance){ // Gaussian randomnormal_distribution<double> gauss_dist(mean, variance);return gauss_dist(gen);}double gaussian(double mu, double sigma, double x){ // Probability of x for 1-dim Gaussian with mean mu and var. sigmareturn exp(-(pow((mu - x), 2)) / (pow(sigma, 2)) / 2.0) / sqrt(2.0 * M_PI * (pow(sigma, 2)));}
};// Functions
double gen_real_random()
{ // Generate real random between 0 and 1uniform_real_distribution<double> real_dist(0.0, 1.0); //Realreturn real_dist(gen);
}double mod(double first_term, double second_term)
{ // Compute the modulusreturn first_term - (second_term)*floor(first_term / (second_term));
}double evaluation(Robot r, Robot p[], int n)
{ //Calculate the mean error of the systemdouble sum = 0.0;for (int i = 0; i < n; i++) { //the second part is because of world's cyclicitydouble dx = mod((p[i].x - r.x + (world_size / 2.0)), world_size) - (world_size / 2.0);double dy = mod((p[i].y - r.y + (world_size / 2.0)), world_size) - (world_size / 2.0);double err = sqrt(pow(dx, 2) + pow(dy, 2));sum += err;}return sum / n;
}
double max(double arr[], int n)
{ // Identify the max element in an arraydouble max = 0;for (int i = 0; i < n; i++) { if (arr[i] > max)max = arr[i];}return max;
}int main()
{ ..
                

更多相关:

  • Qt默认的QSlider和QSpinbox只能实现整数调整,不能实现浮点的变化,因此设计了如下可实现浮点变化的QFloatSlider和QFloatSpinner: QFloatSlider.h class QFloatSlider : public QSlider {Q_OBJECTpublic:QFloatSlider(QWi...

  • 1.精度问题 由于是double类型,r=mid 而不是r=mid-12.如果首位两端(f(0)和f(100))同号,证明解不在[1,100]区间内 这是我之所以TE的原因,没有预先判断3.若在这个区间内,则一定可要求出解 所以binarysearch 返回m#include #include ...

  • 代理(Proxy)模式给某一个对象提供一个代理,并由代理对象控制对原对象的引用。 代理模式的英文叫做Proxy或Surrogate,中文都可译成"代理"。所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 类图:...

  • 题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=41&page=show_problem&problem=1121 题意:给出两点坐标,用一条最短的线(曲线或者直线)连接起来,坐标系中原点处有一个半径为R的圆,连线不能...