28 #include <boost/bind.hpp> 29 #include <boost/thread/mutex.hpp> 45 #include "sensor_msgs/LaserScan.h" 46 #include "geometry_msgs/PoseWithCovarianceStamped.h" 47 #include "geometry_msgs/PoseArray.h" 48 #include "geometry_msgs/Pose.h" 49 #include "nav_msgs/GetMap.h" 50 #include "nav_msgs/SetMap.h" 51 #include "std_srvs/Empty.h" 61 #include "dynamic_reconfigure/server.h" 62 #include "amcl/AMCLConfig.h" 67 #include <boost/foreach.hpp> 69 #define NEW_UNIFORM_SAMPLING 1 90 return atan2(sin(z),cos(z));
99 d2 = 2*M_PI - fabs(d1);
102 if(fabs(d1) < fabs(d2))
119 void runFromBag(
const std::string &in_bag_fn);
122 void savePoseToServer();
142 static pf_vector_t uniformPoseGenerator(
void* arg);
143 #if NEW_UNIFORM_SAMPLING 147 bool globalLocalizationCallback(std_srvs::Empty::Request& req,
148 std_srvs::Empty::Response& res);
149 bool nomotionUpdateCallback(std_srvs::Empty::Request& req,
150 std_srvs::Empty::Response& res);
151 bool setMapCallback(nav_msgs::SetMap::Request& req,
152 nav_msgs::SetMap::Response& res);
154 void laserReceived(
const sensor_msgs::LaserScanConstPtr& laser_scan);
155 void initialPoseReceived(
const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg);
156 void handleInitialPoseMessage(
const geometry_msgs::PoseWithCovarianceStamped& msg);
157 void mapReceived(
const nav_msgs::OccupancyGridConstPtr& msg);
159 void handleMapMessage(
const nav_msgs::OccupancyGrid& msg);
160 void freeMapDependentMemory();
161 map_t* convertMap(
const nav_msgs::OccupancyGrid& map_msg );
162 void updatePoseFromServer();
163 void applyInitialPose();
225 double& x,
double& y,
double& yaw,
226 const ros::Time& t,
const std::string& f);
247 dynamic_reconfigure::Server<amcl::AMCLConfig> *
dsrv_;
252 double alpha1_, alpha2_, alpha3_, alpha4_,
alpha5_;
254 double z_hit_,
z_short_, z_max_, z_rand_, sigma_hit_, lambda_short_;
260 double init_pose_[3];
265 void reconfigureCB(amcl::AMCLConfig &config, uint32_t level);
274 #define USAGE "USAGE: amcl" 281 amcl_node_ptr->savePoseToServer();
295 amcl_node_ptr.reset(
new AmclNode());
302 else if ((argc == 3) && (std::string(argv[1]) ==
"--run-from-bag"))
304 amcl_node_ptr->runFromBag(argv[2]);
308 amcl_node_ptr.reset();
315 sent_first_transform_(false),
316 latest_tf_valid_(false),
323 initial_pose_hyp_(NULL),
324 first_map_received_(false),
325 first_reconfigure_call_(true)
364 std::string tmp_model_type;
365 private_nh_.
param(
"laser_model_type", tmp_model_type, std::string(
"likelihood_field"));
366 if(tmp_model_type ==
"beam")
368 else if(tmp_model_type ==
"likelihood_field")
370 else if(tmp_model_type ==
"likelihood_field_prob"){
375 ROS_WARN(
"Unknown laser model type \"%s\"; defaulting to likelihood_field model",
376 tmp_model_type.c_str());
381 if(tmp_model_type ==
"diff")
383 else if(tmp_model_type ==
"omni")
385 else if(tmp_model_type ==
"diff-corrected")
387 else if(tmp_model_type ==
"omni-corrected")
391 ROS_WARN(
"Unknown odom model type \"%s\"; defaulting to diff model",
392 tmp_model_type.c_str());
411 double bag_scan_period;
442 ROS_INFO(
"Subscribed to map topic.");
449 dynamic_reconfigure::Server<amcl::AMCLConfig>::CallbackType cb = boost::bind(&
AmclNode::reconfigureCB,
this, _1, _2);
450 dsrv_->setCallback(cb);
471 if(config.restore_defaults) {
474 config.restore_defaults =
false;
497 z_hit_ = config.laser_z_hit;
499 z_max_ = config.laser_z_max;
505 if(config.laser_model_type ==
"beam")
507 else if(config.laser_model_type ==
"likelihood_field")
509 else if(config.laser_model_type ==
"likelihood_field_prob")
512 if(config.odom_model_type ==
"diff")
514 else if(config.odom_model_type ==
"omni")
516 else if(config.odom_model_type ==
"diff-corrected")
518 else if(config.odom_model_type ==
"omni-corrected")
521 if(config.min_particles > config.max_particles)
523 ROS_WARN(
"You've set min_particles to be greater than max particles, this isn't allowed so they'll be set to be equal.");
524 config.max_particles = config.min_particles;
542 pf_z_ = config.kld_z;
555 pf_init(
pf_, pf_init_pose_mean, pf_init_pose_cov);
572 ROS_INFO(
"Initializing likelihood field model; this can take some time on large maps...");
577 ROS_INFO(
"Done initializing likelihood field model with probabilities.");
580 ROS_INFO(
"Initializing likelihood field model; this can take some time on large maps...");
583 ROS_INFO(
"Done initializing likelihood field model.");
607 std::vector<std::string> topics;
608 topics.push_back(std::string(
"tf"));
609 std::string scan_topic_name =
"base_scan";
610 topics.push_back(scan_topic_name);
646 tf2_msgs::TFMessage::ConstPtr tf_msg = msg.
instantiate<tf2_msgs::TFMessage>();
650 for (
size_t ii=0; ii<tf_msg->transforms.size(); ++ii)
657 sensor_msgs::LaserScan::ConstPtr base_scan = msg.
instantiate<sensor_msgs::LaserScan>();
658 if (base_scan != NULL)
675 ROS_INFO(
"Bag complete, took %.1f seconds to process, shutting down", runtime);
678 double yaw, pitch, roll;
680 ROS_INFO(
"Final location %.3f, %.3f, %.3f with stamp=%f",
696 double yaw,pitch,roll;
719 init_cov_[2] = (M_PI/12.0) * (M_PI/12.0);
723 if(!std::isnan(tmp_pos))
726 ROS_WARN(
"ignoring NAN in initial pose X position");
728 if(!std::isnan(tmp_pos))
731 ROS_WARN(
"ignoring NAN in initial pose Y position");
733 if(!std::isnan(tmp_pos))
736 ROS_WARN(
"ignoring NAN in initial pose Yaw");
738 if(!std::isnan(tmp_pos))
741 ROS_WARN(
"ignoring NAN in initial covariance XX");
743 if(!std::isnan(tmp_pos))
746 ROS_WARN(
"ignoring NAN in initial covariance YY");
748 if(!std::isnan(tmp_pos))
751 ROS_WARN(
"ignoring NAN in initial covariance AA");
760 ROS_WARN(
"No laser scan received (and thus no pose updates have been published) for %f seconds. Verify that data is being published on the %s topic.",
772 nav_msgs::GetMap::Request req;
773 nav_msgs::GetMap::Response resp;
777 ROS_WARN(
"Request for map failed; trying again...");
801 ROS_INFO(
"Received a %d X %d map @ %.3f m/pix\n",
804 msg.info.resolution);
807 ROS_WARN(
"Frame_id of map received:'%s' doesn't match global_frame_id:'%s'. This could cause issues with reading published topics",
808 msg.header.frame_id.c_str(),
820 #if NEW_UNIFORM_SAMPLING 846 pf_init(
pf_, pf_init_pose_mean, pf_init_pose_cov);
863 ROS_INFO(
"Initializing likelihood field model; this can take some time on large maps...");
868 ROS_INFO(
"Done initializing likelihood field model.");
872 ROS_INFO(
"Initializing likelihood field model; this can take some time on large maps...");
875 ROS_INFO(
"Done initializing likelihood field model.");
911 map->
size_x = map_msg.info.width;
912 map->
size_y = map_msg.info.height;
913 map->
scale = map_msg.info.resolution;
921 if(map_msg.data[i] == 0)
923 else if(map_msg.data[i] == 100)
945 double& x,
double& y,
double& yaw,
946 const ros::Time& t,
const std::string& f)
957 ROS_WARN(
"Failed to compute odom pose, skipping scan (%s)", e.what());
960 x = odom_pose.getOrigin().x();
961 y = odom_pose.getOrigin().y();
963 odom_pose.getBasis().getEulerYPR(yaw, pitch, roll);
973 #if NEW_UNIFORM_SAMPLING 977 p.
v[0] =
MAP_WXGX(map, free_point.first);
978 p.
v[1] =
MAP_WYGY(map, free_point.second);
979 p.
v[2] = drand48() * 2 * M_PI - M_PI;
981 double min_x, max_x, min_y, max_y;
990 ROS_DEBUG(
"Generating new uniform sample");
993 p.
v[0] = min_x + drand48() * (max_x - min_x);
994 p.
v[1] = min_y + drand48() * (max_y - min_y);
995 p.
v[2] = drand48() * 2 * M_PI - M_PI;
1009 std_srvs::Empty::Response& res)
1011 if(
map_ == NULL ) {
1015 ROS_INFO(
"Initializing with uniform distribution");
1018 ROS_INFO(
"Global initialisation done!");
1026 std_srvs::Empty::Response& res)
1035 nav_msgs::SetMap::Response& res)
1047 if(
map_ == NULL ) {
1051 int laser_index = -1;
1056 ROS_DEBUG(
"Setting up laser %d (frame_id=%s)\n", (
int)
frame_to_laser_.size(), laser_scan->header.frame_id.c_str());
1063 ros::Time(), laser_scan->header.frame_id);
1071 ROS_ERROR(
"Couldn't transform from %s to %s, " 1072 "even though the message notifier is in use",
1073 laser_scan->header.frame_id.c_str(),
1079 laser_pose_v.
v[0] = laser_pose.getOrigin().x();
1080 laser_pose_v.
v[1] = laser_pose.getOrigin().y();
1082 laser_pose_v.
v[2] = 0;
1083 lasers_[laser_index]->SetLaserPose(laser_pose_v);
1084 ROS_DEBUG(
"Received laser's pose wrt robot: %.3f %.3f %.3f",
1100 ROS_ERROR(
"Couldn't determine robot's pose associated with laser scan");
1128 bool force_publication =
false;
1141 force_publication =
true;
1156 odata.
delta = delta;
1165 bool resampled =
false;
1178 q.
setRPY(0.0, 0.0, laser_scan->angle_min);
1180 laser_scan->header.frame_id);
1181 q.
setRPY(0.0, 0.0, laser_scan->angle_min + laser_scan->angle_increment);
1183 laser_scan->header.frame_id);
1191 ROS_WARN(
"Unable to transform min/max laser angles into base frame: %s",
1197 double angle_increment =
tf::getYaw(inc_q) - angle_min;
1200 angle_increment = fmod(angle_increment + 5*M_PI, 2*M_PI) - M_PI;
1202 ROS_DEBUG(
"Laser %d angles in base frame: min: %.3f inc: %.3f", laser_index, angle_min, angle_increment);
1208 ldata.
range_max = laser_scan->range_max;
1213 range_min = laser_scan->range_min;
1221 if(laser_scan->ranges[i] <= range_min)
1224 ldata.
ranges[i][0] = laser_scan->ranges[i];
1226 ldata.
ranges[i][1] = angle_min +
1227 (i * angle_increment);
1249 geometry_msgs::PoseArray cloud_msg;
1252 cloud_msg.poses.resize(
set->sample_count);
1253 for(
int i=0;i<
set->sample_count;i++)
1257 set->samples[i].pose.v[1], 0)),
1258 cloud_msg.poses[i]);
1264 if(resampled || force_publication)
1267 double max_weight = 0.0;
1268 int max_weight_hyp = -1;
1269 std::vector<amcl_hyp_t> hyps;
1271 for(
int hyp_count = 0;
1279 ROS_ERROR(
"Couldn't get stats on cluster %d", hyp_count);
1283 hyps[hyp_count].weight = weight;
1284 hyps[hyp_count].pf_pose_mean = pose_mean;
1285 hyps[hyp_count].pf_pose_cov = pose_cov;
1287 if(hyps[hyp_count].weight > max_weight)
1289 max_weight = hyps[hyp_count].weight;
1290 max_weight_hyp = hyp_count;
1294 if(max_weight > 0.0)
1296 ROS_DEBUG(
"Max weight pose: %.3f %.3f %.3f",
1297 hyps[max_weight_hyp].pf_pose_mean.v[0],
1298 hyps[max_weight_hyp].pf_pose_mean.v[1],
1299 hyps[max_weight_hyp].pf_pose_mean.v[2]);
1307 geometry_msgs::PoseWithCovarianceStamped p;
1310 p.header.stamp = laser_scan->header.stamp;
1312 p.pose.pose.position.x = hyps[max_weight_hyp].pf_pose_mean.v[0];
1313 p.pose.pose.position.y = hyps[max_weight_hyp].pf_pose_mean.v[1];
1315 p.pose.pose.orientation);
1318 for(
int i=0; i<2; i++)
1320 for(
int j=0; j<2; j++)
1325 p.pose.covariance[6*i+j] =
set->cov.m[i][j];
1331 p.pose.covariance[6*5+5] =
set->cov.m[2][2];
1346 ROS_DEBUG(
"New pose: %6.3f %6.3f %6.3f",
1347 hyps[max_weight_hyp].pf_pose_mean.v[0],
1348 hyps[max_weight_hyp].pf_pose_mean.v[1],
1349 hyps[max_weight_hyp].pf_pose_mean.v[2]);
1356 tf::Vector3(hyps[max_weight_hyp].pf_pose_mean.v[0],
1357 hyps[max_weight_hyp].pf_pose_mean.v[1],
1360 laser_scan->header.stamp,
1368 ROS_DEBUG(
"Failed to subtract base to odom transform");
1380 ros::Time transform_expiration = (laser_scan->header.stamp +
1383 transform_expiration,
1400 ros::Time transform_expiration = (laser_scan->header.stamp +
1403 transform_expiration,
1423 double yaw, pitch, roll;
1438 if(msg.header.frame_id ==
"")
1441 ROS_WARN(
"Received initial pose with empty frame_id. You should always supply a frame_id.");
1446 ROS_WARN(
"Ignoring initial pose in frame \"%s\"; initial poses must be in the global frame, \"%s\"",
1447 msg.header.frame_id.c_str(),
1473 ROS_WARN(
"Failed to transform initial pose in time (%s)", e.what());
1479 pose_new = pose_old * tx_odom;
1483 ROS_INFO(
"Setting pose (%.6f): %.3f %.3f %.3f",
1490 pf_init_pose_mean.v[0] = pose_new.
getOrigin().
x();
1491 pf_init_pose_mean.v[1] = pose_new.
getOrigin().
y();
1492 pf_init_pose_mean.v[2] =
getYaw(pose_new);
1495 for(
int i=0; i<2; i++)
1497 for(
int j=0; j<2; j++)
1499 pf_init_pose_cov.
m[i][j] = msg.pose.covariance[6*i+j];
1502 pf_init_pose_cov.
m[2][2] = msg.pose.covariance[6*5+5];
ros::ServiceServer set_map_srv_
bool globalLocalizationCallback(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
double beam_skip_distance_
static pf_vector_t uniformPoseGenerator(void *arg)
ros::Time last_laser_received_ts_
double beam_skip_error_threshold_
boost::shared_ptr< AmclNode > amcl_node_ptr
static void poseMsgToTF(const geometry_msgs::Pose &msg, Pose &bt)
void publish(const boost::shared_ptr< M > &message) const
std::string global_frame_id_
dynamic_reconfigure::Server< amcl::AMCLConfig > * dsrv_
void open(std::string const &filename, uint32_t mode=bagmode::Read)
ros::Subscriber initial_pose_sub_
std::map< std::string, int > frame_to_laser_
#define MAP_VALID(map, i, j)
bool setTransform(const geometry_msgs::TransformStamped &transform, const std::string &authority, bool is_static=false)
ros::Publisher particlecloud_pub_
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
pf_vector_t(* pf_init_model_fn_t)(void *init_data)
void getEulerYPR(tfScalar &yaw, tfScalar &pitch, tfScalar &roll, unsigned int solution_number=1) const
void add(const MEvent &evt)
static double getYaw(const Quaternion &bt_q)
void checkLaserReceived(const ros::TimerEvent &event)
bool call(const std::string &service_name, MReq &req, MRes &res)
std::string base_frame_id_
static double normalize(double z)
void mapReceived(const nav_msgs::OccupancyGridConstPtr &msg)
pf_vector_t pf_vector_zero()
double beam_skip_threshold_
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
pf_t * pf_alloc(int min_samples, int max_samples, double alpha_slow, double alpha_fast, pf_init_model_fn_t random_pose_fn, void *random_pose_data)
tf::TransformBroadcaster * tfb_
#define MAP_INDEX(map, i, j)
ROSCPP_DECL std::string resolve(const std::string &name, bool remap=true)
ros::Time last_cloud_pub_time
void updatePoseFromServer()
int main(int argc, char **argv)
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
void sigintHandler(int sig)
void SetModelBeam(double z_hit, double z_short, double z_max, double z_rand, double sigma_hit, double labda_short, double chi_outlier)
virtual bool UpdateAction(pf_t *pf, AMCLSensorData *data)
odom_model_t odom_model_type_
void runFromBag(const std::string &in_bag_fn)
Uses TF and LaserScan messages from bag file to drive AMCL instead.
static tf::Quaternion createIdentityQuaternion()
TransformListenerWrapper * tf_
tf::MessageFilter< sensor_msgs::LaserScan > * laser_scan_filter_
ROSCPP_DECL void spin(Spinner &spinner)
void SetModel(odom_model_t type, double alpha1, double alpha2, double alpha3, double alpha4, double alpha5=0)
void pf_init(pf_t *pf, pf_vector_t mean, pf_matrix_t cov)
void update(const std::string &key, const XmlRpc::XmlRpcValue &v)
std::vector< AMCLLaser * > lasers_
int pf_get_cluster_stats(pf_t *pf, int cluster, double *weight, pf_vector_t *mean, pf_matrix_t *cov)
ROSCPP_DECL CallbackQueue * getGlobalCallbackQueue()
TFSIMD_FORCE_INLINE const tfScalar & x() const
ros::Subscriber initial_pose_sub_old_
ROSCPP_DECL void set(const std::string &key, const XmlRpc::XmlRpcValue &v)
ros::ServiceServer nomotion_update_srv_
double getYaw(tf::Pose &t)
std::string odom_frame_id_
bool first_reconfigure_call_
static Quaternion createQuaternionFromYaw(double yaw)
bool getOdomPose(tf::Stamped< tf::Pose > &pose, double &x, double &y, double &yaw, const ros::Time &t, const std::string &f)
void setRPY(const tfScalar &roll, const tfScalar &pitch, const tfScalar &yaw)
Duration & fromSec(double t)
void handleMapMessage(const nav_msgs::OccupancyGrid &msg)
std::vector< bool > lasers_update_
bool param(const std::string ¶m_name, T ¶m_val, const T &default_val) const
static double angle_diff(double a, double b)
static std::vector< std::pair< int, int > > free_space_indices
ros::Duration cloud_pub_interval
static void quaternionTFToMsg(const Quaternion &bt, geometry_msgs::Quaternion &msg)
boost::shared_ptr< T > instantiate() const
pf_matrix_t pf_matrix_zero()
TFSIMD_FORCE_INLINE const tfScalar & y() const
void laserReceived(const sensor_msgs::LaserScanConstPtr &laser_scan)
amcl_hyp_t * initial_pose_hyp_
ros::Duration transform_tolerance_
ros::Time save_pose_last_time
double getYaw(const tf2::Quaternion &q)
map_t * convertMap(const nav_msgs::OccupancyGrid &map_msg)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void SetModelLikelihoodField(double z_hit, double z_rand, double sigma_hit, double max_occ_dist)
bool sent_first_transform_
void pf_init_model(pf_t *pf, pf_init_model_fn_t init_fn, void *init_data)
ros::WallDuration bag_scan_period_
static const std::string scan_topic_
#define ROS_WARN_STREAM(args)
ros::ServiceServer global_loc_srv_
ros::Timer check_laser_timer_
geometry_msgs::PoseWithCovarianceStamped last_published_pose
boost::recursive_mutex configuration_mutex_
static void poseTFToMsg(const Pose &bt, geometry_msgs::Pose &msg)
void subscribe(ros::NodeHandle &nh, const std::string &topic, uint32_t queue_size, const ros::TransportHints &transport_hints=ros::TransportHints(), ros::CallbackQueueInterface *callback_queue=0)
void pf_update_resample(pf_t *pf)
void map_free(map_t *map)
void freeMapDependentMemory()
ros::Duration gui_publish_period
std::string const & getTopic() const
laser_model_t laser_model_type_
amcl::AMCLConfig default_config_
ros::Duration save_pose_period
bool nomotionUpdateCallback(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
ROSCPP_DECL void shutdown()
void reconfigureCB(amcl::AMCLConfig &config, uint32_t level)
ros::NodeHandle private_nh_
message_filters::Subscriber< sensor_msgs::LaserScan > * laser_scan_sub_
void handleInitialPoseMessage(const geometry_msgs::PoseWithCovarianceStamped &msg)
ros::Duration laser_check_interval_
tf::Stamped< tf::Pose > latest_odom_pose_
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const
void SetModelLikelihoodFieldProb(double z_hit, double z_rand, double sigma_hit, double max_occ_dist, bool do_beamskip, double beam_skip_distance, double beam_skip_threshold, double beam_skip_error_threshold)
double laser_likelihood_max_dist_
pf_vector_t pf_odom_pose_
bool setMapCallback(nav_msgs::SetMap::Request &req, nav_msgs::SetMap::Response &res)
void initialPoseReceived(const geometry_msgs::PoseWithCovarianceStampedConstPtr &msg)
Connection registerCallback(const C &callback)