mirror of
https://github.com/introlab/rtabmap_ros.git
synced 2026-10-05 17:27:46 +08:00
5097 lines
178 KiB
C++
5097 lines
178 KiB
C++
/*
|
|
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the Universite de Sherbrooke nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "rtabmap_slam/CoreWrapper.h"
|
|
|
|
#include <stdio.h>
|
|
#include <thread>
|
|
#include <rclcpp/rclcpp.hpp>
|
|
|
|
#include <nav_msgs/msg/path.hpp>
|
|
#include <std_msgs/msg/int32_multi_array.hpp>
|
|
#include <std_msgs/msg/bool.hpp>
|
|
#include <geometry_msgs/msg/pose_array.hpp>
|
|
#include <sensor_msgs/image_encodings.hpp>
|
|
#ifdef PRE_ROS_IRON
|
|
#include <cv_bridge/cv_bridge.h>
|
|
#else
|
|
#include <cv_bridge/cv_bridge.hpp>
|
|
#endif
|
|
#include <pcl_conversions/pcl_conversions.h>
|
|
#if PCL_VERSION_COMPARE(>, 1, 12, 0)
|
|
#include <pcl/common/io.h>
|
|
#else
|
|
#include <pcl/io/io.h>
|
|
#endif
|
|
|
|
#include <visualization_msgs/msg/marker_array.hpp>
|
|
|
|
#include <rtabmap/utilite/UTimer.h>
|
|
#include <rtabmap/utilite/UDirectory.h>
|
|
#include <rtabmap/utilite/UFile.h>
|
|
#include <rtabmap/utilite/UConversion.h>
|
|
#include <rtabmap/utilite/UStl.h>
|
|
#include <rtabmap/utilite/UMath.h>
|
|
|
|
#include <rtabmap/core/util2d.h>
|
|
#include <rtabmap/core/util3d.h>
|
|
#include <rtabmap/core/util3d_transforms.h>
|
|
#include <rtabmap/core/util3d_surface.h>
|
|
#include <rtabmap/core/Memory.h>
|
|
#include <rtabmap/core/OdometryEvent.h>
|
|
#include <rtabmap/core/Version.h>
|
|
#include <rtabmap/core/OccupancyGrid.h>
|
|
#include <rtabmap/core/DBDriver.h>
|
|
#include <rtabmap/core/Registration.h>
|
|
#include <rtabmap/core/Graph.h>
|
|
#include <rtabmap/core/LocalGridMaker.h>
|
|
#include <rtabmap/core/Optimizer.h>
|
|
|
|
#ifdef WITH_OCTOMAP_MSGS
|
|
#ifdef RTABMAP_OCTOMAP
|
|
#include <octomap_msgs/conversions.h>
|
|
#include <rtabmap/core/OctoMap.h>
|
|
#endif
|
|
#endif
|
|
|
|
#define BAD_COVARIANCE 9999
|
|
|
|
//msgs
|
|
#include "rtabmap_msgs/msg/info.hpp"
|
|
#include "rtabmap_msgs/msg/map_data.hpp"
|
|
#include "rtabmap_msgs/msg/map_graph.hpp"
|
|
#include "rtabmap_msgs/srv/get_map.hpp"
|
|
#include "rtabmap_msgs/srv/publish_map.hpp"
|
|
#include "rtabmap_msgs/msg/path.hpp"
|
|
|
|
#include "rtabmap_conversions/MsgConversion.h"
|
|
|
|
#ifdef PRE_ROS_JAZZY
|
|
namespace rclcpp {
|
|
rmw_qos_profile_t ServicesQoS() {return rmw_qos_profile_services_default;}
|
|
rmw_qos_profile_t ParametersQoS() {return rmw_qos_profile_parameters;}
|
|
}
|
|
#endif
|
|
|
|
using namespace rtabmap;
|
|
|
|
namespace rtabmap_slam {
|
|
|
|
CoreWrapper::CoreWrapper(const rclcpp::NodeOptions & options) :
|
|
Node("rtabmap", options),
|
|
rtabmap_sync::CommonDataSubscriber(*this, false),
|
|
paused_(false),
|
|
lastPose_(Transform::getIdentity()),
|
|
lastPoseIntermediate_(false),
|
|
latestNodeWasReached_(false),
|
|
pubLocPoseOnlyWhenLocalizing_(false),
|
|
graphLatched_(false),
|
|
frameId_("base_link"),
|
|
odomFrameId_(""),
|
|
mapFrameId_("map"),
|
|
groundTruthFrameId_(""), // e.g., "world"
|
|
groundTruthBaseFrameId_(""), // e.g., "base_link_gt"
|
|
configPath_(""),
|
|
odomDefaultAngVariance_(0.001),
|
|
odomDefaultLinVariance_(0.001),
|
|
landmarkDefaultAngVariance_(0.001),
|
|
landmarkDefaultLinVariance_(0.001),
|
|
waitForTransform_(0.2),// 200 ms
|
|
useActionForGoal_(false),
|
|
useSavedMap_(true),
|
|
genScan_(false),
|
|
genScanMaxDepth_(4.0),
|
|
genScanMinDepth_(0.0),
|
|
genDepth_(false),
|
|
genDepthDecimation_(1),
|
|
genDepthFillHolesSize_(0),
|
|
genDepthFillIterations_(1),
|
|
genDepthFillHolesError_(0.1),
|
|
scanCloudMaxPoints_(0),
|
|
scanCloudIs2d_(false),
|
|
mapToOdom_(rtabmap::Transform::getIdentity()),
|
|
transformThread_(0),
|
|
tfThreadRunning_(false),
|
|
interOdomSync_(0),
|
|
stereoToDepth_(false),
|
|
odomSensorSync_(false),
|
|
rate_(Parameters::defaultRtabmapDetectionRate()),
|
|
createIntermediateNodes_(Parameters::defaultRtabmapCreateIntermediateNodes()),
|
|
mappingMaxNodes_(Parameters::defaultGridGlobalMaxNodes()),
|
|
mappingAltitudeDelta_(Parameters::defaultGridGlobalAltitudeDelta()),
|
|
alreadyRectifiedImages_(Parameters::defaultRtabmapImagesAlreadyRectified()),
|
|
twoDMapping_(Parameters::defaultRegForce3DoF()),
|
|
previousStamp_(0),
|
|
ulogToRosout_(this),
|
|
triggerNewMapBeforeNextUpdate_(false)
|
|
{
|
|
char * rosHomePath = getenv("ROS_HOME");
|
|
std::string workingDir = rosHomePath?rosHomePath:UDirectory::homeDir()+"/.ros";
|
|
databasePath_ = workingDir+"/"+rtabmap::Parameters::getDefaultDatabaseName();
|
|
|
|
mapsManager_.init(*this, this->get_name(), true);
|
|
|
|
syncData_.valid = false;
|
|
|
|
tfBuffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
|
|
tfListener_ = std::make_shared<tf2_ros::TransformListener>(*tfBuffer_);
|
|
tfBroadcaster_ = std::make_shared<tf2_ros::TransformBroadcaster>(this);
|
|
|
|
bool publishTf = true;
|
|
std::string initialPoseStr;
|
|
tfDelay = 0.05; // 20 Hz
|
|
tfTolerance = 0.1; // 100 ms
|
|
std::string odomFrameIdInit;
|
|
|
|
configPath_ = this->declare_parameter("config_path", configPath_);
|
|
databasePath_ = this->declare_parameter("database_path", databasePath_);
|
|
|
|
frameId_ = this->declare_parameter("frame_id", frameId_);
|
|
this->get_parameter_or("odom_frame_id", odomFrameId_, odomFrameId_); // set to use odom from TF, ros2: declared later in CommonDataSubscriber
|
|
odomFrameIdInit = this->declare_parameter("odom_frame_id_init", odomFrameIdInit); // set to publish map->odom TF before receiving odom topic
|
|
mapFrameId_ = this->declare_parameter("map_frame_id", mapFrameId_);
|
|
groundTruthFrameId_ = this->declare_parameter("ground_truth_frame_id", groundTruthFrameId_);
|
|
groundTruthBaseFrameId_ = this->declare_parameter("ground_truth_base_frame_id", frameId_);
|
|
|
|
if(!odomFrameIdInit.empty())
|
|
{
|
|
if(odomFrameId_.empty())
|
|
{
|
|
RCLCPP_INFO(get_logger(), "rtabmap: odom_frame_id_init = %s", odomFrameIdInit.c_str());
|
|
odomFrameId_ = odomFrameIdInit;
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(get_logger(), "odom_frame_id_init (%s) is ignored if odom_frame_id (%s) is set.", odomFrameIdInit.c_str(), odomFrameId_.c_str());
|
|
}
|
|
}
|
|
|
|
int eventLevel = ULogger::kFatal;
|
|
eventLevel = this->declare_parameter("log_to_rosout_level", eventLevel);
|
|
UASSERT(eventLevel >= ULogger::kDebug && eventLevel <= ULogger::kFatal);
|
|
ULogger::setEventLevel((ULogger::Level)eventLevel);
|
|
|
|
publishTf = this->declare_parameter("publish_tf", publishTf);
|
|
tfDelay = this->declare_parameter("tf_delay", tfDelay);
|
|
tfTolerance = this->declare_parameter("tf_tolerance", tfTolerance);
|
|
|
|
odomDefaultAngVariance_ = this->declare_parameter("odom_tf_angular_variance", odomDefaultAngVariance_);
|
|
odomDefaultLinVariance_ = this->declare_parameter("odom_tf_linear_variance", odomDefaultLinVariance_);
|
|
|
|
landmarkDefaultAngVariance_ = this->declare_parameter("landmark_angular_variance", landmarkDefaultAngVariance_);
|
|
landmarkDefaultLinVariance_ = this->declare_parameter("landmark_linear_variance", landmarkDefaultLinVariance_);
|
|
|
|
pubLocPoseOnlyWhenLocalizing_ = this->declare_parameter("pub_loc_pose_only_when_localizing", pubLocPoseOnlyWhenLocalizing_);
|
|
waitForTransform_ = this->declare_parameter("wait_for_transform", waitForTransform_);
|
|
initialPoseStr = this->declare_parameter("initial_pose", initialPoseStr);
|
|
useActionForGoal_ = this->declare_parameter("use_action_for_goal", useActionForGoal_);
|
|
#ifndef WITH_NAV2_MSGS
|
|
if(useActionForGoal_)
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "rtabmap: Cannot enable use_action_for_goal because rtabmap_slam is not built with nav2_msgs support.");
|
|
useActionForGoal_ = false;
|
|
}
|
|
#endif
|
|
useSavedMap_ = this->declare_parameter("use_saved_map", useSavedMap_);
|
|
genScan_ = this->declare_parameter("gen_scan", genScan_);
|
|
genScanMaxDepth_ = this->declare_parameter("gen_scan_max_depth", genScanMaxDepth_);
|
|
genScanMinDepth_ = this->declare_parameter("gen_scan_min_depth", genScanMinDepth_);
|
|
genDepth_ = this->declare_parameter("gen_depth", genDepth_);
|
|
genDepthDecimation_ = this->declare_parameter("gen_depth_decimation", genDepthDecimation_);
|
|
genDepthFillHolesSize_ = this->declare_parameter("gen_depth_fill_holes_size", genDepthFillHolesSize_);
|
|
genDepthFillIterations_ = this->declare_parameter("gen_depth_fill_iterations", genDepthFillIterations_);
|
|
genDepthFillHolesError_ = this->declare_parameter("gen_depth_fill_holes_error", genDepthFillHolesError_);
|
|
scanCloudMaxPoints_ = this->declare_parameter("scan_cloud_max_points", scanCloudMaxPoints_);
|
|
scanCloudIs2d_ = this->declare_parameter("scan_cloud_is_2d", scanCloudIs2d_);
|
|
|
|
stereoToDepth_ = this->declare_parameter("stereo_to_depth", stereoToDepth_);
|
|
odomSensorSync_ = this->declare_parameter("odom_sensor_sync", odomSensorSync_);
|
|
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: frame_id = %s", frameId_.c_str());
|
|
if(!odomFrameId_.empty())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: odom_frame_id = %s", odomFrameId_.c_str());
|
|
}
|
|
if(!groundTruthFrameId_.empty())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: ground_truth_frame_id = %s -> ground_truth_base_frame_id = %s",
|
|
groundTruthFrameId_.c_str(),
|
|
groundTruthBaseFrameId_.c_str());
|
|
}
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: map_frame_id = %s", mapFrameId_.c_str());
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: log_to_rosout_level = %d", eventLevel);
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: initial_pose = %s", initialPoseStr.c_str());
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: use_action_for_goal = %s", useActionForGoal_?"true":"false");
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: tf_delay = %f", tfDelay);
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: tf_tolerance = %f", tfTolerance);
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: odom_sensor_sync = %s", odomSensorSync_?"true":"false");
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: pub_loc_pose_only_when_localizing = %s", pubLocPoseOnlyWhenLocalizing_?"true":"false");
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: wait_for_transform = %f", waitForTransform_);
|
|
if(this->isSubscribedToStereo())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: stereo_to_depth = %s", stereoToDepth_?"true":"false");
|
|
}
|
|
|
|
RCLCPP_INFO(get_logger(), "rtabmap: gen_scan = %s", genScan_?"true":"false");
|
|
if(genScan_)
|
|
{
|
|
RCLCPP_INFO(get_logger(), "rtabmap: gen_scan_max_depth = %f", genScanMaxDepth_);
|
|
RCLCPP_INFO(get_logger(), "rtabmap: gen_scan_min_depth = %f", genScanMinDepth_);
|
|
}
|
|
|
|
RCLCPP_INFO(get_logger(), "rtabmap: gen_depth = %s", genDepth_?"true":"false");
|
|
if(genDepth_)
|
|
{
|
|
RCLCPP_INFO(get_logger(), "rtabmap: gen_depth_decimation = %d", genDepthDecimation_);
|
|
RCLCPP_INFO(get_logger(), "rtabmap: gen_depth_fill_holes_size = %d", genDepthFillHolesSize_);
|
|
RCLCPP_INFO(get_logger(), "rtabmap: gen_depth_fill_iterations = %d", genDepthFillIterations_);
|
|
RCLCPP_INFO(get_logger(), "rtabmap: gen_depth_fill_holes_error = %f", genDepthFillHolesError_);
|
|
}
|
|
if(this->isSubscribedToScan3d())
|
|
{
|
|
RCLCPP_INFO(get_logger(), "rtabmap: scan_cloud_max_points = %d", scanCloudMaxPoints_);
|
|
RCLCPP_INFO(get_logger(), "rtabmap: scan_cloud_is_2d = %s", scanCloudIs2d_?"true":"false");
|
|
}
|
|
|
|
// Create the processing timer
|
|
processingCallbackGroup_ = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
syncTimer_ = this->create_wall_timer(0s, std::bind(&CoreWrapper::processAsync, this), processingCallbackGroup_);
|
|
syncTimer_->cancel();
|
|
|
|
rclcpp::SubscriptionOptions subOptions;
|
|
subOptions.callback_group = processingCallbackGroup_;
|
|
|
|
infoPub_ = this->create_publisher<rtabmap_msgs::msg::Info>("info", 1);
|
|
mapDataPub_ = this->create_publisher<rtabmap_msgs::msg::MapData>("mapData", 1);
|
|
mapGraphPub_ = this->create_publisher<rtabmap_msgs::msg::MapGraph>("mapGraph", rclcpp::QoS(1).reliable().durability(mapsManager_.isLatching()?RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL:RMW_QOS_POLICY_DURABILITY_VOLATILE));
|
|
odomCachePub_ = this->create_publisher<rtabmap_msgs::msg::MapGraph>("mapOdomCache", 1);
|
|
landmarksPub_ = this->create_publisher<geometry_msgs::msg::PoseArray>("landmarks", 1);
|
|
labelsPub_ = this->create_publisher<visualization_msgs::msg::MarkerArray>("labels", 1);
|
|
mapPathPub_ = this->create_publisher<nav_msgs::msg::Path>("mapPath", 1);
|
|
localGridObstacle_ = this->create_publisher<sensor_msgs::msg::PointCloud2>("local_grid_obstacle", 1);
|
|
localGridEmpty_ = this->create_publisher<sensor_msgs::msg::PointCloud2>("local_grid_empty", 1);
|
|
localGridGround_ = this->create_publisher<sensor_msgs::msg::PointCloud2>("local_grid_ground", 1);
|
|
localizationPosePub_ = this->create_publisher<geometry_msgs::msg::PoseWithCovarianceStamped>("localization_pose", 1);
|
|
initialPoseSub_ = this->create_subscription<geometry_msgs::msg::PoseWithCovarianceStamped>("initialpose", 5, std::bind(&CoreWrapper::initialPoseCallback, this, std::placeholders::_1), subOptions);
|
|
|
|
// planning topics
|
|
goalSub_ = this->create_subscription<geometry_msgs::msg::PoseStamped>("goal", 5, std::bind(&CoreWrapper::goalCallback, this, std::placeholders::_1), subOptions);
|
|
goalNodeSub_ = this->create_subscription<rtabmap_msgs::msg::Goal>("goal_node", 5, std::bind(&CoreWrapper::goalNodeCallback, this, std::placeholders::_1), subOptions);
|
|
nextMetricGoalPub_ = this->create_publisher<geometry_msgs::msg::PoseStamped>("goal_out", 1);
|
|
goalReachedPub_ = this->create_publisher<std_msgs::msg::Bool>("goal_reached", 1);
|
|
globalPathPub_ = this->create_publisher<nav_msgs::msg::Path>("global_path", 1);
|
|
localPathPub_ = this->create_publisher<nav_msgs::msg::Path>("local_path", 1);
|
|
globalPathNodesPub_ = this->create_publisher<rtabmap_msgs::msg::Path>("global_path_nodes", 1);
|
|
localPathNodesPub_ = this->create_publisher<rtabmap_msgs::msg::Path>("local_path_nodes", 1);
|
|
|
|
configPath_ = uReplaceChar(configPath_, '~', UDirectory::homeDir());
|
|
databasePath_ = uReplaceChar(databasePath_, '~', UDirectory::homeDir());
|
|
#ifndef _WIN32
|
|
if(configPath_.size() && configPath_.at(0) != '/')
|
|
{
|
|
configPath_ = UDirectory::currentDir(true) + configPath_;
|
|
}
|
|
if(databasePath_.size() && databasePath_.at(0) != '/')
|
|
{
|
|
databasePath_ = UDirectory::currentDir(true) + databasePath_;
|
|
}
|
|
#endif
|
|
|
|
ParametersMap allParameters = Parameters::getDefaultParameters();
|
|
// remove Odom parameters
|
|
for(ParametersMap::iterator iter=allParameters.begin(); iter!=allParameters.end();)
|
|
{
|
|
if(iter->first.find("Odom") == 0)
|
|
{
|
|
allParameters.erase(iter++);
|
|
}
|
|
else
|
|
{
|
|
++iter;
|
|
}
|
|
}
|
|
uInsert(allParameters, ParametersPair(Parameters::kRGBDCreateOccupancyGrid(), "true")); // default true in ROS
|
|
uInsert(allParameters, ParametersPair(Parameters::kRtabmapWorkingDirectory(), workingDir)); // change default to ~/.ros
|
|
|
|
// load parameters
|
|
loadParameters(configPath_, parameters_);
|
|
for(ParametersMap::iterator iter=parameters_.begin(); iter!=parameters_.end();)
|
|
{
|
|
if(iter->first.find("Odom") == 0)
|
|
{
|
|
parameters_.erase(iter++);
|
|
}
|
|
else
|
|
{
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
// declare parameters
|
|
paused_ = this->declare_parameter("is_rtabmap_paused", paused_);
|
|
if(paused_)
|
|
{
|
|
RCLCPP_WARN(get_logger(), "Node paused... don't forget to call service \"resume\" to start rtabmap.");
|
|
}
|
|
|
|
const std::map<std::string, rclcpp::ParameterValue> & overrides = this->get_node_parameters_interface()->get_parameter_overrides();
|
|
for(ParametersMap::iterator iter=allParameters.begin(); iter!=allParameters.end(); ++iter)
|
|
{
|
|
std::string vStr = this->declare_parameter(iter->first, iter->second);
|
|
if(overrides.find(iter->first) != overrides.end())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Setting RTAB-Map parameter \"%s\"=\"%s\" (rosparam)", iter->first.c_str(), vStr.c_str());
|
|
|
|
if(iter->first.compare(Parameters::kRtabmapWorkingDirectory()) == 0)
|
|
{
|
|
vStr = uReplaceChar(vStr, '~', UDirectory::homeDir());
|
|
}
|
|
else if(iter->first.compare(Parameters::kKpDictionaryPath()) == 0)
|
|
{
|
|
vStr = uReplaceChar(vStr, '~', UDirectory::homeDir());
|
|
}
|
|
uInsert(parameters_, ParametersPair(iter->first, vStr));
|
|
}
|
|
}
|
|
|
|
//parse input arguments
|
|
std::vector<std::string> tmpList = get_node_options().arguments();
|
|
std::vector<std::string> argList;
|
|
for(unsigned int i=0; i<tmpList.size(); ++i)
|
|
{
|
|
// Issue with ros2 launch files in which we cannot pass a
|
|
// list of strings as argument (they will appear in same string)
|
|
std::list<std::string> v = uSplit(tmpList[i]);
|
|
for(std::list<std::string>::iterator iter=v.begin(); iter!=v.end(); ++iter)
|
|
{
|
|
argList.push_back(*iter);
|
|
}
|
|
}
|
|
|
|
char ** argv = new char*[argList.size()];
|
|
bool deleteDbOnStart = false;
|
|
deleteDbOnStart = this->declare_parameter("delete_db_on_start", deleteDbOnStart);
|
|
for(unsigned int i=0; i<argList.size(); ++i)
|
|
{
|
|
argv[i] = &argList[i].at(0);
|
|
if(strcmp(argv[i], "--delete_db_on_start") == 0 || strcmp(argv[i], "-d") == 0)
|
|
{
|
|
deleteDbOnStart = true;
|
|
}
|
|
}
|
|
rtabmap::ParametersMap parameters = rtabmap::Parameters::parseArguments(argList.size(), argv);
|
|
delete [] argv;
|
|
for(ParametersMap::const_iterator iter=parameters.begin(); iter!=parameters.end(); ++iter)
|
|
{
|
|
uInsert(parameters_, ParametersPair(iter->first, iter->second));
|
|
RCLCPP_INFO(this->get_logger(), "Update RTAB-Map parameter \"%s\"=\"%s\" from arguments", iter->first.c_str(), iter->second.c_str());
|
|
}
|
|
|
|
// Backward compatibility
|
|
for(std::map<std::string, std::pair<bool, std::string> >::const_iterator iter=Parameters::getRemovedParameters().begin();
|
|
iter!=Parameters::getRemovedParameters().end();
|
|
++iter)
|
|
{
|
|
std::string paramValue;
|
|
rclcpp::Parameter parameter;
|
|
if(get_parameter(iter->first, parameter))
|
|
{
|
|
paramValue = parameter.as_string();
|
|
}
|
|
if(!paramValue.empty())
|
|
{
|
|
if(!iter->second.second.empty() && parameters_.find(iter->second.second)!=parameters_.end())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Rtabmap: Parameter name changed: \"%s\" -> \"%s\". The new parameter is already used with value \"%s\", ignoring the old one with value \"%s\".",
|
|
iter->first.c_str(), iter->second.second.c_str(), parameters_.find(iter->second.second)->second.c_str(), paramValue.c_str());
|
|
}
|
|
else if(iter->second.first)
|
|
{
|
|
// can be migrated
|
|
uInsert(parameters_, ParametersPair(iter->second.second, paramValue));
|
|
RCLCPP_WARN(this->get_logger(), "Rtabmap: Parameter name changed: \"%s\" -> \"%s\". Please update your launch file accordingly. Value \"%s\" is still set to the new parameter name.",
|
|
iter->first.c_str(), iter->second.second.c_str(), paramValue.c_str());
|
|
}
|
|
else
|
|
{
|
|
if(iter->second.second.empty())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Rtabmap: Parameter \"%s\" doesn't exist anymore!",
|
|
iter->first.c_str());
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Rtabmap: Parameter \"%s\" doesn't exist anymore! You may look at this similar parameter: \"%s\"",
|
|
iter->first.c_str(), iter->second.second.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Backward compatibility (MapsManager)
|
|
mapsManager_.backwardCompatibilityParameters(*this, parameters_);
|
|
|
|
int gridSensor = Parameters::defaultGridSensor();
|
|
if((this->isSubscribedToScan2d() || this->isSubscribedToScan3d() || genScan_) && parameters_.find(Parameters::kGridSensor()) == parameters_.end())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Setting \"%s\" parameter to 0 (default 1) as \"subscribe_scan\" or \"subscribe_scan_cloud\" or \"gen_scan\" is "
|
|
"true. The occupancy grid map will be constructed from "
|
|
"laser scans. To get occupancy grid map from cloud projection, set \"%s\" "
|
|
"to true. To suppress this warning, "
|
|
"add <param name=\"%s\" type=\"string\" value=\"0\"/>",
|
|
Parameters::kGridSensor().c_str(),
|
|
Parameters::kGridSensor().c_str(),
|
|
Parameters::kGridSensor().c_str());
|
|
parameters_.insert(ParametersPair(Parameters::kGridSensor(), "0"));
|
|
}
|
|
Parameters::parse(parameters_, Parameters::kGridSensor(), gridSensor);
|
|
if((this->isSubscribedToScan2d() || this->isSubscribedToScan3d() || genScan_) && parameters_.find(Parameters::kGridRangeMax()) == parameters_.end() && gridSensor==0)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Setting \"%s\" parameter to 0 (default %f) as \"subscribe_scan\" or \"subscribe_scan_cloud\" or \"gen_scan\" is true and %s is 0.",
|
|
Parameters::kGridRangeMax().c_str(),
|
|
Parameters::defaultGridRangeMax(),
|
|
Parameters::kGridSensor().c_str());
|
|
parameters_.insert(ParametersPair(Parameters::kGridRangeMax(), "0"));
|
|
}
|
|
if(this->isSubscribedToScan3d() && !scanCloudIs2d_ && parameters_.find(Parameters::kIcpPointToPlaneRadius()) == parameters_.end())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Setting \"%s\" parameter to 0 (default %f) as \"subscribe_scan_cloud\" is true.",
|
|
Parameters::kIcpPointToPlaneRadius().c_str(),
|
|
Parameters::defaultIcpPointToPlaneRadius());
|
|
parameters_.insert(ParametersPair(Parameters::kIcpPointToPlaneRadius(), "0"));
|
|
}
|
|
int regStrategy = Parameters::defaultRegStrategy();
|
|
Parameters::parse(parameters_, Parameters::kRegStrategy(), regStrategy);
|
|
if(parameters_.find(Parameters::kRGBDProximityPathMaxNeighbors()) == parameters_.end() &&
|
|
(regStrategy == Registration::kTypeIcp || regStrategy == Registration::kTypeVisIcp))
|
|
{
|
|
if(this->isSubscribedToScan2d() || (this->isSubscribedToScan3d() && scanCloudIs2d_))
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Setting \"%s\" parameter to 10 (default 0) as \"%s\" is "
|
|
"true and \"%s\" uses ICP. Proximity detection by space will be also done by merging close "
|
|
"scans. To disable, set \"%s\" to 0. To suppress this warning, "
|
|
"add <param name=\"%s\" type=\"string\" value=\"10\"/>",
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
|
|
this->isSubscribedToScan2d()?"subscribe_scan":"scan_cloud_is_2d",
|
|
Parameters::kRegStrategy().c_str(),
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str());
|
|
parameters_.insert(ParametersPair(Parameters::kRGBDProximityPathMaxNeighbors(), "10"));
|
|
}
|
|
else if(this->isSubscribedToScan3d())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Setting \"%s\" parameter to 1 (default 0) as \"subscribe_scan_cloud\" is "
|
|
"true and \"%s\" uses ICP. To disable, set \"%s\" to 0. To suppress this warning, "
|
|
"add <param name=\"%s\" type=\"string\" value=\"1\"/>",
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
|
|
Parameters::kRegStrategy().c_str(),
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str());
|
|
parameters_.insert(ParametersPair(Parameters::kRGBDProximityPathMaxNeighbors(), "1"));
|
|
}
|
|
}
|
|
|
|
int estimationType = Parameters::defaultVisEstimationType();
|
|
Parameters::parse(parameters_, Parameters::kVisEstimationType(), estimationType);
|
|
|
|
// modify default parameters with those in the database
|
|
if(!deleteDbOnStart)
|
|
{
|
|
ParametersMap dbParameters;
|
|
rtabmap::DBDriver * driver = rtabmap::DBDriver::create();
|
|
if(driver->openConnection(databasePath_))
|
|
{
|
|
dbParameters = driver->getLastParameters(); // parameter migration is already done
|
|
}
|
|
delete driver;
|
|
for(ParametersMap::iterator iter=dbParameters.begin(); iter!=dbParameters.end(); ++iter)
|
|
{
|
|
if(iter->first.compare(Parameters::kRtabmapWorkingDirectory()) == 0)
|
|
{
|
|
// ignore working directory
|
|
continue;
|
|
}
|
|
if(parameters_.find(iter->first) == parameters_.end() &&
|
|
allParameters.find(iter->first) != allParameters.end() &&
|
|
allParameters.find(iter->first)->second.compare(iter->second) !=0)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Update RTAB-Map parameter \"%s\"=\"%s\" from database", iter->first.c_str(), iter->second.c_str());
|
|
parameters_.insert(*iter);
|
|
}
|
|
}
|
|
}
|
|
ParametersMap modifiedParameters = parameters_;
|
|
// Add all other parameters (not copied if already exists)
|
|
parameters_.insert(allParameters.begin(), allParameters.end());
|
|
|
|
if(parameters_.find(Parameters::kRtabmapDetectionRate()) != parameters_.end())
|
|
{
|
|
Parameters::parse(parameters_, Parameters::kRtabmapDetectionRate(), rate_);
|
|
RCLCPP_INFO(this->get_logger(), "RTAB-Map detection rate = %f Hz", rate_);
|
|
}
|
|
if(parameters_.find(Parameters::kRtabmapCreateIntermediateNodes()) != parameters_.end())
|
|
{
|
|
Parameters::parse(parameters_, Parameters::kRtabmapCreateIntermediateNodes(), createIntermediateNodes_);
|
|
if(createIntermediateNodes_)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Create intermediate nodes");
|
|
if(rate_ == 0.0f)
|
|
{
|
|
bool interOdomInfo = false;
|
|
if(get_parameter("subscribe_inter_odom_info", interOdomInfo))
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Subscribe to inter odom + info messages");
|
|
interOdomSync_ = new message_filters::Synchronizer<MyExactInterOdomSyncPolicy>(MyExactInterOdomSyncPolicy(100), interOdomSyncSub_, interOdomInfoSyncSub_);
|
|
interOdomSync_->registerCallback(std::bind(&CoreWrapper::interOdomInfoCallback, this, std::placeholders::_1, std::placeholders::_2));
|
|
interOdomSyncSub_.subscribe(this, "inter_odom", RCLCPP_QOS(100, rclcpp::ReliabilityPolicy::SystemDefault));
|
|
interOdomInfoSyncSub_.subscribe(this, "inter_odom_info", RCLCPP_QOS(100, rclcpp::ReliabilityPolicy::SystemDefault));
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Subscribe to inter odom messages");
|
|
interOdomSub_ = this->create_subscription<nav_msgs::msg::Odometry>("inter_odom", 100, std::bind(&CoreWrapper::interOdomCallback, this, std::placeholders::_1), subOptions);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
if(parameters_.find(Parameters::kGridGlobalMaxNodes()) != parameters_.end())
|
|
{
|
|
Parameters::parse(parameters_, Parameters::kGridGlobalMaxNodes(), mappingMaxNodes_);
|
|
if(mappingMaxNodes_>0)
|
|
{
|
|
RCLCPP_INFO(get_logger(), "Max mapping nodes = %d", mappingMaxNodes_);
|
|
}
|
|
}
|
|
if(parameters_.find(Parameters::kGridGlobalAltitudeDelta()) != parameters_.end())
|
|
{
|
|
Parameters::parse(parameters_, Parameters::kGridGlobalAltitudeDelta(), mappingAltitudeDelta_);
|
|
if(mappingAltitudeDelta_>0.0)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Mapping altitude delta = %f", mappingAltitudeDelta_);
|
|
}
|
|
}
|
|
if(parameters_.find(Parameters::kRtabmapImagesAlreadyRectified()) != parameters_.end())
|
|
{
|
|
Parameters::parse(parameters_, Parameters::kRtabmapImagesAlreadyRectified(), alreadyRectifiedImages_);
|
|
}
|
|
if(parameters_.find(Parameters::kRegForce3DoF()) != parameters_.end())
|
|
{
|
|
Parameters::parse(parameters_, Parameters::kRegForce3DoF(), twoDMapping_);
|
|
}
|
|
|
|
if(deleteDbOnStart)
|
|
{
|
|
if(UFile::erase(databasePath_) == 0)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Deleted database \"%s\" (--delete_db_on_start or -d are set).", databasePath_.c_str());
|
|
}
|
|
}
|
|
|
|
if(databasePath_.size())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Using database from \"%s\" (%ld MB).", databasePath_.c_str(), UFile::length(databasePath_)/(1024*1024));
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: database_path parameter not set, the map will not be saved.");
|
|
}
|
|
|
|
mapsManager_.setParameters(parameters_);
|
|
|
|
// Init RTAB-Map
|
|
rtabmap_.init(parameters_, databasePath_);
|
|
|
|
if(rtabmap_.getMemory())
|
|
{
|
|
if(useSavedMap_)
|
|
{
|
|
float xMin, yMin, gridCellSize;
|
|
cv::Mat map = rtabmap_.getMemory()->load2DMap(xMin, yMin, gridCellSize);
|
|
if(!map.empty())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: 2D occupancy grid map loaded (%dx%d).", map.cols, map.rows);
|
|
mapsManager_.set2DMap(map, xMin, yMin, gridCellSize, rtabmap_.getLocalOptimizedPoses(), rtabmap_.getMemory());
|
|
}
|
|
}
|
|
if(rtabmap_.getMemory()->getWorkingMem().size()>1)
|
|
{
|
|
RCLCPP_INFO(get_logger(), "rtabmap: Working Memory = %d, Local map = %d.",
|
|
(int)rtabmap_.getMemory()->getWorkingMem().size()-1,
|
|
(int)rtabmap_.getLocalOptimizedPoses().size());
|
|
}
|
|
|
|
if(databasePath_.size())
|
|
{
|
|
RCLCPP_INFO(get_logger(), "rtabmap: Database version = \"%s\".", rtabmap_.getMemory()->getDatabaseVersion().c_str());
|
|
}
|
|
|
|
if(rtabmap_.getMemory()->isIncremental())
|
|
{
|
|
RCLCPP_INFO(get_logger(), "rtabmap: SLAM mode (%s=true)", Parameters::kMemIncrementalMemory().c_str());
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_INFO(get_logger(), "rtabmap: Localization mode (%s=false)", Parameters::kMemIncrementalMemory().c_str());
|
|
}
|
|
}
|
|
|
|
// setup services
|
|
const std::string servicePrefix = get_name() + std::string("/");
|
|
updateSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "update_parameters", std::bind(&CoreWrapper::updateRtabmapCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
resetSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "reset", std::bind(&CoreWrapper::resetRtabmapCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
pauseSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "pause", std::bind(&CoreWrapper::pauseRtabmapCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
resumeSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "resume", std::bind(&CoreWrapper::resumeRtabmapCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
loadDatabaseSrv_ = this->create_service<rtabmap_msgs::srv::LoadDatabase>(servicePrefix + "load_database", std::bind(&CoreWrapper::loadDatabaseCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
triggerNewMapSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "trigger_new_map", std::bind(&CoreWrapper::triggerNewMapCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
backupDatabase_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "backup", std::bind(&CoreWrapper::backupDatabaseCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
detectMoreLoopClosuresSrv_ = this->create_service<rtabmap_msgs::srv::DetectMoreLoopClosures>(servicePrefix + "detect_more_loop_closures", std::bind(&CoreWrapper::detectMoreLoopClosuresCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
globalBundleAdjustmentSrv_ = this->create_service<rtabmap_msgs::srv::GlobalBundleAdjustment>(servicePrefix + "global_bundle_adjustment", std::bind(&CoreWrapper::globalBundleAdjustmentCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
cleanupLocalGridsSrv_ = this->create_service<rtabmap_msgs::srv::CleanupLocalGrids>(servicePrefix + "cleanup_local_grids", std::bind(&CoreWrapper::cleanupLocalGridsCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
setModeLocalizationSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "set_mode_localization", std::bind(&CoreWrapper::setModeLocalizationCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
setModeMappingSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "set_mode_mapping", std::bind(&CoreWrapper::setModeMappingCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
getNodeDataSrv_ = this->create_service<rtabmap_msgs::srv::GetNodeData>(servicePrefix + "get_node_data", std::bind(&CoreWrapper::getNodeDataCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
getMapDataSrv_ = this->create_service<rtabmap_msgs::srv::GetMap>(servicePrefix + "get_map_data", std::bind(&CoreWrapper::getMapDataCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
getMapData2Srv_ = this->create_service<rtabmap_msgs::srv::GetMap2>(servicePrefix + "get_map_data2", std::bind(&CoreWrapper::getMapData2Callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
getMapSrv_ = this->create_service<nav_msgs::srv::GetMap>(servicePrefix + "get_map", std::bind(&CoreWrapper::getMapCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
getProbMapSrv_ = this->create_service<nav_msgs::srv::GetMap>(servicePrefix + "get_prob_map", std::bind(&CoreWrapper::getProbMapCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
publishMapDataSrv_ = this->create_service<rtabmap_msgs::srv::PublishMap>(servicePrefix + "publish_map", std::bind(&CoreWrapper::publishMapCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
getPlanSrv_ = this->create_service<nav_msgs::srv::GetPlan>(servicePrefix + "get_plan", std::bind(&CoreWrapper::getPlanCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
getPlanNodesSrv_ = this->create_service<rtabmap_msgs::srv::GetPlan>(servicePrefix + "get_plan_nodes", std::bind(&CoreWrapper::getPlanNodesCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
setGoalSrv_ = this->create_service<rtabmap_msgs::srv::SetGoal>(servicePrefix + "set_goal", std::bind(&CoreWrapper::setGoalCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
cancelGoalSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "cancel_goal", std::bind(&CoreWrapper::cancelGoalCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
setLabelSrv_ = this->create_service<rtabmap_msgs::srv::SetLabel>(servicePrefix + "set_label", std::bind(&CoreWrapper::setLabelCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
listLabelsSrv_ = this->create_service<rtabmap_msgs::srv::ListLabels>(servicePrefix + "list_labels", std::bind(&CoreWrapper::listLabelsCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
removeLabelSrv_ = this->create_service<rtabmap_msgs::srv::RemoveLabel>(servicePrefix + "remove_label", std::bind(&CoreWrapper::removeLabelCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
addLinkSrv_ = this->create_service<rtabmap_msgs::srv::AddLink>(servicePrefix + "add_link", std::bind(&CoreWrapper::addLinkCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
getNodesInRadiusSrv_ = this->create_service<rtabmap_msgs::srv::GetNodesInRadius>(servicePrefix + "get_nodes_in_radius", std::bind(&CoreWrapper::getNodesInRadiusCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
|
|
#ifdef WITH_OCTOMAP_MSGS
|
|
#ifdef RTABMAP_OCTOMAP
|
|
octomapBinarySrv_ = this->create_service<octomap_msgs::srv::GetOctomap>(servicePrefix + "octomap_binary", std::bind(&CoreWrapper::octomapBinaryCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
octomapFullSrv_ = this->create_service<octomap_msgs::srv::GetOctomap>(servicePrefix + "octomap_full", std::bind(&CoreWrapper::octomapFullCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
#endif
|
|
#endif
|
|
//private services
|
|
setLogDebugSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "log_debug", std::bind(&CoreWrapper::setLogDebug, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
setLogInfoSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "log_info", std::bind(&CoreWrapper::setLogInfo, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
setLogWarnSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "log_warning", std::bind(&CoreWrapper::setLogWarn, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
setLogErrorSrv_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "log_error", std::bind(&CoreWrapper::setLogError, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), rclcpp::ServicesQoS(), processingCallbackGroup_);
|
|
|
|
int optimizeIterations = 0;
|
|
Parameters::parse(parameters_, Parameters::kOptimizerIterations(), optimizeIterations);
|
|
if(publishTf && optimizeIterations != 0)
|
|
{
|
|
tfThreadRunning_ = true;
|
|
transformThread_ = new std::thread([&](){
|
|
if(tfDelay == 0)
|
|
return;
|
|
rclcpp::Rate r(1.0 / tfDelay);
|
|
while(tfThreadRunning_)
|
|
{
|
|
mapToOdomMutex_.lock();
|
|
if(!odomFrameId_.empty())
|
|
{
|
|
geometry_msgs::msg::TransformStamped msg;
|
|
rtabmap_conversions::transformToGeometryMsg(mapToOdom_, msg.transform);
|
|
msg.child_frame_id = odomFrameId_;
|
|
msg.header.frame_id = mapFrameId_;
|
|
msg.header.stamp = now() + rclcpp::Duration::from_seconds(tfTolerance);
|
|
tfBroadcaster_->sendTransform(msg);
|
|
}
|
|
mapToOdomMutex_.unlock();
|
|
try {
|
|
r.sleep();
|
|
}
|
|
catch(std::exception & e) {
|
|
if(rclcpp::ok()) {
|
|
RCLCPP_ERROR(this->get_logger(),
|
|
"Could not sleep: \"%s\", TF \"%s\"->\"%s\" won't be published anymore!",
|
|
e.what(), mapFrameId_.c_str(), odomFrameId_.c_str());
|
|
} // else: the node may have been shutdown
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
else if(publishTf)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "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.",
|
|
Parameters::kOptimizerIterations().c_str(), mapFrameId_.c_str());
|
|
}
|
|
|
|
std::vector<diagnostic_updater::DiagnosticTask*> tasks;
|
|
double localizationThreshold = 0.0f;
|
|
localizationThreshold = this->declare_parameter("loc_thr", localizationThreshold);
|
|
if(rtabmap_.getMemory() && !rtabmap_.getMemory()->isIncremental() && localizationThreshold > 0.0)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: loc_thr = %f", localizationThreshold);
|
|
localizationDiagnostic_.setLocalizationThreshold(localizationThreshold);
|
|
tasks.push_back(&localizationDiagnostic_);
|
|
}
|
|
|
|
RCLCPP_INFO(this->get_logger(), "Setup callbacks");
|
|
setupCallbacks(*this, tasks); // do it at the end
|
|
if(!this->isDataSubscribed())
|
|
{
|
|
bool isRGBD = uStr2Bool(parameters_.at(Parameters::kRGBDEnabled()).c_str());
|
|
if(isRGBD)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "ROS param subscribe_depth, subscribe_stereo and subscribe_rgbd are false, but RTAB-Map "
|
|
"parameter \"%s\" is true! Please set subscribe_depth, subscribe_stereo or subscribe_rgbd "
|
|
"to true to use rtabmap node for RGB-D SLAM, set \"%s\" to false for loop closure "
|
|
"detection on images-only or set subscribe_rgb to true to localize a single RGB camera against pre-built 3D map.",
|
|
Parameters::kRGBDEnabled().c_str(),
|
|
Parameters::kRGBDEnabled().c_str());
|
|
}
|
|
image_transport::TransportHints hints(this);
|
|
defaultSub_ = image_transport::create_subscription(this, "image", std::bind(&CoreWrapper::defaultCallback, this, std::placeholders::_1), hints.getTransport(), rclcpp::QoS(this->getTopicQueueSize()).reliability((rmw_qos_reliability_policy_t)qosImage_).get_rmw_qos_profile(), subOptions);
|
|
|
|
|
|
RCLCPP_INFO(this->get_logger(), "\n%s subscribed to:\n %s", get_name(), defaultSub_.getTopic().c_str());
|
|
}
|
|
else if(!this->isSubscribedToDepth() &&
|
|
!this->isSubscribedToStereo() &&
|
|
!this->isSubscribedToRGBD() &&
|
|
!this->isSubscribedToRGB() &&
|
|
(this->isSubscribedToScan2d() || this->isSubscribedToScan3d() || this->isSubscribedToOdom()) &&
|
|
!this->isSubscribedToSensorData())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "There is no image subscription, bag-of-words loop closure detection will be disabled...");
|
|
int kpMaxFeatures = Parameters::defaultKpMaxFeatures();
|
|
int registrationStrategy = Parameters::defaultRegStrategy();
|
|
Parameters::parse(parameters_, Parameters::kKpMaxFeatures(), kpMaxFeatures);
|
|
Parameters::parse(parameters_, Parameters::kRegStrategy(), registrationStrategy);
|
|
bool updateParams = false;
|
|
if(kpMaxFeatures!= -1)
|
|
{
|
|
uInsert(parameters_, ParametersPair(Parameters::kKpMaxFeatures(), "-1"));
|
|
RCLCPP_WARN(this->get_logger(), "Setting %s=-1 (bag-of-words disabled)", Parameters::kKpMaxFeatures().c_str());
|
|
updateParams = true;
|
|
}
|
|
if((this->isSubscribedToScan2d() || this->isSubscribedToScan3d()) && registrationStrategy != 1)
|
|
{
|
|
uInsert(parameters_, ParametersPair(Parameters::kRegStrategy(), "1"));
|
|
RCLCPP_WARN(this->get_logger(), "Setting %s=1 (ICP)", Parameters::kRegStrategy().c_str());
|
|
updateParams = true;
|
|
|
|
if(modifiedParameters.find(Parameters::kRGBDProximityPathMaxNeighbors()) == modifiedParameters.end())
|
|
{
|
|
if(this->isSubscribedToScan2d())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Setting \"%s\" parameter to 10 (default 0) as \"subscribe_scan\" is "
|
|
"true and \"%s\" uses ICP. Proximity detection by space will be also done by merging close "
|
|
"scans. To disable, set \"%s\" to 0. To suppress this warning, "
|
|
"add <param name=\"%s\" type=\"string\" value=\"10\"/>",
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
|
|
Parameters::kRegStrategy().c_str(),
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str());
|
|
uInsert(parameters_, ParametersPair(Parameters::kRGBDProximityPathMaxNeighbors(), "10"));
|
|
}
|
|
else if(this->isSubscribedToScan3d())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Setting \"%s\" parameter to 1 (default 0) as \"subscribe_scan_cloud\" is "
|
|
"true and \"%s\" uses ICP. To disable, set \"%s\" to 0. To suppress this warning, "
|
|
"add <param name=\"%s\" type=\"string\" value=\"1\"/>",
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
|
|
Parameters::kRegStrategy().c_str(),
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str(),
|
|
Parameters::kRGBDProximityPathMaxNeighbors().c_str());
|
|
uInsert(parameters_, ParametersPair(Parameters::kRGBDProximityPathMaxNeighbors(), "1"));
|
|
}
|
|
}
|
|
}
|
|
if(updateParams)
|
|
{
|
|
rtabmap_.parseParameters(parameters_);
|
|
}
|
|
}
|
|
|
|
// Set initial pose if set
|
|
if(!initialPoseStr.empty())
|
|
{
|
|
Transform intialPose = Transform::fromString(initialPoseStr);
|
|
if(!intialPose.isNull())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Setting initial pose: \"%s\"", intialPose.prettyPrint().c_str());
|
|
rtabmap_.setInitialPose(intialPose);
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Invalid initial_pose: \"%s\"", initialPoseStr.c_str());
|
|
}
|
|
}
|
|
|
|
// Update declared parameters
|
|
std::vector<rclcpp::Parameter> rosParameters;
|
|
for(ParametersMap::iterator iter=parameters_.begin(); iter!=parameters_.end(); ++iter)
|
|
{
|
|
rosParameters.push_back(rclcpp::Parameter(iter->first, iter->second));
|
|
}
|
|
this->set_parameters(rosParameters);
|
|
|
|
// Setup callback groups for any subscriptions that should not be affected by main processing thread.
|
|
userDataAsyncCallbackGroup_ = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
globalPoseAsyncCallbackGroup_ = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
gpsAsyncCallbackGroup_ = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
landmarkCallbackGroup_ = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
imuCallbackGroup_ = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
|
rclcpp::SubscriptionOptions userDataAsyncSubOptions;
|
|
rclcpp::SubscriptionOptions globalPoseAsyncSubOptions;
|
|
rclcpp::SubscriptionOptions gpsAsyncSubOptions;
|
|
rclcpp::SubscriptionOptions landmarkSubOptions;
|
|
rclcpp::SubscriptionOptions imuSubOptions;
|
|
userDataAsyncSubOptions.callback_group = userDataAsyncCallbackGroup_;
|
|
globalPoseAsyncSubOptions.callback_group = globalPoseAsyncCallbackGroup_;
|
|
gpsAsyncSubOptions.callback_group = gpsAsyncCallbackGroup_;
|
|
landmarkSubOptions.callback_group = imuCallbackGroup_;
|
|
imuSubOptions.callback_group = imuCallbackGroup_;
|
|
|
|
int qosGPS = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
|
int qosIMU = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
|
qosGPS = this->declare_parameter("qos_gps", qosGPS);
|
|
qosIMU = this->declare_parameter("qos_imu", qosIMU);
|
|
userDataAsyncSub_ = this->create_subscription<rtabmap_msgs::msg::UserData>("user_data_async", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qosUserData_), std::bind(&CoreWrapper::userDataAsyncCallback, this, std::placeholders::_1), userDataAsyncSubOptions);
|
|
globalPoseAsyncSub_ = this->create_subscription<geometry_msgs::msg::PoseWithCovarianceStamped>("global_pose", 1, std::bind(&CoreWrapper::globalPoseAsyncCallback, this, std::placeholders::_1), globalPoseAsyncSubOptions);
|
|
gpsFixAsyncSub_ = this->create_subscription<sensor_msgs::msg::NavSatFix>("gps/fix", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qosGPS), std::bind(&CoreWrapper::gpsFixAsyncCallback, this, std::placeholders::_1), gpsAsyncSubOptions);
|
|
landmarkDetectionSub_ = this->create_subscription<rtabmap_msgs::msg::LandmarkDetection>("landmark_detection", 1, std::bind(&CoreWrapper::landmarkDetectionAsyncCallback, this, std::placeholders::_1), landmarkSubOptions);
|
|
landmarkDetectionsSub_ = this->create_subscription<rtabmap_msgs::msg::LandmarkDetections>("landmark_detections", 1, std::bind(&CoreWrapper::landmarkDetectionsAsyncCallback, this, std::placeholders::_1), landmarkSubOptions);
|
|
#ifdef WITH_APRILTAG_MSGS
|
|
tagDetectionsSub_ = this->create_subscription<apriltag_msgs::msg::AprilTagDetectionArray>("tag_detections", 5, std::bind(&CoreWrapper::tagDetectionsAsyncCallback, this, std::placeholders::_1), landmarkSubOptions);
|
|
apriltagSub_ = this->create_subscription<apriltag_msgs::msg::AprilTagDetectionArray>("apriltag/detections", 5, std::bind(&CoreWrapper::apriltagAsyncCallback, this, std::placeholders::_1), landmarkSubOptions);
|
|
#endif
|
|
#ifdef WITH_ARUCO_MSGS
|
|
arucoSub_ = this->create_subscription<aruco_msgs::msg::MarkerArray>("aruco/detections", 5, std::bind(&CoreWrapper::arucoAsyncCallback, this, std::placeholders::_1), landmarkSubOptions);
|
|
#endif
|
|
#ifdef WITH_ARUCO_OPENCV_MSGS
|
|
arucoOpencvSub_ = this->create_subscription<aruco_opencv_msgs::msg::ArucoDetection>("aruco_opencv/detections", 5, std::bind(&CoreWrapper::arucoOpencvAsyncCallback, this, std::placeholders::_1), landmarkSubOptions);
|
|
#endif
|
|
#ifdef WITH_ARUCO_MARKERS_MSGS
|
|
arucoMarkersSub_ = this->create_subscription<aruco_markers_msgs::msg::MarkerArray>("aruco_markers/detections", 5, std::bind(&CoreWrapper::arucoMarkersAsyncCallback, this, std::placeholders::_1), landmarkSubOptions);
|
|
#endif
|
|
#ifdef WITH_ROS2_ARUCO_INTERFACES
|
|
arucoInterfacesSub_ = this->create_subscription<ros2_aruco_interfaces::msg::ArucoMarkers>("aruco_interfaces/detections", 5, std::bind(&CoreWrapper::arucoInterfacesAsyncCallback, this, std::placeholders::_1), landmarkSubOptions);
|
|
#endif
|
|
#ifdef WITH_FIDUCIAL_MSGS
|
|
fiducialTransfromsSub_ = this->create_subscription<fiducial_msgs::msg::FiducialTransformArray>("fiducial_transforms", 5, std::bind(&CoreWrapper::fiducialDetectionsAsyncCallback, this, std::placeholders::_1), landmarkSubOptions);
|
|
#endif
|
|
imuSub_ = this->create_subscription<sensor_msgs::msg::Imu>("imu", rclcpp::QoS(100).reliability((rmw_qos_reliability_policy_t)qosIMU), std::bind(&CoreWrapper::imuAsyncCallback, this, std::placeholders::_1), imuSubOptions);
|
|
republishNodeDataSub_ = this->create_subscription<std_msgs::msg::Int32MultiArray>(servicePrefix+"republish_node_data", 1, std::bind(&CoreWrapper::republishNodeDataCallback, this, std::placeholders::_1), subOptions);
|
|
parametersClient_ = std::make_shared<rclcpp::AsyncParametersClient>(this, std::string(), rclcpp::ParametersQoS(), processingCallbackGroup_);
|
|
|
|
auto on_parameter_event_callback =
|
|
[this](const rcl_interfaces::msg::ParameterEvent::SharedPtr event) -> void
|
|
{
|
|
std::string ns = get_namespace();
|
|
if(ns != "/")
|
|
{
|
|
ns += "/";
|
|
}
|
|
if(event->node.compare(ns+get_name()) != 0)
|
|
{
|
|
return;
|
|
}
|
|
RCLCPP_INFO(this->get_logger(), "Parameters event received!");
|
|
if(event->changed_parameters.size())
|
|
{
|
|
for(size_t i=0; i<event->changed_parameters.size(); ++i)
|
|
{
|
|
std::string key = event->changed_parameters[i].name;
|
|
if(parameters_.find(key) != parameters_.end())
|
|
{
|
|
std::string vStr;
|
|
switch(event->changed_parameters[i].value.type) {
|
|
case 1:
|
|
vStr = uBool2Str(event->changed_parameters[i].value.bool_value);
|
|
break;
|
|
case 2:
|
|
vStr = uNumber2Str((int)event->changed_parameters[i].value.integer_value);
|
|
break;
|
|
case 3:
|
|
vStr = uNumber2Str(event->changed_parameters[i].value.double_value);
|
|
break;
|
|
case 4:
|
|
vStr = event->changed_parameters[i].value.string_value;
|
|
break;
|
|
default:
|
|
RCLCPP_WARN(this->get_logger(), "Parameter type %d received for parameter %s is not supported, use string type.",
|
|
(int)event->changed_parameters[i].value.type, key.c_str());
|
|
continue;
|
|
}
|
|
RCLCPP_INFO(this->get_logger(), "Setting RTAB-Map parameter \"%s\"=\"%s\"", key.c_str(), vStr.c_str());
|
|
parameters_.at(key) = vStr;
|
|
}
|
|
}
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Updating parameters");
|
|
if(parameters_.find(Parameters::kRtabmapDetectionRate()) != parameters_.end())
|
|
{
|
|
rate_ = uStr2Float(parameters_.at(Parameters::kRtabmapDetectionRate()));
|
|
RCLCPP_INFO(this->get_logger(), "RTAB-Map rate detection = %f Hz", rate_);
|
|
}
|
|
rtabmap_.parseParameters(parameters_);
|
|
// Don't reset map in localization mode
|
|
if(rtabmap_.getMemory()->isIncremental()) {
|
|
mapsManager_.setParameters(parameters_);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Setup callback for changes to parameters.
|
|
rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void>> paramOptions;
|
|
paramOptions.callback_group = processingCallbackGroup_;
|
|
parameterEventSub_ = parametersClient_->on_parameter_event(
|
|
on_parameter_event_callback,
|
|
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)),
|
|
paramOptions);
|
|
}
|
|
|
|
CoreWrapper::~CoreWrapper()
|
|
{
|
|
if(transformThread_)
|
|
{
|
|
tfThreadRunning_ = false;
|
|
transformThread_->join();
|
|
delete transformThread_;
|
|
}
|
|
|
|
this->saveParameters(configPath_);
|
|
|
|
printf("rtabmap: Saving database/long-term memory... (located at %s)\n", databasePath_.c_str());
|
|
if(rtabmap_.getMemory())
|
|
{
|
|
// save the grid map
|
|
float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f;
|
|
cv::Mat pixels = mapsManager_.getGridMap(xMin, yMin, gridCellSize);
|
|
if(!pixels.empty())
|
|
{
|
|
printf("rtabmap: 2D occupancy grid map saved.\n");
|
|
rtabmap_.getMemory()->save2DMap(pixels, xMin, yMin, gridCellSize);
|
|
}
|
|
}
|
|
|
|
rtabmap_.close();
|
|
printf("rtabmap: Saving database/long-term memory...done! (located at %s, %ld MB)\n", databasePath_.c_str(), UFile::length(databasePath_)/(1024*1024));
|
|
|
|
delete interOdomSync_;
|
|
}
|
|
|
|
void CoreWrapper::loadParameters(const std::string & configFile, ParametersMap & parameters)
|
|
{
|
|
if(!configFile.empty())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Loading parameters from %s", configFile.c_str());
|
|
if(!UFile::exists(configFile.c_str()))
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Config file doesn't exist! It will be generated...");
|
|
}
|
|
Parameters::readINI(configFile.c_str(), parameters);
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::saveParameters(const std::string & configFile)
|
|
{
|
|
if(!configFile.empty())
|
|
{
|
|
printf("Saving parameters to %s\n", configFile.c_str());
|
|
|
|
if(!UFile::exists(configFile.c_str()))
|
|
{
|
|
printf("Config file doesn't exist, a new one will be created.\n");
|
|
}
|
|
Parameters::writeINI(configFile.c_str(), parameters_);
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Parameters are not saved (No configuration file provided...)");
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::defaultCallback(const sensor_msgs::msg::Image::ConstSharedPtr imageMsg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
rclcpp::Time stamp = imageMsg->header.stamp;
|
|
if(stamp.seconds() == 0.0)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "A null stamp has been detected in the input topic. Make sure the stamp is set.");
|
|
return;
|
|
}
|
|
|
|
if(rate_>0.0f)
|
|
{
|
|
if(previousStamp_.seconds() > 0.0 && stamp.seconds() > previousStamp_.seconds() && stamp - previousStamp_ < rclcpp::Duration::from_seconds(1.0f/rate_))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
previousStamp_ = stamp;
|
|
|
|
if(!(imageMsg->encoding.compare(sensor_msgs::image_encodings::MONO8) ==0 ||
|
|
imageMsg->encoding.compare(sensor_msgs::image_encodings::MONO16) ==0 ||
|
|
imageMsg->encoding.compare(sensor_msgs::image_encodings::BGR8) == 0 ||
|
|
imageMsg->encoding.compare(sensor_msgs::image_encodings::RGB8) == 0))
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Input type must be image=mono8,mono16,rgb8,bgr8");
|
|
return;
|
|
}
|
|
|
|
cv_bridge::CvImageConstPtr ptrImage;
|
|
if(imageMsg->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
|
imageMsg->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0)
|
|
{
|
|
ptrImage = cv_bridge::toCvShare(imageMsg, "mono8");
|
|
}
|
|
else
|
|
{
|
|
ptrImage = cv_bridge::toCvShare(imageMsg, "bgr8");
|
|
}
|
|
|
|
// process data
|
|
UTimer timer;
|
|
if(rtabmap_.isIDsGenerated())
|
|
{
|
|
if(!rtabmap_.process(ptrImage->image.clone()))
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "RTAB-Map could not process the data received!");
|
|
}
|
|
else
|
|
{
|
|
this->publishStats(now());
|
|
}
|
|
}
|
|
else if(!rtabmap_.isIDsGenerated())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Ignoring received image because its sequence ID=0. Please "
|
|
"set \"Mem/GenerateIds\"=\"true\" to ignore ros generated sequence id. "
|
|
"Use only \"Mem/GenerateIds\"=\"false\" for once-time run of RTAB-Map and "
|
|
"when you need to have IDs output of RTAB-map synchronised with the source "
|
|
"image sequence ID.");
|
|
}
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Update rate=%fs, Limit=%fs, Processing time = %fs (%d local nodes)",
|
|
1.0f/rate_,
|
|
rtabmap_.getTimeThreshold()/1000.0f,
|
|
timer.ticks(),
|
|
rtabmap_.getWMSize()+rtabmap_.getSTMSize());
|
|
}
|
|
}
|
|
|
|
bool CoreWrapper::odomUpdate(const nav_msgs::msg::Odometry & odomMsg, rclcpp::Time stamp)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
Transform odom = rtabmap_conversions::transformFromPoseMsg(odomMsg.pose.pose);
|
|
if(!odom.isNull())
|
|
{
|
|
Transform odomTF;
|
|
if(stamp.seconds() != 0.0) {
|
|
odomTF = rtabmap_conversions::getTransform(odomMsg.header.frame_id, frameId_, stamp, *tfBuffer_, waitForTransform_);
|
|
}
|
|
if(odomTF.isNull())
|
|
{
|
|
static bool shown = false;
|
|
if(!shown)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "We received odometry message, but we cannot get the "
|
|
"corresponding TF %s->%s at data stamp %fs (odom msg stamp is %fs). Make sure TF of odometry is "
|
|
"also published to get more accurate pose estimation. This "
|
|
"warning is only printed once.", odomMsg.header.frame_id.c_str(), frameId_.c_str(), stamp.seconds(), rtabmap_conversions::timestampFromROS(odomMsg.header.stamp));
|
|
shown = true;
|
|
}
|
|
stamp = odomMsg.header.stamp;
|
|
}
|
|
else
|
|
{
|
|
odom = odomTF;
|
|
}
|
|
}
|
|
|
|
UScopeMutex lock(lastPoseMutex_);
|
|
|
|
if(!lastPose_.isIdentity() && !odom.isNull() && (odom.isIdentity() || (odomMsg.pose.covariance[0] >= BAD_COVARIANCE && odomMsg.twist.covariance[0] >= BAD_COVARIANCE)))
|
|
{
|
|
UWARN("Odometry is reset (identity pose or high variance (%f) detected). Increment map id!", MAX(odomMsg.pose.covariance[0], odomMsg.twist.covariance[0]));
|
|
triggerNewMapBeforeNextUpdate_ = true;
|
|
lastPoseCovariance_ = cv::Mat();
|
|
}
|
|
|
|
lastPoseIntermediate_ = false;
|
|
lastPose_ = odom;
|
|
lastPoseStamp_ = stamp;
|
|
lastPoseVelocity_.resize(6);
|
|
lastPoseVelocity_[0] = odomMsg.twist.twist.linear.x;
|
|
lastPoseVelocity_[1] = odomMsg.twist.twist.linear.y;
|
|
lastPoseVelocity_[2] = odomMsg.twist.twist.linear.z;
|
|
lastPoseVelocity_[3] = odomMsg.twist.twist.angular.x;
|
|
lastPoseVelocity_[4] = odomMsg.twist.twist.angular.y;
|
|
lastPoseVelocity_[5] = odomMsg.twist.twist.angular.z;
|
|
|
|
// Only update variance if odom is not null
|
|
if(!odom.isNull())
|
|
{
|
|
cv::Mat covariance;
|
|
double variance = odomMsg.twist.covariance[0];
|
|
if(variance == BAD_COVARIANCE || variance <= 0.0f)
|
|
{
|
|
//use the one of the pose
|
|
covariance = cv::Mat(6,6,CV_64FC1, (void*)odomMsg.pose.covariance.data()).clone();
|
|
covariance /= 2.0;
|
|
}
|
|
else
|
|
{
|
|
covariance = cv::Mat(6,6,CV_64FC1, (void*)odomMsg.twist.covariance.data()).clone();
|
|
}
|
|
|
|
if(uIsFinite(covariance.at<double>(0,0)) &&
|
|
covariance.at<double>(0,0) != 1.0 &&
|
|
covariance.at<double>(0,0)>0.0)
|
|
{
|
|
// Use largest covariance error (to be independent of the odometry frame rate)
|
|
if(lastPoseCovariance_.empty() || covariance.at<double>(0,0) > lastPoseCovariance_.at<double>(0,0))
|
|
{
|
|
lastPoseCovariance_ = covariance;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Throttle
|
|
bool ignoreFrame = false;
|
|
if(stamp.seconds() == 0.0)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "A null stamp has been detected in the input topics. Make sure the stamp in all input topics is set.");
|
|
ignoreFrame = true;
|
|
}
|
|
if(rate_>0.0f)
|
|
{
|
|
if(previousStamp_.seconds() > 0.0 && stamp.seconds() > previousStamp_.seconds() && stamp.seconds() - previousStamp_.seconds() < 1.0f/rate_)
|
|
{
|
|
ignoreFrame = true;
|
|
}
|
|
}
|
|
if(ignoreFrame)
|
|
{
|
|
if(createIntermediateNodes_)
|
|
{
|
|
lastPoseIntermediate_ = true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CoreWrapper::odomTFUpdate(const std::string & odomFrameId, const rclcpp::Time & stamp)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
// Odom TF ready?
|
|
Transform odom = rtabmap_conversions::getTransform(odomFrameId, frameId_, stamp, *tfBuffer_, waitForTransform_);
|
|
if(odom.isNull())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
UScopeMutex lock(lastPoseMutex_);
|
|
|
|
if(!lastPose_.isIdentity() && odom.isIdentity())
|
|
{
|
|
UWARN("Odometry is reset (identity pose detected). Increment map id!");
|
|
triggerNewMapBeforeNextUpdate_ = true;
|
|
lastPoseCovariance_ = cv::Mat();
|
|
}
|
|
|
|
lastPoseIntermediate_ = false;
|
|
lastPose_ = odom;
|
|
lastPoseStamp_ = stamp;
|
|
lastPoseVelocity_.clear();
|
|
|
|
bool ignoreFrame = false;
|
|
if(stamp.seconds() == 0.0)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "A null stamp has been detected in the input topics. Make sure the stamp in all input topics is set.");
|
|
ignoreFrame = true;
|
|
}
|
|
if(rate_>0.0f)
|
|
{
|
|
if(previousStamp_.seconds() > 0.0 && stamp.seconds() > previousStamp_.seconds() && stamp.seconds() - previousStamp_.seconds() < 1.0f/rate_)
|
|
{
|
|
ignoreFrame = true;
|
|
}
|
|
}
|
|
if(ignoreFrame)
|
|
{
|
|
if(createIntermediateNodes_)
|
|
{
|
|
lastPoseIntermediate_ = true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CoreWrapper::commonMultiCameraCallback(
|
|
const nav_msgs::msg::Odometry::ConstSharedPtr & odomMsg,
|
|
const rtabmap_msgs::msg::UserData::ConstSharedPtr & userDataMsg,
|
|
const std::vector<cv_bridge::CvImageConstPtr> & imageMsgs,
|
|
const std::vector<cv_bridge::CvImageConstPtr> & depthMsgs,
|
|
const std::vector<sensor_msgs::msg::CameraInfo> & cameraInfoMsgs,
|
|
const std::vector<sensor_msgs::msg::CameraInfo> & depthCameraInfoMsgs,
|
|
const sensor_msgs::msg::LaserScan & scan2dMsg,
|
|
const sensor_msgs::msg::PointCloud2 & scan3dMsg,
|
|
const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr& odomInfoMsg,
|
|
const std::vector<rtabmap_msgs::msg::GlobalDescriptor> & globalDescriptorMsgs,
|
|
const std::vector<std::vector<rtabmap_msgs::msg::KeyPoint> > & localKeyPoints,
|
|
const std::vector<std::vector<rtabmap_msgs::msg::Point3f> > & localPoints3d,
|
|
const std::vector<cv::Mat> & localDescriptors)
|
|
{
|
|
std::string odomFrameId;
|
|
if(odomMsg.get())
|
|
{
|
|
odomFrameId = odomMsg->header.frame_id;
|
|
if(!scan2dMsg.ranges.empty())
|
|
{
|
|
if(!odomUpdate(*odomMsg, scan2dMsg.header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else if(!scan3dMsg.data.empty())
|
|
{
|
|
if(!odomUpdate(*odomMsg, scan3dMsg.header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else if(cameraInfoMsgs.size() == 0 || !odomUpdate(*odomMsg, cameraInfoMsgs[0].header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mapToOdomMutex_.lock();
|
|
odomFrameId = odomFrameId_;
|
|
mapToOdomMutex_.unlock();
|
|
if(!scan2dMsg.ranges.empty())
|
|
{
|
|
if(!odomTFUpdate(odomFrameId, scan2dMsg.header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else if(!scan3dMsg.data.empty())
|
|
{
|
|
if(!odomTFUpdate(odomFrameId, scan3dMsg.header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else if(cameraInfoMsgs.size() == 0 || !odomTFUpdate(odomFrameId, cameraInfoMsgs[0].header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(syncTimer_->is_canceled() && syncDataMutex_.lockTry() == 0)
|
|
{
|
|
UScopeMutex lock(lastPoseMutex_);
|
|
commonMultiCameraCallbackImpl(odomFrameId,
|
|
userDataMsg,
|
|
imageMsgs,
|
|
depthMsgs,
|
|
cameraInfoMsgs,
|
|
depthCameraInfoMsgs,
|
|
scan2dMsg,
|
|
scan3dMsg,
|
|
odomInfoMsg,
|
|
globalDescriptorMsgs,
|
|
localKeyPoints,
|
|
localPoints3d,
|
|
localDescriptors);
|
|
|
|
if(syncData_.valid) {
|
|
syncTimer_->reset();
|
|
}
|
|
syncDataMutex_.unlock();
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::commonMultiCameraCallbackImpl(
|
|
const std::string & odomFrameId,
|
|
const rtabmap_msgs::msg::UserData::ConstSharedPtr & userDataMsg,
|
|
const std::vector<cv_bridge::CvImageConstPtr> & imageMsgs,
|
|
const std::vector<cv_bridge::CvImageConstPtr> & depthMsgs,
|
|
const std::vector<sensor_msgs::msg::CameraInfo> & cameraInfoMsgs,
|
|
const std::vector<sensor_msgs::msg::CameraInfo> & depthCameraInfoMsgs,
|
|
const sensor_msgs::msg::LaserScan & scan2dMsg,
|
|
const sensor_msgs::msg::PointCloud2 & scan3dMsg,
|
|
const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr& odomInfoMsg,
|
|
const std::vector<rtabmap_msgs::msg::GlobalDescriptor> & globalDescriptorMsgs,
|
|
const std::vector<std::vector<rtabmap_msgs::msg::KeyPoint> > & localKeyPointsMsgs,
|
|
const std::vector<std::vector<rtabmap_msgs::msg::Point3f> > & localPoints3dMsgs,
|
|
const std::vector<cv::Mat> & localDescriptorsMsgs)
|
|
{
|
|
UTimer timerConversion;
|
|
cv::Mat rgb;
|
|
cv::Mat depth;
|
|
std::vector<rtabmap::CameraModel> cameraModels;
|
|
std::vector<rtabmap::StereoCameraModel> stereoCameraModels;
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
std::vector<cv::Point3f> points;
|
|
cv::Mat descriptors;
|
|
|
|
if(!rtabmap_conversions::convertRGBDMsgs(
|
|
imageMsgs,
|
|
depthMsgs,
|
|
cameraInfoMsgs,
|
|
depthCameraInfoMsgs,
|
|
frameId_,
|
|
odomSensorSync_?odomFrameId:"",
|
|
lastPoseStamp_,
|
|
rgb,
|
|
depth,
|
|
cameraModels,
|
|
stereoCameraModels,
|
|
*tfBuffer_,
|
|
waitForTransform_,
|
|
alreadyRectifiedImages_,
|
|
localKeyPointsMsgs,
|
|
localPoints3dMsgs,
|
|
localDescriptorsMsgs,
|
|
&keypoints,
|
|
&points,
|
|
&descriptors))
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Could not convert rgb/depth msgs! Aborting rtabmap update...");
|
|
return;
|
|
}
|
|
UDEBUG("cameraModels=%ld stereoCameraModels=%ld", cameraModels.size(), stereoCameraModels.size());
|
|
UDEBUG("rgb=%dx%d(type=%d), depth/right=%dx%d(type=%d)", rgb.rows, rgb.cols, rgb.type(), depth.rows, depth.cols, depth.type());
|
|
|
|
if(stereoCameraModels.size() && stereoToDepth_)
|
|
{
|
|
UASSERT(depth.type() == CV_8UC1);
|
|
cv::Mat leftMono;
|
|
if(rgb.channels() == 3)
|
|
{
|
|
cv::cvtColor(rgb, leftMono, CV_BGR2GRAY);
|
|
}
|
|
else
|
|
{
|
|
leftMono = rgb;
|
|
}
|
|
cv::Mat rightMono = depth;
|
|
depth = cv::Mat();
|
|
|
|
UASSERT(int((leftMono.cols/stereoCameraModels.size())*stereoCameraModels.size()) == leftMono.cols);
|
|
UASSERT(int((rightMono.cols/stereoCameraModels.size())*stereoCameraModels.size()) == rightMono.cols);
|
|
int subImageWidth = leftMono.cols/stereoCameraModels.size();
|
|
for(size_t i=0; i<stereoCameraModels.size(); ++i)
|
|
{
|
|
cv::Mat left(leftMono, cv::Rect(subImageWidth*i, 0, subImageWidth, leftMono.rows));
|
|
cv::Mat right(rightMono, cv::Rect(subImageWidth*i, 0, subImageWidth, rightMono.rows));
|
|
|
|
// cv::stereoBM() see "$ rosrun rtabmap_ros rtabmap --params | grep StereoBM" for parameters
|
|
cv::Mat disparity = rtabmap::util2d::disparityFromStereoImages(
|
|
left,
|
|
right,
|
|
parameters_);
|
|
if(disparity.empty())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Could not compute disparity image (\"stereo_to_depth\" is true)!");
|
|
return;
|
|
}
|
|
cv::Mat subDepth = rtabmap::util2d::depthFromDisparity(
|
|
disparity,
|
|
stereoCameraModels[i].left().fx(),
|
|
stereoCameraModels[i].baseline());
|
|
|
|
if(subDepth.empty())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Could not compute depth image (\"stereo_to_depth\" is true)!");
|
|
return;
|
|
}
|
|
UASSERT(subDepth.type() == CV_16UC1 || subDepth.type() == CV_32FC1);
|
|
|
|
if(depth.empty())
|
|
{
|
|
depth = cv::Mat(subDepth.rows, subDepth.cols*stereoCameraModels.size(), subDepth.type());
|
|
}
|
|
|
|
if(subDepth.type() == depth.type())
|
|
{
|
|
subDepth.copyTo(cv::Mat(depth, cv::Rect(i*subDepth.cols, 0, subDepth.cols, subDepth.rows)));
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Some Depth images are not the same type!");
|
|
return;
|
|
}
|
|
|
|
cameraModels.push_back(stereoCameraModels[i].left());
|
|
}
|
|
stereoCameraModels.clear();
|
|
}
|
|
|
|
UASSERT(uContains(parameters_, rtabmap::Parameters::kMemSaveDepth16Format()));
|
|
if(!depth.empty() && depth.type() == CV_32FC1 && uStr2Bool(parameters_.at(Parameters::kMemSaveDepth16Format())))
|
|
{
|
|
depth = rtabmap::util2d::cvtDepthFromFloat(depth);
|
|
static bool shown = false;
|
|
if(!shown)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Save depth data to 16 bits format: depth type detected is "
|
|
"32FC1, use 16UC1 depth format to avoid this conversion "
|
|
"(or set parameter \"Mem/SaveDepth16Format=false\" to use "
|
|
"32bits format). This message is only printed once...");
|
|
shown = true;
|
|
}
|
|
}
|
|
|
|
LaserScan scan;
|
|
bool genMaxScanPts = 0;
|
|
if(scan2dMsg.ranges.empty() && scan3dMsg.data.empty() && !depth.empty() && stereoCameraModels.empty() && genScan_)
|
|
{
|
|
pcl::PointCloud<pcl::PointXYZ>::Ptr scanCloud2d(new pcl::PointCloud<pcl::PointXYZ>);
|
|
*scanCloud2d = util3d::laserScanFromDepthImages(
|
|
depth,
|
|
cameraModels,
|
|
genScanMaxDepth_,
|
|
genScanMinDepth_);
|
|
genMaxScanPts += depth.cols;
|
|
scan = LaserScan(rtabmap::util3d::laserScan2dFromPointCloud(*scanCloud2d), 0, genScanMaxDepth_);
|
|
}
|
|
else if(!scan2dMsg.ranges.empty())
|
|
{
|
|
if(!rtabmap_conversions::convertScanMsg(
|
|
scan2dMsg,
|
|
frameId_,
|
|
odomSensorSync_?odomFrameId:"",
|
|
lastPoseStamp_,
|
|
scan,
|
|
*tfBuffer_,
|
|
waitForTransform_,
|
|
// backward compatibility, project 2D scan in /base_link frame
|
|
rtabmap_.getMemory() && uStrNumCmp(rtabmap_.getMemory()->getDatabaseVersion(), "0.11.10") < 0))
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Could not convert laser scan msg! Aborting rtabmap update...");
|
|
return;
|
|
}
|
|
}
|
|
else if(!scan3dMsg.data.empty())
|
|
{
|
|
if(!rtabmap_conversions::convertScan3dMsg(
|
|
scan3dMsg,
|
|
frameId_,
|
|
odomSensorSync_?odomFrameId:"",
|
|
lastPoseStamp_,
|
|
scan,
|
|
*tfBuffer_,
|
|
waitForTransform_,
|
|
scanCloudMaxPoints_,
|
|
0,
|
|
scanCloudIs2d_))
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Could not convert 3d laser scan msg! Aborting rtabmap update...");
|
|
return;
|
|
}
|
|
|
|
RCLCPP_DEBUG(get_logger(), "%d %d %d %d", rgb.empty()?1:0, depth.empty()?1:0, scan.isEmpty()?1:0, genDepth_?1:0);
|
|
if(!rgb.empty() && depth.empty() && !scan.isEmpty() && genDepth_)
|
|
{
|
|
for(size_t i=0; i<cameraModels.size(); ++i)
|
|
{
|
|
rtabmap::CameraModel model = cameraModels[i];
|
|
if(genDepthDecimation_ > 1)
|
|
{
|
|
if(model.imageWidth()%genDepthDecimation_ == 0 && model.imageHeight()%genDepthDecimation_ == 0)
|
|
{
|
|
model = model.scaled(1.0f/float(genDepthDecimation_));
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(get_logger(), "decimation (%d) not valid for image size %dx%d! Aborting depth generation from scan...",
|
|
genDepthDecimation_,
|
|
model.imageWidth(),
|
|
model.imageHeight());
|
|
depth = cv::Mat();
|
|
break;
|
|
}
|
|
}
|
|
|
|
cv::Mat depthProjected = util3d::projectCloudToCamera(
|
|
model.imageSize(),
|
|
model.K(),
|
|
scan.data(),
|
|
scan.localTransform().inverse()*model.localTransform());
|
|
|
|
if(genDepthFillHolesSize_ > 0 && genDepthFillIterations_ > 0)
|
|
{
|
|
for(int i=0; i<genDepthFillIterations_;++i)
|
|
{
|
|
depthProjected = util2d::fillDepthHoles(
|
|
depthProjected,
|
|
genDepthFillHolesSize_,
|
|
genDepthFillHolesError_);
|
|
}
|
|
}
|
|
|
|
if(depth.empty())
|
|
{
|
|
depth = cv::Mat::zeros(model.imageHeight(), model.imageWidth()*cameraModels.size(), CV_32FC1);
|
|
}
|
|
depthProjected.copyTo(depth.colRange(i*model.imageWidth(), (i+1)*model.imageWidth()));
|
|
}
|
|
}
|
|
}
|
|
|
|
cv::Mat userData;
|
|
if(userDataMsg.get())
|
|
{
|
|
userData = rtabmap_conversions::userDataFromROS(*userDataMsg);
|
|
UScopeMutex lock(userDataMutex_);
|
|
if(!userData_.empty())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Synchronized and asynchronized user data topics cannot be used at the same time. Async user data dropped!");
|
|
userData_ = cv::Mat();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UScopeMutex lock(userDataMutex_);
|
|
userData = userData_;
|
|
userData_ = cv::Mat();
|
|
}
|
|
|
|
if(!stereoCameraModels.empty())
|
|
{
|
|
syncData_.data = SensorData(
|
|
scan,
|
|
rgb,
|
|
depth,
|
|
stereoCameraModels,
|
|
lastPoseIntermediate_?-1:0,
|
|
rtabmap_conversions::timestampFromROS(lastPoseStamp_),
|
|
userData);
|
|
}
|
|
else
|
|
{
|
|
syncData_.data = SensorData(
|
|
scan,
|
|
rgb,
|
|
depth,
|
|
cameraModels,
|
|
lastPoseIntermediate_?-1:0,
|
|
rtabmap_conversions::timestampFromROS(lastPoseStamp_),
|
|
userData);
|
|
}
|
|
|
|
OdometryInfo odomInfo;
|
|
if(odomInfoMsg.get())
|
|
{
|
|
odomInfo = rtabmap_conversions::odomInfoFromROS(*odomInfoMsg);
|
|
}
|
|
|
|
if(!globalDescriptorMsgs.empty())
|
|
{
|
|
syncData_.data.setGlobalDescriptors(rtabmap_conversions::globalDescriptorsFromROS(globalDescriptorMsgs));
|
|
}
|
|
|
|
if(!keypoints.empty())
|
|
{
|
|
UASSERT(points.empty() || points.size() == keypoints.size());
|
|
UASSERT(descriptors.empty() || descriptors.rows == (int)keypoints.size());
|
|
syncData_.data.setFeatures(keypoints, points, descriptors);
|
|
}
|
|
|
|
syncData_.valid = true;
|
|
syncData_.stamp = lastPoseStamp_;
|
|
syncData_.odom = lastPose_;
|
|
syncData_.odomVelocity = lastPoseVelocity_;
|
|
syncData_.odomFrameId = odomFrameId;
|
|
syncData_.odomCovariance = lastPoseCovariance_;
|
|
syncData_.odomInfo = odomInfo;
|
|
syncData_.timeMsgConversion = timerConversion.ticks();
|
|
|
|
if(!lastPoseIntermediate_)
|
|
{
|
|
previousStamp_ = lastPoseStamp_;
|
|
}
|
|
|
|
lastPoseCovariance_ = cv::Mat();
|
|
}
|
|
|
|
void CoreWrapper::commonLaserScanCallback(
|
|
const nav_msgs::msg::Odometry::ConstSharedPtr & odomMsg,
|
|
const rtabmap_msgs::msg::UserData::ConstSharedPtr & userDataMsg,
|
|
const sensor_msgs::msg::LaserScan & scan2dMsg,
|
|
const sensor_msgs::msg::PointCloud2 & scan3dMsg,
|
|
const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr& odomInfoMsg,
|
|
const rtabmap_msgs::msg::GlobalDescriptor & globalDescriptor)
|
|
{
|
|
UTimer timerConversion;
|
|
std::string odomFrameId;
|
|
if(odomMsg.get())
|
|
{
|
|
odomFrameId = odomMsg->header.frame_id;
|
|
if(!scan2dMsg.ranges.empty())
|
|
{
|
|
if(!odomUpdate(*odomMsg, scan2dMsg.header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else if(!scan3dMsg.data.empty())
|
|
{
|
|
if(!odomUpdate(*odomMsg, scan3dMsg.header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mapToOdomMutex_.lock();
|
|
odomFrameId = odomFrameId_;
|
|
mapToOdomMutex_.unlock();
|
|
if(!scan2dMsg.ranges.empty())
|
|
{
|
|
if(!odomTFUpdate(odomFrameId, scan2dMsg.header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else if(!scan3dMsg.data.empty())
|
|
{
|
|
if(!odomTFUpdate(odomFrameId, scan3dMsg.header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(syncTimer_->is_canceled() && syncDataMutex_.lockTry() == 0)
|
|
{
|
|
UScopeMutex lock(lastPoseMutex_);
|
|
LaserScan scan;
|
|
if(!scan2dMsg.ranges.empty())
|
|
{
|
|
if(!rtabmap_conversions::convertScanMsg(
|
|
scan2dMsg,
|
|
frameId_,
|
|
odomSensorSync_?odomFrameId:"",
|
|
lastPoseStamp_,
|
|
scan,
|
|
*tfBuffer_,
|
|
waitForTransform_,
|
|
// backward compatibility, project 2D scan in /base_link frame
|
|
rtabmap_.getMemory() && uStrNumCmp(rtabmap_.getMemory()->getDatabaseVersion(), "0.11.10") < 0))
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Could not convert laser scan msg! Aborting rtabmap update...");
|
|
return;
|
|
}
|
|
}
|
|
else if(!scan3dMsg.data.empty())
|
|
{
|
|
if(!rtabmap_conversions::convertScan3dMsg(
|
|
scan3dMsg,
|
|
frameId_,
|
|
odomSensorSync_?odomFrameId:"",
|
|
lastPoseStamp_,
|
|
scan,
|
|
*tfBuffer_,
|
|
waitForTransform_,
|
|
scanCloudMaxPoints_,
|
|
0,
|
|
scanCloudIs2d_))
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Could not convert 3d laser scan msg! Aborting rtabmap update...");
|
|
return;
|
|
}
|
|
}
|
|
|
|
cv::Mat userData;
|
|
if(userDataMsg.get())
|
|
{
|
|
userData = rtabmap_conversions::userDataFromROS(*userDataMsg);
|
|
UScopeMutex lock(userDataMutex_);
|
|
if(!userData_.empty())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Synchronized and asynchronized user data topics cannot be used at the same time. Async user data dropped!");
|
|
userData_ = cv::Mat();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UScopeMutex lock(userDataMutex_);
|
|
userData = userData_;
|
|
userData_ = cv::Mat();
|
|
}
|
|
|
|
syncData_.data = SensorData(
|
|
scan,
|
|
cv::Mat(),
|
|
cv::Mat(),
|
|
rtabmap::CameraModel(),
|
|
lastPoseIntermediate_?-1:0,
|
|
rtabmap_conversions::timestampFromROS(lastPoseStamp_),
|
|
userData);
|
|
|
|
OdometryInfo odomInfo;
|
|
if(odomInfoMsg.get())
|
|
{
|
|
odomInfo = rtabmap_conversions::odomInfoFromROS(*odomInfoMsg, true);
|
|
}
|
|
|
|
if(!globalDescriptor.data.empty())
|
|
{
|
|
syncData_.data.addGlobalDescriptor(rtabmap_conversions::globalDescriptorFromROS(globalDescriptor));
|
|
}
|
|
|
|
syncData_.valid = true;
|
|
syncData_.stamp = lastPoseStamp_;
|
|
syncData_.odom = lastPose_;
|
|
syncData_.odomVelocity = lastPoseVelocity_;
|
|
syncData_.odomFrameId = odomFrameId;
|
|
syncData_.odomCovariance = lastPoseCovariance_;
|
|
syncData_.odomInfo = odomInfo;
|
|
syncData_.timeMsgConversion = timerConversion.ticks();
|
|
|
|
if(!lastPoseIntermediate_)
|
|
{
|
|
previousStamp_ = lastPoseStamp_;
|
|
}
|
|
|
|
lastPoseCovariance_ = cv::Mat();
|
|
|
|
syncTimer_->reset();
|
|
syncDataMutex_.unlock();
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::commonOdomCallback(
|
|
const nav_msgs::msg::Odometry::ConstSharedPtr & odomMsg,
|
|
const rtabmap_msgs::msg::UserData::ConstSharedPtr & userDataMsg,
|
|
const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr& odomInfoMsg)
|
|
{
|
|
UTimer timerConversion;
|
|
UASSERT(odomMsg.get());
|
|
std::string odomFrameId = odomMsg->header.frame_id;
|
|
if(!odomUpdate(*odomMsg, odomMsg->header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(syncTimer_->is_canceled() && syncDataMutex_.lockTry() == 0)
|
|
{
|
|
UScopeMutex lock(lastPoseMutex_);
|
|
cv::Mat userData;
|
|
if(userDataMsg.get())
|
|
{
|
|
userData = rtabmap_conversions::userDataFromROS(*userDataMsg);
|
|
UScopeMutex lock(userDataMutex_);
|
|
if(!userData_.empty())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Synchronized and asynchronized user data topics cannot be used at the same time. Async user data dropped!");
|
|
userData_ = cv::Mat();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UScopeMutex lock(userDataMutex_);
|
|
userData = userData_;
|
|
userData_ = cv::Mat();
|
|
}
|
|
|
|
syncData_.data = SensorData(
|
|
cv::Mat(),
|
|
cv::Mat(),
|
|
rtabmap::CameraModel(),
|
|
lastPoseIntermediate_?-1:0,
|
|
rtabmap_conversions::timestampFromROS(lastPoseStamp_),
|
|
userData);
|
|
|
|
OdometryInfo odomInfo;
|
|
if(odomInfoMsg.get())
|
|
{
|
|
odomInfo = rtabmap_conversions::odomInfoFromROS(*odomInfoMsg, true);
|
|
}
|
|
|
|
syncData_.valid = true;
|
|
syncData_.stamp = lastPoseStamp_;
|
|
syncData_.odom = lastPose_;
|
|
syncData_.odomVelocity = lastPoseVelocity_;
|
|
syncData_.odomFrameId = odomFrameId;
|
|
syncData_.odomCovariance = lastPoseCovariance_;
|
|
syncData_.odomInfo = odomInfo;
|
|
syncData_.timeMsgConversion = timerConversion.ticks();
|
|
|
|
if(!lastPoseIntermediate_)
|
|
{
|
|
previousStamp_ = lastPoseStamp_;
|
|
}
|
|
|
|
lastPoseCovariance_ = cv::Mat();
|
|
|
|
syncTimer_->reset();
|
|
syncDataMutex_.unlock();
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::commonSensorDataCallback(
|
|
const rtabmap_msgs::msg::SensorData::ConstSharedPtr & sensorDataMsg,
|
|
const nav_msgs::msg::Odometry::ConstSharedPtr & odomMsg,
|
|
const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr& odomInfoMsg)
|
|
{
|
|
UTimer timerConversion;
|
|
UASSERT(sensorDataMsg.get());
|
|
std::string odomFrameId;
|
|
if(odomMsg.get())
|
|
{
|
|
odomFrameId = odomMsg->header.frame_id;
|
|
if(!odomUpdate(*odomMsg, sensorDataMsg->header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mapToOdomMutex_.lock();
|
|
odomFrameId = odomFrameId_;
|
|
mapToOdomMutex_.unlock();
|
|
if(!odomTFUpdate(odomFrameId, sensorDataMsg->header.stamp))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(syncTimer_->is_canceled() && syncDataMutex_.lockTry() == 0)
|
|
{
|
|
UScopeMutex lock(lastPoseMutex_);
|
|
syncData_.data = rtabmap_conversions::sensorDataFromROS(*sensorDataMsg);
|
|
syncData_.data.setId(lastPoseIntermediate_?-1:0);
|
|
|
|
OdometryInfo odomInfo;
|
|
if(odomInfoMsg.get())
|
|
{
|
|
odomInfo = rtabmap_conversions::odomInfoFromROS(*odomInfoMsg, true);
|
|
}
|
|
|
|
syncData_.valid = true;
|
|
syncData_.stamp = lastPoseStamp_;
|
|
syncData_.odom = lastPose_;
|
|
syncData_.odomVelocity = lastPoseVelocity_;
|
|
syncData_.odomFrameId = odomFrameId;
|
|
syncData_.odomCovariance = lastPoseCovariance_;
|
|
syncData_.odomInfo = odomInfo;
|
|
syncData_.timeMsgConversion = timerConversion.ticks();
|
|
|
|
if(!lastPoseIntermediate_)
|
|
{
|
|
previousStamp_ = lastPoseStamp_;
|
|
}
|
|
|
|
lastPoseCovariance_ = cv::Mat();
|
|
|
|
syncTimer_->reset();
|
|
syncDataMutex_.unlock();
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::processAsync()
|
|
{
|
|
UScopeMutex lock(syncDataMutex_);
|
|
|
|
if(triggerNewMapBeforeNextUpdate_)
|
|
{
|
|
rtabmap_.triggerNewMap();
|
|
triggerNewMapBeforeNextUpdate_ = false;
|
|
}
|
|
|
|
if(syncData_.valid)
|
|
{
|
|
process(syncData_.stamp,
|
|
syncData_.data,
|
|
syncData_.odom,
|
|
syncData_.odomVelocity,
|
|
syncData_.odomFrameId,
|
|
syncData_.odomCovariance,
|
|
syncData_.odomInfo,
|
|
syncData_.timeMsgConversion);
|
|
syncData_.valid=false;
|
|
}
|
|
syncTimer_->cancel();
|
|
}
|
|
|
|
void CoreWrapper::process(
|
|
const rclcpp::Time & stamp,
|
|
SensorData & data,
|
|
const Transform & odom,
|
|
const std::vector<float> & odomVelocityIn,
|
|
const std::string & odomFrameId,
|
|
const cv::Mat & odomCovariance,
|
|
const OdometryInfo & odomInfo,
|
|
double timeMsgConversion)
|
|
{
|
|
UTimer timer;
|
|
if(rtabmap_.isIDsGenerated() || data.id() > 0)
|
|
{
|
|
// Add intermediate nodes?
|
|
for(std::list<std::pair<nav_msgs::msg::Odometry, rtabmap_msgs::msg::OdomInfo> >::iterator iter=interOdoms_.begin(); iter!=interOdoms_.end();)
|
|
{
|
|
if(rclcpp::Time(iter->first.header.stamp.sec, iter->first.header.stamp.nanosec) < stamp)
|
|
{
|
|
Transform interOdom;
|
|
if(!rtabmap_.getLocalOptimizedPoses().empty())
|
|
{
|
|
// add intermediate poses only if the current local graph is not empty
|
|
interOdom = rtabmap_conversions::transformFromPoseMsg(iter->first.pose.pose);
|
|
}
|
|
if(!interOdom.isNull())
|
|
{
|
|
cv::Mat covariance;
|
|
double variance = iter->first.twist.covariance[0];
|
|
if(variance == BAD_COVARIANCE || variance <= 0.0f)
|
|
{
|
|
//use the one of the pose
|
|
covariance = cv::Mat(6,6,CV_64FC1, (void*)iter->first.pose.covariance.data()).clone();
|
|
covariance /= 2.0;
|
|
}
|
|
else
|
|
{
|
|
covariance = cv::Mat(6,6,CV_64FC1, (void*)iter->first.twist.covariance.data()).clone();
|
|
}
|
|
if(!uIsFinite(covariance.at<double>(0,0)) || covariance.at<double>(0,0)<=0.0f)
|
|
{
|
|
covariance = cv::Mat::eye(6,6,CV_64FC1);
|
|
if(odomDefaultLinVariance_ > 0.0f)
|
|
{
|
|
covariance.at<double>(0,0) = odomDefaultLinVariance_;
|
|
covariance.at<double>(1,1) = odomDefaultLinVariance_;
|
|
covariance.at<double>(2,2) = odomDefaultLinVariance_;
|
|
}
|
|
if(odomDefaultAngVariance_ > 0.0f)
|
|
{
|
|
covariance.at<double>(3,3) = odomDefaultAngVariance_;
|
|
covariance.at<double>(4,4) = odomDefaultAngVariance_;
|
|
covariance.at<double>(5,5) = odomDefaultAngVariance_;
|
|
}
|
|
}
|
|
else if(twoDMapping_)
|
|
{
|
|
// If 2d mapping, make sure all diagonal values of the covariance that even not used are not null.
|
|
covariance.at<double>(2,2) = uIsFinite(covariance.at<double>(2,2)) && covariance.at<double>(2,2)!=0?covariance.at<double>(2,2):1;
|
|
covariance.at<double>(3,3) = uIsFinite(covariance.at<double>(3,3)) && covariance.at<double>(3,3)!=0?covariance.at<double>(3,3):1;
|
|
covariance.at<double>(4,4) = uIsFinite(covariance.at<double>(4,4)) && covariance.at<double>(4,4)!=0?covariance.at<double>(4,4):1;
|
|
}
|
|
|
|
SensorData interData(cv::Mat(), cv::Mat(), rtabmap::CameraModel(), -1, rtabmap_conversions::timestampFromROS(iter->first.header.stamp));
|
|
Transform gt;
|
|
if(!groundTruthFrameId_.empty())
|
|
{
|
|
gt = rtabmap_conversions::getTransform(groundTruthFrameId_, groundTruthBaseFrameId_, iter->first.header.stamp, *tfBuffer_, waitForTransform_);
|
|
}
|
|
interData.setGroundTruth(gt);
|
|
|
|
std::map<std::string, float> externalStats;
|
|
std::vector<float> odomVelocity;
|
|
if(iter->second.time_estimation != 0.0f)
|
|
{
|
|
OdometryInfo info = rtabmap_conversions::odomInfoFromROS(iter->second, true);
|
|
externalStats = rtabmap_conversions::odomInfoToStatistics(info);
|
|
|
|
if(info.interval>0.0)
|
|
{
|
|
odomVelocity.resize(6);
|
|
float x,y,z,roll,pitch,yaw;
|
|
info.transform.getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
|
|
odomVelocity[0] = x/info.interval;
|
|
odomVelocity[1] = y/info.interval;
|
|
odomVelocity[2] = z/info.interval;
|
|
odomVelocity[3] = roll/info.interval;
|
|
odomVelocity[4] = pitch/info.interval;
|
|
odomVelocity[5] = yaw/info.interval;
|
|
}
|
|
}
|
|
if(odomVelocity.empty())
|
|
{
|
|
odomVelocity.resize(6);
|
|
odomVelocity[0] = iter->first.twist.twist.linear.x;
|
|
odomVelocity[1] = iter->first.twist.twist.linear.y;
|
|
odomVelocity[2] = iter->first.twist.twist.linear.z;
|
|
odomVelocity[3] = iter->first.twist.twist.angular.x;
|
|
odomVelocity[4] = iter->first.twist.twist.angular.y;
|
|
odomVelocity[5] = iter->first.twist.twist.angular.z;
|
|
}
|
|
|
|
rtabmap_.process(interData, interOdom, covariance, odomVelocity, externalStats);
|
|
}
|
|
interOdoms_.erase(iter++);
|
|
}
|
|
else if(iter->first.header.stamp == stamp)
|
|
{
|
|
interOdoms_.erase(iter++);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Add async stuff
|
|
Transform groundTruthPose;
|
|
if(!groundTruthFrameId_.empty())
|
|
{
|
|
groundTruthPose = rtabmap_conversions::getTransform(groundTruthFrameId_, groundTruthBaseFrameId_, stamp, *tfBuffer_, waitForTransform_);
|
|
}
|
|
data.setGroundTruth(groundTruthPose);
|
|
|
|
//global pose
|
|
geometry_msgs::msg::PoseWithCovarianceStamped globalPoseMsg;
|
|
globalPoseMsg.header.stamp = rclcpp::Time(0);
|
|
{
|
|
UScopeMutex lock(globalPoseMutex_);
|
|
if(!globalPoses_.empty())
|
|
{
|
|
auto iter = rtabmap_conversions::getClosestIterator<double, geometry_msgs::msg::PoseWithCovarianceStamped>(globalPoses_, data.stamp());
|
|
// Check if it is not too old
|
|
if(rate_ == 0 || fabs(iter->first - data.stamp()) < 1.0/rate_)
|
|
{
|
|
globalPoseMsg = iter->second;
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Ignoring global pose with stamp %f because it should be inside the update period (%f) of the current data stamp (%f).",
|
|
iter->first,
|
|
1.0/rate_,
|
|
data.stamp());
|
|
}
|
|
globalPoses_.clear();
|
|
}
|
|
}
|
|
if(globalPoseMsg.header.stamp.sec != 0 || globalPoseMsg.header.stamp.nanosec != 0)
|
|
{
|
|
// assume sensor is fixed
|
|
Transform sensorToBase = rtabmap_conversions::getTransform(
|
|
globalPoseMsg.header.frame_id,
|
|
frameId_,
|
|
stamp,
|
|
*tfBuffer_,
|
|
waitForTransform_);
|
|
if(!sensorToBase.isNull())
|
|
{
|
|
Transform globalPose = rtabmap_conversions::transformFromPoseMsg(globalPoseMsg.pose.pose);
|
|
globalPose *= sensorToBase; // transform global pose from sensor frame to robot base frame
|
|
|
|
// Correction of the global pose accounting the odometry movement since we received it
|
|
Transform correction = rtabmap_conversions::getMovingTransform(
|
|
frameId_,
|
|
odomFrameId,
|
|
stamp,
|
|
rclcpp::Time(globalPoseMsg.header.stamp.sec, globalPoseMsg.header.stamp.nanosec),
|
|
*tfBuffer_,
|
|
waitForTransform_);
|
|
if(!correction.isNull())
|
|
{
|
|
globalPose *= correction;
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Could not adjust global pose accordingly to latest odometry pose. "
|
|
"If odometry is small since it received the global pose and "
|
|
"covariance is large, this should not be a problem.");
|
|
}
|
|
cv::Mat globalPoseCovariance = cv::Mat(6,6, CV_64FC1, (void*)globalPoseMsg.pose.covariance.data()).clone();
|
|
data.setGlobalPose(globalPose, globalPoseCovariance);
|
|
}
|
|
}
|
|
|
|
{
|
|
UScopeMutex lock(gpsMutex_);
|
|
if(!gps_.empty())
|
|
{
|
|
std::map<double, rtabmap::GPS>::const_iterator iter = rtabmap_conversions::getClosestIterator<double, rtabmap::GPS>(gps_, data.stamp());
|
|
// Check if it is not too old
|
|
if(rate_ == 0 || fabs(iter->first - data.stamp()) < 1.0/rate_)
|
|
{
|
|
data.setGPS(iter->second);
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Ignoring GPS with stamp %f because it should be inside the update period (%f) of the current data stamp (%f).",
|
|
iter->first,
|
|
1.0/rate_,
|
|
data.stamp());
|
|
}
|
|
gps_.clear();
|
|
}
|
|
}
|
|
|
|
|
|
//tag detections
|
|
landmarksMutex_.lock();
|
|
Landmarks landmarks = rtabmap_conversions::landmarksFromROS(
|
|
landmarks_,
|
|
frameId_,
|
|
odomFrameId,
|
|
stamp,
|
|
*tfBuffer_,
|
|
waitForTransform_,
|
|
landmarkDefaultLinVariance_,
|
|
landmarkDefaultAngVariance_);
|
|
landmarks_.clear();
|
|
landmarksMutex_.unlock();
|
|
if(!landmarks.empty())
|
|
{
|
|
data.setLandmarks(landmarks);
|
|
}
|
|
|
|
// IMU
|
|
imuMutex_.lock();
|
|
if(!imus_.empty())
|
|
{
|
|
Transform t = Transform::getTransform(imus_, data.stamp());
|
|
if(!t.isNull())
|
|
{
|
|
imuMutex_.unlock();
|
|
// get local transform
|
|
rtabmap::Transform localTransform;
|
|
if(frameId_.compare(imuFrameId_) != 0)
|
|
{
|
|
localTransform = rtabmap_conversions::getTransform(frameId_, imuFrameId_, rtabmap_conversions::timestampToROS(data.stamp()), *tfBuffer_, waitForTransform_);
|
|
}
|
|
else
|
|
{
|
|
localTransform = rtabmap::Transform::getIdentity();
|
|
}
|
|
|
|
if(!localTransform.isNull())
|
|
{
|
|
Eigen::Quaterniond q = t.getQuaterniond();
|
|
data.setIMU(IMU(cv::Vec4d(q.x(), q.y(), q.z(), q.w()), cv::Mat::eye(3,3,CV_64FC1),
|
|
cv::Vec3d(), cv::Mat(),
|
|
cv::Vec3d(), cv::Mat(),
|
|
localTransform));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "We are receiving imu data (buffer=%d), but cannot interpolate "
|
|
"imu transform at time %f (latest imu received with stamp %f). IMU won't be added to graph.",
|
|
(int)imus_.size(), data.stamp(), imus_.rbegin()->first);
|
|
imuMutex_.unlock();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
imuMutex_.unlock();
|
|
}
|
|
|
|
double timeRtabmap = 0.0;
|
|
double timeUpdateMaps = 0.0;
|
|
double timePublishMaps = 0.0;
|
|
|
|
cv::Mat covariance = odomCovariance;
|
|
if(covariance.empty() || !uIsFinite(covariance.at<double>(0,0)) || covariance.at<double>(0,0)<=0.0f)
|
|
{
|
|
covariance = cv::Mat::eye(6,6,CV_64FC1);
|
|
if(odomDefaultLinVariance_ > 0.0f)
|
|
{
|
|
covariance.at<double>(0,0) = odomDefaultLinVariance_;
|
|
covariance.at<double>(1,1) = odomDefaultLinVariance_;
|
|
covariance.at<double>(2,2) = odomDefaultLinVariance_;
|
|
}
|
|
if(odomDefaultAngVariance_ > 0.0f)
|
|
{
|
|
covariance.at<double>(3,3) = odomDefaultAngVariance_;
|
|
covariance.at<double>(4,4) = odomDefaultAngVariance_;
|
|
covariance.at<double>(5,5) = odomDefaultAngVariance_;
|
|
}
|
|
}
|
|
else if(twoDMapping_)
|
|
{
|
|
// If 2d mapping, make sure all diagonal values of the covariance that even not used are not null.
|
|
covariance.at<double>(2,2) = uIsFinite(covariance.at<double>(2,2)) && covariance.at<double>(2,2)!=0?covariance.at<double>(2,2):1;
|
|
covariance.at<double>(3,3) = uIsFinite(covariance.at<double>(3,3)) && covariance.at<double>(3,3)!=0?covariance.at<double>(3,3):1;
|
|
covariance.at<double>(4,4) = uIsFinite(covariance.at<double>(4,4)) && covariance.at<double>(4,4)!=0?covariance.at<double>(4,4):1;
|
|
}
|
|
|
|
std::map<std::string, float> externalStats;
|
|
std::vector<float> odomVelocity;
|
|
if(odomInfo.timeEstimation != 0.0f)
|
|
{
|
|
externalStats = rtabmap_conversions::odomInfoToStatistics(odomInfo);
|
|
|
|
if(odomInfo.interval>0.0)
|
|
{
|
|
odomVelocity.resize(6);
|
|
float x,y,z,roll,pitch,yaw;
|
|
odomInfo.transform.getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
|
|
odomVelocity[0] = x/odomInfo.interval;
|
|
odomVelocity[1] = y/odomInfo.interval;
|
|
odomVelocity[2] = z/odomInfo.interval;
|
|
odomVelocity[3] = roll/odomInfo.interval;
|
|
odomVelocity[4] = pitch/odomInfo.interval;
|
|
odomVelocity[5] = yaw/odomInfo.interval;
|
|
}
|
|
}
|
|
if(odomVelocity.empty())
|
|
{
|
|
odomVelocity = odomVelocityIn;
|
|
}
|
|
if(rtabmapROSStats_.size())
|
|
{
|
|
externalStats.insert(rtabmapROSStats_.begin(), rtabmapROSStats_.end());
|
|
rtabmapROSStats_.clear();
|
|
}
|
|
|
|
timeMsgConversion += timer.ticks();
|
|
if(rtabmap_.process(data, odom, covariance, odomVelocity, externalStats))
|
|
{
|
|
timeRtabmap = timer.ticks();
|
|
mapToOdomMutex_.lock();
|
|
mapToOdom_ = rtabmap_.getMapCorrection();
|
|
Transform mapToOdomSafe = mapToOdom_.clone();
|
|
if(!odomFrameId.empty() && !odomFrameId_.empty() && odomFrameId_.compare(odomFrameId)!=0)
|
|
{
|
|
RCLCPP_ERROR(get_logger(), "Odometry received doesn't have same frame_id "
|
|
"than the one previously set (old=%s, new=%s). "
|
|
"Are there multiple nodes publishing on same odometry topic name? "
|
|
"The new frame_id is now used.", odomFrameId_.c_str(), odomFrameId.c_str());
|
|
}
|
|
|
|
odomFrameId_ = odomFrameId;
|
|
mapToOdomMutex_.unlock();
|
|
|
|
if(data.id() < 0)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Intermediate node added");
|
|
}
|
|
else
|
|
{
|
|
if(localizationPosePub_->get_subscription_count())
|
|
{
|
|
bool localized = rtabmap_.getStatistics().loopClosureId()!=0 ||
|
|
rtabmap_.getStatistics().proximityDetectionId()!=0 ||
|
|
static_cast<int>(uValue(rtabmap_.getStatistics().data(), rtabmap::Statistics::kLoopLandmark_detected(), 0.0f))!=0;
|
|
|
|
if(localized || !pubLocPoseOnlyWhenLocalizing_)
|
|
{
|
|
geometry_msgs::msg::PoseWithCovarianceStamped poseMsg;
|
|
poseMsg.header.frame_id = mapFrameId_;
|
|
poseMsg.header.stamp = stamp;
|
|
rtabmap_conversions::transformToPoseMsg(mapToOdomSafe*odom, poseMsg.pose.pose);
|
|
if(!rtabmap_.getStatistics().localizationCovariance().empty())
|
|
{
|
|
const cv::Mat & cov = rtabmap_.getStatistics().localizationCovariance();
|
|
memcpy(poseMsg.pose.covariance.data(), cov.data, cov.total()*sizeof(double));
|
|
}
|
|
else
|
|
{
|
|
// Not yet localized, publish large covariance
|
|
poseMsg.pose.covariance.data()[0] = 9999;
|
|
poseMsg.pose.covariance.data()[7] = 9999;
|
|
poseMsg.pose.covariance.data()[14] = twoDMapping_?rtabmap::Registration::COVARIANCE_LINEAR_EPSILON:9999;
|
|
poseMsg.pose.covariance.data()[21] = twoDMapping_?rtabmap::Registration::COVARIANCE_ANGULAR_EPSILON:9999;
|
|
poseMsg.pose.covariance.data()[28] = twoDMapping_?rtabmap::Registration::COVARIANCE_ANGULAR_EPSILON:9999;
|
|
poseMsg.pose.covariance.data()[35] = 9999;
|
|
}
|
|
localizationPosePub_->publish(poseMsg);
|
|
}
|
|
}
|
|
std::map<int, rtabmap::Transform> filteredPoses(rtabmap_.getLocalOptimizedPoses().lower_bound(1), rtabmap_.getLocalOptimizedPoses().end());
|
|
|
|
// create a tmp signature with latest sensory data if latest signature was ignored
|
|
std::map<int, rtabmap::Signature> tmpSignature;
|
|
if(rtabmap_.getMemory() == 0 ||
|
|
filteredPoses.size() == 0 ||
|
|
rtabmap_.getMemory()->getLastSignatureId() != filteredPoses.rbegin()->first ||
|
|
rtabmap_.getMemory()->getLastWorkingSignature() == 0 ||
|
|
rtabmap_.getMemory()->getLastWorkingSignature()->sensorData().gridCellSize() == 0 ||
|
|
(!mapsManager_.getLocalMapMaker()->isGridFromDepth() && data.laserScanRaw().is2d())) // 2d laser scan would fill empty space for latest data
|
|
{
|
|
SensorData tmpData = data;
|
|
tmpData.setId(0);
|
|
tmpSignature.insert(std::make_pair(0, Signature(0, -1, 0, data.stamp(), "", odom, Transform(), tmpData)));
|
|
filteredPoses.insert(std::make_pair(0, mapToOdomSafe*odom));
|
|
}
|
|
|
|
if((mappingMaxNodes_ > 0 || mappingAltitudeDelta_>0.0) && filteredPoses.size()>1)
|
|
{
|
|
std::map<int, Transform> nearestPoses = filterNodesToAssemble(filteredPoses, mapToOdomSafe*odom);
|
|
//add latest/zero and make sure those on a planned path are not filtered
|
|
std::set<int> onPath;
|
|
if(rtabmap_.getPath().size())
|
|
{
|
|
std::vector<int> nextNodes = rtabmap_.getPathNextNodes();
|
|
onPath.insert(nextNodes.begin(), nextNodes.end());
|
|
}
|
|
for(std::map<int, Transform>::iterator iter=filteredPoses.begin(); iter!=filteredPoses.end(); ++iter)
|
|
{
|
|
if(iter->first == 0 || onPath.find(iter->first) != onPath.end())
|
|
{
|
|
nearestPoses.insert(*iter);
|
|
}
|
|
else if(onPath.empty())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
filteredPoses = nearestPoses;
|
|
}
|
|
|
|
// Update maps
|
|
filteredPoses = mapsManager_.updateMapCaches(
|
|
filteredPoses,
|
|
rtabmap_.getMemory(),
|
|
false,
|
|
false,
|
|
tmpSignature);
|
|
|
|
timeUpdateMaps = timer.ticks();
|
|
|
|
mapsManager_.publishMaps(filteredPoses, stamp, mapFrameId_);
|
|
|
|
// Publish local graph, info
|
|
this->publishStats(stamp);
|
|
|
|
// update goal if planning is enabled
|
|
if(!currentMetricGoal_.isNull())
|
|
{
|
|
if(rtabmap_.getPath().size() == 0)
|
|
{
|
|
// Don't send status yet if nav2 actionlib is used unless it failed,
|
|
// let nav2 finish reaching the goal
|
|
#ifdef WITH_NAV2_MSGS
|
|
if(nav2Client_ == 0 || rtabmap_.getPathStatus() <= 0)
|
|
#else
|
|
if(rtabmap_.getPathStatus() <= 0)
|
|
#endif
|
|
{
|
|
if(rtabmap_.getPathStatus() > 0)
|
|
{
|
|
// Goal reached
|
|
RCLCPP_INFO(this->get_logger(), "Planning: Publishing goal reached!");
|
|
}
|
|
else if(rtabmap_.getPathStatus() <= 0)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Planning: Plan failed!");
|
|
#ifdef WITH_NAV2_MSGS
|
|
if(nav2Client_.get()!=NULL && nav2Client_->action_server_is_ready())
|
|
{
|
|
nav2Client_->async_cancel_all_goals();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if(goalReachedPub_->get_subscription_count())
|
|
{
|
|
std_msgs::msg::Bool result;
|
|
result.data = rtabmap_.getPathStatus() > 0;
|
|
goalReachedPub_->publish(result);
|
|
}
|
|
currentMetricGoal_.setNull();
|
|
lastPublishedMetricGoal_.setNull();
|
|
goalFrameId_.clear();
|
|
latestNodeWasReached_ = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
currentMetricGoal_ = rtabmap_.getPose(rtabmap_.getPathCurrentGoalId());
|
|
if(!currentMetricGoal_.isNull())
|
|
{
|
|
// Adjust the target pose relative to last node
|
|
if(rtabmap_.getPathCurrentGoalId() == rtabmap_.getPath().back().first && rtabmap_.getLocalOptimizedPoses().size())
|
|
{
|
|
if(latestNodeWasReached_ ||
|
|
rtabmap_.getLastLocalizationPose().getDistance(currentMetricGoal_) < rtabmap_.getLocalRadius())
|
|
{
|
|
latestNodeWasReached_ = true;
|
|
Transform goalLocalTransform = Transform::getIdentity();
|
|
if(!goalFrameId_.empty() && goalFrameId_.compare(frameId_) != 0)
|
|
{
|
|
Transform localT = rtabmap_conversions::getTransform(frameId_, goalFrameId_, now(), *tfBuffer_, waitForTransform_);
|
|
if(!localT.isNull())
|
|
{
|
|
goalLocalTransform = localT.inverse().to3DoF();
|
|
}
|
|
}
|
|
currentMetricGoal_ *= rtabmap_.getPathTransformToGoal()*goalLocalTransform;
|
|
}
|
|
}
|
|
|
|
// publish next goal with updated currentMetricGoal_
|
|
publishCurrentGoal(stamp);
|
|
|
|
// publish local path
|
|
publishLocalPath(stamp);
|
|
|
|
// publish global path
|
|
publishGlobalPath(stamp);
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Planning: Local map broken, current goal id=%d (the robot may have moved to far from planned nodes)",
|
|
rtabmap_.getPathCurrentGoalId());
|
|
rtabmap_.clearPath(-1);
|
|
if(goalReachedPub_->get_subscription_count())
|
|
{
|
|
std_msgs::msg::Bool result;
|
|
result.data = false;
|
|
goalReachedPub_->publish(result);
|
|
}
|
|
currentMetricGoal_.setNull();
|
|
lastPublishedMetricGoal_.setNull();
|
|
goalFrameId_.clear();
|
|
latestNodeWasReached_ = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
timePublishMaps = timer.ticks();
|
|
}
|
|
|
|
// If not intermediate node
|
|
if(data.id() >= 0)
|
|
{
|
|
localizationDiagnostic_.updateStatus(rtabmap_.getStatistics().localizationCovariance(), twoDMapping_);
|
|
tick(stamp, rate_>0?rate_:1000.0/(timeMsgConversion+timeRtabmap+timeUpdateMaps+timePublishMaps));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
timeRtabmap = timer.ticks();
|
|
}
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap (%d): Rate=%.2fs, Limit=%.3fs, Conversion=%.4fs, RTAB-Map=%.4fs, Maps update=%.4fs pub=%.4fs delay=%.4fs (local map=%d, WM=%d)",
|
|
rtabmap_.getLastLocationId(),
|
|
rate_>0?1.0f/rate_:0,
|
|
rtabmap_.getTimeThreshold()/1000.0f,
|
|
timeMsgConversion,
|
|
timeRtabmap,
|
|
timeUpdateMaps,
|
|
timePublishMaps,
|
|
(now() - stamp).seconds(),
|
|
(int)rtabmap_.getLocalOptimizedPoses().size(),
|
|
rtabmap_.getWMSize()+rtabmap_.getSTMSize());
|
|
rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/HasSubscribers/"), mapsManager_.hasSubscribers()?1:0));
|
|
rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/TimeMsgConversion/ms"), timeMsgConversion*1000.0f));
|
|
rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/TimeRtabmap/ms"), timeRtabmap*1000.0f));
|
|
rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/TimeUpdatingMaps/ms"), timeUpdateMaps*1000.0f));
|
|
rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/TimePublishing/ms"), timePublishMaps*1000.0f));
|
|
rtabmapROSStats_.insert(std::make_pair(std::string("RtabmapROS/TimeTotal/ms"), (timeMsgConversion+timeRtabmap+timeUpdateMaps+timePublishMaps)*1000.0f));
|
|
}
|
|
else if(!rtabmap_.isIDsGenerated())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Ignoring received image because its sequence ID=0. Please "
|
|
"set \"Mem/GenerateIds\"=\"true\" to ignore ros generated sequence id. "
|
|
"Use only \"Mem/GenerateIds\"=\"false\" for once-time run of RTAB-Map and "
|
|
"when you need to have IDs output of RTAB-map synchronized with the source "
|
|
"image sequence ID.");
|
|
}
|
|
}
|
|
|
|
std::map<int, Transform> CoreWrapper::filterNodesToAssemble(
|
|
const std::map<int, Transform> & nodes,
|
|
const Transform & currentPose)
|
|
{
|
|
std::map<int, Transform> output;
|
|
if(mappingMaxNodes_ > 0)
|
|
{
|
|
std::map<int, float> nodesDist = graph::findNearestNodes(currentPose, nodes, 0, 0, mappingMaxNodes_);
|
|
for(std::map<int, float>::iterator iter=nodesDist.begin(); iter!=nodesDist.end(); ++iter)
|
|
{
|
|
if(mappingAltitudeDelta_<=0.0 ||
|
|
fabs(nodes.at(iter->first).z()-currentPose.z())<mappingAltitudeDelta_)
|
|
{
|
|
output.insert(*nodes.find(iter->first));
|
|
}
|
|
}
|
|
}
|
|
else // mappingAltitudeDelta_>0.0
|
|
{
|
|
for(std::map<int, Transform>::const_iterator iter=nodes.begin(); iter!=nodes.end(); ++iter)
|
|
{
|
|
if(fabs(iter->second.z()-currentPose.z())<mappingAltitudeDelta_)
|
|
{
|
|
output.insert(*iter);
|
|
}
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
void CoreWrapper::userDataAsyncCallback(const rtabmap_msgs::msg::UserData::SharedPtr dataMsg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
UScopeMutex lock(userDataMutex_);
|
|
static bool warningShow = false;
|
|
if(!userData_.empty() && !warningShow)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Overwriting previous user data set. When asynchronous user "
|
|
"data input topic rate is higher than "
|
|
"map update rate (current %s=%f), only latest data is saved "
|
|
"in the next node created. This message will is shown only once.",
|
|
Parameters::kRtabmapDetectionRate().c_str(), rate_);
|
|
warningShow = true;
|
|
}
|
|
userData_ = rtabmap_conversions::userDataFromROS(*dataMsg);
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::globalPoseAsyncCallback(const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr globalPoseMsg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
UScopeMutex lock(globalPoseMutex_);
|
|
globalPoses_.insert(std::make_pair(rtabmap_conversions::timestampFromROS(globalPoseMsg->header.stamp), *globalPoseMsg));
|
|
if(globalPoses_.size() > 1000)
|
|
{
|
|
globalPoses_.erase(globalPoses_.begin());
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::gpsFixAsyncCallback(const sensor_msgs::msg::NavSatFix::SharedPtr gpsFixMsg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
double error = 10.0;
|
|
if(gpsFixMsg->position_covariance_type != sensor_msgs::msg::NavSatFix::COVARIANCE_TYPE_UNKNOWN)
|
|
{
|
|
double variance = uMax3(gpsFixMsg->position_covariance.at(0), gpsFixMsg->position_covariance.at(4), gpsFixMsg->position_covariance.at(8));
|
|
if(variance>0.0)
|
|
{
|
|
error = sqrt(variance);
|
|
}
|
|
}
|
|
|
|
rtabmap::GPS gps(
|
|
rtabmap_conversions::timestampFromROS(gpsFixMsg->header.stamp),
|
|
gpsFixMsg->longitude,
|
|
gpsFixMsg->latitude,
|
|
gpsFixMsg->altitude,
|
|
error,
|
|
0);
|
|
|
|
UScopeMutex lock(gpsMutex_);
|
|
gps_.insert(std::make_pair(gps.stamp(), gps));
|
|
if(gps_.size() > 1000)
|
|
{
|
|
gps_.erase(gps_.begin());
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::landmarkDetectionAsyncCallback(const rtabmap_msgs::msg::LandmarkDetection::SharedPtr landmarkDetection)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
geometry_msgs::msg::PoseWithCovarianceStamped p;
|
|
p.header = landmarkDetection->header;
|
|
p.pose = landmarkDetection->pose;
|
|
UScopeMutex lock(landmarksMutex_);
|
|
uInsert(landmarks_,
|
|
std::make_pair(landmarkDetection->id,
|
|
std::make_pair(p, landmarkDetection->size)));
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::landmarkDetectionsAsyncCallback(const rtabmap_msgs::msg::LandmarkDetections::SharedPtr landmarkDetections)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
UScopeMutex lock(landmarksMutex_);
|
|
for(unsigned int i=0; i<landmarkDetections->landmarks.size(); ++i)
|
|
{
|
|
geometry_msgs::msg::PoseWithCovarianceStamped p;
|
|
p.header = landmarkDetections->landmarks[i].header;
|
|
p.pose = landmarkDetections->landmarks[i].pose;
|
|
uInsert(landmarks_,
|
|
std::make_pair(landmarkDetections->landmarks[i].id,
|
|
std::make_pair(p, landmarkDetections->landmarks[i].size)));
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef WITH_APRILTAG_MSGS
|
|
void CoreWrapper::tagDetectionsAsyncCallback(const apriltag_msgs::msg::AprilTagDetectionArray::SharedPtr msg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
static bool warningShow = false;
|
|
if(!warningShow) {
|
|
RCLCPP_WARN(this->get_logger(), "\"tag_detections\" input topic name for apriltag_msgs is deprecated, remap \"apriltag\" input topic name instead. This message is only printed once.");
|
|
warningShow = true;
|
|
}
|
|
apriltagAsyncCallback(msg);
|
|
}
|
|
}
|
|
void CoreWrapper::apriltagAsyncCallback(const apriltag_msgs::msg::AprilTagDetectionArray::SharedPtr msg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
UScopeMutex lock(landmarksMutex_);
|
|
for(unsigned int i=0; i<msg->detections.size(); ++i)
|
|
{
|
|
std::string tagFrameId = msg->detections[i].family+":"+uNumber2Str(msg->detections[i].id);
|
|
Transform camToTag = rtabmap_conversions::getTransform(
|
|
msg->header.frame_id, // e.g., camera_optical_frame
|
|
tagFrameId, // e.g., tag36h11:42
|
|
msg->header.stamp,
|
|
*tfBuffer_,
|
|
waitForTransform_);
|
|
if(camToTag.isNull())
|
|
{
|
|
RCLCPP_WARN(get_logger(), "Could not get TF between %s and %s frames for tag detection %d.",
|
|
frameId_.c_str(),
|
|
tagFrameId.c_str(),
|
|
msg->detections[i].id);
|
|
continue;
|
|
}
|
|
|
|
geometry_msgs::msg::PoseWithCovarianceStamped p;
|
|
rtabmap_conversions::transformToPoseMsg(camToTag, p.pose.pose);
|
|
p.header = msg->header;
|
|
|
|
uInsert(landmarks_,
|
|
std::make_pair(msg->detections[i].id,
|
|
std::make_pair(p, 0.0f)));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_ARUCO_MSGS
|
|
void CoreWrapper::arucoAsyncCallback(const aruco_msgs::msg::MarkerArray::SharedPtr msg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
UScopeMutex lock(landmarksMutex_);
|
|
for(unsigned int i=0; i<msg->markers.size(); ++i)
|
|
{
|
|
geometry_msgs::msg::PoseWithCovarianceStamped p;
|
|
p.pose = msg->markers[i].pose;
|
|
p.header = msg->markers[i].header;
|
|
|
|
uInsert(landmarks_,
|
|
std::make_pair((int)msg->markers[i].id,
|
|
std::make_pair(p, 0.0f)));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_ARUCO_OPENCV_MSGS
|
|
void CoreWrapper::arucoOpencvAsyncCallback(const aruco_opencv_msgs::msg::ArucoDetection::SharedPtr msg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
UScopeMutex lock(landmarksMutex_);
|
|
for(unsigned int i=0; i<msg->markers.size(); ++i)
|
|
{
|
|
geometry_msgs::msg::PoseWithCovarianceStamped p;
|
|
p.pose.pose = msg->markers[i].pose;
|
|
p.header = msg->header;
|
|
|
|
uInsert(landmarks_,
|
|
std::make_pair((int)msg->markers[i].marker_id,
|
|
std::make_pair(p, 0.0f)));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_ARUCO_MARKERS_MSGS
|
|
void CoreWrapper::arucoMarkersAsyncCallback(const aruco_markers_msgs::msg::MarkerArray::SharedPtr msg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
UScopeMutex lock(landmarksMutex_);
|
|
for(unsigned int i=0; i<msg->markers.size(); ++i)
|
|
{
|
|
geometry_msgs::msg::PoseWithCovarianceStamped p;
|
|
p.pose.pose = msg->markers[i].pose.pose;
|
|
p.header = msg->markers[i].pose.header;
|
|
|
|
uInsert(landmarks_,
|
|
std::make_pair((int)msg->markers[i].id,
|
|
std::make_pair(p, 0.0f)));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_ROS2_ARUCO_INTERFACES
|
|
void CoreWrapper::arucoInterfacesAsyncCallback(const ros2_aruco_interfaces::msg::ArucoMarkers::SharedPtr msg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
UScopeMutex lock(landmarksMutex_);
|
|
UASSERT(msg->marker_ids.size() == msg->poses.size());
|
|
for(unsigned int i=0; i<msg->marker_ids.size(); ++i)
|
|
{
|
|
geometry_msgs::msg::PoseWithCovarianceStamped p;
|
|
p.pose.pose = msg->poses[i];
|
|
p.header = msg->header;
|
|
|
|
uInsert(landmarks_,
|
|
std::make_pair((int)msg->marker_ids[i],
|
|
std::make_pair(p, 0.0f)));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_FIDUCIAL_MSGS
|
|
void CoreWrapper::fiducialDetectionsAsyncCallback(const fiducial_msgs::msg::FiducialTransformArray::SharedPtr fiducialDetections)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
UScopeMutex lock(landmarksMutex_);
|
|
for(unsigned int i=0; i<fiducialDetections.transforms.size(); ++i)
|
|
{
|
|
geometry_msgs::PoseWithCovarianceStamped p;
|
|
p.pose.pose.orientation = fiducialDetections.transforms[i].transform.rotation;
|
|
p.pose.pose.position.x = fiducialDetections.transforms[i].transform.translation.x;
|
|
p.pose.pose.position.y = fiducialDetections.transforms[i].transform.translation.y;
|
|
p.pose.pose.position.z = fiducialDetections.transforms[i].transform.translation.z;
|
|
p.header = fiducialDetections.header;
|
|
uInsert(landmarks_,
|
|
std::make_pair(fiducialDetections.transforms[i].fiducial_id,
|
|
std::make_pair(p, 0.0f)));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CoreWrapper::imuAsyncCallback(const sensor_msgs::msg::Imu::SharedPtr msg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
if(msg->orientation.x == 0 && msg->orientation.y == 0 && msg->orientation.z == 0 && msg->orientation.w == 0)
|
|
{
|
|
UERROR("IMU received doesn't have orientation set, it is ignored.");
|
|
}
|
|
else
|
|
{
|
|
UScopeMutex lock(imuMutex_);
|
|
Transform orientation(0,0,0, msg->orientation.x, msg->orientation.y, msg->orientation.z, msg->orientation.w);
|
|
imus_.insert(std::make_pair(rtabmap_conversions::timestampFromROS(msg->header.stamp), orientation));
|
|
if(imus_.size() > 1000)
|
|
{
|
|
imus_.erase(imus_.begin());
|
|
}
|
|
if(!imuFrameId_.empty() && imuFrameId_.compare(msg->header.frame_id) != 0)
|
|
{
|
|
RCLCPP_ERROR(get_logger(), "IMU frame_id has changed from %s to %s! Are "
|
|
"multiple nodes publishing "
|
|
"on same topic %s? IMU buffer is cleared!",
|
|
imuFrameId_.c_str(),
|
|
msg->header.frame_id.c_str(),
|
|
imuSub_->get_topic_name());
|
|
imus_.clear();
|
|
imuFrameId_.clear();
|
|
}
|
|
else
|
|
{
|
|
imuFrameId_ = msg->header.frame_id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::republishNodeDataCallback(const std_msgs::msg::Int32MultiArray::ConstSharedPtr msg)
|
|
{
|
|
rtabmap_.addNodesToRepublish(msg->data);
|
|
}
|
|
|
|
void CoreWrapper::interOdomCallback(const nav_msgs::msg::Odometry::SharedPtr msg)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
interOdoms_.push_back(std::make_pair(*msg, rtabmap_msgs::msg::OdomInfo()));
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::interOdomInfoCallback(const nav_msgs::msg::Odometry::ConstSharedPtr & msg1, const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr & msg2)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
interOdoms_.push_back(std::make_pair(*msg1, *msg2));
|
|
}
|
|
}
|
|
|
|
|
|
void CoreWrapper::initialPoseCallback(const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr msg)
|
|
{
|
|
Transform mapToPose = Transform::getIdentity();
|
|
if(msg->header.frame_id.empty())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Received initialpose doesn't have frame_id set, assuming it is in %s frame.", mapFrameId_.c_str());
|
|
}
|
|
else if(msg->header.frame_id != mapFrameId_)
|
|
{
|
|
mapToPose = rtabmap_conversions::getTransform(mapFrameId_, msg->header.frame_id, msg->header.stamp, *tfBuffer_, waitForTransform_);
|
|
if(mapToPose.isNull())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Failed to transform initialpose from frame %s to map frame %s", msg->header.frame_id.c_str(), mapFrameId_.c_str());
|
|
return;
|
|
}
|
|
}
|
|
|
|
Transform initialPose = rtabmap_conversions::transformFromPoseMsg(msg->pose.pose);
|
|
if(initialPose.isNull())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "initialpose received is null!");
|
|
return;
|
|
}
|
|
if(mapToPose.isIdentity())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "initialpose received: %s", initialPose.prettyPrint().c_str());
|
|
rtabmap_.setInitialPose(initialPose);
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "initialpose received: %s in %s frame, transformed to %s in %s frame.",
|
|
initialPose.prettyPrint().c_str(),
|
|
msg->header.frame_id.c_str(),
|
|
(mapToPose * initialPose).prettyPrint().c_str(),
|
|
mapFrameId_.c_str());
|
|
rtabmap_.setInitialPose(mapToPose*initialPose);
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::goalCommonCallback(
|
|
int id,
|
|
const std::string & label,
|
|
const std::string & frameId,
|
|
const Transform & pose,
|
|
const rclcpp::Time & stamp,
|
|
double * planningTime)
|
|
{
|
|
UTimer timer;
|
|
|
|
if(id == 0 && !label.empty() && rtabmap_.getMemory())
|
|
{
|
|
id = rtabmap_.getMemory()->getSignatureIdByLabel(label);
|
|
}
|
|
|
|
if(id > 0)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Planning: set goal to node %d", id);
|
|
}
|
|
else if(id < 0)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Planning: set goal to landmark %d", id);
|
|
}
|
|
else if(!pose.isNull())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Planning: set goal %s", pose.prettyPrint().c_str());
|
|
}
|
|
|
|
if(planningTime)
|
|
{
|
|
*planningTime = 0.0;
|
|
}
|
|
|
|
bool success = false;
|
|
if((id != 0 && rtabmap_.computePath(id, true)) ||
|
|
(!pose.isNull() && rtabmap_.computePath(pose)))
|
|
{
|
|
if(planningTime)
|
|
{
|
|
*planningTime = timer.elapsed();
|
|
}
|
|
RCLCPP_INFO(this->get_logger(), "Planning: Time computing path = %f s", timer.ticks());
|
|
const std::vector<std::pair<int, Transform> > & poses = rtabmap_.getPath();
|
|
|
|
currentMetricGoal_.setNull();
|
|
lastPublishedMetricGoal_.setNull();
|
|
goalFrameId_.clear();
|
|
latestNodeWasReached_ = false;
|
|
if(poses.size() == 0)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Planning: Goal already reached (RGBD/GoalReachedRadius=%fm).",
|
|
rtabmap_.getGoalReachedRadius());
|
|
rtabmap_.clearPath(1);
|
|
if(goalReachedPub_->get_subscription_count())
|
|
{
|
|
std_msgs::msg::Bool result;
|
|
result.data = true;
|
|
goalReachedPub_->publish(result);
|
|
}
|
|
success = true;
|
|
}
|
|
else
|
|
{
|
|
currentMetricGoal_ = rtabmap_.getPose(rtabmap_.getPathCurrentGoalId());
|
|
if(!currentMetricGoal_.isNull())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Planning: Path successfully created (size=%d)", (int)poses.size());
|
|
goalFrameId_ = frameId;
|
|
|
|
// Adjust the target pose relative to last node
|
|
if(rtabmap_.getPathCurrentGoalId() == rtabmap_.getPath().back().first && rtabmap_.getLocalOptimizedPoses().size())
|
|
{
|
|
if(rtabmap_.getLastLocalizationPose().getDistance(currentMetricGoal_) < rtabmap_.getLocalRadius())
|
|
{
|
|
latestNodeWasReached_ = true;
|
|
Transform goalLocalTransform = Transform::getIdentity();
|
|
if(!goalFrameId_.empty() && goalFrameId_.compare(frameId_) != 0)
|
|
{
|
|
Transform localT = rtabmap_conversions::getTransform(frameId_, goalFrameId_, stamp, *tfBuffer_, waitForTransform_);
|
|
if(!localT.isNull())
|
|
{
|
|
goalLocalTransform = localT.inverse().to3DoF();
|
|
}
|
|
}
|
|
currentMetricGoal_ *= rtabmap_.getPathTransformToGoal() * goalLocalTransform;
|
|
}
|
|
}
|
|
|
|
publishCurrentGoal(stamp);
|
|
publishLocalPath(stamp);
|
|
publishGlobalPath(stamp);
|
|
|
|
// Just output the path on screen
|
|
std::stringstream stream;
|
|
for(std::vector<std::pair<int, Transform> >::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
|
|
{
|
|
if(iter != poses.begin())
|
|
{
|
|
stream << " ";
|
|
}
|
|
stream << iter->first;
|
|
}
|
|
RCLCPP_INFO(this->get_logger(), "Global path: [%s]", stream.str().c_str());
|
|
success=true;
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Pose of node %d not found!? Cannot send a metric goal...", rtabmap_.getPathCurrentGoalId());
|
|
}
|
|
}
|
|
}
|
|
else if(!label.empty())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Planning: Node with label \"%s\" not found!", label.c_str());
|
|
}
|
|
else if(pose.isNull())
|
|
{
|
|
if(id > 0)
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "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);
|
|
}
|
|
else if(id < 0)
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "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);
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Planning: Node id should be > 0 !");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "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());
|
|
}
|
|
|
|
if(!success)
|
|
{
|
|
rtabmap_.clearPath(-1);
|
|
if(goalReachedPub_->get_subscription_count())
|
|
{
|
|
std_msgs::msg::Bool result;
|
|
result.data = false;
|
|
goalReachedPub_->publish(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::goalCallback(const geometry_msgs::msg::PoseStamped::SharedPtr msg)
|
|
{
|
|
Transform targetPose = rtabmap_conversions::transformFromPoseMsg(msg->pose, true);
|
|
|
|
// transform goal in /map frame
|
|
if(!msg->header.frame_id.empty() && mapFrameId_.compare(msg->header.frame_id) != 0)
|
|
{
|
|
Transform t = rtabmap_conversions::getTransform(mapFrameId_, msg->header.frame_id, msg->header.stamp, *tfBuffer_, waitForTransform_);
|
|
if(t.isNull())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Cannot transform goal pose from \"%s\" frame to \"%s\" frame!",
|
|
msg->header.frame_id.c_str(), mapFrameId_.c_str());
|
|
if(goalReachedPub_->get_subscription_count())
|
|
{
|
|
std_msgs::msg::Bool result;
|
|
result.data = false;
|
|
goalReachedPub_->publish(result);
|
|
}
|
|
return;
|
|
}
|
|
targetPose = t * targetPose;
|
|
}
|
|
// else assume map frame if not set
|
|
|
|
goalCommonCallback(0, "", "", targetPose, msg->header.stamp);
|
|
}
|
|
|
|
void CoreWrapper::goalNodeCallback(const rtabmap_msgs::msg::Goal::SharedPtr msg)
|
|
{
|
|
if(msg->node_id == 0 && msg->node_label.empty())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Node id or label should be set!");
|
|
if(goalReachedPub_->get_subscription_count())
|
|
{
|
|
std_msgs::msg::Bool result;
|
|
result.data = false;
|
|
goalReachedPub_->publish(result);
|
|
}
|
|
return;
|
|
}
|
|
goalCommonCallback(msg->node_id, msg->node_label, msg->frame_id, Transform(), msg->header.stamp);
|
|
}
|
|
|
|
void CoreWrapper::updateRtabmapCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
for(rtabmap::ParametersMap::iterator iter=parameters_.begin(); iter!=parameters_.end(); ++iter)
|
|
{
|
|
std::string paramValue;
|
|
rclcpp::Parameter parameter;
|
|
if(get_parameter(iter->first, parameter))
|
|
{
|
|
paramValue = parameter.as_string();
|
|
if(paramValue.compare(iter->second)!=0)
|
|
{
|
|
RCLCPP_INFO(get_logger(), "Setting RTAB-Map parameter \"%s\"=\"%s\"", iter->first.c_str(), paramValue.c_str());
|
|
iter->second = paramValue;
|
|
}
|
|
}
|
|
}
|
|
RCLCPP_INFO(get_logger(), "rtabmap: Updating parameters");
|
|
if(parameters_.find(Parameters::kRtabmapDetectionRate()) != parameters_.end())
|
|
{
|
|
rate_ = uStr2Float(parameters_.at(Parameters::kRtabmapDetectionRate()));
|
|
RCLCPP_INFO(get_logger(), "RTAB-Map rate detection = %f Hz", rate_);
|
|
}
|
|
if(parameters_.find(Parameters::kRtabmapCreateIntermediateNodes()) != parameters_.end())
|
|
{
|
|
createIntermediateNodes_ = uStr2Bool(parameters_.at(Parameters::kRtabmapCreateIntermediateNodes()));
|
|
RCLCPP_INFO(get_logger(), "Create intermediate nodes = %s", createIntermediateNodes_?"true":"false");
|
|
}
|
|
if(parameters_.find(Parameters::kGridGlobalMaxNodes()) != parameters_.end())
|
|
{
|
|
mappingMaxNodes_ = uStr2Int(parameters_.at(Parameters::kGridGlobalMaxNodes()));
|
|
RCLCPP_INFO(get_logger(), "Max mapping nodes = %d", mappingMaxNodes_);
|
|
}
|
|
if(parameters_.find(Parameters::kGridGlobalAltitudeDelta()) != parameters_.end())
|
|
{
|
|
mappingAltitudeDelta_ = uStr2Float(parameters_.at(Parameters::kGridGlobalAltitudeDelta()));
|
|
RCLCPP_INFO(get_logger(), "Mapping altitude delta = %f", mappingAltitudeDelta_);
|
|
}
|
|
if(parameters_.find(Parameters::kRtabmapImagesAlreadyRectified()) != parameters_.end())
|
|
{
|
|
alreadyRectifiedImages_ = uStr2Bool(parameters_.at(Parameters::kRtabmapImagesAlreadyRectified()));
|
|
RCLCPP_INFO(get_logger(), "Already rectified images = %s", alreadyRectifiedImages_?"true":"false");
|
|
}
|
|
if(parameters_.find(Parameters::kRegForce3DoF()) != parameters_.end())
|
|
{
|
|
twoDMapping_= uStr2Bool(parameters_.at(Parameters::kRegForce3DoF()));
|
|
RCLCPP_INFO(get_logger(), "2D mapping = %s", twoDMapping_?"true":"false");
|
|
}
|
|
rtabmap_.parseParameters(parameters_);
|
|
// Don't reset map in localization mode
|
|
if(rtabmap_.getMemory()->isIncremental()) {
|
|
mapsManager_.setParameters(parameters_);
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::resetRtabmapCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Reset");
|
|
rtabmap_.resetMemory();
|
|
|
|
lastPoseMutex_.lock();
|
|
lastPoseCovariance_ = cv::Mat();
|
|
lastPose_.setIdentity();
|
|
lastPoseStamp_ = rclcpp::Time();
|
|
lastPoseVelocity_.clear();
|
|
lastPoseIntermediate_ = false;
|
|
lastPoseMutex_.unlock();
|
|
|
|
currentMetricGoal_.setNull();
|
|
lastPublishedMetricGoal_.setNull();
|
|
goalFrameId_.clear();
|
|
latestNodeWasReached_ = false;
|
|
graphLatched_ = false;
|
|
mapsManager_.clear();
|
|
previousStamp_ = rclcpp::Time(0);
|
|
globalPoses_.clear();
|
|
gps_.clear();
|
|
landmarksMutex_.lock();
|
|
landmarks_.clear();
|
|
landmarksMutex_.unlock();
|
|
userDataMutex_.lock();
|
|
userData_ = cv::Mat();
|
|
userDataMutex_.unlock();
|
|
imuMutex_.lock();
|
|
imus_.clear();
|
|
imuFrameId_.clear();
|
|
imuMutex_.unlock();
|
|
interOdoms_.clear();
|
|
mapToOdomMutex_.lock();
|
|
mapToOdom_.setIdentity();
|
|
mapToOdomMutex_.unlock();
|
|
}
|
|
|
|
void CoreWrapper::pauseRtabmapCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
if(paused_)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "rtabmap: Already paused!");
|
|
}
|
|
else
|
|
{
|
|
paused_ = true;
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: paused!");
|
|
set_parameter(rclcpp::Parameter("is_rtabmap_paused", true));
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::resumeRtabmapCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
if(!paused_)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "rtabmap: Already running!");
|
|
}
|
|
else
|
|
{
|
|
paused_ = false;
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: resumed!");
|
|
set_parameter(rclcpp::Parameter("is_rtabmap_paused", false));
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::loadDatabaseCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::LoadDatabase::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::LoadDatabase::Response>)
|
|
{
|
|
RCLCPP_INFO(get_logger(), "LoadDatabase: Loading database (%s, clear=%s)...", req->database_path.c_str(), req->clear?"true":"false");
|
|
std::string newDatabasePath = uReplaceChar(req->database_path, '~', UDirectory::homeDir());
|
|
std::string dir = UDirectory::getDir(newDatabasePath);
|
|
if(!UDirectory::exists(dir))
|
|
{
|
|
RCLCPP_ERROR(get_logger(), "Directory %s doesn't exist! Cannot load database \"%s\"", newDatabasePath.c_str(), dir.c_str());
|
|
return;
|
|
}
|
|
|
|
if(UFile::exists(newDatabasePath) && req->clear)
|
|
{
|
|
UFile::erase(newDatabasePath);
|
|
}
|
|
|
|
// Close old database
|
|
RCLCPP_INFO(get_logger(), "LoadDatabase: Saving current map (%s)...", databasePath_.c_str());
|
|
if(rtabmap_.getMemory())
|
|
{
|
|
// save the grid map
|
|
float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f;
|
|
cv::Mat pixels = mapsManager_.getGridMap(xMin, yMin, gridCellSize);
|
|
if(!pixels.empty())
|
|
{
|
|
printf("rtabmap: 2D occupancy grid map saved.\n");
|
|
rtabmap_.getMemory()->save2DMap(pixels, xMin, yMin, gridCellSize);
|
|
}
|
|
}
|
|
rtabmap_.close();
|
|
RCLCPP_INFO(get_logger(), "LoadDatabase: Saving current map (%s, %ld MB)... done!", databasePath_.c_str(), UFile::length(databasePath_)/(1024*1024));
|
|
|
|
lastPoseMutex_.lock();
|
|
lastPoseCovariance_ = cv::Mat();
|
|
lastPose_.setIdentity();
|
|
lastPoseStamp_ = rclcpp::Time();
|
|
lastPoseVelocity_.clear();
|
|
lastPoseIntermediate_ = false;
|
|
lastPoseMutex_.unlock();
|
|
|
|
currentMetricGoal_.setNull();
|
|
lastPublishedMetricGoal_.setNull();
|
|
goalFrameId_.clear();
|
|
latestNodeWasReached_ = false;
|
|
graphLatched_ = false;
|
|
mapsManager_.clear();
|
|
previousStamp_ = rclcpp::Time(0);
|
|
globalPoses_.clear();
|
|
gps_.clear();
|
|
landmarksMutex_.lock();
|
|
landmarks_.clear();
|
|
landmarksMutex_.unlock();
|
|
userDataMutex_.lock();
|
|
userData_ = cv::Mat();
|
|
userDataMutex_.unlock();
|
|
imuMutex_.lock();
|
|
imus_.clear();
|
|
imuFrameId_.clear();
|
|
imuMutex_.unlock();
|
|
interOdoms_.clear();
|
|
mapToOdomMutex_.lock();
|
|
mapToOdom_.setIdentity();
|
|
mapToOdomMutex_.unlock();
|
|
|
|
// Open new database
|
|
databasePath_ = newDatabasePath;
|
|
|
|
//Warn if database's parameters are different than the current ones we are using
|
|
if(!req->clear && UFile::exists(databasePath_))
|
|
{
|
|
ParametersMap dbParameters;
|
|
rtabmap::DBDriver * driver = rtabmap::DBDriver::create();
|
|
std::string databaseVersion = "0.0.0";
|
|
if(driver->openConnection(databasePath_))
|
|
{
|
|
dbParameters = driver->getLastParameters(); // parameter migration is already done
|
|
databaseVersion = driver->getDatabaseVersion();
|
|
}
|
|
delete driver;
|
|
for(ParametersMap::iterator iter=dbParameters.begin(); iter!=dbParameters.end(); ++iter)
|
|
{
|
|
if(iter->first.compare(Parameters::kRtabmapWorkingDirectory()) == 0)
|
|
{
|
|
// ignore working directory
|
|
continue;
|
|
}
|
|
if(iter->first.find("Odom") == 0)
|
|
{
|
|
// ignore odometry params
|
|
continue;
|
|
}
|
|
if(parameters_.find(iter->first) == parameters_.end())
|
|
{
|
|
RCLCPP_WARN(get_logger(), "RTAB-Map parameter \"%s\" from database (%s, version \"%s\") doesn't exist "
|
|
"in current rtabmap version (\"%s\"). The parameter is ignored.",
|
|
iter->first.c_str(),
|
|
iter->second.c_str(),
|
|
databaseVersion.c_str(),
|
|
RTABMAP_VERSION);
|
|
}
|
|
else if(parameters_.find(iter->first)->second.compare(iter->second) !=0)
|
|
{
|
|
RCLCPP_WARN(get_logger(), "RTAB-Map parameter \"%s\" from database (%s, version=\"%s\") is different "
|
|
"from the current used one (%s, version=\"%s\"). We still keep the "
|
|
"current parameter value (%s). If you want to switch between databases "
|
|
"with different configurations, restart rtabmap node instead of using this service.",
|
|
iter->first.c_str(),
|
|
iter->second.c_str(),
|
|
databaseVersion.c_str(),
|
|
parameters_.find(iter->first)->second.c_str(),
|
|
RTABMAP_VERSION,
|
|
parameters_.find(iter->first)->second.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
RCLCPP_INFO(get_logger(), "LoadDatabase: Loading database...");
|
|
rtabmap_.init(parameters_, databasePath_);
|
|
RCLCPP_INFO(get_logger(), "LoadDatabase: Loading database... done!");
|
|
|
|
if(rtabmap_.getMemory())
|
|
{
|
|
if(useSavedMap_ && !rtabmap_.getMemory()->isIncremental())
|
|
{
|
|
float xMin, yMin, gridCellSize;
|
|
cv::Mat map = rtabmap_.getMemory()->load2DMap(xMin, yMin, gridCellSize);
|
|
if(!map.empty())
|
|
{
|
|
RCLCPP_INFO(get_logger(), "LoadDatabase: 2D occupancy grid map loaded (%dx%d).", map.cols, map.rows);
|
|
mapsManager_.set2DMap(map, xMin, yMin, gridCellSize, rtabmap_.getLocalOptimizedPoses(), rtabmap_.getMemory());
|
|
}
|
|
}
|
|
|
|
if(rtabmap_.getMemory()->getWorkingMem().size()>1)
|
|
{
|
|
RCLCPP_INFO(get_logger(), "LoadDatabase: Working Memory = %d, Local map = %d.",
|
|
(int)rtabmap_.getMemory()->getWorkingMem().size()-1,
|
|
(int)rtabmap_.getLocalOptimizedPoses().size());
|
|
}
|
|
|
|
if(databasePath_.size())
|
|
{
|
|
RCLCPP_INFO(get_logger(), "LoadDatabase: Database version = \"%s\".", rtabmap_.getMemory()->getDatabaseVersion().c_str());
|
|
}
|
|
|
|
if(rtabmap_.getMemory()->isIncremental())
|
|
{
|
|
RCLCPP_INFO(get_logger(), "LoadDatabase: SLAM mode (%s=true)", Parameters::kMemIncrementalMemory().c_str());
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_INFO(get_logger(), "LoadDatabase: Localization mode (%s=false)", Parameters::kMemIncrementalMemory().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::triggerNewMapCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Trigger new map");
|
|
rtabmap_.triggerNewMap();
|
|
}
|
|
|
|
void CoreWrapper::backupDatabaseCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Backup: Saving memory...");
|
|
if(rtabmap_.getMemory())
|
|
{
|
|
// save the grid map
|
|
float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f;
|
|
cv::Mat pixels = mapsManager_.getGridMap(xMin, yMin, gridCellSize);
|
|
if(!pixels.empty())
|
|
{
|
|
printf("rtabmap: 2D occupancy grid map saved.\n");
|
|
rtabmap_.getMemory()->save2DMap(pixels, xMin, yMin, gridCellSize);
|
|
}
|
|
}
|
|
rtabmap_.close();
|
|
RCLCPP_INFO(this->get_logger(), "Backup: Saving memory... done!");
|
|
|
|
lastPoseMutex_.lock();
|
|
lastPoseCovariance_ = cv::Mat();
|
|
lastPose_.setIdentity();
|
|
lastPoseStamp_ = rclcpp::Time();
|
|
lastPoseVelocity_.clear();
|
|
lastPoseIntermediate_ = false;
|
|
lastPoseMutex_.unlock();
|
|
|
|
currentMetricGoal_.setNull();
|
|
lastPublishedMetricGoal_.setNull();
|
|
goalFrameId_.clear();
|
|
latestNodeWasReached_ = false;
|
|
graphLatched_ = false;
|
|
userDataMutex_.lock();
|
|
userData_ = cv::Mat();
|
|
userDataMutex_.unlock();
|
|
globalPoses_.clear();
|
|
gps_.clear();
|
|
landmarksMutex_.lock();
|
|
landmarks_.clear();
|
|
landmarksMutex_.unlock();
|
|
|
|
RCLCPP_INFO(this->get_logger(), "Backup: Saving \"%s\" to \"%s\"...", databasePath_.c_str(), (databasePath_+".back").c_str());
|
|
UFile::copy(databasePath_, databasePath_+".back");
|
|
RCLCPP_INFO(this->get_logger(), "Backup: Saving \"%s\" to \"%s\"... done!", databasePath_.c_str(), (databasePath_+".back").c_str());
|
|
|
|
RCLCPP_INFO(this->get_logger(), "Backup: Reloading memory...");
|
|
rtabmap_.init(parameters_, databasePath_);
|
|
RCLCPP_INFO(this->get_logger(), "Backup: Reloading memory... done!");
|
|
}
|
|
|
|
void CoreWrapper::republishMaps()
|
|
{
|
|
rclcpp::Time stamp = now();
|
|
mapsManager_.publishMaps(rtabmap_.getLocalOptimizedPoses(), stamp, mapFrameId_);
|
|
|
|
if(mapDataPub_->get_subscription_count()>0)
|
|
{
|
|
rtabmap_msgs::msg::MapData::UniquePtr msg(new rtabmap_msgs::msg::MapData);
|
|
msg->header.stamp = stamp;
|
|
msg->header.frame_id = mapFrameId_;
|
|
|
|
rtabmap_conversions::mapDataToROS(
|
|
rtabmap_.getLocalOptimizedPoses(),
|
|
rtabmap_.getLocalConstraints(),
|
|
std::map<int, Signature>(),
|
|
rtabmap_.getMapCorrection(),
|
|
*msg);
|
|
|
|
mapDataPub_->publish(std::move(msg));
|
|
}
|
|
|
|
if(mapGraphPub_->get_subscription_count()>0)
|
|
{
|
|
rtabmap_msgs::msg::MapGraph::UniquePtr msg(new rtabmap_msgs::msg::MapGraph);
|
|
msg->header.stamp = stamp;
|
|
msg->header.frame_id = mapFrameId_;
|
|
|
|
rtabmap_conversions::mapGraphToROS(
|
|
rtabmap_.getLocalOptimizedPoses(),
|
|
rtabmap_.getLocalConstraints(),
|
|
rtabmap_.getMapCorrection(),
|
|
*msg);
|
|
|
|
mapGraphPub_->publish(std::move(msg));
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::detectMoreLoopClosuresCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::DetectMoreLoopClosures::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::DetectMoreLoopClosures::Response> res)
|
|
{
|
|
RCLCPP_WARN(get_logger(), "Detect more loop closures service called");
|
|
|
|
UTimer timer;
|
|
float clusterRadiusMax = 1;
|
|
float clusterRadiusMin = 0;
|
|
float clusterAngle = 0;
|
|
int iterations = 1;
|
|
bool intraSession = true;
|
|
bool interSession = true;
|
|
if(req->cluster_radius_max > 0.0f)
|
|
{
|
|
clusterRadiusMax = req->cluster_radius_max;
|
|
}
|
|
if(req->cluster_radius_min >= 0.0f)
|
|
{
|
|
clusterRadiusMin = req->cluster_radius_min;
|
|
}
|
|
if(req->cluster_angle >= 0.0f)
|
|
{
|
|
clusterAngle = req->cluster_angle;
|
|
}
|
|
if(req->iterations >= 1.0f)
|
|
{
|
|
iterations = (int)req->iterations;
|
|
}
|
|
if(req->intra_only)
|
|
{
|
|
interSession = false;
|
|
}
|
|
else if(req->inter_only)
|
|
{
|
|
intraSession = false;
|
|
}
|
|
RCLCPP_WARN(get_logger(), "Post-Processing service called: Detecting more loop closures "
|
|
"(max radius=%f, min radius=%f, angle=%f, iterations=%d, intra=%s, inter=%s)...",
|
|
clusterRadiusMax,
|
|
clusterRadiusMin,
|
|
clusterAngle,
|
|
iterations,
|
|
intraSession?"true":"false",
|
|
interSession?"true":"false");
|
|
res->detected = rtabmap_.detectMoreLoopClosures(
|
|
clusterRadiusMax,
|
|
clusterAngle*M_PI/180.0,
|
|
iterations,
|
|
intraSession,
|
|
interSession,
|
|
0,
|
|
clusterRadiusMin);
|
|
if(res->detected<0)
|
|
{
|
|
RCLCPP_ERROR(get_logger(), "Post-Processing: Detecting more loop closures failed!");
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(get_logger(), "Post-Processing: Detected %d loop closures! (%fs)", res->detected, timer.ticks());
|
|
|
|
if(res->detected>0)
|
|
{
|
|
republishMaps();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::cleanupLocalGridsCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::CleanupLocalGrids::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::CleanupLocalGrids::Response> res)
|
|
{
|
|
RCLCPP_WARN(get_logger(), "Cleanup local grids service called");
|
|
UTimer timer;
|
|
int radius = 1;
|
|
bool filterScans = false;
|
|
if(req->radius > 1.0f)
|
|
{
|
|
radius = (int)req->radius;
|
|
}
|
|
filterScans = req->filter_scans;
|
|
float xMin, yMin, gridCellSize;
|
|
cv::Mat map = mapsManager_.getGridMap(xMin, yMin, gridCellSize);
|
|
if(map.empty())
|
|
{
|
|
RCLCPP_ERROR(get_logger(), "Post-Processing: Cleanup local grids failed! There is no optimized map.");
|
|
}
|
|
std::map<int, Transform> poses = rtabmap_.getLocalOptimizedPoses();
|
|
RCLCPP_WARN(get_logger(), "Post-Processing: Cleanup local grids... (radius=%d, filter scans=%s)",
|
|
radius,
|
|
filterScans?"true":"false");
|
|
res->modified = rtabmap_.cleanupLocalGrids(poses, map, xMin, yMin, gridCellSize, radius, filterScans);
|
|
if(res->modified<0)
|
|
{
|
|
RCLCPP_ERROR(get_logger(), "Post-Processing: Cleanup local grids failed!");
|
|
}
|
|
else
|
|
{
|
|
if(filterScans)
|
|
{
|
|
RCLCPP_WARN(get_logger(), "Post-Processing: %d grids and scans modified! (%fs)", res->modified, timer.ticks());
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(get_logger(), "Post-Processing: %d grids modified! (%fs)", res->modified, timer.ticks());
|
|
}
|
|
if(res->modified > 0)
|
|
{
|
|
// We should update MapsManager's cache with the modifications
|
|
mapsManager_.clear();
|
|
mapsManager_.set2DMap(map, xMin, yMin, gridCellSize, rtabmap_.getLocalOptimizedPoses(), rtabmap_.getMemory());
|
|
|
|
republishMaps();
|
|
}
|
|
}
|
|
}
|
|
void CoreWrapper::globalBundleAdjustmentCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::GlobalBundleAdjustment::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::GlobalBundleAdjustment::Response>)
|
|
{
|
|
RCLCPP_WARN(get_logger(), "Global bundle adjustment service called");
|
|
|
|
UTimer timer;
|
|
int optimizer = (int)Optimizer::kTypeG2O; // g2o
|
|
int iterations = Parameters::defaultOptimizerIterations();
|
|
float pixelVariance = Parameters::defaultg2oPixelVariance();
|
|
bool rematchFeatures = true;
|
|
Parameters::parse(parameters_, Parameters::kOptimizerIterations(), iterations);
|
|
Parameters::parse(parameters_, Parameters::kg2oPixelVariance(), pixelVariance);
|
|
if(req->type == 1.0f)
|
|
{
|
|
optimizer = (int)Optimizer::kTypeCVSBA;
|
|
}
|
|
if(req->iterations >= 1.0f)
|
|
{
|
|
iterations = req->iterations;
|
|
}
|
|
if(req->pixel_variance > 0.0f)
|
|
{
|
|
pixelVariance = req->pixel_variance;
|
|
}
|
|
rematchFeatures = !req->voc_matches;
|
|
|
|
RCLCPP_WARN(get_logger(), "Post-Processing: Global Bundle Adjustment... "
|
|
"(Optimizer=%s, iterations=%d, pixel variance=%f, rematch=%s)...",
|
|
optimizer==Optimizer::kTypeG2O?"g2o":"cvsba",
|
|
iterations,
|
|
pixelVariance,
|
|
rematchFeatures?"true":"false");
|
|
bool success = rtabmap_.globalBundleAdjustment((Optimizer::Type)optimizer, rematchFeatures, iterations, pixelVariance);
|
|
if(!success)
|
|
{
|
|
RCLCPP_ERROR(get_logger(), "Post-Processing: Global Bundle Adjustment failed!");
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(get_logger(), "Post-Processing: Global Bundle Adjustment... done! (%fs)", timer.ticks());
|
|
republishMaps();
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::setModeLocalizationCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Set localization mode");
|
|
rtabmap::ParametersMap parameters;
|
|
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemIncrementalMemory(), "false"));
|
|
set_parameter(rclcpp::Parameter(rtabmap::Parameters::kMemIncrementalMemory(), "false"));
|
|
rtabmap_.parseParameters(parameters);
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Localization mode enabled!");
|
|
}
|
|
|
|
void CoreWrapper::setModeMappingCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Set mapping mode");
|
|
rtabmap::ParametersMap parameters;
|
|
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemIncrementalMemory(), "true"));
|
|
set_parameter(rclcpp::Parameter(rtabmap::Parameters::kMemIncrementalMemory(), "true"));
|
|
rtabmap_.parseParameters(parameters);
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Mapping mode enabled!");
|
|
}
|
|
|
|
void CoreWrapper::setLogDebug(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Set log level to Debug");
|
|
ULogger::setLevel(ULogger::kDebug);
|
|
}
|
|
void CoreWrapper::setLogInfo(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Set log level to Info");
|
|
ULogger::setLevel(ULogger::kInfo);
|
|
}
|
|
void CoreWrapper::setLogWarn(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Set log level to Warning");
|
|
ULogger::setLevel(ULogger::kWarning);
|
|
}
|
|
void CoreWrapper::setLogError(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Set log level to Error");
|
|
ULogger::setLevel(ULogger::kError);
|
|
}
|
|
|
|
void CoreWrapper::getNodeDataCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::GetNodeData::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::GetNodeData::Response> res)
|
|
{
|
|
RCLCPP_INFO(get_logger(), "rtabmap: Getting node data (%d node(s), images=%s scan=%s grid=%s user_data=%s)...",
|
|
(int)req->ids.size(),
|
|
req->images?"true":"false",
|
|
req->scan?"true":"false",
|
|
req->grid?"true":"false",
|
|
req->user_data?"true":"false");
|
|
|
|
if(req->ids.empty() && rtabmap_.getMemory() && rtabmap_.getMemory()->getLastWorkingSignature())
|
|
{
|
|
req->ids.push_back(rtabmap_.getMemory()->getLastWorkingSignature()->id());
|
|
}
|
|
for(size_t i=0; i<req->ids.size(); ++i)
|
|
{
|
|
int id = req->ids[i];
|
|
Signature s = rtabmap_.getSignatureCopy(id, req->images, req->scan, req->user_data, req->grid, true, true);
|
|
|
|
if(s.id()>0)
|
|
{
|
|
rtabmap_msgs::msg::Node msg;
|
|
rtabmap_conversions::nodeToROS(s, msg);
|
|
res->data.push_back(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::getMapDataCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::GetMap::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::GetMap::Response> res)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Getting map (global=%s optimized=%s graphOnly=%s)...",
|
|
req->global_map?"true":"false",
|
|
req->optimized?"true":"false",
|
|
req->graph_only?"true":"false");
|
|
std::map<int, Signature> signatures;
|
|
std::map<int, Transform> poses;
|
|
std::multimap<int, rtabmap::Link> constraints;
|
|
|
|
rtabmap_.getGraph(
|
|
poses,
|
|
constraints,
|
|
req->optimized,
|
|
req->global_map,
|
|
&signatures,
|
|
!req->graph_only,
|
|
!req->graph_only,
|
|
!req->graph_only,
|
|
!req->graph_only);
|
|
|
|
mapToOdomMutex_.lock();
|
|
Transform mapToOdomSafe = mapToOdom_.clone();
|
|
mapToOdomMutex_.unlock();
|
|
|
|
//RGB-D SLAM data
|
|
rtabmap_conversions::mapDataToROS(poses,
|
|
constraints,
|
|
signatures,
|
|
mapToOdomSafe,
|
|
res->data);
|
|
|
|
res->data.header.stamp = now();
|
|
res->data.header.frame_id = mapFrameId_;
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Getting map (global=%s optimized=%s graphOnly=%s)...done!",
|
|
req->global_map?"true":"false",
|
|
req->optimized?"true":"false",
|
|
req->graph_only?"true":"false");
|
|
}
|
|
|
|
void CoreWrapper::getMapData2Callback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::GetMap2::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::GetMap2::Response> res)
|
|
{
|
|
RCLCPP_INFO(get_logger(), "rtabmap: Getting map (global=%s optimized=%s with_images=%s with_scans=%s with_user_data=%s with_grids=%s)...",
|
|
req->global_map?"true":"false",
|
|
req->optimized?"true":"false",
|
|
req->with_images?"true":"false",
|
|
req->with_scans?"true":"false",
|
|
req->with_user_data?"true":"false",
|
|
req->with_grids?"true":"false");
|
|
std::map<int, Signature> signatures;
|
|
std::map<int, Transform> poses;
|
|
std::multimap<int, rtabmap::Link> constraints;
|
|
|
|
rtabmap_.getGraph(
|
|
poses,
|
|
constraints,
|
|
req->optimized,
|
|
req->global_map,
|
|
&signatures,
|
|
req->with_images,
|
|
req->with_scans,
|
|
req->with_user_data,
|
|
req->with_grids,
|
|
req->with_words,
|
|
req->with_global_descriptors);
|
|
|
|
mapToOdomMutex_.lock();
|
|
Transform mapToOdomSafe = mapToOdom_.clone();
|
|
mapToOdomMutex_.unlock();
|
|
|
|
//RGB-D SLAM data
|
|
rtabmap_conversions::mapDataToROS(poses,
|
|
constraints,
|
|
signatures,
|
|
mapToOdomSafe,
|
|
res->data);
|
|
|
|
res->data.header.stamp = now();
|
|
res->data.header.frame_id = mapFrameId_;
|
|
}
|
|
|
|
void CoreWrapper::getMapCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<nav_msgs::srv::GetMap::Request>,
|
|
std::shared_ptr<nav_msgs::srv::GetMap::Response> res)
|
|
{
|
|
// Make sure grid map cache is up to date (in case there is no subscriber on map topics)
|
|
std::map<int, Transform> poses = rtabmap_.getLocalOptimizedPoses();
|
|
mapsManager_.updateMapCaches(poses, rtabmap_.getMemory(), true, false);
|
|
|
|
// create the grid map
|
|
float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f;
|
|
cv::Mat pixels = mapsManager_.getGridMap(xMin, yMin, gridCellSize);
|
|
|
|
if(!pixels.empty())
|
|
{
|
|
//init
|
|
res->map.info.resolution = gridCellSize;
|
|
res->map.info.origin.position.x = 0.0;
|
|
res->map.info.origin.position.y = 0.0;
|
|
res->map.info.origin.position.z = 0.0;
|
|
res->map.info.origin.orientation.x = 0.0;
|
|
res->map.info.origin.orientation.y = 0.0;
|
|
res->map.info.origin.orientation.z = 0.0;
|
|
res->map.info.origin.orientation.w = 1.0;
|
|
|
|
res->map.info.width = pixels.cols;
|
|
res->map.info.height = pixels.rows;
|
|
res->map.info.origin.position.x = xMin;
|
|
res->map.info.origin.position.y = yMin;
|
|
res->map.data.resize(res->map.info.width * res->map.info.height);
|
|
|
|
memcpy(res->map.data.data(), pixels.data, res->map.info.width * res->map.info.height);
|
|
|
|
res->map.header.frame_id = mapFrameId_;
|
|
res->map.header.stamp = now();
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(get_logger(), "rtabmap: The map is empty!");
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::getProbMapCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<nav_msgs::srv::GetMap::Request>,
|
|
std::shared_ptr<nav_msgs::srv::GetMap::Response> res)
|
|
{
|
|
// Make sure grid map cache is up to date (in case there is no subscriber on map topics)
|
|
std::map<int, Transform> poses = rtabmap_.getLocalOptimizedPoses();
|
|
mapsManager_.updateMapCaches(poses, rtabmap_.getMemory(), true, false);
|
|
|
|
// create the grid map
|
|
float xMin=0.0f, yMin=0.0f, gridCellSize = 0.05f;
|
|
cv::Mat pixels = mapsManager_.getGridProbMap(xMin, yMin, gridCellSize);
|
|
|
|
if(!pixels.empty())
|
|
{
|
|
//init
|
|
res->map.info.resolution = gridCellSize;
|
|
res->map.info.origin.position.x = 0.0;
|
|
res->map.info.origin.position.y = 0.0;
|
|
res->map.info.origin.position.z = 0.0;
|
|
res->map.info.origin.orientation.x = 0.0;
|
|
res->map.info.origin.orientation.y = 0.0;
|
|
res->map.info.origin.orientation.z = 0.0;
|
|
res->map.info.origin.orientation.w = 1.0;
|
|
|
|
res->map.info.width = pixels.cols;
|
|
res->map.info.height = pixels.rows;
|
|
res->map.info.origin.position.x = xMin;
|
|
res->map.info.origin.position.y = yMin;
|
|
res->map.data.resize(res->map.info.width * res->map.info.height);
|
|
|
|
memcpy(res->map.data.data(), pixels.data, res->map.info.width * res->map.info.height);
|
|
|
|
res->map.header.frame_id = mapFrameId_;
|
|
res->map.header.stamp = now();
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_WARN(get_logger(), "rtabmap: The map is empty!");
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::publishMapCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::PublishMap::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::PublishMap::Response>)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "rtabmap: Publishing map...");
|
|
|
|
rclcpp::Time stampNow = now();
|
|
|
|
if(mapDataPub_->get_subscription_count() ||
|
|
(!req->graph_only && mapsManager_.hasSubscribers()) ||
|
|
(req->graph_only && (labelsPub_->get_subscription_count() || mapGraphPub_->get_subscription_count() || mapPathPub_->get_subscription_count())))
|
|
{
|
|
std::map<int, Transform> poses;
|
|
std::multimap<int, rtabmap::Link> constraints;
|
|
std::map<int, Signature > signatures;
|
|
|
|
rtabmap_.getGraph(
|
|
poses,
|
|
constraints,
|
|
req->optimized,
|
|
req->global_map,
|
|
&signatures,
|
|
!req->graph_only,
|
|
!req->graph_only,
|
|
!req->graph_only,
|
|
!req->graph_only);
|
|
|
|
mapToOdomMutex_.lock();
|
|
Transform mapToOdomSafe = mapToOdom_.clone();
|
|
mapToOdomMutex_.unlock();
|
|
|
|
if(mapDataPub_->get_subscription_count())
|
|
{
|
|
rtabmap_msgs::msg::MapData::UniquePtr msg(new rtabmap_msgs::msg::MapData);
|
|
msg->header.stamp = stampNow;
|
|
msg->header.frame_id = mapFrameId_;
|
|
|
|
rtabmap_conversions::mapDataToROS(poses,
|
|
constraints,
|
|
signatures,
|
|
mapToOdomSafe,
|
|
*msg);
|
|
|
|
mapDataPub_->publish(std::move(msg));
|
|
}
|
|
|
|
if(mapGraphPub_->get_subscription_count())
|
|
{
|
|
rtabmap_msgs::msg::MapGraph::UniquePtr msg(new rtabmap_msgs::msg::MapGraph);
|
|
msg->header.stamp = stampNow;
|
|
msg->header.frame_id = mapFrameId_;
|
|
|
|
rtabmap_conversions::mapGraphToROS(poses,
|
|
constraints,
|
|
mapToOdomSafe,
|
|
*msg);
|
|
|
|
mapGraphPub_->publish(std::move(msg));
|
|
}
|
|
|
|
bool pubLabels = labelsPub_->get_subscription_count();
|
|
visualization_msgs::msg::MarkerArray markers;
|
|
if((landmarksPub_->get_subscription_count() || pubLabels) && !poses.empty() && poses.begin()->first < 0)
|
|
{
|
|
geometry_msgs::msg::PoseArray::UniquePtr msg(new geometry_msgs::msg::PoseArray);
|
|
msg->header.stamp = stampNow;
|
|
msg->header.frame_id = mapFrameId_;
|
|
for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end() && iter->first<0; ++iter)
|
|
{
|
|
geometry_msgs::msg::Pose p;
|
|
rtabmap_conversions::transformToPoseMsg(iter->second, p);
|
|
msg->poses.push_back(p);
|
|
|
|
if(pubLabels)
|
|
{
|
|
// Add landmark ids
|
|
visualization_msgs::msg::Marker marker;
|
|
marker.header.frame_id = mapFrameId_;
|
|
marker.header.stamp = stampNow;
|
|
marker.ns = "landmarks";
|
|
marker.id = iter->first;
|
|
marker.action = visualization_msgs::msg::Marker::ADD;
|
|
marker.pose.position.x = iter->second.x();
|
|
marker.pose.position.y = iter->second.y();
|
|
marker.pose.position.z = iter->second.z();
|
|
marker.pose.orientation.x = 0.0;
|
|
marker.pose.orientation.y = 0.0;
|
|
marker.pose.orientation.z = 0.0;
|
|
marker.pose.orientation.w = 1.0;
|
|
marker.scale.x = 1;
|
|
marker.scale.y = 1;
|
|
marker.scale.z = 0.35;
|
|
marker.color.a = 0.5;
|
|
marker.color.r = 1.0;
|
|
marker.color.g = 1.0;
|
|
marker.color.b = 0.0;
|
|
marker.lifetime = rclcpp::Duration::from_seconds(2.0f/rate_);
|
|
|
|
marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
|
|
marker.text = uNumber2Str(iter->first);
|
|
|
|
markers.markers.push_back(marker);
|
|
}
|
|
}
|
|
|
|
landmarksPub_->publish(std::move(msg));
|
|
}
|
|
|
|
if(!req->graph_only)
|
|
{
|
|
if(mapsManager_.hasSubscribers())
|
|
{
|
|
std::map<int, Transform> filteredPoses(poses.lower_bound(1), poses.end());
|
|
if((mappingMaxNodes_ > 0 || mappingAltitudeDelta_>0.0) && filteredPoses.size()>1)
|
|
{
|
|
std::map<int, Transform> nearestPoses = filterNodesToAssemble(filteredPoses, filteredPoses.rbegin()->second);
|
|
}
|
|
if(signatures.size())
|
|
{
|
|
filteredPoses = mapsManager_.updateMapCaches(
|
|
filteredPoses,
|
|
rtabmap_.getMemory(),
|
|
false,
|
|
false,
|
|
signatures);
|
|
}
|
|
else
|
|
{
|
|
filteredPoses = mapsManager_.getFilteredPoses(filteredPoses);
|
|
}
|
|
mapsManager_.publishMaps(filteredPoses, stampNow, mapFrameId_);
|
|
}
|
|
else
|
|
{
|
|
// this will cleanup the cache if there are no subscribers
|
|
mapsManager_.publishMaps(std::map<int, Transform>(), stampNow, mapFrameId_);
|
|
}
|
|
}
|
|
|
|
bool pubPath = mapPathPub_->get_subscription_count();
|
|
if(pubLabels || pubPath)
|
|
{
|
|
if(poses.size() && signatures.size())
|
|
{
|
|
nav_msgs::msg::Path path;
|
|
if(pubPath)
|
|
{
|
|
path.poses.resize(poses.size());
|
|
}
|
|
int oi=0;
|
|
for(std::map<int, Signature>::const_iterator iter=signatures.begin();
|
|
iter!=signatures.end();
|
|
++iter)
|
|
{
|
|
std::map<int, Transform>::const_iterator poseIter= poses.find(iter->first);
|
|
if(poseIter!=poses.end())
|
|
{
|
|
if(pubLabels)
|
|
{
|
|
// Add labels
|
|
if(!iter->second.getLabel().empty())
|
|
{
|
|
visualization_msgs::msg::Marker marker;
|
|
marker.header.frame_id = mapFrameId_;
|
|
marker.header.stamp = stampNow;
|
|
marker.ns = "labels";
|
|
marker.id = iter->first;
|
|
marker.action = visualization_msgs::msg::Marker::ADD;
|
|
marker.pose.position.x = poseIter->second.x();
|
|
marker.pose.position.y = poseIter->second.y();
|
|
marker.pose.position.z = poseIter->second.z();
|
|
marker.pose.orientation.x = 0.0;
|
|
marker.pose.orientation.y = 0.0;
|
|
marker.pose.orientation.z = 0.0;
|
|
marker.pose.orientation.w = 1.0;
|
|
marker.scale.x = 1;
|
|
marker.scale.y = 1;
|
|
marker.scale.z = 0.5;
|
|
marker.color.a = 0.7;
|
|
marker.color.r = 1.0;
|
|
marker.color.g = 0.0;
|
|
marker.color.b = 0.0;
|
|
|
|
marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
|
|
marker.text = iter->second.getLabel();
|
|
|
|
markers.markers.push_back(marker);
|
|
}
|
|
// Add node ids
|
|
visualization_msgs::msg::Marker marker;
|
|
marker.header.frame_id = mapFrameId_;
|
|
marker.header.stamp = stampNow;
|
|
marker.ns = "ids";
|
|
marker.id = iter->first;
|
|
marker.action = visualization_msgs::msg::Marker::ADD;
|
|
marker.pose.position.x = poseIter->second.x();
|
|
marker.pose.position.y = poseIter->second.y();
|
|
marker.pose.position.z = poseIter->second.z();
|
|
marker.pose.orientation.x = 0.0;
|
|
marker.pose.orientation.y = 0.0;
|
|
marker.pose.orientation.z = 0.0;
|
|
marker.pose.orientation.w = 1.0;
|
|
marker.scale.x = 1;
|
|
marker.scale.y = 1;
|
|
marker.scale.z = 0.2;
|
|
marker.color.a = 0.5;
|
|
marker.color.r = 1.0;
|
|
marker.color.g = 1.0;
|
|
marker.color.b = 1.0;
|
|
marker.lifetime = rclcpp::Duration::from_seconds(2.0f/rate_);
|
|
|
|
marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
|
|
marker.text = uNumber2Str(iter->first);
|
|
|
|
markers.markers.push_back(marker);
|
|
}
|
|
if(pubPath)
|
|
{
|
|
rtabmap_conversions::transformToPoseMsg(poseIter->second, path.poses.at(oi).pose);
|
|
path.poses.at(oi).header.frame_id = mapFrameId_;
|
|
path.poses.at(oi).header.stamp = rtabmap_conversions::timestampToROS(iter->second.getStamp());
|
|
++oi;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pubLabels && markers.markers.size())
|
|
{
|
|
labelsPub_->publish(markers);
|
|
}
|
|
if(pubPath && oi)
|
|
{
|
|
path.header.frame_id = mapFrameId_;
|
|
path.header.stamp = stampNow;
|
|
path.poses.resize(oi);
|
|
mapPathPub_->publish(path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UWARN("No subscribers, don't need to publish!");
|
|
if(!req->graph_only)
|
|
{
|
|
// this will cleanup the cache if there are no subscribers
|
|
mapsManager_.publishMaps(std::map<int, Transform>(), stampNow, mapFrameId_);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::getPlanCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<nav_msgs::srv::GetPlan::Request> req,
|
|
std::shared_ptr<nav_msgs::srv::GetPlan::Response> res)
|
|
{
|
|
Transform pose = rtabmap_conversions::transformFromPoseMsg(req->goal.pose, true);
|
|
UTimer timer;
|
|
if(!pose.isNull())
|
|
{
|
|
// transform goal in /map frame
|
|
Transform coordinateTransform = Transform::getIdentity();
|
|
if(!req->goal.header.frame_id.empty() && mapFrameId_.compare(req->goal.header.frame_id) != 0)
|
|
{
|
|
coordinateTransform = rtabmap_conversions::getTransform(mapFrameId_, req->goal.header.frame_id, rclcpp::Time(req->goal.header.stamp.sec, req->goal.header.stamp.nanosec), *tfBuffer_, waitForTransform_);
|
|
if(coordinateTransform.isNull())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Cannot transform goal pose from \"%s\" frame to \"%s\" frame!",
|
|
req->goal.header.frame_id.c_str(), mapFrameId_.c_str());
|
|
return;
|
|
}
|
|
pose = coordinateTransform * pose;
|
|
}
|
|
//else assume map frame if not set
|
|
|
|
// To convert back the poses in goal frame
|
|
coordinateTransform = coordinateTransform.inverse();
|
|
|
|
if(rtabmap_.computePath(pose, req->tolerance))
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Planning: Time computing path = %f s", timer.ticks());
|
|
const std::vector<std::pair<int, Transform> > & poses = rtabmap_.getPath();
|
|
res->plan.header.frame_id = req->goal.header.frame_id;
|
|
res->plan.header.stamp = req->goal.header.stamp;
|
|
if(poses.size() == 0)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Planning: Goal already reached (RGBD/GoalReachedRadius=%fm).",
|
|
rtabmap_.getGoalReachedRadius());
|
|
// just set the goal directly
|
|
res->plan.poses.resize(1);
|
|
rtabmap_conversions::transformToPoseMsg(coordinateTransform*pose, res->plan.poses[0].pose);
|
|
}
|
|
else
|
|
{
|
|
res->plan.poses.resize(poses.size());
|
|
int oi = 0;
|
|
for(std::vector<std::pair<int, Transform> >::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
|
|
{
|
|
res->plan.poses[oi].header = res->plan.header;
|
|
rtabmap_conversions::transformToPoseMsg(coordinateTransform*iter->second, res->plan.poses[oi].pose);
|
|
++oi;
|
|
}
|
|
if(!rtabmap_.getPathTransformToGoal().isIdentity())
|
|
{
|
|
res->plan.poses.resize(res->plan.poses.size()+1);
|
|
res->plan.poses[res->plan.poses.size()-1].header = res->plan.header;
|
|
Transform p = rtabmap_.getPath().back().second*rtabmap_.getPathTransformToGoal();
|
|
rtabmap_conversions::transformToPoseMsg(coordinateTransform*p, res->plan.poses[res->plan.poses.size()-1].pose);
|
|
}
|
|
|
|
// Just output the path on screen
|
|
std::stringstream stream;
|
|
for(std::vector<std::pair<int, Transform> >::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
|
|
{
|
|
if(iter != poses.begin())
|
|
{
|
|
stream << " ";
|
|
}
|
|
stream << iter->first;
|
|
}
|
|
RCLCPP_INFO(this->get_logger(), "Planned path: [%s]", stream.str().c_str());
|
|
}
|
|
}
|
|
rtabmap_.clearPath(0);
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::getPlanNodesCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::GetPlan::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::GetPlan::Response> res)
|
|
{
|
|
Transform pose;
|
|
if(req->goal_node <= 0)
|
|
{
|
|
pose = rtabmap_conversions::transformFromPoseMsg(req->goal.pose, true);
|
|
}
|
|
UTimer timer;
|
|
if(req->goal_node > 0 || !pose.isNull())
|
|
{
|
|
Transform coordinateTransform = Transform::getIdentity();
|
|
// transform goal in /map frame
|
|
if(!pose.isNull() && !req->goal.header.frame_id.empty() && mapFrameId_.compare(req->goal.header.frame_id) != 0)
|
|
{
|
|
coordinateTransform = rtabmap_conversions::getTransform(mapFrameId_, req->goal.header.frame_id, req->goal.header.stamp, *tfBuffer_, waitForTransform_);
|
|
if(coordinateTransform.isNull())
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Cannot transform goal pose from \"%s\" frame to \"%s\" frame!",
|
|
req->goal.header.frame_id.c_str(), mapFrameId_.c_str());
|
|
return;
|
|
}
|
|
if(!pose.isNull())
|
|
{
|
|
pose = coordinateTransform * pose;
|
|
}
|
|
}
|
|
//else assume map frame if not set
|
|
|
|
// To convert back the poses in goal frame
|
|
coordinateTransform = coordinateTransform.inverse();
|
|
|
|
if((req->goal_node > 0 && rtabmap_.computePath(req->goal_node, req->tolerance)) ||
|
|
(req->goal_node <= 0 && rtabmap_.computePath(pose, req->tolerance)))
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Planning: Time computing path = %f s", timer.ticks());
|
|
const std::vector<std::pair<int, Transform> > & poses = rtabmap_.getPath();
|
|
res->plan.header.frame_id = mapFrameId_;
|
|
if(req->goal_node > 0)
|
|
{
|
|
res->plan.header.stamp = now();
|
|
}
|
|
else
|
|
{
|
|
res->plan.header.stamp = req->goal.header.stamp;
|
|
}
|
|
if(poses.size() == 0)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Planning: Goal already reached (RGBD/GoalReachedRadius=%fm).",
|
|
rtabmap_.getGoalReachedRadius());
|
|
if(!pose.isNull())
|
|
{
|
|
// just set the goal directly
|
|
res->plan.poses.resize(1);
|
|
res->plan.node_ids.resize(1);
|
|
rtabmap_conversions::transformToPoseMsg(coordinateTransform*pose, res->plan.poses[0]);
|
|
res->plan.node_ids[0] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
res->plan.poses.resize(poses.size());
|
|
res->plan.node_ids.resize(poses.size());
|
|
int oi = 0;
|
|
for(std::vector<std::pair<int, Transform> >::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
|
|
{
|
|
rtabmap_conversions::transformToPoseMsg(coordinateTransform*iter->second, res->plan.poses[oi]);
|
|
res->plan.node_ids[oi] = iter->first;
|
|
++oi;
|
|
}
|
|
if(!rtabmap_.getPathTransformToGoal().isIdentity())
|
|
{
|
|
res->plan.poses.resize(res->plan.poses.size()+1);
|
|
res->plan.node_ids.resize(res->plan.node_ids.size()+1);
|
|
Transform p = rtabmap_.getPath().back().second*rtabmap_.getPathTransformToGoal();
|
|
rtabmap_conversions::transformToPoseMsg(coordinateTransform*p, res->plan.poses[res->plan.poses.size()-1]);
|
|
res->plan.node_ids[res->plan.node_ids.size()-1] = 0;
|
|
}
|
|
|
|
// Just output the path on screen
|
|
std::stringstream stream;
|
|
for(std::vector<std::pair<int, Transform> >::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
|
|
{
|
|
if(iter != poses.begin())
|
|
{
|
|
stream << " ";
|
|
}
|
|
stream << iter->first;
|
|
}
|
|
RCLCPP_INFO(this->get_logger(), "Planned path: [%s]", stream.str().c_str());
|
|
}
|
|
}
|
|
rtabmap_.clearPath(0);
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::setGoalCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::SetGoal::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::SetGoal::Response> res)
|
|
{
|
|
double planningTime = 0.0;
|
|
goalCommonCallback(req->node_id, req->node_label, req->frame_id, Transform(), now(), &planningTime);
|
|
const std::vector<std::pair<int, Transform> > & path = rtabmap_.getPath();
|
|
res->path_ids.resize(path.size());
|
|
res->path_poses.resize(path.size());
|
|
res->planning_time = planningTime;
|
|
for(unsigned int i=0; i<path.size(); ++i)
|
|
{
|
|
res->path_ids[i] = path[i].first;
|
|
rtabmap_conversions::transformToPoseMsg(path[i].second, res->path_poses[i]);
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::cancelGoalCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<std_srvs::srv::Empty::Request>,
|
|
std::shared_ptr<std_srvs::srv::Empty::Response>)
|
|
{
|
|
if(rtabmap_.getPath().size())
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Goal cancelled!");
|
|
rtabmap_.clearPath(0);
|
|
currentMetricGoal_.setNull();
|
|
lastPublishedMetricGoal_.setNull();
|
|
goalFrameId_.clear();
|
|
latestNodeWasReached_ = false;
|
|
if(goalReachedPub_->get_subscription_count())
|
|
{
|
|
std_msgs::msg::Bool result;
|
|
result.data = false;
|
|
goalReachedPub_->publish(result);
|
|
}
|
|
}
|
|
#ifdef WITH_NAV2_MSGS
|
|
if(nav2Client_.get() != NULL && nav2Client_->action_server_is_ready())
|
|
{
|
|
nav2Client_->async_cancel_all_goals();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CoreWrapper::setLabelCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::SetLabel::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::SetLabel::Response>)
|
|
{
|
|
if(rtabmap_.labelLocation(req->node_id, req->node_label))
|
|
{
|
|
if(req->node_id > 0)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Set label \"%s\" to node %d", req->node_label.c_str(), req->node_id);
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Set label \"%s\" to last node", req->node_label.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(req->node_id > 0)
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Could not set label \"%s\" to node %d", req->node_label.c_str(), req->node_id);
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Could not set label \"%s\" to last node", req->node_label.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::listLabelsCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::ListLabels::Request>,
|
|
std::shared_ptr<rtabmap_msgs::srv::ListLabels::Response> res)
|
|
{
|
|
if(rtabmap_.getMemory())
|
|
{
|
|
std::map<int, std::string> labels = rtabmap_.getMemory()->getAllLabels();
|
|
res->ids = uKeys(labels);
|
|
res->labels = uValues(labels);
|
|
RCLCPP_INFO(this->get_logger(), "List labels service: %d labels found.", (int)res->labels.size());
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::removeLabelCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::RemoveLabel::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::RemoveLabel::Response>)
|
|
{
|
|
if(rtabmap_.getMemory())
|
|
{
|
|
int id = rtabmap_.getMemory()->getSignatureIdByLabel(req->label, true);
|
|
if(id == 0)
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Label \"%s\" not found in the map, cannot remove it!", req->label.c_str());
|
|
}
|
|
else if(!rtabmap_.labelLocation(id, ""))
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Failed removing label \"%s\".", req->label.c_str());
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Removed label \"%s\".", req->label.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::addLinkCallback(const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::AddLink::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::AddLink::Response>)
|
|
{
|
|
if(rtabmap_.getMemory())
|
|
{
|
|
RCLCPP_INFO(get_logger(), "Adding external link %d -> %d", req->link.from_id, req->link.to_id);
|
|
rtabmap_.addLink(rtabmap_conversions::linkFromROS(req->link));
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::getNodesInRadiusCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<rtabmap_msgs::srv::GetNodesInRadius::Request> req,
|
|
std::shared_ptr<rtabmap_msgs::srv::GetNodesInRadius::Response> res)
|
|
{
|
|
RCLCPP_INFO(get_logger(), "Get nodes in radius (%f): node_id=%d pose=(%f,%f,%f)", req->radius, req->node_id, req->x, req->y, req->z);
|
|
std::map<int, Transform> poses;
|
|
std::map<int, float> dists;
|
|
if(req->node_id != 0 || (req->x == 0.0f && req->y == 0.0f && req->z == 0.0f))
|
|
{
|
|
poses = rtabmap_.getNodesInRadius(req->node_id, req->radius, req->k, &dists);
|
|
}
|
|
else
|
|
{
|
|
poses = rtabmap_.getNodesInRadius(Transform(req->x, req->y, req->z, 0,0,0), req->radius, req->k, &dists);
|
|
}
|
|
|
|
//Optimized graph
|
|
res->ids.resize(poses.size());
|
|
res->poses.resize(poses.size());
|
|
res->dists_sqr.resize(poses.size());
|
|
int index = 0;
|
|
for(std::map<int, rtabmap::Transform>::const_iterator iter = poses.begin();
|
|
iter != poses.end();
|
|
++iter)
|
|
{
|
|
res->ids[index] = iter->first;
|
|
rtabmap_conversions::transformToPoseMsg(iter->second, res->poses[index]);
|
|
UASSERT(dists.find(iter->first) != dists.end());
|
|
res->dists_sqr[index] = dists.at(iter->first);
|
|
++index;
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::publishStats(const rclcpp::Time & stamp)
|
|
{
|
|
UDEBUG("Publishing stats...");
|
|
const rtabmap::Statistics & stats = rtabmap_.getStatistics();
|
|
|
|
if(infoPub_->get_subscription_count())
|
|
{
|
|
//RCLCPP_INFO(this->get_logger(), "Sending RtabmapInfo msg (last_id=%d)...", stat.refImageId());
|
|
rtabmap_msgs::msg::Info::UniquePtr msg(new rtabmap_msgs::msg::Info);
|
|
msg->header.stamp = stamp;
|
|
msg->header.frame_id = mapFrameId_;
|
|
|
|
rtabmap_conversions::infoToROS(stats, *msg);
|
|
infoPub_->publish(std::move(msg));
|
|
}
|
|
|
|
if(mapDataPub_->get_subscription_count())
|
|
{
|
|
rtabmap_msgs::msg::MapData::UniquePtr msg(new rtabmap_msgs::msg::MapData);
|
|
msg->header.stamp = stamp;
|
|
msg->header.frame_id = mapFrameId_;
|
|
|
|
rtabmap_conversions::mapDataToROS(
|
|
stats.poses(),
|
|
stats.constraints(),
|
|
stats.getSignaturesData(),
|
|
stats.mapCorrection(),
|
|
*msg);
|
|
|
|
mapDataPub_->publish(std::move(msg));
|
|
}
|
|
|
|
if(mapGraphPub_->get_subscription_count())
|
|
{
|
|
if(mapsManager_.isMapUpdated())
|
|
{
|
|
graphLatched_ = false;
|
|
}
|
|
if(!(mapsManager_.isLatching() && graphLatched_))
|
|
{
|
|
rtabmap_msgs::msg::MapGraph::UniquePtr msg(new rtabmap_msgs::msg::MapGraph);
|
|
msg->header.stamp = stamp;
|
|
msg->header.frame_id = mapFrameId_;
|
|
|
|
rtabmap_conversions::mapGraphToROS(
|
|
stats.poses(),
|
|
stats.constraints(),
|
|
stats.mapCorrection(),
|
|
*msg);
|
|
|
|
mapGraphPub_->publish(std::move(msg));
|
|
graphLatched_ = mapsManager_.isLatching();
|
|
}
|
|
// else we already published the latched graph
|
|
}
|
|
else
|
|
{
|
|
graphLatched_ = false;
|
|
}
|
|
|
|
if(odomCachePub_->get_subscription_count())
|
|
{
|
|
rtabmap_msgs::msg::MapGraph::UniquePtr msg(new rtabmap_msgs::msg::MapGraph);
|
|
msg->header.stamp = stamp;
|
|
msg->header.frame_id = mapFrameId_;
|
|
|
|
// For visualization of the constraints (MapGraph rviz plugin), we should include target nodes from the map
|
|
std::map<int, Transform> poses = stats.odomCachePoses();
|
|
// transform in map frame
|
|
for(std::map<int, Transform>::iterator iter=poses.begin();
|
|
iter!=poses.end();
|
|
++iter)
|
|
{
|
|
iter->second = stats.mapCorrection() * iter->second;
|
|
}
|
|
for(std::multimap<int, rtabmap::Link>::const_iterator iter=stats.odomCacheConstraints().begin();
|
|
iter!=stats.odomCacheConstraints().end();
|
|
++iter)
|
|
{
|
|
std::map<int, Transform>::const_iterator pter = stats.poses().find(iter->second.to());
|
|
if(pter != stats.poses().end())
|
|
{
|
|
poses.insert(*pter);
|
|
}
|
|
}
|
|
rtabmap_conversions::mapGraphToROS(
|
|
poses,
|
|
stats.odomCacheConstraints(),
|
|
stats.mapCorrection(),
|
|
*msg);
|
|
|
|
odomCachePub_->publish(std::move(msg));
|
|
}
|
|
|
|
if(localGridObstacle_->get_subscription_count() && !stats.getLastSignatureData().sensorData().gridObstacleCellsRaw().empty())
|
|
{
|
|
pcl::PCLPointCloud2::Ptr cloud = rtabmap::util3d::laserScanToPointCloud2(LaserScan::backwardCompatibility(stats.getLastSignatureData().sensorData().gridObstacleCellsRaw()));
|
|
sensor_msgs::msg::PointCloud2::UniquePtr msg(new sensor_msgs::msg::PointCloud2);
|
|
pcl_conversions::moveFromPCL(*cloud, *msg);
|
|
msg->header.stamp = stamp;
|
|
msg->header.frame_id = frameId_;
|
|
localGridObstacle_->publish(std::move(msg));
|
|
}
|
|
if(localGridEmpty_->get_subscription_count() && !stats.getLastSignatureData().sensorData().gridEmptyCellsRaw().empty())
|
|
{
|
|
pcl::PCLPointCloud2::Ptr cloud = rtabmap::util3d::laserScanToPointCloud2(LaserScan::backwardCompatibility(stats.getLastSignatureData().sensorData().gridEmptyCellsRaw()));
|
|
sensor_msgs::msg::PointCloud2::UniquePtr msg(new sensor_msgs::msg::PointCloud2);
|
|
pcl_conversions::moveFromPCL(*cloud, *msg);
|
|
msg->header.stamp = stamp;
|
|
msg->header.frame_id = frameId_;
|
|
localGridEmpty_->publish(std::move(msg));
|
|
}
|
|
if(localGridGround_->get_subscription_count() && !stats.getLastSignatureData().sensorData().gridGroundCellsRaw().empty())
|
|
{
|
|
pcl::PCLPointCloud2::Ptr cloud = rtabmap::util3d::laserScanToPointCloud2(LaserScan::backwardCompatibility(stats.getLastSignatureData().sensorData().gridGroundCellsRaw()));
|
|
sensor_msgs::msg::PointCloud2::UniquePtr msg(new sensor_msgs::msg::PointCloud2);
|
|
pcl_conversions::moveFromPCL(*cloud, *msg);
|
|
msg->header.stamp = stamp;
|
|
msg->header.frame_id = frameId_;
|
|
localGridGround_->publish(std::move(msg));
|
|
}
|
|
|
|
bool pubLabels = labelsPub_->get_subscription_count();
|
|
visualization_msgs::msg::MarkerArray markers;
|
|
if((landmarksPub_->get_subscription_count() || pubLabels) && !stats.poses().empty() && stats.poses().begin()->first < 0)
|
|
{
|
|
geometry_msgs::msg::PoseArray::UniquePtr msg(new geometry_msgs::msg::PoseArray);
|
|
msg->header.stamp = stamp;
|
|
msg->header.frame_id = mapFrameId_;
|
|
for(std::map<int, Transform>::const_iterator iter=stats.poses().begin(); iter!=stats.poses().end() && iter->first<0; ++iter)
|
|
{
|
|
geometry_msgs::msg::Pose p;
|
|
rtabmap_conversions::transformToPoseMsg(iter->second, p);
|
|
msg->poses.push_back(p);
|
|
|
|
if(pubLabels)
|
|
{
|
|
// Add landmark ids
|
|
visualization_msgs::msg::Marker marker;
|
|
marker.header.frame_id = mapFrameId_;
|
|
marker.header.stamp = stamp;
|
|
marker.ns = "landmarks";
|
|
marker.id = iter->first;
|
|
marker.action = visualization_msgs::msg::Marker::ADD;
|
|
marker.pose.position.x = iter->second.x();
|
|
marker.pose.position.y = iter->second.y();
|
|
marker.pose.position.z = iter->second.z();
|
|
marker.pose.orientation.x = 0.0;
|
|
marker.pose.orientation.y = 0.0;
|
|
marker.pose.orientation.z = 0.0;
|
|
marker.pose.orientation.w = 1.0;
|
|
marker.scale.x = 1;
|
|
marker.scale.y = 1;
|
|
marker.scale.z = 0.35;
|
|
marker.color.a = 0.7;
|
|
marker.color.r = 0.0;
|
|
marker.color.g = 1.0;
|
|
marker.color.b = 0.0;
|
|
marker.lifetime = rclcpp::Duration::from_seconds(2.0f/rate_);
|
|
|
|
marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
|
|
marker.text = uNumber2Str(iter->first);
|
|
|
|
markers.markers.push_back(marker);
|
|
}
|
|
}
|
|
|
|
landmarksPub_->publish(std::move(msg));
|
|
}
|
|
|
|
bool pubPath = mapPathPub_->get_subscription_count();
|
|
if(pubLabels || pubPath)
|
|
{
|
|
if(stats.poses().size())
|
|
{
|
|
nav_msgs::msg::Path path;
|
|
if(pubPath)
|
|
{
|
|
// Ignore pose of current location in Localization mode
|
|
path.poses.resize(stats.poses().size()-(rtabmap_.getMemory()->isIncremental()?0:1));
|
|
}
|
|
int oi = 0;
|
|
for(std::map<int, Transform>::const_iterator poseIter=stats.poses().begin();
|
|
poseIter!=stats.poses().end();
|
|
++poseIter)
|
|
{
|
|
if(pubLabels && rtabmap_.getMemory())
|
|
{
|
|
// Add labels
|
|
std::map<int, std::string>::const_iterator lter = rtabmap_.getMemory()->getAllLabels().find(poseIter->first);
|
|
if(lter != rtabmap_.getMemory()->getAllLabels().end() && !lter->second.empty())
|
|
{
|
|
visualization_msgs::msg::Marker marker;
|
|
marker.header.frame_id = mapFrameId_;
|
|
marker.header.stamp = stamp;
|
|
marker.ns = "labels";
|
|
marker.id = -poseIter->first;
|
|
marker.action = visualization_msgs::msg::Marker::ADD;
|
|
marker.pose.position.x = poseIter->second.x();
|
|
marker.pose.position.y = poseIter->second.y();
|
|
marker.pose.position.z = poseIter->second.z();
|
|
marker.pose.orientation.x = 0.0;
|
|
marker.pose.orientation.y = 0.0;
|
|
marker.pose.orientation.z = 0.0;
|
|
marker.pose.orientation.w = 1.0;
|
|
marker.scale.x = 1;
|
|
marker.scale.y = 1;
|
|
marker.scale.z = 0.5;
|
|
marker.color.a = 0.7;
|
|
marker.color.r = 1.0;
|
|
marker.color.g = 0.0;
|
|
marker.color.b = 0.0;
|
|
|
|
marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
|
|
marker.text = lter->second;
|
|
|
|
markers.markers.push_back(marker);
|
|
}
|
|
|
|
// Add node ids
|
|
visualization_msgs::msg::Marker marker;
|
|
marker.header.frame_id = mapFrameId_;
|
|
marker.header.stamp = stamp;
|
|
marker.ns = "ids";
|
|
marker.id = poseIter->first;
|
|
marker.action = visualization_msgs::msg::Marker::ADD;
|
|
marker.pose.position.x = poseIter->second.x();
|
|
marker.pose.position.y = poseIter->second.y();
|
|
marker.pose.position.z = poseIter->second.z();
|
|
marker.pose.orientation.x = 0.0;
|
|
marker.pose.orientation.y = 0.0;
|
|
marker.pose.orientation.z = 0.0;
|
|
marker.pose.orientation.w = 1.0;
|
|
marker.scale.x = 1;
|
|
marker.scale.y = 1;
|
|
marker.scale.z = 0.2;
|
|
marker.color.a = 0.5;
|
|
marker.color.r = 1.0;
|
|
marker.color.g = 1.0;
|
|
marker.color.b = 1.0;
|
|
marker.lifetime = rclcpp::Duration::from_seconds(2.0f/rate_);
|
|
|
|
marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
|
|
marker.text = uNumber2Str(poseIter->first);
|
|
|
|
markers.markers.push_back(marker);
|
|
}
|
|
if(pubPath && (rtabmap_.getMemory()->isIncremental() || poseIter->first != stats.poses().rbegin()->first))
|
|
{
|
|
rtabmap_conversions::transformToPoseMsg(poseIter->second, path.poses.at(oi).pose);
|
|
path.poses.at(oi).header.frame_id = mapFrameId_;
|
|
path.poses.at(oi).header.stamp = stamp;
|
|
++oi;
|
|
}
|
|
}
|
|
|
|
if(pubLabels && markers.markers.size())
|
|
{
|
|
labelsPub_->publish(markers);
|
|
}
|
|
if(pubPath && oi)
|
|
{
|
|
path.header.frame_id = mapFrameId_;
|
|
path.header.stamp = stamp;
|
|
path.poses.resize(oi);
|
|
mapPathPub_->publish(path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::publishCurrentGoal(const rclcpp::Time & stamp)
|
|
{
|
|
if(!currentMetricGoal_.isNull() && currentMetricGoal_ != lastPublishedMetricGoal_)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Publishing next goal: %d -> %s",
|
|
rtabmap_.getPathCurrentGoalId(), currentMetricGoal_.prettyPrint().c_str());
|
|
|
|
geometry_msgs::msg::PoseStamped poseMsg;
|
|
poseMsg.header.frame_id = mapFrameId_;
|
|
poseMsg.header.stamp = stamp;
|
|
rtabmap_conversions::transformToPoseMsg(currentMetricGoal_, poseMsg.pose);
|
|
#ifdef WITH_NAV2_MSGS
|
|
if(useActionForGoal_)
|
|
{
|
|
if(nav2Client_.get() == NULL || !nav2Client_->action_server_is_ready())
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Connecting to navigate_to_pose action server...");
|
|
if(nav2Client_.get() == NULL)
|
|
{
|
|
nav2Client_ = rclcpp_action::create_client<NavigateToPose>(
|
|
this,
|
|
"navigate_to_pose");
|
|
}
|
|
if (!nav2Client_->wait_for_action_server(std::chrono::duration<double>(5.0))) {
|
|
RCLCPP_ERROR(this->get_logger(), " navigate_to_pose action server not available after waiting 5 seconds");
|
|
}
|
|
}
|
|
if(nav2Client_.get() != NULL && nav2Client_->action_server_is_ready())
|
|
{
|
|
NavigateToPose::Goal goal_msg;
|
|
goal_msg.pose = poseMsg;
|
|
|
|
auto send_goal_options = rclcpp_action::Client<NavigateToPose>::SendGoalOptions();
|
|
send_goal_options.goal_response_callback = std::bind(&CoreWrapper::goalResponseCallback, this, std::placeholders::_1);
|
|
send_goal_options.result_callback = std::bind(&CoreWrapper::resultCallback, this, std::placeholders::_1);
|
|
nav2Client_->async_send_goal(goal_msg, send_goal_options);
|
|
lastPublishedMetricGoal_ = currentMetricGoal_;
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Cannot connect to navigate_to_pose action server!");
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if(nextMetricGoalPub_->get_subscription_count())
|
|
{
|
|
nextMetricGoalPub_->publish(poseMsg);
|
|
lastPublishedMetricGoal_ = currentMetricGoal_;
|
|
}
|
|
}
|
|
}
|
|
#ifdef WITH_NAV2_MSGS
|
|
void CoreWrapper::goalResponseCallback(
|
|
#ifdef NAV_MSGS_FOXY
|
|
std::shared_future<GoalHandleNav2::SharedPtr> future)
|
|
{
|
|
auto goal_handle = future.get();
|
|
#else
|
|
const GoalHandleNav2::SharedPtr & goal_handle)
|
|
{
|
|
#endif
|
|
if (!goal_handle) {
|
|
RCLCPP_ERROR(this->get_logger(), "Goal was rejected by server");
|
|
rtabmap_.clearPath(1);
|
|
currentMetricGoal_.setNull();
|
|
lastPublishedMetricGoal_.setNull();
|
|
goalFrameId_.clear();
|
|
latestNodeWasReached_ = false;
|
|
} else {
|
|
RCLCPP_INFO(this->get_logger(), "Goal accepted by server, waiting for result");
|
|
lastGoalSent_ = goal_handle->get_goal_id();
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::resultCallback(
|
|
const GoalHandleNav2::WrappedResult & result)
|
|
{
|
|
bool ignore = false;
|
|
if(!currentMetricGoal_.isNull())
|
|
{
|
|
if(result.code == rclcpp_action::ResultCode::SUCCEEDED)
|
|
{
|
|
if(rtabmap_.getPath().size() &&
|
|
rtabmap_.getPathCurrentGoalId() != rtabmap_.getPath().back().first &&
|
|
(!uContains(rtabmap_.getLocalOptimizedPoses(), rtabmap_.getPath().back().first) || !latestNodeWasReached_))
|
|
{
|
|
RCLCPP_WARN(this->get_logger(), "Planning: nav2 reached current goal but it is not "
|
|
"the last one planned by rtabmap. A new goal should be sent when "
|
|
"rtabmap will be able to retrieve next locations on the path.");
|
|
ignore = true;
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Planning: nav2 success!");
|
|
}
|
|
}
|
|
else if(result.code==rclcpp_action::ResultCode::ABORTED && result.goal_id != lastGoalSent_)
|
|
{
|
|
// Just ignored, it is from an old goal
|
|
ignore = true;
|
|
}
|
|
else
|
|
{
|
|
RCLCPP_ERROR(this->get_logger(), "Planning: nav2 failed for some reason: %s. Aborting the plan...",
|
|
result.code==rclcpp_action::ResultCode::ABORTED?"Aborted":
|
|
result.code==rclcpp_action::ResultCode::CANCELED?"Canceled":"Unkown");
|
|
}
|
|
|
|
if(!ignore && goalReachedPub_->get_subscription_count())
|
|
{
|
|
std_msgs::msg::Bool resultMsg;
|
|
resultMsg.data = result.code == rclcpp_action::ResultCode::SUCCEEDED;
|
|
goalReachedPub_->publish(resultMsg);
|
|
}
|
|
}
|
|
|
|
if(!ignore)
|
|
{
|
|
rtabmap_.clearPath(1);
|
|
currentMetricGoal_.setNull();
|
|
lastPublishedMetricGoal_.setNull();
|
|
goalFrameId_.clear();
|
|
latestNodeWasReached_ = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CoreWrapper::publishLocalPath(const rclcpp::Time & stamp)
|
|
{
|
|
if(rtabmap_.getPath().size())
|
|
{
|
|
std::vector<std::pair<int, Transform> > poses = rtabmap_.getPathNextPoses();
|
|
if(poses.size())
|
|
{
|
|
if(localPathPub_->get_subscription_count() || localPathNodesPub_->get_subscription_count())
|
|
{
|
|
nav_msgs::msg::Path path;
|
|
rtabmap_msgs::msg::Path pathNodes;
|
|
path.header.frame_id = pathNodes.header.frame_id = mapFrameId_;
|
|
path.header.stamp = pathNodes.header.stamp = stamp;
|
|
path.poses.resize(poses.size());
|
|
pathNodes.node_ids.resize(poses.size());
|
|
pathNodes.poses.resize(poses.size());
|
|
int oi = 0;
|
|
for(std::vector<std::pair<int, Transform> >::iterator iter=poses.begin(); iter!=poses.end(); ++iter)
|
|
{
|
|
path.poses[oi].header = path.header;
|
|
rtabmap_conversions::transformToPoseMsg(iter->second, path.poses[oi].pose);
|
|
pathNodes.poses[oi] = path.poses[oi].pose;
|
|
pathNodes.node_ids[oi] = iter->first;
|
|
++oi;
|
|
}
|
|
if(localPathPub_->get_subscription_count())
|
|
{
|
|
localPathPub_->publish(path);
|
|
}
|
|
if(localPathNodesPub_->get_subscription_count())
|
|
{
|
|
localPathNodesPub_->publish(pathNodes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::publishGlobalPath(const rclcpp::Time & stamp)
|
|
{
|
|
if((globalPathPub_->get_subscription_count() || globalPathNodesPub_->get_subscription_count()) && rtabmap_.getPath().size())
|
|
{
|
|
Transform pose = uValue(rtabmap_.getLocalOptimizedPoses(), rtabmap_.getPathCurrentGoalId(), Transform());
|
|
if(!pose.isNull() && rtabmap_.getPathCurrentGoalIndex() < rtabmap_.getPath().size())
|
|
{
|
|
// transform the global path in the goal referential
|
|
Transform t = pose * rtabmap_.getPath().at(rtabmap_.getPathCurrentGoalIndex()).second.inverse();
|
|
|
|
nav_msgs::msg::Path path;
|
|
rtabmap_msgs::msg::Path pathNodes;
|
|
path.header.frame_id = pathNodes.header.frame_id = mapFrameId_;
|
|
path.header.stamp = pathNodes.header.stamp = stamp;
|
|
path.poses.resize(rtabmap_.getPath().size());
|
|
pathNodes.node_ids.resize(rtabmap_.getPath().size());
|
|
pathNodes.poses.resize(rtabmap_.getPath().size());
|
|
int oi = 0;
|
|
for(std::vector<std::pair<int, Transform> >::const_iterator iter=rtabmap_.getPath().begin(); iter!=rtabmap_.getPath().end(); ++iter)
|
|
{
|
|
path.poses[oi].header = path.header;
|
|
rtabmap_conversions::transformToPoseMsg(t*iter->second, path.poses[oi].pose);
|
|
pathNodes.poses[oi] = path.poses[oi].pose;
|
|
pathNodes.node_ids[oi] = iter->first;
|
|
++oi;
|
|
}
|
|
Transform goalLocalTransform = Transform::getIdentity();
|
|
if(!goalFrameId_.empty() && goalFrameId_.compare(frameId_) != 0)
|
|
{
|
|
Transform localT = rtabmap_conversions::getTransform(frameId_, goalFrameId_, stamp, *tfBuffer_, waitForTransform_);
|
|
if(!localT.isNull())
|
|
{
|
|
goalLocalTransform = localT.inverse().to3DoF();
|
|
}
|
|
}
|
|
|
|
if(!rtabmap_.getPathTransformToGoal().isIdentity() || !goalLocalTransform.isIdentity())
|
|
{
|
|
path.poses.resize(path.poses.size()+1);
|
|
path.poses[path.poses.size()-1].header = path.header;
|
|
pathNodes.node_ids.resize(pathNodes.node_ids.size()+1);
|
|
pathNodes.poses.resize(pathNodes.poses.size()+1);
|
|
Transform p = t * rtabmap_.getPath().back().second*rtabmap_.getPathTransformToGoal() * goalLocalTransform;
|
|
rtabmap_conversions::transformToPoseMsg(p, path.poses[path.poses.size()-1].pose);
|
|
pathNodes.poses[pathNodes.poses.size()-1] = path.poses[path.poses.size()-1].pose;
|
|
pathNodes.node_ids[pathNodes.node_ids.size()-1] = 0;
|
|
}
|
|
if(globalPathPub_->get_subscription_count())
|
|
{
|
|
globalPathPub_->publish(path);
|
|
}
|
|
if(globalPathNodesPub_->get_subscription_count())
|
|
{
|
|
globalPathNodesPub_->publish(pathNodes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CoreWrapper::LocalizationStatusTask::LocalizationStatusTask() :
|
|
diagnostic_updater::DiagnosticTask("Localization status"),
|
|
localizationThreshold_(0.0),
|
|
localizationError_(9999)
|
|
{}
|
|
|
|
void CoreWrapper::LocalizationStatusTask::setLocalizationThreshold(double value)
|
|
{
|
|
localizationThreshold_ = value;
|
|
}
|
|
|
|
void CoreWrapper::LocalizationStatusTask::updateStatus(const cv::Mat & cov, bool twoDMapping)
|
|
{
|
|
if(localizationThreshold_ > 0.0 && !cov.empty())
|
|
{
|
|
if(cov.at<double>(0,0) >= 9999.0)
|
|
{
|
|
localizationError_ = 9999.0;
|
|
}
|
|
else
|
|
{
|
|
localizationError_ = sqrt(uMax3(cov.at<double>(0,0), cov.at<double>(1,1), twoDMapping?0.0:cov.at<double>(2,2)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::LocalizationStatusTask::run(diagnostic_updater::DiagnosticStatusWrapper &stat)
|
|
{
|
|
if(localizationError_>=9999)
|
|
{
|
|
stat.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR, "Not localized!");
|
|
}
|
|
else if(localizationError_ > localizationThreshold_)
|
|
{
|
|
stat.summary(diagnostic_msgs::msg::DiagnosticStatus::ERROR, "Localization error is high!");
|
|
}
|
|
else
|
|
{
|
|
stat.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "Localized.");
|
|
}
|
|
stat.add("Localization error (m)", localizationError_);
|
|
stat.add("loc_thr (m)", localizationThreshold_);
|
|
}
|
|
|
|
#ifdef WITH_OCTOMAP_MSGS
|
|
#ifdef RTABMAP_OCTOMAP
|
|
void CoreWrapper::octomapBinaryCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<octomap_msgs::srv::GetOctomap::Request>,
|
|
std::shared_ptr<octomap_msgs::srv::GetOctomap::Response> res)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Sending binary map data on service request");
|
|
res->map.header.frame_id = mapFrameId_;
|
|
res->map.header.stamp = now();
|
|
|
|
std::map<int, Transform> poses = rtabmap_.getLocalOptimizedPoses();
|
|
if((mappingMaxNodes_ > 0 || mappingAltitudeDelta_>0.0) && poses.size()>1)
|
|
{
|
|
poses = filterNodesToAssemble(poses, poses.rbegin()->second);
|
|
}
|
|
|
|
mapsManager_.updateMapCaches(poses, rtabmap_.getMemory(), false, true);
|
|
|
|
const rtabmap::OctoMap * octomap = mapsManager_.getOctomap();
|
|
if(octomap->octree()->size()>0)
|
|
{
|
|
octomap_msgs::binaryMapToMsg(*octomap->octree(), res->map);
|
|
}
|
|
}
|
|
|
|
void CoreWrapper::octomapFullCallback(
|
|
const std::shared_ptr<rmw_request_id_t>,
|
|
const std::shared_ptr<octomap_msgs::srv::GetOctomap::Request>,
|
|
std::shared_ptr<octomap_msgs::srv::GetOctomap::Response> res)
|
|
{
|
|
RCLCPP_INFO(this->get_logger(), "Sending full map data on service request");
|
|
res->map.header.frame_id = mapFrameId_;
|
|
res->map.header.stamp = now();
|
|
|
|
std::map<int, Transform> poses = rtabmap_.getLocalOptimizedPoses();
|
|
if((mappingMaxNodes_ > 0 || mappingAltitudeDelta_>0.0) && poses.size()>1)
|
|
{
|
|
poses = filterNodesToAssemble(poses, poses.rbegin()->second);
|
|
}
|
|
|
|
mapsManager_.updateMapCaches(poses, rtabmap_.getMemory(), false, true);
|
|
|
|
const rtabmap::OctoMap * octomap = mapsManager_.getOctomap();
|
|
if(octomap->octree()->size()>0)
|
|
{
|
|
octomap_msgs::fullMapToMsg(*octomap->octree(), res->map);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
#include "rclcpp_components/register_node_macro.hpp"
|
|
|
|
// Register the component with class_loader.
|
|
// This acts as a sort of entry point, allowing the component to be discoverable when its library
|
|
// is being loaded into a running process.
|
|
RCLCPP_COMPONENTS_REGISTER_NODE(rtabmap_slam::CoreWrapper)
|