feat(slam): add rtabmap_ros
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(rtabmap_util)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
|
||||
find_package(RTABMap REQUIRED)
|
||||
MESSAGE(STATUS "RTABMap=${RTABMap_TARGETS}")
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
find_package(cv_bridge REQUIRED)
|
||||
find_package(image_transport REQUIRED)
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(rclcpp_components REQUIRED)
|
||||
find_package(sensor_msgs REQUIRED)
|
||||
find_package(stereo_msgs REQUIRED)
|
||||
find_package(nav_msgs REQUIRED)
|
||||
find_package(std_msgs REQUIRED)
|
||||
find_package(tf2 REQUIRED)
|
||||
find_package(tf2_geometry_msgs REQUIRED)
|
||||
find_package(tf2_ros REQUIRED)
|
||||
find_package(laser_geometry REQUIRED)
|
||||
find_package(pcl_conversions REQUIRED)
|
||||
find_package(pcl_ros REQUIRED)
|
||||
find_package(message_filters REQUIRED)
|
||||
find_package(rtabmap_msgs REQUIRED)
|
||||
find_package(rtabmap_conversions REQUIRED)
|
||||
|
||||
# Optional components
|
||||
find_package(octomap_msgs)
|
||||
find_package(grid_map_ros)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
# libraries
|
||||
SET(Libraries
|
||||
cv_bridge
|
||||
image_transport
|
||||
rclcpp
|
||||
rclcpp_components
|
||||
sensor_msgs
|
||||
stereo_msgs
|
||||
nav_msgs
|
||||
std_msgs
|
||||
tf2
|
||||
tf2_geometry_msgs
|
||||
tf2_ros
|
||||
laser_geometry
|
||||
pcl_conversions
|
||||
pcl_ros
|
||||
message_filters
|
||||
rtabmap_msgs
|
||||
rtabmap_conversions
|
||||
)
|
||||
|
||||
if("$ENV{ROS_DISTRO}" STRLESS "jazzy")
|
||||
add_definitions(-DPRE_ROS_JAZZY)
|
||||
endif()
|
||||
|
||||
###########
|
||||
## Build ##
|
||||
###########
|
||||
|
||||
SET(rtabmap_util_plugins_lib_src
|
||||
src/MapsManager.cpp
|
||||
src/nodelets/point_cloud_xyzrgb.cpp
|
||||
src/nodelets/point_cloud_xyz.cpp
|
||||
src/nodelets/disparity_to_depth.cpp
|
||||
src/nodelets/pointcloud_to_depthimage.cpp
|
||||
src/nodelets/obstacles_detection.cpp
|
||||
src/nodelets/point_cloud_aggregator.cpp
|
||||
src/nodelets/point_cloud_assembler.cpp
|
||||
src/nodelets/imu_to_tf.cpp
|
||||
src/nodelets/lidar_deskewing.cpp
|
||||
src/nodelets/rgbd_relay.cpp
|
||||
src/nodelets/rgbd_split.cpp
|
||||
src/nodelets/map_assembler.cpp
|
||||
)
|
||||
|
||||
|
||||
# If octomap is found, add dependency
|
||||
IF(octomap_msgs_FOUND)
|
||||
MESSAGE(STATUS "WITH octomap_msgs")
|
||||
include_directories(
|
||||
${octomap_msgs_INCLUDE_DIRS}
|
||||
)
|
||||
SET(Libraries
|
||||
octomap_msgs
|
||||
${Libraries}
|
||||
)
|
||||
ADD_DEFINITIONS("-DWITH_OCTOMAP_MSGS")
|
||||
ENDIF(octomap_msgs_FOUND)
|
||||
|
||||
# If grid_map is found, add dependency
|
||||
IF(grid_map_ros_FOUND)
|
||||
MESSAGE(STATUS "WITH grid_map_ros")
|
||||
include_directories(
|
||||
${grid_map_ros_INCLUDE_DIRS}
|
||||
)
|
||||
SET(Libraries
|
||||
grid_map_ros
|
||||
${Libraries}
|
||||
)
|
||||
ENDIF(grid_map_ros_FOUND)
|
||||
|
||||
############################
|
||||
## Declare a cpp library
|
||||
############################
|
||||
add_library(rtabmap_util_plugins SHARED
|
||||
${rtabmap_util_plugins_lib_src}
|
||||
)
|
||||
target_include_directories(rtabmap_util_plugins
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
|
||||
IF(octomap_msgs_FOUND)
|
||||
target_compile_definitions(rtabmap_util_plugins PUBLIC -DWITH_OCTOMAP_MSGS)
|
||||
ENDIF(octomap_msgs_FOUND)
|
||||
|
||||
IF(grid_map_ros_FOUND)
|
||||
target_compile_definitions(rtabmap_util_plugins PUBLIC -DWITH_GRID_MAP_ROS)
|
||||
ENDIF(grid_map_ros_FOUND)
|
||||
|
||||
ament_target_dependencies(rtabmap_util_plugins ${Libraries})
|
||||
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::RGBDRelay")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::RGBDSplit")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::DisparityToDepth")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::ImuToTF")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::LidarDeskewing")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::PointCloudXYZ")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::PointCloudXYZRGB")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::PointCloudToDepthImage")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::ObstaclesDetection")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::PointCloudAggregator")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::PointCloudAssembler")
|
||||
rclcpp_components_register_nodes(rtabmap_util_plugins "rtabmap_util::MapAssembler")
|
||||
|
||||
add_executable(rtabmap_rgbd_relay src/RGBDRelayNode.cpp)
|
||||
ament_target_dependencies(rtabmap_rgbd_relay ${Libraries})
|
||||
target_link_libraries(rtabmap_rgbd_relay rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_rgbd_relay PROPERTIES OUTPUT_NAME "rgbd_relay")
|
||||
|
||||
add_executable(rtabmap_rgbd_split src/RGBDSplitNode.cpp)
|
||||
ament_target_dependencies(rtabmap_rgbd_split ${Libraries})
|
||||
target_link_libraries(rtabmap_rgbd_split rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_rgbd_split PROPERTIES OUTPUT_NAME "rgbd_split")
|
||||
|
||||
#add_executable(rtabmap_map_optimizer src/MapOptimizerNode.cpp)
|
||||
#ament_target_dependencies(rtabmap_map_optimizer ${Libraries})
|
||||
#target_link_libraries(rtabmap_map_optimizer rtabmap_util_plugins)
|
||||
#set_target_properties(rtabmap_map_optimizer PROPERTIES OUTPUT_NAME "map_optimizer")
|
||||
|
||||
add_executable(rtabmap_map_assembler src/MapAssemblerNode.cpp)
|
||||
ament_target_dependencies(rtabmap_map_assembler ${Libraries})
|
||||
target_link_libraries(rtabmap_map_assembler rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_map_assembler PROPERTIES OUTPUT_NAME "map_assembler")
|
||||
|
||||
add_executable(rtabmap_imu_to_tf src/ImuToTFNode.cpp)
|
||||
ament_target_dependencies(rtabmap_imu_to_tf ${Libraries})
|
||||
target_link_libraries(rtabmap_imu_to_tf rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_imu_to_tf PROPERTIES OUTPUT_NAME "imu_to_tf")
|
||||
|
||||
add_executable(rtabmap_disparity_to_depth src/DisparityToDepthNode.cpp)
|
||||
ament_target_dependencies(rtabmap_disparity_to_depth ${Libraries})
|
||||
target_link_libraries(rtabmap_disparity_to_depth rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_disparity_to_depth PROPERTIES OUTPUT_NAME "disparity_to_depth")
|
||||
|
||||
add_executable(rtabmap_lidar_deskewing src/LidarDeskewingNode.cpp)
|
||||
ament_target_dependencies(rtabmap_lidar_deskewing ${Libraries})
|
||||
target_link_libraries(rtabmap_lidar_deskewing rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_lidar_deskewing PROPERTIES OUTPUT_NAME "lidar_deskewing")
|
||||
|
||||
add_executable(rtabmap_point_cloud_xyz src/PointCloudXYZNode.cpp)
|
||||
ament_target_dependencies(rtabmap_point_cloud_xyz ${Libraries})
|
||||
target_link_libraries(rtabmap_point_cloud_xyz rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_point_cloud_xyz PROPERTIES OUTPUT_NAME "point_cloud_xyz")
|
||||
|
||||
add_executable(rtabmap_point_cloud_xyzrgb src/PointCloudXYZRGBNode.cpp)
|
||||
ament_target_dependencies(rtabmap_point_cloud_xyzrgb ${Libraries})
|
||||
target_link_libraries(rtabmap_point_cloud_xyzrgb rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_point_cloud_xyzrgb PROPERTIES OUTPUT_NAME "point_cloud_xyzrgb")
|
||||
|
||||
#add_executable(rtabmap_data_player src/DbPlayerNode.cpp)
|
||||
#ament_target_dependencies(rtabmap_data_player ${Libraries})
|
||||
#target_link_libraries(rtabmap_data_player rtabmap_util_plugins)
|
||||
#set_target_properties(rtabmap_data_player PROPERTIES OUTPUT_NAME "data_player")
|
||||
|
||||
#add_executable(rtabmap_odom_msg_to_tf src/OdomMsgToTFNode.cpp)
|
||||
#ament_target_dependencies(rtabmap_odom_msg_to_tf ${Libraries})
|
||||
#target_link_libraries(rtabmap_odom_msg_to_tf rtabmap_util_plugins)
|
||||
#set_target_properties(rtabmap_odom_msg_to_tf PROPERTIES OUTPUT_NAME "odom_msg_to_tf")
|
||||
|
||||
add_executable(rtabmap_pointcloud_to_depthimage src/PointCloudToDepthImageNode.cpp)
|
||||
ament_target_dependencies(rtabmap_pointcloud_to_depthimage ${Libraries})
|
||||
target_link_libraries(rtabmap_pointcloud_to_depthimage rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_pointcloud_to_depthimage PROPERTIES OUTPUT_NAME "pointcloud_to_depthimage")
|
||||
|
||||
add_executable(rtabmap_obstacles_detection src/ObstaclesDetectionNode.cpp)
|
||||
ament_target_dependencies(rtabmap_obstacles_detection ${Libraries})
|
||||
target_link_libraries(rtabmap_obstacles_detection rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_obstacles_detection PROPERTIES OUTPUT_NAME "obstacles_detection")
|
||||
|
||||
add_executable(rtabmap_point_cloud_aggregator src/PointCloudAggregatorNode.cpp)
|
||||
ament_target_dependencies(rtabmap_point_cloud_aggregator ${Libraries})
|
||||
target_link_libraries(rtabmap_point_cloud_aggregator rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_point_cloud_aggregator PROPERTIES OUTPUT_NAME "point_cloud_aggregator")
|
||||
|
||||
add_executable(rtabmap_point_cloud_assembler src/PointCloudAssemblerNode.cpp)
|
||||
ament_target_dependencies(rtabmap_point_cloud_assembler ${Libraries})
|
||||
target_link_libraries(rtabmap_point_cloud_assembler rtabmap_util_plugins)
|
||||
set_target_properties(rtabmap_point_cloud_assembler PROPERTIES OUTPUT_NAME "point_cloud_assembler")
|
||||
|
||||
#############
|
||||
## Install ##
|
||||
#############
|
||||
|
||||
ament_export_dependencies(${Libraries})
|
||||
ament_export_include_directories(include)
|
||||
ament_export_targets(${PROJECT_NAME}) # To include downstream with targets
|
||||
ament_export_libraries(rtabmap_util_plugins) # To include downstream without targets
|
||||
|
||||
|
||||
# Install Python executables
|
||||
install(PROGRAMS
|
||||
# scripts/patrol.py
|
||||
# scripts/objects_to_tags.py
|
||||
# scripts/point_to_tf.py
|
||||
# scripts/netvlad_tf_ros.py
|
||||
# scripts/gazebo_ground_truth.py
|
||||
scripts/transform_to_tf.py
|
||||
scripts/yaml_to_camera_info.py
|
||||
scripts/republish_tf_static.py
|
||||
DESTINATION lib/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
install(TARGETS
|
||||
rtabmap_util_plugins
|
||||
EXPORT ${PROJECT_NAME}
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
install(TARGETS
|
||||
# rtabmap_map_optimizer
|
||||
# rtabmap_data_player
|
||||
# rtabmap_odom_msg_to_tf
|
||||
rtabmap_imu_to_tf
|
||||
rtabmap_disparity_to_depth
|
||||
rtabmap_rgbd_relay
|
||||
rtabmap_rgbd_split
|
||||
rtabmap_lidar_deskewing
|
||||
rtabmap_point_cloud_xyz
|
||||
rtabmap_point_cloud_xyzrgb
|
||||
rtabmap_pointcloud_to_depthimage
|
||||
rtabmap_obstacles_detection
|
||||
rtabmap_point_cloud_aggregator
|
||||
rtabmap_point_cloud_assembler
|
||||
rtabmap_map_assembler
|
||||
DESTINATION lib/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
install(DIRECTORY include/
|
||||
DESTINATION include
|
||||
FILES_MATCHING PATTERN "*.h"
|
||||
)
|
||||
|
||||
ament_package()
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef MAPSMANAGER_H_
|
||||
#define MAPSMANAGER_H_
|
||||
|
||||
#include <rtabmap/core/Signature.h>
|
||||
#include <rtabmap/core/Parameters.h>
|
||||
#include <rtabmap/core/FlannIndex.h>
|
||||
#include <rtabmap/core/LocalGrid.h>
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <sensor_msgs/msg/point_cloud2.hpp>
|
||||
#include <nav_msgs/msg/occupancy_grid.hpp>
|
||||
|
||||
#if defined(WITH_OCTOMAP_MSGS) and defined(RTABMAP_OCTOMAP)
|
||||
#include <octomap_msgs/msg/octomap.hpp>
|
||||
#endif
|
||||
|
||||
#if defined(WITH_GRID_MAP_ROS) and defined(RTABMAP_GRIDMAP)
|
||||
#include <grid_map_msgs/msg/grid_map.hpp>
|
||||
#endif
|
||||
|
||||
namespace rtabmap {
|
||||
class OctoMap;
|
||||
class Memory;
|
||||
class OccupancyGrid;
|
||||
class LocalGridMaker;
|
||||
class GridMap;
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
namespace rtabmap_util {
|
||||
|
||||
class MapsManager {
|
||||
public:
|
||||
MapsManager();
|
||||
virtual ~MapsManager();
|
||||
void init(rclcpp::Node & node, const std::string & name, bool usePublicNamespace);
|
||||
void clear();
|
||||
bool hasSubscribers() const;
|
||||
bool isLatching() const {return latching_;}
|
||||
bool isMapUpdated() const;
|
||||
void backwardCompatibilityParameters(rclcpp::Node & node, rtabmap::ParametersMap & parameters) const;
|
||||
void setParameters(const rtabmap::ParametersMap & parameters);
|
||||
void set2DMap(const cv::Mat & map, float xMin, float yMin, float cellSize, const std::map<int, rtabmap::Transform> & poses, const rtabmap::Memory * memory = 0);
|
||||
|
||||
std::map<int, rtabmap::Transform> getFilteredPoses(
|
||||
const std::map<int, rtabmap::Transform> & poses);
|
||||
|
||||
std::map<int, rtabmap::Transform> updateMapCaches(
|
||||
const std::map<int, rtabmap::Transform> & poses,
|
||||
const rtabmap::Memory * memory,
|
||||
bool updateGrid,
|
||||
bool updateOctomap,
|
||||
const std::map<int, rtabmap::Signature> & signatures = std::map<int, rtabmap::Signature>());
|
||||
|
||||
void publishMaps(
|
||||
const std::map<int, rtabmap::Transform> & poses,
|
||||
const rclcpp::Time & stamp,
|
||||
const std::string & mapFrameId);
|
||||
|
||||
cv::Mat getGridMap(
|
||||
float & xMin,
|
||||
float & yMin,
|
||||
float & gridCellSize);
|
||||
|
||||
cv::Mat getGridProbMap(
|
||||
float & xMin,
|
||||
float & yMin,
|
||||
float & gridCellSize);
|
||||
|
||||
#ifdef RTABMAP_OCTOMAP
|
||||
const rtabmap::OctoMap * getOctomap() const {return octomap_;}
|
||||
#endif
|
||||
const rtabmap::OccupancyGrid * getOccupancyGrid() const {return occupancyGrid_;}
|
||||
const rtabmap::LocalGridMaker * getLocalMapMaker() const {return localMapMaker_;}
|
||||
|
||||
private:
|
||||
// mapping stuff
|
||||
bool cloudOutputVoxelized_;
|
||||
bool cloudSubtractFiltering_;
|
||||
int cloudSubtractFilteringMinNeighbors_;
|
||||
double mapFilterRadius_;
|
||||
double mapFilterAngle_;
|
||||
bool mapCacheCleanup_;
|
||||
bool alwaysUpdateMap_;
|
||||
bool scanEmptyRayTracing_;
|
||||
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr cloudMapPub_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr cloudGroundPub_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr cloudObstaclesPub_;
|
||||
rclcpp::Publisher<nav_msgs::msg::OccupancyGrid>::SharedPtr gridMapPub_;
|
||||
rclcpp::Publisher<nav_msgs::msg::OccupancyGrid>::SharedPtr gridProbMapPub_;
|
||||
#ifdef RTABMAP_OCTOMAP
|
||||
#ifdef WITH_OCTOMAP_MSGS
|
||||
rclcpp::Publisher<octomap_msgs::msg::Octomap>::SharedPtr octoMapPubBin_;
|
||||
rclcpp::Publisher<octomap_msgs::msg::Octomap>::SharedPtr octoMapPubFull_;
|
||||
#endif
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr octoMapCloud_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr octoMapFrontierCloud_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr octoMapGroundCloud_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr octoMapObstacleCloud_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr octoMapEmptySpace_;
|
||||
rclcpp::Publisher<nav_msgs::msg::OccupancyGrid>::SharedPtr octoMapProj_;
|
||||
#endif
|
||||
#if defined(WITH_GRID_MAP_ROS) and defined(RTABMAP_GRIDMAP)
|
||||
rclcpp::Publisher<grid_map_msgs::msg::GridMap>::SharedPtr elevationMapPub_;
|
||||
#endif
|
||||
|
||||
std::map<int, rtabmap::Transform> assembledGroundPoses_;
|
||||
std::map<int, rtabmap::Transform> assembledObstaclePoses_;
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr assembledObstacles_;
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr assembledGround_;
|
||||
rtabmap::FlannIndex assembledGroundIndex_;
|
||||
rtabmap::FlannIndex assembledObstacleIndex_;
|
||||
std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > groundClouds_;
|
||||
std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > obstacleClouds_;
|
||||
|
||||
rtabmap::LocalGridCache localMaps_;
|
||||
|
||||
rtabmap::OccupancyGrid * occupancyGrid_;
|
||||
rtabmap::LocalGridMaker * localMapMaker_;
|
||||
bool gridUpdated_;
|
||||
|
||||
#ifdef RTABMAP_OCTOMAP
|
||||
rtabmap::OctoMap * octomap_;
|
||||
#endif
|
||||
int octomapTreeDepth_;
|
||||
bool octomapUpdated_;
|
||||
|
||||
#ifdef RTABMAP_GRIDMAP
|
||||
rtabmap::GridMap * elevationMap_;
|
||||
#endif
|
||||
bool elevationMapUpdated_;
|
||||
|
||||
rtabmap::ParametersMap parameters_;
|
||||
|
||||
bool latching_;
|
||||
std::map<void*, bool> latched_;
|
||||
};
|
||||
|
||||
} // namespace rtabmap_util
|
||||
|
||||
#endif /* MAPSMANAGER_H_ */
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright (c) 2010-2022, Mathieu Labbe
|
||||
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 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 <rclcpp/rclcpp.hpp>
|
||||
#include <rtabmap/utilite/UEventsHandler.h>
|
||||
|
||||
namespace rtabmap_util {
|
||||
|
||||
class ULogToRosout : public UEventsHandler
|
||||
{
|
||||
public:
|
||||
ULogToRosout(const rclcpp::Node * node) :
|
||||
node_(node)
|
||||
{
|
||||
registerToEventsManager();
|
||||
}
|
||||
virtual ~ULogToRosout()
|
||||
{
|
||||
unregisterFromEventsManager();
|
||||
}
|
||||
protected:
|
||||
virtual bool handleEvent(UEvent * event)
|
||||
{
|
||||
if(event->getClassName().compare("ULogEvent") == 0)
|
||||
{
|
||||
ULogEvent * logEvent = (ULogEvent *)event;
|
||||
if(logEvent->getCode() == ULogger::kDebug)
|
||||
{
|
||||
RCLCPP_DEBUG(node_->get_logger(), "%s", logEvent->getMsg().c_str());
|
||||
}
|
||||
else if(logEvent->getCode() == ULogger::kInfo)
|
||||
{
|
||||
RCLCPP_INFO(node_->get_logger(), "%s", logEvent->getMsg().c_str());
|
||||
}
|
||||
else if(logEvent->getCode() == ULogger::kWarning)
|
||||
{
|
||||
RCLCPP_WARN(node_->get_logger(), "%s", logEvent->getMsg().c_str());
|
||||
}
|
||||
else if(logEvent->getCode() == ULogger::kError)
|
||||
{
|
||||
RCLCPP_ERROR(node_->get_logger(), "%s", logEvent->getMsg().c_str());
|
||||
}
|
||||
else if(logEvent->getCode() == ULogger::kFatal)
|
||||
{
|
||||
RCLCPP_FATAL(node_->get_logger(), "%s", logEvent->getMsg().c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const rclcpp::Node * node_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright (c) 2010-2023, 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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include <sensor_msgs/msg/image.h>
|
||||
#include <stereo_msgs/msg/disparity_image.hpp>
|
||||
|
||||
#include <image_transport/image_transport.hpp>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
class DisparityToDepth : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit DisparityToDepth(const rclcpp::NodeOptions & options);
|
||||
virtual ~DisparityToDepth();
|
||||
|
||||
private:
|
||||
void callback(const stereo_msgs::msg::DisparityImage::ConstSharedPtr msg);
|
||||
|
||||
private:
|
||||
image_transport::Publisher pub32f_;
|
||||
image_transport::Publisher pub16u_;
|
||||
rclcpp::Subscription<stereo_msgs::msg::DisparityImage>::SharedPtr sub_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright (c) 2010-2023, 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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include <sensor_msgs/msg/imu.hpp>
|
||||
|
||||
#include <tf2_ros/buffer.h>
|
||||
#include <tf2_ros/transform_listener.h>
|
||||
#include <tf2_ros/transform_broadcaster.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
class ImuToTF : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit ImuToTF(const rclcpp::NodeOptions & options);
|
||||
virtual ~ImuToTF();
|
||||
|
||||
private:
|
||||
void imuCallback(const sensor_msgs::msg::Imu::ConstSharedPtr msg);
|
||||
|
||||
private:
|
||||
rclcpp::Subscription<sensor_msgs::msg::Imu>::SharedPtr sub_;
|
||||
std::shared_ptr<tf2_ros::TransformBroadcaster> tfBroadcaster_;
|
||||
std::string fixedFrameId_;
|
||||
std::string baseFrameId_;
|
||||
std::shared_ptr<tf2_ros::Buffer> tfBuffer_;
|
||||
std::shared_ptr<tf2_ros::TransformListener> tfListener_;
|
||||
double waitForTransformDuration_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright (c) 2010-2023, 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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include <tf2_ros/buffer.h>
|
||||
#include <tf2_ros/transform_listener.h>
|
||||
|
||||
#include <sensor_msgs/msg/point_cloud2.hpp>
|
||||
#include <sensor_msgs/msg/laser_scan.hpp>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
class LidarDeskewing : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit LidarDeskewing(const rclcpp::NodeOptions & options);
|
||||
virtual ~LidarDeskewing();
|
||||
|
||||
private:
|
||||
void callbackScan(const sensor_msgs::msg::LaserScan::ConstSharedPtr msg);
|
||||
void callbackCloud(const sensor_msgs::msg::PointCloud2::ConstSharedPtr msg);
|
||||
|
||||
private:
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubScan_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubCloud_;
|
||||
rclcpp::Subscription<sensor_msgs::msg::LaserScan>::SharedPtr subScan_;
|
||||
rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr subCloud_;
|
||||
std::string fixedFrameId_;
|
||||
double waitForTransformDuration_;
|
||||
bool slerp_;
|
||||
std::shared_ptr<tf2_ros::Buffer> tfBuffer_;
|
||||
std::shared_ptr<tf2_ros::TransformListener> tfListener_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright (c) 2010-2024, 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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include <rtabmap_msgs/srv/get_map.hpp>
|
||||
#include "rtabmap_msgs/msg/map_data.hpp"
|
||||
#include "rtabmap_util/MapsManager.h"
|
||||
|
||||
#include <std_srvs/srv/empty.hpp>
|
||||
|
||||
#ifdef WITH_OCTOMAP_MSGS
|
||||
#ifdef RTABMAP_OCTOMAP
|
||||
#include <octomap_msgs/srv/get_octomap.hpp>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
class MapAssembler: public rclcpp::Node
|
||||
{
|
||||
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit MapAssembler(const rclcpp::NodeOptions & options);
|
||||
virtual ~MapAssembler();
|
||||
|
||||
private:
|
||||
void mapDataReceivedCallback(const rtabmap_msgs::msg::MapData::ConstSharedPtr msg);
|
||||
|
||||
void processMapData(const rtabmap_msgs::msg::MapData & msg);
|
||||
|
||||
void reset(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>);
|
||||
|
||||
void timerCallback();
|
||||
|
||||
#ifdef WITH_OCTOMAP_MSGS
|
||||
#ifdef RTABMAP_OCTOMAP
|
||||
void 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);
|
||||
|
||||
void 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);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
private:
|
||||
MapsManager mapsManager_;
|
||||
std::map<int, rtabmap::Signature> nodes_;
|
||||
std::map<int, rtabmap::Transform> optimizedPoses_;
|
||||
std::string mapFrameId_;
|
||||
std::string rtabmapNodeName_;
|
||||
|
||||
rclcpp::Subscription<rtabmap_msgs::msg::MapData>::SharedPtr mapDataSub_;
|
||||
|
||||
rclcpp::Service<std_srvs::srv::Empty>::SharedPtr resetService_;
|
||||
|
||||
rclcpp::CallbackGroup::SharedPtr serviceCbGroup_;
|
||||
rclcpp::CallbackGroup::SharedPtr timerCbGroup_;
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
rclcpp::Client<rtabmap_msgs::srv::GetMap>::SharedPtr client_;
|
||||
|
||||
#ifdef WITH_OCTOMAP_MSGS
|
||||
#ifdef RTABMAP_OCTOMAP
|
||||
rclcpp::Service<octomap_msgs::srv::GetOctomap>::SharedPtr octomapBinarySrv_;
|
||||
rclcpp::Service<octomap_msgs::srv::GetOctomap>::SharedPtr octomapFullSrv_;
|
||||
#endif
|
||||
#endif
|
||||
bool localGridsRegenerated_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
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 "rclcpp/rclcpp.hpp"
|
||||
#include <rtabmap_util/visibility.h>
|
||||
|
||||
#include <tf2_ros/buffer.h>
|
||||
#include <tf2_ros/transform_listener.h>
|
||||
|
||||
#include <sensor_msgs/msg/point_cloud2.hpp>
|
||||
|
||||
#include <rtabmap/core/LocalGridMaker.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
class ObstaclesDetection : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit ObstaclesDetection(const rclcpp::NodeOptions & options);
|
||||
virtual ~ObstaclesDetection() {}
|
||||
|
||||
private:
|
||||
void callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg);
|
||||
|
||||
private:
|
||||
std::string frameId_;
|
||||
std::string mapFrameId_;
|
||||
double waitForTransform_;
|
||||
|
||||
rtabmap::LocalGridMaker localMapMaker_;
|
||||
bool mapFrameProjection_;
|
||||
bool warned_;
|
||||
float rangeMin_;
|
||||
float rangeMax_;
|
||||
|
||||
std::shared_ptr<tf2_ros::Buffer> tfBuffer_;
|
||||
std::shared_ptr<tf2_ros::TransformListener> tfListener_;
|
||||
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr groundPub_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr obstaclesPub_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr projObstaclesPub_;
|
||||
|
||||
rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr cloudSub_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, 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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include <sensor_msgs/msg/point_cloud2.hpp>
|
||||
|
||||
#include <tf2_ros/buffer.h>
|
||||
#include <tf2_ros/transform_listener.h>
|
||||
|
||||
#include <message_filters/sync_policies/approximate_time.h>
|
||||
#include <message_filters/subscriber.h>
|
||||
#include <message_filters/sync_policies/exact_time.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
/**
|
||||
* Nodelet used to merge point clouds from different sensors into a single
|
||||
* assembled cloud. If fixed_frame_id is set and approx_sync is true,
|
||||
* the clouds are adjusted to include the displacement of the robot
|
||||
* in the output cloud.
|
||||
*/
|
||||
class PointCloudAggregator : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit PointCloudAggregator(const rclcpp::NodeOptions & options);
|
||||
|
||||
virtual ~PointCloudAggregator();
|
||||
|
||||
private:
|
||||
void clouds4_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_1,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_2,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_3,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_4);
|
||||
void clouds3_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_1,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_2,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_3);
|
||||
void clouds2_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_1,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_2);
|
||||
void combineClouds(const std::vector<sensor_msgs::msg::PointCloud2::ConstSharedPtr> & cloudMsgs);
|
||||
|
||||
std::thread * warningThread_;
|
||||
bool callbackCalled_;
|
||||
|
||||
typedef message_filters::sync_policies::ExactTime<sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2> ExactSync4Policy;
|
||||
typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2> ApproxSync4Policy;
|
||||
typedef message_filters::sync_policies::ExactTime<sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2> ExactSync3Policy;
|
||||
typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2> ApproxSync3Policy;
|
||||
typedef message_filters::sync_policies::ExactTime<sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2> ExactSync2Policy;
|
||||
typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::msg::PointCloud2, sensor_msgs::msg::PointCloud2> ApproxSync2Policy;
|
||||
message_filters::Synchronizer<ExactSync4Policy>* exactSync4_;
|
||||
message_filters::Synchronizer<ApproxSync4Policy>* approxSync4_;
|
||||
message_filters::Synchronizer<ExactSync3Policy>* exactSync3_;
|
||||
message_filters::Synchronizer<ApproxSync3Policy>* approxSync3_;
|
||||
message_filters::Synchronizer<ExactSync2Policy>* exactSync2_;
|
||||
message_filters::Synchronizer<ApproxSync2Policy>* approxSync2_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::PointCloud2> cloudSub_1_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::PointCloud2> cloudSub_2_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::PointCloud2> cloudSub_3_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::PointCloud2> cloudSub_4_;
|
||||
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr cloudPub_;
|
||||
|
||||
std::string frameId_;
|
||||
std::string fixedFrameId_;
|
||||
double waitForTransform_;
|
||||
bool xyzOutput_;
|
||||
std::shared_ptr<tf2_ros::Buffer> tfBuffer_;
|
||||
std::shared_ptr<tf2_ros::TransformListener> tfListener_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, 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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include <tf2_ros/buffer.h>
|
||||
#include <tf2_ros/transform_listener.h>
|
||||
|
||||
#include <nav_msgs/msg/odometry.hpp>
|
||||
#include <sensor_msgs/msg/point_cloud2.hpp>
|
||||
|
||||
#include <message_filters/subscriber.h>
|
||||
#include <message_filters/sync_policies/exact_time.h>
|
||||
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
#include <rtabmap/core/util3d.h>
|
||||
#include <rtabmap/core/util3d_filtering.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
/**
|
||||
* This nodelet can assemble a number of clouds (max_clouds) coming
|
||||
* from the same sensor, taking into account the displacement of the robot based on
|
||||
* fixed_frame_id, then publish the resulting cloud.
|
||||
* If fixed_frame_id is set to "" (empty), the nodelet will subscribe to
|
||||
* an odom topic that should have the exact same stamp than to input cloud.
|
||||
* The output cloud has the same stamp and frame than the last assembled cloud.
|
||||
*/
|
||||
class PointCloudAssembler : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit PointCloudAssembler(const rclcpp::NodeOptions & options);
|
||||
|
||||
virtual ~PointCloudAssembler();
|
||||
|
||||
private:
|
||||
void callbackCloudOdomInfo(
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg,
|
||||
const nav_msgs::msg::Odometry::ConstSharedPtr odomMsg,
|
||||
const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr odomInfoMsg);
|
||||
|
||||
void callbackCloudOdom(
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg,
|
||||
const nav_msgs::msg::Odometry::ConstSharedPtr odomMsg);
|
||||
|
||||
void callbackCloud(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg);
|
||||
|
||||
private:
|
||||
std::thread * warningThread_;
|
||||
bool callbackCalled_;
|
||||
|
||||
rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr cloudSub_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr cloudPub_;
|
||||
|
||||
typedef message_filters::sync_policies::ExactTime<sensor_msgs::msg::PointCloud2, nav_msgs::msg::Odometry> syncPolicy;
|
||||
typedef message_filters::sync_policies::ExactTime<sensor_msgs::msg::PointCloud2, nav_msgs::msg::Odometry, rtabmap_msgs::msg::OdomInfo> syncInfoPolicy;
|
||||
message_filters::Synchronizer<syncPolicy>* exactSync_;
|
||||
message_filters::Synchronizer<syncInfoPolicy>* exactInfoSync_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::PointCloud2> syncCloudSub_;
|
||||
message_filters::Subscriber<nav_msgs::msg::Odometry> syncOdomSub_;
|
||||
message_filters::Subscriber<rtabmap_msgs::msg::OdomInfo> syncOdomInfoSub_;
|
||||
|
||||
int maxClouds_;
|
||||
int skipClouds_;
|
||||
int cloudsSkipped_;
|
||||
bool circularBuffer_;
|
||||
double linearUpdate_;
|
||||
double angularUpdate_;
|
||||
double assemblingTime_;
|
||||
double waitForTransform_;
|
||||
double rangeMin_;
|
||||
double rangeMax_;
|
||||
double voxelSize_;
|
||||
double noiseRadius_;
|
||||
int noiseMinNeighbors_;
|
||||
bool removeZ_;
|
||||
std::string fixedFrameId_;
|
||||
std::string frameId_;
|
||||
std::shared_ptr<tf2_ros::Buffer> tfBuffer_;
|
||||
std::shared_ptr<tf2_ros::TransformListener> tfListener_;
|
||||
rtabmap::Transform previousPose_;
|
||||
|
||||
std::list<pcl::PCLPointCloud2::Ptr> clouds_;
|
||||
|
||||
std::string subscribedTopicsMsg_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include <sensor_msgs/msg/point_cloud2.hpp>
|
||||
#include <sensor_msgs/msg/image.hpp>
|
||||
#include <sensor_msgs/msg/camera_info.hpp>
|
||||
#include <stereo_msgs/msg/disparity_image.hpp>
|
||||
|
||||
#include <image_transport/image_transport.hpp>
|
||||
#include <image_transport/subscriber_filter.hpp>
|
||||
|
||||
#include <message_filters/sync_policies/approximate_time.h>
|
||||
#include <message_filters/sync_policies/exact_time.h>
|
||||
#include <message_filters/subscriber.h>
|
||||
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <pcl/pcl_base.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
class PointCloudXYZ : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit PointCloudXYZ(const rclcpp::NodeOptions & options);
|
||||
virtual ~PointCloudXYZ();
|
||||
|
||||
private:
|
||||
void callback(
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr depth,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfo);
|
||||
|
||||
void callbackDisparity(
|
||||
const stereo_msgs::msg::DisparityImage::ConstSharedPtr disparityMsg,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfo);
|
||||
|
||||
void processAndPublish(pcl::PointCloud<pcl::PointXYZ>::Ptr & pclCloud, pcl::IndicesPtr & indices, const std_msgs::msg::Header & header);
|
||||
|
||||
private:
|
||||
|
||||
double maxDepth_;
|
||||
double minDepth_;
|
||||
double voxelSize_;
|
||||
int decimation_;
|
||||
double noiseFilterRadius_;
|
||||
int noiseFilterMinNeighbors_;
|
||||
int normalK_;
|
||||
double normalRadius_;
|
||||
bool filterNaNs_;
|
||||
std::vector<float> roiRatios_;
|
||||
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr cloudPub_;
|
||||
|
||||
image_transport::SubscriberFilter imageDepthSub_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::CameraInfo> cameraInfoSub_;
|
||||
|
||||
message_filters::Subscriber<stereo_msgs::msg::DisparityImage> disparitySub_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::CameraInfo> disparityCameraInfoSub_;
|
||||
|
||||
typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::msg::Image, sensor_msgs::msg::CameraInfo> MyApproxSyncDepthPolicy;
|
||||
message_filters::Synchronizer<MyApproxSyncDepthPolicy> * approxSyncDepth_;
|
||||
|
||||
typedef message_filters::sync_policies::ApproximateTime<stereo_msgs::msg::DisparityImage, sensor_msgs::msg::CameraInfo> MyApproxSyncDisparityPolicy;
|
||||
message_filters::Synchronizer<MyApproxSyncDisparityPolicy> * approxSyncDisparity_;
|
||||
|
||||
typedef message_filters::sync_policies::ExactTime<sensor_msgs::msg::Image, sensor_msgs::msg::CameraInfo> MyExactSyncDepthPolicy;
|
||||
message_filters::Synchronizer<MyExactSyncDepthPolicy> * exactSyncDepth_;
|
||||
|
||||
typedef message_filters::sync_policies::ExactTime<stereo_msgs::msg::DisparityImage, sensor_msgs::msg::CameraInfo> MyExactSyncDisparityPolicy;
|
||||
message_filters::Synchronizer<MyExactSyncDisparityPolicy> * exactSyncDisparity_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include <sensor_msgs/msg/point_cloud2.hpp>
|
||||
#include <sensor_msgs/msg/image.hpp>
|
||||
#include <sensor_msgs/msg/camera_info.hpp>
|
||||
#include <stereo_msgs/msg/disparity_image.hpp>
|
||||
|
||||
#include <rtabmap_msgs/msg/rgbd_image.hpp>
|
||||
|
||||
#include <image_transport/image_transport.hpp>
|
||||
#include <image_transport/subscriber_filter.hpp>
|
||||
|
||||
#include <message_filters/sync_policies/approximate_time.h>
|
||||
#include <message_filters/sync_policies/exact_time.h>
|
||||
#include <message_filters/subscriber.h>
|
||||
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <pcl/pcl_base.h>
|
||||
|
||||
#include <rtabmap/core/Parameters.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
class PointCloudXYZRGB : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit PointCloudXYZRGB(const rclcpp::NodeOptions & options);
|
||||
virtual ~PointCloudXYZRGB();
|
||||
|
||||
private:
|
||||
void depthCallback(
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr image,
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr imageDepth,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfo);
|
||||
|
||||
void disparityCallback(
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr image,
|
||||
const stereo_msgs::msg::DisparityImage::ConstSharedPtr imageDisparity,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfo);
|
||||
|
||||
void stereoCallback(const sensor_msgs::msg::Image::ConstSharedPtr imageLeft,
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr imageRight,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr camInfoLeft,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr camInfoRight);
|
||||
|
||||
void rgbdImageCallback(const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image);
|
||||
|
||||
void processAndPublish(pcl::PointCloud<pcl::PointXYZRGB>::Ptr & pclCloud, pcl::IndicesPtr & indices, const std_msgs::msg::Header & header);
|
||||
|
||||
private:
|
||||
|
||||
double maxDepth_;
|
||||
double minDepth_;
|
||||
double voxelSize_;
|
||||
int decimation_;
|
||||
double noiseFilterRadius_;
|
||||
int noiseFilterMinNeighbors_;
|
||||
int normalK_;
|
||||
double normalRadius_;
|
||||
bool filterNaNs_;
|
||||
std::vector<float> roiRatios_;
|
||||
rtabmap::ParametersMap stereoBMParameters_;
|
||||
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr cloudPub_;
|
||||
|
||||
rclcpp::Subscription<rtabmap_msgs::msg::RGBDImage>::SharedPtr rgbdImageSub_;
|
||||
|
||||
image_transport::SubscriberFilter imageSub_;
|
||||
image_transport::SubscriberFilter imageDepthSub_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::CameraInfo> cameraInfoSub_;
|
||||
|
||||
message_filters::Subscriber<stereo_msgs::msg::DisparityImage> imageDisparitySub_;
|
||||
|
||||
image_transport::SubscriberFilter imageLeft_;
|
||||
image_transport::SubscriberFilter imageRight_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::CameraInfo> cameraInfoLeft_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::CameraInfo> cameraInfoRight_;
|
||||
|
||||
typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::msg::Image, sensor_msgs::msg::Image, sensor_msgs::msg::CameraInfo> MyApproxSyncDepthPolicy;
|
||||
message_filters::Synchronizer<MyApproxSyncDepthPolicy> * approxSyncDepth_;
|
||||
|
||||
typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::msg::Image, stereo_msgs::msg::DisparityImage, sensor_msgs::msg::CameraInfo> MyApproxSyncDisparityPolicy;
|
||||
message_filters::Synchronizer<MyApproxSyncDisparityPolicy> * approxSyncDisparity_;
|
||||
|
||||
typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::msg::Image, sensor_msgs::msg::Image, sensor_msgs::msg::CameraInfo, sensor_msgs::msg::CameraInfo> MyApproxSyncStereoPolicy;
|
||||
message_filters::Synchronizer<MyApproxSyncStereoPolicy> * approxSyncStereo_;
|
||||
|
||||
typedef message_filters::sync_policies::ExactTime<sensor_msgs::msg::Image, sensor_msgs::msg::Image, sensor_msgs::msg::CameraInfo> MyExactSyncDepthPolicy;
|
||||
message_filters::Synchronizer<MyExactSyncDepthPolicy> * exactSyncDepth_;
|
||||
|
||||
typedef message_filters::sync_policies::ExactTime<sensor_msgs::msg::Image, stereo_msgs::msg::DisparityImage, sensor_msgs::msg::CameraInfo> MyExactSyncDisparityPolicy;
|
||||
message_filters::Synchronizer<MyExactSyncDisparityPolicy> * exactSyncDisparity_;
|
||||
|
||||
typedef message_filters::sync_policies::ExactTime<sensor_msgs::msg::Image, sensor_msgs::msg::Image, sensor_msgs::msg::CameraInfo, sensor_msgs::msg::CameraInfo> MyExactSyncStereoPolicy;
|
||||
message_filters::Synchronizer<MyExactSyncStereoPolicy> * exactSyncStereo_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include <sensor_msgs/msg/point_cloud2.hpp>
|
||||
#include <sensor_msgs/msg/image.hpp>
|
||||
#include <sensor_msgs/msg/camera_info.hpp>
|
||||
|
||||
#include <image_transport/image_transport.hpp>
|
||||
|
||||
#include <tf2_ros/buffer.h>
|
||||
#include <tf2_ros/transform_listener.h>
|
||||
|
||||
#include <message_filters/sync_policies/approximate_time.h>
|
||||
#include <message_filters/sync_policies/exact_time.h>
|
||||
#include <message_filters/subscriber.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
class PointCloudToDepthImage : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit PointCloudToDepthImage(const rclcpp::NodeOptions & options);
|
||||
virtual ~PointCloudToDepthImage();
|
||||
|
||||
private:
|
||||
void callback(
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr pointCloud2Msg,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfoMsg);
|
||||
|
||||
private:
|
||||
image_transport::Publisher depthImage16Pub_;
|
||||
image_transport::Publisher depthImage32Pub_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::CameraInfo>::SharedPtr cameraInfo16Pub_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::CameraInfo>::SharedPtr cameraInfo32Pub_;
|
||||
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pointCloudTransformedPub_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::PointCloud2> pointCloudSub_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::CameraInfo> cameraInfoSub_;
|
||||
std::string fixedFrameId_;
|
||||
std::shared_ptr<tf2_ros::Buffer> tfBuffer_;
|
||||
std::shared_ptr<tf2_ros::TransformListener> tfListener_;
|
||||
double waitForTransform_;
|
||||
int fillHolesSize_;
|
||||
double fillHolesError_;
|
||||
int fillIterations_;
|
||||
int decimation_;
|
||||
bool upscale_;
|
||||
double upscaleDepthErrorRatio_;
|
||||
|
||||
typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::msg::PointCloud2, sensor_msgs::msg::CameraInfo> MyApproxSyncPolicy;
|
||||
message_filters::Synchronizer<MyApproxSyncPolicy> * approxSync_;
|
||||
typedef message_filters::sync_policies::ExactTime<sensor_msgs::msg::PointCloud2, sensor_msgs::msg::CameraInfo> MyExactSyncPolicy;
|
||||
message_filters::Synchronizer<MyExactSyncPolicy> * exactSync_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "rtabmap_msgs/msg/rgbd_image.hpp"
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
class RGBDRelay : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit RGBDRelay(const rclcpp::NodeOptions & options);
|
||||
virtual ~RGBDRelay() {}
|
||||
private:
|
||||
void callback(const rtabmap_msgs::msg::RGBDImage::SharedPtr input) const;
|
||||
|
||||
private:
|
||||
bool compress_;
|
||||
bool uncompress_;
|
||||
rclcpp::Subscription<rtabmap_msgs::msg::RGBDImage>::SharedPtr rgbdImageSub_;
|
||||
rclcpp::Publisher<rtabmap_msgs::msg::RGBDImage>::SharedPtr rgbdImagePub_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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_util/visibility.h>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include <sensor_msgs/image_encodings.hpp>
|
||||
|
||||
#include <image_transport/image_transport.hpp>
|
||||
|
||||
#include "rtabmap_msgs/msg/rgbd_image.hpp"
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
class RGBDSplit : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
RTABMAP_UTIL_PUBLIC
|
||||
explicit RGBDSplit(const rclcpp::NodeOptions & options);
|
||||
|
||||
virtual ~RGBDSplit() {}
|
||||
|
||||
void callback(const rtabmap_msgs::msg::RGBDImage::SharedPtr input) const;
|
||||
|
||||
private:
|
||||
rclcpp::Subscription<rtabmap_msgs::msg::RGBDImage>::SharedPtr rgbdImageSub_;
|
||||
|
||||
image_transport::CameraPublisher rgbPub_;
|
||||
image_transport::CameraPublisher depthPub_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2016 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RTABMAP_UTIL__VISIBILITY_CONTROL_H_
|
||||
#define RTABMAP_UTIL__VISIBILITY_CONTROL_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
|
||||
// https://gcc.gnu.org/wiki/Visibility
|
||||
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#ifdef __GNUC__
|
||||
#define RTABMAP_UTIL_EXPORT __attribute__ ((dllexport))
|
||||
#define RTABMAP_UTIL_IMPORT __attribute__ ((dllimport))
|
||||
#else
|
||||
#define RTABMAP_UTIL_EXPORT __declspec(dllexport)
|
||||
#define RTABMAP_UTIL_IMPORT __declspec(dllimport)
|
||||
#endif
|
||||
#ifdef RTABMAP_UTIL_BUILDING_DLL
|
||||
#define RTABMAP_UTIL_PUBLIC RTABMAP_UTIL_EXPORT
|
||||
#else
|
||||
#define RTABMAP_UTIL_PUBLIC RTABMAP_UTIL_IMPORT
|
||||
#endif
|
||||
#define RTABMAP_UTIL_PUBLIC_TYPE RTABMAP_UTIL_PUBLIC
|
||||
#define RTABMAP_UTIL_LOCAL
|
||||
#else
|
||||
#define RTABMAP_UTIL_EXPORT __attribute__ ((visibility("default")))
|
||||
#define RTABMAP_UTIL_IMPORT
|
||||
#if __GNUC__ >= 4
|
||||
#define RTABMAP_UTIL_PUBLIC __attribute__ ((visibility("default")))
|
||||
#define RTABMAP_UTIL_LOCAL __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define RTABMAP_UTIL_PUBLIC
|
||||
#define RTABMAP_UTIL_LOCAL
|
||||
#endif
|
||||
#define RTABMAP_UTIL_PUBLIC_TYPE
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RTABMAP_UTIL__VISIBILITY_CONTROL_H_
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>rtabmap_util</name>
|
||||
<version>0.22.0</version>
|
||||
<description>RTAB-Map's various useful nodes and nodelets.</description>
|
||||
<maintainer email="matlabbe@gmail.com">Mathieu Labbe</maintainer>
|
||||
<author>Mathieu Labbe</author>
|
||||
<license>BSD</license>
|
||||
<url type="bugtracker">https://github.com/introlab/rtabmap_ros/issues</url>
|
||||
<url type="repository">https://github.com/introlab/rtabmap_ros</url>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<build_depend>ros_environment</build_depend>
|
||||
|
||||
<depend>cv_bridge</depend>
|
||||
<depend>image_transport</depend>
|
||||
<depend>rclcpp</depend>
|
||||
<depend>rclcpp_components</depend>
|
||||
<depend>octomap_msgs</depend>
|
||||
<depend>sensor_msgs</depend>
|
||||
<depend>stereo_msgs</depend>
|
||||
<depend>nav_msgs</depend>
|
||||
<depend>std_msgs</depend>
|
||||
<depend>tf2</depend>
|
||||
<depend>tf2_geometry_msgs</depend>
|
||||
<depend>tf2_ros</depend>
|
||||
<depend>laser_geometry</depend>
|
||||
<depend>pcl_conversions</depend>
|
||||
<depend>pcl_ros</depend>
|
||||
<depend>message_filters</depend>
|
||||
<depend>rtabmap_msgs</depend>
|
||||
<depend>rtabmap_conversions</depend>
|
||||
<depend>grid_map_ros</depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Similar to map_assembler node, this minimal python example shows how
|
||||
# to reconstruct the obstacle map by subscribing only to
|
||||
# graph and latest data added to map (for constant network bandwidth usage).
|
||||
|
||||
import rospy
|
||||
from sets import Set
|
||||
|
||||
import message_filters
|
||||
from rtabmap_msgs.msg import MapGraph
|
||||
from sensor_msgs.msg import PointCloud2
|
||||
from geometry_msgs.msg import Pose
|
||||
from geometry_msgs.msg import TransformStamped
|
||||
from tf2_sensor_msgs.tf2_sensor_msgs import do_transform_cloud
|
||||
|
||||
|
||||
posesDict = {}
|
||||
cloudsDict = {}
|
||||
assembledCloud = PointCloud2()
|
||||
pub = rospy.Publisher('assembled_local_grids', PointCloud2, queue_size=10)
|
||||
|
||||
def callback(graph, cloud):
|
||||
global assembledCloud
|
||||
global posesDict
|
||||
global cloudsDict
|
||||
global pub
|
||||
|
||||
begin = rospy.get_time()
|
||||
|
||||
nodeId = graph.posesId[-1]
|
||||
pose = graph.poses[-1]
|
||||
size = cloud.width
|
||||
|
||||
posesDict[nodeId] = pose
|
||||
cloudsDict[nodeId] = cloud
|
||||
|
||||
# Update pose of our buffered clouds.
|
||||
# Check also if the clouds have moved because of a loop closure. If so, we have to update the rendering.
|
||||
maxDiff = 0
|
||||
for i in range(0,len(graph.posesId)):
|
||||
if graph.posesId[i] in posesDict:
|
||||
currentPose = posesDict[graph.posesId[i]].position
|
||||
newPose = graph.poses[i].position
|
||||
diff = max([abs(currentPose.x-newPose.x), abs(currentPose.y-newPose.y), abs(currentPose.z-newPose.z)])
|
||||
if maxDiff < diff:
|
||||
maxDiff = diff
|
||||
else:
|
||||
rospy.loginfo("Old node %d not found in cache, creating an empty cloud.", graph.posesId[i])
|
||||
posesDict[graph.posesId[i]] = graph.poses[i]
|
||||
cloudsDict[graph.posesId[i]] = PointCloud2()
|
||||
|
||||
# If we don't move, some nodes would be removed from the graph, so remove them from our buffered clouds.
|
||||
newGraph = Set(graph.posesId)
|
||||
totalPoints = 0
|
||||
for p in posesDict.keys():
|
||||
if p not in newGraph:
|
||||
posesDict.pop(p)
|
||||
cloudsDict.pop(p)
|
||||
else:
|
||||
totalPoints = totalPoints + cloudsDict[p].width
|
||||
|
||||
if maxDiff > 0.1:
|
||||
# if any node moved more than 10 cm, request an update of the assembled map so far
|
||||
newAssembledCloud = PointCloud2()
|
||||
rospy.loginfo("Map has been optimized! maxDiff=%.3fm, re-updating the whole map...", maxDiff)
|
||||
for i in range(0,len(graph.posesId)):
|
||||
posesDict[graph.posesId[i]] = graph.poses[i]
|
||||
t = TransformStamped()
|
||||
p = posesDict[graph.posesId[i]]
|
||||
t.transform.translation = p.position
|
||||
t.transform.rotation = p.orientation
|
||||
transformedCloud = do_transform_cloud(cloudsDict[graph.posesId[i]], t)
|
||||
if i==0:
|
||||
newAssembledCloud = transformedCloud
|
||||
else:
|
||||
newAssembledCloud.data = newAssembledCloud.data + transformedCloud.data
|
||||
newAssembledCloud.width = newAssembledCloud.width + transformedCloud.width
|
||||
newAssembledCloud.row_step = newAssembledCloud.row_step + transformedCloud.row_step
|
||||
assembledCloud = newAssembledCloud
|
||||
else:
|
||||
t = TransformStamped()
|
||||
t.transform.translation = pose.position
|
||||
t.transform.rotation = pose.orientation
|
||||
transformedCloud = do_transform_cloud(cloud, t)
|
||||
# just concatenate new cloud to current assembled map
|
||||
if assembledCloud.width == 0:
|
||||
assembledCloud = transformedCloud
|
||||
else:
|
||||
# Adding only the difference would be more efficient
|
||||
assembledCloud.data = assembledCloud.data + transformedCloud.data
|
||||
assembledCloud.width = assembledCloud.width + transformedCloud.width
|
||||
assembledCloud.row_step = assembledCloud.row_step + transformedCloud.row_step
|
||||
|
||||
updateTime = rospy.get_time() - begin
|
||||
|
||||
rospy.loginfo("Received node %d (%d pts) at xyz=%.2f %.2f %.2f, q_xyzw=%.2f %.2f %.2f %.2f (Map: Nodes=%d Points=%d Assembled=%d Update=%.0fms)",
|
||||
nodeId, size,
|
||||
pose.position.x, pose.position.y, pose.position.z,
|
||||
pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w,
|
||||
len(cloudsDict), totalPoints, assembledCloud.width, updateTime*1000)
|
||||
|
||||
assembledCloud.header = graph.header
|
||||
pub.publish(assembledCloud)
|
||||
|
||||
def main():
|
||||
rospy.init_node('assemble_local_grids', anonymous=True)
|
||||
graph_sub = message_filters.Subscriber('rtabmap/mapGraph', MapGraph)
|
||||
cloud_sub = message_filters.Subscriber('rtabmap/local_grid_obstacle', PointCloud2)
|
||||
|
||||
ts = message_filters.TimeSynchronizer([graph_sub, cloud_sub], 2)
|
||||
ts.registerCallback(callback)
|
||||
rospy.spin()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except rospy.ROSInterruptException:
|
||||
pass
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python
|
||||
import rospy
|
||||
import tf
|
||||
|
||||
from tf2_msgs.msg import TFMessage
|
||||
from gazebo_msgs.msg import LinkStates
|
||||
from geometry_msgs.msg import TransformStamped
|
||||
|
||||
target_frame_id = ""
|
||||
|
||||
def callBack(linkStates):
|
||||
global delta, first
|
||||
|
||||
found = False
|
||||
for i in range(len(linkStates.name)):
|
||||
if linkStates.name[i] == gazebo_frame_id:
|
||||
p = linkStates.pose[i]
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
roslog.warn("Gazebo link state \"" + gazebo_frame_id +"\" not found, cannot generate ground truth.")
|
||||
return
|
||||
|
||||
t = TransformStamped()
|
||||
t.header.frame_id = frame_id
|
||||
t.header.stamp = rospy.Time.now()
|
||||
|
||||
t.child_frame_id = child_frame_id
|
||||
|
||||
t.transform.translation.x = p.position.x
|
||||
t.transform.translation.y = p.position.y
|
||||
t.transform.translation.z = p.position.z
|
||||
|
||||
t.transform.rotation.x = p.orientation.x
|
||||
t.transform.rotation.y = p.orientation.y
|
||||
t.transform.rotation.z = p.orientation.z
|
||||
t.transform.rotation.w = p.orientation.w
|
||||
|
||||
tf_pub.publish(TFMessage([t]))
|
||||
|
||||
if __name__ == '__main__':
|
||||
rospy.init_node('generate_gazebo_ground_truth', disable_signals=True)
|
||||
|
||||
frame_id = rospy.get_param('~frame_id', 'world')
|
||||
child_frame_id = rospy.get_param('~child_frame_id', 'base_link_gt')
|
||||
gazebo_frame_id = rospy.get_param('~gazebo_frame_id', 'base_link')
|
||||
|
||||
gazebo_sub = rospy.Subscriber('/gazebo/link_states', LinkStates, callBack)
|
||||
|
||||
tf_pub = rospy.Publisher('/tf', TFMessage, queue_size=10)
|
||||
tf.TransformBroadcaster()
|
||||
|
||||
rospy.spin()
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Using netvlad tensorflow-v1 implementation from https://github.com/uzh-rpg/netvlad_tf_open/
|
||||
# For ROS melodic, follow the following instructions to rebuild cv_bridge with Python3
|
||||
# https://medium.com/@beta_b0t/how-to-setup-ros-with-python-3-44a69ca36674
|
||||
# On Jetpack 4.4 (18.04 and OpenCV4), use vision_opencv's noetic branch. In cv_bridge/CMakeLists.txt,
|
||||
# apply this patch:
|
||||
# -find_package(Boost REQUIRED python37)
|
||||
# +find_package(Boost REQUIRED python3)
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import roslib
|
||||
import sys
|
||||
import rospy
|
||||
import cv2
|
||||
import numpy as np
|
||||
import tensorflow as tf
|
||||
import time
|
||||
|
||||
import netvlad_tf.net_from_mat as nfm
|
||||
import netvlad_tf.nets as nets
|
||||
|
||||
from std_msgs.msg import String
|
||||
from sensor_msgs.msg import Image
|
||||
from cv_bridge import CvBridge, CvBridgeError
|
||||
from rtabmap_python import compression as cp
|
||||
from rtabmap_msgs.msg import GlobalDescriptor
|
||||
|
||||
class netvlad_ros:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.dim = rospy.get_param('~dim', 4096)
|
||||
self.scale = rospy.get_param('~scale', 1.0)
|
||||
rospy.loginfo("Parameter dim=%d", self.dim)
|
||||
rospy.loginfo("Parameter scale=%d", self.scale)
|
||||
|
||||
tf.reset_default_graph()
|
||||
|
||||
self.image_batch = tf.placeholder(
|
||||
dtype=tf.float32, shape=[None, None, None, 3])
|
||||
|
||||
self.net_out = nets.vgg16NetvladPca(self.image_batch)
|
||||
self.saver = tf.train.Saver()
|
||||
|
||||
self.sess = tf.Session()
|
||||
self.saver.restore(self.sess, nets.defaultCheckpoint())
|
||||
|
||||
self.pub = rospy.Publisher('netvlad_descriptor', GlobalDescriptor, queue_size=1)
|
||||
|
||||
self.bridge = CvBridge()
|
||||
self.image_sub = rospy.Subscriber("image",Image,self.callback, queue_size=1)
|
||||
|
||||
def callback(self,data):
|
||||
start = time.time()
|
||||
try:
|
||||
cv_image = self.bridge.imgmsg_to_cv2(data, "rgb8")
|
||||
except CvBridgeError as e:
|
||||
print(e)
|
||||
|
||||
if self.scale != 1.0:
|
||||
width = int(cv_image.shape[1] * self.scale)
|
||||
height = int(cv_image.shape[0] * self.scale)
|
||||
cv_image = cv2.resize(cv_image, (width, height), interpolation = cv2.INTER_AREA)
|
||||
|
||||
batch = np.expand_dims(cv_image, axis=0)
|
||||
result = self.sess.run(self.net_out, feed_dict={self.image_batch: batch})
|
||||
result = result[:,:self.dim]
|
||||
|
||||
descriptor = GlobalDescriptor()
|
||||
descriptor.type = 0
|
||||
descriptor.header = data.header
|
||||
descriptor.data = cp.compress(result)
|
||||
self.pub.publish(descriptor)
|
||||
end = time.time()
|
||||
rospy.loginfo("Extracting descriptor (img=%dx%d, dim=%d): %fs", cv_image.shape[1], cv_image.shape[0], self.dim, end-start)
|
||||
|
||||
def main(args):
|
||||
rospy.init_node('netvlad', anonymous=True)
|
||||
n = netvlad_ros()
|
||||
try:
|
||||
rospy.spin()
|
||||
except KeyboardInterrupt:
|
||||
print("Shutting down")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
import rospy
|
||||
from apriltag_ros.msg import AprilTagDetectionArray
|
||||
from apriltag_ros.msg import AprilTagDetection
|
||||
from find_object_2d.msg import ObjectsStamped
|
||||
import tf
|
||||
import geometry_msgs.msg
|
||||
|
||||
objFramePrefix_ = "object"
|
||||
distanceMax_ = 0.0
|
||||
|
||||
def callback(data):
|
||||
global objFramePrefix_
|
||||
global distanceMax_
|
||||
if len(data.objects.data) > 0:
|
||||
output = AprilTagDetectionArray()
|
||||
output.header = data.header
|
||||
for i in range(0,len(data.objects.data),12):
|
||||
try:
|
||||
objId = data.objects.data[i]
|
||||
(trans,quat) = listener.lookupTransform(data.header.frame_id, objFramePrefix_+'_'+str(int(objId)), data.header.stamp)
|
||||
tag = AprilTagDetection()
|
||||
tag.id.append(objId)
|
||||
tag.pose.pose.pose.position.x = trans[0]
|
||||
tag.pose.pose.pose.position.y = trans[1]
|
||||
tag.pose.pose.pose.position.z = trans[2]
|
||||
tag.pose.pose.pose.orientation.x = quat[0]
|
||||
tag.pose.pose.pose.orientation.y = quat[1]
|
||||
tag.pose.pose.pose.orientation.z = quat[2]
|
||||
tag.pose.pose.pose.orientation.w = quat[3]
|
||||
tag.pose.header = output.header
|
||||
if distanceMax_ <= 0.0 or trans[2] < distanceMax_:
|
||||
output.detections.append(tag)
|
||||
except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
|
||||
continue
|
||||
if len(output.detections) > 0:
|
||||
pub.publish(output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pub = rospy.Publisher('tag_detections', AprilTagDetectionArray, queue_size=10)
|
||||
rospy.init_node('objects_to_tags', anonymous=True)
|
||||
rospy.Subscriber("objectsStamped", ObjectsStamped, callback)
|
||||
objFramePrefix_ = rospy.get_param('~object_prefix', objFramePrefix_)
|
||||
distanceMax_ = rospy.get_param('~distance_max', distanceMax_)
|
||||
listener = tf.TransformListener()
|
||||
rospy.spin()
|
||||
Executable
+87
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python
|
||||
import rospy
|
||||
import sys
|
||||
from std_msgs.msg import Bool
|
||||
from rtabmap_ros.msg import Goal
|
||||
|
||||
pub = rospy.Publisher('rtabmap/goal_node', Goal, queue_size=1)
|
||||
waypoints = []
|
||||
currentIndex = 0
|
||||
waitingTime = 1.0
|
||||
frameId = ""
|
||||
|
||||
def callback(data):
|
||||
global currentIndex
|
||||
global waitingTime
|
||||
global frameId
|
||||
if data.data:
|
||||
rospy.loginfo(rospy.get_caller_id() + ": Goal '%s' reached! Publishing next goal in %.1f sec...", waypoints[currentIndex], waitingTime)
|
||||
else:
|
||||
rospy.loginfo(rospy.get_caller_id() + ": Goal '%s' failed! Publishing next goal in %.1f sec...", waypoints[currentIndex], waitingTime)
|
||||
|
||||
currentIndex = (currentIndex+1) % len(waypoints)
|
||||
|
||||
# Waiting time before sending next goal
|
||||
rospy.sleep(waitingTime)
|
||||
|
||||
msg = Goal()
|
||||
msg.frame_id = frameId
|
||||
try:
|
||||
int(waypoints[currentIndex])
|
||||
is_dig = True
|
||||
except ValueError:
|
||||
is_dig = False
|
||||
if is_dig:
|
||||
msg.node_id = int(waypoints[currentIndex])
|
||||
msg.node_label = ""
|
||||
else:
|
||||
msg.node_id = 0
|
||||
msg.node_label = waypoints[currentIndex]
|
||||
|
||||
rospy.loginfo(rospy.get_caller_id() + ": Publishing goal '%s'! (%d/%d)", waypoints[currentIndex], currentIndex+1, len(waypoints))
|
||||
msg.header.stamp = rospy.get_rostime()
|
||||
pub.publish(msg)
|
||||
|
||||
def main():
|
||||
rospy.init_node('patrol', anonymous=False)
|
||||
sub = rospy.Subscriber("rtabmap/goal_reached", Bool, callback)
|
||||
global waitingTime
|
||||
global frameId
|
||||
waitingTime = rospy.get_param('~time', waitingTime)
|
||||
frameId = rospy.get_param('~frame_id', frameId)
|
||||
rospy.sleep(1.) # make sure that subscribers have seen this node before sending a goal
|
||||
|
||||
rospy.loginfo(rospy.get_caller_id() + ": Waypoints: [%s]", str(waypoints).strip('[]'))
|
||||
rospy.loginfo(rospy.get_caller_id() + ": time: %f", waitingTime)
|
||||
rospy.loginfo(rospy.get_caller_id() + ": publish goal on %s", pub.resolved_name)
|
||||
rospy.loginfo(rospy.get_caller_id() + ": receive goal status on %s", sub.resolved_name)
|
||||
|
||||
# send the first goal
|
||||
msg = Goal()
|
||||
msg.frame_id = frameId
|
||||
try:
|
||||
int(waypoints[currentIndex])
|
||||
is_dig = True
|
||||
except ValueError:
|
||||
is_dig = False
|
||||
if is_dig:
|
||||
msg.node_id = int(waypoints[currentIndex])
|
||||
msg.node_label = ""
|
||||
else:
|
||||
msg.node_id = 0
|
||||
msg.node_label = waypoints[currentIndex]
|
||||
while rospy.Time.now().secs == 0:
|
||||
rospy.loginfo(rospy.get_caller_id() + ": Waiting clock...")
|
||||
rospy.sleep(.1)
|
||||
msg.header.stamp = rospy.Time.now()
|
||||
rospy.loginfo(rospy.get_caller_id() + ": Publishing goal '%s'! (%d/%d)", waypoints[currentIndex], currentIndex+1, len(waypoints))
|
||||
pub.publish(msg)
|
||||
rospy.spin()
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 3:
|
||||
print("usage: patrol.py waypointA waypointB waypointC ... [_time:=1 frame_id:=base_footprint] [topic remaps] (at least 2 waypoints, can be node id, landmark or label)")
|
||||
else:
|
||||
waypoints = sys.argv[1:]
|
||||
waypoints = [x for x in waypoints if not x.startswith('/') and not x.startswith('_')]
|
||||
main()
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
import rospy
|
||||
import tf
|
||||
from geometry_msgs.msg import PointStamped
|
||||
|
||||
def callback(point):
|
||||
global br
|
||||
global frame_id
|
||||
local_frame_id = point.header.frame_id
|
||||
if not local_frame_id:
|
||||
local_frame_id = frame_id
|
||||
br.sendTransform(
|
||||
(point.point.x, point.point.y, point.point.z),
|
||||
tf.transformations.quaternion_from_euler(0,0,0),
|
||||
point.header.stamp,
|
||||
local_frame_id,
|
||||
fixed_frame_id)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
rospy.init_node("point_to_tf", anonymous=True)
|
||||
|
||||
frame_id = rospy.get_param('~frame_id', 'point')
|
||||
fixed_frame_id = rospy.get_param('~fixed_frame_id', 'world')
|
||||
|
||||
br = tf.TransformBroadcaster()
|
||||
rospy.Subscriber("point", PointStamped, callback, queue_size=1)
|
||||
rospy.spin()
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from rclpy.qos import DurabilityPolicy
|
||||
from rclpy.qos import HistoryPolicy
|
||||
from rclpy.qos import QoSProfile
|
||||
from tf2_msgs.msg import TFMessage
|
||||
|
||||
class StaticTransformRepublisher(Node):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('static_transform_republisher')
|
||||
qos = QoSProfile(
|
||||
depth=1,
|
||||
durability=DurabilityPolicy.TRANSIENT_LOCAL,
|
||||
history=HistoryPolicy.KEEP_LAST,
|
||||
)
|
||||
self.publisher_ = self.create_publisher(TFMessage, '/tf_static', qos)
|
||||
self.data = TFMessage()
|
||||
self.subscription = self.create_subscription(
|
||||
TFMessage,
|
||||
'/tf_static_old',
|
||||
self.listener_callback,
|
||||
10)
|
||||
self.subscription # prevent unused variable warning
|
||||
|
||||
def listener_callback(self, msg):
|
||||
if len(self.data.transforms) == 0:
|
||||
self.data = msg
|
||||
else:
|
||||
self.data.transforms = self.data.transforms + msg.transforms
|
||||
self.get_logger().info('"Received /tf_static_old and republising latched /tf_static"')
|
||||
self.publisher_.publish(self.data)
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
static_transform_republisher = StaticTransformRepublisher()
|
||||
rclpy.spin(static_transform_republisher)
|
||||
minimal_publisher.destroy_node()
|
||||
rclpy.shutdown()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from geometry_msgs.msg import TransformStamped
|
||||
from tf2_ros import TransformBroadcaster
|
||||
|
||||
class TransformToTf(Node):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('transform_to_tf')
|
||||
|
||||
self.declare_parameter('frame_id', 'world')
|
||||
self.declare_parameter('child_frame_id', 'transform')
|
||||
self.frame_id = self.get_parameter('frame_id').get_parameter_value().string_value
|
||||
self.child_frame_id = self.get_parameter('child_frame_id').get_parameter_value().string_value
|
||||
|
||||
self.tf_broadcaster = TransformBroadcaster(self)
|
||||
|
||||
self.subscription = self.create_subscription(
|
||||
TransformStamped,
|
||||
'transform',
|
||||
self.callback,
|
||||
1)
|
||||
self.subscription # prevent unused variable warning
|
||||
|
||||
def callback(self, t):
|
||||
if not t.header.frame_id:
|
||||
t.header.frame_id = self.frame_id
|
||||
if not t.child_frame_id:
|
||||
t.child_frame_id = self.child_frame_id
|
||||
|
||||
self.tf_broadcaster.sendTransform(t)
|
||||
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
transform_to_tf = TransformToTf()
|
||||
rclpy.spin(transform_to_tf)
|
||||
transform_to_tf.destroy_node()
|
||||
rclpy.shutdown()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
import rclpy
|
||||
import yaml
|
||||
import sys
|
||||
from rclpy.node import Node
|
||||
from sensor_msgs.msg import CameraInfo
|
||||
from sensor_msgs.msg import Image
|
||||
|
||||
def yaml_to_CameraInfo(yaml_fname):
|
||||
with open(yaml_fname, "r") as file_handle:
|
||||
first_line = file_handle.readline()
|
||||
if "%YAML:" not in first_line:
|
||||
file_handle.seek(0)
|
||||
calib_data = yaml.load(file_handle, Loader=yaml.FullLoader)
|
||||
|
||||
msg = CameraInfo()
|
||||
msg.width = calib_data["image_width"]
|
||||
msg.height = calib_data["image_height"]
|
||||
msg.k = calib_data["camera_matrix"]["data"]
|
||||
msg.d = calib_data["distortion_coefficients"]["data"]
|
||||
msg.r = calib_data["rectification_matrix"]["data"]
|
||||
msg.p = calib_data["projection_matrix"]["data"]
|
||||
msg.distortion_model = calib_data["distortion_model"]
|
||||
return msg
|
||||
|
||||
class YamlToCameraInfo(Node):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('yaml_to_camera_info')
|
||||
|
||||
self.declare_parameter('yaml_path', '')
|
||||
self.declare_parameter('scale', 1.0)
|
||||
yaml_path = self.get_parameter('yaml_path').get_parameter_value().string_value
|
||||
scale = self.get_parameter('scale').get_parameter_value().double_value
|
||||
|
||||
if not yaml_path:
|
||||
print('yaml_path parameter should be set to path of the calibration file!')
|
||||
sys.exit(1)
|
||||
|
||||
self.declare_parameter('frame_id', '')
|
||||
self.frame_id = self.get_parameter('frame_id').get_parameter_value().string_value
|
||||
|
||||
self.camera_info_msg = yaml_to_CameraInfo(yaml_path)
|
||||
|
||||
if scale!=1.0:
|
||||
self.camera_info_msg.k[0] = self.camera_info_msg.k[0]*scale
|
||||
self.camera_info_msg.k[2] = self.camera_info_msg.k[2]*scale
|
||||
self.camera_info_msg.k[4] = self.camera_info_msg.k[4]*scale
|
||||
self.camera_info_msg.k[5] = self.camera_info_msg.k[5]*scale
|
||||
self.camera_info_msg.p[0] = self.camera_info_msg.p[0]*scale
|
||||
self.camera_info_msg.p[2] = self.camera_info_msg.p[2]*scale
|
||||
self.camera_info_msg.p[3] = self.camera_info_msg.p[3]*scale
|
||||
self.camera_info_msg.p[5] = self.camera_info_msg.p[5]*scale
|
||||
self.camera_info_msg.p[6] = self.camera_info_msg.p[6]*scale
|
||||
self.camera_info_msg.width = int(self.camera_info_msg.width*scale)
|
||||
self.camera_info_msg.height = int(self.camera_info_msg.height*scale)
|
||||
|
||||
self.publisher_ = self.create_publisher(CameraInfo, 'camera_info', 1)
|
||||
self.subscription = self.create_subscription(
|
||||
Image,
|
||||
'image',
|
||||
self.callback,
|
||||
1)
|
||||
self.subscription # prevent unused variable warning
|
||||
|
||||
def callback(self, image):
|
||||
self.camera_info_msg.header = image.header
|
||||
if self.frame_id:
|
||||
self.camera_info_msg.header.frame_id = self.frame_id
|
||||
self.publisher_.publish(self.camera_info_msg)
|
||||
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
yaml_to_camera_info = YamlToCameraInfo()
|
||||
rclpy.spin(yaml_to_camera_info)
|
||||
yaml_to_camera_info.destroy_node()
|
||||
rclpy.shutdown()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,686 @@
|
||||
/*
|
||||
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 <ros/ros.h>
|
||||
#include <sensor_msgs/Image.h>
|
||||
#include <sensor_msgs/image_encodings.h>
|
||||
#include <sensor_msgs/PointCloud2.h>
|
||||
#include <sensor_msgs/LaserScan.h>
|
||||
#include <sensor_msgs/CameraInfo.h>
|
||||
#include <sensor_msgs/NavSatFix.h>
|
||||
#include <geometry_msgs/PoseWithCovarianceStamped.h>
|
||||
#include <rosgraph_msgs/Clock.h>
|
||||
#include <pcl_conversions/pcl_conversions.h>
|
||||
#include <nav_msgs/Odometry.h>
|
||||
#ifdef PRE_ROS_IRON
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#else
|
||||
#include <cv_bridge/cv_bridge.hpp>
|
||||
#endif
|
||||
#include <image_transport/image_transport.h>
|
||||
#include <tf2_ros/transform_broadcaster.h>
|
||||
#include <std_srvs/Empty.h>
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
#include <rtabmap_msgs/SetGoal.h>
|
||||
#include <rtabmap/utilite/ULogger.h>
|
||||
#include <rtabmap/utilite/UStl.h>
|
||||
#include <rtabmap/utilite/UThread.h>
|
||||
#include <rtabmap/utilite/UDirectory.h>
|
||||
#include <rtabmap/utilite/UConversion.h>
|
||||
#include <rtabmap/core/util3d.h>
|
||||
#include <rtabmap/core/DBReader.h>
|
||||
#include <rtabmap/core/OdometryEvent.h>
|
||||
#include <cmath>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
bool spacehit()
|
||||
{
|
||||
bool charAvailable = true;
|
||||
bool hit = false;
|
||||
while(charAvailable)
|
||||
{
|
||||
termios term;
|
||||
tcgetattr(0, &term);
|
||||
|
||||
termios term2 = term;
|
||||
term2.c_lflag &= ~ICANON;
|
||||
term2.c_lflag &= ~ECHO;
|
||||
term2.c_lflag &= ~ISIG;
|
||||
term2.c_cc[VMIN] = 0;
|
||||
term2.c_cc[VTIME] = 0;
|
||||
tcsetattr(0, TCSANOW, &term2);
|
||||
|
||||
int c = getchar();
|
||||
if(c != EOF)
|
||||
{
|
||||
if(c == ' ')
|
||||
{
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
charAvailable = false;
|
||||
}
|
||||
|
||||
tcsetattr(0, TCSANOW, &term);
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool paused = false;
|
||||
bool pauseCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
|
||||
{
|
||||
if(paused)
|
||||
{
|
||||
ROS_WARN("Already paused!");
|
||||
}
|
||||
else
|
||||
{
|
||||
paused = true;
|
||||
ROS_INFO("paused!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool resumeCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
|
||||
{
|
||||
if(!paused)
|
||||
{
|
||||
ROS_WARN("Already running!");
|
||||
}
|
||||
else
|
||||
{
|
||||
paused = false;
|
||||
ROS_INFO("resumed!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
ros::init(argc, argv, "data_player");
|
||||
|
||||
//ULogger::setType(ULogger::kTypeConsole);
|
||||
//ULogger::setLevel(ULogger::kDebug);
|
||||
//ULogger::setEventLevel(ULogger::kWarning);
|
||||
|
||||
bool publishClock = false;
|
||||
for(int i=1;i<argc;++i)
|
||||
{
|
||||
if(strcmp(argv[i], "--clock") == 0)
|
||||
{
|
||||
publishClock = true;
|
||||
}
|
||||
}
|
||||
|
||||
ros::NodeHandle nh;
|
||||
ros::NodeHandle pnh("~");
|
||||
|
||||
std::string frameId = "base_link";
|
||||
std::string odomFrameId = "odom";
|
||||
std::string cameraFrameId = "camera_optical_link";
|
||||
std::string scanFrameId = "base_laser_link";
|
||||
double rate = 1.0f;
|
||||
std::string databasePath = "";
|
||||
bool publishTf = true;
|
||||
int startId = 0;
|
||||
bool useDbStamps = true;
|
||||
|
||||
pnh.param("frame_id", frameId, frameId);
|
||||
pnh.param("odom_frame_id", odomFrameId, odomFrameId);
|
||||
pnh.param("camera_frame_id", cameraFrameId, cameraFrameId);
|
||||
pnh.param("scan_frame_id", scanFrameId, scanFrameId);
|
||||
pnh.param("rate", rate, rate); // Ratio of the database stamps
|
||||
pnh.param("database", databasePath, databasePath);
|
||||
pnh.param("publish_tf", publishTf, publishTf);
|
||||
pnh.param("start_id", startId, startId);
|
||||
|
||||
// A general 360 lidar with 0.5 deg increment
|
||||
double scanAngleMin, scanAngleMax, scanAngleIncrement, scanRangeMin, scanRangeMax;
|
||||
pnh.param<double>("scan_angle_min", scanAngleMin, -M_PI);
|
||||
pnh.param<double>("scan_angle_max", scanAngleMax, M_PI);
|
||||
pnh.param<double>("scan_angle_increment", scanAngleIncrement, M_PI / 720.0);
|
||||
pnh.param<double>("scan_range_min", scanRangeMin, 0.0);
|
||||
pnh.param<double>("scan_range_max", scanRangeMax, 60);
|
||||
|
||||
ROS_INFO("frame_id = %s", frameId.c_str());
|
||||
ROS_INFO("odom_frame_id = %s", odomFrameId.c_str());
|
||||
ROS_INFO("camera_frame_id = %s", cameraFrameId.c_str());
|
||||
ROS_INFO("scan_frame_id = %s", scanFrameId.c_str());
|
||||
ROS_INFO("rate = %f", rate);
|
||||
ROS_INFO("publish_tf = %s", publishTf?"true":"false");
|
||||
ROS_INFO("start_id = %d", startId);
|
||||
ROS_INFO("Publish clock (--clock): %s", publishClock?"true":"false");
|
||||
|
||||
if(databasePath.empty())
|
||||
{
|
||||
ROS_ERROR("Parameter \"database\" must be set (path to a RTAB-Map database).");
|
||||
return -1;
|
||||
}
|
||||
databasePath = uReplaceChar(databasePath, '~', UDirectory::homeDir());
|
||||
if(databasePath.size() && databasePath.at(0) != '/')
|
||||
{
|
||||
databasePath = UDirectory::currentDir(true) + databasePath;
|
||||
}
|
||||
ROS_INFO("database = %s", databasePath.c_str());
|
||||
|
||||
rtabmap::DBReader reader(databasePath, -rate, false, false, false, startId);
|
||||
if(!reader.init())
|
||||
{
|
||||
ROS_ERROR("Cannot open database \"%s\".", databasePath.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
ros::ServiceServer pauseSrv = pnh.advertiseService("pause", pauseCallback);
|
||||
ros::ServiceServer resumeSrv = pnh.advertiseService("resume", resumeCallback);
|
||||
|
||||
image_transport::ImageTransport it(nh);
|
||||
image_transport::Publisher imagePub;
|
||||
image_transport::Publisher rgbPub;
|
||||
image_transport::Publisher depthPub;
|
||||
image_transport::Publisher leftPub;
|
||||
image_transport::Publisher rightPub;
|
||||
ros::Publisher rgbCamInfoPub;
|
||||
ros::Publisher depthCamInfoPub;
|
||||
ros::Publisher leftCamInfoPub;
|
||||
ros::Publisher rightCamInfoPub;
|
||||
ros::Publisher odometryPub;
|
||||
ros::Publisher scanPub;
|
||||
ros::Publisher scanCloudPub;
|
||||
ros::Publisher globalPosePub;
|
||||
ros::Publisher gpsFixPub;
|
||||
ros::Publisher clockPub;
|
||||
tf2_ros::TransformBroadcaster tfBroadcaster;
|
||||
|
||||
if(publishClock)
|
||||
{
|
||||
clockPub = nh.advertise<rosgraph_msgs::Clock>("/clock", 1);
|
||||
}
|
||||
|
||||
UTimer timer;
|
||||
rtabmap::CameraInfo cameraInfo;
|
||||
rtabmap::SensorData data = reader.takeImage(&cameraInfo);
|
||||
rtabmap::OdometryInfo odomInfo;
|
||||
odomInfo.reg.covariance = cameraInfo.odomCovariance;
|
||||
rtabmap::OdometryEvent odom(data, cameraInfo.odomPose, odomInfo);
|
||||
double acquisitionTime = timer.ticks();
|
||||
while(ros::ok() && odom.data().id())
|
||||
{
|
||||
ROS_INFO("Reading sensor data %d...", odom.data().id());
|
||||
|
||||
ros::Time time(odom.data().stamp());
|
||||
|
||||
if(publishClock)
|
||||
{
|
||||
rosgraph_msgs::Clock msg;
|
||||
msg.clock = time;
|
||||
clockPub.publish(msg);
|
||||
}
|
||||
|
||||
sensor_msgs::CameraInfo camInfoA; //rgb or left
|
||||
sensor_msgs::CameraInfo camInfoB; //depth or right
|
||||
|
||||
camInfoA.K.assign(0);
|
||||
camInfoA.K[0] = camInfoA.K[4] = camInfoA.K[8] = 1;
|
||||
camInfoA.R.assign(0);
|
||||
camInfoA.R[0] = camInfoA.R[4] = camInfoA.R[8] = 1;
|
||||
camInfoA.P.assign(0);
|
||||
camInfoA.P[10] = 1;
|
||||
|
||||
camInfoA.header.frame_id = cameraFrameId;
|
||||
camInfoA.header.stamp = time;
|
||||
|
||||
camInfoB = camInfoA;
|
||||
|
||||
int type = -1;
|
||||
if(!odom.data().depthRaw().empty() && (odom.data().depthRaw().type() == CV_32FC1 || odom.data().depthRaw().type() == CV_16UC1))
|
||||
{
|
||||
if(odom.data().cameraModels().size() > 1)
|
||||
{
|
||||
ROS_WARN("Multi-cameras detected in database but this node cannot send multi-images yet...");
|
||||
}
|
||||
else
|
||||
{
|
||||
//depth
|
||||
if(odom.data().cameraModels().size())
|
||||
{
|
||||
camInfoA.D.resize(5,0);
|
||||
|
||||
camInfoA.P[0] = odom.data().cameraModels()[0].fx();
|
||||
camInfoA.K[0] = odom.data().cameraModels()[0].fx();
|
||||
camInfoA.P[5] = odom.data().cameraModels()[0].fy();
|
||||
camInfoA.K[4] = odom.data().cameraModels()[0].fy();
|
||||
camInfoA.P[2] = odom.data().cameraModels()[0].cx();
|
||||
camInfoA.K[2] = odom.data().cameraModels()[0].cx();
|
||||
camInfoA.P[6] = odom.data().cameraModels()[0].cy();
|
||||
camInfoA.K[5] = odom.data().cameraModels()[0].cy();
|
||||
|
||||
camInfoB = camInfoA;
|
||||
}
|
||||
|
||||
type=0;
|
||||
|
||||
if(rgbPub.getTopic().empty()) rgbPub = it.advertise("rgb/image", 1);
|
||||
if(depthPub.getTopic().empty()) depthPub = it.advertise("depth_registered/image", 1);
|
||||
if(rgbCamInfoPub.getTopic().empty()) rgbCamInfoPub = nh.advertise<sensor_msgs::CameraInfo>("rgb/camera_info", 1);
|
||||
if(depthCamInfoPub.getTopic().empty()) depthCamInfoPub = nh.advertise<sensor_msgs::CameraInfo>("depth_registered/camera_info", 1);
|
||||
}
|
||||
}
|
||||
else if(!odom.data().rightRaw().empty() && odom.data().rightRaw().type() == CV_8U)
|
||||
{
|
||||
if(odom.data().stereoCameraModels().size() > 1)
|
||||
{
|
||||
ROS_WARN("Multi-cameras detected in database but this node cannot send multi-images yet...");
|
||||
}
|
||||
else
|
||||
{
|
||||
//stereo
|
||||
if(odom.data().stereoCameraModels()[0].isValidForProjection())
|
||||
{
|
||||
camInfoA.D.resize(8,0);
|
||||
|
||||
camInfoA.P[0] = odom.data().stereoCameraModels()[0].left().fx();
|
||||
camInfoA.K[0] = odom.data().stereoCameraModels()[0].left().fx();
|
||||
camInfoA.P[5] = odom.data().stereoCameraModels()[0].left().fy();
|
||||
camInfoA.K[4] = odom.data().stereoCameraModels()[0].left().fy();
|
||||
camInfoA.P[2] = odom.data().stereoCameraModels()[0].left().cx();
|
||||
camInfoA.K[2] = odom.data().stereoCameraModels()[0].left().cx();
|
||||
camInfoA.P[6] = odom.data().stereoCameraModels()[0].left().cy();
|
||||
camInfoA.K[5] = odom.data().stereoCameraModels()[0].left().cy();
|
||||
|
||||
camInfoB = camInfoA;
|
||||
camInfoB.P[3] = odom.data().stereoCameraModels()[0].right().Tx(); // Right_Tx = -baseline*fx
|
||||
}
|
||||
|
||||
type=1;
|
||||
|
||||
if(leftPub.getTopic().empty()) leftPub = it.advertise("left/image", 1);
|
||||
if(rightPub.getTopic().empty()) rightPub = it.advertise("right/image", 1);
|
||||
if(leftCamInfoPub.getTopic().empty()) leftCamInfoPub = nh.advertise<sensor_msgs::CameraInfo>("left/camera_info", 1);
|
||||
if(rightCamInfoPub.getTopic().empty()) rightCamInfoPub = nh.advertise<sensor_msgs::CameraInfo>("right/camera_info", 1);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(imagePub.getTopic().empty()) imagePub = it.advertise("image", 1);
|
||||
}
|
||||
|
||||
camInfoA.height = odom.data().imageRaw().rows;
|
||||
camInfoA.width = odom.data().imageRaw().cols;
|
||||
camInfoB.height = odom.data().depthOrRightRaw().rows;
|
||||
camInfoB.width = odom.data().depthOrRightRaw().cols;
|
||||
|
||||
if(!odom.data().laserScanRaw().isEmpty())
|
||||
{
|
||||
if(scanPub.getTopic().empty() && odom.data().laserScanRaw().is2d())
|
||||
{
|
||||
scanPub = nh.advertise<sensor_msgs::LaserScan>("scan", 1);
|
||||
if(odom.data().laserScanRaw().angleIncrement() > 0.0f)
|
||||
{
|
||||
ROS_INFO("Scan will be published.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ROS_INFO("Scan will be published with those parameters:");
|
||||
ROS_INFO(" scan_angle_min=%f", scanAngleMin);
|
||||
ROS_INFO(" scan_angle_max=%f", scanAngleMax);
|
||||
ROS_INFO(" scan_angle_increment=%f", scanAngleIncrement);
|
||||
ROS_INFO(" scan_range_min=%f", scanRangeMin);
|
||||
ROS_INFO(" scan_range_max=%f", scanRangeMax);
|
||||
}
|
||||
}
|
||||
else if(scanCloudPub.getTopic().empty())
|
||||
{
|
||||
scanCloudPub = nh.advertise<sensor_msgs::PointCloud2>("scan_cloud", 1);
|
||||
ROS_INFO("Scan cloud will be published.");
|
||||
}
|
||||
}
|
||||
|
||||
if(!odom.data().globalPose().isNull() &&
|
||||
odom.data().globalPoseCovariance().cols==6 &&
|
||||
odom.data().globalPoseCovariance().rows==6)
|
||||
{
|
||||
if(globalPosePub.getTopic().empty())
|
||||
{
|
||||
globalPosePub = nh.advertise<geometry_msgs::PoseWithCovarianceStamped>("global_pose", 1);
|
||||
ROS_INFO("Global pose will be published.");
|
||||
}
|
||||
}
|
||||
|
||||
if(odom.data().gps().stamp() > 0.0)
|
||||
{
|
||||
if(gpsFixPub.getTopic().empty())
|
||||
{
|
||||
gpsFixPub = nh.advertise<sensor_msgs::NavSatFix>("gps/fix", 1);
|
||||
ROS_INFO("GPS will be published.");
|
||||
}
|
||||
}
|
||||
|
||||
// publish transforms first
|
||||
if(publishTf)
|
||||
{
|
||||
rtabmap::Transform localTransform;
|
||||
if(odom.data().cameraModels().size() == 1)
|
||||
{
|
||||
localTransform = odom.data().cameraModels()[0].localTransform();
|
||||
}
|
||||
else if(odom.data().stereoCameraModels().size() == 1)
|
||||
{
|
||||
localTransform = odom.data().stereoCameraModels()[0].left().localTransform();
|
||||
}
|
||||
if(!localTransform.isNull())
|
||||
{
|
||||
geometry_msgs::TransformStamped baseToCamera;
|
||||
baseToCamera.child_frame_id = cameraFrameId;
|
||||
baseToCamera.header.frame_id = frameId;
|
||||
baseToCamera.header.stamp = time;
|
||||
rtabmap_conversions::transformToGeometryMsg(localTransform, baseToCamera.transform);
|
||||
tfBroadcaster.sendTransform(baseToCamera);
|
||||
}
|
||||
|
||||
if(!odom.pose().isNull())
|
||||
{
|
||||
geometry_msgs::TransformStamped odomToBase;
|
||||
odomToBase.child_frame_id = frameId;
|
||||
odomToBase.header.frame_id = odomFrameId;
|
||||
odomToBase.header.stamp = time;
|
||||
rtabmap_conversions::transformToGeometryMsg(odom.pose(), odomToBase.transform);
|
||||
tfBroadcaster.sendTransform(odomToBase);
|
||||
}
|
||||
|
||||
if(!scanPub.getTopic().empty() || !scanCloudPub.getTopic().empty())
|
||||
{
|
||||
geometry_msgs::TransformStamped baseToLaserScan;
|
||||
baseToLaserScan.child_frame_id = scanFrameId;
|
||||
baseToLaserScan.header.frame_id = frameId;
|
||||
baseToLaserScan.header.stamp = time;
|
||||
rtabmap_conversions::transformToGeometryMsg(odom.data().laserScanCompressed().localTransform(), baseToLaserScan.transform);
|
||||
tfBroadcaster.sendTransform(baseToLaserScan);
|
||||
}
|
||||
}
|
||||
if(!odom.pose().isNull())
|
||||
{
|
||||
if(odometryPub.getTopic().empty()) odometryPub = nh.advertise<nav_msgs::Odometry>("odom", 1);
|
||||
|
||||
if(odometryPub.getNumSubscribers())
|
||||
{
|
||||
nav_msgs::Odometry odomMsg;
|
||||
odomMsg.child_frame_id = frameId;
|
||||
odomMsg.header.frame_id = odomFrameId;
|
||||
odomMsg.header.stamp = time;
|
||||
rtabmap_conversions::transformToPoseMsg(odom.pose(), odomMsg.pose.pose);
|
||||
UASSERT(odomMsg.pose.covariance.size() == 36 &&
|
||||
odom.covariance().total() == 36 &&
|
||||
odom.covariance().type() == CV_64FC1);
|
||||
memcpy(odomMsg.pose.covariance.begin(), odom.covariance().data, 36*sizeof(double));
|
||||
odometryPub.publish(odomMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// Publish async topics first (so that they can catched by rtabmap before the image topics)
|
||||
if(globalPosePub.getNumSubscribers() > 0 &&
|
||||
!odom.data().globalPose().isNull() &&
|
||||
odom.data().globalPoseCovariance().cols==6 &&
|
||||
odom.data().globalPoseCovariance().rows==6)
|
||||
{
|
||||
geometry_msgs::PoseWithCovarianceStamped msg;
|
||||
rtabmap_conversions::transformToPoseMsg(odom.data().globalPose(), msg.pose.pose);
|
||||
memcpy(msg.pose.covariance.data(), odom.data().globalPoseCovariance().data, 36*sizeof(double));
|
||||
msg.header.frame_id = frameId;
|
||||
msg.header.stamp = time;
|
||||
globalPosePub.publish(msg);
|
||||
}
|
||||
|
||||
if(odom.data().gps().stamp() > 0.0)
|
||||
{
|
||||
sensor_msgs::NavSatFix msg;
|
||||
msg.longitude = odom.data().gps().longitude();
|
||||
msg.latitude = odom.data().gps().latitude();
|
||||
msg.altitude = odom.data().gps().altitude();
|
||||
msg.position_covariance_type = sensor_msgs::NavSatFix::COVARIANCE_TYPE_DIAGONAL_KNOWN;
|
||||
msg.position_covariance.at(0) = msg.position_covariance.at(4) = msg.position_covariance.at(8)= odom.data().gps().error()* odom.data().gps().error();
|
||||
msg.header.frame_id = frameId;
|
||||
msg.header.stamp.fromSec(odom.data().gps().stamp());
|
||||
gpsFixPub.publish(msg);
|
||||
}
|
||||
|
||||
if(type >= 0)
|
||||
{
|
||||
if(rgbCamInfoPub.getNumSubscribers() && type == 0)
|
||||
{
|
||||
rgbCamInfoPub.publish(camInfoA);
|
||||
}
|
||||
if(leftCamInfoPub.getNumSubscribers() && type == 1)
|
||||
{
|
||||
leftCamInfoPub.publish(camInfoA);
|
||||
}
|
||||
if(depthCamInfoPub.getNumSubscribers() && type == 0)
|
||||
{
|
||||
depthCamInfoPub.publish(camInfoB);
|
||||
}
|
||||
if(rightCamInfoPub.getNumSubscribers() && type == 1)
|
||||
{
|
||||
rightCamInfoPub.publish(camInfoB);
|
||||
}
|
||||
}
|
||||
|
||||
if(imagePub.getNumSubscribers() || rgbPub.getNumSubscribers() || leftPub.getNumSubscribers())
|
||||
{
|
||||
cv_bridge::CvImage img;
|
||||
if(odom.data().imageRaw().channels() == 1)
|
||||
{
|
||||
img.encoding = sensor_msgs::image_encodings::MONO8;
|
||||
}
|
||||
else
|
||||
{
|
||||
img.encoding = sensor_msgs::image_encodings::BGR8;
|
||||
}
|
||||
img.image = odom.data().imageRaw();
|
||||
sensor_msgs::ImagePtr imageRosMsg = img.toImageMsg();
|
||||
imageRosMsg->header.frame_id = cameraFrameId;
|
||||
imageRosMsg->header.stamp = time;
|
||||
|
||||
if(imagePub.getNumSubscribers())
|
||||
{
|
||||
imagePub.publish(imageRosMsg);
|
||||
}
|
||||
if(rgbPub.getNumSubscribers() && type == 0)
|
||||
{
|
||||
rgbPub.publish(imageRosMsg);
|
||||
}
|
||||
if(leftPub.getNumSubscribers() && type == 1)
|
||||
{
|
||||
leftPub.publish(imageRosMsg);
|
||||
leftCamInfoPub.publish(camInfoA);
|
||||
}
|
||||
}
|
||||
|
||||
if(depthPub.getNumSubscribers() && !odom.data().depthRaw().empty() && type==0)
|
||||
{
|
||||
cv_bridge::CvImage img;
|
||||
if(odom.data().depthRaw().type() == CV_32FC1)
|
||||
{
|
||||
img.encoding = sensor_msgs::image_encodings::TYPE_32FC1;
|
||||
}
|
||||
else
|
||||
{
|
||||
img.encoding = sensor_msgs::image_encodings::TYPE_16UC1;
|
||||
}
|
||||
img.image = odom.data().depthRaw();
|
||||
sensor_msgs::ImagePtr imageRosMsg = img.toImageMsg();
|
||||
imageRosMsg->header.frame_id = cameraFrameId;
|
||||
imageRosMsg->header.stamp = time;
|
||||
|
||||
depthPub.publish(imageRosMsg);
|
||||
depthCamInfoPub.publish(camInfoB);
|
||||
}
|
||||
|
||||
if(rightPub.getNumSubscribers() && !odom.data().rightRaw().empty() && type==1)
|
||||
{
|
||||
cv_bridge::CvImage img;
|
||||
img.encoding = sensor_msgs::image_encodings::MONO8;
|
||||
img.image = odom.data().rightRaw();
|
||||
sensor_msgs::ImagePtr imageRosMsg = img.toImageMsg();
|
||||
imageRosMsg->header.frame_id = cameraFrameId;
|
||||
imageRosMsg->header.stamp = time;
|
||||
|
||||
rightPub.publish(imageRosMsg);
|
||||
rightCamInfoPub.publish(camInfoB);
|
||||
}
|
||||
|
||||
if(!odom.data().laserScanRaw().isEmpty())
|
||||
{
|
||||
if(scanPub.getNumSubscribers() && odom.data().laserScanRaw().is2d())
|
||||
{
|
||||
//inspired from pointcloud_to_laserscan package
|
||||
sensor_msgs::LaserScan msg;
|
||||
msg.header.frame_id = scanFrameId;
|
||||
msg.header.stamp = time;
|
||||
|
||||
msg.angle_min = scanAngleMin;
|
||||
msg.angle_max = scanAngleMax;
|
||||
msg.angle_increment = scanAngleIncrement;
|
||||
msg.time_increment = 0.0;
|
||||
msg.scan_time = 0;
|
||||
msg.range_min = scanRangeMin;
|
||||
msg.range_max = scanRangeMax;
|
||||
if(odom.data().laserScanRaw().angleIncrement() > 0.0f)
|
||||
{
|
||||
msg.angle_min = odom.data().laserScanRaw().angleMin();
|
||||
msg.angle_max = odom.data().laserScanRaw().angleMax();
|
||||
msg.angle_increment = odom.data().laserScanRaw().angleIncrement();
|
||||
msg.range_min = odom.data().laserScanRaw().rangeMin();
|
||||
msg.range_max = odom.data().laserScanRaw().rangeMax();
|
||||
}
|
||||
|
||||
uint32_t rangesSize = std::ceil((msg.angle_max - msg.angle_min) / msg.angle_increment);
|
||||
msg.ranges.assign(rangesSize, 0.0);
|
||||
|
||||
const cv::Mat & scan = odom.data().laserScanRaw().data();
|
||||
for (int i=0; i<scan.cols; ++i)
|
||||
{
|
||||
const float * ptr = scan.ptr<float>(0,i);
|
||||
double range = hypot(ptr[0], ptr[1]);
|
||||
if (range >= msg.range_min && range <=msg.range_max)
|
||||
{
|
||||
double angle = atan2(ptr[1], ptr[0]);
|
||||
if (angle >= msg.angle_min && angle <= msg.angle_max)
|
||||
{
|
||||
int index = (angle - msg.angle_min) / msg.angle_increment;
|
||||
if (index>=0 && index<rangesSize && (range < msg.ranges[index] || msg.ranges[index]==0))
|
||||
{
|
||||
msg.ranges[index] = range;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scanPub.publish(msg);
|
||||
}
|
||||
else if(scanCloudPub.getNumSubscribers())
|
||||
{
|
||||
sensor_msgs::PointCloud2 msg;
|
||||
pcl_conversions::moveFromPCL(*rtabmap::util3d::laserScanToPointCloud2(odom.data().laserScanRaw()), msg);
|
||||
msg.header.frame_id = scanFrameId;
|
||||
msg.header.stamp = time;
|
||||
scanCloudPub.publish(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if(odom.data().userDataRaw().type() == CV_8SC1 &&
|
||||
odom.data().userDataRaw().cols >= 7 && // including null str ending
|
||||
odom.data().userDataRaw().rows == 1 &&
|
||||
memcmp(odom.data().userDataRaw().data, "GOAL:", 5) == 0)
|
||||
{
|
||||
//GOAL format detected, remove it from the user data and send it as goal event
|
||||
std::string goalStr = (const char *)odom.data().userDataRaw().data;
|
||||
if(!goalStr.empty())
|
||||
{
|
||||
std::list<std::string> strs = uSplit(goalStr, ':');
|
||||
if(strs.size() == 2)
|
||||
{
|
||||
int goalId = atoi(strs.rbegin()->c_str());
|
||||
|
||||
if(goalId > 0)
|
||||
{
|
||||
ROS_WARN("Goal %d detected, calling rtabmap's set_goal service!", goalId);
|
||||
rtabmap_msgs::SetGoal setGoalSrv;
|
||||
setGoalSrv.request.node_id = goalId;
|
||||
setGoalSrv.request.node_label = "";
|
||||
if(!ros::service::call("set_goal", setGoalSrv))
|
||||
{
|
||||
ROS_ERROR("Can't call \"set_goal\" service");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ros::spinOnce();
|
||||
|
||||
while(ros::ok())
|
||||
{
|
||||
#ifndef _WIN32
|
||||
if (spacehit()) {
|
||||
paused = !paused;
|
||||
if(paused)
|
||||
{
|
||||
ROS_INFO("paused!");
|
||||
}
|
||||
else
|
||||
{
|
||||
ROS_INFO("resumed!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!paused)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
uSleep(100);
|
||||
ros::spinOnce();
|
||||
}
|
||||
|
||||
timer.restart();
|
||||
cameraInfo = rtabmap::CameraInfo();
|
||||
data = reader.takeImage(&cameraInfo);
|
||||
odomInfo.reg.covariance = cameraInfo.odomCovariance;
|
||||
odom = rtabmap::OdometryEvent(data, cameraInfo.odomPose, odomInfo);
|
||||
acquisitionTime = timer.ticks();
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, 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_util/disparity_to_depth.hpp"
|
||||
#include "rtabmap/utilite/ULogger.h"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ULogger::setType(ULogger::kTypeConsole);
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::DisparityToDepth>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, 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_util/imu_to_tf.hpp"
|
||||
#include "rtabmap/utilite/ULogger.h"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ULogger::setType(ULogger::kTypeConsole);
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::ImuToTF>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
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_util/lidar_deskewing.hpp"
|
||||
#include "rtabmap/utilite/ULogger.h"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ULogger::setType(ULogger::kTypeConsole);
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::LidarDeskewing>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
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_util/map_assembler.hpp"
|
||||
|
||||
#include <rtabmap/utilite/UStl.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ULogger::setType(ULogger::kTypeConsole);
|
||||
ULogger::setLevel(ULogger::kError);
|
||||
|
||||
// process "--params" argument
|
||||
std::vector<std::string> arguments;
|
||||
for(int i=1;i<argc;++i)
|
||||
{
|
||||
if(strcmp(argv[i], "--params") == 0)
|
||||
{
|
||||
rtabmap::ParametersMap parameters;
|
||||
uInsert(parameters, rtabmap::Parameters::getDefaultParameters("Grid"));
|
||||
uInsert(parameters, rtabmap::Parameters::getDefaultParameters("GridGlobal"));
|
||||
uInsert(parameters, rtabmap::Parameters::getDefaultParameters("StereoBM"));
|
||||
uInsert(parameters, rtabmap::Parameters::getDefaultParameters("StereoSGBM"));
|
||||
uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kIcpPointToPlaneGroundNormalsUp(), uNumber2Str(rtabmap::Parameters::defaultIcpPointToPlaneGroundNormalsUp())));
|
||||
for(rtabmap::ParametersMap::iterator iter=parameters.begin(); iter!=parameters.end(); ++iter)
|
||||
{
|
||||
std::string str = "Param: " + iter->first + " = \"" + iter->second + "\"";
|
||||
std::cout <<
|
||||
str <<
|
||||
std::setw(60 - str.size()) <<
|
||||
" [" <<
|
||||
rtabmap::Parameters::getDescription(iter->first).c_str() <<
|
||||
"]" <<
|
||||
std::endl;
|
||||
}
|
||||
UWARN("Node will now exit after showing default parameters because "
|
||||
"argument \"--params\" is detected!");
|
||||
exit(0);
|
||||
}
|
||||
else if(strcmp(argv[i], "--udebug") == 0)
|
||||
{
|
||||
ULogger::setLevel(ULogger::kDebug);
|
||||
}
|
||||
else if(strcmp(argv[i], "--uinfo") == 0)
|
||||
{
|
||||
ULogger::setLevel(ULogger::kInfo);
|
||||
}
|
||||
else if(strcmp(argv[i], "--uwarn") == 0)
|
||||
{
|
||||
ULogger::setLevel(ULogger::kWarning);
|
||||
}
|
||||
arguments.push_back(argv[i]);
|
||||
}
|
||||
|
||||
rclcpp::NodeOptions options;
|
||||
options.arguments(arguments);
|
||||
rclcpp::init(argc, argv);
|
||||
auto node = std::make_shared<rtabmap_util::MapAssembler>(options);
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
executor.add_node(node);
|
||||
executor.spin();
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
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 <ros/ros.h>
|
||||
#include "rtabmap_msgs/MapData.h"
|
||||
#include "rtabmap_msgs/MapGraph.h"
|
||||
#include "rtabmap_conversions/MsgConversion.h"
|
||||
#include <rtabmap/core/util3d.h>
|
||||
#include <rtabmap/core/Graph.h>
|
||||
#include <rtabmap/core/Optimizer.h>
|
||||
#include <rtabmap/core/Parameters.h>
|
||||
#include <rtabmap/utilite/ULogger.h>
|
||||
#include <rtabmap/utilite/UTimer.h>
|
||||
#include <rtabmap/utilite/UConversion.h>
|
||||
#include <ros/subscriber.h>
|
||||
#include <ros/publisher.h>
|
||||
#include <tf2_ros/transform_broadcaster.h>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
using namespace rtabmap;
|
||||
|
||||
class MapOptimizer
|
||||
{
|
||||
|
||||
public:
|
||||
MapOptimizer() :
|
||||
mapFrameId_("map"),
|
||||
odomFrameId_("odom"),
|
||||
globalOptimization_(true),
|
||||
optimizeFromLastNode_(false),
|
||||
mapToOdom_(rtabmap::Transform::getIdentity()),
|
||||
transformThread_(0)
|
||||
{
|
||||
ros::NodeHandle nh;
|
||||
ros::NodeHandle pnh("~");
|
||||
|
||||
double epsilon = 0.0;
|
||||
bool robust = true;
|
||||
bool slam2d =false;
|
||||
int strategy = 0; // 0=TORO, 1=g2o, 2=GTSAM
|
||||
int iterations = 100;
|
||||
bool ignoreVariance = false;
|
||||
|
||||
pnh.param("map_frame_id", mapFrameId_, mapFrameId_);
|
||||
pnh.param("odom_frame_id", odomFrameId_, odomFrameId_);
|
||||
pnh.param("iterations", iterations, iterations);
|
||||
pnh.param("ignore_variance", ignoreVariance, ignoreVariance);
|
||||
pnh.param("global_optimization", globalOptimization_, globalOptimization_);
|
||||
pnh.param("optimize_from_last_node", optimizeFromLastNode_, optimizeFromLastNode_);
|
||||
pnh.param("epsilon", epsilon, epsilon);
|
||||
pnh.param("robust", robust, robust);
|
||||
pnh.param("slam_2d", slam2d, slam2d);
|
||||
pnh.param("strategy", strategy, strategy);
|
||||
|
||||
|
||||
UASSERT(iterations > 0);
|
||||
|
||||
ParametersMap parameters;
|
||||
parameters.insert(ParametersPair(Parameters::kOptimizerStrategy(), uNumber2Str(strategy)));
|
||||
parameters.insert(ParametersPair(Parameters::kOptimizerEpsilon(), uNumber2Str(epsilon)));
|
||||
parameters.insert(ParametersPair(Parameters::kOptimizerIterations(), uNumber2Str(iterations)));
|
||||
parameters.insert(ParametersPair(Parameters::kOptimizerRobust(), uBool2Str(robust)));
|
||||
parameters.insert(ParametersPair(Parameters::kRegForce3DoF(), uBool2Str(slam2d)));
|
||||
parameters.insert(ParametersPair(Parameters::kOptimizerVarianceIgnored(), uBool2Str(ignoreVariance)));
|
||||
optimizer_ = Optimizer::create(parameters);
|
||||
|
||||
double tfDelay = 0.05; // 20 Hz
|
||||
bool publishTf = true;
|
||||
pnh.param("publish_tf", publishTf, publishTf);
|
||||
pnh.param("tf_delay", tfDelay, tfDelay);
|
||||
|
||||
mapDataTopic_ = nh.subscribe("mapData", 1, &MapOptimizer::mapDataReceivedCallback, this);
|
||||
mapDataPub_ = nh.advertise<rtabmap_msgs::MapData>(nh.resolveName("mapData")+"_optimized", 1);
|
||||
mapGraphPub_ = nh.advertise<rtabmap_msgs::MapGraph>(nh.resolveName("mapData")+"Graph_optimized", 1);
|
||||
|
||||
if(publishTf)
|
||||
{
|
||||
ROS_INFO("map_optimizer will publish tf between frames \"%s\" and \"%s\"", mapFrameId_.c_str(), odomFrameId_.c_str());
|
||||
ROS_INFO("map_optimizer: map_frame_id = %s", mapFrameId_.c_str());
|
||||
ROS_INFO("map_optimizer: odom_frame_id = %s", odomFrameId_.c_str());
|
||||
ROS_INFO("map_optimizer: tf_delay = %f", tfDelay);
|
||||
transformThread_ = new boost::thread(boost::bind(&MapOptimizer::publishLoop, this, tfDelay));
|
||||
}
|
||||
}
|
||||
|
||||
~MapOptimizer()
|
||||
{
|
||||
if(transformThread_)
|
||||
{
|
||||
transformThread_->join();
|
||||
delete transformThread_;
|
||||
}
|
||||
}
|
||||
|
||||
void publishLoop(double tfDelay)
|
||||
{
|
||||
if(tfDelay == 0)
|
||||
return;
|
||||
ros::Rate r(1.0 / tfDelay);
|
||||
while(ros::ok())
|
||||
{
|
||||
mapToOdomMutex_.lock();
|
||||
ros::Time tfExpiration = ros::Time::now() + ros::Duration(tfDelay);
|
||||
geometry_msgs::TransformStamped msg;
|
||||
msg.child_frame_id = odomFrameId_;
|
||||
msg.header.frame_id = mapFrameId_;
|
||||
msg.header.stamp = tfExpiration;
|
||||
rtabmap_conversions::transformToGeometryMsg(mapToOdom_, msg.transform);
|
||||
tfBroadcaster_.sendTransform(msg);
|
||||
mapToOdomMutex_.unlock();
|
||||
r.sleep();
|
||||
}
|
||||
}
|
||||
|
||||
void mapDataReceivedCallback(const rtabmap_msgs::MapDataConstPtr & msg)
|
||||
{
|
||||
// save new poses and constraints
|
||||
// Assuming that nodes/constraints are all linked together
|
||||
UASSERT(msg->graph.posesId.size() == msg->graph.poses.size());
|
||||
|
||||
bool dataChanged = false;
|
||||
|
||||
std::multimap<int, Link> newConstraints;
|
||||
for(unsigned int i=0; i<msg->graph.links.size(); ++i)
|
||||
{
|
||||
Link link = rtabmap_conversions::linkFromROS(msg->graph.links[i]);
|
||||
newConstraints.insert(std::make_pair(link.from(), link));
|
||||
|
||||
bool edgeAlreadyAdded = false;
|
||||
for(std::multimap<int, Link>::iterator iter = cachedConstraints_.lower_bound(link.from());
|
||||
iter != cachedConstraints_.end() && iter->first == link.from();
|
||||
++iter)
|
||||
{
|
||||
if(iter->second.to() == link.to())
|
||||
{
|
||||
edgeAlreadyAdded = true;
|
||||
if(iter->second.transform().getDistanceSquared(link.transform()) > 0.0001)
|
||||
{
|
||||
ROS_WARN("%d ->%d (%s vs %s)",iter->second.from(), iter->second.to(), iter->second.transform().prettyPrint().c_str(),
|
||||
link.transform().prettyPrint().c_str());
|
||||
dataChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!edgeAlreadyAdded)
|
||||
{
|
||||
cachedConstraints_.insert(std::make_pair(link.from(), link));
|
||||
}
|
||||
}
|
||||
|
||||
std::map<int, Signature> newNodeInfos;
|
||||
// add new odometry poses
|
||||
for(unsigned int i=0; i<msg->nodes.size(); ++i)
|
||||
{
|
||||
int id = msg->nodes[i].id;
|
||||
Transform pose = rtabmap_conversions::transformFromPoseMsg(msg->nodes[i].pose);
|
||||
Signature s = rtabmap_conversions::nodeInfoFromROS(msg->nodes[i]);
|
||||
newNodeInfos.insert(std::make_pair(id, s));
|
||||
|
||||
std::pair<std::map<int, Signature>::iterator, bool> p = cachedNodeInfos_.insert(std::make_pair(id, s));
|
||||
if(!p.second && pose.getDistanceSquared(cachedNodeInfos_.at(id).getPose()) > 0.0001)
|
||||
{
|
||||
dataChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(dataChanged)
|
||||
{
|
||||
ROS_WARN("Graph data has changed! Reset cache...");
|
||||
cachedConstraints_ = newConstraints;
|
||||
cachedNodeInfos_ = newNodeInfos;
|
||||
}
|
||||
|
||||
//match poses in the graph
|
||||
std::multimap<int, Link> constraints;
|
||||
std::map<int, Signature> nodeInfos;
|
||||
if(globalOptimization_)
|
||||
{
|
||||
constraints = cachedConstraints_;
|
||||
nodeInfos = cachedNodeInfos_;
|
||||
}
|
||||
else
|
||||
{
|
||||
constraints = newConstraints;
|
||||
for(unsigned int i=0; i<msg->graph.posesId.size(); ++i)
|
||||
{
|
||||
std::map<int, Signature>::iterator iter = cachedNodeInfos_.find(msg->graph.posesId[i]);
|
||||
if(iter != cachedNodeInfos_.end())
|
||||
{
|
||||
nodeInfos.insert(*iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
ROS_ERROR("Odometry pose of node %d not found in cache!", msg->graph.posesId[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<int, Transform> poses;
|
||||
for(std::map<int, Signature>::iterator iter=nodeInfos.begin(); iter!=nodeInfos.end(); ++iter)
|
||||
{
|
||||
poses.insert(std::make_pair(iter->first, iter->second.getPose()));
|
||||
}
|
||||
|
||||
// Optimize only if there is a subscriber
|
||||
if(mapDataPub_.getNumSubscribers() || mapGraphPub_.getNumSubscribers())
|
||||
{
|
||||
UTimer timer;
|
||||
std::map<int, Transform> optimizedPoses;
|
||||
Transform mapCorrection = Transform::getIdentity();
|
||||
std::map<int, rtabmap::Transform> posesOut;
|
||||
std::multimap<int, rtabmap::Link> linksOut;
|
||||
if(poses.size() > 1 && constraints.size() > 0)
|
||||
{
|
||||
int fromId = optimizeFromLastNode_?poses.rbegin()->first:poses.begin()->first;
|
||||
optimizer_->getConnectedGraph(
|
||||
fromId,
|
||||
poses,
|
||||
constraints,
|
||||
posesOut,
|
||||
linksOut);
|
||||
optimizedPoses = optimizer_->optimize(fromId, posesOut, linksOut);
|
||||
mapToOdomMutex_.lock();
|
||||
mapCorrection = optimizedPoses.at(posesOut.rbegin()->first) * posesOut.rbegin()->second.inverse();
|
||||
mapToOdom_ = mapCorrection;
|
||||
mapToOdomMutex_.unlock();
|
||||
}
|
||||
else if(poses.size() == 1 && constraints.size() == 0)
|
||||
{
|
||||
optimizedPoses = poses;
|
||||
}
|
||||
else if(poses.size() == 0 && constraints.size())
|
||||
{
|
||||
ROS_ERROR("map_optimizer: Poses=%d and edges=%d: poses must "
|
||||
"not be null if there are edges.",
|
||||
(int)poses.size(), (int)constraints.size());
|
||||
}
|
||||
|
||||
rtabmap_msgs::MapData outputDataMsg;
|
||||
rtabmap_msgs::MapGraph outputGraphMsg;
|
||||
rtabmap_conversions::mapGraphToROS(optimizedPoses,
|
||||
linksOut,
|
||||
mapCorrection,
|
||||
outputGraphMsg);
|
||||
|
||||
if(mapGraphPub_.getNumSubscribers())
|
||||
{
|
||||
outputGraphMsg.header = msg->header;
|
||||
mapGraphPub_.publish(outputGraphMsg);
|
||||
}
|
||||
|
||||
if(mapDataPub_.getNumSubscribers())
|
||||
{
|
||||
outputDataMsg.header = msg->header;
|
||||
outputDataMsg.graph = outputGraphMsg;
|
||||
outputDataMsg.nodes = msg->nodes;
|
||||
if(posesOut.size() > msg->nodes.size())
|
||||
{
|
||||
std::set<int> addedNodes;
|
||||
for(unsigned int i=0; i<msg->nodes.size(); ++i)
|
||||
{
|
||||
addedNodes.insert(msg->nodes[i].id);
|
||||
}
|
||||
std::list<int> toAdd;
|
||||
for(std::map<int, Transform>::iterator iter=posesOut.begin(); iter!=posesOut.end(); ++iter)
|
||||
{
|
||||
if(addedNodes.find(iter->first) == addedNodes.end())
|
||||
{
|
||||
toAdd.push_back(iter->first);
|
||||
}
|
||||
}
|
||||
if(toAdd.size())
|
||||
{
|
||||
int oi = outputDataMsg.nodes.size();
|
||||
outputDataMsg.nodes.resize(outputDataMsg.nodes.size()+toAdd.size());
|
||||
for(std::list<int>::iterator iter=toAdd.begin(); iter!=toAdd.end(); ++iter)
|
||||
{
|
||||
UASSERT(cachedNodeInfos_.find(*iter) != cachedNodeInfos_.end());
|
||||
rtabmap_conversions::nodeToROS(cachedNodeInfos_.at(*iter), outputDataMsg.nodes[oi]);
|
||||
++oi;
|
||||
}
|
||||
}
|
||||
}
|
||||
mapDataPub_.publish(outputDataMsg);
|
||||
}
|
||||
|
||||
ROS_INFO("Time graph optimization = %f s", timer.ticks());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mapFrameId_;
|
||||
std::string odomFrameId_;
|
||||
bool globalOptimization_;
|
||||
bool optimizeFromLastNode_;
|
||||
Optimizer * optimizer_;
|
||||
|
||||
rtabmap::Transform mapToOdom_;
|
||||
boost::mutex mapToOdomMutex_;
|
||||
|
||||
ros::Subscriber mapDataTopic_;
|
||||
|
||||
ros::Publisher mapDataPub_;
|
||||
ros::Publisher mapGraphPub_;
|
||||
|
||||
std::multimap<int, Link> cachedConstraints_;
|
||||
std::map<int, Signature> cachedNodeInfos_;
|
||||
|
||||
tf2_ros::TransformBroadcaster tfBroadcaster_;
|
||||
boost::thread* transformThread_;
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
ros::init(argc, argv, "map_optimizer");
|
||||
MapOptimizer optimizer;
|
||||
ros::spin();
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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_util/obstacles_detection.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::ObstaclesDetection>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
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 <ros/ros.h>
|
||||
#include <nav_msgs/Odometry.h>
|
||||
#include <tf2_ros/transform_broadcaster.h>
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
|
||||
class OdomMsgToTF
|
||||
{
|
||||
|
||||
public:
|
||||
OdomMsgToTF() :
|
||||
frameId_(""),
|
||||
odomFrameId_("")
|
||||
{
|
||||
ros::NodeHandle pnh("~");
|
||||
pnh.param("frame_id", frameId_, frameId_);
|
||||
pnh.param("odom_frame_id", odomFrameId_, odomFrameId_);
|
||||
|
||||
ros::NodeHandle nh;
|
||||
odomTopic_ = nh.subscribe("odom", 1, &OdomMsgToTF::odomReceivedCallback, this);
|
||||
}
|
||||
|
||||
virtual ~OdomMsgToTF(){}
|
||||
|
||||
void odomReceivedCallback(const nav_msgs::OdometryConstPtr & msg)
|
||||
{
|
||||
if(frameId_.empty())
|
||||
{
|
||||
frameId_ = msg->child_frame_id;
|
||||
}
|
||||
if(odomFrameId_.empty())
|
||||
{
|
||||
odomFrameId_ = msg->header.frame_id;
|
||||
}
|
||||
geometry_msgs::TransformStamped t;
|
||||
rtabmap::Transform pose = rtabmap_conversions::transformFromPoseMsg(msg->pose.pose);
|
||||
if(pose.isNull())
|
||||
{
|
||||
ROS_WARN("Odometry received is null! Cannot send tf...");
|
||||
}
|
||||
else
|
||||
{
|
||||
t.child_frame_id = frameId_;
|
||||
t.header.frame_id = odomFrameId_;
|
||||
t.header.stamp = msg->header.stamp;
|
||||
rtabmap_conversions::transformToGeometryMsg(pose, t.transform);
|
||||
tfBroadcaster_.sendTransform(t);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string frameId_;
|
||||
std::string odomFrameId_;
|
||||
|
||||
ros::Subscriber odomTopic_;
|
||||
tf2_ros::TransformBroadcaster tfBroadcaster_;
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
ros::init(argc, argv, "odom_msg_to_tf");
|
||||
OdomMsgToTF odomToTf;
|
||||
ros::spin();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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_util/point_cloud_aggregator.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::PointCloudAggregator>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
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_util/point_cloud_assembler.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ULogger::setType(ULogger::kTypeConsole);
|
||||
ULogger::setLevel(ULogger::kWarning);
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::PointCloudAssembler>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
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/utilite/ULogger.h>
|
||||
#include "rtabmap_util/pointcloud_to_depthimage.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ULogger::setType(ULogger::kTypeConsole);
|
||||
ULogger::setLevel(ULogger::kWarning);
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::PointCloudToDepthImage>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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_util/point_cloud_xyz.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::PointCloudXYZ>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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_util/point_cloud_xyzrgb.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::PointCloudXYZRGB>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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 <memory>
|
||||
#include "rtabmap_util/rgbd_relay.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::RGBDRelay>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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 <memory>
|
||||
#include "rtabmap_util/rgbd_split.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<rtabmap_util::RGBDSplit>(rclcpp::NodeOptions()));
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
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_util/disparity_to_depth.hpp>
|
||||
#include <sensor_msgs/image_encodings.hpp>
|
||||
|
||||
#include <image_transport/image_transport.hpp>
|
||||
|
||||
#ifdef PRE_ROS_IRON
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#else
|
||||
#include <cv_bridge/cv_bridge.hpp>
|
||||
#endif
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
DisparityToDepth::DisparityToDepth(const rclcpp::NodeOptions & options) :
|
||||
rclcpp::Node("disparity_to_depth", options)
|
||||
{
|
||||
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
|
||||
pub32f_ = image_transport::create_publisher(this, "depth", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
pub16u_ = image_transport::create_publisher(this, "depth_raw", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
|
||||
sub_ = create_subscription<stereo_msgs::msg::DisparityImage>("disparity", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos), std::bind(&DisparityToDepth::callback, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
DisparityToDepth::~DisparityToDepth(){}
|
||||
|
||||
void DisparityToDepth::callback(const stereo_msgs::msg::DisparityImage::ConstSharedPtr disparityMsg)
|
||||
{
|
||||
if(disparityMsg->image.encoding.compare(sensor_msgs::image_encodings::TYPE_32FC1) !=0)
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Input type must be disparity=32FC1");
|
||||
return;
|
||||
}
|
||||
|
||||
bool publish32f = pub32f_.getNumSubscribers();
|
||||
bool publish16u = pub16u_.getNumSubscribers();
|
||||
|
||||
if(publish32f || publish16u)
|
||||
{
|
||||
// sensor_msgs::image_encodings::TYPE_32FC1
|
||||
cv::Mat disparity(disparityMsg->image.height, disparityMsg->image.width, CV_32FC1, const_cast<uchar*>(disparityMsg->image.data.data()));
|
||||
|
||||
cv::Mat depth32f;
|
||||
cv::Mat depth16u;
|
||||
if(publish32f)
|
||||
{
|
||||
depth32f = cv::Mat::zeros(disparity.rows, disparity.cols, CV_32F);
|
||||
}
|
||||
if(publish16u)
|
||||
{
|
||||
depth16u = cv::Mat::zeros(disparity.rows, disparity.cols, CV_16U);
|
||||
}
|
||||
float * depth32fPtr=0;
|
||||
unsigned short * depth16uPtr=0;
|
||||
for (int i = 0; i < disparity.rows; ++i)
|
||||
{
|
||||
const float * rowPtr = (const float*)disparity.ptr(i);
|
||||
if(publish32f)
|
||||
{
|
||||
depth32fPtr = (float*)depth32f.ptr(i);
|
||||
}
|
||||
if(publish16u)
|
||||
{
|
||||
depth16uPtr = (unsigned short*)depth16u.ptr(i);
|
||||
}
|
||||
for (int j = 0; j < disparity.cols; ++j)
|
||||
{
|
||||
const float & disparity_value = rowPtr[j];
|
||||
if (disparity_value > disparityMsg->min_disparity && disparity_value < disparityMsg->max_disparity)
|
||||
{
|
||||
// baseline * focal / disparity
|
||||
float depth = disparityMsg->t * disparityMsg->f / disparity_value;
|
||||
if(publish32f)
|
||||
{
|
||||
depth32fPtr[j] = depth;
|
||||
}
|
||||
if(publish16u)
|
||||
{
|
||||
depth16uPtr[j] = (unsigned short)(depth*1000.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(publish32f)
|
||||
{
|
||||
// convert to ROS sensor_msg::Image
|
||||
cv_bridge::CvImage cvDepth(disparityMsg->header, sensor_msgs::image_encodings::TYPE_32FC1, depth32f);
|
||||
sensor_msgs::msg::Image depthMsg;
|
||||
cvDepth.toImageMsg(depthMsg);
|
||||
|
||||
//publish the message
|
||||
pub32f_.publish(depthMsg);
|
||||
}
|
||||
|
||||
if(publish16u)
|
||||
{
|
||||
// convert to ROS sensor_msg::Image
|
||||
cv_bridge::CvImage cvDepth(disparityMsg->header, sensor_msgs::image_encodings::TYPE_16UC1, depth16u);
|
||||
sensor_msgs::msg::Image depthMsg;
|
||||
cvDepth.toImageMsg(depthMsg);
|
||||
|
||||
//publish the message
|
||||
pub16u_.publish(depthMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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_util::DisparityToDepth)
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, 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_util/imu_to_tf.hpp>
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
|
||||
#include <tf2/LinearMath/Transform.h>
|
||||
#include <tf2/utils.hpp>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
ImuToTF::ImuToTF(const rclcpp::NodeOptions & options) :
|
||||
rclcpp::Node("imu_to_tf", options),
|
||||
fixedFrameId_("odom"),
|
||||
waitForTransformDuration_(0.1)
|
||||
{
|
||||
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);
|
||||
|
||||
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
fixedFrameId_ = this->declare_parameter("fixed_frame_id", fixedFrameId_);
|
||||
baseFrameId_ = this->declare_parameter("base_frame_id", baseFrameId_);
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
waitForTransformDuration_ = this->declare_parameter("wait_for_transform_duration", waitForTransformDuration_);
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "fixed_frame_id: %s", fixedFrameId_.c_str());
|
||||
RCLCPP_INFO(this->get_logger(), "base_frame_id: %s", baseFrameId_.c_str());
|
||||
RCLCPP_INFO(this->get_logger(), "qos: %d", qos);
|
||||
|
||||
sub_ = create_subscription<sensor_msgs::msg::Imu>("imu/data", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos), std::bind(&ImuToTF::imuCallback, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
ImuToTF::~ImuToTF()
|
||||
{
|
||||
}
|
||||
|
||||
void ImuToTF::imuCallback(const sensor_msgs::msg::Imu::ConstSharedPtr msg)
|
||||
{
|
||||
tf2::Quaternion q;
|
||||
tf2::fromMsg(msg->orientation, q);
|
||||
tf2::Transform st(q);
|
||||
|
||||
std::string childFrameId = msg->header.frame_id;
|
||||
|
||||
if(!baseFrameId_.empty() &&
|
||||
baseFrameId_.compare(msg->header.frame_id) != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string errorMsg;
|
||||
if(!tfBuffer_->canTransform(baseFrameId_, msg->header.frame_id, msg->header.stamp, rclcpp::Duration::from_seconds(waitForTransformDuration_), &errorMsg))
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Could not get transform from %s to %s after %f seconds (for stamp=%f)! Error=\"%s\".",
|
||||
baseFrameId_.c_str(), msg->header.frame_id.c_str(), 0.1, rtabmap_conversions::timestampFromROS(msg->header.stamp), errorMsg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
geometry_msgs::msg::TransformStamped tmp = tfBuffer_->lookupTransform(baseFrameId_, msg->header.frame_id, msg->header.stamp);
|
||||
tf2::Transform tmp_t;
|
||||
tf2::fromMsg(tmp.transform, tmp_t);
|
||||
tf2::Quaternion q;
|
||||
q.setRPY(0.0,0.0,tf2::getYaw(tmp_t.getRotation()));
|
||||
tf2::Transform t = tf2::Transform(q)*st*tmp_t.inverse(); // base_frame orientation
|
||||
st.setRotation(t.getRotation());
|
||||
childFrameId = baseFrameId_;
|
||||
}
|
||||
catch(tf2::TransformException & ex)
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "(getting transform %s -> %s) %s", baseFrameId_.c_str(), msg->header.frame_id.c_str(), ex.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
geometry_msgs::msg::TransformStamped output;
|
||||
output.header.frame_id = fixedFrameId_;
|
||||
output.header.stamp = msg->header.stamp;
|
||||
output.child_frame_id = childFrameId;
|
||||
output.transform = tf2::toMsg(st);
|
||||
tfBroadcaster_->sendTransform(output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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_util::ImuToTF)
|
||||
@@ -0,0 +1,103 @@
|
||||
|
||||
#include <rtabmap_util/lidar_deskewing.hpp>
|
||||
|
||||
#include <laser_geometry/laser_geometry.hpp>
|
||||
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
LidarDeskewing::LidarDeskewing(const rclcpp::NodeOptions & options) :
|
||||
Node("lidar_deskewing", options),
|
||||
waitForTransformDuration_(0.01),
|
||||
slerp_(false)
|
||||
{
|
||||
tfBuffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
|
||||
tfListener_ = std::make_shared<tf2_ros::TransformListener>(*tfBuffer_);
|
||||
|
||||
int queueSize = 5;
|
||||
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
queueSize = this->declare_parameter("queue_size", queueSize);
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
fixedFrameId_ = this->declare_parameter("fixed_frame_id", fixedFrameId_);
|
||||
waitForTransformDuration_ = this->declare_parameter("wait_for_transform", waitForTransformDuration_);
|
||||
slerp_ = this->declare_parameter("slerp", slerp_);
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), " fixed_frame_id: %s", fixedFrameId_.c_str());
|
||||
RCLCPP_INFO(this->get_logger(), " wait_for_transform: %fs", waitForTransformDuration_);
|
||||
RCLCPP_INFO(this->get_logger(), " slerp: %s", slerp_?"true":"false");
|
||||
|
||||
if(fixedFrameId_.empty())
|
||||
{
|
||||
RCLCPP_FATAL(this->get_logger(), "fixed_frame_id parameter cannot be empty!");
|
||||
}
|
||||
|
||||
subScan_ = create_subscription<sensor_msgs::msg::LaserScan>("input_scan", rclcpp::QoS(queueSize).reliability((rmw_qos_reliability_policy_t)qos), std::bind(&LidarDeskewing::callbackScan, this, std::placeholders::_1));
|
||||
subCloud_ = create_subscription<sensor_msgs::msg::PointCloud2>("input_cloud", rclcpp::QoS(queueSize).reliability((rmw_qos_reliability_policy_t)qos), std::bind(&LidarDeskewing::callbackCloud, this, std::placeholders::_1));
|
||||
|
||||
pubScan_ = create_publisher<sensor_msgs::msg::PointCloud2>(std::string(subScan_->get_topic_name()) + "/deskewed", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
pubCloud_ = create_publisher<sensor_msgs::msg::PointCloud2>(std::string(subCloud_->get_topic_name()) + "/deskewed", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
}
|
||||
|
||||
LidarDeskewing::~LidarDeskewing()
|
||||
{
|
||||
}
|
||||
|
||||
void LidarDeskewing::callbackScan(const sensor_msgs::msg::LaserScan::ConstSharedPtr msg)
|
||||
{
|
||||
// make sure the frame of the laser is updated during the whole scan time
|
||||
rtabmap::Transform tmpT = rtabmap_conversions::getMovingTransform(
|
||||
msg->header.frame_id,
|
||||
fixedFrameId_,
|
||||
msg->header.stamp,
|
||||
rclcpp::Time(msg->header.stamp.sec, msg->header.stamp.nanosec) + rclcpp::Duration::from_seconds(msg->ranges.size()*msg->time_increment),
|
||||
*tfBuffer_,
|
||||
waitForTransformDuration_);
|
||||
if(tmpT.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sensor_msgs::msg::PointCloud2 scanOut;
|
||||
laser_geometry::LaserProjection projection;
|
||||
projection.transformLaserScanToPointCloud(fixedFrameId_, *msg, scanOut, *tfBuffer_);
|
||||
|
||||
rtabmap::Transform t = rtabmap_conversions::getTransform(msg->header.frame_id, scanOut.header.frame_id, msg->header.stamp, *tfBuffer_, waitForTransformDuration_);
|
||||
if(t.isNull())
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Cannot transform back projected scan from \"%s\" frame to \"%s\" frame at time %fs.",
|
||||
scanOut.header.frame_id.c_str(), msg->header.frame_id.c_str(), rtabmap_conversions::timestampFromROS(msg->header.stamp));
|
||||
return;
|
||||
}
|
||||
|
||||
sensor_msgs::msg::PointCloud2 scanOutDeskewed;
|
||||
rtabmap_conversions::transformPointCloud(t.toEigen4f(), scanOut, scanOutDeskewed);
|
||||
scanOutDeskewed.header.frame_id = msg->header.frame_id;
|
||||
pubScan_->publish(scanOutDeskewed);
|
||||
}
|
||||
|
||||
void LidarDeskewing::callbackCloud(const sensor_msgs::msg::PointCloud2::ConstSharedPtr msg)
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2 msgDeskewed;
|
||||
if(rtabmap_conversions::deskew(*msg, msgDeskewed, fixedFrameId_, *tfBuffer_, waitForTransformDuration_, slerp_))
|
||||
{
|
||||
pubCloud_->publish(msgDeskewed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just republish the msg to not breakdown downstream
|
||||
// A warning should be already shown (see deskew() source code)
|
||||
RCLCPP_WARN(this->get_logger(), "deskewing failed! returning possible skewed cloud!");
|
||||
pubCloud_->publish(*msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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_util::LidarDeskewing)
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
Copyright (c) 2010-2024, 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_util/map_assembler.hpp>
|
||||
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
|
||||
#include <rtabmap/utilite/UFile.h>
|
||||
#include <rtabmap/utilite/UStl.h>
|
||||
#include <rtabmap/utilite/UTimer.h>
|
||||
|
||||
#ifdef WITH_OCTOMAP_MSGS
|
||||
#ifdef RTABMAP_OCTOMAP
|
||||
#include <octomap_msgs/conversions.h>
|
||||
#include <rtabmap/core/OctoMap.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef PRE_ROS_JAZZY
|
||||
namespace rclcpp{
|
||||
rmw_qos_profile_t ServicesQoS() {return rmw_qos_profile_services_default;}
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
MapAssembler::MapAssembler(const rclcpp::NodeOptions & options) :
|
||||
Node("map_assembler", options),
|
||||
rtabmapNodeName_("rtabmap"),
|
||||
localGridsRegenerated_(false)
|
||||
{
|
||||
std::string configPath;
|
||||
configPath = this->declare_parameter("config_path", configPath);
|
||||
localGridsRegenerated_ = this->declare_parameter("regenerate_local_grids", localGridsRegenerated_);
|
||||
rtabmapNodeName_ = this->declare_parameter("rtabmap", rtabmapNodeName_);
|
||||
|
||||
//parameters
|
||||
rtabmap::ParametersMap parameters;
|
||||
uInsert(parameters, rtabmap::Parameters::getDefaultParameters("Grid"));
|
||||
uInsert(parameters, rtabmap::Parameters::getDefaultParameters("GridGlobal"));
|
||||
uInsert(parameters, rtabmap::Parameters::getDefaultParameters("StereoBM"));
|
||||
uInsert(parameters, rtabmap::Parameters::getDefaultParameters("StereoSGBM"));
|
||||
uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kIcpPointToPlaneGroundNormalsUp(), uNumber2Str(rtabmap::Parameters::defaultIcpPointToPlaneGroundNormalsUp())));
|
||||
if(!configPath.empty())
|
||||
{
|
||||
if(UFile::exists(configPath.c_str()))
|
||||
{
|
||||
RCLCPP_INFO(this->get_logger(), "MapAssembler: Loading parameters from %s", configPath.c_str());
|
||||
rtabmap::ParametersMap allParameters;
|
||||
rtabmap::Parameters::readINI(configPath.c_str(), allParameters);
|
||||
// only update odometry parameters
|
||||
for(rtabmap::ParametersMap::iterator iter=parameters.begin(); iter!=parameters.end(); ++iter)
|
||||
{
|
||||
rtabmap::ParametersMap::iterator jter = allParameters.find(iter->first);
|
||||
if(jter!=allParameters.end())
|
||||
{
|
||||
iter->second = jter->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Config file \"%s\" not found!", configPath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
for(rtabmap::ParametersMap::iterator iter=parameters.begin(); iter!=parameters.end(); ++iter)
|
||||
{
|
||||
rclcpp::Parameter parameter;
|
||||
std::string vStr = this->declare_parameter(iter->first, iter->second);
|
||||
if(vStr.compare(iter->second)!=0)
|
||||
{
|
||||
RCLCPP_INFO(this->get_logger(), "MapAssembler: Setting parameter \"%s\"=\"%s\"", iter->first.c_str(), vStr.c_str());
|
||||
iter->second = vStr;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> tmpList = this->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()];
|
||||
for(unsigned int i=0; i<argList.size(); ++i)
|
||||
{
|
||||
argv[i] = &argList[i].at(0);
|
||||
}
|
||||
|
||||
rtabmap::ParametersMap argParameters = rtabmap::Parameters::parseArguments(argList.size(), argv);
|
||||
delete [] argv;
|
||||
for(rtabmap::ParametersMap::iterator iter=argParameters.begin(); iter!=argParameters.end(); ++iter)
|
||||
{
|
||||
rtabmap::ParametersMap::iterator jter = parameters.find(iter->first);
|
||||
if(jter!=parameters.end())
|
||||
{
|
||||
RCLCPP_INFO(this->get_logger(), "MapAssembler: Update parameter \"%s\"=\"%s\" from arguments", iter->first.c_str(), iter->second.c_str());
|
||||
jter->second = iter->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_INFO(this->get_logger(), "MapAssembler: Ignored 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=rtabmap::Parameters::getRemovedParameters().begin();
|
||||
iter!=rtabmap::Parameters::getRemovedParameters().end();
|
||||
++iter)
|
||||
{
|
||||
rclcpp::Parameter parameter;
|
||||
if(get_parameter(iter->first, parameter))
|
||||
{
|
||||
std::string vStr = parameter.as_string();
|
||||
if(!iter->second.second.empty() && parameters.find(iter->second.second)!=parameters.end())
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "MapAssembler: 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(), vStr.c_str());
|
||||
}
|
||||
else if(iter->second.first && parameters.find(iter->second.second) != parameters.end())
|
||||
{
|
||||
// can be migrated
|
||||
parameters.at(iter->second.second)= vStr;
|
||||
RCLCPP_WARN(this->get_logger(), "MapAssembler: 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(), vStr.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(iter->second.second.empty())
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "MapAssembler: Parameter \"%s\" doesn't exist anymore!",
|
||||
iter->first.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "MapAssembler: Parameter \"%s\" doesn't exist anymore! You may look at this similar parameter: \"%s\"",
|
||||
iter->first.c_str(), iter->second.second.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "%s: regenerate_local_grids = %s", this->get_name(), localGridsRegenerated_?"true":"false");
|
||||
mapsManager_.init(*this, this->get_name(), true);
|
||||
mapsManager_.backwardCompatibilityParameters(*this, parameters);
|
||||
mapsManager_.setParameters(parameters);
|
||||
|
||||
const std::string servicePrefix = get_name() + std::string("/");
|
||||
resetService_ = this->create_service<std_srvs::srv::Empty>(servicePrefix + "reset", std::bind(&MapAssembler::reset, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
|
||||
#ifdef WITH_OCTOMAP_MSGS
|
||||
#ifdef RTABMAP_OCTOMAP
|
||||
octomapBinarySrv_ = this->create_service<octomap_msgs::srv::GetOctomap>(servicePrefix + "octomap_binary", std::bind(&MapAssembler::octomapBinaryCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
octomapFullSrv_ = this->create_service<octomap_msgs::srv::GetOctomap>(servicePrefix + "octomap_full", std::bind(&MapAssembler::octomapFullCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
std::string getMapSrv = rtabmapNodeName_+"/get_map_data";
|
||||
|
||||
// We cannot call the service and wait in the constructor, lets call it later and subscribe afterwards
|
||||
serviceCbGroup_ = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
timerCbGroup_ = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
client_ = this->create_client<rtabmap_msgs::srv::GetMap>(getMapSrv, rclcpp::ServicesQoS(), serviceCbGroup_); // Put it in a different group than the timer
|
||||
timer_ = this->create_wall_timer(1s, std::bind(&MapAssembler::timerCallback, this), timerCbGroup_);
|
||||
}
|
||||
|
||||
MapAssembler::~MapAssembler() {}
|
||||
|
||||
void MapAssembler::timerCallback()
|
||||
{
|
||||
// Just do this callback one time
|
||||
timer_->cancel();
|
||||
if(mapDataSub_.get())
|
||||
{
|
||||
// double call? ignore
|
||||
return;
|
||||
}
|
||||
|
||||
std::string getMapSrv = rtabmapNodeName_+"/get_map_data";
|
||||
RCLCPP_INFO(this->get_logger(), "Calling service \"%s\"...", getMapSrv.c_str());
|
||||
|
||||
if(client_->wait_for_service(5s))
|
||||
{
|
||||
auto request = std::make_shared<rtabmap_msgs::srv::GetMap::Request>();
|
||||
request->global_map = false;
|
||||
request->optimized = true;
|
||||
request->graph_only = false;
|
||||
|
||||
auto future = client_->async_send_request(request);
|
||||
std::future_status status = future.wait_for(10s);
|
||||
|
||||
if (status == std::future_status::ready) {
|
||||
RCLCPP_INFO(this->get_logger(), "Initializing cache...");
|
||||
processMapData(future.get()->data);
|
||||
RCLCPP_INFO(this->get_logger(), "Initializing cache... done! The map"
|
||||
" will be assembled on next subscriber connection.");
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "Service \"%s\" not responding after waiting for 10 seconds.",
|
||||
getMapSrv.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "Service \"%s\" not available after waiting for 5 seconds, "
|
||||
"may not be a problem if rtabmap is started afterwards. If rtabmap "
|
||||
"is started after in localization mode, call %s/publish_maps "
|
||||
"service with graph_only=false to make sure map_assembler has all the data.",
|
||||
getMapSrv.c_str(),
|
||||
rtabmapNodeName_.c_str());
|
||||
}
|
||||
|
||||
rclcpp::SubscriptionOptions options;
|
||||
options.callback_group = timerCbGroup_;
|
||||
mapDataSub_ = create_subscription<rtabmap_msgs::msg::MapData>("mapData", rclcpp::QoS(1),
|
||||
std::bind(&MapAssembler::mapDataReceivedCallback, this, std::placeholders::_1), options);
|
||||
}
|
||||
|
||||
void MapAssembler::mapDataReceivedCallback(const rtabmap_msgs::msg::MapData::ConstSharedPtr msg)
|
||||
{
|
||||
processMapData(*msg);
|
||||
}
|
||||
void MapAssembler::processMapData(const rtabmap_msgs::msg::MapData & msg)
|
||||
{
|
||||
UTimer timer;
|
||||
|
||||
std::map<int, rtabmap::Transform> poses;
|
||||
std::multimap<int, rtabmap::Link> constraints;
|
||||
rtabmap::Transform mapOdom;
|
||||
rtabmap_conversions::mapGraphFromROS(msg.graph, poses, constraints, mapOdom);
|
||||
for(unsigned int i=0; i<msg.nodes.size(); ++i)
|
||||
{
|
||||
if(msg.nodes[i].data.left_compressed.size() ||
|
||||
msg.nodes[i].data.right_compressed.size() ||
|
||||
msg.nodes[i].data.laser_scan_compressed.size())
|
||||
{
|
||||
rtabmap::Signature data = rtabmap_conversions::nodeFromROS(msg.nodes[i]);
|
||||
if(localGridsRegenerated_)
|
||||
{
|
||||
data.sensorData().setOccupancyGrid(cv::Mat(), cv::Mat(), cv::Mat(), 0, cv::Point3f());
|
||||
}
|
||||
uInsert(nodes_, std::make_pair(msg.nodes[i].id, data));
|
||||
}
|
||||
}
|
||||
|
||||
// create a tmp signature with latest sensory data
|
||||
if(poses.size() && nodes_.find(poses.rbegin()->first) != nodes_.end())
|
||||
{
|
||||
rtabmap::Signature tmpS = nodes_.at(poses.rbegin()->first);
|
||||
rtabmap::SensorData tmpData = tmpS.sensorData();
|
||||
tmpData.setId(0);
|
||||
uInsert(nodes_, std::make_pair(0, rtabmap::Signature(0, -1, 0, tmpS.getStamp(), "", tmpS.getPose(), rtabmap::Transform(), tmpData)));
|
||||
poses.insert(std::make_pair(0, poses.rbegin()->second));
|
||||
}
|
||||
|
||||
// Update maps
|
||||
if(!nodes_.empty())
|
||||
{
|
||||
poses = mapsManager_.updateMapCaches(
|
||||
poses,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
nodes_);
|
||||
}
|
||||
double updateTime = timer.ticks();
|
||||
|
||||
mapFrameId_ = msg.header.frame_id;
|
||||
optimizedPoses_ = poses;
|
||||
|
||||
mapsManager_.publishMaps(poses, msg.header.stamp, msg.header.frame_id);
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "map_assembler: Updating = %fs, Publishing data = %fs (subscribers=%s)", updateTime, timer.ticks(), mapsManager_.hasSubscribers()?"true":"false");
|
||||
}
|
||||
|
||||
void MapAssembler::reset(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(), "map_assembler: reset!");
|
||||
mapsManager_.clear();
|
||||
}
|
||||
|
||||
#ifdef WITH_OCTOMAP_MSGS
|
||||
#ifdef RTABMAP_OCTOMAP
|
||||
void MapAssembler::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();
|
||||
|
||||
mapsManager_.updateMapCaches(optimizedPoses_, 0, false, true, nodes_);
|
||||
|
||||
const rtabmap::OctoMap * octomap = mapsManager_.getOctomap();
|
||||
if(octomap->octree()->size())
|
||||
octomap_msgs::binaryMapToMsg(*octomap->octree(), res->map);
|
||||
}
|
||||
|
||||
void MapAssembler::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();
|
||||
|
||||
mapsManager_.updateMapCaches(optimizedPoses_, 0, false, true, nodes_);
|
||||
|
||||
const rtabmap::OctoMap * octomap = mapsManager_.getOctomap();
|
||||
if(octomap->octree()->size())
|
||||
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_util::MapAssembler)
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
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_util/obstacles_detection.hpp>
|
||||
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <pcl_conversions/pcl_conversions.h>
|
||||
#include <pcl/filters/filter.h>
|
||||
#include <rtabmap/core/LocalGridMaker.h>
|
||||
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
|
||||
#include "rtabmap/utilite/UStl.h"
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
ObstaclesDetection::ObstaclesDetection(const rclcpp::NodeOptions & options) :
|
||||
Node("obstacles_detection", options),
|
||||
frameId_("base_link"),
|
||||
waitForTransform_(0.2),
|
||||
mapFrameProjection_(rtabmap::Parameters::defaultGridMapFrameProjection()),
|
||||
warned_(false),
|
||||
rangeMin_(0),
|
||||
rangeMax_(0)
|
||||
{
|
||||
ULogger::setType(ULogger::kTypeConsole);
|
||||
ULogger::setLevel(ULogger::kWarning);
|
||||
|
||||
frameId_ = this->declare_parameter("frame_id", frameId_);
|
||||
mapFrameId_ = this->declare_parameter("map_frame_id", mapFrameId_);
|
||||
waitForTransform_ = this->declare_parameter("wait_for_transform", waitForTransform_);
|
||||
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
|
||||
rtabmap::ParametersMap gridParameters = rtabmap::Parameters::getDefaultParameters("Grid");
|
||||
for(rtabmap::ParametersMap::iterator iter=gridParameters.begin(); iter!=gridParameters.end(); ++iter)
|
||||
{
|
||||
std::string vStr = declare_parameter(iter->first, iter->second);
|
||||
if(vStr.compare(iter->second) != 0)
|
||||
{
|
||||
RCLCPP_INFO(this->get_logger(), "obstacles_detection: Setting parameter \"%s\"=\"%s\"", iter->first.c_str(), vStr.c_str());
|
||||
iter->second = vStr;
|
||||
}
|
||||
}
|
||||
|
||||
UASSERT(uContains(gridParameters, rtabmap::Parameters::kGridMapFrameProjection()));
|
||||
mapFrameProjection_ = uStr2Bool(gridParameters.at(rtabmap::Parameters::kGridMapFrameProjection()));
|
||||
if(mapFrameProjection_ && mapFrameId_.empty())
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "obstacles_detection: Parameter \"%s\" is true but map_frame_id is not set!", rtabmap::Parameters::kGridMapFrameProjection().c_str());
|
||||
}
|
||||
|
||||
localMapMaker_.parseParameters(gridParameters);
|
||||
rtabmap::Parameters::parse(gridParameters, rtabmap::Parameters::kGridRangeMin(), rangeMin_);
|
||||
rtabmap::Parameters::parse(gridParameters, rtabmap::Parameters::kGridRangeMax(), rangeMax_);
|
||||
|
||||
tfBuffer_ = std::make_shared< tf2_ros::Buffer >(this->get_clock());
|
||||
tfListener_ = std::make_shared< tf2_ros::TransformListener >(*tfBuffer_);
|
||||
|
||||
groundPub_ = create_publisher<sensor_msgs::msg::PointCloud2>("ground", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
obstaclesPub_ = create_publisher<sensor_msgs::msg::PointCloud2>("obstacles", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
projObstaclesPub_ = create_publisher<sensor_msgs::msg::PointCloud2>("proj_obstacles", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
|
||||
cloudSub_ = create_subscription<sensor_msgs::msg::PointCloud2>("cloud", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos), std::bind(&ObstaclesDetection::callback, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ> rangeFiltering(
|
||||
const pcl::PointCloud<pcl::PointXYZ> & cloud,
|
||||
float rangeMin,
|
||||
float rangeMax)
|
||||
{
|
||||
if(!cloud.empty() && (rangeMin > 0.0f || rangeMax > 0.0f))
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZ> output;
|
||||
output.reserve(cloud.size());
|
||||
int oi = 0;
|
||||
float rangeMinSqrd = rangeMin * rangeMin;
|
||||
float rangeMaxSqrd = rangeMax * rangeMax;
|
||||
for(size_t i=0; i<cloud.size(); ++i)
|
||||
{
|
||||
const pcl::PointXYZ & pt = cloud.at(i);
|
||||
float r = pt.x*pt.x + pt.y*pt.y + pt.z*pt.z;
|
||||
|
||||
if(rangeMin > 0.0f && r < rangeMinSqrd)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(rangeMax > 0.0f && r > rangeMaxSqrd)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
output.push_back(pt);
|
||||
++oi;
|
||||
}
|
||||
output.resize(oi);
|
||||
return output;
|
||||
}
|
||||
|
||||
return cloud;
|
||||
}
|
||||
|
||||
void ObstaclesDetection::callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg)
|
||||
{
|
||||
rclcpp::Time time = now();
|
||||
|
||||
if (groundPub_->get_subscription_count() == 0 && obstaclesPub_->get_subscription_count() == 0 && projObstaclesPub_->get_subscription_count() == 0)
|
||||
{
|
||||
// no one wants the results
|
||||
return;
|
||||
}
|
||||
|
||||
rtabmap::Transform localTransform = rtabmap::Transform::getIdentity();
|
||||
localTransform = rtabmap_conversions::getTransform(frameId_, cloudMsg->header.frame_id, cloudMsg->header.stamp, *tfBuffer_, waitForTransform_);
|
||||
if(localTransform.isNull())
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Failed to get transform between %s and %s frames", frameId_.c_str(), cloudMsg->header.frame_id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
rtabmap::Transform pose = rtabmap::Transform::getIdentity();
|
||||
if(!mapFrameId_.empty())
|
||||
{
|
||||
pose = rtabmap_conversions::getTransform(mapFrameId_, frameId_, cloudMsg->header.stamp, *tfBuffer_, waitForTransform_);
|
||||
if(pose.isNull())
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Failed to get transform between %s and %s frames", mapFrameId_.c_str(), frameId_.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UASSERT_MSG(cloudMsg->data.size() == cloudMsg->row_step*cloudMsg->height,
|
||||
uFormat("data=%d row_step=%d height=%d", cloudMsg->data.size(), cloudMsg->row_step, cloudMsg->height).c_str());
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr inputCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::fromROSMsg(*cloudMsg, *inputCloud);
|
||||
if(inputCloud->isOrganized())
|
||||
{
|
||||
std::vector<int> indices;
|
||||
pcl::removeNaNFromPointCloud(*inputCloud, *inputCloud, indices);
|
||||
}
|
||||
else if(!inputCloud->is_dense && inputCloud->height == 1)
|
||||
{
|
||||
if(!warned_)
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "Detected possible wrong format of point cloud \"%s\", it is "
|
||||
"indicated that it is not dense, but there is only one row. "
|
||||
"Assuming it is dense... This message will only appear once.", cloudSub_->get_topic_name());
|
||||
warned_ = true;
|
||||
}
|
||||
inputCloud->is_dense = true;
|
||||
}
|
||||
|
||||
if(rangeMin_ > 0.0f || rangeMax_ > 0.0f)
|
||||
{
|
||||
*inputCloud = rangeFiltering(*inputCloud, rangeMin_, rangeMax_);
|
||||
}
|
||||
|
||||
//Common variables for all strategies
|
||||
pcl::IndicesPtr ground, obstacles;
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloudWithoutFlatSurfaces(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
|
||||
if(inputCloud->size())
|
||||
{
|
||||
inputCloud = rtabmap::util3d::transformPointCloud(inputCloud, localTransform);
|
||||
|
||||
pcl::IndicesPtr flatObstacles(new std::vector<int>);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = localMapMaker_.segmentCloud<pcl::PointXYZ>(
|
||||
inputCloud,
|
||||
pcl::IndicesPtr(new std::vector<int>),
|
||||
pose,
|
||||
cv::Point3f(localTransform.x(), localTransform.y(), localTransform.z()),
|
||||
ground,
|
||||
obstacles,
|
||||
&flatObstacles);
|
||||
|
||||
if(cloud->size() && ((ground.get() && ground->size()) || (obstacles.get() && obstacles->size())))
|
||||
{
|
||||
if(groundPub_->get_subscription_count() &&
|
||||
ground.get() && ground->size())
|
||||
{
|
||||
pcl::copyPointCloud(*cloud, *ground, *groundCloud);
|
||||
}
|
||||
|
||||
if((obstaclesPub_->get_subscription_count() || projObstaclesPub_->get_subscription_count()) &&
|
||||
obstacles.get() && obstacles->size())
|
||||
{
|
||||
// remove flat obstacles from obstacles
|
||||
std::set<int> flatObstaclesSet;
|
||||
if(projObstaclesPub_->get_subscription_count())
|
||||
{
|
||||
flatObstaclesSet.insert(flatObstacles->begin(), flatObstacles->end());
|
||||
}
|
||||
|
||||
obstaclesCloud->resize(obstacles->size());
|
||||
obstaclesCloudWithoutFlatSurfaces->resize(obstacles->size());
|
||||
|
||||
int oi=0;
|
||||
for(unsigned int i=0; i<obstacles->size(); ++i)
|
||||
{
|
||||
obstaclesCloud->points[i] = cloud->at(obstacles->at(i));
|
||||
if(flatObstaclesSet.size() == 0 ||
|
||||
flatObstaclesSet.find(obstacles->at(i))==flatObstaclesSet.end())
|
||||
{
|
||||
obstaclesCloudWithoutFlatSurfaces->points[oi] = obstaclesCloud->points[i];
|
||||
obstaclesCloudWithoutFlatSurfaces->points[oi].z = 0;
|
||||
++oi;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
obstaclesCloudWithoutFlatSurfaces->resize(oi);
|
||||
}
|
||||
|
||||
if(!localTransform.isIdentity() || !pose.isIdentity())
|
||||
{
|
||||
//transform back in topic frame for 3d clouds and base frame for 2d clouds
|
||||
|
||||
float roll, pitch, yaw;
|
||||
pose.getEulerAngles(roll, pitch, yaw);
|
||||
rtabmap::Transform t = rtabmap::Transform(0,0, mapFrameProjection_?pose.z():0, roll, pitch, 0);
|
||||
|
||||
if(obstaclesCloudWithoutFlatSurfaces->size() && !pose.isIdentity())
|
||||
{
|
||||
obstaclesCloudWithoutFlatSurfaces = rtabmap::util3d::transformPointCloud(obstaclesCloudWithoutFlatSurfaces, t.inverse());
|
||||
}
|
||||
|
||||
t = (t*localTransform).inverse();
|
||||
if(groundCloud->size())
|
||||
{
|
||||
groundCloud = rtabmap::util3d::transformPointCloud(groundCloud, t);
|
||||
}
|
||||
if(obstaclesCloud->size())
|
||||
{
|
||||
obstaclesCloud = rtabmap::util3d::transformPointCloud(obstaclesCloud, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "obstacles_detection: Input cloud is empty! (%d x %d, is_dense=%d)", cloudMsg->width, cloudMsg->height, cloudMsg->is_dense?1:0);
|
||||
}
|
||||
|
||||
if(groundPub_->get_subscription_count())
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2::UniquePtr rosCloud(new sensor_msgs::msg::PointCloud2);
|
||||
pcl::toROSMsg(*groundCloud, *rosCloud);
|
||||
rosCloud->header = cloudMsg->header;
|
||||
|
||||
//publish the message
|
||||
groundPub_->publish(std::move(rosCloud));
|
||||
}
|
||||
|
||||
if(obstaclesPub_->get_subscription_count())
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2::UniquePtr rosCloud(new sensor_msgs::msg::PointCloud2);
|
||||
pcl::toROSMsg(*obstaclesCloud, *rosCloud);
|
||||
rosCloud->header = cloudMsg->header;
|
||||
|
||||
//publish the message
|
||||
obstaclesPub_->publish(std::move(rosCloud));
|
||||
}
|
||||
|
||||
if(projObstaclesPub_->get_subscription_count())
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2::UniquePtr rosCloud(new sensor_msgs::msg::PointCloud2);
|
||||
pcl::toROSMsg(*obstaclesCloudWithoutFlatSurfaces, *rosCloud);
|
||||
rosCloud->header.stamp = cloudMsg->header.stamp;
|
||||
rosCloud->header.frame_id = frameId_;
|
||||
|
||||
//publish the message
|
||||
projObstaclesPub_->publish(std::move(rosCloud));
|
||||
}
|
||||
|
||||
RCLCPP_DEBUG(this->get_logger(), "Obstacles segmentation time = %f s", (now() - time).seconds());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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_util::ObstaclesDetection)
|
||||
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, 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_util/point_cloud_aggregator.hpp>
|
||||
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <pcl_conversions/pcl_conversions.h>
|
||||
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
#include <rtabmap/utilite/UConversion.h>
|
||||
#include <rtabmap/utilite/ULogger.h>
|
||||
#include <rtabmap/core/util3d_filtering.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
PointCloudAggregator::PointCloudAggregator(const rclcpp::NodeOptions & options) :
|
||||
Node("point_cloud_aggregator", options),
|
||||
warningThread_(0),
|
||||
callbackCalled_(false),
|
||||
exactSync4_(0),
|
||||
approxSync4_(0),
|
||||
exactSync3_(0),
|
||||
approxSync3_(0),
|
||||
exactSync2_(0),
|
||||
approxSync2_(0),
|
||||
waitForTransform_(0.1),
|
||||
xyzOutput_(false)
|
||||
{
|
||||
tfBuffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
|
||||
//auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
|
||||
// this->get_node_base_interface(),
|
||||
// this->get_node_timers_interface());
|
||||
//tfBuffer_->setCreateTimerInterface(timer_interface);
|
||||
tfListener_ = std::make_shared<tf2_ros::TransformListener>(*tfBuffer_);
|
||||
|
||||
int topicQueueSize = 1;
|
||||
int syncQueueSize = 5;
|
||||
int count = 2;
|
||||
bool approx=true;
|
||||
double approxSyncMaxInterval = 0.0;
|
||||
int qos=RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
topicQueueSize = this->declare_parameter("topic_queue_size", topicQueueSize);
|
||||
int queueSize = this->declare_parameter("queue_size", -1);
|
||||
if(queueSize != -1)
|
||||
{
|
||||
syncQueueSize = queueSize;
|
||||
RCLCPP_WARN(this->get_logger(), "Parameter \"queue_size\" has been renamed "
|
||||
"to \"sync_queue_size\" and will be removed "
|
||||
"in future versions! The value (%d) is copied to "
|
||||
"\"sync_queue_size\".", syncQueueSize);
|
||||
}
|
||||
syncQueueSize = this->declare_parameter("sync_queue_size", syncQueueSize);
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
frameId_ = this->declare_parameter("frame_id", frameId_);
|
||||
fixedFrameId_ = this->declare_parameter("fixed_frame_id", fixedFrameId_);
|
||||
approx = this->declare_parameter("approx_sync", approx);
|
||||
approxSyncMaxInterval = this->declare_parameter("approx_sync_max_interval", approxSyncMaxInterval);
|
||||
count = this->declare_parameter("count", count);
|
||||
waitForTransform_ = this->declare_parameter("wait_for_transform", waitForTransform_);
|
||||
xyzOutput_ = this->declare_parameter("xyz_output", xyzOutput_);
|
||||
|
||||
cloudPub_ = create_publisher<sensor_msgs::msg::PointCloud2>("combined_cloud", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
|
||||
cloudSub_1_.subscribe(this, "cloud1", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
cloudSub_2_.subscribe(this, "cloud2", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
|
||||
std::string subscribedTopicsMsg;
|
||||
if(count == 4)
|
||||
{
|
||||
cloudSub_3_.subscribe(this, "cloud3", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
cloudSub_4_.subscribe(this, "cloud4", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
if(approx)
|
||||
{
|
||||
approxSync4_ = new message_filters::Synchronizer<ApproxSync4Policy>(ApproxSync4Policy(syncQueueSize), cloudSub_1_, cloudSub_2_, cloudSub_3_, cloudSub_4_);
|
||||
if(approxSyncMaxInterval > 0.0)
|
||||
approxSync4_->setMaxIntervalDuration(rclcpp::Duration::from_seconds(approxSyncMaxInterval));
|
||||
approxSync4_->registerCallback(std::bind(&rtabmap_util::PointCloudAggregator::clouds4_callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
}
|
||||
else
|
||||
{
|
||||
exactSync4_ = new message_filters::Synchronizer<ExactSync4Policy>(ExactSync4Policy(syncQueueSize), cloudSub_1_, cloudSub_2_, cloudSub_3_, cloudSub_4_);
|
||||
exactSync4_->registerCallback(std::bind(&rtabmap_util::PointCloudAggregator::clouds4_callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
}
|
||||
subscribedTopicsMsg = uFormat("\n%s subscribed to (%s sync%s):\n %s,\n %s,\n %s,\n %s",
|
||||
get_name(),
|
||||
approx?"approx":"exact",
|
||||
approx&&approxSyncMaxInterval!=0.0?uFormat(", max interval=%fs", approxSyncMaxInterval).c_str():"",
|
||||
cloudSub_1_.getSubscriber()->get_topic_name(),
|
||||
cloudSub_2_.getSubscriber()->get_topic_name(),
|
||||
cloudSub_3_.getSubscriber()->get_topic_name(),
|
||||
cloudSub_4_.getSubscriber()->get_topic_name());
|
||||
}
|
||||
else if(count == 3)
|
||||
{
|
||||
cloudSub_3_.subscribe(this, "cloud3", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
if(approx)
|
||||
{
|
||||
approxSync3_ = new message_filters::Synchronizer<ApproxSync3Policy>(ApproxSync3Policy(syncQueueSize), cloudSub_1_, cloudSub_2_, cloudSub_3_);
|
||||
if(approxSyncMaxInterval > 0.0)
|
||||
approxSync3_->setMaxIntervalDuration(rclcpp::Duration::from_seconds(approxSyncMaxInterval));
|
||||
approxSync3_->registerCallback(std::bind(&rtabmap_util::PointCloudAggregator::clouds3_callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
else
|
||||
{
|
||||
exactSync3_ = new message_filters::Synchronizer<ExactSync3Policy>(ExactSync3Policy(syncQueueSize), cloudSub_1_, cloudSub_2_, cloudSub_3_);
|
||||
exactSync3_->registerCallback(std::bind(&rtabmap_util::PointCloudAggregator::clouds3_callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
subscribedTopicsMsg = uFormat("\n%s subscribed to (%s sync%s):\n %s,\n %s,\n %s",
|
||||
this->get_name(),
|
||||
approx?"approx":"exact",
|
||||
approx&&approxSyncMaxInterval!=0.0?uFormat(", max interval=%fs", approxSyncMaxInterval).c_str():"",
|
||||
cloudSub_1_.getSubscriber()->get_topic_name(),
|
||||
cloudSub_2_.getSubscriber()->get_topic_name(),
|
||||
cloudSub_3_.getSubscriber()->get_topic_name());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(approx)
|
||||
{
|
||||
approxSync2_ = new message_filters::Synchronizer<ApproxSync2Policy>(ApproxSync2Policy(syncQueueSize), cloudSub_1_, cloudSub_2_);
|
||||
if(approxSyncMaxInterval > 0.0)
|
||||
approxSync2_->setMaxIntervalDuration(rclcpp::Duration::from_seconds(approxSyncMaxInterval));
|
||||
approxSync2_->registerCallback(std::bind(&rtabmap_util::PointCloudAggregator::clouds2_callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
else
|
||||
{
|
||||
exactSync2_ = new message_filters::Synchronizer<ExactSync2Policy>(ExactSync2Policy(syncQueueSize), cloudSub_1_, cloudSub_2_);
|
||||
exactSync2_->registerCallback(std::bind(&rtabmap_util::PointCloudAggregator::clouds2_callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
subscribedTopicsMsg = uFormat("\n%s subscribed to (%s sync%s):\n %s,\n %s",
|
||||
this->get_name(),
|
||||
approx?"approx":"exact",
|
||||
approx&&approxSyncMaxInterval!=0.0?uFormat(", max interval=%fs", approxSyncMaxInterval).c_str():"",
|
||||
cloudSub_1_.getSubscriber()->get_topic_name(),
|
||||
cloudSub_2_.getSubscriber()->get_topic_name());
|
||||
}
|
||||
|
||||
|
||||
warningThread_ = new std::thread([&](){
|
||||
rclcpp::Rate r(1.0/5.0);
|
||||
while(!callbackCalled_)
|
||||
{
|
||||
r.sleep();
|
||||
if(!callbackCalled_)
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "%s: Did not receive data since 5 seconds! Make sure the input topics are "
|
||||
"published (\"$ ros2 topic hz my_topic\") and the timestamps in their "
|
||||
"header are set. %s%s",
|
||||
this->get_name(),
|
||||
approx?"":"Parameter \"approx_sync\" is false, which means that input "
|
||||
"topics should have all the exact timestamp for the callback to be called.",
|
||||
subscribedTopicsMsg.c_str());
|
||||
}
|
||||
}
|
||||
});
|
||||
RCLCPP_INFO(this->get_logger(), "%s", subscribedTopicsMsg.c_str());
|
||||
}
|
||||
|
||||
PointCloudAggregator::~PointCloudAggregator()
|
||||
{
|
||||
delete exactSync4_;
|
||||
delete approxSync4_;
|
||||
delete exactSync3_;
|
||||
delete approxSync3_;
|
||||
delete exactSync2_;
|
||||
delete approxSync2_;
|
||||
|
||||
if(warningThread_)
|
||||
{
|
||||
callbackCalled_=true;
|
||||
warningThread_->join();
|
||||
delete warningThread_;
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloudAggregator::clouds4_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_1,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_2,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_3,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_4)
|
||||
{
|
||||
std::vector<sensor_msgs::msg::PointCloud2::ConstSharedPtr> clouds;
|
||||
clouds.push_back(cloudMsg_1);
|
||||
clouds.push_back(cloudMsg_2);
|
||||
clouds.push_back(cloudMsg_3);
|
||||
clouds.push_back(cloudMsg_4);
|
||||
|
||||
combineClouds(clouds);
|
||||
}
|
||||
void PointCloudAggregator::clouds3_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_1,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_2,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_3)
|
||||
{
|
||||
std::vector<sensor_msgs::msg::PointCloud2::ConstSharedPtr> clouds;
|
||||
clouds.push_back(cloudMsg_1);
|
||||
clouds.push_back(cloudMsg_2);
|
||||
clouds.push_back(cloudMsg_3);
|
||||
|
||||
combineClouds(clouds);
|
||||
}
|
||||
void PointCloudAggregator::clouds2_callback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_1,
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg_2)
|
||||
{
|
||||
std::vector<sensor_msgs::msg::PointCloud2::ConstSharedPtr> clouds;
|
||||
clouds.push_back(cloudMsg_1);
|
||||
clouds.push_back(cloudMsg_2);
|
||||
|
||||
combineClouds(clouds);
|
||||
}
|
||||
void PointCloudAggregator::combineClouds(const std::vector<sensor_msgs::msg::PointCloud2::ConstSharedPtr> & cloudMsgs)
|
||||
{
|
||||
callbackCalled_ = true;
|
||||
UASSERT(cloudMsgs.size() > 1);
|
||||
if(cloudPub_->get_subscription_count())
|
||||
{
|
||||
pcl::PCLPointCloud2::Ptr output(new pcl::PCLPointCloud2);
|
||||
|
||||
std::string frameId = frameId_;
|
||||
if(!frameId.empty() && frameId.compare(cloudMsgs[0]->header.frame_id) != 0)
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2 tmp;
|
||||
rtabmap::Transform t = rtabmap_conversions::getTransform(frameId, cloudMsgs[0]->header.frame_id, cloudMsgs[0]->header.stamp, *tfBuffer_, waitForTransform_);
|
||||
if(t.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
rtabmap_conversions::transformPointCloud(t.toEigen4f(), *cloudMsgs[0], tmp);
|
||||
pcl_conversions::toPCL(tmp, *output);
|
||||
}
|
||||
else
|
||||
{
|
||||
pcl_conversions::toPCL(*cloudMsgs[0], *output);
|
||||
frameId = cloudMsgs[0]->header.frame_id;
|
||||
}
|
||||
|
||||
if(xyzOutput_ && !output->data.empty())
|
||||
{
|
||||
// convert only if not already XYZ cloud
|
||||
bool hasField[4] = {false};
|
||||
for(size_t i=0; i<output->fields.size(); ++i)
|
||||
{
|
||||
if(output->fields[i].name.compare("x") == 0)
|
||||
{
|
||||
hasField[0] = true;
|
||||
}
|
||||
else if(output->fields[i].name.compare("y") == 0)
|
||||
{
|
||||
hasField[1] = true;
|
||||
}
|
||||
else if(output->fields[i].name.compare("z") == 0)
|
||||
{
|
||||
hasField[2] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasField[3] = true; // other
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(hasField[0] && hasField[1] && hasField[2] && !hasField[3])
|
||||
{
|
||||
// do nothing, already XYZ
|
||||
}
|
||||
else
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZ> cloudxyz;
|
||||
pcl::fromPCLPointCloud2(*output, cloudxyz);
|
||||
pcl::toPCLPointCloud2(cloudxyz, *output);
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int i=1; i<cloudMsgs.size(); ++i)
|
||||
{
|
||||
rtabmap::Transform cloudDisplacement;
|
||||
if(!fixedFrameId_.empty() &&
|
||||
cloudMsgs[0]->header.stamp != cloudMsgs[i]->header.stamp)
|
||||
{
|
||||
// approx sync
|
||||
cloudDisplacement = rtabmap_conversions::getMovingTransform(
|
||||
frameId, //sourceTargetFrame
|
||||
fixedFrameId_, //fixedFrame
|
||||
cloudMsgs[0]->header.stamp, //stampTarget
|
||||
cloudMsgs[i]->header.stamp, //stampSource
|
||||
*tfBuffer_,
|
||||
waitForTransform_);
|
||||
}
|
||||
|
||||
pcl::PCLPointCloud2::Ptr cloud2(new pcl::PCLPointCloud2);
|
||||
if(frameId.compare(cloudMsgs[i]->header.frame_id) != 0)
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2 tmp;
|
||||
rtabmap::Transform t = rtabmap_conversions::getTransform(frameId, cloudMsgs[i]->header.frame_id, cloudMsgs[i]->header.stamp, *tfBuffer_, waitForTransform_);
|
||||
rtabmap_conversions::transformPointCloud(t.toEigen4f(), *cloudMsgs[i], tmp);
|
||||
if(!cloudDisplacement.isNull())
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2 tmp2;
|
||||
rtabmap_conversions::transformPointCloud(cloudDisplacement.toEigen4f(), tmp, tmp2);
|
||||
pcl_conversions::toPCL(tmp2, *cloud2);
|
||||
}
|
||||
else
|
||||
{
|
||||
pcl_conversions::toPCL(tmp, *cloud2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!cloudDisplacement.isNull())
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2 tmp;
|
||||
rtabmap_conversions::transformPointCloud(cloudDisplacement.toEigen4f(), *cloudMsgs[i], tmp);
|
||||
pcl_conversions::toPCL(tmp, *cloud2);
|
||||
}
|
||||
else
|
||||
{
|
||||
pcl_conversions::toPCL(*cloudMsgs[i], *cloud2);
|
||||
}
|
||||
}
|
||||
|
||||
if(!cloud2->is_dense)
|
||||
{
|
||||
// remove nans
|
||||
cloud2 = rtabmap::util3d::removeNaNFromPointCloud(cloud2);
|
||||
}
|
||||
|
||||
if(xyzOutput_ && !cloud2->data.empty())
|
||||
{
|
||||
// convert only if not already XYZ cloud
|
||||
bool hasField[4] = {false};
|
||||
for(size_t i=0; i<cloud2->fields.size(); ++i)
|
||||
{
|
||||
if(cloud2->fields[i].name.compare("x") == 0)
|
||||
{
|
||||
hasField[0] = true;
|
||||
}
|
||||
else if(cloud2->fields[i].name.compare("y") == 0)
|
||||
{
|
||||
hasField[1] = true;
|
||||
}
|
||||
else if(cloud2->fields[i].name.compare("z") == 0)
|
||||
{
|
||||
hasField[2] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasField[3] = true; // other
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(hasField[0] && hasField[1] && hasField[2] && !hasField[3])
|
||||
{
|
||||
// do nothing, already XYZ
|
||||
}
|
||||
else
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZ> cloudxyz;
|
||||
pcl::fromPCLPointCloud2(*cloud2, cloudxyz);
|
||||
pcl::toPCLPointCloud2(cloudxyz, *cloud2);
|
||||
}
|
||||
}
|
||||
|
||||
if(output->data.empty())
|
||||
{
|
||||
output = cloud2;
|
||||
}
|
||||
else if(!cloud2->data.empty())
|
||||
{
|
||||
|
||||
if(output->fields.size() != cloud2->fields.size())
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "%s: Input topics don't have all the "
|
||||
"same number of fields (cloud1=%d, cloud%d=%d), concatenation "
|
||||
"may fails. You can enable \"xyz_output\" option "
|
||||
"to convert all inputs to XYZ.",
|
||||
get_name(),
|
||||
(int)output->fields.size(),
|
||||
i+1,
|
||||
(int)output->fields.size());
|
||||
}
|
||||
|
||||
pcl::PCLPointCloud2::Ptr tmp_output(new pcl::PCLPointCloud2);
|
||||
#if PCL_VERSION_COMPARE(>=, 1, 10, 0)
|
||||
pcl::concatenate(*output, *cloud2, *tmp_output);
|
||||
#else
|
||||
pcl::concatenatePointCloud(*output, *cloud2, *tmp_output);
|
||||
#endif
|
||||
//Make sure row_step is the sum of both
|
||||
tmp_output->row_step = tmp_output->width * tmp_output->point_step;
|
||||
output = tmp_output;
|
||||
}
|
||||
}
|
||||
|
||||
sensor_msgs::msg::PointCloud2::UniquePtr rosCloud(new sensor_msgs::msg::PointCloud2);
|
||||
pcl_conversions::moveFromPCL(*output, *rosCloud);
|
||||
rosCloud->header.stamp = cloudMsgs[0]->header.stamp;
|
||||
rosCloud->header.frame_id = frameId;
|
||||
cloudPub_->publish(std::move(rosCloud));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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_util::PointCloudAggregator)
|
||||
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, 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_util/point_cloud_assembler.hpp>
|
||||
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <pcl_conversions/pcl_conversions.h>
|
||||
#include <pcl/io/pcd_io.h>
|
||||
#include <pcl/filters/voxel_grid.h>
|
||||
#include <pcl/filters/radius_outlier_removal.h>
|
||||
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
#include <rtabmap_msgs/msg/odom_info.hpp>
|
||||
#include <rtabmap/core/util3d.h>
|
||||
#include <rtabmap/core/util3d_filtering.h>
|
||||
#include <rtabmap/core/Version.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
PointCloudAssembler::PointCloudAssembler(const rclcpp::NodeOptions & options) :
|
||||
Node("point_cloud_assembler", options),
|
||||
warningThread_(0),
|
||||
callbackCalled_(false),
|
||||
exactSync_(0),
|
||||
exactInfoSync_(0),
|
||||
maxClouds_(0),
|
||||
skipClouds_(0),
|
||||
cloudsSkipped_(0),
|
||||
circularBuffer_(false),
|
||||
linearUpdate_(0),
|
||||
angularUpdate_(0),
|
||||
assemblingTime_(0),
|
||||
waitForTransform_(0.1),
|
||||
rangeMin_(0),
|
||||
rangeMax_(0),
|
||||
voxelSize_(0),
|
||||
noiseRadius_(0),
|
||||
noiseMinNeighbors_(5),
|
||||
removeZ_(false),
|
||||
fixedFrameId_("odom"),
|
||||
frameId_("")
|
||||
{
|
||||
tfBuffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
|
||||
//auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
|
||||
// this->get_node_base_interface(),
|
||||
// this->get_node_timers_interface());
|
||||
//tfBuffer_->setCreateTimerInterface(timer_interface);
|
||||
tfListener_ = std::make_shared<tf2_ros::TransformListener>(*tfBuffer_);
|
||||
|
||||
int topicQueueSize = 10;
|
||||
int syncQueueSize = 10;
|
||||
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
bool subscribeOdomInfo = false;
|
||||
|
||||
topicQueueSize = this->declare_parameter("topic_queue_size", topicQueueSize);
|
||||
int queueSize = this->declare_parameter("queue_size", -1);
|
||||
if(queueSize != -1)
|
||||
{
|
||||
syncQueueSize = queueSize;
|
||||
RCLCPP_WARN(this->get_logger(), "Parameter \"queue_size\" has been renamed "
|
||||
"to \"sync_queue_size\" and will be removed "
|
||||
"in future versions! The value (%d) is copied to "
|
||||
"\"sync_queue_size\".", syncQueueSize);
|
||||
}
|
||||
syncQueueSize = this->declare_parameter("sync_queue_size", syncQueueSize);
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
int qosOdom = this->declare_parameter("qos_odom", qos);
|
||||
fixedFrameId_ = this->declare_parameter("fixed_frame_id", fixedFrameId_);
|
||||
frameId_ = this->declare_parameter("frame_id", frameId_);
|
||||
maxClouds_ = this->declare_parameter("max_clouds", maxClouds_);
|
||||
assemblingTime_ = this->declare_parameter("assembling_time", assemblingTime_);
|
||||
skipClouds_ = this->declare_parameter("skip_clouds", skipClouds_);
|
||||
circularBuffer_ = this->declare_parameter("circular_buffer", circularBuffer_);
|
||||
linearUpdate_ = this->declare_parameter("linear_update", linearUpdate_);
|
||||
angularUpdate_ = this->declare_parameter("angular_update", angularUpdate_);
|
||||
waitForTransform_ = this->declare_parameter("wait_for_transform", waitForTransform_);
|
||||
rangeMin_ = this->declare_parameter("range_min", rangeMin_);
|
||||
rangeMax_ = this->declare_parameter("range_max", rangeMax_);
|
||||
voxelSize_ = this->declare_parameter("voxel_size", voxelSize_);
|
||||
noiseRadius_ = this->declare_parameter("noise_radius", noiseRadius_);
|
||||
noiseMinNeighbors_ = this->declare_parameter("noise_min_neighbors", noiseMinNeighbors_);
|
||||
removeZ_ = this->declare_parameter("remove_z", removeZ_);
|
||||
subscribeOdomInfo = this->declare_parameter("subscribe_odom_info", subscribeOdomInfo);
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "%s: topic_queue_size=%d", get_name(), topicQueueSize);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: sync_queue_size=%d", get_name(), syncQueueSize);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: qos=%d", get_name(), qos);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: qos_odom=%d", get_name(), qosOdom);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: fixed_frame_id=%s", get_name(), fixedFrameId_.c_str());
|
||||
RCLCPP_INFO(this->get_logger(), "%s: frame_id=%s", get_name(), frameId_.c_str());
|
||||
RCLCPP_INFO(this->get_logger(), "%s: max_clouds=%d", get_name(), maxClouds_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: assembling_time=%fs", get_name(), assemblingTime_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: skip_clouds=%d", get_name(), skipClouds_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: circular_buffer=%s", get_name(), circularBuffer_?"true":"false");
|
||||
RCLCPP_INFO(this->get_logger(), "%s: linear_update=%f m", get_name(), linearUpdate_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: angular_update=%f rad", get_name(), angularUpdate_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: wait_for_transform=%f", get_name(), waitForTransform_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: range_min=%f", get_name(), rangeMin_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: range_max=%f", get_name(), rangeMax_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: voxel_size=%fm", get_name(), voxelSize_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: noise_radius=%fm", get_name(), noiseRadius_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: noise_min_neighbors=%d", get_name(), noiseMinNeighbors_);
|
||||
RCLCPP_INFO(this->get_logger(), "%s: remove_z=%s", get_name(), removeZ_?"true":"false");
|
||||
|
||||
if(maxClouds_==0 && assemblingTime_ ==0.0)
|
||||
{
|
||||
RCLCPP_ERROR(get_logger(), "point_cloud_assembler: max_clouds or assembling_time parameters should be set!");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
cloudsSkipped_ = skipClouds_;
|
||||
|
||||
cloudPub_ = create_publisher<sensor_msgs::msg::PointCloud2>("assembled_cloud", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
|
||||
if(!fixedFrameId_.empty())
|
||||
{
|
||||
cloudSub_ = create_subscription<sensor_msgs::msg::PointCloud2>("cloud", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos), std::bind(&PointCloudAssembler::callbackCloud, this, std::placeholders::_1));
|
||||
subscribedTopicsMsg_ = uFormat("\n%s subscribed to %s",
|
||||
get_name(),
|
||||
cloudSub_->get_topic_name());
|
||||
}
|
||||
else if(subscribeOdomInfo)
|
||||
{
|
||||
syncCloudSub_.subscribe(this, "cloud", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
syncOdomSub_.subscribe(this, "odom", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosOdom).get_rmw_qos_profile());
|
||||
syncOdomInfoSub_.subscribe(this, "odom_info", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosOdom).get_rmw_qos_profile());
|
||||
exactInfoSync_ = new message_filters::Synchronizer<syncInfoPolicy>(syncInfoPolicy(syncQueueSize), syncCloudSub_, syncOdomSub_, syncOdomInfoSub_);
|
||||
exactInfoSync_->registerCallback(std::bind(&rtabmap_util::PointCloudAssembler::callbackCloudOdomInfo, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
subscribedTopicsMsg_ = uFormat("\n%s subscribed to (exact sync):\n %s,\n %s",
|
||||
get_name(),
|
||||
syncCloudSub_.getSubscriber()->get_topic_name(),
|
||||
syncOdomSub_.getSubscriber()->get_topic_name(),
|
||||
syncOdomInfoSub_.getSubscriber()->get_topic_name());
|
||||
}
|
||||
else
|
||||
{
|
||||
syncCloudSub_.subscribe(this, "cloud", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
syncOdomSub_.subscribe(this, "odom", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosOdom).get_rmw_qos_profile());
|
||||
exactSync_ = new message_filters::Synchronizer<syncPolicy>(syncPolicy(syncQueueSize), syncCloudSub_, syncOdomSub_);
|
||||
exactSync_->registerCallback(std::bind(&rtabmap_util::PointCloudAssembler::callbackCloudOdom, this, std::placeholders::_1, std::placeholders::_2));
|
||||
subscribedTopicsMsg_ = uFormat("\n%s subscribed to (exact sync):\n %s,\n %s",
|
||||
get_name(),
|
||||
syncCloudSub_.getSubscriber()->get_topic_name(),
|
||||
syncOdomSub_.getSubscriber()->get_topic_name());
|
||||
}
|
||||
|
||||
warningThread_ = new std::thread([&](){
|
||||
rclcpp::Rate r(1.0/5.0);
|
||||
while(!callbackCalled_)
|
||||
{
|
||||
r.sleep();
|
||||
if(!callbackCalled_)
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(),
|
||||
"%s: Did not receive data since 5 seconds! Make sure the input topics are "
|
||||
"published (\"$ ros2 topic hz my_topic\") and the timestamps in their "
|
||||
"header are set. %s",
|
||||
get_name(),
|
||||
subscribedTopicsMsg_.c_str());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "%s", subscribedTopicsMsg_.c_str());
|
||||
}
|
||||
|
||||
PointCloudAssembler::~PointCloudAssembler()
|
||||
{
|
||||
delete exactSync_;
|
||||
delete exactInfoSync_;
|
||||
|
||||
if(warningThread_)
|
||||
{
|
||||
callbackCalled_=true;
|
||||
warningThread_->join();
|
||||
delete warningThread_;
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloudAssembler::callbackCloudOdom(
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg,
|
||||
const nav_msgs::msg::Odometry::ConstSharedPtr odomMsg)
|
||||
{
|
||||
callbackCalled_ = true;
|
||||
rtabmap::Transform odom = rtabmap_conversions::transformFromPoseMsg(odomMsg->pose.pose);
|
||||
if(!odom.isNull())
|
||||
{
|
||||
fixedFrameId_ = odomMsg->header.frame_id;
|
||||
callbackCloud(cloudMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "Reseting point cloud assembler as null odometry has been received.");
|
||||
clouds_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
sensor_msgs::msg::PointCloud2 removeField(const sensor_msgs::msg::PointCloud2 & input, const std::string & field)
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2 output;
|
||||
int offset = 0;
|
||||
std::vector<int> inputFieldIndex;
|
||||
for(size_t i=0; i<input.fields.size(); ++i)
|
||||
{
|
||||
if(input.fields[i].name.compare(field) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensor_msgs::msg::PointField outputField = input.fields[i];
|
||||
outputField.offset = offset;
|
||||
offset += outputField.count * rtabmap_conversions::sizeOfPointField(outputField.datatype);
|
||||
output.fields.push_back(outputField);
|
||||
inputFieldIndex.push_back(i);
|
||||
}
|
||||
}
|
||||
output.header = input.header;
|
||||
output.height = input.height;
|
||||
output.width = input.width;
|
||||
output.is_bigendian = input.is_bigendian;
|
||||
output.is_dense = input.is_dense;
|
||||
output.point_step = offset;
|
||||
output.row_step = output.width * output.point_step;
|
||||
output.data.resize(output.height*output.row_step);
|
||||
int total = output.height*output.width;
|
||||
for(int i=0; i<total; ++i)
|
||||
{
|
||||
// for each point, copy fields
|
||||
int oi = i*output.point_step;
|
||||
int pi = i*input.point_step;
|
||||
for(size_t j=0;j<output.fields.size(); ++j)
|
||||
{
|
||||
memcpy(&output.data[oi + output.fields[j].offset],
|
||||
&input.data[pi + input.fields[inputFieldIndex[j]].offset],
|
||||
output.fields[j].count * rtabmap_conversions::sizeOfPointField(output.fields[j].datatype));
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void PointCloudAssembler::callbackCloudOdomInfo(
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg,
|
||||
const nav_msgs::msg::Odometry::ConstSharedPtr odomMsg,
|
||||
const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr odomInfoMsg)
|
||||
{
|
||||
callbackCalled_ = true;
|
||||
rtabmap::Transform odom = rtabmap_conversions::transformFromPoseMsg(odomMsg->pose.pose);
|
||||
if(!odom.isNull())
|
||||
{
|
||||
if(odomInfoMsg->key_frame_added)
|
||||
{
|
||||
fixedFrameId_ = odomMsg->header.frame_id;
|
||||
callbackCloud(cloudMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_INFO(this->get_logger(), "Skipping non keyframe...");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "Resetting point cloud assembler as null odometry has been received.");
|
||||
clouds_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloudAssembler::callbackCloud(const sensor_msgs::msg::PointCloud2::ConstSharedPtr cloudMsg)
|
||||
{
|
||||
callbackCalled_ = true;
|
||||
if(cloudPub_->get_subscription_count())
|
||||
{
|
||||
UASSERT_MSG(cloudMsg->data.size() == cloudMsg->row_step*cloudMsg->height,
|
||||
uFormat("data=%d row_step=%d height=%d", cloudMsg->data.size(), cloudMsg->row_step, cloudMsg->height).c_str());
|
||||
|
||||
if(skipClouds_<=0 || cloudsSkipped_ >= skipClouds_)
|
||||
{
|
||||
cloudsSkipped_ = 0;
|
||||
|
||||
rtabmap::Transform pose = rtabmap_conversions::getTransform(
|
||||
fixedFrameId_, //fromFrame
|
||||
cloudMsg->header.frame_id, //toFrame
|
||||
cloudMsg->header.stamp,
|
||||
*tfBuffer_,
|
||||
waitForTransform_);
|
||||
|
||||
if(pose.isNull())
|
||||
{
|
||||
RCLCPP_ERROR(get_logger(), "Cloud not transform all clouds! Resetting...");
|
||||
clouds_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
bool isMoving = true;
|
||||
if(!previousPose_.isNull() && (linearUpdate_>0 || angularUpdate_>0))
|
||||
{
|
||||
rtabmap::Transform delta = previousPose_.inverse()*pose;
|
||||
float roll, pitch, yaw;
|
||||
delta.getEulerAngles(roll, pitch, yaw);
|
||||
isMoving = fabs(delta.x()) > linearUpdate_ ||
|
||||
fabs(delta.y()) > linearUpdate_ ||
|
||||
fabs(delta.z()) > linearUpdate_ ||
|
||||
(angularUpdate_>0.0f && (
|
||||
fabs(roll) > angularUpdate_ ||
|
||||
fabs(pitch) > angularUpdate_ ||
|
||||
fabs(yaw) > angularUpdate_));
|
||||
}
|
||||
|
||||
pcl::PCLPointCloud2::Ptr newCloud(new pcl::PCLPointCloud2);
|
||||
if(rangeMin_ > 0.0 || rangeMax_ > 0.0 || voxelSize_ > 0.0f)
|
||||
{
|
||||
pcl_conversions::toPCL(*cloudMsg, *newCloud);
|
||||
rtabmap::LaserScan scan = rtabmap::util3d::laserScanFromPointCloud(*newCloud);
|
||||
scan = rtabmap::util3d::commonFiltering(scan, 1, rangeMin_, rangeMax_, voxelSize_);
|
||||
#if PCL_VERSION_COMPARE(>=, 1, 10, 0)
|
||||
std::uint64_t stamp = newCloud->header.stamp;
|
||||
#else
|
||||
pcl::uint64_t stamp = newCloud->header.stamp;
|
||||
#endif
|
||||
newCloud = rtabmap::util3d::laserScanToPointCloud2(scan, pose);
|
||||
newCloud->header.stamp = stamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2 output;
|
||||
rtabmap_conversions::transformPointCloud(pose.toEigen4f(), *cloudMsg, output);
|
||||
pcl_conversions::toPCL(output, *newCloud);
|
||||
}
|
||||
|
||||
if(!newCloud->is_dense)
|
||||
{
|
||||
// remove nans
|
||||
newCloud = rtabmap::util3d::removeNaNFromPointCloud(newCloud);
|
||||
}
|
||||
|
||||
clouds_.push_back(newCloud);
|
||||
|
||||
#if PCL_VERSION_COMPARE(>=, 1, 10, 0)
|
||||
bool reachedMaxSize =
|
||||
((int)clouds_.size() >= maxClouds_ && maxClouds_ > 0)
|
||||
||
|
||||
((*newCloud).header.stamp >= clouds_.front()->header.stamp + static_cast<std::uint64_t>(assemblingTime_*1000000.0) && assemblingTime_ > 0.0);
|
||||
#else
|
||||
bool reachedMaxSize =
|
||||
((int)clouds_.size() >= maxClouds_ && maxClouds_ > 0)
|
||||
||
|
||||
((*newCloud).header.stamp >= clouds_.front()->header.stamp + static_cast<pcl::uint64_t>(assemblingTime_*1000000.0) && assemblingTime_ > 0.0);
|
||||
#endif
|
||||
|
||||
if( circularBuffer_ || reachedMaxSize )
|
||||
{
|
||||
pcl::PCLPointCloud2Ptr assembled(new pcl::PCLPointCloud2);
|
||||
for(std::list<pcl::PCLPointCloud2::Ptr>::iterator iter=clouds_.begin(); iter!=clouds_.end(); ++iter)
|
||||
{
|
||||
if(assembled->data.empty())
|
||||
{
|
||||
*assembled = *(*iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
pcl::PCLPointCloud2Ptr assembledTmp(new pcl::PCLPointCloud2);
|
||||
#if PCL_VERSION_COMPARE(>=, 1, 10, 0)
|
||||
pcl::concatenate(*assembled, *(*iter), *assembledTmp);
|
||||
#else
|
||||
pcl::concatenatePointCloud(*assembled, *(*iter), *assembledTmp);
|
||||
#endif
|
||||
//Make sure row_step is the sum of both
|
||||
assembledTmp->row_step = assembled->row_step + (*iter)->row_step;
|
||||
assembled = assembledTmp;
|
||||
}
|
||||
}
|
||||
|
||||
sensor_msgs::msg::PointCloud2 rosCloud;
|
||||
if(voxelSize_>0.0)
|
||||
{
|
||||
// estimate if there would be an overflow
|
||||
int x_idx=-1, y_idx=-1, z_idx=-1;
|
||||
for (std::size_t d = 0; d < assembled->fields.size (); ++d)
|
||||
{
|
||||
if (assembled->fields[d].name.compare("x")==0)
|
||||
x_idx = d;
|
||||
if (assembled->fields[d].name.compare("y")==0)
|
||||
y_idx = d;
|
||||
if (assembled->fields[d].name.compare("z")==0)
|
||||
z_idx = d;
|
||||
}
|
||||
bool overflow = false;
|
||||
if(x_idx>=0 && y_idx>=0 && z_idx>=0) {
|
||||
Eigen::Vector4f min_p, max_p;
|
||||
pcl::getMinMax3D(assembled, x_idx, y_idx, z_idx, min_p, max_p);
|
||||
float inverseVoxelSize = 1.0f/voxelSize_;
|
||||
std::int64_t dx = static_cast<std::int64_t>((max_p[0] - min_p[0]) * inverseVoxelSize)+1;
|
||||
std::int64_t dy = static_cast<std::int64_t>((max_p[1] - min_p[1]) * inverseVoxelSize)+1;
|
||||
std::int64_t dz = static_cast<std::int64_t>((max_p[2] - min_p[2]) * inverseVoxelSize)+1;
|
||||
|
||||
if ((dx*dy*dz) > static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()))
|
||||
{
|
||||
overflow = true;
|
||||
}
|
||||
}
|
||||
if(overflow)
|
||||
{
|
||||
rtabmap::LaserScan scan = rtabmap::util3d::laserScanFromPointCloud(*assembled);
|
||||
scan = rtabmap::util3d::commonFiltering(scan, 1, 0, 0, voxelSize_);
|
||||
#if PCL_VERSION_COMPARE(>=, 1, 10, 0)
|
||||
std::uint64_t stamp = assembled->header.stamp;
|
||||
#else
|
||||
pcl::uint64_t stamp = assembled->header.stamp;
|
||||
#endif
|
||||
assembled = rtabmap::util3d::laserScanToPointCloud2(scan);
|
||||
assembled->header.stamp = stamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
pcl::VoxelGrid<pcl::PCLPointCloud2> filter;
|
||||
filter.setLeafSize(voxelSize_, voxelSize_, voxelSize_);
|
||||
filter.setInputCloud(assembled);
|
||||
pcl::PCLPointCloud2Ptr output(new pcl::PCLPointCloud2);
|
||||
filter.filter(*output);
|
||||
assembled = output;
|
||||
}
|
||||
}
|
||||
if(noiseRadius_>0.0 && noiseMinNeighbors_>0)
|
||||
{
|
||||
pcl::RadiusOutlierRemoval<pcl::PCLPointCloud2> filter;
|
||||
filter.setRadiusSearch(noiseRadius_);
|
||||
filter.setMinNeighborsInRadius(noiseMinNeighbors_);
|
||||
filter.setInputCloud(assembled);
|
||||
pcl::PCLPointCloud2Ptr output(new pcl::PCLPointCloud2);
|
||||
filter.filter(*output);
|
||||
assembled = output;
|
||||
}
|
||||
pcl_conversions::moveFromPCL(*assembled, rosCloud);
|
||||
rtabmap::Transform t = pose;
|
||||
if(!frameId_.empty())
|
||||
{
|
||||
// transform in target frame_id instead of sensor frame
|
||||
t = rtabmap_conversions::getTransform(
|
||||
fixedFrameId_, //fromFrame
|
||||
frameId_, //toFrame
|
||||
cloudMsg->header.stamp,
|
||||
*tfBuffer_,
|
||||
waitForTransform_);
|
||||
if(t.isNull())
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Cloud not transform back assembled clouds in target frame \"%s\"! Resetting...", frameId_.c_str());
|
||||
clouds_.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
rtabmap_conversions::transformPointCloud(t.toEigen4f().inverse(), rosCloud, rosCloud);
|
||||
|
||||
if(removeZ_)
|
||||
{
|
||||
rosCloud = removeField(rosCloud, "z");
|
||||
}
|
||||
|
||||
rosCloud.header = cloudMsg->header;
|
||||
if(!frameId_.empty())
|
||||
{
|
||||
rosCloud.header.frame_id = frameId_;
|
||||
}
|
||||
cloudPub_->publish(rosCloud);
|
||||
if(circularBuffer_)
|
||||
{
|
||||
if(!isMoving)
|
||||
{
|
||||
clouds_.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
previousPose_ = pose;
|
||||
if(reachedMaxSize)
|
||||
{
|
||||
clouds_.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
clouds_.clear();
|
||||
previousPose_.setNull();
|
||||
}
|
||||
}
|
||||
else if(!isMoving)
|
||||
{
|
||||
clouds_.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
previousPose_ = pose;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++cloudsSkipped_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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_util::PointCloudAssembler)
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
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_util/point_cloud_xyz.hpp>
|
||||
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
|
||||
#include <pcl_conversions/pcl_conversions.h>
|
||||
#ifdef PRE_ROS_IRON
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#include <image_geometry/pinhole_camera_model.h>
|
||||
#else
|
||||
#include <cv_bridge/cv_bridge.hpp>
|
||||
#include <image_geometry/pinhole_camera_model.hpp>
|
||||
#endif
|
||||
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
|
||||
#include "rtabmap/core/util2d.h"
|
||||
#include "rtabmap/core/util3d.h"
|
||||
#include "rtabmap/core/util3d_filtering.h"
|
||||
#include "rtabmap/core/util3d_surface.h"
|
||||
#include "rtabmap/utilite/UConversion.h"
|
||||
#include "rtabmap/utilite/UStl.h"
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
PointCloudXYZ::PointCloudXYZ(const rclcpp::NodeOptions & options) :
|
||||
Node("point_cloud_xyz", options),
|
||||
maxDepth_(0.0),
|
||||
minDepth_(0.0),
|
||||
voxelSize_(0.0),
|
||||
decimation_(1),
|
||||
noiseFilterRadius_(0.0),
|
||||
noiseFilterMinNeighbors_(5),
|
||||
normalK_(0),
|
||||
normalRadius_(0.0),
|
||||
filterNaNs_(false),
|
||||
approxSyncDepth_(0),
|
||||
approxSyncDisparity_(0),
|
||||
exactSyncDepth_(0),
|
||||
exactSyncDisparity_(0)
|
||||
{
|
||||
int topicQueueSize = 1;
|
||||
int syncQueueSize = 10;
|
||||
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
bool approxSync = true;
|
||||
std::string roiStr;
|
||||
double approxSyncMaxInterval = 0.0;
|
||||
approxSync = this->declare_parameter("approx_sync", approxSync);
|
||||
approxSyncMaxInterval = this->declare_parameter("approx_sync_max_interval", approxSyncMaxInterval);
|
||||
topicQueueSize = this->declare_parameter("topic_queue_size", topicQueueSize);
|
||||
int queueSize = this->declare_parameter("queue_size", -1);
|
||||
if(queueSize != -1)
|
||||
{
|
||||
syncQueueSize = queueSize;
|
||||
RCLCPP_WARN(this->get_logger(), "Parameter \"queue_size\" has been renamed "
|
||||
"to \"sync_queue_size\" and will be removed "
|
||||
"in future versions! The value (%d) is copied to "
|
||||
"\"sync_queue_size\".", syncQueueSize);
|
||||
}
|
||||
syncQueueSize = this->declare_parameter("sync_queue_size", syncQueueSize);
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
int qosCamInfo = this->declare_parameter("qos_camera_info", qos);
|
||||
maxDepth_ = this->declare_parameter("max_depth", maxDepth_);
|
||||
minDepth_ = this->declare_parameter("min_depth", minDepth_);
|
||||
voxelSize_ = this->declare_parameter("voxel_size", voxelSize_);
|
||||
decimation_ = this->declare_parameter("decimation", decimation_);
|
||||
noiseFilterRadius_ = this->declare_parameter("noise_filter_radius", noiseFilterRadius_);
|
||||
noiseFilterMinNeighbors_ = this->declare_parameter("noise_filter_min_neighbors", noiseFilterMinNeighbors_);
|
||||
normalK_ = this->declare_parameter("normal_k", normalK_);
|
||||
normalRadius_ = this->declare_parameter("normal_radius", normalRadius_);
|
||||
filterNaNs_ = this->declare_parameter("filter_nans", filterNaNs_);
|
||||
roiStr = this->declare_parameter("roi_ratios", roiStr);
|
||||
|
||||
//parse roi (region of interest)
|
||||
roiRatios_.resize(4, 0);
|
||||
if(!roiStr.empty())
|
||||
{
|
||||
std::list<std::string> strValues = uSplit(roiStr, ' ');
|
||||
if(strValues.size() != 4)
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "The number of values must be 4 (\"roi_ratios\"=\"%s\")", roiStr.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<float> tmpValues(4);
|
||||
unsigned int i=0;
|
||||
for(std::list<std::string>::iterator jter = strValues.begin(); jter!=strValues.end(); ++jter)
|
||||
{
|
||||
tmpValues[i] = uStr2Float(*jter);
|
||||
++i;
|
||||
}
|
||||
|
||||
if(tmpValues[0] >= 0 && tmpValues[0] < 1 && tmpValues[0] < 1.0f-tmpValues[1] &&
|
||||
tmpValues[1] >= 0 && tmpValues[1] < 1 && tmpValues[1] < 1.0f-tmpValues[0] &&
|
||||
tmpValues[2] >= 0 && tmpValues[2] < 1 && tmpValues[2] < 1.0f-tmpValues[3] &&
|
||||
tmpValues[3] >= 0 && tmpValues[3] < 1 && tmpValues[3] < 1.0f-tmpValues[2])
|
||||
{
|
||||
roiRatios_ = tmpValues;
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "The roi ratios are not valid (\"roi_ratios\"=\"%s\")", roiStr.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "Approximate time sync = %s", approxSync?"true":"false");
|
||||
|
||||
if(approxSync)
|
||||
{
|
||||
approxSyncDepth_ = new message_filters::Synchronizer<MyApproxSyncDepthPolicy>(MyApproxSyncDepthPolicy(syncQueueSize), imageDepthSub_, cameraInfoSub_);
|
||||
if(approxSyncMaxInterval > 0.0)
|
||||
approxSyncDepth_->setMaxIntervalDuration(rclcpp::Duration::from_seconds(approxSyncMaxInterval));
|
||||
approxSyncDepth_->registerCallback(std::bind(&PointCloudXYZ::callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
approxSyncDisparity_ = new message_filters::Synchronizer<MyApproxSyncDisparityPolicy>(MyApproxSyncDisparityPolicy(syncQueueSize), disparitySub_, disparityCameraInfoSub_);
|
||||
if(approxSyncMaxInterval > 0.0)
|
||||
approxSyncDisparity_->setMaxIntervalDuration(rclcpp::Duration::from_seconds(approxSyncMaxInterval));
|
||||
approxSyncDisparity_->registerCallback(std::bind(&PointCloudXYZ::callbackDisparity, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
else
|
||||
{
|
||||
exactSyncDepth_ = new message_filters::Synchronizer<MyExactSyncDepthPolicy>(MyExactSyncDepthPolicy(syncQueueSize), imageDepthSub_, cameraInfoSub_);
|
||||
exactSyncDepth_->registerCallback(std::bind(&PointCloudXYZ::callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
exactSyncDisparity_ = new message_filters::Synchronizer<MyExactSyncDisparityPolicy>(MyExactSyncDisparityPolicy(syncQueueSize), disparitySub_, disparityCameraInfoSub_);
|
||||
exactSyncDisparity_->registerCallback(std::bind(&PointCloudXYZ::callbackDisparity, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
cloudPub_ = create_publisher<sensor_msgs::msg::PointCloud2>("cloud", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
|
||||
image_transport::TransportHints hints(this);
|
||||
imageDepthSub_.subscribe(this, "depth/image", hints.getTransport(), rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
cameraInfoSub_.subscribe(this, "depth/camera_info", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile());
|
||||
|
||||
disparitySub_.subscribe(this, "disparity/image", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
disparityCameraInfoSub_.subscribe(this, "disparity/camera_info", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile());
|
||||
}
|
||||
|
||||
PointCloudXYZ::~PointCloudXYZ()
|
||||
{
|
||||
delete approxSyncDepth_;
|
||||
delete approxSyncDisparity_;
|
||||
delete exactSyncDepth_;
|
||||
delete exactSyncDisparity_;
|
||||
}
|
||||
void PointCloudXYZ::callback(
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr depthMsg,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfo)
|
||||
{
|
||||
if(depthMsg->encoding.compare(sensor_msgs::image_encodings::TYPE_16UC1)!=0 &&
|
||||
depthMsg->encoding.compare(sensor_msgs::image_encodings::TYPE_32FC1)!=0 &&
|
||||
depthMsg->encoding.compare(sensor_msgs::image_encodings::MONO16)!=0)
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Input type depth=32FC1,16UC1,MONO16");
|
||||
return;
|
||||
}
|
||||
|
||||
if(cloudPub_->get_subscription_count())
|
||||
{
|
||||
rclcpp::Time time = now();
|
||||
|
||||
cv_bridge::CvImageConstPtr imageDepthPtr = cv_bridge::toCvShare(depthMsg);
|
||||
|
||||
rtabmap::CameraModel model = rtabmap_conversions::cameraModelFromROS(*cameraInfo);
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr pclCloud;
|
||||
|
||||
cv::Mat depth = imageDepthPtr->image;
|
||||
if( roiRatios_.size() == 4 &&
|
||||
((roiRatios_[0] > 0.0f && roiRatios_[0] <= 1.0f) ||
|
||||
(roiRatios_[1] > 0.0f && roiRatios_[1] <= 1.0f) ||
|
||||
(roiRatios_[2] > 0.0f && roiRatios_[2] <= 1.0f) ||
|
||||
(roiRatios_[3] > 0.0f && roiRatios_[3] <= 1.0f)))
|
||||
{
|
||||
cv::Rect roiDepth = rtabmap::util2d::computeRoi(depth, roiRatios_);
|
||||
cv::Rect roiRgb;
|
||||
if(model.imageWidth() && model.imageHeight())
|
||||
{
|
||||
roiRgb = rtabmap::util2d::computeRoi(model.imageSize(), roiRatios_);
|
||||
}
|
||||
if( roiDepth.width%decimation_==0 &&
|
||||
roiDepth.height%decimation_==0 &&
|
||||
(roiRgb.width != 0 ||
|
||||
(roiRgb.width%decimation_==0 &&
|
||||
roiRgb.height%decimation_==0)))
|
||||
{
|
||||
depth = cv::Mat(depth, roiDepth);
|
||||
if(model.imageWidth() != 0 && model.imageHeight() != 0)
|
||||
{
|
||||
model = model.roi(roiRgb);
|
||||
}
|
||||
else
|
||||
{
|
||||
model = model.roi(roiDepth);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Cannot apply ROI ratios [%f,%f,%f,%f] because resulting "
|
||||
"dimension (depth=%dx%d rgb=%dx%d) cannot be divided exactly "
|
||||
"by decimation parameter (%d). Ignoring ROI ratios...",
|
||||
roiRatios_[0],
|
||||
roiRatios_[1],
|
||||
roiRatios_[2],
|
||||
roiRatios_[3],
|
||||
roiDepth.width,
|
||||
roiDepth.height,
|
||||
roiRgb.width,
|
||||
roiRgb.height,
|
||||
decimation_);
|
||||
}
|
||||
}
|
||||
|
||||
pcl::IndicesPtr indices(new std::vector<int>);
|
||||
pclCloud = rtabmap::util3d::cloudFromDepth(
|
||||
depth,
|
||||
model,
|
||||
decimation_,
|
||||
maxDepth_,
|
||||
minDepth_,
|
||||
indices.get());
|
||||
processAndPublish(pclCloud, indices, depthMsg->header);
|
||||
RCLCPP_DEBUG(this->get_logger(), "point_cloud_xyz from depth time = %f s", (now() - time).seconds());
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloudXYZ::callbackDisparity(
|
||||
const stereo_msgs::msg::DisparityImage::ConstSharedPtr disparityMsg,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfo)
|
||||
{
|
||||
if(disparityMsg->image.encoding.compare(sensor_msgs::image_encodings::TYPE_32FC1) !=0 &&
|
||||
disparityMsg->image.encoding.compare(sensor_msgs::image_encodings::TYPE_16SC1) !=0)
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Input type must be disparity=32FC1 or 16SC1");
|
||||
return;
|
||||
}
|
||||
|
||||
cv::Mat disparity;
|
||||
if(disparityMsg->image.encoding.compare(sensor_msgs::image_encodings::TYPE_32FC1) == 0)
|
||||
{
|
||||
disparity = cv::Mat(disparityMsg->image.height, disparityMsg->image.width, CV_32FC1, const_cast<uchar*>(disparityMsg->image.data.data()));
|
||||
}
|
||||
else
|
||||
{
|
||||
disparity = cv::Mat(disparityMsg->image.height, disparityMsg->image.width, CV_16SC1, const_cast<uchar*>(disparityMsg->image.data.data()));
|
||||
}
|
||||
|
||||
if(cloudPub_->get_subscription_count())
|
||||
{
|
||||
rclcpp::Time time = now();
|
||||
|
||||
cv::Rect roi = rtabmap::util2d::computeRoi(disparity, roiRatios_);
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr pclCloud;
|
||||
rtabmap::CameraModel leftModel = rtabmap_conversions::cameraModelFromROS(*cameraInfo);
|
||||
UASSERT(disparity.cols == leftModel.imageWidth() && disparity.rows == leftModel.imageHeight());
|
||||
rtabmap::StereoCameraModel stereoModel(disparityMsg->f, disparityMsg->f, leftModel.cx()-roiRatios_[0]*double(disparity.cols), leftModel.cy()-roiRatios_[2]*double(disparity.rows), disparityMsg->t);
|
||||
pcl::IndicesPtr indices(new std::vector<int>);
|
||||
pclCloud = rtabmap::util3d::cloudFromDisparity(
|
||||
cv::Mat(disparity, roi),
|
||||
stereoModel,
|
||||
decimation_,
|
||||
maxDepth_,
|
||||
minDepth_,
|
||||
indices.get());
|
||||
|
||||
processAndPublish(pclCloud, indices, disparityMsg->header);
|
||||
RCLCPP_DEBUG(this->get_logger(), "point_cloud_xyz from disparity time = %f s", (now() - time).seconds());
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloudXYZ::processAndPublish(pcl::PointCloud<pcl::PointXYZ>::Ptr & pclCloud, pcl::IndicesPtr & indices, const std_msgs::msg::Header & header)
|
||||
{
|
||||
if(indices->size() && voxelSize_ > 0.0)
|
||||
{
|
||||
pclCloud = rtabmap::util3d::voxelize(pclCloud, indices, voxelSize_);
|
||||
pclCloud->is_dense = true;
|
||||
}
|
||||
|
||||
// Do radius filtering after voxel filtering ( a lot faster)
|
||||
if(!pclCloud->empty() && (pclCloud->is_dense || !indices->empty()) && noiseFilterRadius_ > 0.0 && noiseFilterMinNeighbors_ > 0)
|
||||
{
|
||||
if(pclCloud->is_dense)
|
||||
{
|
||||
indices = rtabmap::util3d::radiusFiltering(pclCloud, noiseFilterRadius_, noiseFilterMinNeighbors_);
|
||||
}
|
||||
else
|
||||
{
|
||||
indices = rtabmap::util3d::radiusFiltering(pclCloud, indices, noiseFilterRadius_, noiseFilterMinNeighbors_);
|
||||
}
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr tmp(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::copyPointCloud(*pclCloud, *indices, *tmp);
|
||||
pclCloud = tmp;
|
||||
}
|
||||
|
||||
sensor_msgs::msg::PointCloud2::UniquePtr rosCloud(new sensor_msgs::msg::PointCloud2);
|
||||
if(!pclCloud->empty() && (pclCloud->is_dense || !indices->empty()) && (normalK_ > 0 || normalRadius_ > 0.0f))
|
||||
{
|
||||
//compute normals
|
||||
pcl::PointCloud<pcl::Normal>::Ptr normals = rtabmap::util3d::computeNormals(pclCloud, normalK_, normalRadius_);
|
||||
pcl::PointCloud<pcl::PointNormal>::Ptr pclCloudNormal(new pcl::PointCloud<pcl::PointNormal>);
|
||||
pcl::concatenateFields(*pclCloud, *normals, *pclCloudNormal);
|
||||
if(filterNaNs_)
|
||||
{
|
||||
pclCloudNormal = rtabmap::util3d::removeNaNNormalsFromPointCloud(pclCloudNormal);
|
||||
}
|
||||
pcl::toROSMsg(*pclCloudNormal, *rosCloud);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(filterNaNs_ && !pclCloud->is_dense)
|
||||
{
|
||||
pclCloud = rtabmap::util3d::removeNaNFromPointCloud(pclCloud);
|
||||
}
|
||||
pcl::toROSMsg(*pclCloud, *rosCloud);
|
||||
}
|
||||
rosCloud->header.stamp = header.stamp;
|
||||
rosCloud->header.frame_id = header.frame_id;
|
||||
|
||||
//publish the message
|
||||
cloudPub_->publish(std::move(rosCloud));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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_util::PointCloudXYZ)
|
||||
|
||||
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
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_util/point_cloud_xyzrgb.hpp>
|
||||
|
||||
#include <pcl_conversions/pcl_conversions.h>
|
||||
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
|
||||
#ifdef PRE_ROS_IRON
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#include <image_geometry/pinhole_camera_model.h>
|
||||
#include <image_geometry/stereo_camera_model.h>
|
||||
#else
|
||||
#include <cv_bridge/cv_bridge.hpp>
|
||||
#include <image_geometry/pinhole_camera_model.hpp>
|
||||
#include <image_geometry/stereo_camera_model.hpp>
|
||||
#endif
|
||||
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
|
||||
#include "rtabmap/core/util2d.h"
|
||||
#include "rtabmap/core/util3d.h"
|
||||
#include "rtabmap/core/util3d_filtering.h"
|
||||
#include "rtabmap/core/util3d_surface.h"
|
||||
#include "rtabmap/utilite/UConversion.h"
|
||||
#include "rtabmap/utilite/UStl.h"
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
PointCloudXYZRGB::PointCloudXYZRGB(const rclcpp::NodeOptions & options) :
|
||||
Node("point_cloud_xyzrgb", options),
|
||||
maxDepth_(0.0),
|
||||
minDepth_(0.0),
|
||||
voxelSize_(0.0),
|
||||
decimation_(1),
|
||||
noiseFilterRadius_(0.0),
|
||||
noiseFilterMinNeighbors_(5),
|
||||
normalK_(0),
|
||||
normalRadius_(0.0),
|
||||
filterNaNs_(false),
|
||||
approxSyncDepth_(0),
|
||||
approxSyncDisparity_(0),
|
||||
approxSyncStereo_(0),
|
||||
exactSyncDepth_(0),
|
||||
exactSyncDisparity_(0),
|
||||
exactSyncStereo_(0)
|
||||
{
|
||||
bool approxSync = true;
|
||||
std::string roiStr;
|
||||
int topicQueueSize = 1;
|
||||
int syncQueueSize = 10;
|
||||
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
double approxSyncMaxInterval = 0.0;
|
||||
approxSync = this->declare_parameter("approx_sync", approxSync);
|
||||
approxSyncMaxInterval = this->declare_parameter("approx_sync_max_interval", approxSyncMaxInterval);
|
||||
topicQueueSize = this->declare_parameter("topic_queue_size", topicQueueSize);
|
||||
int queueSize = this->declare_parameter("queue_size", -1);
|
||||
if(queueSize != -1)
|
||||
{
|
||||
syncQueueSize = queueSize;
|
||||
RCLCPP_WARN(this->get_logger(), "Parameter \"queue_size\" has been renamed "
|
||||
"to \"sync_queue_size\" and will be removed "
|
||||
"in future versions! The value (%d) is copied to "
|
||||
"\"sync_queue_size\".", syncQueueSize);
|
||||
}
|
||||
syncQueueSize = this->declare_parameter("sync_queue_size", syncQueueSize);
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
int qosCamInfo = this->declare_parameter("qos_camera_info", qos);
|
||||
maxDepth_ = this->declare_parameter("max_depth", maxDepth_);
|
||||
minDepth_ = this->declare_parameter("min_depth", minDepth_);
|
||||
voxelSize_ = this->declare_parameter("voxel_size", voxelSize_);
|
||||
decimation_ = this->declare_parameter("decimation", decimation_);
|
||||
noiseFilterRadius_ = this->declare_parameter("noise_filter_radius", noiseFilterRadius_);
|
||||
noiseFilterMinNeighbors_ = this->declare_parameter("noise_filter_min_neighbors", noiseFilterMinNeighbors_);
|
||||
normalK_ = this->declare_parameter("normal_k", normalK_);
|
||||
normalRadius_ = this->declare_parameter("normal_radius", normalRadius_);
|
||||
filterNaNs_ = this->declare_parameter("filter_nans", filterNaNs_);
|
||||
roiStr = this->declare_parameter("roi_ratios", roiStr);
|
||||
|
||||
//parse roi (region of interest)
|
||||
roiRatios_.resize(4, 0);
|
||||
if(!roiStr.empty())
|
||||
{
|
||||
std::list<std::string> strValues = uSplit(roiStr, ' ');
|
||||
if(strValues.size() != 4)
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "The number of values must be 4 (\"roi_ratios\"=\"%s\")", roiStr.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<float> tmpValues(4);
|
||||
unsigned int i=0;
|
||||
for(std::list<std::string>::iterator jter = strValues.begin(); jter!=strValues.end(); ++jter)
|
||||
{
|
||||
tmpValues[i] = uStr2Float(*jter);
|
||||
++i;
|
||||
}
|
||||
|
||||
if(tmpValues[0] >= 0 && tmpValues[0] < 1 && tmpValues[0] < 1.0f-tmpValues[1] &&
|
||||
tmpValues[1] >= 0 && tmpValues[1] < 1 && tmpValues[1] < 1.0f-tmpValues[0] &&
|
||||
tmpValues[2] >= 0 && tmpValues[2] < 1 && tmpValues[2] < 1.0f-tmpValues[3] &&
|
||||
tmpValues[3] >= 0 && tmpValues[3] < 1 && tmpValues[3] < 1.0f-tmpValues[2])
|
||||
{
|
||||
roiRatios_ = tmpValues;
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "The roi ratios are not valid (\"roi_ratios\"=\"%s\")", roiStr.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StereoBM parameters
|
||||
stereoBMParameters_ = rtabmap::Parameters::getDefaultParameters("StereoBM");
|
||||
for(rtabmap::ParametersMap::iterator iter=stereoBMParameters_.begin(); iter!=stereoBMParameters_.end(); ++iter)
|
||||
{
|
||||
std::string vStr = declare_parameter(iter->first, iter->second);
|
||||
if(vStr.compare(iter->second)!=0)
|
||||
{
|
||||
RCLCPP_INFO(this->get_logger(), "point_cloud_xyzrgb: Setting parameter \"%s\"=\"%s\"", iter->first.c_str(), vStr.c_str());
|
||||
iter->second = vStr;
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "Approximate time sync = %s", approxSync?"true":"false");
|
||||
|
||||
cloudPub_ = create_publisher<sensor_msgs::msg::PointCloud2>("cloud", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
|
||||
rgbdImageSub_ = create_subscription<rtabmap_msgs::msg::RGBDImage>("rgbd_image", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos), std::bind(&PointCloudXYZRGB::rgbdImageCallback, this, std::placeholders::_1));
|
||||
|
||||
if(approxSync)
|
||||
{
|
||||
|
||||
approxSyncDepth_ = new message_filters::Synchronizer<MyApproxSyncDepthPolicy>(MyApproxSyncDepthPolicy(syncQueueSize), imageSub_, imageDepthSub_, cameraInfoSub_);
|
||||
if(approxSyncMaxInterval > 0.0)
|
||||
approxSyncDepth_->setMaxIntervalDuration(rclcpp::Duration::from_seconds(approxSyncMaxInterval));
|
||||
approxSyncDepth_->registerCallback(std::bind(&PointCloudXYZRGB::depthCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
|
||||
approxSyncDisparity_ = new message_filters::Synchronizer<MyApproxSyncDisparityPolicy>(MyApproxSyncDisparityPolicy(syncQueueSize), imageLeft_, imageDisparitySub_, cameraInfoLeft_);
|
||||
if(approxSyncMaxInterval > 0.0)
|
||||
approxSyncDisparity_->setMaxIntervalDuration(rclcpp::Duration::from_seconds(approxSyncMaxInterval));
|
||||
approxSyncDisparity_->registerCallback(std::bind(&PointCloudXYZRGB::disparityCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
|
||||
approxSyncStereo_ = new message_filters::Synchronizer<MyApproxSyncStereoPolicy>(MyApproxSyncStereoPolicy(syncQueueSize), imageLeft_, imageRight_, cameraInfoLeft_, cameraInfoRight_);
|
||||
if(approxSyncMaxInterval > 0.0)
|
||||
approxSyncStereo_->setMaxIntervalDuration(rclcpp::Duration::from_seconds(approxSyncMaxInterval));
|
||||
approxSyncStereo_->registerCallback(std::bind(&PointCloudXYZRGB::stereoCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
}
|
||||
else
|
||||
{
|
||||
exactSyncDepth_ = new message_filters::Synchronizer<MyExactSyncDepthPolicy>(MyExactSyncDepthPolicy(syncQueueSize), imageSub_, imageDepthSub_, cameraInfoSub_);
|
||||
exactSyncDepth_->registerCallback(std::bind(&PointCloudXYZRGB::depthCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
|
||||
exactSyncDisparity_ = new message_filters::Synchronizer<MyExactSyncDisparityPolicy>(MyExactSyncDisparityPolicy(syncQueueSize), imageLeft_, imageDisparitySub_, cameraInfoLeft_);
|
||||
exactSyncDisparity_->registerCallback(std::bind(&PointCloudXYZRGB::disparityCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
|
||||
exactSyncStereo_ = new message_filters::Synchronizer<MyExactSyncStereoPolicy>(MyExactSyncStereoPolicy(syncQueueSize), imageLeft_, imageRight_, cameraInfoLeft_, cameraInfoRight_);
|
||||
exactSyncStereo_->registerCallback(std::bind(&PointCloudXYZRGB::stereoCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
}
|
||||
|
||||
image_transport::TransportHints hints(this);
|
||||
imageSub_.subscribe(this, "rgb/image", hints.getTransport(), rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
imageDepthSub_.subscribe(this, "depth/image", hints.getTransport(), rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
cameraInfoSub_.subscribe(this, "rgb/camera_info", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile());
|
||||
|
||||
imageDisparitySub_.subscribe(this, "disparity", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
|
||||
imageLeft_.subscribe(this, "left/image", hints.getTransport(), rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
imageRight_.subscribe(this, "right/image", hints.getTransport(), rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
cameraInfoLeft_.subscribe(this, "left/camera_info", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile());
|
||||
cameraInfoRight_.subscribe(this, "right/camera_info", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile());
|
||||
}
|
||||
|
||||
PointCloudXYZRGB::~PointCloudXYZRGB()
|
||||
{
|
||||
delete approxSyncDepth_;
|
||||
delete approxSyncDisparity_;
|
||||
delete approxSyncStereo_;
|
||||
delete exactSyncDepth_;
|
||||
delete exactSyncDisparity_;
|
||||
delete exactSyncStereo_;
|
||||
}
|
||||
|
||||
void PointCloudXYZRGB::depthCallback(
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr image,
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr imageDepth,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfo)
|
||||
{
|
||||
if(!(image->encoding.compare(sensor_msgs::image_encodings::TYPE_8UC1) ==0 ||
|
||||
image->encoding.compare(sensor_msgs::image_encodings::MONO8) ==0 ||
|
||||
image->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0 ||
|
||||
image->encoding.compare(sensor_msgs::image_encodings::BGR8) == 0 ||
|
||||
image->encoding.compare(sensor_msgs::image_encodings::RGB8) == 0 ||
|
||||
image->encoding.compare(sensor_msgs::image_encodings::BGRA8) == 0 ||
|
||||
image->encoding.compare(sensor_msgs::image_encodings::RGBA8) == 0 ||
|
||||
image->encoding.compare(sensor_msgs::image_encodings::BAYER_GRBG8) == 0) ||
|
||||
!(imageDepth->encoding.compare(sensor_msgs::image_encodings::TYPE_16UC1)==0 ||
|
||||
imageDepth->encoding.compare(sensor_msgs::image_encodings::TYPE_32FC1)==0 ||
|
||||
imageDepth->encoding.compare(sensor_msgs::image_encodings::MONO16)==0))
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Input type must be image=mono8,mono16,rgb8,bgr8 and image_depth=32FC1,16UC1,mono16");
|
||||
return;
|
||||
}
|
||||
|
||||
if(cloudPub_->get_subscription_count())
|
||||
{
|
||||
rclcpp::Time time = now();
|
||||
|
||||
cv_bridge::CvImageConstPtr imagePtr;
|
||||
if(image->encoding.compare(sensor_msgs::image_encodings::TYPE_8UC1)==0)
|
||||
{
|
||||
imagePtr = cv_bridge::toCvShare(image);
|
||||
}
|
||||
else if(image->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
image->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0)
|
||||
{
|
||||
imagePtr = cv_bridge::toCvShare(image, "mono8");
|
||||
}
|
||||
else
|
||||
{
|
||||
imagePtr = cv_bridge::toCvShare(image, "bgr8");
|
||||
}
|
||||
|
||||
cv_bridge::CvImageConstPtr imageDepthPtr = cv_bridge::toCvShare(imageDepth);
|
||||
|
||||
rtabmap::CameraModel model = rtabmap_conversions::cameraModelFromROS(*cameraInfo);
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr pclCloud;
|
||||
|
||||
cv::Mat rgb = imagePtr->image;
|
||||
cv::Mat depth = imageDepthPtr->image;
|
||||
if( roiRatios_.size() == 4 &&
|
||||
((roiRatios_[0] > 0.0f && roiRatios_[0] <= 1.0f) ||
|
||||
(roiRatios_[1] > 0.0f && roiRatios_[1] <= 1.0f) ||
|
||||
(roiRatios_[2] > 0.0f && roiRatios_[2] <= 1.0f) ||
|
||||
(roiRatios_[3] > 0.0f && roiRatios_[3] <= 1.0f)))
|
||||
{
|
||||
cv::Rect roiDepth = rtabmap::util2d::computeRoi(depth, roiRatios_);
|
||||
cv::Rect roiRgb = rtabmap::util2d::computeRoi(rgb, roiRatios_);
|
||||
if( roiDepth.width%decimation_==0 &&
|
||||
roiDepth.height%decimation_==0 &&
|
||||
roiRgb.width%decimation_==0 &&
|
||||
roiRgb.height%decimation_==0)
|
||||
{
|
||||
depth = cv::Mat(depth, roiDepth);
|
||||
rgb = cv::Mat(rgb, roiRgb);
|
||||
model = model.roi(roiRgb);
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Cannot apply ROI ratios [%f,%f,%f,%f] because resulting "
|
||||
"dimension (depth=%dx%d rgb=%dx%d) cannot be divided exactly "
|
||||
"by decimation parameter (%d). Ignoring ROI ratios...",
|
||||
roiRatios_[0],
|
||||
roiRatios_[1],
|
||||
roiRatios_[2],
|
||||
roiRatios_[3],
|
||||
roiDepth.width,
|
||||
roiDepth.height,
|
||||
roiRgb.width,
|
||||
roiRgb.height,
|
||||
decimation_);
|
||||
}
|
||||
}
|
||||
|
||||
pcl::IndicesPtr indices(new std::vector<int>);
|
||||
pclCloud = rtabmap::util3d::cloudFromDepthRGB(
|
||||
rgb,
|
||||
depth,
|
||||
model,
|
||||
decimation_,
|
||||
maxDepth_,
|
||||
minDepth_,
|
||||
indices.get());
|
||||
|
||||
processAndPublish(pclCloud, indices, imagePtr->header);
|
||||
|
||||
RCLCPP_DEBUG(this->get_logger(), "point_cloud_xyzrgb from RGB-D time = %f s", (now() - time).seconds());
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloudXYZRGB::disparityCallback(
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr image,
|
||||
const stereo_msgs::msg::DisparityImage::ConstSharedPtr imageDisparity,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfo)
|
||||
{
|
||||
cv_bridge::CvImageConstPtr imagePtr;
|
||||
if(image->encoding.compare(sensor_msgs::image_encodings::TYPE_8UC1)==0)
|
||||
{
|
||||
imagePtr = cv_bridge::toCvShare(image);
|
||||
}
|
||||
else if(image->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
image->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0)
|
||||
{
|
||||
imagePtr = cv_bridge::toCvShare(image, "mono8");
|
||||
}
|
||||
else
|
||||
{
|
||||
imagePtr = cv_bridge::toCvShare(image, "bgr8");
|
||||
}
|
||||
|
||||
if(imageDisparity->image.encoding.compare(sensor_msgs::image_encodings::TYPE_32FC1) !=0 &&
|
||||
imageDisparity->image.encoding.compare(sensor_msgs::image_encodings::TYPE_16SC1) !=0)
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Input type must be disparity=32FC1 or 16SC1");
|
||||
return;
|
||||
}
|
||||
|
||||
cv::Mat disparity;
|
||||
if(imageDisparity->image.encoding.compare(sensor_msgs::image_encodings::TYPE_32FC1) == 0)
|
||||
{
|
||||
disparity = cv::Mat(imageDisparity->image.height, imageDisparity->image.width, CV_32FC1, const_cast<uchar*>(imageDisparity->image.data.data()));
|
||||
}
|
||||
else
|
||||
{
|
||||
disparity = cv::Mat(imageDisparity->image.height, imageDisparity->image.width, CV_16SC1, const_cast<uchar*>(imageDisparity->image.data.data()));
|
||||
}
|
||||
|
||||
if(cloudPub_->get_subscription_count())
|
||||
{
|
||||
rclcpp::Time time = now();
|
||||
|
||||
cv::Rect roi = rtabmap::util2d::computeRoi(disparity, roiRatios_);
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr pclCloud;
|
||||
rtabmap::CameraModel leftModel = rtabmap_conversions::cameraModelFromROS(*cameraInfo);
|
||||
UASSERT(disparity.cols == leftModel.imageWidth() && disparity.rows == leftModel.imageHeight());
|
||||
UASSERT(imagePtr->image.cols == leftModel.imageWidth() && imagePtr->image.rows == leftModel.imageHeight());
|
||||
rtabmap::StereoCameraModel stereoModel(imageDisparity->f, imageDisparity->f, leftModel.cx()-roiRatios_[0]*double(disparity.cols), leftModel.cy()-roiRatios_[2]*double(disparity.rows), imageDisparity->t);
|
||||
pcl::IndicesPtr indices(new std::vector<int>);
|
||||
pclCloud = rtabmap::util3d::cloudFromDisparityRGB(
|
||||
cv::Mat(imagePtr->image, roi),
|
||||
cv::Mat(disparity, roi),
|
||||
stereoModel,
|
||||
decimation_,
|
||||
maxDepth_,
|
||||
minDepth_,
|
||||
indices.get());
|
||||
|
||||
processAndPublish(pclCloud, indices, imageDisparity->header);
|
||||
|
||||
RCLCPP_DEBUG(this->get_logger(), "point_cloud_xyzrgb from disparity time = %f s", (now() - time).seconds());
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloudXYZRGB::stereoCallback(
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr imageLeft,
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr imageRight,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr camInfoLeft,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr camInfoRight)
|
||||
{
|
||||
if(!(imageLeft->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
imageLeft->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0 ||
|
||||
imageLeft->encoding.compare(sensor_msgs::image_encodings::BGR8) == 0 ||
|
||||
imageLeft->encoding.compare(sensor_msgs::image_encodings::RGB8) == 0 ||
|
||||
imageLeft->encoding.compare(sensor_msgs::image_encodings::RGBA8) == 0 ||
|
||||
imageLeft->encoding.compare(sensor_msgs::image_encodings::BGRA8) == 0) ||
|
||||
!(imageRight->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
imageRight->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0 ||
|
||||
imageRight->encoding.compare(sensor_msgs::image_encodings::BGR8) == 0 ||
|
||||
imageRight->encoding.compare(sensor_msgs::image_encodings::RGB8) == 0 ||
|
||||
imageRight->encoding.compare(sensor_msgs::image_encodings::RGBA8) == 0 ||
|
||||
imageRight->encoding.compare(sensor_msgs::image_encodings::BGRA8) == 0))
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Input type must be image=mono8,mono16,rgb8,bgr8,rgba8,bgra8 (enc=%s)", imageLeft->encoding.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if(cloudPub_->get_subscription_count())
|
||||
{
|
||||
rclcpp::Time time = now();
|
||||
|
||||
cv_bridge::CvImageConstPtr ptrLeftImage, ptrRightImage;
|
||||
if(imageLeft->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
imageLeft->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0)
|
||||
{
|
||||
ptrLeftImage = cv_bridge::toCvShare(imageLeft, "mono8");
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrLeftImage = cv_bridge::toCvShare(imageLeft, "bgr8");
|
||||
}
|
||||
ptrRightImage = cv_bridge::toCvShare(imageRight, "mono8");
|
||||
|
||||
if(roiRatios_[0]!=0.0f || roiRatios_[1]!=0.0f || roiRatios_[2]!=0.0f || roiRatios_[3]!=0.0f)
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "\"roi_ratios\" set but ignored for stereo images.");
|
||||
}
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr pclCloud;
|
||||
pcl::IndicesPtr indices(new std::vector<int>);
|
||||
pclCloud = rtabmap::util3d::cloudFromStereoImages(
|
||||
ptrLeftImage->image,
|
||||
ptrRightImage->image,
|
||||
rtabmap_conversions::stereoCameraModelFromROS(*camInfoLeft, *camInfoRight),
|
||||
decimation_,
|
||||
maxDepth_,
|
||||
minDepth_,
|
||||
indices.get(),
|
||||
stereoBMParameters_);
|
||||
|
||||
processAndPublish(pclCloud, indices, imageLeft->header);
|
||||
|
||||
RCLCPP_DEBUG(this->get_logger(), "point_cloud_xyzrgb from stereo time = %f s", (now() - time).seconds());
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloudXYZRGB::rgbdImageCallback(
|
||||
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image)
|
||||
{
|
||||
if(cloudPub_->get_subscription_count())
|
||||
{
|
||||
rclcpp::Time time = now();
|
||||
|
||||
rtabmap::SensorData data = rtabmap_conversions::rgbdImageFromROS(image);
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr pclCloud;
|
||||
pcl::IndicesPtr indices(new std::vector<int>);
|
||||
if(data.isValid())
|
||||
{
|
||||
pclCloud = rtabmap::util3d::cloudRGBFromSensorData(
|
||||
data,
|
||||
decimation_,
|
||||
maxDepth_,
|
||||
minDepth_,
|
||||
indices.get(),
|
||||
stereoBMParameters_,
|
||||
roiRatios_);
|
||||
|
||||
processAndPublish(pclCloud, indices, image->header);
|
||||
}
|
||||
|
||||
RCLCPP_DEBUG(this->get_logger(), "point_cloud_xyzrgb from rgbd_image time = %f s", (now() - time).seconds());
|
||||
}
|
||||
}
|
||||
|
||||
void PointCloudXYZRGB::processAndPublish(
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr & pclCloud,
|
||||
pcl::IndicesPtr & indices,
|
||||
const std_msgs::msg::Header & header)
|
||||
{
|
||||
if(indices->size() && voxelSize_ > 0.0)
|
||||
{
|
||||
pclCloud = rtabmap::util3d::voxelize(pclCloud, indices, voxelSize_);
|
||||
pclCloud->is_dense = true;
|
||||
}
|
||||
|
||||
// Do radius filtering after voxel filtering ( a lot faster)
|
||||
if(!pclCloud->empty() && (pclCloud->is_dense || !indices->empty()) && noiseFilterRadius_ > 0.0 && noiseFilterMinNeighbors_ > 0)
|
||||
{
|
||||
if(pclCloud->is_dense)
|
||||
{
|
||||
indices = rtabmap::util3d::radiusFiltering(pclCloud, noiseFilterRadius_, noiseFilterMinNeighbors_);
|
||||
}
|
||||
else
|
||||
{
|
||||
indices = rtabmap::util3d::radiusFiltering(pclCloud, indices, noiseFilterRadius_, noiseFilterMinNeighbors_);
|
||||
}
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr tmp(new pcl::PointCloud<pcl::PointXYZRGB>);
|
||||
pcl::copyPointCloud(*pclCloud, *indices, *tmp);
|
||||
pclCloud = tmp;
|
||||
}
|
||||
|
||||
sensor_msgs::msg::PointCloud2::UniquePtr rosCloud(new sensor_msgs::msg::PointCloud2);
|
||||
if(!pclCloud->empty() && (pclCloud->is_dense || !indices->empty()) && (normalK_ > 0 || normalRadius_ > 0.0f))
|
||||
{
|
||||
//compute normals
|
||||
pcl::PointCloud<pcl::Normal>::Ptr normals = rtabmap::util3d::computeNormals(pclCloud, normalK_, normalRadius_);
|
||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr pclCloudNormal(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
|
||||
pcl::concatenateFields(*pclCloud, *normals, *pclCloudNormal);
|
||||
if(filterNaNs_)
|
||||
{
|
||||
pclCloudNormal = rtabmap::util3d::removeNaNNormalsFromPointCloud(pclCloudNormal);
|
||||
}
|
||||
pcl::toROSMsg(*pclCloudNormal, *rosCloud);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(filterNaNs_ && !pclCloud->is_dense)
|
||||
{
|
||||
pclCloud = rtabmap::util3d::removeNaNFromPointCloud(pclCloud);
|
||||
}
|
||||
pcl::toROSMsg(*pclCloud, *rosCloud);
|
||||
}
|
||||
rosCloud->header.stamp = header.stamp;
|
||||
rosCloud->header.frame_id = header.frame_id;
|
||||
|
||||
//publish the message
|
||||
cloudPub_->publish(std::move(rosCloud));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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_util::PointCloudXYZRGB)
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
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_util/pointcloud_to_depthimage.hpp>
|
||||
|
||||
#include <rtabmap_conversions/MsgConversion.h>
|
||||
#include <rtabmap/core/util3d.h>
|
||||
#include <rtabmap/core/util2d.h>
|
||||
#include <rtabmap/utilite/ULogger.h>
|
||||
|
||||
#include <sensor_msgs/image_encodings.hpp>
|
||||
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <pcl_conversions/pcl_conversions.h>
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
PointCloudToDepthImage::PointCloudToDepthImage(const rclcpp::NodeOptions & options) :
|
||||
Node("pointcloud_to_depthimage", options),
|
||||
waitForTransform_(0.1),
|
||||
fillHolesSize_ (0),
|
||||
fillHolesError_(0.1),
|
||||
fillIterations_(1),
|
||||
decimation_(1),
|
||||
upscale_(false),
|
||||
upscaleDepthErrorRatio_(0.02),
|
||||
approxSync_(0),
|
||||
exactSync_(0)
|
||||
{
|
||||
tfBuffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
|
||||
//auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
|
||||
// this->get_node_base_interface(),
|
||||
// this->get_node_timers_interface());
|
||||
//tfBuffer_->setCreateTimerInterface(timer_interface);
|
||||
tfListener_ = std::make_shared<tf2_ros::TransformListener>(*tfBuffer_);
|
||||
|
||||
int topicQueueSize = 10;
|
||||
int syncQueueSize = 10;
|
||||
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
bool approx = true;
|
||||
topicQueueSize = this->declare_parameter("topic_queue_size", topicQueueSize);
|
||||
int queueSize = this->declare_parameter("queue_size", -1);
|
||||
if(queueSize != -1)
|
||||
{
|
||||
syncQueueSize = queueSize;
|
||||
RCLCPP_WARN(this->get_logger(), "Parameter \"queue_size\" has been renamed "
|
||||
"to \"sync_queue_size\" and will be removed "
|
||||
"in future versions! The value (%d) is copied to "
|
||||
"\"sync_queue_size\".", syncQueueSize);
|
||||
}
|
||||
syncQueueSize = this->declare_parameter("sync_queue_size", syncQueueSize);
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
int qosCamInfo = this->declare_parameter("qos_camera_info", qos);
|
||||
fixedFrameId_ = this->declare_parameter("fixed_frame_id", fixedFrameId_);
|
||||
waitForTransform_ = this->declare_parameter("wait_for_transform", waitForTransform_);
|
||||
fillHolesSize_ = this->declare_parameter("fill_holes_size", fillHolesSize_);
|
||||
fillHolesError_ = this->declare_parameter("fill_holes_error", fillHolesError_);
|
||||
fillIterations_ = this->declare_parameter("fill_iterations", fillIterations_);
|
||||
decimation_ = this->declare_parameter("decimation", decimation_);
|
||||
approx = this->declare_parameter("approx", approx);
|
||||
upscale_ = this->declare_parameter("upscale", upscale_);
|
||||
upscaleDepthErrorRatio_ = this->declare_parameter("upscale_depth_error_ratio", upscaleDepthErrorRatio_);
|
||||
|
||||
if(fixedFrameId_.empty() && approx)
|
||||
{
|
||||
RCLCPP_FATAL(this->get_logger(), "fixed_frame_id should be set when using approximate "
|
||||
"time synchronization (approx=true)! If the robot "
|
||||
"is moving, it could be \"odom\". If not moving, it "
|
||||
"could be \"base_link\".");
|
||||
}
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "Params:");
|
||||
RCLCPP_INFO(this->get_logger(), " approx=%s", approx?"true":"false");
|
||||
RCLCPP_INFO(this->get_logger(), " topic_queue_size=%d", topicQueueSize);
|
||||
RCLCPP_INFO(this->get_logger(), " sync_queue_size=%d", syncQueueSize);
|
||||
RCLCPP_INFO(this->get_logger(), " fixed_frame_id=%s", fixedFrameId_.c_str());
|
||||
RCLCPP_INFO(this->get_logger(), " wait_for_transform=%fs", waitForTransform_);
|
||||
RCLCPP_INFO(this->get_logger(), " fill_holes_size=%d pixels (0=disabled)", fillHolesSize_);
|
||||
RCLCPP_INFO(this->get_logger(), " fill_holes_error=%f", fillHolesError_);
|
||||
RCLCPP_INFO(this->get_logger(), " fill_iterations=%d", fillIterations_);
|
||||
RCLCPP_INFO(this->get_logger(), " decimation=%d", decimation_);
|
||||
RCLCPP_INFO(this->get_logger(), " upscale=%s (upscale_depth_error_ratio=%f)", upscale_?"true":"false", upscaleDepthErrorRatio_);
|
||||
|
||||
depthImage16Pub_ = image_transport::create_publisher(this, "image_raw", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile()); // 16 bits unsigned in mm
|
||||
depthImage32Pub_ = image_transport::create_publisher(this, "image", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());// 32 bits float in meters
|
||||
pointCloudTransformedPub_ = create_publisher<sensor_msgs::msg::PointCloud2>("cloud_transformed", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
cameraInfo16Pub_ = create_publisher<sensor_msgs::msg::CameraInfo>(depthImage16Pub_.getTopic()+"/camera_info", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qosCamInfo));
|
||||
cameraInfo32Pub_ = create_publisher<sensor_msgs::msg::CameraInfo>(depthImage32Pub_.getTopic()+"/camera_info", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qosCamInfo));
|
||||
|
||||
if(approx)
|
||||
{
|
||||
approxSync_ = new message_filters::Synchronizer<MyApproxSyncPolicy>(MyApproxSyncPolicy(syncQueueSize), pointCloudSub_, cameraInfoSub_);
|
||||
approxSync_->registerCallback(std::bind(&PointCloudToDepthImage::callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
else
|
||||
{
|
||||
fixedFrameId_.clear();
|
||||
exactSync_ = new message_filters::Synchronizer<MyExactSyncPolicy>(MyExactSyncPolicy(syncQueueSize), pointCloudSub_, cameraInfoSub_);
|
||||
exactSync_->registerCallback(std::bind(&PointCloudToDepthImage::callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
pointCloudSub_.subscribe(this, "cloud", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
cameraInfoSub_.subscribe(this, "camera_info", rclcpp::QoS(topicQueueSize).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile());
|
||||
}
|
||||
|
||||
PointCloudToDepthImage::~PointCloudToDepthImage()
|
||||
{
|
||||
delete approxSync_;
|
||||
delete exactSync_;
|
||||
}
|
||||
|
||||
void PointCloudToDepthImage::callback(
|
||||
const sensor_msgs::msg::PointCloud2::ConstSharedPtr pointCloud2Msg,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfoMsg)
|
||||
{
|
||||
if(depthImage32Pub_.getNumSubscribers() > 0 || depthImage16Pub_.getNumSubscribers() > 0)
|
||||
{
|
||||
double cloudStamp = rtabmap_conversions::timestampFromROS(pointCloud2Msg->header.stamp);
|
||||
double infoStamp = rtabmap_conversions::timestampFromROS(cameraInfoMsg->header.stamp);
|
||||
|
||||
rtabmap::Transform cloudDisplacement = rtabmap::Transform::getIdentity();
|
||||
if(!fixedFrameId_.empty())
|
||||
{
|
||||
// approx sync
|
||||
cloudDisplacement = rtabmap_conversions::getMovingTransform(
|
||||
pointCloud2Msg->header.frame_id,
|
||||
fixedFrameId_,
|
||||
pointCloud2Msg->header.stamp,
|
||||
cameraInfoMsg->header.stamp,
|
||||
*tfBuffer_,
|
||||
waitForTransform_);
|
||||
}
|
||||
|
||||
if(cloudDisplacement.isNull())
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Could not find transform between %s and %s, accordingly to %s, aborting!",
|
||||
pointCloud2Msg->header.frame_id.c_str(),
|
||||
cameraInfoMsg->header.frame_id.c_str(),
|
||||
fixedFrameId_.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
rtabmap::Transform cloudToCamera = rtabmap_conversions::getTransform(
|
||||
pointCloud2Msg->header.frame_id,
|
||||
cameraInfoMsg->header.frame_id,
|
||||
cameraInfoMsg->header.stamp,
|
||||
*tfBuffer_,
|
||||
waitForTransform_);
|
||||
|
||||
if(cloudToCamera.isNull())
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Could not find transform between %s and %s, aborting!",
|
||||
pointCloud2Msg->header.frame_id.c_str(),
|
||||
cameraInfoMsg->header.frame_id.c_str());
|
||||
return;
|
||||
}
|
||||
rtabmap::Transform localTransform = cloudDisplacement*cloudToCamera;
|
||||
|
||||
rtabmap::CameraModel model = rtabmap_conversions::cameraModelFromROS(*cameraInfoMsg, localTransform);
|
||||
sensor_msgs::msg::CameraInfo cameraInfoMsgOut = *cameraInfoMsg;
|
||||
|
||||
if(decimation_ > 1)
|
||||
{
|
||||
if(model.imageWidth()%decimation_ == 0 && model.imageHeight()%decimation_ == 0)
|
||||
{
|
||||
float scale = 1.0f/float(decimation_);
|
||||
model = model.scaled(scale);
|
||||
rtabmap_conversions::cameraModelToROS(model, cameraInfoMsgOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "decimation (%d) not valid for image size %dx%d",
|
||||
decimation_,
|
||||
model.imageWidth(),
|
||||
model.imageHeight());
|
||||
}
|
||||
}
|
||||
|
||||
UASSERT_MSG(pointCloud2Msg->data.size() == pointCloud2Msg->row_step*pointCloud2Msg->height,
|
||||
uFormat("data=%d row_step=%d height=%d", pointCloud2Msg->data.size(), pointCloud2Msg->row_step, pointCloud2Msg->height).c_str());
|
||||
|
||||
pcl::PCLPointCloud2::Ptr cloud(new pcl::PCLPointCloud2);
|
||||
pcl_conversions::toPCL(*pointCloud2Msg, *cloud);
|
||||
|
||||
cv_bridge::CvImage depthImage;
|
||||
|
||||
if(cloud->data.empty())
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "Received an empty cloud on topic \"%s\"! A depth image with all zeros is returned.", pointCloudSub_.getTopic().c_str());
|
||||
depthImage.image = cv::Mat::zeros(model.imageSize(), CV_32FC1);
|
||||
}
|
||||
else
|
||||
{
|
||||
depthImage.image = rtabmap::util3d::projectCloudToCamera(model.imageSize(), model.K(), cloud, model.localTransform());
|
||||
|
||||
if(fillHolesSize_ > 0 && fillIterations_ > 0)
|
||||
{
|
||||
for(int i=0; i<fillIterations_;++i)
|
||||
{
|
||||
depthImage.image = rtabmap::util2d::fillDepthHoles(depthImage.image, fillHolesSize_, fillHolesError_);
|
||||
}
|
||||
|
||||
if(pointCloudTransformedPub_->get_subscription_count()>0)
|
||||
{
|
||||
sensor_msgs::msg::PointCloud2 pointCloud2Out;
|
||||
rtabmap_conversions::transformPointCloud(model.localTransform().inverse().toEigen4f(), *pointCloud2Msg, pointCloud2Out);
|
||||
pointCloud2Out.header = cameraInfoMsg->header;
|
||||
pointCloudTransformedPub_->publish(pointCloud2Out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
depthImage.header = cameraInfoMsg->header;
|
||||
|
||||
if(decimation_>1 && upscale_)
|
||||
{
|
||||
depthImage.image = rtabmap::util2d::interpolate(depthImage.image, decimation_, upscaleDepthErrorRatio_);
|
||||
}
|
||||
|
||||
if(depthImage32Pub_.getNumSubscribers())
|
||||
{
|
||||
depthImage.encoding = sensor_msgs::image_encodings::TYPE_32FC1;
|
||||
depthImage32Pub_.publish(depthImage.toImageMsg());
|
||||
if(cameraInfo32Pub_->get_subscription_count())
|
||||
{
|
||||
cameraInfo32Pub_->publish(cameraInfoMsgOut);
|
||||
}
|
||||
}
|
||||
|
||||
if(depthImage16Pub_.getNumSubscribers())
|
||||
{
|
||||
depthImage.encoding = sensor_msgs::image_encodings::TYPE_16UC1;
|
||||
depthImage.image = rtabmap::util2d::cvtDepthFromFloat(depthImage.image);
|
||||
depthImage16Pub_.publish(depthImage.toImageMsg());
|
||||
if(cameraInfo16Pub_->get_subscription_count())
|
||||
{
|
||||
cameraInfo16Pub_->publish(cameraInfoMsgOut);
|
||||
}
|
||||
}
|
||||
|
||||
if( cloudStamp != rtabmap_conversions::timestampFromROS(pointCloud2Msg->header.stamp) ||
|
||||
infoStamp != rtabmap_conversions::timestampFromROS(cameraInfoMsg->header.stamp))
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Input stamps changed between the beginning and the end of the callback! Make "
|
||||
"sure the node publishing the topics doesn't override the same data after publishing them. A "
|
||||
"solution is to use this node within another nodelet manager. Stamps: "
|
||||
"cloud=%f->%f info=%f->%f",
|
||||
cloudStamp, rtabmap_conversions::timestampFromROS(pointCloud2Msg->header.stamp),
|
||||
infoStamp, rtabmap_conversions::timestampFromROS(cameraInfoMsg->header.stamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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_util::PointCloudToDepthImage)
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
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_util/rgbd_relay.hpp"
|
||||
|
||||
#include <sensor_msgs/msg/image.hpp>
|
||||
#include <sensor_msgs/msg/compressed_image.hpp>
|
||||
#include <sensor_msgs/msg/camera_info.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 <opencv2/highgui/highgui.hpp>
|
||||
|
||||
#include "rtabmap_conversions/MsgConversion.h"
|
||||
|
||||
#include "rtabmap/core/Compression.h"
|
||||
#include "rtabmap/utilite/UConversion.h"
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
RGBDRelay::RGBDRelay(const rclcpp::NodeOptions & options) :
|
||||
Node("rgbd_relay", options),
|
||||
compress_(false),
|
||||
uncompress_(false)
|
||||
{
|
||||
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
compress_ = this->declare_parameter("compress", compress_);
|
||||
uncompress_ = this->declare_parameter("uncompress", uncompress_);
|
||||
|
||||
rgbdImageSub_ = create_subscription<rtabmap_msgs::msg::RGBDImage>("rgbd_image", rclcpp::QoS(5).reliability((rmw_qos_reliability_policy_t)qos), std::bind(&RGBDRelay::callback, this, std::placeholders::_1));
|
||||
rgbdImagePub_ = create_publisher<rtabmap_msgs::msg::RGBDImage>("rgbd_image_relay", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos));
|
||||
}
|
||||
|
||||
void RGBDRelay::callback(const rtabmap_msgs::msg::RGBDImage::SharedPtr input) const
|
||||
{
|
||||
if(rgbdImagePub_->get_subscription_count())
|
||||
{
|
||||
if(!compress_ && !uncompress_)
|
||||
{
|
||||
//just republish it
|
||||
rgbdImagePub_->publish(*input);
|
||||
return;
|
||||
}
|
||||
|
||||
auto output = std::make_unique<rtabmap_msgs::msg::RGBDImage>();
|
||||
output->header = input->header;
|
||||
output->rgb_camera_info = input->rgb_camera_info;
|
||||
output->depth_camera_info = input->depth_camera_info;
|
||||
output->key_points = input->key_points;
|
||||
output->points = input->points;
|
||||
output->descriptors = input->descriptors;
|
||||
output->global_descriptor = input->global_descriptor;
|
||||
|
||||
rtabmap::StereoCameraModel stereoModel = rtabmap_conversions::stereoCameraModelFromROS(input->rgb_camera_info, input->depth_camera_info, rtabmap::Transform::getIdentity());
|
||||
|
||||
if(compress_)
|
||||
{
|
||||
if(!input->rgb_compressed.data.empty())
|
||||
{
|
||||
// already compressed, just copy pointer
|
||||
output->rgb_compressed = input->rgb_compressed;
|
||||
}
|
||||
else if(!input->rgb.data.empty())
|
||||
{
|
||||
cv_bridge::CvImageConstPtr rgb = cv_bridge::toCvShare(input->rgb, input);
|
||||
rgb->toCompressedImageMsg(output->rgb_compressed, cv_bridge::JPG);
|
||||
}
|
||||
|
||||
if(!input->depth_compressed.data.empty())
|
||||
{
|
||||
// already compressed, just copy pointer
|
||||
output->depth_compressed = input->depth_compressed;
|
||||
}
|
||||
else if(!input->depth.data.empty())
|
||||
{
|
||||
if(stereoModel.isValidForProjection())
|
||||
{
|
||||
// right stereo image
|
||||
cv_bridge::CvImageConstPtr imageRightPtr = cv_bridge::toCvShare(input->depth, input);
|
||||
imageRightPtr->toCompressedImageMsg(output->depth_compressed, cv_bridge::JPG);
|
||||
}
|
||||
else
|
||||
{
|
||||
// depth image
|
||||
cv_bridge::CvImageConstPtr imageDepthPtr = cv_bridge::toCvShare(input->depth, input);
|
||||
output->depth_compressed.data = rtabmap::compressImage(imageDepthPtr->image, ".png");
|
||||
output->depth_compressed.format = "png";
|
||||
}
|
||||
}
|
||||
}
|
||||
if(uncompress_)
|
||||
{
|
||||
if(!input->rgb.data.empty())
|
||||
{
|
||||
// already raw, just copy pointer
|
||||
output->rgb = input->rgb;
|
||||
}
|
||||
if(!input->rgb_compressed.data.empty())
|
||||
{
|
||||
cv_bridge::toCvCopy(input->rgb_compressed)->toImageMsg(output->rgb);
|
||||
}
|
||||
|
||||
if(!input->depth.data.empty())
|
||||
{
|
||||
// already raw, just copy pointer
|
||||
output->depth = input->depth;
|
||||
}
|
||||
else if(input->depth_compressed.format.compare("jpg")==0)
|
||||
{
|
||||
// right stereo image
|
||||
cv_bridge::toCvCopy(input->depth_compressed)->toImageMsg(output->depth);
|
||||
}
|
||||
else
|
||||
{
|
||||
// dpeth image
|
||||
auto cvImg = std::make_unique<cv_bridge::CvImage>();
|
||||
cvImg->header = input->depth_compressed.header;
|
||||
cvImg->image = rtabmap::uncompressImage(input->depth_compressed.data);
|
||||
UASSERT(cvImg->image.empty() || cvImg->image.type() == CV_32FC1 || cvImg->image.type() == CV_16UC1);
|
||||
cvImg->encoding = cvImg->image.empty()?"":cvImg->image.type() == CV_32FC1?sensor_msgs::image_encodings::TYPE_32FC1:sensor_msgs::image_encodings::TYPE_16UC1;
|
||||
cvImg->toImageMsg(output->depth);
|
||||
}
|
||||
}
|
||||
|
||||
rgbdImagePub_->publish(std::move(output));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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_util::RGBDRelay)
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright (c) 2010-2022, 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_util/rgbd_split.hpp>
|
||||
|
||||
#ifdef PRE_ROS_IRON
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#else
|
||||
#include <cv_bridge/cv_bridge.hpp>
|
||||
#endif
|
||||
|
||||
namespace rtabmap_util
|
||||
{
|
||||
|
||||
RGBDSplit::RGBDSplit(const rclcpp::NodeOptions & options) :
|
||||
Node("rgbd_split", options)
|
||||
{
|
||||
int qos = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
|
||||
qos = this->declare_parameter("qos", qos);
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "%s: qos = %d", get_name(), qos);
|
||||
|
||||
rgbdImageSub_ = create_subscription<rtabmap_msgs::msg::RGBDImage>("rgbd_image", rclcpp::QoS(5).reliability((rmw_qos_reliability_policy_t)qos), std::bind(&RGBDSplit::callback, this, std::placeholders::_1));
|
||||
|
||||
rgbPub_ = image_transport::create_camera_publisher(this, std::string(rgbdImageSub_->get_topic_name()) + "/rgb", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
depthPub_ = image_transport::create_camera_publisher(this, std::string(rgbdImageSub_->get_topic_name()) + "/depth", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos).get_rmw_qos_profile());
|
||||
}
|
||||
|
||||
|
||||
void RGBDSplit::callback(const rtabmap_msgs::msg::RGBDImage::SharedPtr input) const
|
||||
{
|
||||
if(rgbPub_.getNumSubscribers())
|
||||
{
|
||||
sensor_msgs::msg::Image outputImage;
|
||||
sensor_msgs::msg::CameraInfo outputCameraInfo;
|
||||
outputImage.header = outputCameraInfo.header = input->header;
|
||||
outputCameraInfo = input->rgb_camera_info;
|
||||
|
||||
if(!input->rgb.data.empty())
|
||||
{
|
||||
// already raw, just copy pointer
|
||||
outputImage = input->rgb;
|
||||
}
|
||||
else if(!input->rgb_compressed.data.empty())
|
||||
{
|
||||
#ifdef CV_BRIDGE_HYDRO
|
||||
ROS_ERROR("Unsupported compressed image copy, please upgrade at least to ROS Indigo to use this.");
|
||||
#else
|
||||
cv_bridge::toCvCopy(input->rgb_compressed)->toImageMsg(outputImage);
|
||||
#endif
|
||||
}
|
||||
rgbPub_.publish(outputImage, outputCameraInfo);
|
||||
}
|
||||
|
||||
if(depthPub_.getNumSubscribers())
|
||||
{
|
||||
sensor_msgs::msg::Image outputImage;
|
||||
sensor_msgs::msg::CameraInfo outputCameraInfo;
|
||||
outputCameraInfo = input->depth_camera_info;
|
||||
|
||||
if(!input->depth.data.empty())
|
||||
{
|
||||
// already raw, just copy pointer
|
||||
outputImage = input->depth;
|
||||
}
|
||||
else if(!input->depth_compressed.data.empty())
|
||||
{
|
||||
#ifdef CV_BRIDGE_HYDRO
|
||||
ROS_ERROR("Unsupported compressed image copy, please upgrade at least to ROS Indigo to use this.");
|
||||
#else
|
||||
cv_bridge::toCvCopy(input->depth_compressed)->toImageMsg(outputImage);
|
||||
#endif
|
||||
}
|
||||
outputImage.header = outputCameraInfo.header = input->header;
|
||||
depthPub_.publish(outputImage, outputCameraInfo);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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_util::RGBDSplit)
|
||||
|
||||
Reference in New Issue
Block a user