1.amcl的cmakelists.txt文件

add_executable(amcl  src/amcl_node.cpp)

target_link_libraries(amcl

    amcl_sensors amcl_map amcl_pf
${Boost_LIBRARIES}
${catkin_LIBRARIES}
)

该项目生成一个amcl节点;以及amcl_sensors amcl_map amcl_pf三个库

2.amcl node

2.1 类结构

class amcl_node
{
public:
amcl_node();
~amcl_node();
void runFromBag(const std::string &in_bag_fn);//根据信息记录包来运行amcl int process();
void savePoseToServer();////把位姿信息保存到参数服务器 private:
std::shared_ptr<tf2_ros::TransformBroadcaster> tfb_;
std::shared_ptr<tf2_ros::TransformListener> tfl_;
std::shared_ptr<tf2_ros::Buffer> tf_;
bool sent_first_transform_;
tf2::Transform latest_tf_;
bool latest_tf_valid_; static pf_vector_t uniformPoseGenerator(void* arg); static std::vector<std::pair<int, int> > free_space_indices;
// Callbacks
bool globalLocalizationCallback(std_srvs::Empty::Request& req,
std_srvs::Empty::Response& res);
bool nomotionUpdateCallback(std_srvs::Empty::Request& req,
std_srvs::Empty::Response& res);
bool setMapCallback(nav_msgs::SetMap::Request& req,
nav_msgs::SetMap::Response& res); void laserReceived(const sensor_msgs::LaserScanConstPtr& laser_scan);
void initialPoseReceived(const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg);
void handleInitialPoseMessage(const geometry_msgs::PoseWithCovarianceStamped& msg);
void mapReceived(const nav_msgs::OccupancyGridConstPtr& msg); void handleMapMessage(const nav_msgs::OccupancyGrid& msg);
void freeMapDependentMemory();
map_t* convertMap(const nav_msgs::OccupancyGrid& map_msg);
void updatePoseFromServer();
void applyInitialPose(); //parameter for what odom to use
std::string odom_frame_id_; //paramater to store latest odom pose
geometry_msgs::PoseStamped latest_odom_pose_; //parameter for what base to use
std::string base_frame_id_;
std::string global_frame_id_; bool use_map_topic_;
bool first_map_only_; ros::Duration gui_publish_period;
ros::Time save_pose_last_time;
ros::Duration save_pose_period; geometry_msgs::PoseWithCovarianceStamped last_published_pose; map_t* map_;
char* mapdata;
int sx, sy;
double resolution; message_filters::Subscriber<sensor_msgs::LaserScan>* laser_scan_sub_;
tf2_ros::MessageFilter<sensor_msgs::LaserScan>* laser_scan_filter_;
ros::Subscriber initial_pose_sub_;
std::vector< AMCLLaser* > lasers_;
std::vector< bool > lasers_update_;
std::map< std::string, int > frame_to_laser_; // Particle filter
pf_t *pf_;
double pf_err_, pf_z_;
bool pf_init_;
pf_vector_t pf_odom_pose_;
double d_thresh_, a_thresh_;
int resample_interval_;
int resample_count_;
double laser_min_range_;
double laser_max_range_; //Nomotion update control
bool m_force_update; // used to temporarily let amcl update samples even when no motion occurs... AMCLOdom* odom_;
AMCLLaser* laser_; ros::Duration cloud_pub_interval;
ros::Time last_cloud_pub_time; // For slowing play-back when reading directly from a bag file
ros::WallDuration bag_scan_period_; void requestMap();//请求服务static_server提供map,然后调用handleMapMessage处理地图信息 // Helper to get odometric pose from transform system
bool getOdomPose(geometry_msgs::PoseStamped& pose,
double& x, double& y, double& yaw,
const ros::Time& t, const std::string& f); //time for tolerance on the published transform,
//basically defines how long a map->odom transform is good for
ros::Duration transform_tolerance_; ros::NodeHandle nh_;
ros::NodeHandle private_nh_;
ros::Publisher pose_pub_;
ros::Publisher particlecloud_pub_;
ros::ServiceServer global_loc_srv_;
ros::ServiceServer nomotion_update_srv_; //to let amcl update samples without requiring motion
ros::ServiceServer set_map_srv_;
ros::Subscriber initial_pose_sub_old_;
ros::Subscriber map_sub_; amcl_hyp_t* initial_pose_hyp_;
bool first_map_received_;
bool first_reconfigure_call_; boost::recursive_mutex configuration_mutex_;
dynamic_reconfigure::Server<amcl::AMCLConfig> *dsrv_;
amcl::AMCLConfig default_config_;
ros::Timer check_laser_timer_; int max_beams_, min_particles_, max_particles_;
double alpha1_, alpha2_, alpha3_, alpha4_, alpha5_;
double alpha_slow_, alpha_fast_;
double z_hit_, z_short_, z_max_, z_rand_, sigma_hit_, lambda_short_;
//beam skip related params
bool do_beamskip_;
double beam_skip_distance_, beam_skip_threshold_, beam_skip_error_threshold_;
double laser_likelihood_max_dist_;
odom_model_t odom_model_type_;
double init_pose_[];
double init_cov_[];
laser_model_t laser_model_type_;
bool tf_broadcast_; void reconfigureCB(amcl::AMCLConfig &config, uint32_t level); ros::Time last_laser_received_ts_;
ros::Duration laser_check_interval_;
void checkLaserReceived(const ros::TimerEvent& event);
};

