gmaping原理及代码解析(三)

本文详细解析了GMAPPING中的里程计运动模型和扫描匹配模型。在里程计模型中,介绍了如何将里程计数据转换并加入噪声来模拟真实情况。在扫描匹配模型部分,阐述了scanMatch函数通过优化寻找最优位姿的过程,以及score函数如何计算匹配程度。此外,文章还提到了开源GMAPPING实现与论文方法的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(三)gmapping详细代码解析


A、里程计运动模型

我们先来看一下Gmapping里面实现的里程计模型代码:

/*
@desc 里程计运动模型
@p    表示粒子估计的最优位置(机器人上一个时刻的最优位置)
@pnew 表示里程计算出来的新的位置
@pold 表示里程计算出来的旧的位置(即上一个里程计的位置)
*/
OrientedPoint 
MotionModel::drawFromMotion(const OrientedPoint& p, const OrientedPoint& pnew, const OrientedPoint& pold) const{
	double sxy=0.3*srr;

	OrientedPoint delta=absoluteDifference(pnew, pold); // 计算新位姿相对于旧位姿的偏移量
	
	/*初始化一个点*/
	OrientedPoint noisypoint(delta);
	
	/*走过的X、y、z轴方向的距离加入噪声*/
	noisypoint.x+=sampleGaussian(srr*fabs(delta.x)+str*fabs(delta.theta)+sxy*fabs(delta.y));
	noisypoint.y+=sampleGaussian(srr*fabs(delta.y)+str*fabs(delta.theta)+sxy*fabs(delta.x));
	noisypoint.theta+=sampleGaussian(stt*fabs(delta.theta)+srt*sqrt(delta.x*delta.x+delta.y*delta.y));
	
	/*限制角度的范围*/
	noisypoint.theta=fmod(noisypoint.theta, 2*M_PI);
	if (noisypoint.theta>M_PI)
		noisypoint.theta-=2*M_PI;
	
	/*把加入了噪声的值 加到粒子估计的最优的位置上  即得到新的位置(根据运动模型推算出来的位置)*/
	return absoluteSum(p,noisypoint);
}
  • OrientPoint点的结构:

/*带方向的点  即除了位置之外,还有角度,可以理解为机器人的位姿*/
template <class T, class A>
struct orientedpoint: public point<T>
{
  inline orientedpoint() : point<T>(0,0), theta(0) {};
	inline orientedpoint(const point<T>& p);
	inline orientedpoint(T x, T y, A _theta): point<T>(x,y), theta(_theta){}
        inline void normalize();

    inline orientedpoint<T,A> rotate(A alpha)
    {
		T s=sin(alpha), c=cos(alpha);
		A a=alpha+theta;
		a=atan2(sin(a),cos(a));
		return orientedpoint(
			c*this->x-s*this->y,
			s*this->x+c*this->y, 
			a);
	}
	A theta;
};
  • absoluteDifference函数:

absoluteDifference()这个函数是将t时刻与t-1时刻里程计测量数据之差转换到t-1时刻的车辆坐标系下,计算位姿差

/*
@desc 两个位姿的差值,算出来P1在以P2为原点的坐标系里面的坐标。
*/
template <class T, class A>
orientedpoint<T,A> absoluteDifference(const orientedpoint<T,A>& p1,const orientedpoint<T,A>& p2)
{
	orientedpoint<T,A> delta=p1-p2;
	delta.theta=atan2(sin(delta.theta), cos(delta.theta));
	double s=sin(p2.theta), c=cos(p2.theta);
	return orientedpoint<T,A>(c*delta.x+s*delta.y, 
	                         -s*delta.x+c*delta.y, delta.theta);
}
  • sampleGaussian函数(这里我把源代码中的注释给删掉了):
double sampleGaussian(double sigma, unsigned int S) {
	if (S!=0)
        {
                srand(S);
        }
	if (sigma==0)
		return 0;
	return pf_ran_gaussian (sigma);
}

sampleGaussian函数里面的pf_ran_gaussian()函数,从方差为sigma,均值为0的高斯分布中抽取随机数,

参考:https://www.taygeta.com/random/gaussian.html(没太懂):

