CoreWrapper.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the name of the Universite de Sherbrooke nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
29 
30 #include <stdio.h>
31 #include <ros/ros.h>
33 
34 #include <nav_msgs/Path.h>
35 #include <std_msgs/Int32MultiArray.h>
36 #include <std_msgs/Bool.h>
37 #include <geometry_msgs/PoseArray.h>
39 #include <cv_bridge/cv_bridge.h>
41 #include <pcl/io/io.h>
42 
43 #include <visualization_msgs/MarkerArray.h>
44 
45 #include <rtabmap/utilite/UTimer.h>
47 #include <rtabmap/utilite/UFile.h>
49 #include <rtabmap/utilite/UStl.h>
50 #include <rtabmap/utilite/UMath.h>
51 
52 #include <rtabmap/core/util2d.h>
53 #include <rtabmap/core/util3d.h>
56 #include <rtabmap/core/Memory.h>
58 #include <rtabmap/core/Version.h>
60 #include <rtabmap/core/DBDriver.h>
62 #include <rtabmap/core/Graph.h>
63 #include <rtabmap/core/Optimizer.h>
64 
65 #ifdef WITH_OCTOMAP_MSGS
66 #ifdef RTABMAP_OCTOMAP
68 #include <rtabmap/core/OctoMap.h>
69 #endif
70 #endif
71 
72 #define BAD_COVARIANCE 9999
73 
74 //msgs
75 #include "rtabmap_ros/Info.h"
76 #include "rtabmap_ros/MapData.h"
77 #include "rtabmap_ros/MapGraph.h"
78 #include "rtabmap_ros/Path.h"
79 
81 
82 using namespace rtabmap;
83 
84 namespace rtabmap_ros {
85 
86 CoreWrapper::CoreWrapper() :
88  paused_(false),
89  lastPose_(Transform::getIdentity()),
90  lastPoseIntermediate_(false),
91  latestNodeWasReached_(false),
92  frameId_("base_link"),
93  odomFrameId_(""),
94  mapFrameId_("map"),
95  groundTruthFrameId_(""), // e.g., "world"
96  groundTruthBaseFrameId_(""), // e.g., "base_link_gt"
97  configPath_(""),
98  odomDefaultAngVariance_(0.001),
99  odomDefaultLinVariance_(0.001),
100  landmarkDefaultAngVariance_(0.001),
101  landmarkDefaultLinVariance_(0.001),
102  waitForTransform_(true),
103  waitForTransformDuration_(0.2), // 200 ms
104  useActionForGoal_(false),
105  useSavedMap_(true),
106  genScan_(false),
107  genScanMaxDepth_(4.0),
108  genScanMinDepth_(0.0),
109  genDepth_(false),
110  genDepthDecimation_(1),
111  genDepthFillHolesSize_(0),
112  genDepthFillIterations_(1),
113  genDepthFillHolesError_(0.1),
114  scanCloudMaxPoints_(0),
115  scanCloudIs2d_(false),
116  mapToOdom_(rtabmap::Transform::getIdentity()),
117  transformThread_(0),
118  tfThreadRunning_(false),
119  stereoToDepth_(false),
120  interOdomSync_(0),
121  odomSensorSync_(false),
122  rate_(Parameters::defaultRtabmapDetectionRate()),
123  createIntermediateNodes_(Parameters::defaultRtabmapCreateIntermediateNodes()),
124  mappingMaxNodes_(Parameters::defaultGridGlobalMaxNodes()),
125  mappingAltitudeDelta_(Parameters::defaultGridGlobalAltitudeDelta()),
126  alreadyRectifiedImages_(Parameters::defaultRtabmapImagesAlreadyRectified()),
127  twoDMapping_(Parameters::defaultRegForce3DoF()),
128  previousStamp_(0),
129  mbClient_(0)
130 {
131  char * rosHomePath = getenv("ROS_HOME");
132  std::string workingDir = rosHomePath?rosHomePath:UDirectory::homeDir()+"/.ros";
134  globalPose_.header.stamp = ros::Time(0);
135 }
136 
138 {
141 
142  mapsManager_.init(nh, pnh, getName(), true);
143 
144  bool publishTf = true;
145  std::string initialPoseStr;
146  double tfDelay = 0.05; // 20 Hz
147  double tfTolerance = 0.1; // 100 ms
148  std::string odomFrameIdInit;
149 
150  pnh.param("config_path", configPath_, configPath_);
151  pnh.param("database_path", databasePath_, databasePath_);
152 
153  pnh.param("frame_id", frameId_, frameId_);
154  pnh.param("odom_frame_id", odomFrameId_, odomFrameId_); // set to use odom from TF
155  pnh.param("odom_frame_id_init", odomFrameIdInit, odomFrameIdInit); // set to publish map->odom TF before receiving odom topic
156  pnh.param("map_frame_id", mapFrameId_, mapFrameId_);
157  pnh.param("ground_truth_frame_id", groundTruthFrameId_, groundTruthFrameId_);
158  pnh.param("ground_truth_base_frame_id", groundTruthBaseFrameId_, frameId_);
159  if(pnh.hasParam("depth_cameras") && !pnh.hasParam("depth_cameras"))
160  {
161  NODELET_ERROR("\"depth_cameras\" parameter doesn't exist "
162  "anymore! It is replaced by \"rgbd_cameras\" parameter "
163  "used when \"subscribe_rgbd\" is true");
164  }
165  if(!odomFrameIdInit.empty())
166  {
167  if(odomFrameId_.empty())
168  {
169  ROS_INFO("rtabmap: odom_frame_id_init = %s", odomFrameIdInit.c_str());
170  odomFrameId_ = odomFrameIdInit;
171  }
172  else
173  {
174  ROS_WARN("odom_frame_id_init (%s) is ignored if odom_frame_id (%s) is set.", odomFrameIdInit.c_str(), odomFrameId_.c_str());
175  }
176  }
177 
178  int eventLevel = ULogger::kFatal;
179  pnh.param("log_to_rosout_level", eventLevel, eventLevel);
180  UASSERT(eventLevel >= ULogger::kDebug && eventLevel <= ULogger::kFatal);
182 
183  pnh.param("publish_tf", publishTf, publishTf);
184  pnh.param("tf_delay", tfDelay, tfDelay);
185  if(pnh.hasParam("tf_prefix"))
186  {
187  ROS_ERROR("tf_prefix parameter has been removed, use directly map_frame_id, odom_frame_id and frame_id parameters.");
188  }
189  pnh.param("tf_tolerance", tfTolerance, tfTolerance);
190  pnh.param("odom_tf_angular_variance", odomDefaultAngVariance_, odomDefaultAngVariance_);
191  pnh.param("odom_tf_linear_variance", odomDefaultLinVariance_, odomDefaultLinVariance_);
192  pnh.param("landmark_angular_variance", landmarkDefaultAngVariance_, landmarkDefaultAngVariance_);
193  pnh.param("landmark_linear_variance", landmarkDefaultLinVariance_, landmarkDefaultLinVariance_);
194  pnh.param("wait_for_transform", waitForTransform_, waitForTransform_);
195  pnh.param("wait_for_transform_duration", waitForTransformDuration_, waitForTransformDuration_);
196  pnh.param("initial_pose", initialPoseStr, initialPoseStr);
197  pnh.param("use_action_for_goal", useActionForGoal_, useActionForGoal_);
198  pnh.param("use_saved_map", useSavedMap_, useSavedMap_);
199  pnh.param("gen_scan", genScan_, genScan_);
200  pnh.param("gen_scan_max_depth", genScanMaxDepth_, genScanMaxDepth_);
201  pnh.param("gen_scan_min_depth", genScanMinDepth_, genScanMinDepth_);
202  pnh.param("gen_depth", genDepth_, genDepth_);
203  pnh.param("gen_depth_decimation", genDepthDecimation_, genDepthDecimation_);
204  pnh.param("gen_depth_fill_holes_size", genDepthFillHolesSize_, genDepthFillHolesSize_);
205  pnh.param("gen_depth_fill_iterations", genDepthFillIterations_, genDepthFillIterations_);
206  pnh.param("gen_depth_fill_holes_error", genDepthFillHolesError_, genDepthFillHolesError_);
207  pnh.param("scan_cloud_max_points", scanCloudMaxPoints_, scanCloudMaxPoints_);
208  pnh.param("scan_cloud_is_2d", scanCloudIs2d_, scanCloudIs2d_);
209  if(pnh.hasParam("scan_cloud_normal_k"))
210  {
211  ROS_WARN("rtabmap: Parameter \"scan_cloud_normal_k\" has been removed. RTAB-Map's parameter \"%s\" should be used instead. "
212  "The value is copied. Use \"%s\" to avoid this warning.",
213  Parameters::kMemLaserScanNormalK().c_str(),
214  Parameters::kMemLaserScanNormalK().c_str());
215  double value;
216  pnh.getParam("scan_cloud_normal_k", value);
217  uInsert(parameters_, ParametersPair(Parameters::kMemLaserScanNormalK(), uNumber2Str(value)));
218  }
219  pnh.param("stereo_to_depth", stereoToDepth_, stereoToDepth_);
220  pnh.param("odom_sensor_sync", odomSensorSync_, odomSensorSync_);
221  if(pnh.hasParam("flip_scan"))
222  {
223  NODELET_WARN("Parameter \"flip_scan\" doesn't exist anymore. Rtabmap now "
224  "detects automatically if the laser is upside down with /tf, then if so, it "
225  "switches scan values.");
226  }
227 
228  NODELET_INFO("rtabmap: frame_id = %s", frameId_.c_str());
229  if(!odomFrameId_.empty())
230  {
231  NODELET_INFO("rtabmap: odom_frame_id = %s", odomFrameId_.c_str());
232  }
233  if(!groundTruthFrameId_.empty())
234  {
235  NODELET_INFO("rtabmap: ground_truth_frame_id = %s -> ground_truth_base_frame_id = %s",
236  groundTruthFrameId_.c_str(),
237  groundTruthBaseFrameId_.c_str());
238  }
239  NODELET_INFO("rtabmap: map_frame_id = %s", mapFrameId_.c_str());
240  NODELET_INFO("rtabmap: log_to_rosout_level = %d", eventLevel);
241  NODELET_INFO("rtabmap: initial_pose = %s", initialPoseStr.c_str());
242  NODELET_INFO("rtabmap: use_action_for_goal = %s", useActionForGoal_?"true":"false");
243  NODELET_INFO("rtabmap: tf_delay = %f", tfDelay);
244  NODELET_INFO("rtabmap: tf_tolerance = %f", tfTolerance);
245  NODELET_INFO("rtabmap: odom_sensor_sync = %s", odomSensorSync_?"true":"false");
246  bool subscribeStereo = false;
247  pnh.param("subscribe_stereo", subscribeStereo, subscribeStereo);
248  if(subscribeStereo)
249  {
250  NODELET_INFO("rtabmap: stereo_to_depth = %s", stereoToDepth_?"true":"false");
251  }
252 
253  NODELET_INFO("rtabmap: gen_scan = %s", genScan_?"true":"false");
254  if(genScan_)
255  {
256  NODELET_INFO("rtabmap: gen_scan_max_depth = %f", genScanMaxDepth_);
257  NODELET_INFO("rtabmap: gen_scan_min_depth = %f", genScanMinDepth_);
258  }
259 
260  NODELET_INFO("rtabmap: gen_depth = %s", genDepth_?"true":"false");
261  if(genDepth_)
262  {
263  NODELET_INFO("rtabmap: gen_depth_decimation = %d", genDepthDecimation_);
264  NODELET_INFO("rtabmap: gen_depth_fill_holes_size = %d", genDepthFillHolesSize_);
265  NODELET_INFO("rtabmap: gen_depth_fill_iterations = %d", genDepthFillIterations_);
266  NODELET_INFO("rtabmap: gen_depth_fill_holes_error = %f", genDepthFillHolesError_);
267  }
268  bool subscribeScanCloud = false;
269  pnh.param("subscribe_scan_cloud", subscribeScanCloud, subscribeScanCloud);
270  if(subscribeScanCloud)
271  {
272  NODELET_INFO("rtabmap: scan_cloud_max_points = %d", scanCloudMaxPoints_);
273  NODELET_INFO("rtabmap: scan_cloud_is_2d = %s", scanCloudIs2d_?"true":"false");
274  }
275 
276  infoPub_ = nh.advertise<rtabmap_ros::Info>("info", 1);
277  mapDataPub_ = nh.advertise<rtabmap_ros::MapData>("mapData", 1);
278  mapGraphPub_ = nh.advertise<rtabmap_ros::MapGraph>("mapGraph", 1);
279  odomCachePub_ = nh.advertise<rtabmap_ros::MapGraph>("mapOdomCache", 1);
280  landmarksPub_ = nh.advertise<geometry_msgs::PoseArray>("landmarks", 1);
282  mapPathPub_ = nh.advertise<nav_msgs::Path>("mapPath", 1);
283  localGridObstacle_ = nh.advertise<sensor_msgs::PointCloud2>("local_grid_obstacle", 1);
284  localGridEmpty_ = nh.advertise<sensor_msgs::PointCloud2>("local_grid_empty", 1);
285  localGridGround_ = nh.advertise<sensor_msgs::PointCloud2>("local_grid_ground", 1);
286  localizationPosePub_ = nh.advertise<geometry_msgs::PoseWithCovarianceStamped>("localization_pose", 1);
287  initialPoseSub_ = nh.subscribe("initialpose", 1, &CoreWrapper::initialPoseCallback, this);
288 
289  // planning topics
290  goalSub_ = nh.subscribe("goal", 1, &CoreWrapper::goalCallback, this);
291  goalNodeSub_ = nh.subscribe("goal_node", 1, &CoreWrapper::goalNodeCallback, this);
292  nextMetricGoalPub_ = nh.advertise<geometry_msgs::PoseStamped>("goal_out", 1);
293  goalReachedPub_ = nh.advertise<std_msgs::Bool>("goal_reached", 1);
294  globalPathPub_ = nh.advertise<nav_msgs::Path>("global_path", 1);
295  localPathPub_ = nh.advertise<nav_msgs::Path>("local_path", 1);
296  globalPathNodesPub_ = nh.advertise<rtabmap_ros::Path>("global_path_nodes", 1);
297  localPathNodesPub_ = nh.advertise<rtabmap_ros::Path>("local_path_nodes", 1);
298 
299  ros::Publisher nextMetricGoal_;
300  ros::Publisher goalReached_;
301  ros::Publisher path_;
302 
305 #ifndef _WIN32
306  if(configPath_.size() && configPath_.at(0) != '/')
307  {
309  }
310  if(databasePath_.size() && databasePath_.at(0) != '/')
311  {
313  }
314 #endif
315 
316  ParametersMap allParameters = Parameters::getDefaultParameters();
317  // remove Odom parameters
318  for(ParametersMap::iterator iter=allParameters.begin(); iter!=allParameters.end();)
319  {
320  if(iter->first.find("Odom") == 0)
321  {
322  allParameters.erase(iter++);
323  }
324  else
325  {
326  ++iter;
327  }
328  }
329  uInsert(allParameters, ParametersPair(Parameters::kRGBDCreateOccupancyGrid(), "true")); // default true in ROS
330  char * rosHomePath = getenv("ROS_HOME");
331  std::string workingDir = rosHomePath?rosHomePath:UDirectory::homeDir()+"/.ros";
332  uInsert(allParameters, ParametersPair(Parameters::kRtabmapWorkingDirectory(), workingDir)); // change default to ~/.ros
333 
334  // load parameters
336  for(ParametersMap::iterator iter=parameters_.begin(); iter!=parameters_.end();)
337  {
338  if(iter->first.find("Odom") == 0)
339  {
340  parameters_.erase(iter++);
341  }
342  else
343  {
344  ++iter;
345  }
346  }
347 
348  // update parameters with user input parameters (private)
349  for(ParametersMap::iterator iter=allParameters.begin(); iter!=allParameters.end(); ++iter)
350  {
351  std::string vStr;
352  bool vBool;
353  int vInt;
354  double vDouble;
355  if(pnh.getParam(iter->first, vStr))
356  {
357  NODELET_INFO("Setting RTAB-Map parameter \"%s\"=\"%s\"", iter->first.c_str(), vStr.c_str());
358 
359  if(iter->first.compare(Parameters::kRtabmapWorkingDirectory()) == 0)
360  {
361  vStr = uReplaceChar(vStr, '~', UDirectory::homeDir());
362  }
363  else if(iter->first.compare(Parameters::kKpDictionaryPath()) == 0)
364  {
365  vStr = uReplaceChar(vStr, '~', UDirectory::homeDir());
366  }
367  uInsert(parameters_, ParametersPair(iter->first, vStr));
368  }
369  else if(pnh.getParam(iter->first, vBool))
370  {
371  NODELET_INFO("Setting RTAB-Map parameter \"%s\"=\"%s\"", iter->first.c_str(), uBool2Str(vBool).c_str());
372  uInsert(parameters_, ParametersPair(iter->first, uBool2Str(vBool)));
373  }
374  else if(pnh.getParam(iter->first, vDouble))
375  {
376  NODELET_INFO("Setting RTAB-Map parameter \"%s\"=\"%s\"", iter->first.c_str(), uNumber2Str(vDouble).c_str());
377  uInsert(parameters_, ParametersPair(iter->first, uNumber2Str(vDouble)));
378  }
379  else if(pnh.getParam(iter->first, vInt))
380  {
381  NODELET_INFO("Setting RTAB-Map parameter \"%s\"=\"%s\"", iter->first.c_str(), uNumber2Str(vInt).c_str());
382  uInsert(parameters_, ParametersPair(iter->first, uNumber2Str(vInt)));
383  }
384  }
385 
386  //parse input arguments
387  std::vector<std::string> argList = getMyArgv();
388  char ** argv = new char*[argList.size()];
389  bool deleteDbOnStart = false;
390  for(unsigned int i=0; i<argList.size(); ++i)
391  {
392  argv[i] = &argList[i].at(0);
393  if(strcmp(argv[i], "--delete_db_on_start") == 0 || strcmp(argv[i], "-d") == 0)
394  {
395  deleteDbOnStart = true;
396  }
397  }
398  rtabmap::ParametersMap parameters = rtabmap::Parameters::parseArguments(argList.size(), argv);
399  delete [] argv;
400  for(ParametersMap::const_iterator iter=parameters.begin(); iter!=parameters.end(); ++iter)
401  {
402  uInsert(parameters_, ParametersPair(iter->first, iter->second));
403  NODELET_INFO("Update RTAB-Map parameter \"%s\"=\"%s\" from arguments", iter->first.c_str(), iter->second.c_str());
404  }
405 
406  // Backward compatibility
407  for(std::map<std::string, std::pair<bool, std::string> >::const_iterator iter=Parameters::getRemovedParameters().begin();
408  iter!=Parameters::getRemovedParameters().end();
409  ++iter)
410  {
411  std::string vStr;
412  bool vBool;
413  int vInt;
414  double vDouble;
415  std::string paramValue;
416  if(pnh.getParam(iter->first, vStr))
417  {
418  paramValue = vStr;
419  }
420  else if(pnh.getParam(iter->first, vBool))
421  {
422  paramValue = uBool2Str(vBool);
423  }
424  else if(pnh.getParam(iter->first, vDouble))
425  {
426  paramValue = uNumber2Str(vDouble);
427  }
428  else if(pnh.getParam(iter->first, vInt))
429  {
430  paramValue = uNumber2Str(vInt);
431  }
432  if(!paramValue.empty())
433  {
434  if(!iter->second.second.empty() && parameters_.find(iter->second.second)!=parameters_.end())
435  {
436  NODELET_WARN("Rtabmap: Parameter name changed: \"%s\" -> \"%s\". The new parameter is already used with value \"%s\", ignoring the old one with value \"%s\".",
437  iter->first.c_str(), iter->second.second.c_str(), parameters_.find(iter->second.second)->second.c_str(), paramValue.c_str());
438  }
439  else if(iter->second.first)
440  {
441  // can be migrated
442  uInsert(parameters_, ParametersPair(iter->second.second, paramValue));
443  NODELET_WARN("Rtabmap: Parameter name changed: \"%s\" -> \"%s\". Please update your launch file accordingly. Value \"%s\" is still set to the new parameter name.",
444  iter->first.c_str(), iter->second.second.c_str(), paramValue.c_str());
445  }
446  else
447  {
448  if(iter->second.second.empty())
449  {
450  NODELET_ERROR("Rtabmap: Parameter \"%s\" doesn't exist anymore!",
451  iter->first.c_str());
452  }
453  else
454  {
455  NODELET_ERROR("Rtabmap: Parameter \"%s\" doesn't exist anymore! You may look at this similar parameter: \"%s\"",
456  iter->first.c_str(), iter->second.second.c_str());
457  }
458  }
459  }
460  }
461 
462  // Backward compatibility (MapsManager)
464 
465  bool subscribeScan2d = false;
466  bool subscribeScan3d = false;
467  pnh.param("subscribe_scan", subscribeScan2d, subscribeScan2d);
468  pnh.param("subscribe_scan_cloud", subscribeScan3d, subscribeScan3d);
469  int gridSensor = Parameters::defaultGridSensor();
470  if((subscribeScan2d || subscribeScan3d || genScan_) && parameters_.find(Parameters::kGridSensor()) == parameters_.end())
471  {
472  NODELET_WARN("Setting \"%s\" parameter to 0 (default 1) as \"subscribe_scan\", \"subscribe_scan_cloud\" or \"gen_scan\" is "
473  "true. The occupancy grid map will be constructed from "
474  "laser scans. To get occupancy grid map from camera's cloud projection, set \"%s\" "
475  "to 1. To suppress this warning, "
476  "add <param name=\"%s\" type=\"string\" value=\"0\"/>",
477  Parameters::kGridSensor().c_str(),
478  Parameters::kGridSensor().c_str(),
479  Parameters::kGridSensor().c_str());
480  parameters_.insert(ParametersPair(Parameters::kGridSensor(), "0"));
481  }
482  Parameters::parse(parameters_, Parameters::kGridSensor(), gridSensor);
483  if((subscribeScan2d || subscribeScan3d || genScan_) && parameters_.find(Parameters::kGridRangeMax()) == parameters_.end() && gridSensor==0)
484  {
485  NODELET_INFO("Setting \"%s\" parameter to 0 (default %f) as \"subscribe_scan\", \"subscribe_scan_cloud\" or \"gen_scan\" is true and %s is 0.",
486  Parameters::kGridRangeMax().c_str(),
487  Parameters::defaultGridRangeMax(),
488  Parameters::kGridSensor().c_str());
489  parameters_.insert(ParametersPair(Parameters::kGridRangeMax(), "0"));
490  }
491  if(subscribeScan3d && !scanCloudIs2d_ && parameters_.find(Parameters::kIcpPointToPlaneRadius()) == parameters_.end())
492  {
493  NODELET_INFO("Setting \"%s\" parameter to 0 (default %f) as \"subscribe_scan_cloud\" is true.",
494  Parameters::kIcpPointToPlaneRadius().c_str(),
495  Parameters::defaultIcpPointToPlaneRadius());
496  parameters_.insert(ParametersPair(Parameters::kIcpPointToPlaneRadius(), "0"));
497  }
498  int regStrategy = Parameters::defaultRegStrategy();
499  Parameters::parse(parameters_, Parameters::kRegStrategy(), regStrategy);
500  if(parameters_.find(Parameters::kRGBDProximityPathMaxNeighbors()) == parameters_.end() &&
501  (regStrategy == Registration::kTypeIcp || regStrategy == Registration::kTypeVisIcp))
502  {
503  if(subscribeScan2d || (subscribeScan3d && scanCloudIs2d_))
504  {
505  NODELET_WARN("Setting \"%s\" parameter to 10 (default 0) as \"%s\" is "
506  "true and \"%s\" uses ICP. Proximity detection by space will be also done by merging close "
507  "scans. To disable, set \"%s\" to 0. To suppress this warning, "
508  "add <param name=\"%s\" type=\"string\" value=\"10\"/>",
509  Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
510  subscribeScan2d?"subscribe_scan":"scan_cloud_is_2d",
511  Parameters::kRegStrategy().c_str(),
512  Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
513  Parameters::kRGBDProximityPathMaxNeighbors().c_str());
514  parameters_.insert(ParametersPair(Parameters::kRGBDProximityPathMaxNeighbors(), "10"));
515  }
516  else if(subscribeScan3d)
517  {
518  NODELET_WARN("Setting \"%s\" parameter to 1 (default 0) as \"subscribe_scan_cloud\" is "
519  "true and \"%s\" uses ICP. To disable, set \"%s\" to 0. To suppress this warning, "
520  "add <param name=\"%s\" type=\"string\" value=\"1\"/>",
521  Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
522  Parameters::kRegStrategy().c_str(),
523  Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
524  Parameters::kRGBDProximityPathMaxNeighbors().c_str());
525  parameters_.insert(ParametersPair(Parameters::kRGBDProximityPathMaxNeighbors(), "1"));
526  }
527  }
528 
529  int estimationType = Parameters::defaultVisEstimationType();
530  Parameters::parse(parameters_, Parameters::kVisEstimationType(), estimationType);
531  int cameras = 0;
532  bool subscribeRGBD = false;
533  pnh.param("rgbd_cameras", cameras, cameras);
534  pnh.param("subscribe_rgbd", subscribeRGBD, subscribeRGBD);
535 
536  // modify default parameters with those in the database
537  if(!deleteDbOnStart)
538  {
539  ParametersMap dbParameters;
541  if(driver->openConnection(databasePath_))
542  {
543  dbParameters = driver->getLastParameters(); // parameter migration is already done
544  }
545  delete driver;
546  for(ParametersMap::iterator iter=dbParameters.begin(); iter!=dbParameters.end(); ++iter)
547  {
548  if(iter->first.compare(Parameters::kRtabmapWorkingDirectory()) == 0)
549  {
550  // ignore working directory
551  continue;
552  }
553  if(parameters_.find(iter->first) == parameters_.end() &&
554  allParameters.find(iter->first) != allParameters.end() &&
555  allParameters.find(iter->first)->second.compare(iter->second) !=0)
556  {
557  NODELET_INFO("Update RTAB-Map parameter \"%s\"=\"%s\" from database", iter->first.c_str(), iter->second.c_str());
558  parameters_.insert(*iter);
559  }
560  }
561  }
562  ParametersMap modifiedParameters = parameters_;
563  // Add all other parameters (not copied if already exists)
564  parameters_.insert(allParameters.begin(), allParameters.end());
565 
566  if(parameters_.find(Parameters::kRtabmapDetectionRate()) != parameters_.end())
567  {
568  Parameters::parse(parameters_, Parameters::kRtabmapDetectionRate(), rate_);
569  NODELET_INFO("RTAB-Map detection rate = %f Hz", rate_);
570  }
571  if(parameters_.find(Parameters::kRtabmapCreateIntermediateNodes()) != parameters_.end())
572  {
573  Parameters::parse(parameters_, Parameters::kRtabmapCreateIntermediateNodes(), createIntermediateNodes_);
575  {
576  NODELET_INFO("Create intermediate nodes");
577  if(rate_ == 0.0f)
578  {
579  bool interOdomInfo = false;
580  pnh.getParam("subscribe_inter_odom_info", interOdomInfo);
581  if(interOdomInfo)
582  {
583  NODELET_INFO("Subscribe to inter odom + info messages");
585  interOdomSync_->registerCallback(boost::bind(&CoreWrapper::interOdomInfoCallback, this, boost::placeholders::_1, boost::placeholders::_2));
586  interOdomSyncSub_.subscribe(nh, "inter_odom", 100);
587  interOdomInfoSyncSub_.subscribe(nh, "inter_odom_info", 100);
588  }
589  else
590  {
591  NODELET_INFO("Subscribe to inter odom messages");
592  interOdomSub_ = nh.subscribe("inter_odom", 100, &CoreWrapper::interOdomCallback, this);
593  }
594 
595  }
596  }
597  }
598  if(parameters_.find(Parameters::kGridGlobalMaxNodes()) != parameters_.end())
599  {
600  Parameters::parse(parameters_, Parameters::kGridGlobalMaxNodes(), mappingMaxNodes_);
601  if(mappingMaxNodes_>0)
602  {
603  NODELET_INFO("Max mapping nodes = %d", mappingMaxNodes_);
604  }
605  }
606  if(parameters_.find(Parameters::kGridGlobalAltitudeDelta()) != parameters_.end())
607  {
608  Parameters::parse(parameters_, Parameters::kGridGlobalAltitudeDelta(), mappingAltitudeDelta_);
609  if(mappingAltitudeDelta_>0.0)
610  {
611  NODELET_INFO("Mapping altitude delta = %f", mappingAltitudeDelta_);
612  }
613  }
614  if(parameters_.find(Parameters::kRtabmapImagesAlreadyRectified()) != parameters_.end())
615  {
616  Parameters::parse(parameters_, Parameters::kRtabmapImagesAlreadyRectified(), alreadyRectifiedImages_);
617  }
618  if(parameters_.find(Parameters::kRegForce3DoF()) != parameters_.end())
619  {
620  Parameters::parse(parameters_, Parameters::kRegForce3DoF(), twoDMapping_);
621  }
622 
623  paused_ = pnh.param("is_rtabmap_paused", paused_);
624  if(paused_)
625  {
626  NODELET_WARN("Node paused... don't forget to call service \"resume\" to start rtabmap.");
627  }
628 
629  if(deleteDbOnStart)
630  {
631  if(UFile::erase(databasePath_) == 0)
632  {
633  NODELET_INFO("rtabmap: Deleted database \"%s\" (--delete_db_on_start or -d are set).", databasePath_.c_str());
634  }
635  }
636 
637  if(databasePath_.size())
638  {
639  NODELET_INFO("rtabmap: Using database from \"%s\" (%ld MB).", databasePath_.c_str(), UFile::length(databasePath_)/(1024*1024));
640  }
641  else
642  {
643  NODELET_INFO("rtabmap: database_path parameter not set, the map will not be saved.");
644  }
645 
647 
648  // Init RTAB-Map
650 
651  if(rtabmap_.getMemory())
652  {
653  if(useSavedMap_)
654  {
655  float xMin, yMin, gridCellSize;
656  cv::Mat map = rtabmap_.getMemory()->load2DMap(xMin, yMin, gridCellSize);
657  if(!map.empty())
658  {
659  NODELET_INFO("rtabmap: 2D occupancy grid map loaded (%dx%d).", map.cols, map.rows);
660  mapsManager_.set2DMap(map, xMin, yMin, gridCellSize, rtabmap_.getLocalOptimizedPoses(), rtabmap_.getMemory());
661  }
662  }
663 
664  if(rtabmap_.getMemory()->getWorkingMem().size()>1)
665  {
666  NODELET_INFO("rtabmap: Working Memory = %d, Local map = %d.",
667  (int)rtabmap_.getMemory()->getWorkingMem().size()-1,
668  (int)rtabmap_.getLocalOptimizedPoses().size());
669  }
670 
671  if(databasePath_.size())
672  {
673  NODELET_INFO("rtabmap: Database version = \"%s\".", rtabmap_.getMemory()->getDatabaseVersion().c_str());
674  }
675 
677  {
678  NODELET_INFO("rtabmap: SLAM mode (%s=true)", Parameters::kMemIncrementalMemory().c_str());
679  }
680  else
681  {
682  NODELET_INFO("rtabmap: Localization mode (%s=false)", Parameters::kMemIncrementalMemory().c_str());
683  }
684  }
685 
686  // setup services
687  updateSrv_ = nh.advertiseService("update_parameters", &CoreWrapper::updateRtabmapCallback, this);
716 #ifdef WITH_OCTOMAP_MSGS
717 #ifdef RTABMAP_OCTOMAP
718  octomapBinarySrv_ = nh.advertiseService("octomap_binary", &CoreWrapper::octomapBinaryCallback, this);
719  octomapFullSrv_ = nh.advertiseService("octomap_full", &CoreWrapper::octomapFullCallback, this);
720 #endif
721 #endif
722  //private services
723  setLogDebugSrv_ = pnh.advertiseService("log_debug", &CoreWrapper::setLogDebug, this);
724  setLogInfoSrv_ = pnh.advertiseService("log_info", &CoreWrapper::setLogInfo, this);
725  setLogWarnSrv_ = pnh.advertiseService("log_warning", &CoreWrapper::setLogWarn, this);
726  setLogErrorSrv_ = pnh.advertiseService("log_error", &CoreWrapper::setLogError, this);
727 
728  int optimizeIterations = 0;
729  Parameters::parse(parameters_, Parameters::kOptimizerIterations(), optimizeIterations);
730  if(publishTf && optimizeIterations != 0)
731  {
732  tfThreadRunning_ = true;
733  transformThread_ = new boost::thread(boost::bind(&CoreWrapper::publishLoop, this, tfDelay, tfTolerance));
734  }
735  else if(publishTf)
736  {
737  NODELET_WARN("Graph optimization is disabled (%s=0), the tf between frame \"%s\" and odometry frame will not be published. You can safely ignore this warning if you are using map_optimizer node.",
738  Parameters::kOptimizerIterations().c_str(), mapFrameId_.c_str());
739  }
740 
741  setupCallbacks(nh, pnh, getName()); // do it at the end
742  if(!this->isDataSubscribed())
743  {
744  bool isRGBD = uStr2Bool(parameters_.at(Parameters::kRGBDEnabled()).c_str());
745  if(isRGBD)
746  {
747  NODELET_WARN("ROS param subscribe_depth, subscribe_stereo and subscribe_rgbd are false, but RTAB-Map "
748  "parameter \"%s\" is true! Please set subscribe_depth, subscribe_stereo or subscribe_rgbd "
749  "to true to use rtabmap node for RGB-D SLAM, set \"%s\" to false for loop closure "
750  "detection on images-only or set subscribe_rgb to true to localize a single RGB camera against pre-built 3D map.",
751  Parameters::kRGBDEnabled().c_str(),
752  Parameters::kRGBDEnabled().c_str());
753  }
754  ros::NodeHandle rgb_nh(nh, "rgb");
755  ros::NodeHandle rgb_pnh(pnh, "rgb");
756  image_transport::ImageTransport rgb_it(rgb_nh);
757  image_transport::TransportHints hintsRgb("raw", ros::TransportHints(), rgb_pnh);
758  defaultSub_ = rgb_it.subscribe("image", 1, &CoreWrapper::defaultCallback, this);
759 
760  NODELET_INFO("\n%s subscribed to:\n %s", getName().c_str(), defaultSub_.getTopic().c_str());
761  }
762  else if(!this->isSubscribedToDepth() &&
763  !this->isSubscribedToStereo() &&
764  !this->isSubscribedToRGBD() &&
765  !this->isSubscribedToRGB() &&
766  (this->isSubscribedToScan2d() || this->isSubscribedToScan3d() || this->isSubscribedToOdom()))
767  {
768  NODELET_WARN("There is no image subscription, bag-of-words loop closure detection will be disabled...");
769  int kpMaxFeatures = Parameters::defaultKpMaxFeatures();
770  int registrationStrategy = Parameters::defaultRegStrategy();
771  Parameters::parse(parameters_, Parameters::kKpMaxFeatures(), kpMaxFeatures);
772  Parameters::parse(parameters_, Parameters::kRegStrategy(), registrationStrategy);
773  bool updateParams = false;
774  if(kpMaxFeatures!= -1)
775  {
776  uInsert(parameters_, ParametersPair(Parameters::kKpMaxFeatures(), "-1"));
777  NODELET_WARN("Setting %s=-1 (bag-of-words disabled)", Parameters::kKpMaxFeatures().c_str());
778  updateParams = true;
779  }
780  if((this->isSubscribedToScan2d() || this->isSubscribedToScan3d()) && registrationStrategy != 1)
781  {
782  uInsert(parameters_, ParametersPair(Parameters::kRegStrategy(), "1"));
783  NODELET_WARN("Setting %s=1 (ICP)", Parameters::kRegStrategy().c_str());
784  updateParams = true;
785 
786  if(modifiedParameters.find(Parameters::kRGBDProximityPathMaxNeighbors()) == modifiedParameters.end())
787  {
788  if(this->isSubscribedToScan2d())
789  {
790  NODELET_WARN("Setting \"%s\" parameter to 10 (default 0) as \"subscribe_scan\" is "
791  "true and \"%s\" uses ICP. Proximity detection by space will be also done by merging close "
792  "scans. To disable, set \"%s\" to 0. To suppress this warning, "
793  "add <param name=\"%s\" type=\"string\" value=\"10\"/>",
794  Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
795  Parameters::kRegStrategy().c_str(),
796  Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
797  Parameters::kRGBDProximityPathMaxNeighbors().c_str());
798  uInsert(parameters_, ParametersPair(Parameters::kRGBDProximityPathMaxNeighbors(), "10"));
799  }
800  else if(this->isSubscribedToScan3d())
801  {
802  NODELET_WARN("Setting \"%s\" parameter to 1 (default 0) as \"subscribe_scan_cloud\" is "
803  "true and \"%s\" uses ICP. To disable, set \"%s\" to 0. To suppress this warning, "
804  "add <param name=\"%s\" type=\"string\" value=\"1\"/>",
805  Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
806  Parameters::kRegStrategy().c_str(),
807  Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
808  Parameters::kRGBDProximityPathMaxNeighbors().c_str());
809  uInsert(parameters_, ParametersPair(Parameters::kRGBDProximityPathMaxNeighbors(), "1"));
810  }
811  }
812  }
813  if(updateParams)
814  {
816  }
817  }
818 
819  // Set initial pose if set
820  if(!initialPoseStr.empty())
821  {
822  Transform intialPose = Transform::fromString(initialPoseStr);
823  if(!intialPose.isNull())
824  {
825  NODELET_INFO("Setting initial pose: \"%s\"", intialPose.prettyPrint().c_str());
826  rtabmap_.setInitialPose(intialPose);
827  }
828  else
829  {
830  NODELET_ERROR("Invalid initial_pose: \"%s\"", initialPoseStr.c_str());
831  }
832  }
833 
834  // set private parameters
835  for(ParametersMap::iterator iter=parameters_.begin(); iter!=parameters_.end(); ++iter)
836  {
837  pnh.setParam(iter->first, iter->second);
838  }
839 
840  userDataAsyncSub_ = nh.subscribe("user_data_async", 1, &CoreWrapper::userDataAsyncCallback, this);
843 #ifdef WITH_APRILTAG_ROS
844  tagDetectionsSub_ = nh.subscribe("tag_detections", 1, &CoreWrapper::tagDetectionsAsyncCallback, this);
845 #endif
846 #ifdef WITH_FIDUCIAL_MSGS
847  fiducialTransfromsSub_ = nh.subscribe("fiducial_transforms", 1, &CoreWrapper::fiducialDetectionsAsyncCallback, this);
848 #endif
849  imuSub_ = nh.subscribe("imu", 100, &CoreWrapper::imuAsyncCallback, this);
850  republishNodeDataSub_ = nh.subscribe("republish_node_data", 100, &CoreWrapper::republishNodeDataCallback, this);
851 }
852 
854 {
855  if(transformThread_)
856  {
857  tfThreadRunning_ = false;
858  transformThread_->join();
859  delete transformThread_;
860  }
861 
863 
864  printf("rtabmap: Saving database/long-term memory... (located at %s)\n", databasePath_.c_str());
865  if(rtabmap_.getMemory())
866  {
867  // save the grid map
868  float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f;
869  cv::Mat pixels = mapsManager_.getGridMap(xMin, yMin, gridCellSize);
870  if(!pixels.empty())
871  {
872  printf("rtabmap: 2D occupancy grid map saved.\n");
873  rtabmap_.getMemory()->save2DMap(pixels, xMin, yMin, gridCellSize);
874  }
875  }
876 
877  rtabmap_.close();
878  printf("rtabmap: Saving database/long-term memory...done! (located at %s, %ld MB)\n", databasePath_.c_str(), UFile::length(databasePath_)/(1024*1024));
879 
880  delete interOdomSync_;
881  delete mbClient_;
882 }
883 
884 void CoreWrapper::loadParameters(const std::string & configFile, ParametersMap & parameters)
885 {
886  if(!configFile.empty())
887  {
888  NODELET_INFO("Loading parameters from %s", configFile.c_str());
889  if(!UFile::exists(configFile.c_str()))
890  {
891  NODELET_WARN("Config file doesn't exist! It will be generated...");
892  }
893  Parameters::readINI(configFile.c_str(), parameters);
894  }
895 }
896 
897 void CoreWrapper::saveParameters(const std::string & configFile)
898 {
899  if(!configFile.empty())
900  {
901  printf("Saving parameters to %s\n", configFile.c_str());
902 
903  if(!UFile::exists(configFile.c_str()))
904  {
905  printf("Config file doesn't exist, a new one will be created.\n");
906  }
907  Parameters::writeINI(configFile.c_str(), parameters_);
908  }
909  else
910  {
911  NODELET_INFO("Parameters are not saved! (No configuration file provided...)");
912  }
913 }
914 
915 void CoreWrapper::publishLoop(double tfDelay, double tfTolerance)
916 {
917  if(tfDelay == 0)
918  return;
919  ros::Rate r(1.0 / tfDelay);
920  while(tfThreadRunning_)
921  {
922  if(!odomFrameId_.empty())
923  {
924  mapToOdomMutex_.lock();
925  ros::Time tfExpiration = ros::Time::now() + ros::Duration(tfTolerance);
927  msg.child_frame_id = odomFrameId_;
928  msg.header.frame_id = mapFrameId_;
929  msg.header.stamp = tfExpiration;
932  mapToOdomMutex_.unlock();
933  }
934  r.sleep();
935  }
936 }
937 
938 void CoreWrapper::defaultCallback(const sensor_msgs::ImageConstPtr & imageMsg)
939 {
940  if(!paused_)
941  {
942  ros::Time stamp = imageMsg->header.stamp;
943  if(stamp.toSec() == 0.0)
944  {
945  ROS_WARN("A null stamp has been detected in the input topic. Make sure the stamp is set.");
946  return;
947  }
948 
949  if(rate_>0.0f)
950  {
951  if(previousStamp_.toSec() > 0.0 && stamp.toSec() > previousStamp_.toSec() && stamp - previousStamp_ < ros::Duration(1.0f/rate_))
952  {
953  return;
954  }
955  }
957 
958  if(!(imageMsg->encoding.compare(sensor_msgs::image_encodings::MONO8) ==0 ||
959  imageMsg->encoding.compare(sensor_msgs::image_encodings::MONO16) ==0 ||
960  imageMsg->encoding.compare(sensor_msgs::image_encodings::BGR8) == 0 ||
961  imageMsg->encoding.compare(sensor_msgs::image_encodings::RGB8) == 0))
962  {
963  NODELET_ERROR("Input type must be image=mono8,mono16,rgb8,bgr8");
964  return;
965  }
966 
968  if(imageMsg->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
969  imageMsg->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0)
970  {
971  ptrImage = cv_bridge::toCvShare(imageMsg, "mono8");
972  }
973  else
974  {
975  ptrImage = cv_bridge::toCvShare(imageMsg, "bgr8");
976  }
977 
978  // process data
979  UTimer timer;
980  if(rtabmap_.isIDsGenerated() || ptrImage->header.seq > 0)
981  {
982  if(!rtabmap_.process(ptrImage->image.clone(), ptrImage->header.seq))
983  {
984  NODELET_WARN("RTAB-Map could not process the data received! (ROS id = %d)", ptrImage->header.seq);
985  }
986  else
987  {
988  this->publishStats(ros::Time::now());
989  }
990  }
991  else if(!rtabmap_.isIDsGenerated())
992  {
993  NODELET_WARN("Ignoring received image because its sequence ID=0. Please "
994  "set \"Mem/GenerateIds\"=\"true\" to ignore ros generated sequence id. "
995  "Use only \"Mem/GenerateIds\"=\"false\" for once-time run of RTAB-Map and "
996  "when you need to have IDs output of RTAB-map synchronised with the source "
997  "image sequence ID.");
998  }
999  NODELET_INFO("rtabmap: Update rate=%fs, Limit=%fs, Processing time = %fs (%d local nodes)",
1000  1.0f/rate_,
1001  rtabmap_.getTimeThreshold()/1000.0f,
1002  timer.ticks(),
1004  }
1005 }
1006 
1007 bool CoreWrapper::odomUpdate(const nav_msgs::OdometryConstPtr & odomMsg, ros::Time stamp)
1008 {
1009  if(!paused_)
1010  {
1011  Transform odom = rtabmap_ros::transformFromPoseMsg(odomMsg->pose.pose);
1012  if(!odom.isNull())
1013  {
1014  Transform odomTF;
1015  if(!stamp.isZero()) {
1016  odomTF = rtabmap_ros::getTransform(odomMsg->header.frame_id, frameId_, stamp, tfListener_, waitForTransform_?waitForTransformDuration_:0.0);
1017  }
1018  if(odomTF.isNull())
1019  {
1020  static bool shown = false;
1021  if(!shown)
1022  {
1023  NODELET_WARN("We received odometry message, but we cannot get the "
1024  "corresponding TF %s->%s at data stamp %fs (odom msg stamp is %fs). Make sure TF of odometry is "
1025  "also published to get more accurate pose estimation. This "
1026  "warning is only printed once.", odomMsg->header.frame_id.c_str(), frameId_.c_str(), stamp.toSec(), odomMsg->header.stamp.toSec());
1027  shown = true;
1028  }
1029  stamp = odomMsg->header.stamp;
1030  }
1031  else
1032  {
1033  odom = odomTF;
1034  }
1035  }
1036 
1037  if(!lastPose_.isIdentity() && !odom.isNull() && (odom.isIdentity() || (odomMsg->pose.covariance[0] >= BAD_COVARIANCE && odomMsg->twist.covariance[0] >= BAD_COVARIANCE)))
1038  {
1039  UWARN("Odometry is reset (identity pose or high variance (%f) detected). Increment map id!", MAX(odomMsg->pose.covariance[0], odomMsg->twist.covariance[0]));
1041  covariance_ = cv::Mat();
1042  }
1043 
1044  lastPoseIntermediate_ = false;
1045  lastPose_ = odom;
1047  lastPoseVelocity_.resize(6);
1048  lastPoseVelocity_[0] = odomMsg->twist.twist.linear.x;
1049  lastPoseVelocity_[1] = odomMsg->twist.twist.linear.y;
1050  lastPoseVelocity_[2] = odomMsg->twist.twist.linear.z;
1051  lastPoseVelocity_[3] = odomMsg->twist.twist.angular.x;
1052  lastPoseVelocity_[4] = odomMsg->twist.twist.angular.y;
1053  lastPoseVelocity_[5] = odomMsg->twist.twist.angular.z;
1054 
1055  // Only update variance if odom is not null
1056  if(!odom.isNull())
1057  {
1058  cv::Mat covariance;
1059  double variance = odomMsg->twist.covariance[0];
1060  if(variance == BAD_COVARIANCE || variance <= 0.0f)
1061  {
1062  //use the one of the pose
1063  covariance = cv::Mat(6,6,CV_64FC1, (void*)odomMsg->pose.covariance.data()).clone();
1064  covariance /= 2.0;
1065  }
1066  else
1067  {
1068  covariance = cv::Mat(6,6,CV_64FC1, (void*)odomMsg->twist.covariance.data()).clone();
1069  }
1070 
1071  if(uIsFinite(covariance.at<double>(0,0)) &&
1072  covariance.at<double>(0,0) != 1.0 &&
1073  covariance.at<double>(0,0)>0.0)
1074  {
1075  // Use largest covariance error (to be independent of the odometry frame rate)
1076  if(covariance_.empty() || covariance.at<double>(0,0) > covariance_.at<double>(0,0))
1077  {
1079  }
1080  }
1081  }
1082 
1083  // Throttle
1084  bool ignoreFrame = false;
1085  if(stamp.toSec() == 0.0)
1086  {
1087  ROS_WARN("A null stamp has been detected in the input topics. Make sure the stamp in all input topics is set.");
1088  ignoreFrame = true;
1089  }
1090  if(rate_>0.0f)
1091  {
1092  if(previousStamp_.toSec() > 0.0 && stamp.toSec() > previousStamp_.toSec() && stamp - previousStamp_ < ros::Duration(1.0f/rate_))
1093  {
1094  ignoreFrame = true;
1095  }
1096  }
1097  if(ignoreFrame)
1098  {
1100  {
1101  lastPoseIntermediate_ = true;
1102  }
1103  else
1104  {
1105  return false;
1106  }
1107  }
1108  else if(!ignoreFrame)
1109  {
1111  }
1112 
1113  return true;
1114  }
1115  return false;
1116 }
1117 
1119 {
1120  if(!paused_)
1121  {
1122  // Odom TF ready?
1124  if(odom.isNull())
1125  {
1126  return false;
1127  }
1128 
1129  if(!lastPose_.isIdentity() && odom.isIdentity())
1130  {
1131  UWARN("Odometry is reset (identity pose detected). Increment map id!");
1133  covariance_ = cv::Mat();
1134  }
1135 
1136  lastPoseIntermediate_ = false;
1137  lastPose_ = odom;
1139  lastPoseVelocity_.clear();
1140 
1141  bool ignoreFrame = false;
1142  if(stamp.toSec() == 0.0)
1143  {
1144  ROS_WARN("A null stamp has been detected in the input topics. Make sure the stamp in all input topics is set.");
1145  ignoreFrame = true;
1146  }
1147  if(rate_>0.0f)
1148  {
1149  if(previousStamp_.toSec() > 0.0 && stamp.toSec() > previousStamp_.toSec() && stamp - previousStamp_ < ros::Duration(1.0f/rate_))
1150  {
1151  ignoreFrame = true;
1152  }
1153  }
1154  if(ignoreFrame)
1155  {
1157  {
1158  lastPoseIntermediate_ = true;
1159  }
1160  else
1161  {
1162  return false;
1163  }
1164  }
1165  else if(!ignoreFrame)
1166  {
1168  }
1169 
1170  return true;
1171  }
1172  return false;
1173 }
1174 
1176  const nav_msgs::OdometryConstPtr & odomMsg,
1177  const rtabmap_ros::UserDataConstPtr & userDataMsg,
1178  const std::vector<cv_bridge::CvImageConstPtr> & imageMsgs,
1179  const std::vector<cv_bridge::CvImageConstPtr> & depthMsgs,
1180  const std::vector<sensor_msgs::CameraInfo> & cameraInfoMsgs,
1181  const std::vector<sensor_msgs::CameraInfo> & depthCameraInfoMsgs,
1182  const sensor_msgs::LaserScan& scan2dMsg,
1183  const sensor_msgs::PointCloud2& scan3dMsg,
1184  const rtabmap_ros::OdomInfoConstPtr& odomInfoMsg,
1185  const std::vector<rtabmap_ros::GlobalDescriptor> & globalDescriptorMsgs,
1186  const std::vector<std::vector<rtabmap_ros::KeyPoint> > & localKeyPoints,
1187  const std::vector<std::vector<rtabmap_ros::Point3f> > & localPoints3d,
1188  const std::vector<cv::Mat> & localDescriptors)
1189 {
1190  std::string odomFrameId = odomFrameId_;
1191  if(odomMsg.get())
1192  {
1193  odomFrameId = odomMsg->header.frame_id;
1194  if(!scan2dMsg.ranges.empty())
1195  {
1196  if(!odomUpdate(odomMsg, scan2dMsg.header.stamp))
1197  {
1198  return;
1199  }
1200  }
1201  else if(!scan3dMsg.data.empty())
1202  {
1203  if(!odomUpdate(odomMsg, scan3dMsg.header.stamp))
1204  {
1205  return;
1206  }
1207  }
1208  else if(cameraInfoMsgs.size() == 0 || !odomUpdate(odomMsg, cameraInfoMsgs[0].header.stamp))
1209  {
1210  return;
1211  }
1212  }
1213  else if(!scan2dMsg.ranges.empty())
1214  {
1215  if(!odomTFUpdate(scan2dMsg.header.stamp))
1216  {
1217  return;
1218  }
1219  }
1220  else if(!scan3dMsg.data.empty())
1221  {
1222  if(!odomTFUpdate(scan3dMsg.header.stamp))
1223  {
1224  return;
1225  }
1226  }
1227  else if(cameraInfoMsgs.size() == 0 || !odomTFUpdate(cameraInfoMsgs[0].header.stamp))
1228  {
1229  return;
1230  }
1231 
1232  commonMultiCameraCallbackImpl(odomFrameId,
1233  userDataMsg,
1234  imageMsgs,
1235  depthMsgs,
1236  cameraInfoMsgs,
1237  depthCameraInfoMsgs,
1238  scan2dMsg,
1239  scan3dMsg,
1240  odomInfoMsg,
1241  globalDescriptorMsgs,
1242  localKeyPoints,
1243  localPoints3d,
1244  localDescriptors);
1245 }
1246 
1248  const std::string & odomFrameId,
1249  const rtabmap_ros::UserDataConstPtr & userDataMsg,
1250  const std::vector<cv_bridge::CvImageConstPtr> & imageMsgs,
1251  const std::vector<cv_bridge::CvImageConstPtr> & depthMsgs,
1252  const std::vector<sensor_msgs::CameraInfo> & cameraInfoMsgs,
1253  const std::vector<sensor_msgs::CameraInfo> & depthCameraInfoMsgs,
1254  const sensor_msgs::LaserScan& scan2dMsg,
1255  const sensor_msgs::PointCloud2& scan3dMsg,
1256  const rtabmap_ros::OdomInfoConstPtr& odomInfoMsg,
1257  const std::vector<rtabmap_ros::GlobalDescriptor> & globalDescriptorMsgs,
1258  const std::vector<std::vector<rtabmap_ros::KeyPoint> > & localKeyPointsMsgs,
1259  const std::vector<std::vector<rtabmap_ros::Point3f> > & localPoints3dMsgs,
1260  const std::vector<cv::Mat> & localDescriptorsMsgs)
1261 {
1262  UTimer timerConversion;
1263  cv::Mat rgb;
1264  cv::Mat depth;
1265  std::vector<rtabmap::CameraModel> cameraModels;
1266  std::vector<rtabmap::StereoCameraModel> stereoCameraModels;
1267  std::vector<cv::KeyPoint> keypoints;
1268  std::vector<cv::Point3f> points;
1269  cv::Mat descriptors;
1270 
1272  imageMsgs,
1273  depthMsgs,
1274  cameraInfoMsgs,
1275  depthCameraInfoMsgs,
1276  frameId_,
1277  odomSensorSync_?odomFrameId:"",
1279  rgb,
1280  depth,
1281  cameraModels,
1282  stereoCameraModels,
1283  tfListener_,
1286  localKeyPointsMsgs,
1287  localPoints3dMsgs,
1288  localDescriptorsMsgs,
1289  &keypoints,
1290  &points,
1291  &descriptors))
1292  {
1293  NODELET_ERROR("Could not convert rgb/depth msgs! Aborting rtabmap update...");
1294  return;
1295  }
1296  UDEBUG("cameraModels=%ld stereoCameraModels=%ld", cameraModels.size(), stereoCameraModels.size());
1297  UDEBUG("rgb=%dx%d(type=%d), depth/right=%dx%d(type=%d)", rgb.rows, rgb.cols, rgb.type(), depth.rows, depth.cols, depth.type());
1298 
1299  if(stereoCameraModels.size() && stereoToDepth_)
1300  {
1301  UASSERT(depth.type() == CV_8UC1);
1302  cv::Mat leftMono;
1303  if(rgb.channels() == 3)
1304  {
1305  cv::cvtColor(rgb, leftMono, CV_BGR2GRAY);
1306  }
1307  else
1308  {
1309  leftMono = rgb;
1310  }
1311  cv::Mat rightMono = depth;
1312  depth = cv::Mat();
1313 
1314  UASSERT(int((leftMono.cols/stereoCameraModels.size())*stereoCameraModels.size()) == leftMono.cols);
1315  UASSERT(int((rightMono.cols/stereoCameraModels.size())*stereoCameraModels.size()) == rightMono.cols);
1316  int subImageWidth = leftMono.cols/stereoCameraModels.size();
1317  for(size_t i=0; i<stereoCameraModels.size(); ++i)
1318  {
1319  cv::Mat left(leftMono, cv::Rect(subImageWidth*i, 0, subImageWidth, leftMono.rows));
1320  cv::Mat right(rightMono, cv::Rect(subImageWidth*i, 0, subImageWidth, rightMono.rows));
1321 
1322  // cv::stereoBM() see "$ rosrun rtabmap_ros rtabmap --params | grep StereoBM" for parameters
1323  cv::Mat disparity = rtabmap::util2d::disparityFromStereoImages(
1324  left,
1325  right,
1326  parameters_);
1327  if(disparity.empty())
1328  {
1329  NODELET_ERROR("Could not compute disparity image (\"stereo_to_depth\" is true)!");
1330  return;
1331  }
1332  cv::Mat subDepth = rtabmap::util2d::depthFromDisparity(
1333  disparity,
1334  stereoCameraModels[i].left().fx(),
1335  stereoCameraModels[i].baseline());
1336 
1337  if(subDepth.empty())
1338  {
1339  NODELET_ERROR("Could not compute depth image (\"stereo_to_depth\" is true)!");
1340  return;
1341  }
1342  UASSERT(subDepth.type() == CV_16UC1 || subDepth.type() == CV_32FC1);
1343 
1344  if(depth.empty())
1345  {
1346  depth = cv::Mat(subDepth.rows, subDepth.cols*stereoCameraModels.size(), subDepth.type());
1347  }
1348 
1349  if(subDepth.type() == depth.type())
1350  {
1351  subDepth.copyTo(cv::Mat(depth, cv::Rect(i*subDepth.cols, 0, subDepth.cols, subDepth.rows)));
1352  }
1353  else
1354  {
1355  ROS_ERROR("Some Depth images are not the same type!");
1356  return;
1357  }
1358 
1359  cameraModels.push_back(stereoCameraModels[i].left());
1360  }
1361  stereoCameraModels.clear();
1362  }
1363 
1364  UASSERT(uContains(parameters_, rtabmap::Parameters::kMemSaveDepth16Format()));
1365  if(!depth.empty() && depth.type() == CV_32FC1 && uStr2Bool(parameters_.at(Parameters::kMemSaveDepth16Format())))
1366  {
1367  depth = rtabmap::util2d::cvtDepthFromFloat(depth);
1368  static bool shown = false;
1369  if(!shown)
1370  {
1371  NODELET_WARN("Save depth data to 16 bits format: depth type detected is "
1372  "32FC1, use 16UC1 depth format to avoid this conversion "
1373  "(or set parameter \"Mem/SaveDepth16Format=false\" to use "
1374  "32bits format). This message is only printed once...");
1375  shown = true;
1376  }
1377  }
1378 
1379  LaserScan scan;
1380  bool genMaxScanPts = 0;
1381  if(scan2dMsg.ranges.empty() && scan3dMsg.data.empty() && !depth.empty() && stereoCameraModels.empty() && genScan_)
1382  {
1383  pcl::PointCloud<pcl::PointXYZ>::Ptr scanCloud2d(new pcl::PointCloud<pcl::PointXYZ>);
1384  *scanCloud2d = util3d::laserScanFromDepthImages(
1385  depth,
1386  cameraModels,
1389  genMaxScanPts += depth.cols;
1391  }
1392  else if(!scan2dMsg.ranges.empty())
1393  {
1395  scan2dMsg,
1396  frameId_,
1397  odomSensorSync_?odomFrameId:"",
1399  scan,
1400  tfListener_,
1401  waitForTransform_?waitForTransformDuration_:0,
1402  // backward compatibility, project 2D scan in /base_link frame
1404  {
1405  NODELET_ERROR("Could not convert laser scan msg! Aborting rtabmap update...");
1406  return;
1407  }
1408  }
1409  else if(!scan3dMsg.data.empty())
1410  {
1412  scan3dMsg,
1413  frameId_,
1414  odomSensorSync_?odomFrameId:"",
1416  scan,
1417  tfListener_,
1418  waitForTransform_?waitForTransformDuration_:0,
1420  0,
1421  scanCloudIs2d_))
1422  {
1423  NODELET_ERROR("Could not convert 3d laser scan msg! Aborting rtabmap update...");
1424  return;
1425  }
1426 
1427  ROS_DEBUG("%d %d %d %d", rgb.empty()?1:0, depth.empty()?1:0, scan.isEmpty()?1:0, genDepth_?1:0);
1428  if(!rgb.empty() && depth.empty() && !scan.isEmpty() && genDepth_)
1429  {
1430  for(size_t i=0; i<cameraModels.size(); ++i)
1431  {
1432  rtabmap::CameraModel model = cameraModels[i];
1433  if(genDepthDecimation_ > 1)
1434  {
1435  if(model.imageWidth()%genDepthDecimation_ == 0 && model.imageHeight()%genDepthDecimation_ == 0)
1436  {
1437  model = model.scaled(1.0f/float(genDepthDecimation_));
1438  }
1439  else
1440  {
1441  ROS_ERROR("decimation (%d) not valid for image size %dx%d! Aborting depth generation from scan...",
1443  model.imageWidth(),
1444  model.imageHeight());
1445  depth = cv::Mat();
1446  break;
1447  }
1448  }
1449 
1450  cv::Mat depthProjected = util3d::projectCloudToCamera(
1451  model.imageSize(),
1452  model.K(),
1453  scan.data(),
1454  scan.localTransform().inverse()*model.localTransform());
1455 
1457  {
1458  for(int i=0; i<genDepthFillIterations_;++i)
1459  {
1460  depthProjected = util2d::fillDepthHoles(
1461  depthProjected,
1464  }
1465  }
1466 
1467  if(depth.empty())
1468  {
1469  depth = cv::Mat::zeros(model.imageHeight(), model.imageWidth()*cameraModels.size(), CV_32FC1);
1470  }
1471  depthProjected.copyTo(depth.colRange(i*model.imageWidth(), (i+1)*model.imageWidth()));
1472  }
1473  }
1474  }
1475 
1476  cv::Mat userData;
1477  if(userDataMsg.get())
1478  {
1479  userData = rtabmap_ros::userDataFromROS(*userDataMsg);
1481  if(!userData_.empty())
1482  {
1483  NODELET_WARN("Synchronized and asynchronized user data topics cannot be used at the same time. Async user data dropped!");
1484  userData_ = cv::Mat();
1485  }
1486  }
1487  else
1488  {
1490  userData = userData_;
1491  userData_ = cv::Mat();
1492  }
1493 
1494  SensorData data;
1495  if(!stereoCameraModels.empty())
1496  {
1497  data = SensorData(
1498  scan,
1499  rgb,
1500  depth,
1501  stereoCameraModels,
1502  lastPoseIntermediate_?-1:!cameraInfoMsgs.empty()?cameraInfoMsgs[0].header.seq:0,
1504  userData);
1505  }
1506  else
1507  {
1508  data = SensorData(
1509  scan,
1510  rgb,
1511  depth,
1512  cameraModels,
1513  lastPoseIntermediate_?-1:!cameraInfoMsgs.empty()?cameraInfoMsgs[0].header.seq:0,
1515  userData);
1516  }
1517 
1518  OdometryInfo odomInfo;
1519  if(odomInfoMsg.get())
1520  {
1521  odomInfo = odomInfoFromROS(*odomInfoMsg);
1522  }
1523 
1524  if(!globalDescriptorMsgs.empty())
1525  {
1526  data.setGlobalDescriptors(rtabmap_ros::globalDescriptorsFromROS(globalDescriptorMsgs));
1527  }
1528 
1529  if(!keypoints.empty())
1530  {
1531  UASSERT(points.empty() || points.size() == keypoints.size());
1532  UASSERT(descriptors.empty() || descriptors.rows == (int)keypoints.size());
1533  data.setFeatures(keypoints, points, descriptors);
1534  }
1535 
1537  data,
1538  lastPose_,
1540  odomFrameId,
1541  covariance_,
1542  odomInfo,
1543  timerConversion.ticks());
1544  covariance_ = cv::Mat();
1545 }
1546 
1548  const nav_msgs::OdometryConstPtr & odomMsg,
1549  const rtabmap_ros::UserDataConstPtr & userDataMsg,
1550  const sensor_msgs::LaserScan& scan2dMsg,
1551  const sensor_msgs::PointCloud2& scan3dMsg,
1552  const rtabmap_ros::OdomInfoConstPtr& odomInfoMsg,
1553  const rtabmap_ros::GlobalDescriptor & globalDescriptor)
1554 {
1555  UTimer timerConversion;
1556  std::string odomFrameId = odomFrameId_;
1557  if(odomMsg.get())
1558  {
1559  odomFrameId = odomMsg->header.frame_id;
1560  if(!scan2dMsg.ranges.empty())
1561  {
1562  if(!odomUpdate(odomMsg, scan2dMsg.header.stamp))
1563  {
1564  return;
1565  }
1566  }
1567  else if(!scan3dMsg.data.empty())
1568  {
1569  if(!odomUpdate(odomMsg, scan3dMsg.header.stamp))
1570  {
1571  return;
1572  }
1573  }
1574  else
1575  {
1576  return;
1577  }
1578  }
1579  else if(!scan2dMsg.ranges.empty())
1580  {
1581  if(!odomTFUpdate(scan2dMsg.header.stamp))
1582  {
1583  return;
1584  }
1585  }
1586  else if(!scan3dMsg.data.empty())
1587  {
1588  if(!odomTFUpdate(scan3dMsg.header.stamp))
1589  {
1590  return;
1591  }
1592  }
1593  else
1594  {
1595  return;
1596  }
1597 
1598  LaserScan scan;
1599  if(!scan2dMsg.ranges.empty())
1600  {
1602  scan2dMsg,
1603  frameId_,
1604  odomSensorSync_?odomFrameId:"",
1606  scan,
1607  tfListener_,
1609  // backward compatibility, project 2D scan in /base_link frame
1611  {
1612  NODELET_ERROR("Could not convert laser scan msg! Aborting rtabmap update...");
1613  return;
1614  }
1615  }
1616  else if(!scan3dMsg.data.empty())
1617  {
1619  scan3dMsg,
1620  frameId_,
1621  odomSensorSync_?odomFrameId:"",
1623  scan,
1624  tfListener_,
1627  0,
1628  scanCloudIs2d_))
1629  {
1630  NODELET_ERROR("Could not convert 3d laser scan msg! Aborting rtabmap update...");
1631  return;
1632  }
1633  }
1634 
1635  cv::Mat userData;
1636  if(userDataMsg.get())
1637  {
1638  userData = rtabmap_ros::userDataFromROS(*userDataMsg);
1640  if(!userData_.empty())
1641  {
1642  NODELET_WARN("Synchronized and asynchronized user data topics cannot be used at the same time. Async user data dropped!");
1643  userData_ = cv::Mat();
1644  }
1645  }
1646  else
1647  {
1649  userData = userData_;
1650  userData_ = cv::Mat();
1651  }
1652 
1653  SensorData data(
1654  scan,
1655  cv::Mat(),
1656  cv::Mat(),
1658  lastPoseIntermediate_?-1:!scan2dMsg.ranges.empty()?scan2dMsg.header.seq:scan3dMsg.header.seq,
1660  userData);
1661 
1662  OdometryInfo odomInfo;
1663  if(odomInfoMsg.get())
1664  {
1665  odomInfo = odomInfoFromROS(*odomInfoMsg);
1666  }
1667 
1668  if(!globalDescriptor.data.empty())
1669  {
1670  data.addGlobalDescriptor(rtabmap_ros::globalDescriptorFromROS(globalDescriptor));
1671  }
1672 
1674  data,
1675  lastPose_,
1677  odomFrameId,
1678  covariance_,
1679  odomInfo,
1680  timerConversion.ticks());
1681 
1682  covariance_ = cv::Mat();
1683 }
1684 
1686  const nav_msgs::OdometryConstPtr & odomMsg,
1687  const rtabmap_ros::UserDataConstPtr & userDataMsg,
1688  const rtabmap_ros::OdomInfoConstPtr& odomInfoMsg)
1689 {
1690  UTimer timerConversion;
1691  UASSERT(odomMsg.get());
1692  std::string odomFrameId = odomFrameId_;
1693 
1694  odomFrameId = odomMsg->header.frame_id;
1695  if(!odomUpdate(odomMsg, odomMsg->header.stamp))
1696  {
1697  return;
1698  }
1699 
1700  cv::Mat userData;
1701  if(userDataMsg.get())
1702  {
1703  userData = rtabmap_ros::userDataFromROS(*userDataMsg);
1705  if(!userData_.empty())
1706  {
1707  NODELET_WARN("Synchronized and asynchronized user data topics cannot be used at the same time. Async user data dropped!");
1708  userData_ = cv::Mat();
1709  }
1710  }
1711  else
1712  {
1714  userData = userData_;
1715  userData_ = cv::Mat();
1716  }
1717 
1718  SensorData data(
1719  cv::Mat(),
1720  cv::Mat(),
1722  lastPoseIntermediate_?-1:odomMsg->header.seq,
1724  userData);
1725 
1726  OdometryInfo odomInfo;
1727  if(odomInfoMsg.get())
1728  {
1729  odomInfo = odomInfoFromROS(*odomInfoMsg);
1730  }
1731 
1733  data,
1734  lastPose_,
1736  odomFrameId,
1737  covariance_,
1738  odomInfo,
1739  timerConversion.ticks());
1740 
1741  covariance_ = cv::Mat();
1742 }
1743 
1745  const ros::Time & stamp,
1746  SensorData & data,
1747  const Transform & odom,
1748  const std::vector<float> & odomVelocityIn,
1749  const std::string & odomFrameId,
1750  const cv::Mat & odomCovariance,
1751  const OdometryInfo & odomInfo,
1752  double timeMsgConversion)
1753 {
1754  UTimer timer;
1755  if(rtabmap_.isIDsGenerated() || data.id() > 0)
1756  {
1757  // Add intermediate nodes?
1758  for(std::list<std::pair<nav_msgs::Odometry, rtabmap_ros::OdomInfo> >::iterator iter=interOdoms_.begin(); iter!=interOdoms_.end();)
1759  {
1760  if(iter->first.header.stamp < lastPoseStamp_)
1761  {
1762  Transform interOdom;
1763  if(!rtabmap_.getLocalOptimizedPoses().empty())
1764  {
1765  // add intermediate poses only if the current local graph is not empty
1766  interOdom = rtabmap_ros::transformFromPoseMsg(iter->first.pose.pose);
1767  }
1768  if(!interOdom.isNull())
1769  {
1770  cv::Mat covariance;
1771  double variance = iter->first.twist.covariance[0];
1772  if(variance == BAD_COVARIANCE || variance <= 0.0f)
1773  {
1774  //use the one of the pose
1775  covariance = cv::Mat(6,6,CV_64FC1, (void*)iter->first.pose.covariance.data()).clone();
1776  covariance /= 2.0;
1777  }
1778  else
1779  {
1780  covariance = cv::Mat(6,6,CV_64FC1, (void*)iter->first.twist.covariance.data()).clone();
1781  }
1782  if(!uIsFinite(covariance.at<double>(0,0)) || covariance.at<double>(0,0)<=0.0f)
1783  {
1784  covariance = cv::Mat::eye(6,6,CV_64FC1);
1785  if(odomDefaultLinVariance_ > 0.0f)
1786  {
1787  covariance.at<double>(0,0) = odomDefaultLinVariance_;
1788  covariance.at<double>(1,1) = odomDefaultLinVariance_;
1789  covariance.at<double>(2,2) = odomDefaultLinVariance_;
1790  }
1791  if(odomDefaultAngVariance_ > 0.0f)
1792  {
1793  covariance.at<double>(3,3) = odomDefaultAngVariance_;
1794  covariance.at<double>(4,4) = odomDefaultAngVariance_;
1795  covariance.at<double>(5,5) = odomDefaultAngVariance_;
1796  }
1797  }
1798  else if(twoDMapping_)
1799  {
1800  // If 2d mapping, make sure all diagonal values of the covariance that even not used are not null.
1801  covariance.at<double>(2,2) = uIsFinite(covariance.at<double>(2,2)) && covariance.at<double>(2,2)!=0?covariance.at<double>(2,2):1;
1802  covariance.at<double>(3,3) = uIsFinite(covariance.at<double>(3,3)) && covariance.at<double>(3,3)!=0?covariance.at<double>(3,3):1;
1803  covariance.at<double>(4,4) = uIsFinite(covariance.at<double>(4,4)) && covariance.at<double>(4,4)!=0?covariance.at<double>(4,4):1;
1804  }
1805 
1806  SensorData interData(cv::Mat(), cv::Mat(), rtabmap::CameraModel(), -1, rtabmap_ros::timestampFromROS(iter->first.header.stamp));
1807  Transform gt;
1808  if(!groundTruthFrameId_.empty())
1809  {
1811  }
1812  interData.setGroundTruth(gt);
1813 
1814  std::map<std::string, float> externalStats;
1815  std::vector<float> odomVelocity;
1816  if(iter->second.timeEstimation != 0.0f)
1817  {
1818  OdometryInfo info = odomInfoFromROS(iter->second, true);
1819  externalStats = rtabmap_ros::odomInfoToStatistics(info);
1820 
1821  if(info.interval>0.0)
1822  {
1823  odomVelocity.resize(6);
1824  float x,y,z,roll,pitch,yaw;
1825  info.transform.getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
1826  odomVelocity[0] = x/info.interval;
1827  odomVelocity[1] = y/info.interval;
1828  odomVelocity[2] = z/info.interval;
1829  odomVelocity[3] = roll/info.interval;
1830  odomVelocity[4] = pitch/info.interval;
1831  odomVelocity[5] = yaw/info.interval;
1832  }
1833  }
1834  if(odomVelocity.empty())
1835  {
1836  odomVelocity.resize(6);
1837  odomVelocity[0] = iter->first.twist.twist.linear.x;
1838  odomVelocity[1] = iter->first.twist.twist.linear.y;
1839  odomVelocity[2] = iter->first.twist.twist.linear.z;
1840  odomVelocity[3] = iter->first.twist.twist.angular.x;
1841  odomVelocity[4] = iter->first.twist.twist.angular.y;
1842  odomVelocity[5] = iter->first.twist.twist.angular.z;
1843  }
1844 
1845  rtabmap_.process(interData, interOdom, covariance, odomVelocity, externalStats);
1846  }
1847  interOdoms_.erase(iter++);
1848  }
1849  else if(iter->first.header.stamp == lastPoseStamp_)
1850  {
1851  interOdoms_.erase(iter++);
1852  break;
1853  }
1854  else
1855  {
1856  break;
1857  }
1858  }
1859 
1860  //Add async stuff
1861  Transform groundTruthPose;
1862  if(!groundTruthFrameId_.empty())
1863  {
1865  }
1866  data.setGroundTruth(groundTruthPose);
1867 
1868  //global pose
1869  if(!globalPose_.header.stamp.isZero())
1870  {
1871  // assume sensor is fixed
1872  Transform sensorToBase = rtabmap_ros::getTransform(
1873  globalPose_.header.frame_id,
1874  frameId_,
1876  tfListener_,
1878  if(!sensorToBase.isNull())
1879  {
1881  globalPose *= sensorToBase; // transform global pose from sensor frame to robot base frame
1882 
1883  // Correction of the global pose accounting the odometry movement since we received it
1884  Transform correction = rtabmap_ros::getTransform(
1885  frameId_,
1886  odomFrameId,
1887  globalPose_.header.stamp,
1889  tfListener_,
1890  waitForTransform_?waitForTransformDuration_:0.0);
1891  if(!correction.isNull())
1892  {
1893  globalPose *= correction;
1894  }
1895  else
1896  {
1897  NODELET_WARN("Could not adjust global pose accordingly to latest odometry pose. "
1898  "If odometry is small since it received the global pose and "
1899  "covariance is large, this should not be a problem.");
1900  }
1901  cv::Mat globalPoseCovariance = cv::Mat(6,6, CV_64FC1, (void*)globalPose_.pose.covariance.data()).clone();
1902  data.setGlobalPose(globalPose, globalPoseCovariance);
1903  }
1904  }
1905  globalPose_.header.stamp = ros::Time(0);
1906 
1907  if(gps_.stamp() > 0.0)
1908  {
1909  data.setGPS(gps_);
1910  }
1911  gps_ = rtabmap::GPS();
1912 
1913  //tag detections
1915  tags_,
1916  frameId_,
1917  odomFrameId,
1919  tfListener_,
1923  tags_.clear();
1924  if(!landmarks.empty())
1925  {
1926  data.setLandmarks(landmarks);
1927  }
1928 
1929  // IMU
1930  if(!imus_.empty())
1931  {
1933  if(!t.isNull())
1934  {
1935  // get local transform
1936  rtabmap::Transform localTransform;
1937  if(frameId_.compare(imuFrameId_) != 0)
1938  {
1939  localTransform = getTransform(frameId_, imuFrameId_, ros::Time(data.stamp()), tfListener_, waitForTransform_?waitForTransformDuration_:0.0);
1940  }
1941  else
1942  {
1943  localTransform = rtabmap::Transform::getIdentity();
1944  }
1945 
1946  if(!localTransform.isNull())
1947  {
1948  Eigen::Quaterniond q = t.getQuaterniond();
1949  data.setIMU(IMU(cv::Vec4d(q.x(), q.y(), q.z(), q.w()), cv::Mat::eye(3,3,CV_64FC1),
1950  cv::Vec3d(), cv::Mat(),
1951  cv::Vec3d(), cv::Mat(),
1952  localTransform));
1953  }
1954  }
1955  else
1956  {
1957  ROS_WARN("We are receiving imu data (buffer=%d), but cannot interpolate "
1958  "imu transform at time %f. IMU won't be added to graph.",
1959  (int)imus_.size(), data.stamp());
1960  }
1961  }
1962 
1963  double timeRtabmap = 0.0;
1964  double timeUpdateMaps = 0.0;
1965  double timePublishMaps = 0.0;
1966 
1967  cv::Mat covariance = odomCovariance;
1968  if(covariance.empty() || !uIsFinite(covariance.at<double>(0,0)) || covariance.at<double>(0,0)<=0.0f)
1969  {
1970  covariance = cv::Mat::eye(6,6,CV_64FC1);
1971  if(odomDefaultLinVariance_ > 0.0f)
1972  {
1973  covariance.at<double>(0,0) = odomDefaultLinVariance_;
1974  covariance.at<double>(1,1) = odomDefaultLinVariance_;
1975  covariance.at<double>(2,2) = odomDefaultLinVariance_;
1976  }
1977  if(odomDefaultAngVariance_ > 0.0f)
1978  {
1979  covariance.at<double>(3,3) = odomDefaultAngVariance_;
1980  covariance.at<double>(4,4) = odomDefaultAngVariance_;
1981  covariance.at<double>(5,5) = odomDefaultAngVariance_;
1982  }
1983  }
1984  else if(twoDMapping_)
1985  {
1986  // If 2d mapping, make sure all diagonal values of the covariance that even not used are not null.
1987  covariance.at<double>(2,2) = uIsFinite(covariance.at<double>(2,2)) && covariance.at<double>(2,2)!=0?covariance.at<double>(2,2):1;
1988  covariance.at<double>(3,3) = uIsFinite(covariance.at<double>(3,3)) && covariance.at<double>(3,3)!=0?covariance.at<double>(3,3):1;
1989  covariance.at<double>(4,4) = uIsFinite(covariance.at<double>(4,4)) && covariance.at<double>(4,4)!=0?covariance.at<double>(4,4):1;
1990  }
1991 
1992  std::map<std::string, float> externalStats;
1993  std::vector<float> odomVelocity;
1994  if(odomInfo.timeEstimation != 0.0f)
1995  {
1996  externalStats = rtabmap_ros::odomInfoToStatistics(odomInfo);
1997 
1998  if(odomInfo.interval>0.0)
1999  {
2000  odomVelocity.resize(6);
2001  float x,y,z,roll,pitch,yaw;
2002  odomInfo.transform.getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
2003  odomVelocity[0] = x/odomInfo.interval;
2004  odomVelocity[1] = y/odomInfo.interval;
2005  odomVelocity[2] = z/odomInfo.interval;
2006  odomVelocity[3] = roll/odomInfo.interval;
2007  odomVelocity[4] = pitch/odomInfo.interval;
2008  odomVelocity[5] = yaw/odomInfo.interval;
2009  }
2010  }
2011  if(odomVelocity.empty())
2012  {
2013  odomVelocity = odomVelocityIn;
2014  }
2015  if(rtabmapROSStats_.size())
2016  {
2017  externalStats.insert(rtabmapROSStats_.begin(), rtabmapROSStats_.end());
2018  rtabmapROSStats_.clear();
2019  }
2020 
2021  timeMsgConversion += timer.ticks();
2022  if(rtabmap_.process(data, odom, covariance, odomVelocity, externalStats))
2023  {
2024  timeRtabmap = timer.ticks();
2025  mapToOdomMutex_.lock();
2027 
2028  if(!odomFrameId.empty() && !odomFrameId_.empty() && odomFrameId_.compare(odomFrameId)!=0)
2029  {
2030  ROS_ERROR("Odometry received doesn't have same frame_id "
2031  "than the one previously set (old=%s, new=%s). "
2032  "Are there multiple nodes publishing on same odometry topic name? "
2033  "The new frame_id is now used.", odomFrameId_.c_str(), odomFrameId.c_str());
2034  }
2035 
2036  odomFrameId_ = odomFrameId;
2037  mapToOdomMutex_.unlock();
2038 
2039  if(data.id() < 0)
2040  {
2041  NODELET_INFO("Intermediate node added");
2042  }
2043  else
2044  {
2045  // Publish local graph, info
2046  this->publishStats(stamp);
2049  {
2050  geometry_msgs::PoseWithCovarianceStamped poseMsg;
2051  poseMsg.header.frame_id = mapFrameId_;
2052  poseMsg.header.stamp = stamp;
2053  rtabmap_ros::transformToPoseMsg(mapToOdom_*odom, poseMsg.pose.pose);
2054  poseMsg.pose.covariance;
2055  const cv::Mat & cov = rtabmap_.getStatistics().localizationCovariance();
2056  memcpy(poseMsg.pose.covariance.data(), cov.data, cov.total()*sizeof(double));
2057  localizationPosePub_.publish(poseMsg);
2058  }
2059  std::map<int, rtabmap::Transform> filteredPoses(rtabmap_.getLocalOptimizedPoses().lower_bound(1), rtabmap_.getLocalOptimizedPoses().end());
2060 
2061  // create a tmp signature with latest sensory data if latest signature was ignored
2062  std::map<int, rtabmap::Signature> tmpSignature;
2063  if(rtabmap_.getMemory() == 0 ||
2064  filteredPoses.size() == 0 ||
2065  rtabmap_.getMemory()->getLastSignatureId() != filteredPoses.rbegin()->first ||
2068  (!mapsManager_.getOccupancyGrid()->isGridFromDepth() && data.laserScanRaw().is2d())) // 2d laser scan would fill empty space for latest data
2069  {
2070  SensorData tmpData = data;
2071  tmpData.setId(0);
2072  tmpSignature.insert(std::make_pair(0, Signature(0, -1, 0, data.stamp(), "", odom, Transform(), tmpData)));
2073  filteredPoses.insert(std::make_pair(0, mapToOdom_*odom));
2074  }
2075 
2076  if((mappingMaxNodes_ > 0 || mappingAltitudeDelta_>0.0) && filteredPoses.size()>1)
2077  {
2078  std::map<int, Transform> nearestPoses = filterNodesToAssemble(filteredPoses, mapToOdom_*odom);
2079 
2080  //add latest/zero and make sure those on a planned path are not filtered
2081  std::set<int> onPath;
2082  if(rtabmap_.getPath().size())
2083  {
2084  std::vector<int> nextNodes = rtabmap_.getPathNextNodes();
2085  onPath.insert(nextNodes.begin(), nextNodes.end());
2086  }
2087  for(std::map<int, Transform>::iterator iter=filteredPoses.begin(); iter!=filteredPoses.end(); ++iter)
2088  {
2089  if(iter->first == 0 || onPath.find(iter->first) != onPath.end())
2090  {
2091  nearestPoses.insert(*iter);
2092  }
2093  else if(onPath.empty())
2094  {
2095  break;
2096  }
2097  }
2098 
2099  filteredPoses = nearestPoses;
2100  }
2101 
2102  // Update maps
2103  filteredPoses = mapsManager_.updateMapCaches(
2104  filteredPoses,
2105  rtabmap_.getMemory(),
2106  false,
2107  false,
2108  tmpSignature);
2109 
2110  timeUpdateMaps = timer.ticks();
2111 
2112  mapsManager_.publishMaps(filteredPoses, stamp, mapFrameId_);
2113 
2114  // update goal if planning is enabled
2115  if(!currentMetricGoal_.isNull())
2116  {
2117  if(rtabmap_.getPath().size() == 0)
2118  {
2119  // Don't send status yet if move_base actionlib is used unless it failed,
2120  // let move_base finish reaching the goal
2121  if(mbClient_ == 0 || rtabmap_.getPathStatus() <= 0)
2122  {
2123  if(rtabmap_.getPathStatus() > 0)
2124  {
2125  // Goal reached
2126  NODELET_INFO("Planning: Publishing goal reached!");
2127  }
2128  else if(rtabmap_.getPathStatus() <= 0)
2129  {
2130  NODELET_WARN("Planning: Plan failed!");
2132  {
2133  mbClient_->cancelGoal();
2134  }
2135  }
2136 
2138  {
2139  std_msgs::Bool result;
2140  result.data = rtabmap_.getPathStatus() > 0;
2141  goalReachedPub_.publish(result);
2142  }
2145  goalFrameId_.clear();
2146  latestNodeWasReached_ = false;
2147  }
2148  }
2149  else
2150  {
2152  if(!currentMetricGoal_.isNull())
2153  {
2154  // Adjust the target pose relative to last node
2155  if(rtabmap_.getPathCurrentGoalId() == rtabmap_.getPath().back().first && rtabmap_.getLocalOptimizedPoses().size())
2156  {
2157  if(latestNodeWasReached_ ||
2159  {
2160  latestNodeWasReached_ = true;
2161  Transform goalLocalTransform = Transform::getIdentity();
2162  if(!goalFrameId_.empty() && goalFrameId_.compare(frameId_) != 0)
2163  {
2165  if(!localT.isNull())
2166  {
2167  goalLocalTransform = localT.inverse().to3DoF();
2168  }
2169  }
2170  currentMetricGoal_ *= rtabmap_.getPathTransformToGoal()*goalLocalTransform;
2171  }
2172  }
2173 
2174  // publish next goal with updated currentMetricGoal_
2175  publishCurrentGoal(stamp);
2176 
2177  // publish local path
2178  publishLocalPath(stamp);
2179 
2180  // publish global path
2181  publishGlobalPath(stamp);
2182  }
2183  else
2184  {
2185  NODELET_ERROR("Planning: Local map broken, current goal id=%d (the robot may have moved to far from planned nodes)",
2187  rtabmap_.clearPath(-1);
2189  {
2190  std_msgs::Bool result;
2191  result.data = false;
2192  goalReachedPub_.publish(result);
2193  }
2196  goalFrameId_.clear();
2197  latestNodeWasReached_ = false;
2198  }
2199  }
2200  }
2201 
2202  timePublishMaps = timer.ticks();
2203  }
2204  }
2205  else
2206  {
2207  timeRtabmap = timer.ticks();
2208  }
2209  NODELET_INFO("rtabmap (%d): Rate=%.2fs, Limit=%.3fs, Conversion=%.4fs, RTAB-Map=%.4fs, Maps update=%.4fs pub=%.4fs (local map=%d, WM=%d)",
2211  rate_>0?1.0f/rate_:0,
2212  rtabmap_.getTimeThreshold()/1000.0f,
2213  timeMsgConversion,
2214  timeRtabmap,
2215  timeUpdateMaps,
2216  timePublishMaps,
2217  (int)rtabmap_.getLocalOptimizedPoses().size(),
2219  rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/HasSubscribers/"), mapsManager_.hasSubscribers()?1:0));
2220  rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/TimeMsgConversion/ms"), timeMsgConversion*1000.0f));
2221  rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/TimeRtabmap/ms"), timeRtabmap*1000.0f));
2222  rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/TimeUpdatingMaps/ms"), timeUpdateMaps*1000.0f));
2223  rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/TimePublishing/ms"), timePublishMaps*1000.0f));
2224  rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/TimeTotal/ms"), (timeMsgConversion+timeRtabmap+timeUpdateMaps+timePublishMaps)*1000.0f));
2225  }
2226  else if(!rtabmap_.isIDsGenerated())
2227  {
2228  NODELET_WARN("Ignoring received image because its sequence ID=0. Please "
2229  "set \"Mem/GenerateIds\"=\"true\" to ignore ros generated sequence id. "
2230  "Use only \"Mem/GenerateIds\"=\"false\" for once-time run of RTAB-Map and "
2231  "when you need to have IDs output of RTAB-map synchronized with the source "
2232  "image sequence ID.");
2233  }
2234 }
2235 
2236 std::map<int, Transform> CoreWrapper::filterNodesToAssemble(
2237  const std::map<int, Transform> & nodes,
2238  const Transform & currentPose)
2239 {
2240  std::map<int, Transform> output;
2241  if(mappingMaxNodes_ > 0)
2242  {
2243  std::map<int, float> nodesDist = graph::findNearestNodes(currentPose, nodes, 0, 0, mappingMaxNodes_);
2244  for(std::map<int, float>::iterator iter=nodesDist.begin(); iter!=nodesDist.end(); ++iter)
2245  {
2246  if(mappingAltitudeDelta_<=0.0 ||
2247  fabs(nodes.at(iter->first).z()-currentPose.z())<mappingAltitudeDelta_)
2248  {
2249  output.insert(*nodes.find(iter->first));
2250  }
2251  }
2252  }
2253  else // mappingAltitudeDelta_>0.0
2254  {
2255  for(std::map<int, Transform>::const_iterator iter=nodes.begin(); iter!=nodes.end(); ++iter)
2256  {
2257  if(fabs(iter->second.z()-currentPose.z())<mappingAltitudeDelta_)
2258  {
2259  output.insert(*iter);
2260  }
2261  }
2262  }
2263  return output;
2264 }
2265 
2266 void CoreWrapper::userDataAsyncCallback(const rtabmap_ros::UserDataConstPtr & dataMsg)
2267 {
2268  if(!paused_)
2269  {
2271  static bool warningShow = false;
2272  if(!userData_.empty() && !warningShow)
2273  {
2274  ROS_WARN("Overwriting previous user data set. When asynchronous user "
2275  "data input topic rate is higher than "
2276  "map update rate (current %s=%f), only latest data is saved "
2277  "in the next node created. This message will is shown only once.",
2278  Parameters::kRtabmapDetectionRate().c_str(), rate_);
2279  warningShow = true;
2280  }
2282  }
2283 }
2284 
2285 void CoreWrapper::globalPoseAsyncCallback(const geometry_msgs::PoseWithCovarianceStampedConstPtr & globalPoseMsg)
2286 {
2287  if(!paused_)
2288  {
2289  globalPose_ = *globalPoseMsg;
2290  }
2291 }
2292 
2293 void CoreWrapper::gpsFixAsyncCallback(const sensor_msgs::NavSatFixConstPtr & gpsFixMsg)
2294 {
2295  if(!paused_)
2296  {
2297  double error = 10.0;
2298  if(gpsFixMsg->position_covariance_type != sensor_msgs::NavSatFix::COVARIANCE_TYPE_UNKNOWN)
2299  {
2300  double variance = uMax3(gpsFixMsg->position_covariance.at(0), gpsFixMsg->position_covariance.at(4), gpsFixMsg->position_covariance.at(8));
2301  if(variance>0.0)
2302  {
2303  error = sqrt(variance);
2304  }
2305  }
2306  gps_ = rtabmap::GPS(
2307  gpsFixMsg->header.stamp.toSec(),
2308  gpsFixMsg->longitude,
2309  gpsFixMsg->latitude,
2310  gpsFixMsg->altitude,
2311  error,
2312  0);
2313  }
2314 }
2315 
2316 #ifdef WITH_APRILTAG_ROS
2317 void CoreWrapper::tagDetectionsAsyncCallback(const apriltag_ros::AprilTagDetectionArray & tagDetections)
2318 {
2319  if(!paused_)
2320  {
2321  for(unsigned int i=0; i<tagDetections.detections.size(); ++i)
2322  {
2323  if(tagDetections.detections[i].id.size() >= 1)
2324  {
2325  geometry_msgs::PoseWithCovarianceStamped p = tagDetections.detections[i].pose;
2326  p.header = tagDetections.header;
2327  if(!tagDetections.detections[i].pose.header.frame_id.empty())
2328  {
2329  p.header.frame_id = tagDetections.detections[i].pose.header.frame_id;
2330 
2331  static bool warned = false;
2332  if(!warned &&
2333  !tagDetections.header.frame_id.empty() &&
2334  tagDetections.detections[i].pose.header.frame_id.compare(tagDetections.header.frame_id)!=0)
2335  {
2336  NODELET_WARN("frame_id set for individual tag detections (%s) doesn't match the frame_id of the message (%s), "
2337  "the resulting pose of the tag may be wrong. This message is only printed once.",
2338  tagDetections.detections[i].pose.header.frame_id.c_str(), tagDetections.header.frame_id.c_str());
2339  warned = true;
2340  }
2341  }
2342  if(!tagDetections.detections[i].pose.header.stamp.isZero())
2343  {
2344  p.header.stamp = tagDetections.detections[i].pose.header.stamp;
2345 
2346  static bool warned = false;
2347  if(!warned &&
2348  !tagDetections.header.stamp.isZero() &&
2349  tagDetections.detections[i].pose.header.stamp != tagDetections.header.stamp)
2350  {
2351  NODELET_WARN("stamp set for individual tag detections (%f) doesn't match the stamp of the message (%f), "
2352  "the resulting pose of the tag may be wrongly interpolated. This message is only printed once.",
2353  tagDetections.detections[i].pose.header.stamp.toSec(), tagDetections.header.stamp.toSec());
2354  warned = true;
2355  }
2356  }
2357  uInsert(tags_,
2358  std::make_pair(tagDetections.detections[i].id[0],
2359  std::make_pair(p, tagDetections.detections[i].size.size()==1?(float)tagDetections.detections[i].size[0]:0.0f)));
2360  }
2361  }
2362  }
2363 }
2364 #endif
2365 
2366 #ifdef WITH_FIDUCIAL_MSGS
2367 void CoreWrapper::fiducialDetectionsAsyncCallback(const fiducial_msgs::FiducialTransformArray & fiducialDetections)
2368 {
2369  if(!paused_)
2370  {
2371  for(unsigned int i=0; i<fiducialDetections.transforms.size(); ++i)
2372  {
2373  geometry_msgs::PoseWithCovarianceStamped p;
2374  p.pose.pose.orientation = fiducialDetections.transforms[i].transform.rotation;
2375  p.pose.pose.position.x = fiducialDetections.transforms[i].transform.translation.x;
2376  p.pose.pose.position.y = fiducialDetections.transforms[i].transform.translation.y;
2377  p.pose.pose.position.z = fiducialDetections.transforms[i].transform.translation.z;
2378  p.header = fiducialDetections.header;
2379  uInsert(tags_,
2380  std::make_pair(fiducialDetections.transforms[i].fiducial_id,
2381  std::make_pair(p, 0.0f)));
2382  }
2383  }
2384 }
2385 #endif
2386 
2387 void CoreWrapper::imuAsyncCallback(const sensor_msgs::ImuConstPtr & msg)
2388 {
2389  if(!paused_)
2390  {
2391  if(msg->orientation.x == 0 && msg->orientation.y == 0 && msg->orientation.z == 0 && msg->orientation.w == 0)
2392  {
2393  UERROR("IMU received doesn't have orientation set, it is ignored.");
2394  }
2395  else
2396  {
2397  Transform orientation(0,0,0, msg->orientation.x, msg->orientation.y, msg->orientation.z, msg->orientation.w);
2398  imus_.insert(std::make_pair(msg->header.stamp.toSec(), orientation));
2399  if(imus_.size() > 1000)
2400  {
2401  imus_.erase(imus_.begin());
2402  }
2403  if(!imuFrameId_.empty() && imuFrameId_.compare(msg->header.frame_id) != 0)
2404  {
2405  ROS_ERROR("IMU frame_id has changed from %s to %s! Are "
2406  "multiple nodes publishing "
2407  "on same topic %s? IMU buffer is cleared!",
2408  imuFrameId_.c_str(),
2409  msg->header.frame_id.c_str(),
2410  imuSub_.getTopic().c_str());
2411  imus_.clear();
2412  imuFrameId_.clear();
2413  }
2414  else
2415  {
2416  imuFrameId_ = msg->header.frame_id;
2417  }
2418  }
2419  }
2420 }
2421 
2422 void CoreWrapper::republishNodeDataCallback(const std_msgs::Int32MultiArray::ConstPtr& msg)
2423 {
2424  rtabmap_.addNodesToRepublish(msg->data);
2425 }
2426 
2427 void CoreWrapper::interOdomCallback(const nav_msgs::OdometryConstPtr & msg)
2428 {
2429  if(!paused_)
2430  {
2431  interOdoms_.push_back(std::make_pair(*msg, rtabmap_ros::OdomInfo()));
2432  }
2433 }
2434 
2435 void CoreWrapper::interOdomInfoCallback(const nav_msgs::OdometryConstPtr & msg1, const rtabmap_ros::OdomInfoConstPtr & msg2)
2436 {
2437  if(!paused_)
2438  {
2439  interOdoms_.push_back(std::make_pair(*msg1, *msg2));
2440  }
2441 }
2442 
2443 
2444 void CoreWrapper::initialPoseCallback(const geometry_msgs::PoseWithCovarianceStampedConstPtr & msg)
2445 {
2446  Transform intialPose = rtabmap_ros::transformFromPoseMsg(msg->pose.pose);
2447  if(intialPose.isNull())
2448  {
2449  NODELET_ERROR("Pose received is null!");
2450  return;
2451  }
2452 
2453  rtabmap_.setInitialPose(intialPose);
2454 }
2455 
2457  int id,
2458  const std::string & label,
2459  const std::string & frameId,
2460  const Transform & pose,
2461  const ros::Time & stamp,
2462  double * planningTime)
2463 {
2464  UTimer timer;
2465 
2466  if(id == 0 && !label.empty() && rtabmap_.getMemory())
2467  {
2468  id = rtabmap_.getMemory()->getSignatureIdByLabel(label);
2469  }
2470 
2471  if(id > 0)
2472  {
2473  NODELET_INFO("Planning: set goal to node %d", id);
2474  }
2475  else if(id < 0)
2476  {
2477  NODELET_INFO("Planning: set goal to landmark %d", id);
2478  }
2479  else if(!pose.isNull())
2480  {
2481  NODELET_INFO("Planning: set goal %s", pose.prettyPrint().c_str());
2482  }
2483 
2484  if(planningTime)
2485  {
2486  *planningTime = 0.0;
2487  }
2488 
2489  bool success = false;
2490  if((id != 0 && rtabmap_.computePath(id, true)) ||
2491  (!pose.isNull() && rtabmap_.computePath(pose)))
2492  {
2493  if(planningTime)
2494  {
2495  *planningTime = timer.elapsed();
2496  }
2497  NODELET_INFO("Planning: Time computing path = %f s", timer.ticks());
2498  const std::vector<std::pair<int, Transform> > & poses = rtabmap_.getPath();
2499 
2502  goalFrameId_.clear();
2503  latestNodeWasReached_ = false;
2504  if(poses.size() == 0)
2505  {
2506  NODELET_WARN("Planning: Goal already reached (RGBD/GoalReachedRadius=%fm).",
2508  rtabmap_.clearPath(1);
2510  {
2511  std_msgs::Bool result;
2512  result.data = true;
2513  goalReachedPub_.publish(result);
2514  }
2515  success = true;
2516  }
2517  else
2518  {
2520  if(!currentMetricGoal_.isNull())
2521  {
2522  NODELET_INFO("Planning: Path successfully created (size=%d)", (int)poses.size());
2524 
2525  // Adjust the target pose relative to last node
2526  if(rtabmap_.getPathCurrentGoalId() == rtabmap_.getPath().back().first && rtabmap_.getLocalOptimizedPoses().size())
2527  {
2529  {
2530  latestNodeWasReached_ = true;
2531  Transform goalLocalTransform = Transform::getIdentity();
2532  if(!goalFrameId_.empty() && goalFrameId_.compare(frameId_) != 0)
2533  {
2535  if(!localT.isNull())
2536  {
2537  goalLocalTransform = localT.inverse().to3DoF();
2538  }
2539  }
2540  currentMetricGoal_ *= rtabmap_.getPathTransformToGoal() * goalLocalTransform;
2541  }
2542  }
2543 
2544  publishCurrentGoal(stamp);
2545  publishLocalPath(stamp);
2546  publishGlobalPath(stamp);
2547 
2548  // Just output the path on screen
2549  std::stringstream stream;
2550  for(std::vector<std::pair<int, Transform> >::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
2551  {
2552  if(iter != poses.begin())
2553  {
2554  stream << " ";
2555  }
2556  stream << iter->first;
2557  }
2558  NODELET_INFO("Global path: [%s]", stream.str().c_str());
2559  success=true;
2560  }
2561  else
2562  {
2563  NODELET_ERROR("Pose of node %d not found!? Cannot send a metric goal...", rtabmap_.getPathCurrentGoalId());
2564  }
2565  }
2566  }
2567  else if(!label.empty())
2568  {
2569  NODELET_ERROR("Planning: Node with label \"%s\" not found!", label.c_str());
2570  }
2571  else if(pose.isNull())
2572  {
2573  if(id > 0)
2574  {
2575  NODELET_ERROR("Planning: Could not plan to node %d! The node is not in map's graph (look for warnings before this message for more details).", id);
2576  }
2577  else if(id < 0)
2578  {
2579  NODELET_ERROR("Planning: Could not plan to landmark %d! The landmark is not in map's graph (look for warnings before this message for more details).", id);
2580  }
2581  else
2582  {
2583  NODELET_ERROR("Planning: Node id should be > 0 !");
2584  }
2585  }
2586  else
2587  {
2588  NODELET_ERROR("Planning: A node near the goal's pose not found! The pose may be to far from the graph (RGBD/LocalRadius=%f m)", rtabmap_.getLocalRadius());
2589  }
2590 
2591  if(!success)
2592  {
2593  rtabmap_.clearPath(-1);
2595  {
2596  std_msgs::Bool result;
2597  result.data = false;
2598  goalReachedPub_.publish(result);
2599  }
2600  }
2601 }
2602 
2603 void CoreWrapper::goalCallback(const geometry_msgs::PoseStampedConstPtr & msg)
2604 {
2605  Transform targetPose = rtabmap_ros::transformFromPoseMsg(msg->pose, true);
2606 
2607  // transform goal in /map frame
2608  if(!msg->header.frame_id.empty() && mapFrameId_.compare(msg->header.frame_id) != 0)
2609  {
2611  if(t.isNull())
2612  {
2613  NODELET_ERROR("Cannot transform goal pose from \"%s\" frame to \"%s\" frame!",
2614  msg->header.frame_id.c_str(), mapFrameId_.c_str());
2616  {
2617  std_msgs::Bool result;
2618  result.data = false;
2619  goalReachedPub_.publish(result);
2620  }
2621  return;
2622  }
2623  targetPose = t * targetPose;
2624  }
2625  // else assume map frame if not set
2626 
2627  goalCommonCallback(0, "", "", targetPose, msg->header.stamp);
2628 }
2629 
2630 void CoreWrapper::goalNodeCallback(const rtabmap_ros::GoalConstPtr & msg)
2631 {
2632  if(msg->node_id == 0 && msg->node_label.empty())
2633  {
2634  NODELET_ERROR("Node id or label should be set!");
2636  {
2637  std_msgs::Bool result;
2638  result.data = false;
2639  goalReachedPub_.publish(result);
2640  }
2641  return;
2642  }
2643  goalCommonCallback(msg->node_id, msg->node_label, msg->frame_id, Transform(), msg->header.stamp);
2644 }
2645 
2646 bool CoreWrapper::updateRtabmapCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
2647 {
2648  ros::NodeHandle pnh("~");
2649  for(rtabmap::ParametersMap::iterator iter=parameters_.begin(); iter!=parameters_.end(); ++iter)
2650  {
2651  std::string vStr;
2652  bool vBool;
2653  int vInt;
2654  double vDouble;
2655  if(pnh.getParam(iter->first, vStr))
2656  {
2657  NODELET_INFO("Setting RTAB-Map parameter \"%s\"=\"%s\"", iter->first.c_str(), vStr.c_str());
2658  iter->second = vStr;
2659  }
2660  else if(pnh.getParam(iter->first, vBool))
2661  {
2662  NODELET_INFO("Setting RTAB-Map parameter \"%s\"=\"%s\"", iter->first.c_str(), uBool2Str(vBool).c_str());
2663  iter->second = uBool2Str(vBool);
2664  }
2665  else if(pnh.getParam(iter->first, vInt))
2666  {
2667  NODELET_INFO("Setting RTAB-Map parameter \"%s\"=\"%s\"", iter->first.c_str(), uNumber2Str(vInt).c_str());
2668  iter->second = uNumber2Str(vInt).c_str();
2669  }
2670  else if(pnh.getParam(iter->first, vDouble))
2671  {
2672  NODELET_INFO("Setting RTAB-Map parameter \"%s\"=\"%s\"", iter->first.c_str(), uNumber2Str(vDouble).c_str());
2673  iter->second = uNumber2Str(vDouble).c_str();
2674  }
2675  }
2676  NODELET_INFO("rtabmap: Updating parameters");
2677  if(parameters_.find(Parameters::kRtabmapDetectionRate()) != parameters_.end())
2678  {
2679  rate_ = uStr2Float(parameters_.at(Parameters::kRtabmapDetectionRate()));
2680  NODELET_INFO("RTAB-Map rate detection = %f Hz", rate_);
2681  }
2682  if(parameters_.find(Parameters::kRtabmapCreateIntermediateNodes()) != parameters_.end())
2683  {
2684  createIntermediateNodes_ = uStr2Bool(parameters_.at(Parameters::kRtabmapCreateIntermediateNodes()));
2685  NODELET_INFO("Create intermediate nodes = %s", createIntermediateNodes_?"true":"false");
2686  }
2687  if(parameters_.find(Parameters::kGridGlobalMaxNodes()) != parameters_.end())
2688  {
2689  mappingMaxNodes_ = uStr2Int(parameters_.at(Parameters::kGridGlobalMaxNodes()));
2690  NODELET_INFO("Max mapping nodes = %d", mappingMaxNodes_);
2691  }
2692  if(parameters_.find(Parameters::kGridGlobalAltitudeDelta()) != parameters_.end())
2693  {
2694  mappingAltitudeDelta_ = uStr2Float(parameters_.at(Parameters::kGridGlobalAltitudeDelta()));
2695  NODELET_INFO("Mapping altitude delta = %f", mappingAltitudeDelta_);
2696  }
2697  if(parameters_.find(Parameters::kRtabmapImagesAlreadyRectified()) != parameters_.end())
2698  {
2699  alreadyRectifiedImages_ = uStr2Bool(parameters_.at(Parameters::kRtabmapImagesAlreadyRectified()));
2700  NODELET_INFO("Already rectified images = %s", alreadyRectifiedImages_?"true":"false");
2701  }
2702  if(parameters_.find(Parameters::kRegForce3DoF()) != parameters_.end())
2703  {
2704  twoDMapping_= uStr2Bool(parameters_.at(Parameters::kRegForce3DoF()));
2705  NODELET_INFO("2D mapping = %s", twoDMapping_?"true":"false");
2706  }
2709  return true;
2710 }
2711 
2712 bool CoreWrapper::resetRtabmapCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
2713 {
2714  NODELET_INFO("rtabmap: Reset");
2716  covariance_ = cv::Mat();
2718  lastPoseVelocity_.clear();
2719  lastPoseIntermediate_ = false;
2722  goalFrameId_.clear();
2723  latestNodeWasReached_ = false;
2724  mapsManager_.clear();
2726  globalPose_.header.stamp = ros::Time(0);
2727  gps_ = rtabmap::GPS();
2728  tags_.clear();
2729  userDataMutex_.lock();
2730  userData_ = cv::Mat();
2732  imus_.clear();
2733  imuFrameId_.clear();
2734  interOdoms_.clear();
2735  mapToOdomMutex_.lock();
2737  mapToOdomMutex_.unlock();
2738 
2739  return true;
2740 }
2741 
2742 bool CoreWrapper::pauseRtabmapCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
2743 {
2744  if(paused_)
2745  {
2746  NODELET_WARN("rtabmap: Already paused!");
2747  }
2748  else
2749  {
2750  paused_ = true;
2751  NODELET_INFO("rtabmap: paused!");
2752  ros::NodeHandle pnh("~");
2753  pnh.setParam("is_rtabmap_paused", true);
2754  }
2755  return true;
2756 }
2757 
2758 bool CoreWrapper::resumeRtabmapCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
2759 {
2760  if(!paused_)
2761  {
2762  NODELET_WARN("rtabmap: Already running!");
2763  }
2764  else
2765  {
2766  paused_ = false;
2767  NODELET_INFO("rtabmap: resumed!");
2768  ros::NodeHandle pnh("~");
2769  pnh.setParam("is_rtabmap_paused", false);
2770  }
2771  return true;
2772 }
2773 
2774 bool CoreWrapper::loadDatabaseCallback(rtabmap_ros::LoadDatabase::Request& req, rtabmap_ros::LoadDatabase::Response&)
2775 {
2776  NODELET_INFO("LoadDatabase: Loading database (%s, clear=%s)...", req.database_path.c_str(), req.clear?"true":"false");
2777  std::string newDatabasePath = uReplaceChar(req.database_path, '~', UDirectory::homeDir());
2778  std::string dir = UDirectory::getDir(newDatabasePath);
2779  if(!UDirectory::exists(dir))
2780  {
2781  ROS_ERROR("Directory %s doesn't exist! Cannot load database \"%s\"", newDatabasePath.c_str(), dir.c_str());
2782  return false;
2783  }
2784 
2785  if(UFile::exists(newDatabasePath) && req.clear)
2786  {
2787  UFile::erase(newDatabasePath);
2788  }
2789 
2790  // Close old database
2791  NODELET_INFO("LoadDatabase: Saving current map (%s)...", databasePath_.c_str());
2792  if(rtabmap_.getMemory())
2793  {
2794  // save the grid map
2795  float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f;
2796  cv::Mat pixels = mapsManager_.getGridMap(xMin, yMin, gridCellSize);
2797  if(!pixels.empty())
2798  {
2799  printf("rtabmap: 2D occupancy grid map saved.\n");
2800  rtabmap_.getMemory()->save2DMap(pixels, xMin, yMin, gridCellSize);
2801  }
2802  }
2803  rtabmap_.close();
2804  NODELET_INFO("LoadDatabase: Saving current map (%s, %ld MB)... done!", databasePath_.c_str(), UFile::length(databasePath_)/(1024*1024));
2805 
2806  covariance_ = cv::Mat();
2808  lastPoseVelocity_.clear();
2809  lastPoseIntermediate_ = false;
2812  goalFrameId_.clear();
2813  latestNodeWasReached_ = false;
2814  mapsManager_.clear();
2816  globalPose_.header.stamp = ros::Time(0);
2817  gps_ = rtabmap::GPS();
2818  tags_.clear();
2819  userDataMutex_.lock();
2820  userData_ = cv::Mat();
2822  imus_.clear();
2823  imuFrameId_.clear();
2824  interOdoms_.clear();
2825  mapToOdomMutex_.lock();
2827  mapToOdomMutex_.unlock();
2828 
2829  // Open new database
2830  databasePath_ = newDatabasePath;
2831 
2832  // modify default parameters with those in the database
2833  if(!req.clear && UFile::exists(databasePath_))
2834  {
2835  ParametersMap dbParameters;
2837  if(driver->openConnection(databasePath_))
2838  {
2839  dbParameters = driver->getLastParameters(); // parameter migration is already done
2840  }
2841  delete driver;
2842  for(ParametersMap::iterator iter=dbParameters.begin(); iter!=dbParameters.end(); ++iter)
2843  {
2844  if(iter->first.compare(Parameters::kRtabmapWorkingDirectory()) == 0)
2845  {
2846  // ignore working directory
2847  continue;
2848  }
2849  if(parameters_.find(iter->first) == parameters_.end() &&
2850  parameters_.find(iter->first)->second.compare(iter->second) !=0)
2851  {
2852  NODELET_WARN("RTAB-Map parameter \"%s\" from database (%s) is different "
2853  "from the current used one (%s). We still keep the "
2854  "current parameter value (%s). If you want to switch between databases "
2855  "with different configurations, restart rtabmap node instead of using this service.",
2856  iter->first.c_str(), iter->second.c_str(),
2857  parameters_.find(iter->first)->second.c_str(),
2858  parameters_.find(iter->first)->second.c_str());
2859  }
2860  }
2861  }
2862 
2863  NODELET_INFO("LoadDatabase: Loading database...");
2865  NODELET_INFO("LoadDatabase: Loading database... done!");
2866 
2867  if(rtabmap_.getMemory())
2868  {
2870  {
2871  float xMin, yMin, gridCellSize;
2872  cv::Mat map = rtabmap_.getMemory()->load2DMap(xMin, yMin, gridCellSize);
2873  if(!map.empty())
2874  {
2875  NODELET_INFO("LoadDatabase: 2D occupancy grid map loaded (%dx%d).", map.cols, map.rows);
2876  mapsManager_.set2DMap(map, xMin, yMin, gridCellSize, rtabmap_.getLocalOptimizedPoses(), rtabmap_.getMemory());
2877  }
2878  }
2879 
2880  if(rtabmap_.getMemory()->getWorkingMem().size()>1)
2881  {
2882  NODELET_INFO("LoadDatabase: Working Memory = %d, Local map = %d.",
2883  (int)rtabmap_.getMemory()->getWorkingMem().size()-1,
2884  (int)rtabmap_.getLocalOptimizedPoses().size());
2885  }
2886 
2887  if(databasePath_.size())
2888  {
2889  NODELET_INFO("LoadDatabase: Database version = \"%s\".", rtabmap_.getMemory()->getDatabaseVersion().c_str());
2890  }
2891 
2893  {
2894  NODELET_INFO("LoadDatabase: SLAM mode (%s=true)", Parameters::kMemIncrementalMemory().c_str());
2895  }
2896  else
2897  {
2898  NODELET_INFO("LoadDatabase: Localization mode (%s=false)", Parameters::kMemIncrementalMemory().c_str());
2899  }
2900 
2901  return true;
2902  }
2903 
2904  return false;
2905 }
2906 
2907 bool CoreWrapper::triggerNewMapCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
2908 {
2909  NODELET_INFO("rtabmap: Trigger new map");
2911  return true;
2912 }
2913 
2914 bool CoreWrapper::backupDatabaseCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
2915 {
2916  NODELET_INFO("Backup: Saving memory...");
2917  if(rtabmap_.getMemory())
2918  {
2919  // save the grid map
2920  float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f;
2921  cv::Mat pixels = mapsManager_.getGridMap(xMin, yMin, gridCellSize);
2922  if(!pixels.empty())
2923  {
2924  printf("rtabmap: 2D occupancy grid map saved.\n");
2925  rtabmap_.getMemory()->save2DMap(pixels, xMin, yMin, gridCellSize);
2926  }
2927  }
2928  rtabmap_.close();
2929  NODELET_INFO("Backup: Saving memory... done!");
2930 
2931  covariance_ = cv::Mat();
2933  lastPoseVelocity_.clear();
2936  goalFrameId_.clear();
2937  latestNodeWasReached_ = false;
2938  userDataMutex_.lock();
2939  userData_ = cv::Mat();
2941  globalPose_.header.stamp = ros::Time(0);
2942  gps_ = rtabmap::GPS();
2943  tags_.clear();
2944 
2945  NODELET_INFO("Backup: Saving \"%s\" to \"%s\"...", databasePath_.c_str(), (databasePath_+".back").c_str());
2947  NODELET_INFO("Backup: Saving \"%s\" to \"%s\"... done!", databasePath_.c_str(), (databasePath_+".back").c_str());
2948 
2949  NODELET_INFO("Backup: Reloading memory...");
2951  NODELET_INFO("Backup: Reloading memory... done!");
2952 
2953  return true;
2954 }
2955 
2957 {
2960 
2962  {
2963  rtabmap_ros::MapDataPtr msg(new rtabmap_ros::MapData);
2964  msg->header.stamp = stamp;
2965  msg->header.frame_id = mapFrameId_;
2966 
2970  std::map<int, Signature>(),
2972  *msg);
2973 
2974  mapDataPub_.publish(msg);
2975  }
2976 
2978  {
2979  rtabmap_ros::MapGraphPtr msg(new rtabmap_ros::MapGraph);
2980  msg->header.stamp = stamp;
2981  msg->header.frame_id = mapFrameId_;
2982 
2987  *msg);
2988 
2989  mapGraphPub_.publish(msg);
2990  }
2991 }
2992 
2993 bool CoreWrapper::detectMoreLoopClosuresCallback(rtabmap_ros::DetectMoreLoopClosures::Request& req, rtabmap_ros::DetectMoreLoopClosures::Response& res)
2994 {
2995  NODELET_WARN("Detect more loop closures service called");
2996 
2997  UTimer timer;
2998  float clusterRadiusMax = 1;
2999  float clusterRadiusMin = 0;
3000  float clusterAngle = 0;
3001  int iterations = 1;
3002  bool intraSession = true;
3003  bool interSession = true;
3004  if(req.cluster_radius_max > 0.0f)
3005  {
3006  clusterRadiusMax = req.cluster_radius_max;
3007  }
3008  if(req.cluster_radius_min >= 0.0f)
3009  {
3010  clusterRadiusMin = req.cluster_radius_min;
3011  }
3012  if(req.cluster_angle >= 0.0f)
3013  {
3014  clusterAngle = req.cluster_angle;
3015  }
3016  if(req.iterations >= 1.0f)
3017  {
3018  iterations = (int)req.iterations;
3019  }
3020  if(req.intra_only)
3021  {
3022  interSession = false;
3023  }
3024  else if(req.inter_only)
3025  {
3026  intraSession = false;
3027  }
3028  NODELET_WARN("Post-Processing service called: Detecting more loop closures "
3029  "(max radius=%f, min radius=%f, angle=%f, iterations=%d, intra=%s, inter=%s)...",
3030  clusterRadiusMax,
3031  clusterRadiusMin,
3032  clusterAngle,
3033  iterations,
3034  intraSession?"true":"false",
3035  interSession?"true":"false");
3036  res.detected = rtabmap_.detectMoreLoopClosures(
3037  clusterRadiusMax,
3038  clusterAngle*M_PI/180.0,
3039  iterations,
3040  intraSession,
3041  interSession,
3042  0,
3043  clusterRadiusMin);
3044  if(res.detected<0)
3045  {
3046  NODELET_ERROR("Post-Processing: Detecting more loop closures failed!");
3047  }
3048  else
3049  {
3050  NODELET_WARN("Post-Processing: Detected %d loop closures! (%fs)", res.detected, timer.ticks());
3051 
3052  if(res.detected>0)
3053  {
3054  republishMaps();
3055  }
3056  return true;
3057  }
3058  return false;
3059 }
3060 
3061 bool CoreWrapper::cleanupLocalGridsCallback(rtabmap_ros::CleanupLocalGrids::Request& req, rtabmap_ros::CleanupLocalGrids::Response& res)
3062 {
3063  NODELET_WARN("Cleanup local grids service called");
3064  UTimer timer;
3065  int radius = 1;
3066  bool filterScans = false;
3067  if(req.radius > 1.0f)
3068  {
3069  radius = (int)req.radius;
3070  }
3071  filterScans = req.filter_scans;
3072  float xMin, yMin, gridCellSize;
3073  cv::Mat map = mapsManager_.getGridMap(xMin, yMin, gridCellSize);
3074  if(map.empty())
3075  {
3076  NODELET_ERROR("Post-Processing: Cleanup local grids failed! There is no optimized map.");
3077  return false;
3078  }
3079  std::map<int, Transform> poses = rtabmap_.getLocalOptimizedPoses();
3080  NODELET_WARN("Post-Processing: Cleanup local grids... (radius=%d, filter scans=%s)",
3081  radius,
3082  filterScans?"true":"false");
3083  res.modified = rtabmap_.cleanupLocalGrids(poses, map, xMin, yMin, gridCellSize, radius, filterScans);
3084  if(res.modified<0)
3085  {
3086  NODELET_ERROR("Post-Processing: Cleanup local grids failed!");
3087  }
3088  else
3089  {
3090  if(filterScans)
3091  {
3092  NODELET_WARN("Post-Processing: %d grids and scans modified! (%fs)", res.modified, timer.ticks());
3093  }
3094  else
3095  {
3096  NODELET_WARN("Post-Processing: %d grids modified! (%fs)", res.modified, timer.ticks());
3097  }
3098  if(res.modified > 0)
3099  {
3100  // We should update MapsManager's cache with the modifications
3101  mapsManager_.clear();
3102  mapsManager_.set2DMap(map, xMin, yMin, gridCellSize, rtabmap_.getLocalOptimizedPoses(), rtabmap_.getMemory());
3103 
3104  republishMaps();
3105  }
3106  return true;
3107  }
3108 
3109  return false;
3110 }
3111 bool CoreWrapper::globalBundleAdjustmentCallback(rtabmap_ros::GlobalBundleAdjustment::Request& req, rtabmap_ros::GlobalBundleAdjustment::Response& res)
3112 {
3113  NODELET_WARN("Global bundle adjustment service called");
3114 
3115  UTimer timer;
3116  int optimizer = (int)Optimizer::kTypeG2O; // g2o
3117  int iterations = Parameters::defaultOptimizerIterations();
3118  float pixelVariance = Parameters::defaultg2oPixelVariance();
3119  bool rematchFeatures = true;
3120  Parameters::parse(parameters_, Parameters::kOptimizerIterations(), iterations);
3121  Parameters::parse(parameters_, Parameters::kg2oPixelVariance(), pixelVariance);
3122  if(req.type == 1.0f)
3123  {
3124  optimizer = (int)Optimizer::kTypeCVSBA;
3125  }
3126  if(req.iterations >= 1.0f)
3127  {
3128  iterations = req.iterations;
3129  }
3130  if(req.pixel_variance > 0.0f)
3131  {
3132  pixelVariance = req.pixel_variance;
3133  }
3134  rematchFeatures = !req.voc_matches;
3135 
3136  NODELET_WARN("Post-Processing: Global Bundle Adjustment... "
3137  "(Optimizer=%s, iterations=%d, pixel variance=%f, rematch=%s)...",
3138  optimizer==Optimizer::kTypeG2O?"g2o":"cvsba",
3139  iterations,
3140  pixelVariance,
3141  rematchFeatures?"true":"false");
3142  bool success = rtabmap_.globalBundleAdjustment((Optimizer::Type)optimizer, rematchFeatures, iterations, pixelVariance);
3143  if(!success)
3144  {
3145  NODELET_ERROR("Post-Processing: Global Bundle Adjustment failed!");
3146  }
3147  else
3148  {
3149  NODELET_WARN("Post-Processing: Global Bundle Adjustment... done! (%fs)", timer.ticks());
3150  republishMaps();
3151  return true;
3152  }
3153 
3154  return false;
3155 }
3156 
3157 bool CoreWrapper::setModeLocalizationCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
3158 {
3159  NODELET_INFO("rtabmap: Set localization mode");
3160  rtabmap::ParametersMap parameters;
3161  parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemIncrementalMemory(), "false"));
3162  ros::NodeHandle & nh = getNodeHandle();
3163  nh.setParam(rtabmap::Parameters::kMemIncrementalMemory(), "false");
3164  rtabmap_.parseParameters(parameters);
3165  NODELET_INFO("rtabmap: Localization mode enabled!");
3166  return true;
3167 }
3168 
3169 bool CoreWrapper::setModeMappingCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
3170 {
3171  NODELET_INFO("rtabmap: Set mapping mode");
3172  rtabmap::ParametersMap parameters;
3173  parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemIncrementalMemory(), "true"));
3174  ros::NodeHandle & nh = getNodeHandle();
3175  nh.setParam(rtabmap::Parameters::kMemIncrementalMemory(), "true");
3176  rtabmap_.parseParameters(parameters);
3177  NODELET_INFO("rtabmap: Mapping mode enabled!");
3178  return true;
3179 }
3180 
3181 bool CoreWrapper::setLogDebug(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
3182 {
3183  NODELET_INFO("rtabmap: Set log level to Debug");
3185  return true;
3186 }
3187 bool CoreWrapper::setLogInfo(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
3188 {
3189  NODELET_INFO("rtabmap: Set log level to Info");
3191  return true;
3192 }
3193 bool CoreWrapper::setLogWarn(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
3194 {
3195  NODELET_INFO("rtabmap: Set log level to Warning");
3197  return true;
3198 }
3199 bool CoreWrapper::setLogError(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
3200 {
3201  NODELET_INFO("rtabmap: Set log level to Error");
3203  return true;
3204 }
3205 
3206 bool CoreWrapper::getNodeDataCallback(rtabmap_ros::GetNodeData::Request& req, rtabmap_ros::GetNodeData::Response& res)
3207 {
3208  NODELET_INFO("rtabmap: Getting node data (%d node(s), images=%s scan=%s grid=%s user_data=%s)...",
3209  (int)req.ids.size(),
3210  req.images?"true":"false",
3211  req.scan?"true":"false",
3212  req.grid?"true":"false",
3213  req.user_data?"true":"false");
3214 
3215  if(req.ids.empty() && rtabmap_.getMemory() && rtabmap_.getMemory()->getLastWorkingSignature())
3216  {
3217  req.ids.push_back(rtabmap_.getMemory()->getLastWorkingSignature()->id());
3218  }
3219  for(size_t i=0; i<req.ids.size(); ++i)
3220  {
3221  int id = req.ids[i];
3222  Signature s = rtabmap_.getSignatureCopy(id, req.images, req.scan, req.user_data, req.grid, true, true);
3223 
3224  if(s.id()>0)
3225  {
3226  NodeData msg;
3228  res.data.push_back(msg);
3229  }
3230  }
3231 
3232  return !res.data.empty();
3233 }
3234 
3235 bool CoreWrapper::getMapDataCallback(rtabmap_ros::GetMap::Request& req, rtabmap_ros::GetMap::Response& res)
3236 {
3237  NODELET_INFO("rtabmap: Getting map (global=%s optimized=%s graphOnly=%s)...",
3238  req.global?"true":"false",
3239  req.optimized?"true":"false",
3240  req.graphOnly?"true":"false");
3241  std::map<int, Signature> signatures;
3242  std::map<int, Transform> poses;
3243  std::multimap<int, rtabmap::Link> constraints;
3244 
3246  poses,
3247  constraints,
3248  req.optimized,
3249  req.global,
3250  &signatures,
3251  !req.graphOnly,
3252  !req.graphOnly,
3253  !req.graphOnly,
3254  !req.graphOnly);
3255 
3256  //RGB-D SLAM data
3258  constraints,
3259  signatures,
3260  mapToOdom_,
3261  res.data);
3262 
3263  res.data.header.stamp = ros::Time::now();
3264  res.data.header.frame_id = mapFrameId_;
3265 
3266  return true;
3267 }
3268 
3269 bool CoreWrapper::getMapData2Callback(rtabmap_ros::GetMap2::Request& req, rtabmap_ros::GetMap2::Response& res)
3270 {
3271  NODELET_INFO("rtabmap: Getting map (global=%s optimized=%s with_images=%s with_scans=%s with_user_data=%s with_grids=%s)...",
3272  req.global?"true":"false",
3273  req.optimized?"true":"false",
3274  req.with_images?"true":"false",
3275  req.with_scans?"true":"false",
3276  req.with_user_data?"true":"false",
3277  req.with_grids?"true":"false");
3278  std::map<int, Signature> signatures;
3279  std::map<int, Transform> poses;
3280  std::multimap<int, rtabmap::Link> constraints;
3281 
3283  poses,
3284  constraints,
3285  req.optimized,
3286  req.global,
3287  &signatures,
3288  req.with_images,
3289  req.with_scans,
3290  req.with_user_data,
3291  req.with_grids,
3292  req.with_words,
3293  req.with_global_descriptors);
3294 
3295  //RGB-D SLAM data
3297  constraints,
3298  signatures,
3299  mapToOdom_,
3300  res.data);
3301 
3302  res.data.header.stamp = ros::Time::now();
3303  res.data.header.frame_id = mapFrameId_;
3304 
3305  return true;
3306 }
3307 
3308 bool CoreWrapper::getProjMapCallback(nav_msgs::GetMap::Request &req, nav_msgs::GetMap::Response &res)
3309 {
3310  if(parameters_.find(Parameters::kGridSensor()) != parameters_.end() &&
3311  uStr2Int(parameters_.at(Parameters::kGridSensor()))==0)
3312  {
3313  NODELET_WARN("/get_proj_map service is deprecated! Call /get_grid_map service "
3314  "instead with <param name=\"%s\" type=\"string\" value=\"1\"/>. "
3315  "Do \"$ rosrun rtabmap_ros rtabmap --params | grep Grid\" to see "
3316  "all occupancy grid parameters.",
3317  Parameters::kGridSensor().c_str());
3318  }
3319  else
3320  {
3321  NODELET_WARN("/get_proj_map service is deprecated! Call /get_grid_map service instead.");
3322  }
3323  return getGridMapCallback(req, res);
3324 }
3325 
3326 bool CoreWrapper::getGridMapCallback(nav_msgs::GetMap::Request &req, nav_msgs::GetMap::Response &res)
3327 {
3328  NODELET_WARN("/get_grid_map service is deprecated! Call /get_map service instead.");
3329  return getMapCallback(req, res);
3330 }
3331 
3332 bool CoreWrapper::getMapCallback(nav_msgs::GetMap::Request &req, nav_msgs::GetMap::Response &res)
3333 {
3334  // Make sure grid map cache is up to date (in case there is no subscriber on map topics)
3335  std::map<int, Transform> poses = rtabmap_.getLocalOptimizedPoses();
3336  mapsManager_.updateMapCaches(poses, rtabmap_.getMemory(), true, false);
3337 
3338  // create the grid map
3339  float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f;
3340  cv::Mat pixels = mapsManager_.getGridMap(xMin, yMin, gridCellSize);
3341 
3342  if(!pixels.empty())
3343  {
3344  //init
3345  res.map.info.resolution = gridCellSize;
3346  res.map.info.origin.position.x = 0.0;
3347  res.map.info.origin.position.y = 0.0;
3348  res.map.info.origin.position.z = 0.0;
3349  res.map.info.origin.orientation.x = 0.0;
3350  res.map.info.origin.orientation.y = 0.0;
3351  res.map.info.origin.orientation.z = 0.0;
3352  res.map.info.origin.orientation.w = 1.0;
3353 
3354  res.map.info.width = pixels.cols;
3355  res.map.info.height = pixels.rows;
3356  res.map.info.origin.position.x = xMin;
3357  res.map.info.origin.position.y = yMin;
3358  res.map.data.resize(res.map.info.width * res.map.info.height);
3359 
3360  memcpy(res.map.data.data(), pixels.data, res.map.info.width * res.map.info.height);
3361 
3362  res.map.header.frame_id = mapFrameId_;
3363  res.map.header.stamp = ros::Time::now();
3364  return true;
3365  }
3366  else
3367  {
3368  NODELET_WARN("rtabmap: The map is empty!");
3369  }
3370  return false;
3371 }
3372 
3373 bool CoreWrapper::getProbMapCallback(nav_msgs::GetMap::Request &req, nav_msgs::GetMap::Response &res)
3374 {
3375  // Make sure grid map cache is up to date (in case there is no subscriber on map topics)
3376  std::map<int, Transform> poses = rtabmap_.getLocalOptimizedPoses();
3377  mapsManager_.updateMapCaches(poses, rtabmap_.getMemory(), true, false);
3378 
3379  // create the grid map
3380  float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f;
3381  cv::Mat pixels = mapsManager_.getGridProbMap(xMin, yMin, gridCellSize);
3382 
3383  if(!pixels.empty())
3384  {
3385  //init
3386  res.map.info.resolution = gridCellSize;
3387  res.map.info.origin.position.x = 0.0;
3388  res.map.info.origin.position.y = 0.0;
3389  res.map.info.origin.position.z = 0.0;
3390  res.map.info.origin.orientation.x = 0.0;
3391  res.map.info.origin.orientation.y = 0.0;
3392  res.map.info.origin.orientation.z = 0.0;
3393  res.map.info.origin.orientation.w = 1.0;
3394 
3395  res.map.info.width = pixels.cols;
3396  res.map.info.height = pixels.rows;
3397  res.map.info.origin.position.x = xMin;
3398  res.map.info.origin.position.y = yMin;
3399  res.map.data.resize(res.map.info.width * res.map.info.height);
3400 
3401  memcpy(res.map.data.data(), pixels.data, res.map.info.width * res.map.info.height);
3402 
3403  res.map.header.frame_id = mapFrameId_;
3404  res.map.header.stamp = ros::Time::now();
3405  return true;
3406  }
3407  else
3408  {
3409  NODELET_WARN("rtabmap: The map is empty!");
3410  }
3411  return false;
3412 }
3413 
3414 bool CoreWrapper::publishMapCallback(rtabmap_ros::PublishMap::Request& req, rtabmap_ros::PublishMap::Response& res)
3415 {
3416  NODELET_INFO("rtabmap: Publishing map...");
3417 
3419 
3421  (!req.graphOnly && mapsManager_.hasSubscribers()) ||
3423  {
3424  std::map<int, Transform> poses;
3425  std::multimap<int, rtabmap::Link> constraints;
3426  std::map<int, Signature > signatures;
3427 
3429  poses,
3430  constraints,
3431  req.optimized,
3432  req.global,
3433  &signatures,
3434  !req.graphOnly,
3435  !req.graphOnly,
3436  !req.graphOnly,
3437  !req.graphOnly);
3438 
3440  {
3441  rtabmap_ros::MapDataPtr msg(new rtabmap_ros::MapData);
3442  msg->header.stamp = now;
3443  msg->header.frame_id = mapFrameId_;
3444 
3446  constraints,
3447  signatures,
3448  mapToOdom_,
3449  *msg);
3450 
3451  mapDataPub_.publish(msg);
3452  }
3453 
3455  {
3456  rtabmap_ros::MapGraphPtr msg(new rtabmap_ros::MapGraph);
3457  msg->header.stamp = now;
3458  msg->header.frame_id = mapFrameId_;
3459 
3461  constraints,
3462  mapToOdom_,
3463  *msg);
3464 
3465  mapGraphPub_.publish(msg);
3466  }
3467 
3468  bool pubLabels = labelsPub_.getNumSubscribers();
3470  if((landmarksPub_.getNumSubscribers() || pubLabels) && !poses.empty() && poses.begin()->first < 0)
3471  {
3472  geometry_msgs::PoseArrayPtr msg(new geometry_msgs::PoseArray);
3473  msg->header.stamp = now;
3474  msg->header.frame_id = mapFrameId_;
3475  for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end() && iter->first<0; ++iter)
3476  {
3477  geometry_msgs::Pose p;
3478  rtabmap_ros::transformToPoseMsg(iter->second, p);
3479  msg->poses.push_back(p);
3480 
3481  if(pubLabels)
3482  {
3483  // Add landmark ids
3484  visualization_msgs::Marker marker;
3485  marker.header.frame_id = mapFrameId_;
3486  marker.header.stamp = now;
3487  marker.ns = "landmarks";
3488  marker.id = iter->first;
3489  marker.action = visualization_msgs::Marker::ADD;
3490  marker.pose.position.x = iter->second.x();
3491  marker.pose.position.y = iter->second.y();
3492  marker.pose.position.z = iter->second.z();
3493  marker.pose.orientation.x = 0.0;
3494  marker.pose.orientation.y = 0.0;
3495  marker.pose.orientation.z = 0.0;
3496  marker.pose.orientation.w = 1.0;
3497  marker.scale.x = 1;
3498  marker.scale.y = 1;
3499  marker.scale.z = 0.35;
3500  marker.color.a = 0.5;
3501  marker.color.r = 1.0;
3502  marker.color.g = 1.0;
3503  marker.color.b = 0.0;
3504  marker.lifetime = ros::Duration(2.0f/rate_);
3505 
3506  marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
3507  marker.text = uNumber2Str(iter->first);
3508 
3509  markers.markers.push_back(marker);
3510  }
3511  }
3512 
3513  landmarksPub_.publish(msg);
3514  }
3515 
3516  if(!req.graphOnly)
3517  {
3519  {
3520  std::map<int, Transform> filteredPoses(poses.lower_bound(1), poses.end());
3521  if((mappingMaxNodes_ > 0 || mappingAltitudeDelta_>0.0) && filteredPoses.size()>1)
3522  {
3523  std::map<int, Transform> nearestPoses = filterNodesToAssemble(filteredPoses, filteredPoses.rbegin()->second);
3524  }
3525  if(signatures.size())
3526  {
3527  filteredPoses = mapsManager_.updateMapCaches(
3528  filteredPoses,
3529  rtabmap_.getMemory(),
3530  false,
3531  false,
3532  signatures);
3533  }
3534  else
3535  {
3536  filteredPoses = mapsManager_.getFilteredPoses(filteredPoses);
3537  }
3538  mapsManager_.publishMaps(filteredPoses, now, mapFrameId_);
3539  }
3540  else
3541  {
3542  // this will cleanup the cache if there are no subscribers
3543  mapsManager_.publishMaps(std::map<int, Transform>(), now, mapFrameId_);
3544  }
3545  }
3546 
3547  bool pubPath = mapPathPub_.getNumSubscribers();
3548  if(pubLabels || pubPath)
3549  {
3550  if(poses.size() && signatures.size())
3551  {
3552  nav_msgs::Path path;
3553  if(pubPath)
3554  {
3555  path.poses.resize(poses.size());
3556  }
3557  int oi=0;
3558  for(std::map<int, Signature>::const_iterator iter=signatures.begin();
3559  iter!=signatures.end();
3560  ++iter)
3561  {
3562  std::map<int, Transform>::const_iterator poseIter= poses.find(iter->first);
3563  if(poseIter!=poses.end())
3564  {
3565  if(pubLabels)
3566  {
3567  // Add labels
3568  if(!iter->second.getLabel().empty())
3569  {
3570  visualization_msgs::Marker marker;
3571  marker.header.frame_id = mapFrameId_;
3572  marker.header.stamp = now;
3573  marker.ns = "labels";
3574  marker.id = iter->first;
3575  marker.action = visualization_msgs::Marker::ADD;
3576  marker.pose.position.x = poseIter->second.x();
3577  marker.pose.position.y = poseIter->second.y();
3578  marker.pose.position.z = poseIter->second.z();
3579  marker.pose.orientation.x = 0.0;
3580  marker.pose.orientation.y = 0.0;
3581  marker.pose.orientation.z = 0.0;
3582  marker.pose.orientation.w = 1.0;
3583  marker.scale.x = 1;
3584  marker.scale.y = 1;
3585  marker.scale.z = 0.5;
3586  marker.color.a = 0.7;
3587  marker.color.r = 1.0;
3588  marker.color.g = 0.0;
3589  marker.color.b = 0.0;
3590 
3591  marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
3592  marker.text = iter->second.getLabel();
3593 
3594  markers.markers.push_back(marker);
3595  }
3596  // Add node ids
3597  visualization_msgs::Marker marker;
3598  marker.header.frame_id = mapFrameId_;
3599  marker.header.stamp = now;
3600  marker.ns = "ids";
3601  marker.id = iter->first;
3602  marker.action = visualization_msgs::Marker::ADD;
3603  marker.pose.position.x = poseIter->second.x();
3604  marker.pose.position.y = poseIter->second.y();
3605  marker.pose.position.z = poseIter->second.z();
3606  marker.pose.orientation.x = 0.0;
3607  marker.pose.orientation.y = 0.0;
3608  marker.pose.orientation.z = 0.0;
3609  marker.pose.orientation.w = 1.0;
3610  marker.scale.x = 1;
3611  marker.scale.y = 1;
3612  marker.scale.z = 0.2;
3613  marker.color.a = 0.5;
3614  marker.color.r = 1.0;
3615  marker.color.g = 1.0;
3616  marker.color.b = 1.0;
3617  marker.lifetime = ros::Duration(2.0f/rate_);
3618 
3619  marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
3620  marker.text = uNumber2Str(iter->first);
3621 
3622  markers.markers.push_back(marker);
3623  }
3624  if(pubPath)
3625  {
3626  rtabmap_ros::transformToPoseMsg(poseIter->second, path.poses.at(oi).pose);
3627  path.poses.at(oi).header.frame_id = mapFrameId_;
3628  path.poses.at(oi).header.stamp = ros::Time(iter->second.getStamp());
3629  ++oi;
3630  }
3631  }
3632  }
3633 
3634  if(pubLabels && markers.markers.size())
3635  {
3636  labelsPub_.publish(markers);
3637  }
3638  if(pubPath && oi)
3639  {
3640  path.header.frame_id = mapFrameId_;
3641  path.header.stamp = now;
3642  path.poses.resize(oi);
3643  mapPathPub_.publish(path);
3644  }
3645  }
3646  }
3647  }
3648  else
3649  {
3650  UWARN("No subscribers, don't need to publish!");
3651  if(!req.graphOnly)
3652  {
3653  // this will cleanup the cache if there are no subscribers
3654  mapsManager_.publishMaps(std::map<int, Transform>(), now, mapFrameId_);
3655  }
3656  }
3657 
3658  return true;
3659 }
3660 
3661 bool CoreWrapper::getPlanCallback(nav_msgs::GetPlan::Request &req, nav_msgs::GetPlan::Response &res)
3662 {
3663  Transform pose = rtabmap_ros::transformFromPoseMsg(req.goal.pose, true);
3664  UTimer timer;
3665  if(!pose.isNull())
3666  {
3667  // transform goal in /map frame
3668  Transform coordinateTransform = Transform::getIdentity();
3669  if(!req.goal.header.frame_id.empty() && mapFrameId_.compare(req.goal.header.frame_id) != 0)
3670  {
3671  coordinateTransform = rtabmap_ros::getTransform(mapFrameId_, req.goal.header.frame_id, req.goal.header.stamp, tfListener_, waitForTransform_?waitForTransformDuration_:0.0);
3672  if(coordinateTransform.isNull())
3673  {
3674  NODELET_ERROR("Cannot transform goal pose from \"%s\" frame to \"%s\" frame!",
3675  req.goal.header.frame_id.c_str(), mapFrameId_.c_str());
3676  return false;
3677  }
3678  pose = coordinateTransform * pose;
3679  }
3680  //else assume map frame if not set
3681 
3682  // To convert back the poses in goal frame
3683  coordinateTransform = coordinateTransform.inverse();
3684 
3685  if(rtabmap_.computePath(pose, req.tolerance))
3686  {
3687  NODELET_INFO("Planning: Time computing path = %f s", timer.ticks());
3688  const std::vector<std::pair<int, Transform> > & poses = rtabmap_.getPath();
3689  res.plan.header.frame_id = req.goal.header.frame_id;
3690  res.plan.header.stamp = req.goal.header.stamp;
3691  if(poses.size() == 0)
3692  {
3693  NODELET_WARN("Planning: Goal already reached (RGBD/GoalReachedRadius=%fm).",
3695  // just set the goal directly
3696  res.plan.poses.resize(1);
3697  rtabmap_ros::transformToPoseMsg(coordinateTransform*pose, res.plan.poses[0].pose);
3698  }
3699  else
3700  {
3701  res.plan.poses.resize(poses.size());
3702  int oi = 0;
3703  for(std::vector<std::pair<int, Transform> >::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
3704  {
3705  res.plan.poses[oi].header = res.plan.header;
3706  rtabmap_ros::transformToPoseMsg(coordinateTransform*iter->second, res.plan.poses[oi].pose);
3707  ++oi;
3708  }
3710  {
3711  res.plan.poses.resize(res.plan.poses.size()+1);
3712  res.plan.poses[res.plan.poses.size()-1].header = res.plan.header;
3714  rtabmap_ros::transformToPoseMsg(coordinateTransform*p, res.plan.poses[res.plan.poses.size()-1].pose);
3715  }
3716 
3717  // Just output the path on screen
3718  std::stringstream stream;
3719  for(std::vector<std::pair<int, Transform> >::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
3720  {
3721  if(iter != poses.begin())
3722  {
3723  stream << " ";
3724  }
3725  stream << iter->first;
3726  }
3727  NODELET_INFO("Planned path: [%s]", stream.str().c_str());
3728  }
3729  }
3730  rtabmap_.clearPath(0);
3731  }
3732  return true;
3733 }
3734 
3735 bool CoreWrapper::getPlanNodesCallback(rtabmap_ros::GetPlan::Request &req, rtabmap_ros::GetPlan::Response &res)
3736 {
3737  Transform pose;
3738  if(req.goal_node <= 0)
3739  {
3740  pose = rtabmap_ros::transformFromPoseMsg(req.goal.pose, true);
3741  }
3742  UTimer timer;
3743  if(req.goal_node > 0 || !pose.isNull())
3744  {
3745  Transform coordinateTransform = Transform::getIdentity();
3746  // transform goal in /map frame
3747  if(!pose.isNull() && !req.goal.header.frame_id.empty() && mapFrameId_.compare(req.goal.header.frame_id) != 0)
3748  {
3749  coordinateTransform = rtabmap_ros::getTransform(mapFrameId_, req.goal.header.frame_id, req.goal.header.stamp, tfListener_, waitForTransform_?waitForTransformDuration_:0.0);
3750  if(coordinateTransform.isNull())
3751  {
3752  NODELET_ERROR("Cannot transform goal pose from \"%s\" frame to \"%s\" frame!",
3753  req.goal.header.frame_id.c_str(), mapFrameId_.c_str());
3754  return false;
3755  }
3756  if(!pose.isNull())
3757  {
3758  pose = coordinateTransform * pose;
3759  }
3760  }
3761  //else assume map frame if not set
3762 
3763  // To convert back the poses in goal frame
3764  coordinateTransform = coordinateTransform.inverse();
3765 
3766  if((req.goal_node > 0 && rtabmap_.computePath(req.goal_node, req.tolerance)) ||
3767  (req.goal_node <= 0 && rtabmap_.computePath(pose, req.tolerance)))
3768  {
3769  NODELET_INFO("Planning: Time computing path = %f s", timer.ticks());
3770  const std::vector<std::pair<int, Transform> > & poses = rtabmap_.getPath();
3771  res.plan.header.frame_id = mapFrameId_;
3772  res.plan.header.stamp = req.goal_node > 0?ros::Time::now():req.goal.header.stamp;
3773  if(poses.size() == 0)
3774  {
3775  NODELET_WARN("Planning: Goal already reached (RGBD/GoalReachedRadius=%fm).",
3777  if(!pose.isNull())
3778  {
3779  // just set the goal directly
3780  res.plan.poses.resize(1);
3781  res.plan.nodeIds.resize(1);
3782  rtabmap_ros::transformToPoseMsg(coordinateTransform*pose, res.plan.poses[0]);
3783  res.plan.nodeIds[0] = 0;
3784  }
3785  }
3786  else
3787  {
3788  res.plan.poses.resize(poses.size());
3789  res.plan.nodeIds.resize(poses.size());
3790  int oi = 0;
3791  for(std::vector<std::pair<int, Transform> >::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
3792  {
3793  rtabmap_ros::transformToPoseMsg(coordinateTransform*iter->second, res.plan.poses[oi]);
3794  res.plan.nodeIds[oi] = iter->first;
3795  ++oi;
3796  }
3798  {
3799  res.plan.poses.resize(res.plan.poses.size()+1);
3800  res.plan.nodeIds.resize(res.plan.nodeIds.size()+1);
3802  rtabmap_ros::transformToPoseMsg(coordinateTransform*p, res.plan.poses[res.plan.poses.size()-1]);
3803  res.plan.nodeIds[res.plan.nodeIds.size()-1] = 0;
3804  }
3805 
3806  // Just output the path on screen
3807  std::stringstream stream;
3808  for(std::vector<std::pair<int, Transform> >::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
3809  {
3810  if(iter != poses.begin())
3811  {
3812  stream << " ";
3813  }
3814  stream << iter->first;
3815  }
3816  NODELET_INFO("Planned path: [%s]", stream.str().c_str());
3817  }
3818  }
3819  rtabmap_.clearPath(0);
3820  }
3821  return true;
3822 }
3823 
3824 bool CoreWrapper::setGoalCallback(rtabmap_ros::SetGoal::Request& req, rtabmap_ros::SetGoal::Response& res)
3825 {
3826  double planningTime = 0.0;
3827  goalCommonCallback(req.node_id, req.node_label, req.frame_id, Transform(), ros::Time::now(), &planningTime);
3828  const std::vector<std::pair<int, Transform> > & path = rtabmap_.getPath();
3829  res.path_ids.resize(path.size());
3830  res.path_poses.resize(path.size());
3831  res.planning_time = planningTime;
3832  for(unsigned int i=0; i<path.size(); ++i)
3833  {
3834  res.path_ids[i] = path[i].first;
3835  rtabmap_ros::transformToPoseMsg(path[i].second, res.path_poses[i]);
3836  }
3837  return true;
3838 }
3839 
3840 bool CoreWrapper::cancelGoalCallback(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res)
3841 {
3842  if(rtabmap_.getPath().size())
3843  {
3844  NODELET_WARN("Goal cancelled!");
3845  rtabmap_.clearPath(0);
3848  goalFrameId_.clear();
3849  latestNodeWasReached_ = false;
3851  {
3852  std_msgs::Bool result;
3853  result.data = false;
3854  goalReachedPub_.publish(result);
3855  }
3856  }
3858  {
3859  mbClient_->cancelGoal();
3860  }
3861 
3862  return true;
3863 }
3864 
3865 bool CoreWrapper::setLabelCallback(rtabmap_ros::SetLabel::Request& req, rtabmap_ros::SetLabel::Response& res)
3866 {
3867  if(rtabmap_.labelLocation(req.node_id, req.node_label))
3868  {
3869  if(req.node_id > 0)
3870  {
3871  NODELET_INFO("Set label \"%s\" to node %d", req.node_label.c_str(), req.node_id);
3872  }
3873  else
3874  {
3875  NODELET_INFO("Set label \"%s\" to last node", req.node_label.c_str());
3876  }
3877  }
3878  else
3879  {
3880  if(req.node_id > 0)
3881  {
3882  NODELET_ERROR("Could not set label \"%s\" to node %d", req.node_label.c_str(), req.node_id);
3883  }
3884  else
3885  {
3886  NODELET_ERROR("Could not set label \"%s\" to last node", req.node_label.c_str());
3887  }
3888  }
3889  return true;
3890 }
3891 
3892 bool CoreWrapper::listLabelsCallback(rtabmap_ros::ListLabels::Request& req, rtabmap_ros::ListLabels::Response& res)
3893 {
3894  if(rtabmap_.getMemory())
3895  {
3896  std::map<int, std::string> labels = rtabmap_.getMemory()->getAllLabels();
3897  res.ids = uKeys(labels);
3898  res.labels = uValues(labels);
3899  NODELET_INFO("List labels service: %d labels found.", (int)res.labels.size());
3900  }
3901  return true;
3902 }
3903 
3904 bool CoreWrapper::removeLabelCallback(rtabmap_ros::RemoveLabel::Request& req, rtabmap_ros::RemoveLabel::Response& res)
3905 {
3906  if(rtabmap_.getMemory())
3907  {
3908  int id = rtabmap_.getMemory()->getSignatureIdByLabel(req.label, true);
3909  if(id == 0)
3910  {
3911  NODELET_WARN("Label \"%s\" not found in the map, cannot remove it!", req.label.c_str());
3912  }
3913  else if(!rtabmap_.labelLocation(id, ""))
3914  {
3915  NODELET_ERROR("Failed removing label \"%s\".", req.label.c_str());
3916  }
3917  else
3918  {
3919  NODELET_INFO("Removed label \"%s\".", req.label.c_str());
3920  }
3921  }
3922  return true;
3923 }
3924 
3925 bool CoreWrapper::addLinkCallback(rtabmap_ros::AddLink::Request& req, rtabmap_ros::AddLink::Response&)
3926 {
3927  if(rtabmap_.getMemory())
3928  {
3929  ROS_INFO("Adding external link %d -> %d", req.link.fromId, req.link.toId);
3930  rtabmap_.addLink(linkFromROS(req.link));
3931  return true;
3932  }
3933  return false;
3934 }
3935 
3936 bool CoreWrapper::getNodesInRadiusCallback(rtabmap_ros::GetNodesInRadius::Request& req, rtabmap_ros::GetNodesInRadius::Response& res)
3937 {
3938  ROS_INFO("Get nodes in radius (%f): node_id=%d pose=(%f,%f,%f)", req.radius, req.node_id, req.x, req.y, req.z);
3939  std::map<int, Transform> poses;
3940  std::map<int, float> dists;
3941  if(req.node_id != 0 || (req.x == 0.0f && req.y == 0.0f && req.z == 0.0f))
3942  {
3943  poses = rtabmap_.getNodesInRadius(req.node_id, req.radius, req.k, &dists);
3944  }
3945  else
3946  {
3947  poses = rtabmap_.getNodesInRadius(Transform(req.x, req.y, req.z, 0,0,0), req.radius, req.k, &dists);
3948  }
3949 
3950  //Optimized graph
3951  res.ids.resize(poses.size());
3952  res.poses.resize(poses.size());
3953  res.distsSqr.resize(poses.size());
3954  int index = 0;
3955  for(std::map<int, rtabmap::Transform>::const_iterator iter = poses.begin();
3956  iter != poses.end();
3957  ++iter)
3958  {
3959  res.ids[index] = iter->first;
3960  transformToPoseMsg(iter->second, res.poses[index]);
3961  UASSERT(dists.find(iter->first) != dists.end());
3962  res.distsSqr[index] = dists.at(iter->first);
3963  ++index;
3964  }
3965 
3966  return true;
3967 }
3968 
3970 {
3971  UDEBUG("Publishing stats...");
3972  const rtabmap::Statistics & stats = rtabmap_.getStatistics();
3973 
3975  {
3976  //NODELET_INFO("Sending RtabmapInfo msg (last_id=%d)...", stat.refImageId());
3977  rtabmap_ros::InfoPtr msg(new rtabmap_ros::Info);
3978  msg->header.stamp = stamp;
3979  msg->header.frame_id = mapFrameId_;
3980 
3981  rtabmap_ros::infoToROS(stats, *msg);
3982  infoPub_.publish(msg);
3983  }
3984 
3986  {
3987  rtabmap_ros::MapDataPtr msg(new rtabmap_ros::MapData);
3988  msg->header.stamp = stamp;
3989  msg->header.frame_id = mapFrameId_;
3990 
3992  stats.poses(),
3993  stats.constraints(),
3994  stats.getSignaturesData(),
3995  stats.mapCorrection(),
3996  *msg);
3997 
3998  mapDataPub_.publish(msg);
3999  }
4000 
4002  {
4003  rtabmap_ros::MapGraphPtr msg(new rtabmap_ros::MapGraph);
4004  msg->header.stamp = stamp;
4005  msg->header.frame_id = mapFrameId_;
4006 
4008  stats.poses(),
4009  stats.constraints(),
4010  stats.mapCorrection(),
4011  *msg);
4012 
4013  mapGraphPub_.publish(msg);
4014  }
4015 
4017  {
4018  rtabmap_ros::MapGraphPtr msg(new rtabmap_ros::MapGraph);
4019  msg->header.stamp = stamp;
4020  msg->header.frame_id = mapFrameId_;
4021 
4022  // For visualization of the constraints (MapGraph rviz plugin), we should include target nodes from the map
4023  std::map<int, Transform> poses = stats.odomCachePoses();
4024  // transform in map frame
4025  for(std::map<int, Transform>::iterator iter=poses.begin();
4026  iter!=poses.end();
4027  ++iter)
4028  {
4029  iter->second = stats.mapCorrection() * iter->second;
4030  }
4031  for(std::multimap<int, rtabmap::Link>::const_iterator iter=stats.odomCacheConstraints().begin();
4032  iter!=stats.odomCacheConstraints().end();
4033  ++iter)
4034  {
4035  std::map<int, Transform>::const_iterator pter = stats.poses().find(iter->second.to());
4036  if(pter != stats.poses().end())
4037  {
4038  poses.insert(*pter);
4039  }
4040  }
4042  poses,
4043  stats.odomCacheConstraints(),
4044  stats.mapCorrection(),
4045  *msg);
4046 
4047  odomCachePub_.publish(msg);
4048  }
4049 
4051  {
4052  pcl::PCLPointCloud2::Ptr cloud = rtabmap::util3d::laserScanToPointCloud2(LaserScan::backwardCompatibility(stats.getLastSignatureData().sensorData().gridObstacleCellsRaw()));
4053  sensor_msgs::PointCloud2 msg;
4054  pcl_conversions::moveFromPCL(*cloud, msg);
4055  msg.header.stamp = stamp;
4056  msg.header.frame_id = frameId_;
4058  }
4060  {
4061  pcl::PCLPointCloud2::Ptr cloud = rtabmap::util3d::laserScanToPointCloud2(LaserScan::backwardCompatibility(stats.getLastSignatureData().sensorData().gridEmptyCellsRaw()));
4062  sensor_msgs::PointCloud2 msg;
4063  pcl_conversions::moveFromPCL(*cloud, msg);
4064  msg.header.stamp = stamp;
4065  msg.header.frame_id = frameId_;
4066  localGridEmpty_.publish(msg);
4067  }
4069  {
4070  pcl::PCLPointCloud2::Ptr cloud = rtabmap::util3d::laserScanToPointCloud2(LaserScan::backwardCompatibility(stats.getLastSignatureData().sensorData().gridGroundCellsRaw()));
4071  sensor_msgs::PointCloud2 msg;
4072  pcl_conversions::moveFromPCL(*cloud, msg);
4073  msg.header.stamp = stamp;
4074  msg.header.frame_id = frameId_;
4076  }
4077 
4078  bool pubLabels = labelsPub_.getNumSubscribers();
4080  if((landmarksPub_.getNumSubscribers() || pubLabels) && !stats.poses().empty() && stats.poses().begin()->first < 0)
4081  {
4082  geometry_msgs::PoseArrayPtr msg(new geometry_msgs::PoseArray);
4083  msg->header.stamp = stamp;
4084  msg->header.frame_id = mapFrameId_;
4085  for(std::map<int, Transform>::const_iterator iter=stats.poses().begin(); iter!=stats.poses().end() && iter->first<0; ++iter)
4086  {
4087  geometry_msgs::Pose p;
4088  rtabmap_ros::transformToPoseMsg(iter->second, p);
4089  msg->poses.push_back(p);
4090 
4091  if(pubLabels)
4092  {
4093  // Add landmark ids
4094  visualization_msgs::Marker marker;
4095  marker.header.frame_id = mapFrameId_;
4096  marker.header.stamp = stamp;
4097  marker.ns = "landmarks";
4098  marker.id = iter->first;
4099  marker.action = visualization_msgs::Marker::ADD;
4100  marker.pose.position.x = iter->second.x();
4101  marker.pose.position.y = iter->second.y();
4102  marker.pose.position.z = iter->second.z();
4103  marker.pose.orientation.x = 0.0;
4104  marker.pose.orientation.y = 0.0;
4105  marker.pose.orientation.z = 0.0;
4106  marker.pose.orientation.w = 1.0;
4107  marker.scale.x = 1;
4108  marker.scale.y = 1;
4109  marker.scale.z = 0.35;
4110  marker.color.a = 0.7;
4111  marker.color.r = 0.0;
4112  marker.color.g = 1.0;
4113  marker.color.b = 0.0;
4114  marker.lifetime = ros::Duration(2.0f/rate_);
4115 
4116  marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
4117  marker.text = uNumber2Str(iter->first);
4118 
4119  markers.markers.push_back(marker);
4120  }
4121  }
4122 
4123  landmarksPub_.publish(msg);
4124  }
4125 
4126  bool pubPath = mapPathPub_.getNumSubscribers();
4127  if(pubLabels || pubPath)
4128  {
4129  if(stats.poses().size())
4130  {
4131  nav_msgs::Path path;
4132  if(pubPath)
4133  {
4134  // Ignore pose of current location in Localization mode
4135  path.poses.resize(stats.poses().size()-(rtabmap_.getMemory()->isIncremental()?0:1));
4136  }
4137  int oi = 0;
4138  for(std::map<int, Transform>::const_iterator poseIter=stats.poses().begin();
4139  poseIter!=stats.poses().end();
4140  ++poseIter)
4141  {
4142  if(pubLabels && rtabmap_.getMemory())
4143  {
4144  // Add labels
4145  std::map<int, std::string>::const_iterator lter = rtabmap_.getMemory()->getAllLabels().find(poseIter->first);
4146  if(lter != rtabmap_.getMemory()->getAllLabels().end() && !lter->second.empty())
4147  {
4148  visualization_msgs::Marker marker;
4149  marker.header.frame_id = mapFrameId_;
4150  marker.header.stamp = stamp;
4151  marker.ns = "labels";
4152  marker.id = -poseIter->first;
4153  marker.action = visualization_msgs::Marker::ADD;
4154  marker.pose.position.x = poseIter->second.x();
4155  marker.pose.position.y = poseIter->second.y();
4156  marker.pose.position.z = poseIter->second.z();
4157  marker.pose.orientation.x = 0.0;
4158  marker.pose.orientation.y = 0.0;
4159  marker.pose.orientation.z = 0.0;
4160  marker.pose.orientation.w = 1.0;
4161  marker.scale.x = 1;
4162  marker.scale.y = 1;
4163  marker.scale.z = 0.5;
4164  marker.color.a = 0.7;
4165  marker.color.r = 1.0;
4166  marker.color.g = 0.0;
4167  marker.color.b = 0.0;
4168 
4169  marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
4170  marker.text = lter->second;
4171 
4172  markers.markers.push_back(marker);
4173  }
4174 
4175  // Add node ids
4176  visualization_msgs::Marker marker;
4177  marker.header.frame_id = mapFrameId_;
4178  marker.header.stamp = stamp;
4179  marker.ns = "ids";
4180  marker.id = poseIter->first;
4181  marker.action = visualization_msgs::Marker::ADD;
4182  marker.pose.position.x = poseIter->second.x();
4183  marker.pose.position.y = poseIter->second.y();
4184  marker.pose.position.z = poseIter->second.z();
4185  marker.pose.orientation.x = 0.0;
4186  marker.pose.orientation.y = 0.0;
4187  marker.pose.orientation.z = 0.0;
4188  marker.pose.orientation.w = 1.0;
4189  marker.scale.x = 1;
4190  marker.scale.y = 1;
4191  marker.scale.z = 0.2;
4192  marker.color.a = 0.5;
4193  marker.color.r = 1.0;
4194  marker.color.g = 1.0;
4195  marker.color.b = 1.0;
4196  marker.lifetime = ros::Duration(2.0f/rate_);
4197 
4198  marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
4199  marker.text = uNumber2Str(poseIter->first);
4200 
4201  markers.markers.push_back(marker);
4202  }
4203  if(pubPath && (rtabmap_.getMemory()->isIncremental() || poseIter->first != stats.poses().rbegin()->first))
4204  {
4205  rtabmap_ros::transformToPoseMsg(poseIter->second, path.poses.at(oi).pose);
4206  path.poses.at(oi).header.frame_id = mapFrameId_;
4207  path.poses.at(oi).header.stamp = stamp;
4208  ++oi;
4209  }
4210  }
4211 
4212  if(pubLabels && markers.markers.size())
4213  {
4214  labelsPub_.publish(markers);
4215  }
4216  if(pubPath && oi)
4217  {
4218  path.header.frame_id = mapFrameId_;
4219  path.header.stamp = stamp;
4220  path.poses.resize(oi);
4221  mapPathPub_.publish(path);
4222  }
4223  }
4224  }
4225 }
4226 
4228 {
4230  {
4231  NODELET_INFO("Publishing next goal: %d -> %s",
4233 
4234  geometry_msgs::PoseStamped poseMsg;
4235  poseMsg.header.frame_id = mapFrameId_;
4236  poseMsg.header.stamp = stamp;
4238 
4239  if(useActionForGoal_)
4240  {
4241  if(mbClient_ == 0 || !mbClient_->isServerConnected())
4242  {
4243  NODELET_INFO("Connecting to move_base action server...");
4244  if(mbClient_ == 0)
4245  {
4246  mbClient_ = new MoveBaseClient("move_base", true);
4247  }
4249  }
4251  {
4252  move_base_msgs::MoveBaseGoal goal;
4253  goal.target_pose = poseMsg;
4254 
4255  mbClient_->sendGoal(goal,
4256  boost::bind(&CoreWrapper::goalDoneCb, this, boost::placeholders::_1, boost::placeholders::_2),
4257  boost::bind(&CoreWrapper::goalActiveCb, this),
4258  boost::bind(&CoreWrapper::goalFeedbackCb, this, boost::placeholders::_1));
4260  }
4261  else
4262  {
4263  NODELET_ERROR("Cannot connect to move_base action server (called \"%s\")!", this->getNodeHandle().resolveName("move_base").c_str());
4264  }
4265  }
4267  {
4268  nextMetricGoalPub_.publish(poseMsg);
4269  if(!useActionForGoal_)
4270  {
4272  }
4273  }
4274  }
4275 }
4276 
4277 // Called once when the goal completes
4279  const move_base_msgs::MoveBaseResultConstPtr& result)
4280 {
4281  bool ignore = false;
4282  if(!currentMetricGoal_.isNull())
4283  {
4285  {
4286  if(rtabmap_.getPath().size() &&
4287  rtabmap_.getPathCurrentGoalId() != rtabmap_.getPath().back().first &&
4289  {
4290  NODELET_WARN("Planning: move_base reached current goal but it is not "
4291  "the last one planned by rtabmap. A new goal should be sent when "
4292  "rtabmap will be able to retrieve next locations on the path.");
4293  ignore = true;
4294  }
4295  else
4296  {
4297  NODELET_INFO("Planning: move_base success!");
4298  }
4299  }
4300  else
4301  {
4302  NODELET_ERROR("Planning: move_base failed for some reason. Aborting the plan...");
4303  }
4304 
4305  if(!ignore && goalReachedPub_.getNumSubscribers())
4306  {
4307  std_msgs::Bool result;
4308  result.data = state == actionlib::SimpleClientGoalState::SUCCEEDED;
4309  goalReachedPub_.publish(result);
4310  }
4311  }
4312 
4313  if(!ignore)
4314  {
4315  rtabmap_.clearPath(1);
4318  goalFrameId_.clear();
4319  latestNodeWasReached_ = false;
4320  }
4321 }
4322 
4323 // Called once when the goal becomes active
4325 {
4326  //NODELET_INFO("Planning: Goal just went active");
4327 }
4328 
4329 // Called every time feedback is received for the goal
4330 void CoreWrapper::goalFeedbackCb(const move_base_msgs::MoveBaseFeedbackConstPtr& feedback)
4331 {
4332  //Transform basePosition = rtabmap_ros::transformFromPoseMsg(feedback->base_position.pose);
4333  //NODELET_INFO("Planning: feedback base_position = %s", basePosition.prettyPrint().c_str());
4334 }
4335 
4337 {
4338  if(rtabmap_.getPath().size())
4339  {
4340  std::vector<std::pair<int, Transform> > poses = rtabmap_.getPathNextPoses();
4341  if(poses.size())
4342  {
4344  {
4345  nav_msgs::Path path;
4346  rtabmap_ros::Path pathNodes;
4347  path.header.frame_id = pathNodes.header.frame_id = mapFrameId_;
4348  path.header.stamp = pathNodes.header.stamp = stamp;
4349  path.poses.resize(poses.size());
4350  pathNodes.nodeIds.resize(poses.size());
4351  pathNodes.poses.resize(poses.size());
4352  int oi = 0;
4353  for(std::vector<std::pair<int, Transform> >::iterator iter=poses.begin(); iter!=poses.end(); ++iter)
4354  {
4355  path.poses[oi].header = path.header;
4356  rtabmap_ros::transformToPoseMsg(iter->second, path.poses[oi].pose);
4357  pathNodes.poses[oi] = path.poses[oi].pose;
4358  pathNodes.nodeIds[oi] = iter->first;
4359  ++oi;
4360  }
4362  {
4363  localPathPub_.publish(path);
4364  }
4366  {
4367  localPathNodesPub_.publish(pathNodes);
4368  }
4369  }
4370  }
4371  }
4372 }
4373 
4375 {
4377  {
4379  if(!pose.isNull() && rtabmap_.getPathCurrentGoalIndex() < rtabmap_.getPath().size())
4380  {
4381  // transform the global path in the goal referential
4382  Transform t = pose * rtabmap_.getPath().at(rtabmap_.getPathCurrentGoalIndex()).second.inverse();
4383 
4384  nav_msgs::Path path;
4385  rtabmap_ros::Path pathNodes;
4386  path.header.frame_id = pathNodes.header.frame_id = mapFrameId_;
4387  path.header.stamp = pathNodes.header.stamp = stamp;
4388  path.poses.resize(rtabmap_.getPath().size());
4389  pathNodes.nodeIds.resize(rtabmap_.getPath().size());
4390  pathNodes.poses.resize(rtabmap_.getPath().size());
4391  int oi = 0;
4392  for(std::vector<std::pair<int, Transform> >::const_iterator iter=rtabmap_.getPath().begin(); iter!=rtabmap_.getPath().end(); ++iter)
4393  {
4394  path.poses[oi].header = path.header;
4395  rtabmap_ros::transformToPoseMsg(t*iter->second, path.poses[oi].pose);
4396  pathNodes.poses[oi] = path.poses[oi].pose;
4397  pathNodes.nodeIds[oi] = iter->first;
4398  ++oi;
4399  }
4400  Transform goalLocalTransform = Transform::getIdentity();
4401  if(!goalFrameId_.empty() && goalFrameId_.compare(frameId_) != 0)
4402  {
4404  if(!localT.isNull())
4405  {
4406  goalLocalTransform = localT.inverse().to3DoF();
4407  }
4408  }
4409 
4410  if(!rtabmap_.getPathTransformToGoal().isIdentity() || !goalLocalTransform.isIdentity())
4411  {
4412  path.poses.resize(path.poses.size()+1);
4413  path.poses[path.poses.size()-1].header = path.header;
4414  pathNodes.nodeIds.resize(pathNodes.nodeIds.size()+1);
4415  pathNodes.poses.resize(pathNodes.poses.size()+1);
4416  Transform p = t * rtabmap_.getPath().back().second*rtabmap_.getPathTransformToGoal() * goalLocalTransform;
4417  rtabmap_ros::transformToPoseMsg(p, path.poses[path.poses.size()-1].pose);
4418  pathNodes.poses[pathNodes.poses.size()-1] = path.poses[path.poses.size()-1].pose;
4419  pathNodes.nodeIds[pathNodes.nodeIds.size()-1] = 0;
4420  }
4422  {
4423  globalPathPub_.publish(path);
4424  }
4426  {
4427  globalPathNodesPub_.publish(pathNodes);
4428  }
4429  }
4430  }
4431 }
4432 
4433 #ifdef WITH_OCTOMAP_MSGS
4434 #ifdef RTABMAP_OCTOMAP
4435 bool CoreWrapper::octomapBinaryCallback(
4436  octomap_msgs::GetOctomap::Request &req,
4437  octomap_msgs::GetOctomap::Response &res)
4438 {
4439  NODELET_INFO("Sending binary map data on service request");
4440  res.map.header.frame_id = mapFrameId_;
4441  res.map.header.stamp = ros::Time::now();
4442 
4443  std::map<int, Transform> poses = rtabmap_.getLocalOptimizedPoses();
4444  if((mappingMaxNodes_ > 0 || mappingAltitudeDelta_>0.0) && poses.size()>1)
4445  {
4446  poses = filterNodesToAssemble(poses, poses.rbegin()->second);
4447  }
4448 
4449  mapsManager_.updateMapCaches(poses, rtabmap_.getMemory(), false, true);
4450 
4452  bool success = octomap->octree()->size() && octomap_msgs::binaryMapToMsg(*octomap->octree(), res.map);
4453  return success;
4454 }
4455 
4456 bool CoreWrapper::octomapFullCallback(
4457  octomap_msgs::GetOctomap::Request &req,
4458  octomap_msgs::GetOctomap::Response &res)
4459 {
4460  NODELET_INFO("Sending full map data on service request");
4461  res.map.header.frame_id = mapFrameId_;
4462  res.map.header.stamp = ros::Time::now();
4463 
4464  std::map<int, Transform> poses = rtabmap_.getLocalOptimizedPoses();
4465  if((mappingMaxNodes_ > 0 || mappingAltitudeDelta_>0.0) && poses.size()>1)
4466  {
4467  poses = filterNodesToAssemble(poses, poses.rbegin()->second);
4468  }
4469 
4470  mapsManager_.updateMapCaches(poses, rtabmap_.getMemory(), false, true);
4471 
4473  bool success = octomap->octree()->size() && octomap_msgs::fullMapToMsg(*octomap->octree(), res.map);
4474  return success;
4475 }
4476 #endif
4477 #endif
4478 
4480 
4481 }
geometry_msgs::PoseWithCovarianceStamped globalPose_
Definition: CoreWrapper.h:372
ros::ServiceServer setLogErrorSrv_
Definition: CoreWrapper.h:336
bool detectMoreLoopClosuresCallback(rtabmap_ros::DetectMoreLoopClosures::Request &, rtabmap_ros::DetectMoreLoopClosures::Response &)
ros::ServiceServer getMapDataSrv_
Definition: CoreWrapper.h:338
cv::Mat RTABMAP_EXP disparityFromStereoImages(const cv::Mat &leftImage, const cv::Mat &rightImage, const ParametersMap &parameters=ParametersMap())
ros::Subscriber gpsFixAsyncSub_
Definition: CoreWrapper.h:373
CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
static std::string homeDir()
GLM_FUNC_DECL T roll(detail::tquat< T, P > const &x)
Subscriber subscribe(const std::string &base_topic, uint32_t queue_size, const boost::function< void(const sensor_msgs::ImageConstPtr &)> &callback, const ros::VoidPtr &tracked_object=ros::VoidPtr(), const TransportHints &transport_hints=TransportHints())
bool getMapDataCallback(rtabmap_ros::GetMap::Request &req, rtabmap_ros::GetMap::Response &res)
ros::ServiceServer getNodesInRadiusSrv_
Definition: CoreWrapper.h:353
void set2DMap(const cv::Mat &map, float xMin, float yMin, float cellSize, const std::map< int, rtabmap::Transform > &poses, const rtabmap::Memory *memory=0)
std::map< int, rtabmap::Transform > getFilteredPoses(const std::map< int, rtabmap::Transform > &poses)
bool getNodeDataCallback(rtabmap_ros::GetNodeData::Request &req, rtabmap_ros::GetNodeData::Response &res)
const cv::Size & imageSize() const
ros::ServiceServer getProjMapSrv_
Definition: CoreWrapper.h:340
const rtabmap::OccupancyGrid * getOccupancyGrid() const
Definition: MapsManager.h:83
ros::ServiceServer pauseSrv_
Definition: CoreWrapper.h:323
bool isIDsGenerated() const
ros::Publisher localPathNodesPub_
Definition: CoreWrapper.h:315
void defaultCallback(const sensor_msgs::ImageConstPtr &imageMsg)
void setParameters(const rtabmap::ParametersMap &parameters)
void userDataAsyncCallback(const rtabmap_ros::UserDataConstPtr &dataMsg)
std::string getTopic() const
int imageHeight() const
ros::Subscriber republishNodeDataSub_
Definition: CoreWrapper.h:381
const rtabmap::OctoMap * getOctomap() const
Definition: MapsManager.h:82
float getTimeThreshold() const
#define NODELET_ERROR(...)
int getLastSignatureId() const
ros::ServiceServer setLogDebugSrv_
Definition: CoreWrapper.h:333
const double & stamp() const
long length()
CameraModel scaled(double scale) const
bool convertRGBDMsgs(const std::vector< cv_bridge::CvImageConstPtr > &imageMsgs, const std::vector< cv_bridge::CvImageConstPtr > &depthMsgs, const std::vector< sensor_msgs::CameraInfo > &cameraInfoMsgs, const std::vector< sensor_msgs::CameraInfo > &depthCameraInfoMsgs, const std::string &frameId, const std::string &odomFrameId, const ros::Time &odomStamp, cv::Mat &rgb, cv::Mat &depth, std::vector< rtabmap::CameraModel > &cameraModels, std::vector< rtabmap::StereoCameraModel > &stereoCameraModels, tf::TransformListener &listener, double waitForTransform, bool alreadRectifiedImages, const std::vector< std::vector< rtabmap_ros::KeyPoint > > &localKeyPointsMsgs=std::vector< std::vector< rtabmap_ros::KeyPoint > >(), const std::vector< std::vector< rtabmap_ros::Point3f > > &localPoints3dMsgs=std::vector< std::vector< rtabmap_ros::Point3f > >(), const std::vector< cv::Mat > &localDescriptorsMsgs=std::vector< cv::Mat >(), std::vector< cv::KeyPoint > *localKeyPoints=0, std::vector< cv::Point3f > *localPoints3d=0, cv::Mat *localDescriptors=0)
rtabmap::Transform lastPose_
Definition: CoreWrapper.h:252
bool cleanupLocalGridsCallback(rtabmap_ros::CleanupLocalGrids::Request &, rtabmap_ros::CleanupLocalGrids::Response &)
void clearPath(int status)
#define NODELET_WARN(...)
virtual void commonOdomCallback(const nav_msgs::OdometryConstPtr &odomMsg, const rtabmap_ros::UserDataConstPtr &userDataMsg, const rtabmap_ros::OdomInfoConstPtr &odomInfoMsg)
unsigned int getPathCurrentGoalIndex() const
bool setLogWarn(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
ros::Publisher globalPathNodesPub_
Definition: CoreWrapper.h:314
std::string getTopic() const
ros::ServiceServer publishMapDataSrv_
Definition: CoreWrapper.h:344
void goalDoneCb(const actionlib::SimpleClientGoalState &state, const move_base_msgs::MoveBaseResultConstPtr &result)
double elapsed()
std::vector< std::pair< int, Transform > > getPathNextPoses() const
ros::ServiceServer detectMoreLoopClosuresSrv_
Definition: CoreWrapper.h:328
f
virtual void commonMultiCameraCallback(const nav_msgs::OdometryConstPtr &odomMsg, const rtabmap_ros::UserDataConstPtr &userDataMsg, const std::vector< cv_bridge::CvImageConstPtr > &imageMsgs, const std::vector< cv_bridge::CvImageConstPtr > &depthMsgs, const std::vector< sensor_msgs::CameraInfo > &cameraInfoMsgs, const std::vector< sensor_msgs::CameraInfo > &depthCameraInfoMsgs, const sensor_msgs::LaserScan &scanMsg, const sensor_msgs::PointCloud2 &scan3dMsg, const rtabmap_ros::OdomInfoConstPtr &odomInfoMsg, const std::vector< rtabmap_ros::GlobalDescriptor > &globalDescriptorMsgs=std::vector< rtabmap_ros::GlobalDescriptor >(), const std::vector< std::vector< rtabmap_ros::KeyPoint > > &localKeyPoints=std::vector< std::vector< rtabmap_ros::KeyPoint > >(), const std::vector< std::vector< rtabmap_ros::Point3f > > &localPoints3d=std::vector< std::vector< rtabmap_ros::Point3f > >(), const std::vector< cv::Mat > &localDescriptors=std::vector< cv::Mat >())
x
ros::ServiceServer setLogWarnSrv_
Definition: CoreWrapper.h:335
std::string uReplaceChar(const std::string &str, char before, char after)
image_transport::Subscriber defaultSub_
Definition: CoreWrapper.h:365
#define MAX(A, B)
ros::ServiceServer resetSrv_
Definition: CoreWrapper.h:322
void mapGraphToROS(const std::map< int, rtabmap::Transform > &poses, const std::multimap< int, rtabmap::Link > &links, const rtabmap::Transform &mapToOdom, rtabmap_ros::MapGraph &msg)
bool computePath(int targetNode, bool global)
int getSTMSize() const
bool getProbMapCallback(nav_msgs::GetMap::Request &req, nav_msgs::GetMap::Response &res)
virtual void commonLaserScanCallback(const nav_msgs::OdometryConstPtr &odomMsg, const rtabmap_ros::UserDataConstPtr &userDataMsg, const sensor_msgs::LaserScan &scanMsg, const sensor_msgs::PointCloud2 &scan3dMsg, const rtabmap_ros::OdomInfoConstPtr &odomInfoMsg, const rtabmap_ros::GlobalDescriptor &globalDescriptor=rtabmap_ros::GlobalDescriptor())
rtabmap::ParametersMap parameters_
Definition: CoreWrapper.h:260
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
GLM_FUNC_DECL vecType< T, P > sqrt(vecType< T, P > const &x)
int uStrNumCmp(const std::string &a, const std::string &b)
Signature getSignatureCopy(int id, bool images, bool scan, bool userData, bool occupancyGrid, bool withWords, bool withGlobalDescriptors) const
void publishCurrentGoal(const ros::Time &stamp)
std::vector< rtabmap::GlobalDescriptor > globalDescriptorsFromROS(const std::vector< rtabmap_ros::GlobalDescriptor > &msg)
const Signature * getLastWorkingSignature() const
const Statistics & getStatistics() const
static std::string getDir(const std::string &filePath)
ros::ServiceServer addLinkSrv_
Definition: CoreWrapper.h:352
void saveParameters(const std::string &configFile)
void setGlobalPose(const Transform &pose, const cv::Mat &covariance)
ros::Subscriber tagDetectionsSub_
Definition: CoreWrapper.h:375
bool resetRtabmapCallback(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
ros::Publisher localGridEmpty_
Definition: CoreWrapper.h:302
std::pair< std::string, std::string > ParametersPair
ros::ServiceServer getNodeDataSrv_
Definition: CoreWrapper.h:337
static Transform getIdentity()
bool isIncremental() const
ros::NodeHandle & getNodeHandle() const
bool isGridFromDepth() const
ros::ServiceServer getMapSrv_
Definition: CoreWrapper.h:341
int getPathCurrentGoalId() const
bool setModeMappingCallback(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
ros::ServiceServer getGridMapSrv_
Definition: CoreWrapper.h:343
rtabmap::Rtabmap rtabmap_
Definition: CoreWrapper.h:250
void setInitialPose(const Transform &initialPose)
bool globalBundleAdjustmentCallback(rtabmap_ros::GlobalBundleAdjustment::Request &, rtabmap_ros::GlobalBundleAdjustment::Response &)
bool labelLocation(int id, const std::string &label)
XmlRpcServer s
static bool fullMapToMsg(const OctomapT &octomap, Octomap &msg)
void goalFeedbackCb(const move_base_msgs::MoveBaseFeedbackConstPtr &feedback)
data
ros::ServiceServer removeLabelSrv_
Definition: CoreWrapper.h:351
void transformToGeometryMsg(const rtabmap::Transform &transform, geometry_msgs::Transform &msg)
pcl::PCLPointCloud2::Ptr RTABMAP_EXP laserScanToPointCloud2(const LaserScan &laserScan, const Transform &transform=Transform())
static ParametersMap parseArguments(int argc, char *argv[], bool onlyParameters=false)
rtabmap::Landmarks landmarksFromROS(const std::map< int, std::pair< geometry_msgs::PoseWithCovarianceStamped, float > > &tags, const std::string &frameId, const std::string &odomFrameId, const ros::Time &odomStamp, tf::TransformListener &listener, double waitForTransform, double defaultLinVariance, double defaultAngVariance)
cv::Mat RTABMAP_EXP depthFromDisparity(const cv::Mat &disparity, float fx, float baseline, int type=CV_32FC1)
void publishLocalPath(const ros::Time &stamp)
static int erase(const std::string &filePath)
bool removeLabelCallback(rtabmap_ros::RemoveLabel::Request &req, rtabmap_ros::RemoveLabel::Response &res)
void process(const ros::Time &stamp, rtabmap::SensorData &data, const rtabmap::Transform &odom=rtabmap::Transform(), const std::vector< float > &odomVelocity=std::vector< float >(), const std::string &odomFrameId="", const cv::Mat &odomCovariance=cv::Mat::eye(6, 6, CV_64FC1), const rtabmap::OdometryInfo &odomInfo=rtabmap::OdometryInfo(), double timeMsgConversion=0.0)
std::map< int, Landmark > Landmarks
void interOdomCallback(const nav_msgs::OdometryConstPtr &msg)
ros::Publisher localizationPosePub_
Definition: CoreWrapper.h:304
ros::Subscriber fiducialTransfromsSub_
Definition: CoreWrapper.h:376
bool publishMapCallback(rtabmap_ros::PublishMap::Request &, rtabmap_ros::PublishMap::Response &)
void copy(const std::string &to)
rtabmap::Transform lastPublishedMetricGoal_
Definition: CoreWrapper.h:258
bool listLabelsCallback(rtabmap_ros::ListLabels::Request &req, rtabmap_ros::ListLabels::Response &res)
const cv::Mat & data() const
message_filters::Synchronizer< MyExactInterOdomSyncPolicy > * interOdomSync_
Definition: CoreWrapper.h:388
rtabmap::Transform transformFromPoseMsg(const geometry_msgs::Pose &msg, bool ignoreRotationIfNotSet=false)
std::map< std::string, std::string > ParametersMap
cv::Mat getGridProbMap(float &xMin, float &yMin, float &gridCellSize)
ros::Publisher infoPub_
Definition: CoreWrapper.h:294
void init(const ParametersMap &parameters, const std::string &databasePath="", bool loadDatabaseParameters=false)
message_filters::sync_policies::ExactTime< nav_msgs::Odometry, rtabmap_ros::OdomInfo > MyExactInterOdomSyncPolicy
Definition: CoreWrapper.h:387
ros::ServiceServer getProbMapSrv_
Definition: CoreWrapper.h:342
int getLastLocationId() const
void setGPS(const GPS &gps)
void setId(int id)
const float * data() const
bool cancelGoalCallback(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
bool globalBundleAdjustment(int optimizerType=1, bool rematchFeatures=true, int iterations=0, float pixelVariance=0.0f)
bool getPlanCallback(nav_msgs::GetPlan::Request &req, nav_msgs::GetPlan::Response &res)
int getSignatureIdByLabel(const std::string &label, bool lookInDatabase=true) const
ros::NodeHandle & getPrivateNodeHandle() const
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
void addNodesToRepublish(const std::vector< int > &ids)
GLM_FUNC_DECL T pitch(detail::tquat< T, P > const &x)
float getGoalReachedRadius() const
bool setLabelCallback(rtabmap_ros::SetLabel::Request &req, rtabmap_ros::SetLabel::Response &res)
bool isEmpty() const
ros::Publisher landmarksPub_
Definition: CoreWrapper.h:298
void republishNodeDataCallback(const std_msgs::Int32MultiArray::ConstPtr &msg)
ros::Subscriber imuSub_
Definition: CoreWrapper.h:378
bool is2d() const
message_filters::Subscriber< rtabmap_ros::OdomInfo > interOdomInfoSyncSub_
Definition: CoreWrapper.h:386
void getTranslationAndEulerAngles(float &x, float &y, float &z, float &roll, float &pitch, float &yaw) const
static void setLevel(ULogger::Level level)
void imuAsyncCallback(const sensor_msgs::ImuConstPtr &tagDetections)
#define ROS_WARN(...)
geometry_msgs::TransformStamped t
rtabmap::Transform getTransform(const std::string &fromFrameId, const std::string &toFrameId, const ros::Time &stamp, tf::TransformListener &listener, double waitForTransform)
bool resumeRtabmapCallback(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
static bool binaryMapToMsg(const OctomapT &octomap, Octomap &msg)
bool odomUpdate(const nav_msgs::OdometryConstPtr &odomMsg, ros::Time stamp)
std::string uBool2Str(bool boolean)
bool setLogDebug(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
ros::ServiceServer getMapData2Srv_
Definition: CoreWrapper.h:339
ros::ServiceServer cleanupLocalGridsSrv_
Definition: CoreWrapper.h:330
const std::map< int, Transform > & poses() const
ros::Publisher nextMetricGoalPub_
Definition: CoreWrapper.h:310
float gridCellSize() const
const std::map< int, std::string > & getAllLabels() const
void initialPoseCallback(const geometry_msgs::PoseWithCovarianceStampedConstPtr &msg)
ros::Subscriber initialPoseSub_
Definition: CoreWrapper.h:305
void goalCallback(const geometry_msgs::PoseStampedConstPtr &msg)
Transform getMapCorrection() const
bool uIsFinite(const T &value)
bool openConnection(const std::string &url, bool overwritten=false)
#define UASSERT(condition)
ros::Publisher mapDataPub_
Definition: CoreWrapper.h:295
void init(ros::NodeHandle &nh, ros::NodeHandle &pnh, const std::string &name, bool usePublicNamespace)
Definition: MapsManager.cpp:83
void globalPoseAsyncCallback(const geometry_msgs::PoseWithCovarianceStampedConstPtr &globalPoseMsg)
ros::Subscriber globalPoseAsyncSub_
Definition: CoreWrapper.h:371
bool waitForServer(const ros::Duration &timeout=ros::Duration(0, 0)) const
const std::string & getName() const
std::map< int, Transform > getNodesInRadius(const Transform &pose, float radius, int k=0, std::map< int, float > *distsSqr=0)
ros::Subscriber goalSub_
Definition: CoreWrapper.h:308
static std::string currentDir(bool trailingSeparator=false)
int lock() const
bool odomTFUpdate(const ros::Time &stamp)
std::map< std::string, float > odomInfoToStatistics(const rtabmap::OdometryInfo &info)
int uStr2Int(const std::string &str)
ros::ServiceServer globalBundleAdjustmentSrv_
Definition: CoreWrapper.h:329
std::string uNumber2Str(unsigned int number)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
#define true
void publish(const boost::shared_ptr< M > &message) const
T uMax3(const T &a, const T &b, const T &c)
rtabmap::OdometryInfo odomInfoFromROS(const rtabmap_ros::OdomInfo &msg, bool ignoreData=false)
void close(bool databaseSaved=true, const std::string &ouputDatabasePath="")
int getWMSize() const
ros::ServiceServer setModeMappingSrv_
Definition: CoreWrapper.h:332
ros::Subscriber userDataAsyncSub_
Definition: CoreWrapper.h:367
void publishGlobalPath(const ros::Time &stamp)
bool addLinkCallback(rtabmap_ros::AddLink::Request &, rtabmap_ros::AddLink::Response &)
void save2DMap(const cv::Mat &map, float xMin, float yMin, float cellSize) const
int detectMoreLoopClosures(float clusterRadiusMax=0.5f, float clusterAngle=M_PI/6.0f, int iterations=1, bool intraSession=true, bool interSession=true, const ProgressState *state=0, float clusterRadiusMin=0.0f)
std::map< int, rtabmap::Transform > updateMapCaches(const std::map< int, rtabmap::Transform > &poses, const rtabmap::Memory *memory, bool updateGrid, bool updateOctomap, const std::map< int, rtabmap::Signature > &signatures=std::map< int, rtabmap::Signature >())
std::vector< int > getPathNextNodes() const
ros::ServiceServer updateSrv_
Definition: CoreWrapper.h:321
rtabmap::Link linkFromROS(const rtabmap_ros::Link &msg)
bool pauseRtabmapCallback(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
const Transform & getPathTransformToGoal() const
ros::ServiceServer backupDatabase_
Definition: CoreWrapper.h:327
void setupCallbacks(ros::NodeHandle &nh, ros::NodeHandle &pnh, const std::string &name)
ros::ServiceServer setLogInfoSrv_
Definition: CoreWrapper.h:334
void mapDataToROS(const std::map< int, rtabmap::Transform > &poses, const std::multimap< int, rtabmap::Link > &links, const std::map< int, rtabmap::Signature > &signatures, const rtabmap::Transform &mapToOdom, rtabmap_ros::MapData &msg)
std::map< std::string, float > rtabmapROSStats_
Definition: CoreWrapper.h:261
bool setGoalCallback(rtabmap_ros::SetGoal::Request &req, rtabmap_ros::SetGoal::Response &res)
const Memory * getMemory() const
#define ROS_INFO(...)
bool getParam(const std::string &key, std::string &s) const
void commonMultiCameraCallbackImpl(const std::string &odomFrameId, const rtabmap_ros::UserDataConstPtr &userDataMsg, const std::vector< cv_bridge::CvImageConstPtr > &imageMsgs, const std::vector< cv_bridge::CvImageConstPtr > &depthMsgs, const std::vector< sensor_msgs::CameraInfo > &cameraInfoMsgs, const std::vector< sensor_msgs::CameraInfo > &depthCameraInfoMsgs, const sensor_msgs::LaserScan &scan2dMsg, const sensor_msgs::PointCloud2 &scan3dMsg, const rtabmap_ros::OdomInfoConstPtr &odomInfoMsg, const std::vector< rtabmap_ros::GlobalDescriptor > &globalDescriptorMsgs, const std::vector< std::vector< rtabmap_ros::KeyPoint > > &localKeyPoints, const std::vector< std::vector< rtabmap_ros::Point3f > > &localPoints3d, const std::vector< cv::Mat > &localDescriptors)
bool getGridMapCallback(nav_msgs::GetMap::Request &req, nav_msgs::GetMap::Response &res)
bool updateRtabmapCallback(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
const Transform & mapCorrection() const
void loadParameters(const std::string &configFile, rtabmap::ParametersMap &parameters)
cv::Mat K() const
std::string prettyPrint() const
void interOdomInfoCallback(const nav_msgs::OdometryConstPtr &msg1, const rtabmap_ros::OdomInfoConstPtr &msg2)
static void setEventLevel(ULogger::Level eventSentLevel)
ros::Subscriber interOdomSub_
Definition: CoreWrapper.h:383
int unlock() const
bool addLink(const Link &link)
bool process(const SensorData &data, Transform odomPose, const cv::Mat &odomCovariance=cv::Mat::eye(6, 6, CV_64FC1), const std::vector< float > &odomVelocity=std::vector< float >(), const std::map< std::string, float > &externalStats=std::map< std::string, float >())
ros::ServiceServer setLabelSrv_
Definition: CoreWrapper.h:349
int getPathStatus() const
const Transform & localTransform() const
tf2_ros::TransformBroadcaster tfBroadcaster_
Definition: CoreWrapper.h:318
bool triggerNewMapCallback(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
ros::Publisher localGridObstacle_
Definition: CoreWrapper.h:301
const std::vector< std::pair< int, Transform > > & getPath() const
const std::multimap< int, Link > & odomCacheConstraints() const
const RtabmapColorOcTree * octree() const
QMap< QString, QVariant > ParametersMap
const std::map< int, Signature > & getSignaturesData() const
const std::multimap< int, Link > & getLocalConstraints() const
message_filters::Subscriber< nav_msgs::Odometry > interOdomSyncSub_
Definition: CoreWrapper.h:385
MoveBaseClient * mbClient_
Definition: CoreWrapper.h:359
boost::mutex mapToOdomMutex_
Definition: CoreWrapper.h:290
float getLocalRadius() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool uStr2Bool(const char *str)
ros::ServiceServer cancelGoalSrv_
Definition: CoreWrapper.h:348
const std::string MONO16
const std::map< int, Transform > & getLocalOptimizedPoses() const
tf::TransformListener tfListener_
Definition: CoreWrapper.h:319
bool setModeLocalizationCallback(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
std::list< V > uValues(const std::multimap< K, V > &mm)
void goalNodeCallback(const rtabmap_ros::GoalConstPtr &msg)
void sendTransform(const geometry_msgs::TransformStamped &transform)
bool getProjMapCallback(nav_msgs::GetMap::Request &req, nav_msgs::GetMap::Response &res)
bool uContains(const std::list< V > &list, const V &value)
static DBDriver * create(const ParametersMap &parameters=ParametersMap())
void parseParameters(const ParametersMap &parameters)
cv::Mat userDataFromROS(const rtabmap_ros::UserData &dataMsg)
bool hasParam(const std::string &key) const
rtabmap::Transform mapToOdom_
Definition: CoreWrapper.h:289
std::vector< float > lastPoseVelocity_
Definition: CoreWrapper.h:254
float getDistance(const Transform &t) const
bool sleep()
bool isNull() const
void transformToPoseMsg(const rtabmap::Transform &transform, geometry_msgs::Pose &msg)
const V_string & getMyArgv() const
string frameId
Definition: patrol.py:11
#define BAD_COVARIANCE
Definition: CoreWrapper.cpp:72
#define false
const std::multimap< int, Link > & constraints() const
void publishMaps(const std::map< int, rtabmap::Transform > &poses, const ros::Time &stamp, const std::string &mapFrameId)
bool convertScanMsg(const sensor_msgs::LaserScan &scan2dMsg, const std::string &frameId, const std::string &odomFrameId, const ros::Time &odomStamp, rtabmap::LaserScan &scan, tf::TransformListener &listener, double waitForTransform, bool outputInFrameId=false)
const std::map< int, double > & getWorkingMem() const
void sendGoal(const Goal &goal, SimpleDoneCallback done_cb=SimpleDoneCallback(), SimpleActiveCallback active_cb=SimpleActiveCallback(), SimpleFeedbackCallback feedback_cb=SimpleFeedbackCallback())
#define NODELET_INFO(...)
rtabmap::GlobalDescriptor globalDescriptorFromROS(const rtabmap_ros::GlobalDescriptor &msg)
void gpsFixAsyncCallback(const sensor_msgs::NavSatFixConstPtr &gpsFixMsg)
actionlib::SimpleActionClient< move_base_msgs::MoveBaseAction > MoveBaseClient
Definition: CoreWrapper.h:95
std::map< int, rtabmap::Transform > filterNodesToAssemble(const std::map< int, rtabmap::Transform > &nodes, const rtabmap::Transform &currentPose)
const LaserScan & laserScanRaw() const
bool getMapCallback(nav_msgs::GetMap::Request &req, nav_msgs::GetMap::Response &res)
void setGlobalDescriptors(const std::vector< GlobalDescriptor > &descriptors)
#define UDEBUG(...)
int cleanupLocalGrids(const std::map< int, Transform > &mapPoses, const cv::Mat &map, float xMin, float yMin, float cellSize, int cropRadius=1, bool filterScans=false)
SensorData & sensorData()
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)
ros::Publisher odomCachePub_
Definition: CoreWrapper.h:297
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const
bool exists()
std::list< K > uKeys(const std::multimap< K, V > &mm)
std::string getDatabaseVersion() const
#define UERROR(...)
const cv::Mat & gridEmptyCellsRaw() const
argv
boost::thread * transformThread_
Definition: CoreWrapper.h:361
rtabmap::Transform currentMetricGoal_
Definition: CoreWrapper.h:257
bool setLogError(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
const cv::Mat & gridGroundCellsRaw() const
#define UWARN(...)
ros::Subscriber goalNodeSub_
Definition: CoreWrapper.h:309
void backwardCompatibilityParameters(ros::NodeHandle &pnh, rtabmap::ParametersMap &parameters) const
const Transform & getLastLocalizationPose() const
double stamp() const
const Signature & getLastSignatureData() const
ros::ServiceServer listLabelsSrv_
Definition: CoreWrapper.h:350
void getGraph(std::map< int, Transform > &poses, std::multimap< int, Link > &constraints, bool optimized, bool global, std::map< int, Signature > *signatures=0, bool withImages=false, bool withScan=false, bool withUserData=false, bool withGrid=false, bool withWords=true, bool withGlobalDescriptors=true) const
ParametersMap getLastParameters() const
static Time now()
static std::string getDefaultDatabaseName()
double ticks()
double timestampFromROS(const ros::Time &stamp)
GLM_FUNC_DECL T yaw(detail::tquat< T, P > const &x)
float uStr2Float(const std::string &str)
ros::Publisher labelsPub_
Definition: CoreWrapper.h:299
static bool exists(const std::string &dirPath)
bool setLogInfo(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
model
const std::string header
void setLandmarks(const Landmarks &landmarks)
ros::Publisher mapPathPub_
Definition: CoreWrapper.h:300
PLUGINLIB_EXPORT_CLASS(rtabmap_ros::CoreWrapper, nodelet::Nodelet)
void setIMU(const IMU &imu)
cv::Mat load2DMap(float &xMin, float &yMin, float &cellSize) const
void setGroundTruth(const Transform &pose)
cv::Mat RTABMAP_EXP cvtDepthFromFloat(const cv::Mat &depth32F)
const cv::Mat & gridObstacleCellsRaw() const
cv::Mat getGridMap(float &xMin, float &yMin, float &gridCellSize)
Transform getPose(int locationId) const
Transform to3DoF() const
ros::ServiceServer getPlanNodesSrv_
Definition: CoreWrapper.h:346
const cv::Mat & localizationCovariance() const
ros::Publisher localPathPub_
Definition: CoreWrapper.h:313
bool getPlanNodesCallback(rtabmap_ros::GetPlan::Request &req, rtabmap_ros::GetPlan::Response &res)
void moveFromPCL(pcl::PCLImage &pcl_image, sensor_msgs::Image &image)
int imageWidth() const
std::map< int, std::pair< geometry_msgs::PoseWithCovarianceStamped, float > > tags_
Definition: CoreWrapper.h:377
void goalCommonCallback(int id, const std::string &label, const std::string &frameId, const rtabmap::Transform &pose, const ros::Time &stamp, double *planningTime=0)
std::string groundTruthFrameId_
Definition: CoreWrapper.h:266
bool loadDatabaseCallback(rtabmap_ros::LoadDatabase::Request &, rtabmap_ros::LoadDatabase::Response &)
uint32_t getNumSubscribers() const
std::map< double, rtabmap::Transform > imus_
Definition: CoreWrapper.h:379
ros::ServiceServer loadDatabaseSrv_
Definition: CoreWrapper.h:325
r
bool backupDatabaseCallback(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
bool hasSubscribers() const
ros::ServiceServer getPlanSrv_
Definition: CoreWrapper.h:345
#define ROS_ERROR(...)
std::list< std::pair< nav_msgs::Odometry, rtabmap_ros::OdomInfo > > interOdoms_
Definition: CoreWrapper.h:384
void publishStats(const ros::Time &stamp)
ros::ServiceServer setGoalSrv_
Definition: CoreWrapper.h:347
void publishLoop(double tfDelay, double tfTolerance)
ros::Publisher globalPathPub_
Definition: CoreWrapper.h:312
#define M_PI
bool getMapData2Callback(rtabmap_ros::GetMap2::Request &req, rtabmap_ros::GetMap2::Response &res)
void nodeDataToROS(const rtabmap::Signature &signature, rtabmap_ros::NodeData &msg)
Eigen::Quaterniond getQuaterniond() const
res
std::string groundTruthBaseFrameId_
Definition: CoreWrapper.h:267
ros::ServiceServer setModeLocalizationSrv_
Definition: CoreWrapper.h:331
Transform localTransform() const
bool convertScan3dMsg(const sensor_msgs::PointCloud2 &scan3dMsg, const std::string &frameId, const std::string &odomFrameId, const ros::Time &odomStamp, rtabmap::LaserScan &scan, tf::TransformListener &listener, double waitForTransform, int maxPoints=0, float maxRange=0.0f, bool is2D=false)
ros::Publisher goalReachedPub_
Definition: CoreWrapper.h:311
LaserScan RTABMAP_EXP laserScan2dFromPointCloud(const pcl::PointCloud< pcl::PointXYZ > &cloud, const Transform &transform=Transform(), bool filterNaNs=true)
V uValue(const std::map< K, V > &m, const K &key, const V &defaultValue=V())
void setFeatures(const std::vector< cv::KeyPoint > &keypoints, const std::vector< cv::Point3f > &keypoints3D, const cv::Mat &descriptors)
const std::map< int, Transform > & odomCachePoses() const
void infoToROS(const rtabmap::Statistics &stats, rtabmap_ros::Info &info)
bool getNodesInRadiusCallback(rtabmap_ros::GetNodesInRadius::Request &, rtabmap_ros::GetNodesInRadius::Response &)
ros::ServiceServer triggerNewMapSrv_
Definition: CoreWrapper.h:326
bool isIdentity() const
Transform inverse() const
q
GLM_FUNC_DECL detail::tmat4x4< T, P > orientation(detail::tvec3< T, P > const &Normal, detail::tvec3< T, P > const &Up)
ros::Publisher mapGraphPub_
Definition: CoreWrapper.h:296
void uInsert(std::map< K, V > &map, const std::pair< K, V > &pair)
ros::Publisher localGridGround_
Definition: CoreWrapper.h:303
ros::ServiceServer resumeSrv_
Definition: CoreWrapper.h:324
#define ROS_DEBUG(...)


rtabmap_ros
Author(s): Mathieu Labbe
autogenerated on Tue Jan 24 2023 04:04:40