2.2 main函数

int main(int argc, char** argv)
{
ros::init(argc, argv, "amcl");
ros::NodeHandle nh; // Override default sigint handler
signal(SIGINT, sigintHandler); // Make our node available to sigintHandler
amcl_node_ptr.reset(new AmclNode()); if (argc == )
{
// run using ROS input
ros::spin();
}
else if ((argc == ) && (std::string(argv[]) == "--run-from-bag"))
{
amcl_node_ptr->runFromBag(argv[]);
} // Without this, our boost locks are not shut down nicely
amcl_node_ptr.reset(); // To quote Morgan, Hooray!
return();
}

2.3 关键步骤

0.构造函数AmclNode()

——>参数配置:粒子滤波参数,运动模型参数,观测模型参数等

——>updatePoseFromServer():从参数服务器中获取初始位姿及初始分布

——>pose和particle息发布:

  1. amcl_pose: geometry_msgs::PoseWithCovarianceStamped,后验位姿+一个6*6的协方差矩阵(xyz+三个转角)
  2. particlecloud:geometry_msgs::PoseArray,粒子位姿的数组

——>创建服务:

  1. global_localization:&AmclNode::globalLocalizationCallback,这里是没有给定初始位姿的情况下在全局范围内初始化粒子位姿,该Callback调用pf_init_model,然后调用AmclNode::uniformPoseGenerator在地图的free点随机生成pf->max_samples个粒子
  2. request_nomotion_update:&AmclNode::nomotionUpdateCallback没运动模型更新的情况下也暂时更新粒子群
  3. set_map:&AmclNode::setMapCallback://handleMapMessage()进行地图转换 ,记录free space ,以及初始化pf_t 结构体,实例化运动模型(odom)和观测模型(laser);          //handleInitialPoseMessage(req.initial_pose); 根据接收的初始位姿消息,在该位姿附近高斯采样重新生成粒子集
  4. dynamic_reconfigure::Server动态参数配置器。

——>订阅话题:

  1. scan_topic_:sensor_msgs::LaserScan,AmclNode::laserReceived():回调函数laserReceived是粒子滤波主要过程,根据激光扫描数据更新粒子
  2. initialpose:AmclNode::initialPoseReceived():这个应该就是订阅rviz中给的初始化位姿,调用AmclNode::handleInitialPoseMessage,只接受global_frame_id_(一般为map)的坐标,并重新生成粒子。在接收到的初始位姿附近采样生成 粒子集。
  3. map:AmclNode::mapReceived这个在use_map_topic_的时候才订阅,否则requestMap();我这里也没有订阅,因为只使用了一个固定的地图。

——>一个15秒的定时器:AmclNode::checkLaserReceived,检查 15上一次收到激光雷达数据至今是否超过15秒,如超过则报错。

1.requestmap()

——>requestMap:一直请求服务static_map直到成功