double pf_ran_gaussian(double sigma)
{
  double x1, x2, w;
  double r;

  do
  {
    do { r = drand48(); } while (r == 0.0);
    x1 = 2.0 * r - 1.0;
    do { r = drand48(); } while (r == 0.0);
    x2 = 2.0 * drand48() - 1.0;
    w = x1*x1 + x2*x2;
  } while(w > 1.0 || w==0.0);

  return(sigma * x2 * sqrt(-2.0*log(w)/w));
}
  • fod(x,y)是返回x/y的余数,将角度限制到(-pi,pi)之间

gmapping的里程计采样模型采取的方法是《概率机器人》里的里程计运动模型,具体如下:

  • 运动模型示意图:

伪代码:

(1)2-4行表示将t时刻数据转换到t-1时刻的车辆坐标系下

(2)5-6行给新的偏移量加入一个包含噪声的偏移值,噪声是一个高斯采样值

(3)8-9行把得到的新的偏移量转换到没有噪声的车辆坐标下,再叠加t-1时刻的位姿,得到t时刻的位姿估计

控制量ut就是里程计给出的信息,,其中

是里程计信息在t-1时刻的位姿,是里程计在t时刻位姿,因为里程计是机器人内部的传感器,它计算的是机器人一点都没噪声的理想状态时的行进距离,所以由于机器人在行进的时候的偏移和打滑,这个是不准确的,所以在5~6行我们加入一定的噪声来表示机器人实际行进的“真”值。


B、scan-match模型

先上代码,gmapping里的函数scanMatch(),函数位于gridslamprocessor.hxx中;

来看在scanMatch中调用第一个比较重要的函数---optimize函数,位于scanmatcher.cpp中:

double ScanMatcher::optimize(OrientedPoint& pnew, const ScanMatcherMap& map, const OrientedPoint& init, const double* readings) const
{
    double bestScore=-1;
    /*计算当前位置的得分*/
    OrientedPoint currentPose=init;
    double currentScore=score(map, currentPose, readings);
	
    /*所有时的步进增量*/
    double adelta=m_optAngularDelta, ldelta=m_optLinearDelta;
	
    /*精确搜索的次数*/
    unsigned int refinement=0;
	
    /*搜索的方向*/
    enum Move{Front, Back, Left, Right, TurnLeft, TurnRight, Done};
    //enum Move{Front, Back, Left, Right, TurnLeft, TurnRight, Done};
    int c_iterations=0;
    do
    {
        /*如果这一次(currentScore)算出来比上一次(bestScore)差,则有可能是走太多了,要减少搜索步长 这个策略跟LM有点像*/
        if (bestScore>=currentScore)
### 关于 `test_gmapping` 文件的相关信息 #### ROS中的GMapping包概述 在ROS(机器人操作系统)环境中,`gmapping` 是一个用于构建二维占用栅格地图的开源算法。此节点订阅激光雷达数据并将其转换成环境的地图表示形式[^1]。 #### 测试 GMapping 的配置方法 为了测试 gmapping 节点的功能,通常会创建名为 `test_gmapping.launch` 或者类似的启动文件来加载必要的参数设置以及初始化各个所需的节点。这些参数可能包括但不限于: - 激光扫描话题名称 (`scan_topic`) - 地图更新频率 (`odom_frame`, `base_frame`, 和 `map_update_interval`) - 占用概率阈值(`minimum_score`, `linearUpdate`, `angularUpdate`) ```xml <launch> <!-- 启动gmapping节点 --> <node pkg="gmapping" type="slam_gmapping" name="gmapping_node"> <param name="scan_topic" value="/scan"/> <param name="odom_frame" value="odom"/> <param name="base_frame" value="base_footprint"/> <param name="map_update_interval" value="2.0"/> ... </node> </launch> ``` 上述 XML 片段展示了如何通过 launch 文件指定一些基本选项给 gmapping 节点。 #### 数据记录与回放功能的应用场景 对于调试目的而言,在实际运行过程中收集传感器读数和其他相关信息到 bag 文件里是非常有用的实践方式之一。之后可以通过命令行工具 rosbag 来重播录制的数据流以便离线分析 gmapping 表现情况而不必每次都依赖真实世界实验条件。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值