mrpt_ekf_slam_2d_wrapper.cpp
Go to the documentation of this file.
1 /*
2  * File: mrpt_ekf_slam_2d_wrapper.cpp
3  * Author: Vladislav Tananaev
4  *
5  */
6 
8 
9 #include <mrpt/version.h>
10 
12  rawlog_play_ = false;
13  mrpt_bridge::convert(ros::Time(0), timeLastUpdate_);
14 }
16 bool EKFslamWrapper::is_file_exists(const std::string &name) {
17  std::ifstream f(name.c_str());
18  return f.good();
19 }
20 
22  ROS_INFO("READ PARAM FROM LAUNCH FILE");
23  n_.param<double>("ellipse_scale", ellipse_scale_, 1);
24  ROS_INFO("ellipse_scale: %f", ellipse_scale_);
25 
26  n_.param<double>("rawlog_play_delay", rawlog_play_delay, 0.1);
27  ROS_INFO("rawlog_play_delay: %f", rawlog_play_delay);
28 
29  n_.getParam("rawlog_filename", rawlog_filename);
30  ROS_INFO("rawlog_filename: %s", rawlog_filename.c_str());
31 
32  n_.getParam("ini_filename", ini_filename);
33  ROS_INFO("ini_filename: %s", ini_filename.c_str());
34 
35  n_.param<std::string>("global_frame_id", global_frame_id, "map");
36  ROS_INFO("global_frame_id: %s", global_frame_id.c_str());
37 
38  n_.param<std::string>("odom_frame_id", odom_frame_id, "odom");
39  ROS_INFO("odom_frame_id: %s", odom_frame_id.c_str());
40 
41  n_.param<std::string>("base_frame_id", base_frame_id, "base_link");
42  ROS_INFO("base_frame_id: %s", base_frame_id.c_str());
43 
44  n_.param<std::string>("sensor_source", sensor_source, "scan");
45  ROS_INFO("sensor_source: %s", sensor_source.c_str());
46 }
48  // get parameters from ini file
49 
51  ROS_ERROR_STREAM("CAN'T READ INI FILE");
52  return;
53  }
54 
56  // read rawlog file if it exists
58  ROS_WARN_STREAM("PLAY FROM RAWLOG FILE: " << rawlog_filename.c_str());
59  rawlog_play_ = true;
60  }
61 
63  n_.advertise<visualization_msgs::MarkerArray>("/state_viz", 1); // map
64  data_association_viz_pub_ = n_.advertise<visualization_msgs::MarkerArray>(
65  "/data_association_viz", 1); // data_association
66 
67  // read sensor topics
68  std::vector<std::string> lstSources;
69  mrpt::system::tokenize(sensor_source, " ,\t\n", lstSources);
70  ROS_ASSERT_MSG(!lstSources.empty(),
71  "*Fatal*: At least one sensor source must be provided in "
72  "~sensor_sources (e.g. "
73  "\"scan\" or \"beacon\")");
74 
76  sensorSub_.resize(lstSources.size());
77  for (size_t i = 0; i < lstSources.size(); i++) {
78  if (lstSources[i].find("landmark") != std::string::npos) {
79  sensorSub_[i] = n_.subscribe(lstSources[i], 1,
81  } else {
82  ROS_ERROR("Can't find the sensor topics. The sensor topics should "
83  "contain the word \"landmark\" in the name");
84  }
85  }
86 
87  init3Dwindow();
88 }
89 
90 void EKFslamWrapper::odometryForCallback(CObservationOdometry::Ptr &_odometry,
91  const std_msgs::Header &_msg_header) {
92  mrpt::poses::CPose3D poseOdom;
93  if (this->waitForTransform(poseOdom, odom_frame_id, base_frame_id,
94  _msg_header.stamp, ros::Duration(1))) {
95  _odometry = CObservationOdometry::Create();
96  _odometry->sensorLabel = odom_frame_id;
97  _odometry->hasEncodersInfo = false;
98  _odometry->hasVelocities = false;
99  _odometry->odometry.x() = poseOdom.x();
100  _odometry->odometry.y() = poseOdom.y();
101  _odometry->odometry.phi() = poseOdom.yaw();
102  }
103 }
104 
105 void EKFslamWrapper::updateSensorPose(std::string _frame_id) {
106  CPose3D pose;
107  tf::StampedTransform transform;
108  try {
110  transform);
111 
112  tf::Vector3 translation = transform.getOrigin();
113  tf::Quaternion quat = transform.getRotation();
114  pose.x() = translation.x();
115  pose.y() = translation.y();
116  pose.z() = translation.z();
117  tf::Matrix3x3 Rsrc(quat);
118  CMatrixDouble33 Rdes;
119  for (int c = 0; c < 3; c++)
120  for (int r = 0; r < 3; r++)
121  Rdes(r, c) = Rsrc.getRow(r)[c];
122  pose.setRotationMatrix(Rdes);
123  landmark_poses_[_frame_id] = pose;
124  } catch (tf::TransformException ex) {
125  ROS_ERROR("%s", ex.what());
126  ros::Duration(1.0).sleep();
127  }
128 }
129 
131  mrpt::poses::CPose3D &des, const std::string &target_frame,
132  const std::string &source_frame, const ros::Time &time,
133  const ros::Duration &timeout, const ros::Duration &polling_sleep_duration) {
134  tf::StampedTransform transform;
135  try {
136  listenerTF_.waitForTransform(target_frame, source_frame, time,
137  polling_sleep_duration);
138  listenerTF_.lookupTransform(target_frame, source_frame, time, transform);
139  } catch (tf::TransformException) {
140  ROS_INFO("Failed to get transform target_frame (%s) to source_frame (%s)",
141  target_frame.c_str(), source_frame.c_str());
142  return false;
143  }
144  mrpt_bridge::convert(transform, des);
145  return true;
146 }
147 
149  const mrpt_msgs::ObservationRangeBearing &_msg) {
150 #if MRPT_VERSION >= 0x130
151  using namespace mrpt::maps;
152  using namespace mrpt::obs;
153 #else
154  using namespace mrpt::slam;
155 #endif
156  CObservationBearingRange::Ptr landmark = CObservationBearingRange::Create();
157 
158  if (landmark_poses_.find(_msg.header.frame_id) == landmark_poses_.end()) {
159  updateSensorPose(_msg.header.frame_id);
160  } else {
161  mrpt::poses::CPose3D pose = landmark_poses_[_msg.header.frame_id];
162  mrpt_bridge::convert(_msg, landmark_poses_[_msg.header.frame_id],
163  *landmark);
164 
165  sf = CSensoryFrame::Create();
166  CObservationOdometry::Ptr odometry;
167  odometryForCallback(odometry, _msg.header);
168 
169  CObservation::Ptr obs = CObservation::Ptr(landmark);
170  sf->insert(obs);
171  observation(sf, odometry);
172  timeLastUpdate_ = sf->getObservationByIndex(0)->timestamp;
173 
174  tictac.Tic();
175  mapping.processActionObservation(action, sf);
176  t_exec = tictac.Tac();
177  ROS_INFO("Map building executed in %.03fms", 1000.0f * t_exec);
179  mapping.getCurrentState(robotPose_, LMs_, LM_IDs_, fullState_, fullCov_);
180  viz_state();
182  run3Dwindow();
183  publishTF();
184  }
185 }
186 
188  if (rawlog_play_ == false) {
189  return false;
190  } else {
191  size_t rawlogEntry = 0;
192 #if MRPT_VERSION >= 0x199
193 #include <mrpt/serialization/CArchive.h>
194  CFileGZInputStream rawlog_stream(rawlog_filename);
195  auto rawlogFile = mrpt::serialization::archiveFrom(rawlog_stream);
196 #else
197  CFileGZInputStream rawlogFile(rawlog_filename);
198 #endif
199  CActionCollection::Ptr action;
200  CSensoryFrame::Ptr observations;
201 
202  for (;;) {
203  if (ros::ok()) {
204  if (!CRawlog::readActionObservationPair(rawlogFile, action,
205  observations, rawlogEntry)) {
206  break; // file EOF
207  }
208  tictac.Tic();
209  mapping.processActionObservation(action, observations);
210  t_exec = tictac.Tac();
211  ROS_INFO("Map building executed in %.03fms", 1000.0f * t_exec);
213  mapping.getCurrentState(robotPose_, LMs_, LM_IDs_, fullState_,
214  fullCov_);
215 
216  viz_state();
218  run3Dwindow();
219  }
220  }
221  if (win3d) {
222  cout << "\n Close the 3D window to quit the application.\n";
223  win3d->waitForKey();
224  }
225  return true;
226  }
227 }
228 
229 // Local function to force the axis to be right handed for 2D. Based on the one
230 // from ecl_statistics
231 void EKFslamWrapper::makeRightHanded(Eigen::Matrix2d &eigenvectors,
232  Eigen::Vector2d &eigenvalues) {
233  // Note that sorting of eigenvalues may end up with left-hand coordinate
234  // system. So here we correctly sort it so that it does end up being
235  // righ-handed and normalised.
236  Eigen::Vector3d c0;
237  c0.setZero();
238  c0.head<2>() = eigenvectors.col(0);
239  c0.normalize();
240  Eigen::Vector3d c1;
241  c1.setZero();
242  c1.head<2>() = eigenvectors.col(1);
243  c1.normalize();
244  Eigen::Vector3d cc = c0.cross(c1);
245  if (cc[2] < 0) {
246  eigenvectors << c1.head<2>(), c0.head<2>();
247  double e = eigenvalues[0];
248  eigenvalues[0] = eigenvalues[1];
249  eigenvalues[1] = e;
250  } else {
251  eigenvectors << c0.head<2>(), c1.head<2>();
252  }
253 }
254 
256  tf::Quaternion &orientation, Eigen::Vector2d &scale,
257  const mrpt::math::CMatrixDouble covariance) {
258  tf::Matrix3x3 tf3d;
259  Eigen::Vector2d eigenvalues(Eigen::Vector2d::Identity());
260  Eigen::Matrix2d eigenvectors(Eigen::Matrix2d::Zero());
261 
262  // NOTE: The SelfAdjointEigenSolver only references the lower triangular part
263  // of the covariance matrix
264  Eigen::SelfAdjointEigenSolver<Eigen::Matrix2d> eigensolver(
265 #if MRPT_VERSION >= 0x199
266  covariance.asEigen()
267 #else
268  covariance
269 #endif
270  );
271 
272  // Compute eigenvectors and eigenvalues
273  if (eigensolver.info() == Eigen::Success) {
274  eigenvalues = eigensolver.eigenvalues();
275  eigenvectors = eigensolver.eigenvectors();
276  } else {
277  ROS_ERROR_STREAM("failed to compute eigen vectors/values for position. Is "
278  "the covariance matrix correct?");
279  eigenvalues = Eigen::Vector2d::Zero(); // Setting the scale to zero will
280  // hide it on the screen
281  eigenvectors = Eigen::Matrix2d::Identity();
282  }
283 
284  // Be sure we have a right-handed orientation system
285  makeRightHanded(eigenvectors, eigenvalues);
286 
287  // Rotation matrix around z axis
288  tf3d.setValue(eigenvectors(0, 0), eigenvectors(0, 1), 0, eigenvectors(1, 0),
289  eigenvectors(1, 1), 0, 0, 0, 1);
290 
291  // get orientation from rotation matrix
292  tf3d.getRotation(orientation);
293  // get scale
294  scale[0] = eigenvalues[0];
295  scale[1] = eigenvalues[1];
296 }
297 
299  // robot pose
300  mrpt::poses::CPose3D robotPose;
301  robotPose = CPose3D(robotPose_.mean);
302  geometry_msgs::Point pointRobotPose;
303  pointRobotPose.z = 0;
304  pointRobotPose.x = robotPose.x();
305  pointRobotPose.y = robotPose.y();
306 
307  // visualization of the data association
308  visualization_msgs::MarkerArray ma;
309  visualization_msgs::Marker line_strip;
310 
311  line_strip.header.frame_id = "/map";
312  line_strip.header.stamp = ros::Time::now();
313 
314  line_strip.id = 0;
315  line_strip.type = visualization_msgs::Marker::LINE_STRIP;
316  line_strip.action = visualization_msgs::Marker::ADD;
317 
318  line_strip.lifetime = ros::Duration(0.1);
319  line_strip.pose.position.x = 0;
320  line_strip.pose.position.y = 0;
321  line_strip.pose.position.z = 0;
322  line_strip.pose.orientation.x = 0.0;
323  line_strip.pose.orientation.y = 0.0;
324  line_strip.pose.orientation.z = 0.0;
325  line_strip.pose.orientation.w = 1.0;
326  line_strip.scale.x = 0.02; // line uses only x component
327  line_strip.scale.y = 0.0;
328  line_strip.scale.z = 0.0;
329  line_strip.color.a = 1.0;
330  line_strip.color.r = 1.0;
331  line_strip.color.g = 1.0;
332  line_strip.color.b = 1.0;
333 
334  // Draw latest data association:
335  const CRangeBearingKFSLAM2D::TDataAssocInfo &da =
336  mapping.getLastDataAssociation();
337 
338  for (auto it = da.results.associations.begin();
339  it != da.results.associations.end(); ++it) {
340  const prediction_index_t idxPred = it->second;
341  // This index must match the internal list of features in the map:
342  CRangeBearingKFSLAM2D::KFArray_FEAT featMean;
343  mapping.getLandmarkMean(idxPred, featMean);
344 
345  line_strip.points.clear();
346  line_strip.points.push_back(pointRobotPose);
347  geometry_msgs::Point pointLm;
348  pointLm.z = 0.0;
349  pointLm.x = featMean[0];
350  pointLm.y = featMean[1];
351  line_strip.points.push_back(pointLm);
352  ma.markers.push_back(line_strip);
353  line_strip.id++;
354  }
355 
357 }
359  visualization_msgs::MarkerArray ma;
360  visualization_msgs::Marker marker;
361  marker.header.frame_id = "/map";
362  marker.id = 0;
363  marker.type = visualization_msgs::Marker::SPHERE;
364  marker.action = visualization_msgs::Marker::ADD;
365  marker.lifetime = ros::Duration(0);
366 
367  // get the covariance matrix 2x2 for each ellipsoid including robot pose
368  mrpt::opengl::CSetOfObjects::Ptr objs;
369  objs = mrpt::opengl::CSetOfObjects::Create();
370  mapping.getAs3DObject(objs);
371 
372  // Count the number of landmarks
373  unsigned int objs_counter = 0;
374  while (objs->getByClass<mrpt::opengl::CEllipsoid>(objs_counter)) {
375  objs_counter++;
376  }
377 
378  mrpt::opengl::CEllipsoid::Ptr landmark;
379  for (size_t i = 0; i < objs_counter; i++) {
380  landmark = objs->getByClass<mrpt::opengl::CEllipsoid>(i);
381 
382  mrpt::math::CMatrixDouble covariance = landmark->getCovMatrix();
383  float quantiles = landmark->getQuantiles();
384 
385  // mean position
386  CPose3D pose = mrpt::poses::CPose3D(
387  landmark->getPose()); // pose of the robot and landmarks (x,y,z=0)
388 
389  // covariance ellipses
390  tf::Quaternion orientation;
391  Eigen::Vector2d scale;
392 
393  computeEllipseOrientationScale2D(orientation, scale, covariance);
394 
395  marker.id++;
396  marker.color.a = 1.0;
397  if (i == 0) { // robot position
398  marker.color.r = 1.0;
399  marker.color.g = 0.0;
400  marker.color.b = 0.0;
401  } else {
402  marker.color.r = 0.0;
403  marker.color.g = 0.0;
404  marker.color.b = 1.0;
405  }
406  marker.type = visualization_msgs::Marker::SPHERE;
407  marker.pose.position.x = pose.x();
408  marker.pose.position.y = pose.y();
409  marker.pose.position.z = 0;
410  marker.pose.orientation.x = orientation.x();
411  marker.pose.orientation.y = orientation.y();
412  marker.pose.orientation.z = orientation.z();
413  marker.pose.orientation.w = orientation.w();
414  marker.scale.x = ellipse_scale_ * quantiles * sqrt(scale[0]);
415  marker.scale.y = ellipse_scale_ * quantiles * sqrt(scale[1]);
416  marker.scale.z = 0.00001; // Z can't be 0, limitation of ROS
417  ma.markers.push_back(marker);
418 
419  marker.id++;
420  marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
421  if (i == 0) {
422  marker.text = "robot";
423  } else {
424  marker.text = std::to_string(LM_IDs_[i]);
425  }
426  marker.pose.position.x = pose.x();
427  marker.pose.position.y = pose.y();
428  marker.pose.position.z = 0.1;
429  marker.color.r = 1.0;
430  marker.color.g = 1.0;
431  marker.color.b = 1.0;
432  marker.scale.x = 0.3;
433  marker.scale.y = 0.3;
434  marker.scale.z = 0.3;
435  ma.markers.push_back(marker);
436  }
437 
439 }
440 
442  mapping.getCurrentState(robotPose_, LMs_, LM_IDs_, fullState_, fullCov_);
443  // Most of this code was copy and pase from ros::amcl
444  mrpt::poses::CPose3D robotPose;
445  robotPose = CPose3D(robotPose_.mean);
446 
447  tf::Stamped<tf::Pose> odom_to_map;
448  tf::Transform tmp_tf;
449  ros::Time stamp;
450  mrpt_bridge::convert(timeLastUpdate_, stamp);
451  mrpt_bridge::convert(robotPose, tmp_tf);
452 
453  try {
454  tf::Stamped<tf::Pose> tmp_tf_stamped(tmp_tf.inverse(), stamp,
455  base_frame_id);
456  listenerTF_.transformPose(odom_frame_id, tmp_tf_stamped, odom_to_map);
457  } catch (tf::TransformException) {
458  ROS_INFO("Failed to subtract global_frame (%s) from odom_frame (%s)",
459  global_frame_id.c_str(), odom_frame_id.c_str());
460  return;
461  }
462 
463  tf::Transform latest_tf_ =
464  tf::Transform(tf::Quaternion(odom_to_map.getRotation()),
465  tf::Point(odom_to_map.getOrigin()));
466 
467  // We want to send a transform that is good up until a
468  // tolerance time so that odom can be used
469  ros::Duration transform_tolerance_(0.1);
470  ros::Time transform_expiration = (stamp + transform_tolerance_);
471  tf::StampedTransform tmp_tf_stamped(latest_tf_.inverse(),
472  transform_expiration, global_frame_id,
473  odom_frame_id);
474  tf_broadcaster_.sendTransform(tmp_tf_stamped);
475 }
float t_exec
the time which take one SLAM update execution
void viz_dataAssociation()
visualize the data associations for the landmarks observed by robot at the each step ...
CTicTac tictac
timer for SLAM performance evaluation
void updateSensorPose(std::string _frame_id)
update the pose of the sensor with respect to the robot
void publish(const boost::shared_ptr< M > &message) const
void odometryForCallback(CObservationOdometry::Ptr &_odometry, const std_msgs::Header &_msg_header)
get the odometry for received observation
f
std::string odom_frame_id
/odom frame
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
ros::Publisher data_association_viz_pub_
CVectorDouble fullState_
full state vector
bool sleep() const
double rawlog_play_delay
delay of replay from rawlog file
bool rawlogPlay()
play rawlog file
CMatrixDouble fullCov_
full covariance matrix
std::string global_frame_id
/map frame
std::string base_frame_id
robot frame
CPosePDFGaussian robotPose_
current robot pose
void setValue(const tfScalar &xx, const tfScalar &xy, const tfScalar &xz, const tfScalar &yx, const tfScalar &yy, const tfScalar &yz, const tfScalar &zx, const tfScalar &zy, const tfScalar &zz)
void run3Dwindow()
run 3D window update from mrpt lib
void getRotation(Quaternion &q) const
TFSIMD_FORCE_INLINE const Vector3 & getRow(int i) const
ros::NodeHandle n_
Node handler.
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
void landmarkCallback(const mrpt_msgs::ObservationRangeBearing &_msg)
callback function for the landmarks
bool is_file_exists(const std::string &name)
check the existance of the file
std::vector< mrpt::math::TPoint2D > LMs_
vector of the landmarks
TFSIMD_FORCE_INLINE const tfScalar & x() const
void init3Dwindow()
init 3D window from mrpt lib
void get_param()
read the parameters from launch file
bool rawlog_play_
true if rawlog file exists
TFSIMD_FORCE_INLINE const tfScalar & z() const
std::map< unsigned int, CLandmark::TLandmarkID > LM_IDs_
vector of the landmarks ID
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
tf::TransformListener listenerTF_
transform listener
#define ROS_ASSERT_MSG(cond,...)
ROSCPP_DECL bool ok()
TFSIMD_FORCE_INLINE const tfScalar & y() const
void sendTransform(const StampedTransform &transform)
CRangeBearingKFSLAM2D mapping
EKF slam 2d class.
std::string sensor_source
2D laser scans
Transform inverse() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
mrpt::system::TTimeStamp timeLastUpdate_
last update of the pose and map
void makeRightHanded(Eigen::Matrix2d &eigenvectors, Eigen::Vector2d &eigenvalues)
compute the correct orientation and scale of covariance ellipsoids (make sure that we output covarian...
#define ROS_WARN_STREAM(args)
TFSIMD_FORCE_INLINE Vector3 & getOrigin()
std::string rawlog_filename
name of rawlog file
void lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, StampedTransform &transform) const
void init()
initialize publishers subscribers and EKF 2d slam
bool waitForTransform(mrpt::poses::CPose3D &des, const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01))
wait for transform between odometry frame and the robot frame
void publishTF()
publish tf tree
double ellipse_scale_
Scale of covariance ellipses.
std::string ini_filename
name of ini file
void transformPose(const std::string &target_frame, const geometry_msgs::PoseStamped &stamped_in, geometry_msgs::PoseStamped &stamped_out) const
Quaternion getRotation() const
void viz_state()
visualize the covariance ellipsoids for robot and landmarks
bool getParam(const std::string &key, std::string &s) const
static Time now()
void computeEllipseOrientationScale2D(tf::Quaternion &orientation, Eigen::Vector2d &scale, const mrpt::math::CMatrixDouble covariance)
compute the orientation and scale of covariance ellipsoids
std::vector< ros::Subscriber > sensorSub_
list of sensors topics
void observation(CSensoryFrame::Ptr _sf, CObservationOdometry::Ptr _odometry)
calculate the actions from odometry model for current observation
#define ROS_ERROR_STREAM(args)
mrpt::gui::CDisplayWindow3D::Ptr win3d
MRPT window.
r
tf::TransformBroadcaster tf_broadcaster_
transform broadcaster
#define ROS_ERROR(...)
std::map< std::string, mrpt::poses::CPose3D > landmark_poses_
landmark poses with respect to the map
ros::Publisher state_viz_pub_
publishers
void read_iniFile(std::string ini_filename)
read ini file
CActionCollection::Ptr action
actions
CSensoryFrame::Ptr sf
observations


mrpt_ekf_slam_2d
Author(s): Jose Luis, Vladislav Tananaev
autogenerated on Thu Jun 6 2019 19:37:43