——>handleMapMessage():  1.将受到的msg转换成标准地图,0->-1(不是障碍);100->+1(障碍);else->0(不明)

              2.提取非障碍部分,列入Vector类型的free_space_indices

              3.创建粒子滤波器——>updatePoseFromServer()——>初始化粒子滤波器——>初始化传感器(odom,laser)——>applyInitialPose()

2.laserReceived()

——>获取laser对应于baselink的坐标

——>获取baselink对应于odom的坐标
——>根据里程计的变化值+高斯噪音 更新 pf_t中samples的内里程计值(运动模型)
odom->updateAction()
——>根据当前雷达数据更新各里程计对应的权值weights
laser_[laser_index]->updateSensor()
——>得到滤波结果后,分别在话题/amcl_pose和/ particlecloud上发布位姿和粒子集

3.主要过程

  • 构造时初始化,从参数服务器中获取数据初始化各类参数;(接收地图设置,gui显示发布频率,保存位姿到参数服务器频率,laser测距范围及其概率模型参数,odom概率模型参数,粒子滤波及kld重采样参数,从参数服务器获取初始位姿,然后初始化了订阅者,发布者,服务)
  • 地图加载,两种方式(1.订阅/map话题2.请求服务得到地图),得到地图后也有个初始化过程(将消息类型的地图转换为定义的map类数据,统计free状态的栅格索引,从参数服务器获取位姿信息,并初始化粒子滤波器pf_,初始化odom模型参数,初始化laser模型参数)
  • 粒子滤波,订阅laser_scan的回调函数中处理,得到结果后发布位姿和粒子集
  • initialpose的回调,接收到初始位姿消息后,融入最新的里程改变,然后在该位姿附近重新生成粒子集

4.主要数据类型与算法

4.1 pf

1. eig3.c

实现的是一个3x3对称矩阵的特征值与特征向量的计算,首先用Householder矩阵将矩阵变换为三对角矩阵,然后使用ql分解迭代计算 。

2. pf_kdtree.c定义了一个kdtree以及维护方法来管理所有粒子 :创建、销毁、清除元素、插入元素、计算概率估计、比较、查找、

typedef struct
{
// Cell size
double size[]; // The root node of the tree
pf_kdtree_node_t *root; // The number of nodes in the tree
int node_count, node_max_count;
pf_kdtree_node_t *nodes; // The number of leaf nodes in the tree
int leaf_count; } pf_kdtree_t;

3.pf_pdf.c主要定义了一个从给定pdf中采样粒子的方法

4.pf_vector.c定义了三维列向量和三维矩阵和基本的运算方法:加、减、全局和局部坐标系变换、是否NAN或INF

5.pf.c定义了粒子单元pf_sample_t,粒子集pf_sample_set_t,粒子滤波pf_t的数据类型,还有一个 pf_cluster_t表示粒子集的聚类信息,关键函数主要包含如下三个,分别对应粒子滤波中的运动更新,观测更新,重采样三个过程

4.2 sensors

1. amcl_sencor.cpp

——>定义了基类,以虚函数InitSensor()、UpdateSensor()、UpdateAction()提供接口

2. amcl_laser.cpp

——>定义了激光数据类型,三种观测更新模型(详细见<<概率机器人>>),具体实现了UpdateSensor,用于计算粒子权值

3. amcl_odom.cpp
——>具体实现了基类定义的UpdateAction函数,用于根据运动更新粒子,定义了两种运动模型,差分和全向

4.3 map

——>map中主要定义了概率栅格地图的数据表示

typedef struct
{
int occ_state;// Occupancy state (-1 = free, 0 = unknown, +1 = occ)
double occ_dist;// Distance to the nearest occupied cell
} map_cell_t;
// Description for a map
typedef struct
{
// Map origin; the map is a viewport onto a conceptual larger map.
double origin_x, origin_y; // Map scale (m/cell)
double scale; // Map dimensions (number of cells)
int size_x, size_y; // The map data, stored as a grid
map_cell_t *cells; // Max distance at which we care about obstacles, for constructing
// likelihood field
double max_occ_dist; } map_t;

部分参考:https://blog.csdn.net/qq_27753669/article/details/80011156

