mrpt_ekf_slam_3d_wrapper.cpp
Go to the documentation of this file.
1 /*
2  * File: mrpt_ekf_slam_3d_wrapper.cpp
3  * Author: Vladislav Tananaev
4  *
5  */
6 
8 
9 #include <mrpt/version.h>
10 #if MRPT_VERSION >= 0x199
11 #include <mrpt/serialization/CArchive.h>
12 #endif
13 
15  rawlog_play_ = false;
17 }
19 bool EKFslamWrapper::is_file_exists(const std::string &name) {
20  std::ifstream f(name.c_str());
21  return f.good();
22 }
23 
25  ROS_INFO("READ PARAM FROM LAUNCH FILE");
26  n_.param<double>("ellipse_scale", ellipse_scale_, 1);
27  ROS_INFO("ellipse_scale: %f", ellipse_scale_);
28 
29  n_.param<double>("rawlog_play_delay", rawlog_play_delay, 0.1);
30  ROS_INFO("rawlog_play_delay: %f", rawlog_play_delay);
31 
32  n_.getParam("rawlog_filename", rawlog_filename);
33  ROS_INFO("rawlog_filename: %s", rawlog_filename.c_str());
34 
35  n_.getParam("ini_filename", ini_filename);
36  ROS_INFO("ini_filename: %s", ini_filename.c_str());
37 
38  n_.param<std::string>("global_frame_id", global_frame_id, "map");
39  ROS_INFO("global_frame_id: %s", global_frame_id.c_str());
40 
41  n_.param<std::string>("odom_frame_id", odom_frame_id, "odom");
42  ROS_INFO("odom_frame_id: %s", odom_frame_id.c_str());
43 
44  n_.param<std::string>("base_frame_id", base_frame_id, "base_link");
45  ROS_INFO("base_frame_id: %s", base_frame_id.c_str());
46 
47  n_.param<std::string>("sensor_source", sensor_source, "scan");
48  ROS_INFO("sensor_source: %s", sensor_source.c_str());
49 }
51  // get parameters from ini file
53  ROS_ERROR_STREAM("CAN'T READ INI FILE");
54  return;
55  }
56 
58  // read rawlog file if it exists
60  ROS_WARN_STREAM("PLAY FROM RAWLOG FILE: " << rawlog_filename.c_str());
61  rawlog_play_ = true;
62  }
63 
65  n_.advertise<visualization_msgs::MarkerArray>("/state_viz", 1); // map
66  data_association_viz_pub_ = n_.advertise<visualization_msgs::MarkerArray>(
67  "/data_association_viz", 1); // data_association
68 
69  // read sensor topics
70  std::vector<std::string> lstSources;
71  mrpt::system::tokenize(sensor_source, " ,\t\n", lstSources);
72  ROS_ASSERT_MSG(!lstSources.empty(),
73  "*Fatal*: At least one sensor source must be provided in "
74  "~sensor_sources (e.g. "
75  "\"scan\" or \"beacon\")");
76 
78  sensorSub_.resize(lstSources.size());
79  for (size_t i = 0; i < lstSources.size(); i++) {
80  if (lstSources[i].find("landmark") != std::string::npos) {
81  sensorSub_[i] = n_.subscribe(lstSources[i], 1,
83  } else {
84  ROS_ERROR("Can't find the sensor topics. The sensor topics should "
85  "contain the word \"landmark\" in the name");
86  }
87  }
88 
89  init3Dwindow();
90 }
91 
92 void EKFslamWrapper::odometryForCallback(CObservationOdometry::Ptr &_odometry,
93  const std_msgs::Header &_msg_header) {
94  mrpt::poses::CPose3D poseOdom;
95  if (this->waitForTransform(poseOdom, odom_frame_id, base_frame_id,
96  _msg_header.stamp, ros::Duration(1))) {
97  _odometry = CObservationOdometry::Create();
98  _odometry->sensorLabel = odom_frame_id;
99  _odometry->hasEncodersInfo = false;
100  _odometry->hasVelocities = false;
101  _odometry->odometry.x() = poseOdom.x();
102  _odometry->odometry.y() = poseOdom.y();
103  _odometry->odometry.phi() = poseOdom.yaw();
104  }
105 }
106 
107 void EKFslamWrapper::updateSensorPose(std::string _frame_id) {
108  CPose3D pose;
110  try {
112  transform);
113 
114  tf::Vector3 translation = transform.getOrigin();
115  tf::Quaternion quat = transform.getRotation();
116  pose.x() = translation.x();
117  pose.y() = translation.y();
118  pose.z() = translation.z();
119  tf::Matrix3x3 Rsrc(quat);
120  CMatrixDouble33 Rdes;
121  for (int c = 0; c < 3; c++)
122  for (int r = 0; r < 3; r++)
123  Rdes(r, c) = Rsrc.getRow(r)[c];
124  pose.setRotationMatrix(Rdes);
125  landmark_poses_[_frame_id] = pose;
126  } catch (tf::TransformException ex) {
127  ROS_ERROR("%s", ex.what());
128  ros::Duration(1.0).sleep();
129  }
130 }
131 
133  mrpt::poses::CPose3D &des, const std::string &target_frame,
134  const std::string &source_frame, const ros::Time &time,
135  const ros::Duration &timeout, const ros::Duration &polling_sleep_duration) {
137  try {
138  listenerTF_.waitForTransform(target_frame, source_frame, time,
139  polling_sleep_duration);
140  listenerTF_.lookupTransform(target_frame, source_frame, time, transform);
141  } catch (tf::TransformException) {
142  ROS_INFO("Failed to get transform target_frame (%s) to source_frame (%s)",
143  target_frame.c_str(), source_frame.c_str());
144  return false;
145  }
146  mrpt_bridge::convert(transform, des);
147  return true;
148 }
149 
152 #if MRPT_VERSION >= 0x130
153  using namespace mrpt::maps;
154  using namespace mrpt::obs;
155 #else
156  using namespace mrpt::slam;
157 #endif
158  CObservationBearingRange::Ptr landmark = CObservationBearingRange::Create();
159 
160  if (landmark_poses_.find(_msg.header.frame_id) == landmark_poses_.end()) {
161  updateSensorPose(_msg.header.frame_id);
162  } else {
163  mrpt::poses::CPose3D pose = landmark_poses_[_msg.header.frame_id];
164  mrpt_bridge::convert(_msg, landmark_poses_[_msg.header.frame_id],
165  *landmark);
166 
167  sf = CSensoryFrame::Create();
168  CObservationOdometry::Ptr odometry;
169  odometryForCallback(odometry, _msg.header);
170 
171  CObservation::Ptr obs = CObservation::Ptr(landmark);
172  sf->insert(obs);
173  observation(sf, odometry);
174  timeLastUpdate_ = sf->getObservationByIndex(0)->timestamp;
175 
176  tictac.Tic();
177  mapping.processActionObservation(action, sf);
178  t_exec = tictac.Tac();
179  ROS_INFO("Map building executed in %.03fms", 1000.0f * t_exec);
181  mapping.getCurrentState(robotPose_, LMs_, LM_IDs_, fullState_, fullCov_);
182  viz_state();
184  run3Dwindow();
185  publishTF();
186  }
187 }
188 
190  if (rawlog_play_ == false) {
191  return false;
192  } else {
193  size_t rawlogEntry = 0;
194 #if MRPT_VERSION >= 0x199
196  auto rawlogFile = mrpt::serialization::archiveFrom(f);
197 #else
199 #endif
200 
201  CActionCollection::Ptr action;
202  CSensoryFrame::Ptr observations;
203 
204  for (;;) {
205  if (ros::ok()) {
206  if (!CRawlog::readActionObservationPair(rawlogFile, action,
207  observations, rawlogEntry)) {
208  break; // file EOF
209  }
210  tictac.Tic();
211  mapping.processActionObservation(action, observations);
212  t_exec = tictac.Tac();
213  ROS_INFO("Map building executed in %.03fms", 1000.0f * t_exec);
215  mapping.getCurrentState(robotPose_, LMs_, LM_IDs_, fullState_,
216  fullCov_);
217  // ros::spinOnce();
218  viz_state();
220  run3Dwindow();
221  }
222  }
223  if (win3d) {
224  cout << "\n Close the 3D window to quit the application.\n";
225  win3d->waitForKey();
226  }
227  return true;
228  }
229 }
230 
231 // Local function to force the axis to be right handed for 3D. Taken from
232 // ecl_statistics
233 void EKFslamWrapper::makeRightHanded(Eigen::Matrix3d &eigenvectors,
234  Eigen::Vector3d &eigenvalues) {
235  // Note that sorting of eigenvalues may end up with left-hand coordinate
236  // system. So here we correctly sort it so that it does end up being
237  // righ-handed and normalised.
238  Eigen::Vector3d c0 = eigenvectors.block<3, 1>(0, 0);
239  c0.normalize();
240  Eigen::Vector3d c1 = eigenvectors.block<3, 1>(0, 1);
241  c1.normalize();
242  Eigen::Vector3d c2 = eigenvectors.block<3, 1>(0, 2);
243  c2.normalize();
244  Eigen::Vector3d cc = c0.cross(c1);
245  if (cc.dot(c2) < 0) {
246  eigenvectors << c1, c0, c2;
247  double e = eigenvalues[0];
248  eigenvalues[0] = eigenvalues[1];
249  eigenvalues[1] = e;
250  } else {
251  eigenvectors << c0, c1, c2;
252  }
253 }
254 
256  tf::Quaternion &orientation, Eigen::Vector3d &scale,
257  const mrpt::math::CMatrixDouble covariance) {
258  // initialize variables and empty matrices for eigen vectors and eigen values
259  tf::Matrix3x3 tf3d;
260  Eigen::Vector3d eigenvalues(Eigen::Vector3d::Identity());
261  Eigen::Matrix3d eigenvectors(Eigen::Matrix3d::Zero());
262 
263  // compute eigen vectors and eigen values from covariance matrix
264  Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> 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::Vector3d::Zero(); // Setting the scale to zero will
280  // hide it on the screen
281  eigenvectors = Eigen::Matrix3d::Identity();
282  }
283 
284  // Be sure we have a right-handed orientation system
285  makeRightHanded(eigenvectors, eigenvalues);
286 
287  // Rotation matrix
288  tf3d.setValue(eigenvectors(0, 0), eigenvectors(0, 1), eigenvectors(0, 2),
289  eigenvectors(1, 0), eigenvectors(1, 1), eigenvectors(1, 2),
290  eigenvectors(2, 0), eigenvectors(2, 1), eigenvectors(2, 2));
291 
292  // get orientation from rotation matrix
293  tf3d.getRotation(orientation);
294  // get scale
295  scale[0] = eigenvalues[0];
296  scale[1] = eigenvalues[1];
297  scale[2] = eigenvalues[2];
298 }
300  // robot pose
301  mrpt::poses::CPose3D robotPose;
302  robotPose = CPose3D(robotPose_.mean);
303  geometry_msgs::Point pointRobotPose;
304  pointRobotPose.z = robotPose.z();
305  pointRobotPose.x = robotPose.x();
306  pointRobotPose.y = robotPose.y();
307 
308  // visualization of the data association
309  visualization_msgs::MarkerArray ma;
310  visualization_msgs::Marker line_strip;
311 
312  line_strip.header.frame_id = "/map";
313  line_strip.header.stamp = ros::Time::now();
314 
315  line_strip.id = 0;
316  line_strip.type = visualization_msgs::Marker::LINE_STRIP;
317  line_strip.action = visualization_msgs::Marker::ADD;
318 
319  line_strip.lifetime = ros::Duration(0.1);
320  line_strip.pose.position.x = 0;
321  line_strip.pose.position.y = 0;
322  line_strip.pose.position.z = 0;
323  line_strip.pose.orientation.x = 0.0;
324  line_strip.pose.orientation.y = 0.0;
325  line_strip.pose.orientation.z = 0.0;
326  line_strip.pose.orientation.w = 1.0;
327  line_strip.scale.x = 0.02; // line uses only x component
328  line_strip.scale.y = 0.0;
329  line_strip.scale.z = 0.0;
330  line_strip.color.a = 1.0;
331  line_strip.color.r = 1.0;
332  line_strip.color.g = 1.0;
333  line_strip.color.b = 1.0;
334 
335  // Draw latest data association:
337  mapping.getLastDataAssociation();
338 
339  for (auto it = da.results.associations.begin();
340  it != da.results.associations.end(); ++it) {
341  const prediction_index_t idxPred = it->second;
342  // This index must match the internal list of features in the map:
343  CRangeBearingKFSLAM::KFArray_FEAT featMean;
344  mapping.getLandmarkMean(idxPred, featMean);
345 
346  line_strip.points.clear();
347  line_strip.points.push_back(pointRobotPose);
348  geometry_msgs::Point pointLm;
349  pointLm.z = featMean[2];
350  pointLm.x = featMean[0];
351  pointLm.y = featMean[1];
352  line_strip.points.push_back(pointLm);
353  ma.markers.push_back(line_strip);
354  line_strip.id++;
355  }
356 
358 }
359 
361  visualization_msgs::MarkerArray ma;
362  visualization_msgs::Marker marker;
363  marker.header.frame_id = "/map";
364  marker.id = 0;
365  marker.type = visualization_msgs::Marker::SPHERE;
366  marker.action = visualization_msgs::Marker::ADD;
367  marker.lifetime = ros::Duration(0);
368 
369  // get the covariance matrix 3x3 for each ellipsoid including robot pose
370  mrpt::opengl::CSetOfObjects::Ptr objs;
371  objs = mrpt::opengl::CSetOfObjects::Create();
372  mapping.getAs3DObject(objs);
373 
374  // Count the number of landmarks + robot
375  unsigned int objs_counter = 0;
376  while (objs->getByClass<mrpt::opengl::CEllipsoid>(objs_counter)) {
377  objs_counter++;
378  }
379 
380  mrpt::opengl::CEllipsoid::Ptr landmark;
381 
382  for (size_t i = 0; i < objs_counter; i++) {
383  landmark = objs->getByClass<mrpt::opengl::CEllipsoid>(i);
384 
385  float quantiles =
386  landmark->getQuantiles(); // the scale of ellipse covariance
387  // visualization (usually 3 sigma)
388  mrpt::math::CMatrixDouble covariance = landmark->getCovMatrix();
389 
390  // the landmark (or robot) mean position
391  CPose3D pose = mrpt::poses::CPose3D(landmark->getPose());
392  // For visualization of the covariance ellipses we need the size of the axis
393  // and orientation
394 
395  Eigen::Vector3d scale; // size of axis of the ellipse
396  tf::Quaternion orientation;
397  computeEllipseOrientationScale(orientation, scale, covariance);
398 
399  marker.id++;
400  marker.color.a = 1.0;
401  if (i == 0) { // robot position
402  marker.color.r = 1.0;
403  marker.color.g = 0.0;
404  marker.color.b = 0.0;
405  } else {
406  marker.color.r = 0.0;
407  marker.color.g = 0.0;
408  marker.color.b = 1.0;
409  }
410  marker.type = visualization_msgs::Marker::SPHERE;
411  marker.pose.position.x = pose.x();
412  marker.pose.position.y = pose.y();
413  marker.pose.position.z = pose.z();
414  marker.pose.orientation.x = orientation.x();
415  marker.pose.orientation.y = orientation.y();
416  marker.pose.orientation.z = orientation.z();
417  marker.pose.orientation.w = orientation.w();
418  marker.scale.x = ellipse_scale_ * quantiles * std::sqrt(scale[0]);
419  marker.scale.y = ellipse_scale_ * quantiles * std::sqrt(scale[1]);
420  marker.scale.z = ellipse_scale_ * quantiles * std::sqrt(scale[2]);
421  ma.markers.push_back(marker);
422 
423  marker.id++;
424  marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
425  if (i == 0) {
426  marker.text = "robot";
427  } else {
428  marker.text = std::to_string(LM_IDs_[i]);
429  }
430  marker.pose.position.x = pose.x();
431  marker.pose.position.y = pose.y();
432  marker.pose.position.z = pose.z() + marker.scale.z + 0.1;
433  marker.color.r = 1.0;
434  marker.color.g = 1.0;
435  marker.color.b = 1.0;
436  marker.scale.x = 0.3;
437  marker.scale.y = 0.3;
438  marker.scale.z = 0.3;
439  ma.markers.push_back(marker);
440  }
441 
443 }
444 
446  mapping.getCurrentState(robotPose_, LMs_, LM_IDs_, fullState_, fullCov_);
447  // Most of this code was copy and pase from ros::amcl
448  mrpt::poses::CPose3D robotPose;
449 
450  robotPose = CPose3D(robotPose_.mean);
451  // mapBuilder->mapPDF.getEstimatedPosePDF(curPDF);
452 
453  // curPDF.getMean(robotPose);
454 
455  tf::Stamped<tf::Pose> odom_to_map;
456  tf::Transform tmp_tf;
457  ros::Time stamp;
459  mrpt_bridge::convert(robotPose, tmp_tf);
460 
461  try {
462  tf::Stamped<tf::Pose> tmp_tf_stamped(tmp_tf.inverse(), stamp,
463  base_frame_id);
464  listenerTF_.transformPose(odom_frame_id, tmp_tf_stamped, odom_to_map);
465  } catch (tf::TransformException) {
466  ROS_INFO("Failed to subtract global_frame (%s) from odom_frame (%s)",
467  global_frame_id.c_str(), odom_frame_id.c_str());
468  return;
469  }
470 
471  tf::Transform latest_tf_ =
473  tf::Point(odom_to_map.getOrigin()));
474 
475  // We want to send a transform that is good up until a
476  // tolerance time so that odom can be used
477 
478  ros::Duration transform_tolerance_(0.1);
479  ros::Time transform_expiration = (stamp + transform_tolerance_);
480  tf::StampedTransform tmp_tf_stamped(latest_tf_.inverse(),
481  transform_expiration, global_frame_id,
482  odom_frame_id);
483  tf_broadcaster_.sendTransform(tmp_tf_stamped);
484 }
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
GLenum GLenum GLenum GLenum GLenum scale
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
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())
const GLfloat * c
size_t rawlogEntry
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
std::vector< mrpt::math::TPoint3D > LMs_
vector of the landmarks
CPose3DQuatPDFGaussian 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
int marker
GLuint GLenum GLenum transform
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
TFSIMD_FORCE_INLINE const tfScalar & x() const
void init3Dwindow()
init 3D window from mrpt lib
obs
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
double yaw() const
#define ROS_INFO(...)
GLuint const GLchar * name
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)
GLenum GLenum GLenum GLenum mapping
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
#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 3d slam
void convert(const ros::Time &src, mrpt::system::TTimeStamp &des)
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 transfor between odometry frame and the robot frame
void publishTF()
publis tf tree
const_iterator find(const KEY &key) const
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
GLdouble GLdouble GLdouble r
std::map< observation_index_t, prediction_index_t > associations
bool getParam(const std::string &key, std::string &s) const
size_t prediction_index_t
static Time now()
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)
void computeEllipseOrientationScale(tf::Quaternion &orientation, Eigen::Vector3d &scale, const mrpt::math::CMatrixDouble covariance)
compute the orientation and scale of covariance ellipsoids
void makeRightHanded(Eigen::Matrix3d &eigenvectors, Eigen::Vector3d &eigenvalues)
compute the correct orientation and scale of covariance ellipsoids (make sure that we output covarian...
mrpt::gui::CDisplayWindow3D::Ptr win3d
MRPT window.
void BASE_IMPEXP tokenize(const std::string &inString, const std::string &inDelimiters, std::deque< std::string > &outTokens, bool skipBlankTokens=true) MRPT_NO_THROWS
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_
void read_iniFile(std::string ini_filename)
read ini file
CActionCollection::Ptr action
actions
CSensoryFrame::Ptr sf
observations
float getQuantiles() const
void setRotationMatrix(const mrpt::math::CMatrixDouble33 &ROT)
GLdouble GLdouble GLdouble GLdouble GLdouble GLdouble f


mrpt_ekf_slam_3d
Author(s): Jose Luis, Vladislav Tananaev
autogenerated on Sat May 2 2020 03:44:08