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))
122 void runFromBag(
const std::string &in_bag_fn,
bool trigger_global_localization =
false);
125 void savePoseToServer();
145 static pf_vector_t uniformPoseGenerator(
void* arg);
146 #if NEW_UNIFORM_SAMPLING 150 bool globalLocalizationCallback(std_srvs::Empty::Request& req,
151 std_srvs::Empty::Response& res);
152 bool nomotionUpdateCallback(std_srvs::Empty::Request& req,
153 std_srvs::Empty::Response& res);
154 bool setMapCallback(nav_msgs::SetMap::Request& req,
155 nav_msgs::SetMap::Response& res);
157 void laserReceived(
const sensor_msgs::LaserScanConstPtr& laser_scan);
158 void initialPoseReceived(
const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg);
159 void handleInitialPoseMessage(
const geometry_msgs::PoseWithCovarianceStamped& msg);
160 void mapReceived(
const nav_msgs::OccupancyGridConstPtr& msg);
162 void handleMapMessage(
const nav_msgs::OccupancyGrid& msg);
163 void freeMapDependentMemory();
164 map_t* convertMap(
const nav_msgs::OccupancyGrid& map_msg );
165 void updatePoseFromServer();
166 void applyInitialPose();
228 double& x,
double& y,
double& yaw,
229 const ros::Time& t,
const std::string& f);
250 dynamic_reconfigure::Server<amcl::AMCLConfig> *
dsrv_;
255 double alpha1_, alpha2_, alpha3_, alpha4_,
alpha5_;
257 double z_hit_,
z_short_, z_max_, z_rand_, sigma_hit_, lambda_short_;
263 double init_pose_[3];
269 void reconfigureCB(amcl::AMCLConfig &config, uint32_t level);
276 #if NEW_UNIFORM_SAMPLING 280 #define USAGE "USAGE: amcl" 287 amcl_node_ptr->savePoseToServer();
301 amcl_node_ptr.reset(
new AmclNode());
308 else if ((argc >= 3) && (std::string(argv[1]) ==
"--run-from-bag"))
312 amcl_node_ptr->runFromBag(argv[2]);
314 else if ((argc == 4) && (std::string(argv[3]) ==
"--global-localization"))
316 amcl_node_ptr->runFromBag(argv[2],
true);
321 amcl_node_ptr.reset();
328 sent_first_transform_(false),
329 latest_tf_valid_(false),
336 initial_pose_hyp_(NULL),
337 first_map_received_(false),
338 first_reconfigure_call_(true)
384 std::string tmp_model_type;
385 private_nh_.
param(
"laser_model_type", tmp_model_type, std::string(
"likelihood_field"));
386 if(tmp_model_type ==
"beam")
388 else if(tmp_model_type ==
"likelihood_field")
390 else if(tmp_model_type ==
"likelihood_field_prob"){
395 ROS_WARN(
"Unknown laser model type \"%s\"; defaulting to likelihood_field model",
396 tmp_model_type.c_str());
401 if(tmp_model_type ==
"diff")
403 else if(tmp_model_type ==
"omni")
405 else if(tmp_model_type ==
"diff-corrected")
407 else if(tmp_model_type ==
"omni-corrected")
411 ROS_WARN(
"Unknown odom model type \"%s\"; defaulting to diff model",
412 tmp_model_type.c_str());
432 double bag_scan_period;
463 ROS_INFO(
"Subscribed to map topic.");
470 dynamic_reconfigure::Server<amcl::AMCLConfig>::CallbackType cb = boost::bind(&
AmclNode::reconfigureCB,
this, _1, _2);
471 dsrv_->setCallback(cb);
492 if(config.restore_defaults) {
495 config.restore_defaults =
false;
518 z_hit_ = config.laser_z_hit;
520 z_max_ = config.laser_z_max;
526 if(config.laser_model_type ==
"beam")
528 else if(config.laser_model_type ==
"likelihood_field")
530 else if(config.laser_model_type ==
"likelihood_field_prob")
533 if(config.odom_model_type ==
"diff")
535 else if(config.odom_model_type ==
"omni")
537 else if(config.odom_model_type ==
"diff-corrected")
539 else if(config.odom_model_type ==
"omni-corrected")
542 if(config.min_particles > config.max_particles)
544 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.");
545 config.max_particles = config.min_particles;
564 pf_z_ = config.kld_z;
577 pf_init(
pf_, pf_init_pose_mean, pf_init_pose_cov);
594 ROS_INFO(
"Initializing likelihood field model; this can take some time on large maps...");
599 ROS_INFO(
"Done initializing likelihood field model with probabilities.");
602 ROS_INFO(
"Initializing likelihood field model; this can take some time on large maps...");
605 ROS_INFO(
"Done initializing likelihood field model.");
629 std::vector<std::string> topics;
630 topics.push_back(std::string(
"tf"));
631 std::string scan_topic_name =
"base_scan";
632 topics.push_back(scan_topic_name);
658 if (trigger_global_localization)
660 std_srvs::Empty empty_srv;
674 tf2_msgs::TFMessage::ConstPtr tf_msg = msg.
instantiate<tf2_msgs::TFMessage>();
678 for (
size_t ii=0; ii<tf_msg->transforms.size(); ++ii)
685 sensor_msgs::LaserScan::ConstPtr base_scan = msg.
instantiate<sensor_msgs::LaserScan>();
686 if (base_scan != NULL)
703 ROS_INFO(
"Bag complete, took %.1f seconds to process, shutting down", runtime);
706 double yaw, pitch, roll;
708 ROS_INFO(
"Final location %.3f, %.3f, %.3f with stamp=%f",
724 double yaw,pitch,roll;
747 init_cov_[2] = (M_PI/12.0) * (M_PI/12.0);
751 if(!std::isnan(tmp_pos))
754 ROS_WARN(
"ignoring NAN in initial pose X position");
756 if(!std::isnan(tmp_pos))
759 ROS_WARN(
"ignoring NAN in initial pose Y position");
761 if(!std::isnan(tmp_pos))
764 ROS_WARN(
"ignoring NAN in initial pose Yaw");
766 if(!std::isnan(tmp_pos))
769 ROS_WARN(
"ignoring NAN in initial covariance XX");
771 if(!std::isnan(tmp_pos))
774 ROS_WARN(
"ignoring NAN in initial covariance YY");
776 if(!std::isnan(tmp_pos))
779 ROS_WARN(
"ignoring NAN in initial covariance AA");
788 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.",
800 nav_msgs::GetMap::Request req;
801 nav_msgs::GetMap::Response resp;
805 ROS_WARN(
"Request for map failed; trying again...");
829 ROS_INFO(
"Received a %d X %d map @ %.3f m/pix\n",
832 msg.info.resolution);
835 ROS_WARN(
"Frame_id of map received:'%s' doesn't match global_frame_id:'%s'. This could cause issues with reading published topics",
836 msg.header.frame_id.c_str(),
848 #if NEW_UNIFORM_SAMPLING 875 pf_init(
pf_, pf_init_pose_mean, pf_init_pose_cov);
892 ROS_INFO(
"Initializing likelihood field model; this can take some time on large maps...");
897 ROS_INFO(
"Done initializing likelihood field model.");
901 ROS_INFO(
"Initializing likelihood field model; this can take some time on large maps...");
904 ROS_INFO(
"Done initializing likelihood field model.");
940 map->
size_x = map_msg.info.width;
941 map->
size_y = map_msg.info.height;
942 map->
scale = map_msg.info.resolution;
950 if(map_msg.data[i] == 0)
952 else if(map_msg.data[i] == 100)
974 double& x,
double& y,
double& yaw,
975 const ros::Time& t,
const std::string& f)
986 ROS_WARN(
"Failed to compute odom pose, skipping scan (%s)", e.what());
989 x = odom_pose.getOrigin().x();
990 y = odom_pose.getOrigin().y();
992 odom_pose.getBasis().getEulerYPR(yaw, pitch, roll);
1002 #if NEW_UNIFORM_SAMPLING 1006 p.
v[0] =
MAP_WXGX(map, free_point.first);
1007 p.
v[1] =
MAP_WYGY(map, free_point.second);
1008 p.
v[2] = drand48() * 2 * M_PI - M_PI;
1010 double min_x, max_x, min_y, max_y;
1019 ROS_DEBUG(
"Generating new uniform sample");
1022 p.
v[0] = min_x + drand48() * (max_x - min_x);
1023 p.
v[1] = min_y + drand48() * (max_y - min_y);
1024 p.
v[2] = drand48() * 2 * M_PI - M_PI;
1038 std_srvs::Empty::Response& res)
1040 if(
map_ == NULL ) {
1044 ROS_INFO(
"Initializing with uniform distribution");
1047 ROS_INFO(
"Global initialisation done!");
1055 std_srvs::Empty::Response& res)
1064 nav_msgs::SetMap::Response& res)
1076 if(
map_ == NULL ) {
1080 int laser_index = -1;
1085 ROS_DEBUG(
"Setting up laser %d (frame_id=%s)\n", (
int)
frame_to_laser_.size(), laser_scan->header.frame_id.c_str());
1092 ros::Time(), laser_scan->header.frame_id);
1100 ROS_ERROR(
"Couldn't transform from %s to %s, " 1101 "even though the message notifier is in use",
1102 laser_scan->header.frame_id.c_str(),
1108 laser_pose_v.
v[0] = laser_pose.getOrigin().x();
1109 laser_pose_v.
v[1] = laser_pose.getOrigin().y();
1111 laser_pose_v.
v[2] = 0;
1112 lasers_[laser_index]->SetLaserPose(laser_pose_v);
1113 ROS_DEBUG(
"Received laser's pose wrt robot: %.3f %.3f %.3f",
1129 ROS_ERROR(
"Couldn't determine robot's pose associated with laser scan");
1157 bool force_publication =
false;
1170 force_publication =
true;
1185 odata.
delta = delta;
1194 bool resampled =
false;
1207 q.
setRPY(0.0, 0.0, laser_scan->angle_min);
1209 laser_scan->header.frame_id);
1210 q.
setRPY(0.0, 0.0, laser_scan->angle_min + laser_scan->angle_increment);
1212 laser_scan->header.frame_id);
1220 ROS_WARN(
"Unable to transform min/max laser angles into base frame: %s",
1226 double angle_increment =
tf::getYaw(inc_q) - angle_min;
1229 angle_increment = fmod(angle_increment + 5*M_PI, 2*M_PI) - M_PI;
1231 ROS_DEBUG(
"Laser %d angles in base frame: min: %.3f inc: %.3f", laser_index, angle_min, angle_increment);
1237 ldata.
range_max = laser_scan->range_max;
1242 range_min = laser_scan->range_min;
1250 if(laser_scan->ranges[i] <= range_min)
1253 ldata.
ranges[i][0] = laser_scan->ranges[i];
1255 ldata.
ranges[i][1] = angle_min +
1256 (i * angle_increment);
1278 geometry_msgs::PoseArray cloud_msg;
1281 cloud_msg.poses.resize(
set->sample_count);
1282 for(
int i=0;i<
set->sample_count;i++)
1286 set->samples[i].pose.v[1], 0)),
1287 cloud_msg.poses[i]);
1293 if(resampled || force_publication)
1296 double max_weight = 0.0;
1297 int max_weight_hyp = -1;
1298 std::vector<amcl_hyp_t> hyps;
1300 for(
int hyp_count = 0;
1308 ROS_ERROR(
"Couldn't get stats on cluster %d", hyp_count);
1312 hyps[hyp_count].weight = weight;
1313 hyps[hyp_count].pf_pose_mean = pose_mean;
1314 hyps[hyp_count].pf_pose_cov = pose_cov;
1316 if(hyps[hyp_count].weight > max_weight)
1318 max_weight = hyps[hyp_count].weight;
1319 max_weight_hyp = hyp_count;
1323 if(max_weight > 0.0)
1325 ROS_DEBUG(
"Max weight pose: %.3f %.3f %.3f",
1326 hyps[max_weight_hyp].pf_pose_mean.v[0],
1327 hyps[max_weight_hyp].pf_pose_mean.v[1],
1328 hyps[max_weight_hyp].pf_pose_mean.v[2]);
1336 geometry_msgs::PoseWithCovarianceStamped p;
1339 p.header.stamp = laser_scan->header.stamp;
1341 p.pose.pose.position.x = hyps[max_weight_hyp].pf_pose_mean.v[0];
1342 p.pose.pose.position.y = hyps[max_weight_hyp].pf_pose_mean.v[1];
1344 p.pose.pose.orientation);
1347 for(
int i=0; i<2; i++)
1349 for(
int j=0; j<2; j++)
1354 p.pose.covariance[6*i+j] =
set->cov.m[i][j];
1360 p.pose.covariance[6*5+5] =
set->cov.m[2][2];
1375 ROS_DEBUG(
"New pose: %6.3f %6.3f %6.3f",
1376 hyps[max_weight_hyp].pf_pose_mean.v[0],
1377 hyps[max_weight_hyp].pf_pose_mean.v[1],
1378 hyps[max_weight_hyp].pf_pose_mean.v[2]);
1385 tf::Vector3(hyps[max_weight_hyp].pf_pose_mean.v[0],
1386 hyps[max_weight_hyp].pf_pose_mean.v[1],
1389 laser_scan->header.stamp,
1397 ROS_DEBUG(
"Failed to subtract base to odom transform");
1409 ros::Time transform_expiration = (laser_scan->header.stamp +
1412 transform_expiration,
1429 ros::Time transform_expiration = (laser_scan->header.stamp +
1432 transform_expiration,
1452 double yaw, pitch, roll;
1467 if(msg.header.frame_id ==
"")
1470 ROS_WARN(
"Received initial pose with empty frame_id. You should always supply a frame_id.");
1475 ROS_WARN(
"Ignoring initial pose in frame \"%s\"; initial poses must be in the global frame, \"%s\"",
1476 msg.header.frame_id.c_str(),
1502 ROS_WARN(
"Failed to transform initial pose in time (%s)", e.what());
1508 pose_new = pose_old * tx_odom;
1512 ROS_INFO(
"Setting pose (%.6f): %.3f %.3f %.3f",
1519 pf_init_pose_mean.v[0] = pose_new.
getOrigin().
x();
1520 pf_init_pose_mean.v[1] = pose_new.
getOrigin().
y();
1521 pf_init_pose_mean.v[2] =
getYaw(pose_new);
1524 for(
int i=0; i<2; i++)
1526 for(
int j=0; j<2; j++)
1528 pf_init_pose_cov.
m[i][j] = msg.pose.covariance[6*i+j];
1531 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_
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
bool selective_resampling_
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
void pf_set_selective_resampling(pf_t *pf, int selective_resampling)
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)
void runFromBag(const std::string &in_bag_fn, bool trigger_global_localization=false)
Uses TF and LaserScan messages from bag file to drive AMCL instead.
ros::NodeHandle private_nh_
message_filters::Subscriber< sensor_msgs::LaserScan > * laser_scan_sub_
bool hasParam(const std::string &key) const
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)