Ros学习——movebase源码解读之amcl的更多相关文章

  1. Ros学习——Movebase源码解读

    1.总体框架 goal global planner-------global_costmap<——map server amcl local planner---------local_cos ...

  2. etcd学习(6)-etcd实现raft源码解读

    etcd中raft实现源码解读 前言 raft实现 看下etcd中的raftexample newRaftNode startRaft serveChannels 领导者选举 启动并初始化node节点 ...

  3. Spark学习之路 (十六)SparkCore的源码解读(二)spark-submit提交脚本

    一.概述 上一篇主要是介绍了spark启动的一些脚本,这篇主要分析一下Spark源码中提交任务脚本的处理逻辑,从spark-submit一步步深入进去看看任务提交的整体流程,首先看一下整体的流程概要图 ...

  4. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  5. SDWebImage源码解读之SDWebImageCache(下)

    第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...

  6. AFNetworking 3.0 源码解读 总结(干货)(上)

    养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...

  7. AFNetworking 3.0 源码解读(十)之 UIActivityIndicatorView/UIRefreshControl/UIImageView + AFNetworking

    我们应该看到过很多类似这样的例子:某个控件拥有加载网络图片的能力.但这究竟是怎么做到的呢?看完这篇文章就明白了. 前言 这篇我们会介绍 AFNetworking 中的3个UIKit中的分类.UIAct ...

  8. AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization

    这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...

  9. AFNetworking 3.0 源码解读 总结

    终于写完了 AFNetworking 的源码解读.这一过程耗时数天.当我回过头又重头到尾的读了一篇,又有所收获.不禁让我想起了当初上学时的种种情景.我们应该对知识进行反复的记忆和理解.下边是我总结的 ...

随机推荐

  1. 【剑指offer】Q14:调整数组顺序使奇数位于偶数前面

    def isOdd(n): return n & 1 def Reorder(data, cf = isOdd): odd = 0 even = len( data ) - 1 while T ...

  2. 性能指标术语&理发店模型

    2015-11-26 09:13:53 响应时间 响应时间=呈现时间+系统响应时间 呈现时间取决于数据在被客户端收后到呈现出页面所消耗的时间: 系统响应时间指应用系统从请求发出开始到客户端接收到数据所 ...

  3. 初始化Lights Out 100(ilo100)密码

    初始化Lights Out 100(ilo100)密码 /HP ProLiant General /HP Software /初始化Lights Out 100(ilo100)密码 2014年5月21 ...

  4. 【转】VC 模式对话框和非模式对话框的创建,销毁和区别

    原文网址:http://blog.csdn.net/mycaibo/article/details/6002151 VC 模式对话框和非模式对话框的创建,销毁和区别   在WIN32中,模式对话框的创 ...

  5. Zookeeper--安装及命令

    Zookeeper--单机模式安装 及命令 下载地址: http://zookeeper.apache.org/releases.html tar -zxvf zookeeper-3.4.10.tar ...

  6. app的apk 安装的方法--adb--命令安装 (含把apk放某个文件夹,每次启动自己安装)

    adb安装 1.在app自动化之前,首先手机上有要被测试的app,如何把电脑本地上的app安装到手机上呢?可以在运行自动化代码前,在cmd输入adb指令,把电脑app安装到手机上 adb instal ...

  7. Java中的三元运算:a = (a > b)?a:b

    格式:逻辑值 ? 表达式1 : 表达式2 执行顺序:先执行逻辑值,如果逻辑值为true,则执行表达式1:反之则执行表达式2 a = (a > b)?a:b 如果a>b成立,返回a: 如果a ...

  8. Java测试用例简介

    最近需要向组内其他成员普及一下关于Java测试用例的相关知识,特在此进行一下简单的学习和总结. JUnit简介 JUnit是一个开源的Java单元测试框架,JUnit4对原有的JUnit框架进行了大幅 ...

  9. 福利向:几款给力的Unity脚本插件推荐

    转自:http://www.gamelook.com.cn/2016/09/264877 Unity的Asset Store中除了拥有非常强大的Unity编辑器扩展工具之外,还有一些让开发过程事半功倍 ...

  10. Django前端的文本编辑器,滑动登陆

    文本编译器 # 添加文章 url(r'^addarticle/$', views.addarticle), # 利用文本编辑器添加文章 def addarticle(request): ''' 添加文 ...