mirror of
https://github.com/introlab/rtabmap_ros.git
synced 2026-10-04 08:47:45 +08:00
Added lidar_deskewing node and nodelet. Added "deskewing" option for icp_odometry. Updated velodyne and ouster examples with deskewing option. Added velodyne+T265 deskewing example.
This commit is contained in:
@@ -263,6 +263,7 @@ SET(rtabmap_plugins_lib_src
|
|||||||
src/nodelets/undistort_depth.cpp
|
src/nodelets/undistort_depth.cpp
|
||||||
src/nodelets/imu_to_tf.cpp
|
src/nodelets/imu_to_tf.cpp
|
||||||
src/nodelets/rgbdx_sync.cpp
|
src/nodelets/rgbdx_sync.cpp
|
||||||
|
src/nodelets/lidar_deskewing.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
IF(${cv_bridge_VERSION_MAJOR} GREATER 1 OR ${cv_bridge_VERSION_MINOR} GREATER 10)
|
IF(${cv_bridge_VERSION_MAJOR} GREATER 1 OR ${cv_bridge_VERSION_MINOR} GREATER 10)
|
||||||
@@ -386,6 +387,10 @@ add_executable(rtabmap_imu_to_tf src/ImuToTFNode.cpp)
|
|||||||
target_link_libraries(rtabmap_imu_to_tf ${Libraries})
|
target_link_libraries(rtabmap_imu_to_tf ${Libraries})
|
||||||
set_target_properties(rtabmap_imu_to_tf PROPERTIES OUTPUT_NAME "imu_to_tf")
|
set_target_properties(rtabmap_imu_to_tf PROPERTIES OUTPUT_NAME "imu_to_tf")
|
||||||
|
|
||||||
|
add_executable(rtabmap_lidar_deskewing src/LidarDeskewingNode.cpp)
|
||||||
|
target_link_libraries(rtabmap_lidar_deskewing ${Libraries})
|
||||||
|
set_target_properties(rtabmap_lidar_deskewing PROPERTIES OUTPUT_NAME "lidar_deskewing")
|
||||||
|
|
||||||
IF(NOT WIN32)
|
IF(NOT WIN32)
|
||||||
add_executable(rtabmap_wifi_signal_pub src/WifiSignalPubNode.cpp)
|
add_executable(rtabmap_wifi_signal_pub src/WifiSignalPubNode.cpp)
|
||||||
target_link_libraries(rtabmap_wifi_signal_pub rtabmap_ros)
|
target_link_libraries(rtabmap_wifi_signal_pub rtabmap_ros)
|
||||||
|
|||||||
@@ -264,6 +264,20 @@ bool convertScan3dMsg(
|
|||||||
int maxPoints = 0,
|
int maxPoints = 0,
|
||||||
float maxRange = 0.0f);
|
float maxRange = 0.0f);
|
||||||
|
|
||||||
|
bool deskew(
|
||||||
|
const sensor_msgs::PointCloud2 & input,
|
||||||
|
sensor_msgs::PointCloud2 & output,
|
||||||
|
const std::string & fixedFrameId,
|
||||||
|
tf::TransformListener & listener,
|
||||||
|
double waitForTransform,
|
||||||
|
bool slerp = false);
|
||||||
|
|
||||||
|
bool deskew(
|
||||||
|
const sensor_msgs::PointCloud2 & input,
|
||||||
|
sensor_msgs::PointCloud2 & output,
|
||||||
|
double previousStamp,
|
||||||
|
const rtabmap::Transform & velocity);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MSGCONVERSION_H_ */
|
#endif /* MSGCONVERSION_H_ */
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ public:
|
|||||||
|
|
||||||
const std::string & frameId() const {return frameId_;}
|
const std::string & frameId() const {return frameId_;}
|
||||||
const std::string & odomFrameId() const {return odomFrameId_;}
|
const std::string & odomFrameId() const {return odomFrameId_;}
|
||||||
|
const std::string & guessFrameId() const {return guessFrameId_;}
|
||||||
const rtabmap::ParametersMap & parameters() const {return parameters_;}
|
const rtabmap::ParametersMap & parameters() const {return parameters_;}
|
||||||
bool isPaused() const {return paused_;}
|
bool isPaused() const {return paused_;}
|
||||||
rtabmap::Transform getTransform(const std::string & fromFrameId, const std::string & toFrameId, const ros::Time & stamp) const;
|
rtabmap::Transform getTransform(const std::string & fromFrameId, const std::string & toFrameId, const ros::Time & stamp) const;
|
||||||
@@ -80,6 +81,9 @@ protected:
|
|||||||
|
|
||||||
virtual void flushCallbacks() = 0;
|
virtual void flushCallbacks() = 0;
|
||||||
tf::TransformListener & tfListener() {return tfListener_;}
|
tf::TransformListener & tfListener() {return tfListener_;}
|
||||||
|
double waitForTransformDuration() const {return waitForTransform_?waitForTransformDuration_:0.0;}
|
||||||
|
rtabmap::Transform velocityGuess() const;
|
||||||
|
double previousStamp() const {return previousStamp_;}
|
||||||
virtual void postProcessData(const rtabmap::SensorData & data, const std_msgs::Header & header) const {}
|
virtual void postProcessData(const rtabmap::SensorData & data, const std_msgs::Header & header) const {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
<remap from="odom_info" to="/rtabmap/odom_info"/>
|
<remap from="odom_info" to="/rtabmap/odom_info"/>
|
||||||
|
|
||||||
<param name="frame_id" type="string" value="base_footprint"/>
|
<param name="frame_id" type="string" value="base_footprint"/>
|
||||||
|
<param name="deskewing" type="string" value="true"/>
|
||||||
|
|
||||||
<param if="$(arg odom_guess)" name="odom_frame_id" type="string" value="icp_odom"/>
|
<param if="$(arg odom_guess)" name="odom_frame_id" type="string" value="icp_odom"/>
|
||||||
<param if="$(arg odom_guess)" name="guess_frame_id" type="string" value="odom"/>
|
<param if="$(arg odom_guess)" name="guess_frame_id" type="string" value="odom"/>
|
||||||
|
|||||||
+16
-3
@@ -96,8 +96,10 @@
|
|||||||
<arg name="scan_cloud_topic" default="/scan_cloud"/>
|
<arg name="scan_cloud_topic" default="/scan_cloud"/>
|
||||||
<arg name="subscribe_scan_descriptor" default="false"/>
|
<arg name="subscribe_scan_descriptor" default="false"/>
|
||||||
<arg name="scan_descriptor_topic" default="/scan_descriptor"/>
|
<arg name="scan_descriptor_topic" default="/scan_descriptor"/>
|
||||||
|
<arg name="scan_deskewing" default="false"/>
|
||||||
|
<arg name="scan_deskewing_slerp" default="false"/>
|
||||||
<arg name="scan_cloud_max_points" default="0"/>
|
<arg name="scan_cloud_max_points" default="0"/>
|
||||||
<arg name="scan_cloud_filtered" default="false"/> <!-- use filtered cloud from icp_odometry for mapping -->
|
<arg name="scan_cloud_filtered" default="$(arg scan_deskewing)"/> <!-- use filtered cloud from icp_odometry for mapping -->
|
||||||
<arg name="gen_scan" default="false"/> <!-- only works with depth image and if not subscribing to scan topic-->
|
<arg name="gen_scan" default="false"/> <!-- only works with depth image and if not subscribing to scan topic-->
|
||||||
|
|
||||||
<arg name="gen_depth" default="false" /> <!-- Generate depth image from scan_cloud -->
|
<arg name="gen_depth" default="false" /> <!-- Generate depth image from scan_cloud -->
|
||||||
@@ -312,6 +314,17 @@
|
|||||||
<param name="scan_cloud_max_points" type="int" value="$(arg scan_cloud_max_points)"/>
|
<param name="scan_cloud_max_points" type="int" value="$(arg scan_cloud_max_points)"/>
|
||||||
<param name="expected_update_rate" type="double" value="$(arg odom_expected_rate)"/>
|
<param name="expected_update_rate" type="double" value="$(arg odom_expected_rate)"/>
|
||||||
<param name="max_update_rate" type="double" value="$(arg odom_max_rate)"/>
|
<param name="max_update_rate" type="double" value="$(arg odom_max_rate)"/>
|
||||||
|
<param name="deskewing" type="bool" value="$(arg scan_deskewing)"/>
|
||||||
|
<param name="deskewing_slerp" type="bool" value="$(arg scan_deskewing_slerp)"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node if="$(eval not icp_odometry and scan_deskewing and subscribe_scan_cloud)" pkg="rtabmap_ros" type="lidar_deskewing" name="lidar_deskewing" clear_params="$(arg clear_params)" output="$(arg output)">
|
||||||
|
<param name="wait_for_transform" value="$(arg wait_for_transform)"/>
|
||||||
|
<param if="$(arg visual_odometry)" name="fixed_frame_id" value="$(arg vo_frame_id)"/>
|
||||||
|
<param unless="$(arg visual_odometry)" name="fixed_frame_id" value="$(arg odom_frame_id)"/>
|
||||||
|
<param name="slerp" value="$(arg scan_deskewing_slerp)"/>
|
||||||
|
<remap from="input_cloud" to="$(arg scan_cloud_topic)"/>
|
||||||
|
<remap from="$(arg scan_cloud_topic)/deskewed" to="odom_filtered_input_scan"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<node if="$(arg scan_cloud_assembling)" pkg="rtabmap_ros" type="point_cloud_assembler" name="point_cloud_assembler" clear_params="$(arg clear_params)" output="$(arg output)">
|
<node if="$(arg scan_cloud_assembling)" pkg="rtabmap_ros" type="point_cloud_assembler" name="point_cloud_assembler" clear_params="$(arg clear_params)" output="$(arg output)">
|
||||||
@@ -432,8 +445,8 @@
|
|||||||
<remap from="right/camera_info" to="$(arg right_camera_info_topic)"/>
|
<remap from="right/camera_info" to="$(arg right_camera_info_topic)"/>
|
||||||
|
|
||||||
<remap unless="$(arg icp_odometry)" from="scan" to="$(arg scan_topic)"/>
|
<remap unless="$(arg icp_odometry)" from="scan" to="$(arg scan_topic)"/>
|
||||||
<remap if="$(arg icp_odometry)" from="scan_cloud" to="odom_filtered_input_scan"/>
|
<remap if="$(eval icp_odometry or scan_cloud_filtered)" from="scan_cloud" to="odom_filtered_input_scan"/>
|
||||||
<remap unless="$(arg icp_odometry)" from="scan_cloud" to="$(arg scan_cloud_topic)"/>
|
<remap unless="$(eval icp_odometry or scan_cloud_filtered)" from="scan_cloud" to="$(arg scan_cloud_topic)"/>
|
||||||
<remap from="scan_descriptor" to="$(arg scan_descriptor_topic)"/>
|
<remap from="scan_descriptor" to="$(arg scan_descriptor_topic)"/>
|
||||||
<remap from="odom" to="$(arg odom_topic)"/>
|
<remap from="odom" to="$(arg odom_topic)"/>
|
||||||
</node>
|
</node>
|
||||||
|
|||||||
@@ -12,14 +12,15 @@
|
|||||||
coming from the first cloud sent by os1_cloud_node, which may be poorly synchronized with IMU data.
|
coming from the first cloud sent by os1_cloud_node, which may be poorly synchronized with IMU data.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- Required: -->
|
<arg name="use_sim_time" default="false"/>
|
||||||
<arg name="os1_hostname"/>
|
|
||||||
<arg name="os1_udp_dest"/>
|
|
||||||
|
|
||||||
<arg name="frame_id" default="os1_sensor"/>
|
<!-- Required: -->
|
||||||
|
<arg unless="$(arg use_sim_time)" name="os1_hostname"/>
|
||||||
|
<arg unless="$(arg use_sim_time)" name="os1_udp_dest"/>
|
||||||
|
|
||||||
|
<arg name="frame_id" default="os1_sensor"/>
|
||||||
<arg name="rtabmapviz" default="true"/>
|
<arg name="rtabmapviz" default="true"/>
|
||||||
<arg name="scan_20_hz" default="true"/>
|
<arg name="scan_20_hz" default="true"/>
|
||||||
<arg name="use_sim_time" default="false"/>
|
|
||||||
<param if="$(arg use_sim_time)" name="use_sim_time" value="true"/>
|
<param if="$(arg use_sim_time)" name="use_sim_time" value="true"/>
|
||||||
|
|
||||||
<!-- Ouster -->
|
<!-- Ouster -->
|
||||||
|
|||||||
@@ -40,11 +40,14 @@
|
|||||||
|
|
||||||
<arg name="frame_id" default="os_sensor"/>
|
<arg name="frame_id" default="os_sensor"/>
|
||||||
<arg name="rtabmapviz" default="true"/>
|
<arg name="rtabmapviz" default="true"/>
|
||||||
|
<arg name="deskewing" default="true"/>
|
||||||
|
<arg name="slerp" default="false"/>
|
||||||
<arg name="scan_20_hz" default="true"/>
|
<arg name="scan_20_hz" default="true"/>
|
||||||
<arg name="voxel_size" default="0.15"/> <!-- indoor: 0.1 to 0.3, outdoor: 0.3 to 0.5 -->
|
<arg name="voxel_size" default="0.15"/> <!-- indoor: 0.1 to 0.3, outdoor: 0.3 to 0.5 -->
|
||||||
<arg name="assemble" default="false"/>
|
<arg name="assemble" default="false"/>
|
||||||
<arg name="ptp" default="false"/> <!-- See comments in header to start before launching the launch -->
|
<arg name="ptp" default="false"/> <!-- See comments in header to start before launching the launch -->
|
||||||
<arg name="distortion_correction" default="false"/> <!-- Requires this pull request: https://github.com/ouster-lidar/ouster_example/pull/245 -->
|
<arg name="imu_topic" default="/os_cloud_node/imu"/>
|
||||||
|
<arg name="scan_topic" default="/os_cloud_node/points"/>
|
||||||
|
|
||||||
<param if="$(arg use_sim_time)" name="use_sim_time" value="true"/>
|
<param if="$(arg use_sim_time)" name="use_sim_time" value="true"/>
|
||||||
|
|
||||||
@@ -56,13 +59,12 @@
|
|||||||
<arg if="$(arg scan_20_hz)" name="lidar_mode" value="1024x20"/>
|
<arg if="$(arg scan_20_hz)" name="lidar_mode" value="1024x20"/>
|
||||||
<arg unless="$(arg scan_20_hz)" name="lidar_mode" value="1024x10"/>
|
<arg unless="$(arg scan_20_hz)" name="lidar_mode" value="1024x10"/>
|
||||||
<arg if="$(arg ptp)" name="timestamp_mode" value="TIME_FROM_PTP_1588"/>
|
<arg if="$(arg ptp)" name="timestamp_mode" value="TIME_FROM_PTP_1588"/>
|
||||||
<arg if="$(arg distortion_correction)" name="fixed_frame_id" value="$(arg frame_id)_stabilized"/>
|
|
||||||
</include>
|
</include>
|
||||||
|
|
||||||
<!-- IMU orientation estimation and publish tf accordingly to os_sensor frame -->
|
<!-- IMU orientation estimation and publish tf accordingly to os_sensor frame -->
|
||||||
<node pkg="nodelet" type="nodelet" name="imu_nodelet_manager" args="manager">
|
<node pkg="nodelet" type="nodelet" name="imu_nodelet_manager" args="manager">
|
||||||
<remap from="imu/data_raw" to="/os_cloud_node/imu"/>
|
<remap from="imu/data_raw" to="$(arg imu_topic)"/>
|
||||||
<remap from="imu/data" to="/os_cloud_node/imu/data"/>
|
<remap from="imu/data" to="$(arg imu_topic)/filtered"/>
|
||||||
</node>
|
</node>
|
||||||
<node pkg="nodelet" type="nodelet" name="imu_filter" args="load imu_filter_madgwick/ImuFilterNodelet imu_nodelet_manager">
|
<node pkg="nodelet" type="nodelet" name="imu_filter" args="load imu_filter_madgwick/ImuFilterNodelet imu_nodelet_manager">
|
||||||
<param name="use_mag" value="false"/>
|
<param name="use_mag" value="false"/>
|
||||||
@@ -70,15 +72,26 @@
|
|||||||
<param name="publish_tf" value="false"/>
|
<param name="publish_tf" value="false"/>
|
||||||
</node>
|
</node>
|
||||||
<node pkg="nodelet" type="nodelet" name="imu_to_tf" args="load rtabmap_ros/imu_to_tf imu_nodelet_manager">
|
<node pkg="nodelet" type="nodelet" name="imu_to_tf" args="load rtabmap_ros/imu_to_tf imu_nodelet_manager">
|
||||||
<remap from="imu/data" to="/os_cloud_node/imu/data"/>
|
<remap from="imu/data" to="$(arg imu_topic)/filtered"/>
|
||||||
<param name="fixed_frame_id" value="$(arg frame_id)_stabilized"/>
|
<param name="fixed_frame_id" value="$(arg frame_id)_stabilized"/>
|
||||||
<param name="base_frame_id" value="$(arg frame_id)"/>
|
<param name="base_frame_id" value="$(arg frame_id)"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
|
<!-- Lidar Deskewing -->
|
||||||
|
<node if="$(arg deskewing)" pkg="nodelet" type="nodelet" name="lidar_deskewing" args="standalone rtabmap_ros/lidar_deskewing" output="screen">
|
||||||
|
<param name="wait_for_transform" value="0.01"/>
|
||||||
|
<param name="fixed_frame_id" value="$(arg frame_id)_stabilized"/>
|
||||||
|
<param name="slerp" value="$(arg slerp)"/>
|
||||||
|
<remap from="input_cloud" to="$(arg scan_topic)"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<arg if="$(arg deskewing)" name="scan_topic_deskewed" default="$(arg scan_topic)/deskewed"/>
|
||||||
|
<arg unless="$(arg deskewing)" name="scan_topic_deskewed" default="$(arg scan_topic)"/>
|
||||||
|
|
||||||
<group ns="rtabmap">
|
<group ns="rtabmap">
|
||||||
<node pkg="rtabmap_ros" type="icp_odometry" name="icp_odometry" output="screen">
|
<node pkg="rtabmap_ros" type="icp_odometry" name="icp_odometry" output="screen">
|
||||||
<remap from="scan_cloud" to="/os_cloud_node/points"/>
|
<remap from="scan_cloud" to="$(arg scan_topic_deskewed)"/>
|
||||||
<remap from="imu" to="/os_cloud_node/imu/data"/>
|
<remap from="imu" to="$(arg imu_topic)/filtered"/>
|
||||||
<param name="frame_id" type="string" value="$(arg frame_id)"/>
|
<param name="frame_id" type="string" value="$(arg frame_id)"/>
|
||||||
<param name="odom_frame_id" type="string" value="odom"/>
|
<param name="odom_frame_id" type="string" value="odom"/>
|
||||||
<param if="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="25"/>
|
<param if="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="25"/>
|
||||||
@@ -116,8 +129,8 @@
|
|||||||
<param name="approx_sync" type="bool" value="false"/>
|
<param name="approx_sync" type="bool" value="false"/>
|
||||||
|
|
||||||
<remap if="$(arg assemble)" from="scan_cloud" to="assembled_cloud"/>
|
<remap if="$(arg assemble)" from="scan_cloud" to="assembled_cloud"/>
|
||||||
<remap unless="$(arg assemble)" from="scan_cloud" to="/os_cloud_node/points"/>
|
<remap unless="$(arg assemble)" from="scan_cloud" to="$(arg scan_topic_deskewed)"/>
|
||||||
<remap from="imu" to="/os_cloud_node/imu/data"/>
|
<remap from="imu" to="$(arg imu_topic)/filtered"/>
|
||||||
|
|
||||||
<!-- RTAB-Map's parameters -->
|
<!-- RTAB-Map's parameters -->
|
||||||
<param if="$(arg assemble)" name="Rtabmap/DetectionRate" type="string" value="0"/> <!-- already set 1 Hz in point_cloud_assembler -->
|
<param if="$(arg assemble)" name="Rtabmap/DetectionRate" type="string" value="0"/> <!-- already set 1 Hz in point_cloud_assembler -->
|
||||||
@@ -158,7 +171,7 @@
|
|||||||
</node>
|
</node>
|
||||||
|
|
||||||
<node if="$(arg assemble)" pkg="rtabmap_ros" type="point_cloud_assembler" name="point_cloud_assembler" output="screen">
|
<node if="$(arg assemble)" pkg="rtabmap_ros" type="point_cloud_assembler" name="point_cloud_assembler" output="screen">
|
||||||
<remap from="cloud" to="/os_cloud_node/points"/>
|
<remap from="cloud" to="$(arg scan_topic_deskewed)"/>
|
||||||
<remap from="odom" to="odom"/>
|
<remap from="odom" to="odom"/>
|
||||||
<param name="assembling_time" type="double" value="1" />
|
<param name="assembling_time" type="double" value="1" />
|
||||||
<param name="fixed_frame_id" type="string" value="" />
|
<param name="fixed_frame_id" type="string" value="" />
|
||||||
@@ -170,7 +183,7 @@
|
|||||||
<param name="subscribe_odom_info" type="bool" value="true"/>
|
<param name="subscribe_odom_info" type="bool" value="true"/>
|
||||||
<param name="subscribe_scan_cloud" type="bool" value="true"/>
|
<param name="subscribe_scan_cloud" type="bool" value="true"/>
|
||||||
<param name="approx_sync" type="bool" value="false"/>
|
<param name="approx_sync" type="bool" value="false"/>
|
||||||
<remap from="scan_cloud" to="/os_cloud_node/points"/>
|
<remap from="scan_cloud" to="odom_filtered_input_scan"/>
|
||||||
</node>
|
</node>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,9 @@
|
|||||||
<arg name="use_imu" default="false"/> <!-- Assuming IMU fixed to lidar with /velodyne -> /imu_link TF -->
|
<arg name="use_imu" default="false"/> <!-- Assuming IMU fixed to lidar with /velodyne -> /imu_link TF -->
|
||||||
<arg name="imu_topic" default="/imu/data"/>
|
<arg name="imu_topic" default="/imu/data"/>
|
||||||
<arg name="scan_20_hz" default="false"/> <!-- If we launch the velodyne with "rpm:=1200" argument -->
|
<arg name="scan_20_hz" default="false"/> <!-- If we launch the velodyne with "rpm:=1200" argument -->
|
||||||
<arg name="organize_cloud" default="false"/>
|
<arg name="deskewing" default="true"/>
|
||||||
|
<arg name="slerp" default="false"/> <!-- If true, a slerp between the first and last time will be used to deskew each point, which is faster than using tf for every point but less accurate -->
|
||||||
|
<arg name="organize_cloud" default="$(arg deskewing)"/> <!-- Should be organized if deskewing is enabled -->
|
||||||
<arg name="scan_topic" default="/velodyne_points"/>
|
<arg name="scan_topic" default="/velodyne_points"/>
|
||||||
<arg name="use_sim_time" default="false"/>
|
<arg name="use_sim_time" default="false"/>
|
||||||
<param if="$(arg use_sim_time)" name="use_sim_time" value="true"/>
|
<param if="$(arg use_sim_time)" name="use_sim_time" value="true"/>
|
||||||
@@ -24,7 +26,7 @@
|
|||||||
<arg name="queue_size_odom" default="1"/> <!-- Set to 100 for kitti dataset to make sure all scans are processed -->
|
<arg name="queue_size_odom" default="1"/> <!-- Set to 100 for kitti dataset to make sure all scans are processed -->
|
||||||
<arg name="loop_ratio" default="0.2"/>
|
<arg name="loop_ratio" default="0.2"/>
|
||||||
|
|
||||||
<arg name="resolution" default="0.1"/> <!-- set 0.1-0.3 for indoor, set 0.3-0.5 for outdoor (0.4 for kitti) -->
|
<arg name="resolution" default="0.05"/> <!-- set 0.05-0.3 for indoor, set 0.3-0.5 for outdoor (0.4 for kitti) -->
|
||||||
<arg name="iterations" default="10"/>
|
<arg name="iterations" default="10"/>
|
||||||
|
|
||||||
<!-- Grid parameters -->
|
<!-- Grid parameters -->
|
||||||
@@ -34,7 +36,7 @@
|
|||||||
<!-- For F2M Odometry -->
|
<!-- For F2M Odometry -->
|
||||||
<arg name="ground_normals_up" default="false"/> <!-- set to true when velodyne is always horizontal to ground (ground robot, car, kitti) -->
|
<arg name="ground_normals_up" default="false"/> <!-- set to true when velodyne is always horizontal to ground (ground robot, car, kitti) -->
|
||||||
<arg name="local_map_size" default="15000"/>
|
<arg name="local_map_size" default="15000"/>
|
||||||
<arg name="key_frame_thr" default="0.8"/>
|
<arg name="key_frame_thr" default="0.6"/>
|
||||||
|
|
||||||
<!-- For FLOAM Odometry -->
|
<!-- For FLOAM Odometry -->
|
||||||
<arg name="floam" default="false"/> <!-- RTAB-Map should be built with FLOAM http://official-rtab-map-forum.206.s1.nabble.com/icp-odometry-with-LOAM-crash-tp8261p8563.html -->
|
<arg name="floam" default="false"/> <!-- RTAB-Map should be built with FLOAM http://official-rtab-map-forum.206.s1.nabble.com/icp-odometry-with-LOAM-crash-tp8261p8563.html -->
|
||||||
@@ -46,9 +48,18 @@
|
|||||||
<arg name="organize_cloud" value="$(arg organize_cloud)"/>
|
<arg name="organize_cloud" value="$(arg organize_cloud)"/>
|
||||||
</include>
|
</include>
|
||||||
|
|
||||||
<!-- IMU orientation estimation and publish tf accordingly to os1_sensor frame -->
|
<!-- IMU orientation estimation and publish tf -->
|
||||||
<node if="$(arg use_imu)" pkg="rtabmap_ros" type="imu_to_tf" name="imu_to_tf">
|
<node if="$(arg use_imu)" pkg="nodelet" type="nodelet" name="imu_nodelet_manager" args="manager">
|
||||||
<remap from="imu/data" to="$(arg imu_topic)"/>
|
<remap from="imu/data_raw" to="$(arg imu_topic)"/>
|
||||||
|
<remap from="imu/data" to="$(arg imu_topic)/filtered"/>
|
||||||
|
</node>
|
||||||
|
<node if="$(arg use_imu)" pkg="nodelet" type="nodelet" name="imu_filter" args="load imu_filter_madgwick/ImuFilterNodelet imu_nodelet_manager">
|
||||||
|
<param name="use_mag" value="false"/>
|
||||||
|
<param name="world_frame" value="enu"/>
|
||||||
|
<param name="publish_tf" value="false"/>
|
||||||
|
</node>
|
||||||
|
<node if="$(arg use_imu)" pkg="nodelet" type="nodelet" name="imu_to_tf" args="load rtabmap_ros/imu_to_tf imu_nodelet_manager">
|
||||||
|
<remap from="imu/data" to="$(arg imu_topic)/filtered"/>
|
||||||
<param name="fixed_frame_id" value="$(arg frame_id)_stabilized"/>
|
<param name="fixed_frame_id" value="$(arg frame_id)_stabilized"/>
|
||||||
<param name="base_frame_id" value="$(arg frame_id)"/>
|
<param name="base_frame_id" value="$(arg frame_id)"/>
|
||||||
</node>
|
</node>
|
||||||
@@ -58,12 +69,14 @@
|
|||||||
<remap from="scan_cloud" to="$(arg scan_topic)"/>
|
<remap from="scan_cloud" to="$(arg scan_topic)"/>
|
||||||
<param name="frame_id" type="string" value="$(arg frame_id)"/>
|
<param name="frame_id" type="string" value="$(arg frame_id)"/>
|
||||||
<param name="odom_frame_id" type="string" value="odom"/>
|
<param name="odom_frame_id" type="string" value="odom"/>
|
||||||
|
<param name="deskewing" type="bool" value="$(arg deskewing)"/>
|
||||||
|
<param name="deskewing_slerp" type="bool" value="$(arg slerp)"/>
|
||||||
<param name="queue_size" type="int" value="$(arg queue_size_odom)"/>
|
<param name="queue_size" type="int" value="$(arg queue_size_odom)"/>
|
||||||
<param name="wait_for_transform_duration" type="double" value="0.2"/>
|
<param name="wait_for_transform_duration" type="double" value="0.2"/>
|
||||||
<param if="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="25"/>
|
<param if="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="25"/>
|
||||||
<param unless="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="15"/>
|
<param unless="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="15"/>
|
||||||
|
|
||||||
<remap if="$(arg use_imu)" from="imu" to="$(arg imu_topic)"/>
|
<remap if="$(arg use_imu)" from="imu" to="$(arg imu_topic)/filtered"/>
|
||||||
<param if="$(arg use_imu)" name="guess_frame_id" type="string" value="$(arg frame_id)_stabilized"/>
|
<param if="$(arg use_imu)" name="guess_frame_id" type="string" value="$(arg frame_id)_stabilized"/>
|
||||||
<param if="$(arg use_imu)" name="wait_imu_to_init" type="bool" value="true"/>
|
<param if="$(arg use_imu)" name="wait_imu_to_init" type="bool" value="true"/>
|
||||||
|
|
||||||
@@ -92,8 +105,9 @@
|
|||||||
<param name="OdomF2M/ScanMaxSize" type="string" value="$(arg local_map_size)"/>
|
<param name="OdomF2M/ScanMaxSize" type="string" value="$(arg local_map_size)"/>
|
||||||
<param name="OdomLOAM/Sensor" type="string" value="$(arg floam_sensor)"/>
|
<param name="OdomLOAM/Sensor" type="string" value="$(arg floam_sensor)"/>
|
||||||
<param name="OdomLOAM/Resolution" type="string" value="$(arg resolution)"/>
|
<param name="OdomLOAM/Resolution" type="string" value="$(arg resolution)"/>
|
||||||
<param if="$(arg scan_20_hz)" name="OdomLOAM/ScanPeriod" type="string" value="0.05"/>
|
<param if="$(eval not deskewing and scan_20_hz)" name="OdomLOAM/ScanPeriod" type="string" value="0.05"/>
|
||||||
<param unless="$(arg scan_20_hz)" name="OdomLOAM/ScanPeriod" type="string" value="0.1"/>
|
<param if="$(eval not deskewing and not scan_20_hz)" name="OdomLOAM/ScanPeriod" type="string" value="0.1"/>
|
||||||
|
<param if="$(arg deskewing)" name="OdomLOAM/ScanPeriod" type="string" value="0"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<node pkg="rtabmap_ros" type="rtabmap" name="rtabmap" output="screen" args="-d">
|
<node pkg="rtabmap_ros" type="rtabmap" name="rtabmap" output="screen" args="-d">
|
||||||
@@ -105,7 +119,7 @@
|
|||||||
<param name="wait_for_transform_duration" type="double" value="0.2"/>
|
<param name="wait_for_transform_duration" type="double" value="0.2"/>
|
||||||
|
|
||||||
<remap from="scan_cloud" to="assembled_cloud"/>
|
<remap from="scan_cloud" to="assembled_cloud"/>
|
||||||
<remap from="imu" to="$(arg imu_topic)"/>
|
<remap from="imu" to="$(arg imu_topic)/filtered"/>
|
||||||
|
|
||||||
<!-- RTAB-Map's parameters -->
|
<!-- RTAB-Map's parameters -->
|
||||||
<param name="Rtabmap/DetectionRate" type="string" value="1"/>
|
<param name="Rtabmap/DetectionRate" type="string" value="1"/>
|
||||||
@@ -127,7 +141,7 @@
|
|||||||
<param name="Optimizer/GravitySigma" type="string" value="0.3"/>
|
<param name="Optimizer/GravitySigma" type="string" value="0.3"/>
|
||||||
|
|
||||||
<!-- ICP parameters -->
|
<!-- ICP parameters -->
|
||||||
<param name="Icp/VoxelSize" type="string" value="0"/> <!-- already voxelized by point_cloud_assembler below -->
|
<param name="Icp/VoxelSize" type="string" value="$(arg resolution)"/>
|
||||||
<param name="Icp/PointToPlaneK" type="string" value="20"/>
|
<param name="Icp/PointToPlaneK" type="string" value="20"/>
|
||||||
<param name="Icp/PointToPlaneRadius" type="string" value="0"/>
|
<param name="Icp/PointToPlaneRadius" type="string" value="0"/>
|
||||||
<param name="Icp/PointToPlane" type="string" value="true"/>
|
<param name="Icp/PointToPlane" type="string" value="true"/>
|
||||||
@@ -151,12 +165,12 @@
|
|||||||
</node>
|
</node>
|
||||||
|
|
||||||
<node pkg="nodelet" type="nodelet" name="point_cloud_assembler" args="standalone rtabmap_ros/point_cloud_assembler" output="screen">
|
<node pkg="nodelet" type="nodelet" name="point_cloud_assembler" args="standalone rtabmap_ros/point_cloud_assembler" output="screen">
|
||||||
<remap from="cloud" to="$(arg scan_topic)"/>
|
<remap if="$(arg deskewing)" from="cloud" to="odom_filtered_input_scan"/>
|
||||||
|
<remap unless="$(arg deskewing)" from="cloud" to="$(arg scan_topic)"/>
|
||||||
<remap from="odom" to="odom"/>
|
<remap from="odom" to="odom"/>
|
||||||
<param if="$(arg scan_20_hz)" name="max_clouds" type="int" value="20" />
|
<param if="$(arg scan_20_hz)" name="max_clouds" type="int" value="20" />
|
||||||
<param unless="$(arg scan_20_hz)" name="max_clouds" type="int" value="10" />
|
<param unless="$(arg scan_20_hz)" name="max_clouds" type="int" value="10" />
|
||||||
<param name="fixed_frame_id" type="string" value="" />
|
<param name="fixed_frame_id" type="string" value="" />
|
||||||
<param name="voxel_size" type="double" value="$(arg resolution)" />
|
|
||||||
<param name="queue_size" type="int" value="$(arg queue_size)" />
|
<param name="queue_size" type="int" value="$(arg queue_size)" />
|
||||||
</node>
|
</node>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
<!-- -->
|
||||||
|
<launch>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Hand-held 3D lidar mapping example using only a Velodyne PUCK and external odometry (using t265 as example).
|
||||||
|
Prerequisities: rtabmap should be built with libpointmatcher
|
||||||
|
We use T265 only for lidar deskewing and icp_odometry guess in this example.
|
||||||
|
Example:
|
||||||
|
$ roslaunch rtabmap_ros test_velodyne_t265_deskewing.launch
|
||||||
|
$ rosrun rviz rviz -f map
|
||||||
|
$ Show TF and /rtabmap/cloud_map topics
|
||||||
|
-->
|
||||||
|
|
||||||
|
<arg name="rtabmapviz" default="true"/>
|
||||||
|
<arg name="scan_20_hz" default="false"/> <!-- If we launch the velodyne with "rpm:=1200" argument -->
|
||||||
|
<arg name="deskewing" default="true"/>
|
||||||
|
<arg name="slerp" default="false"/> <!-- If true, a slerp between the first and last time will be used to deskew each point, which is faster than using tf for every point but less accurate -->
|
||||||
|
<arg name="scan_topic" default="/velodyne_points"/>
|
||||||
|
<arg name="use_sim_time" default="false"/>
|
||||||
|
<param if="$(arg use_sim_time)" name="use_sim_time" value="true"/>
|
||||||
|
|
||||||
|
<arg name="odom_frame_id" default="t265_odom_frame"/> <!-- input odometry: here we use T265 odometry, but it could be wheel odometry -->
|
||||||
|
<arg name="frame_id" default="velodyne"/> <!-- base frame of the robot: for this example, we use velodyne as base frame -->
|
||||||
|
<arg name="queue_size" default="10"/>
|
||||||
|
<arg name="queue_size_odom" default="1"/>
|
||||||
|
<arg name="loop_ratio" default="0.2"/>
|
||||||
|
|
||||||
|
<arg name="resolution" default="0.05"/> <!-- set 0.05-0.3 for indoor, set 0.3-0.5 for outdoor -->
|
||||||
|
<arg name="iterations" default="10"/>
|
||||||
|
|
||||||
|
<!-- Grid parameters -->
|
||||||
|
<arg name="ground_is_obstacle" default="true"/>
|
||||||
|
<arg name="grid_max_range" default="20"/>
|
||||||
|
|
||||||
|
<!-- For F2M Odometry -->
|
||||||
|
<arg name="ground_normals_up" default="false"/> <!-- set to true when velodyne is always horizontal to ground (ground robot, car) -->
|
||||||
|
<arg name="local_map_size" default="15000"/>
|
||||||
|
<arg name="key_frame_thr" default="0.6"/>
|
||||||
|
|
||||||
|
<!-- For FLOAM Odometry -->
|
||||||
|
<arg name="floam" default="false"/> <!-- RTAB-Map should be built with FLOAM http://official-rtab-map-forum.206.s1.nabble.com/icp-odometry-with-LOAM-crash-tp8261p8563.html -->
|
||||||
|
<arg name="floam_sensor" default="0"/> <!-- 0=16 rings (VLP16), 1=32 rings, 2=64 rings -->
|
||||||
|
|
||||||
|
<!-- Static transform between velodyne and T265: TODO: Adjust with real position/orientation!!! -->
|
||||||
|
<node unless="$(arg use_sim_time)" pkg="tf" type="static_transform_publisher" name="T265_to_velodyne_tf" args="-0.01 0 0.055 0 0 0 t265_link velodyne 100"/>
|
||||||
|
|
||||||
|
<!-- Velodyne sensor VLP16 -->
|
||||||
|
<include unless="$(arg use_sim_time)" file="$(find velodyne_pointcloud)/launch/VLP16_points.launch">
|
||||||
|
<arg if="$(arg scan_20_hz)" name="rpm" value="1200"/>
|
||||||
|
<arg unless="$(arg scan_20_hz)" name="rpm" value="600"/>
|
||||||
|
<arg name="organize_cloud" value="true"/> <!-- should be organized for deskewing -->
|
||||||
|
</include>
|
||||||
|
|
||||||
|
<!-- T265 -->
|
||||||
|
<group unless="$(arg use_sim_time)" ns="t265">
|
||||||
|
<include file="$(find realsense2_camera)/launch/includes/nodelet.launch.xml">
|
||||||
|
<arg name="device_type" value="t265"/>
|
||||||
|
<arg name="serial_no" value=""/>
|
||||||
|
<arg name="tf_prefix" value="t265"/>
|
||||||
|
<arg name="initial_reset" value="false"/>
|
||||||
|
<arg name="enable_fisheye1" value="false"/>
|
||||||
|
<arg name="enable_fisheye2" value="false"/>
|
||||||
|
<arg name="topic_odom_in" value=""/>
|
||||||
|
<arg name="calib_odom_file" value=""/>
|
||||||
|
<arg name="enable_pose" value="true"/>
|
||||||
|
</include>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<!-- Lidar Deskewing -->
|
||||||
|
<node if="$(arg deskewing)" pkg="nodelet" type="nodelet" name="lidar_deskewing" args="standalone rtabmap_ros/lidar_deskewing" output="screen">
|
||||||
|
<param name="wait_for_transform" value="0.01"/>
|
||||||
|
<param name="fixed_frame_id" value="$(arg odom_frame_id)"/>
|
||||||
|
<param name="slerp" value="$(arg slerp)"/>
|
||||||
|
<remap from="input_cloud" to="$(arg scan_topic)"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<arg if="$(arg deskewing)" name="scan_topic_deskewed" default="$(arg scan_topic)/deskewed"/>
|
||||||
|
<arg unless="$(arg deskewing)" name="scan_topic_deskewed" default="$(arg scan_topic)"/>
|
||||||
|
|
||||||
|
<group ns="rtabmap">
|
||||||
|
<node pkg="rtabmap_ros" type="icp_odometry" name="icp_odometry" output="screen">
|
||||||
|
<remap from="scan_cloud" to="$(arg scan_topic_deskewed)"/>
|
||||||
|
<param name="guess_frame_id" type="string" value="$(arg odom_frame_id)"/>
|
||||||
|
<param name="frame_id" type="string" value="$(arg frame_id)"/>
|
||||||
|
<param name="odom_frame_id" type="string" value="odom"/>
|
||||||
|
<param name="queue_size" type="int" value="$(arg queue_size_odom)"/>
|
||||||
|
<param name="wait_for_transform_duration" type="double" value="0.2"/>
|
||||||
|
<param if="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="25"/>
|
||||||
|
<param unless="$(arg scan_20_hz)" name="expected_update_rate" type="double" value="15"/>
|
||||||
|
|
||||||
|
<!-- ICP parameters -->
|
||||||
|
<param name="Icp/PointToPlane" type="string" value="true"/>
|
||||||
|
<param name="Icp/Iterations" type="string" value="$(arg iterations)"/>
|
||||||
|
<param if="$(arg floam)" name="Icp/VoxelSize" type="string" value="0"/>
|
||||||
|
<param unless="$(arg floam)" name="Icp/VoxelSize" type="string" value="$(arg resolution)"/>
|
||||||
|
<param name="Icp/DownsamplingStep" type="string" value="1"/> <!-- cannot be increased with ring-like lidar -->
|
||||||
|
<param name="Icp/Epsilon" type="string" value="0.001"/>
|
||||||
|
<param if="$(arg floam)" name="Icp/PointToPlaneK" type="string" value="0"/>
|
||||||
|
<param unless="$(arg floam)" name="Icp/PointToPlaneK" type="string" value="20"/>
|
||||||
|
<param name="Icp/PointToPlaneRadius" type="string" value="0"/>
|
||||||
|
<param name="Icp/MaxTranslation" type="string" value="2"/>
|
||||||
|
<param name="Icp/MaxCorrespondenceDistance" type="string" value="$(eval resolution*10)"/>
|
||||||
|
<param name="Icp/PM" type="string" value="true"/>
|
||||||
|
<param name="Icp/PMOutlierRatio" type="string" value="0.7"/>
|
||||||
|
<param name="Icp/CorrespondenceRatio" type="string" value="0.01"/>
|
||||||
|
<param if="$(arg ground_normals_up)" name="Icp/PointToPlaneGroundNormalsUp" type="string" value="0.8"/>
|
||||||
|
|
||||||
|
<!-- Odom parameters -->
|
||||||
|
<param name="Odom/ScanKeyFrameThr" type="string" value="$(arg key_frame_thr)"/>
|
||||||
|
<param if="$(arg floam)" name="Odom/Strategy" type="string" value="11"/>
|
||||||
|
<param unless="$(arg floam)" name="Odom/Strategy" type="string" value="0"/>
|
||||||
|
<param name="OdomF2M/ScanSubtractRadius" type="string" value="$(arg resolution)"/>
|
||||||
|
<param name="OdomF2M/ScanMaxSize" type="string" value="$(arg local_map_size)"/>
|
||||||
|
<param name="OdomLOAM/Sensor" type="string" value="$(arg floam_sensor)"/>
|
||||||
|
<param name="OdomLOAM/Resolution" type="string" value="$(arg resolution)"/>
|
||||||
|
<param if="$(eval not deskewing and scan_20_hz)" name="OdomLOAM/ScanPeriod" type="string" value="0.05"/>
|
||||||
|
<param if="$(eval not deskewing and not scan_20_hz)" name="OdomLOAM/ScanPeriod" type="string" value="0.1"/>
|
||||||
|
<param if="$(arg deskewing)" name="OdomLOAM/ScanPeriod" type="string" value="0"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node pkg="rtabmap_ros" type="rtabmap" name="rtabmap" output="screen" args="-d">
|
||||||
|
<param name="frame_id" type="string" value="$(arg frame_id)"/>
|
||||||
|
<param name="subscribe_depth" type="bool" value="false"/>
|
||||||
|
<param name="subscribe_rgb" type="bool" value="false"/>
|
||||||
|
<param name="subscribe_scan_cloud" type="bool" value="true"/>
|
||||||
|
<param name="approx_sync" type="bool" value="false"/>
|
||||||
|
<param name="wait_for_transform_duration" type="double" value="0.2"/>
|
||||||
|
|
||||||
|
<remap from="scan_cloud" to="assembled_cloud"/>
|
||||||
|
|
||||||
|
<!-- RTAB-Map's parameters -->
|
||||||
|
<param name="Rtabmap/DetectionRate" type="string" value="1"/>
|
||||||
|
<param name="RGBD/NeighborLinkRefining" type="string" value="false"/>
|
||||||
|
<param name="RGBD/ProximityBySpace" type="string" value="true"/>
|
||||||
|
<param name="RGBD/ProximityMaxGraphDepth" type="string" value="0"/>
|
||||||
|
<param name="RGBD/ProximityPathMaxNeighbors" type="string" value="1"/>
|
||||||
|
<param name="RGBD/AngularUpdate" type="string" value="0.05"/>
|
||||||
|
<param name="RGBD/LinearUpdate" type="string" value="0.05"/>
|
||||||
|
<param name="Mem/NotLinkedNodesKept" type="string" value="false"/>
|
||||||
|
<param name="Mem/STMSize" type="string" value="30"/>
|
||||||
|
<param name="Mem/LaserScanNormalK" type="string" value="20"/>
|
||||||
|
|
||||||
|
<param name="Reg/Strategy" type="string" value="1"/>
|
||||||
|
<param name="Grid/CellSize" type="string" value="$(arg resolution)"/>
|
||||||
|
<param name="Grid/RangeMax" type="string" value="$(arg grid_max_range)"/>
|
||||||
|
<param name="Grid/ClusterRadius" type="string" value="1"/>
|
||||||
|
<param name="Grid/GroundIsObstacle" type="string" value="$(arg ground_is_obstacle)"/>
|
||||||
|
<param name="Optimizer/GravitySigma" type="string" value="0.3"/>
|
||||||
|
|
||||||
|
<!-- ICP parameters -->
|
||||||
|
<param name="Icp/VoxelSize" type="string" value="$(arg resolution)"/>
|
||||||
|
<param name="Icp/PointToPlaneK" type="string" value="20"/>
|
||||||
|
<param name="Icp/PointToPlaneRadius" type="string" value="0"/>
|
||||||
|
<param name="Icp/PointToPlane" type="string" value="true"/>
|
||||||
|
<param name="Icp/Iterations" type="string" value="$(arg iterations)"/>
|
||||||
|
<param name="Icp/Epsilon" type="string" value="0.001"/>
|
||||||
|
<param name="Icp/MaxTranslation" type="string" value="3"/>
|
||||||
|
<param name="Icp/MaxCorrespondenceDistance" type="string" value="$(eval resolution*10)"/>
|
||||||
|
<param name="Icp/PM" type="string" value="true"/>
|
||||||
|
<param name="Icp/PMOutlierRatio" type="string" value="0.7"/>
|
||||||
|
<param name="Icp/CorrespondenceRatio" type="string" value="$(arg loop_ratio)"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node if="$(arg rtabmapviz)" name="rtabmapviz" pkg="rtabmap_ros" type="rtabmapviz" output="screen">
|
||||||
|
<param name="frame_id" type="string" value="$(arg frame_id)"/>
|
||||||
|
<param name="odom_frame_id" type="string" value="odom"/>
|
||||||
|
<param name="subscribe_odom_info" type="bool" value="true"/>
|
||||||
|
<param name="subscribe_scan_cloud" type="bool" value="true"/>
|
||||||
|
<param name="approx_sync" type="bool" value="false"/>
|
||||||
|
<remap from="scan_cloud" to="odom_filtered_input_scan"/>
|
||||||
|
<remap from="odom_info" to="odom_info"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node pkg="nodelet" type="nodelet" name="point_cloud_assembler" args="standalone rtabmap_ros/point_cloud_assembler" output="screen">
|
||||||
|
<remap from="cloud" to="$(arg scan_topic_deskewed)"/>
|
||||||
|
<remap from="odom" to="odom"/>
|
||||||
|
<param if="$(arg scan_20_hz)" name="max_clouds" type="int" value="20" />
|
||||||
|
<param unless="$(arg scan_20_hz)" name="max_clouds" type="int" value="10" />
|
||||||
|
<param name="fixed_frame_id" type="string" value="" />
|
||||||
|
<param name="queue_size" type="int" value="$(arg queue_size)" />
|
||||||
|
</node>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
</launch>
|
||||||
@@ -176,6 +176,14 @@
|
|||||||
</description>
|
</description>
|
||||||
</class>
|
</class>
|
||||||
|
|
||||||
|
<class name="rtabmap_ros/lidar_deskewing"
|
||||||
|
type="rtabmap_ros::LidarDeskewing"
|
||||||
|
base_class_type="nodelet::Nodelet">
|
||||||
|
<description>
|
||||||
|
This is my nodelet.
|
||||||
|
</description>
|
||||||
|
</class>
|
||||||
|
|
||||||
</library>
|
</library>
|
||||||
|
|
||||||
<library path="lib/librtabmap_sync">
|
<library path="lib/librtabmap_sync">
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
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 "nodelet/loader.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
ros::init(argc, argv, "lidar_deskewing");
|
||||||
|
|
||||||
|
nodelet::V_string nargv;
|
||||||
|
for(int i=1;i<argc;++i)
|
||||||
|
{
|
||||||
|
nargv.push_back(argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
nodelet::Loader nodelet;
|
||||||
|
nodelet::M_string remap(ros::names::getRemappings());
|
||||||
|
std::string nodelet_name = ros::this_node::getName();
|
||||||
|
nodelet.load(nodelet_name, "rtabmap_ros/lidar_deskewing", remap, nargv);
|
||||||
|
ros::spin();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <rtabmap/core/Compression.h>
|
#include <rtabmap/core/Compression.h>
|
||||||
#include <rtabmap/utilite/UStl.h>
|
#include <rtabmap/utilite/UStl.h>
|
||||||
#include <rtabmap/utilite/ULogger.h>
|
#include <rtabmap/utilite/ULogger.h>
|
||||||
|
#include <rtabmap/utilite/UTimer.h>
|
||||||
#include <pcl_conversions/pcl_conversions.h>
|
#include <pcl_conversions/pcl_conversions.h>
|
||||||
#include <eigen_conversions/eigen_msg.h>
|
#include <eigen_conversions/eigen_msg.h>
|
||||||
#include <tf_conversions/tf_eigen.h>
|
#include <tf_conversions/tf_eigen.h>
|
||||||
@@ -2487,4 +2488,419 @@ bool convertScan3dMsg(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool deskew_impl(
|
||||||
|
const sensor_msgs::PointCloud2 & input,
|
||||||
|
sensor_msgs::PointCloud2 & output,
|
||||||
|
const std::string & fixedFrameId,
|
||||||
|
tf::TransformListener * listener,
|
||||||
|
double waitForTransform,
|
||||||
|
bool slerp,
|
||||||
|
const rtabmap::Transform & velocity,
|
||||||
|
double previousStamp)
|
||||||
|
{
|
||||||
|
if(listener != 0)
|
||||||
|
{
|
||||||
|
if(input.header.frame_id.empty())
|
||||||
|
{
|
||||||
|
ROS_ERROR("Input cloud has empty frame_id!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fixedFrameId.empty())
|
||||||
|
{
|
||||||
|
ROS_ERROR("fixedFrameId parameter should be set!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!slerp)
|
||||||
|
{
|
||||||
|
ROS_ERROR("slerp should be true when constant velocity model is used!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(previousStamp <= 0.0)
|
||||||
|
{
|
||||||
|
ROS_ERROR("previousStamp should be >0 when constant velocity model is used!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(velocity.isNull())
|
||||||
|
{
|
||||||
|
ROS_ERROR("velocity should be valid when constant velocity model is used!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int offsetTime = -1;
|
||||||
|
int offsetX = -1;
|
||||||
|
int offsetY = -1;
|
||||||
|
int offsetZ = -1;
|
||||||
|
int timeDatatype = 6;
|
||||||
|
for(size_t i=0; i<input.fields.size(); ++i)
|
||||||
|
{
|
||||||
|
if(input.fields[i].name.compare("t") == 0)
|
||||||
|
{
|
||||||
|
if(offsetTime != -1)
|
||||||
|
{
|
||||||
|
ROS_WARN("The input cloud should have only one of these fields: t, time or stamps. Overriding with %s.", input.fields[i].name.c_str());
|
||||||
|
}
|
||||||
|
offsetTime = input.fields[i].offset;
|
||||||
|
timeDatatype = input.fields[i].datatype;
|
||||||
|
}
|
||||||
|
else if(input.fields[i].name.compare("time") == 0)
|
||||||
|
{
|
||||||
|
if(offsetTime != -1)
|
||||||
|
{
|
||||||
|
ROS_WARN("The input cloud should have only one of these fields: t, time or stamps. Overriding with %s.", input.fields[i].name.c_str());
|
||||||
|
}
|
||||||
|
offsetTime = input.fields[i].offset;
|
||||||
|
timeDatatype = input.fields[i].datatype;
|
||||||
|
}
|
||||||
|
else if(input.fields[i].name.compare("stamps") == 0)
|
||||||
|
{
|
||||||
|
if(offsetTime != -1)
|
||||||
|
{
|
||||||
|
ROS_WARN("The input cloud should have only one of these fields: t, time or stamps. Overriding with %s.", input.fields[i].name.c_str());
|
||||||
|
}
|
||||||
|
offsetTime = input.fields[i].offset;
|
||||||
|
timeDatatype = input.fields[i].datatype;
|
||||||
|
}
|
||||||
|
else if(input.fields[i].name.compare("x") == 0)
|
||||||
|
{
|
||||||
|
ROS_ASSERT(input.fields[i].datatype==7);
|
||||||
|
offsetX = input.fields[i].offset;
|
||||||
|
}
|
||||||
|
else if(input.fields[i].name.compare("y") == 0)
|
||||||
|
{
|
||||||
|
ROS_ASSERT(input.fields[i].datatype==7);
|
||||||
|
offsetY = input.fields[i].offset;
|
||||||
|
}
|
||||||
|
else if(input.fields[i].name.compare("z") == 0)
|
||||||
|
{
|
||||||
|
ROS_ASSERT(input.fields[i].datatype==7);
|
||||||
|
offsetZ = input.fields[i].offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(offsetTime < 0)
|
||||||
|
{
|
||||||
|
ROS_ERROR("Input cloud doesn't have \"t\" or \"time\" field!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(offsetX < 0)
|
||||||
|
{
|
||||||
|
ROS_ERROR("Input cloud doesn't have \"x\" field!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(offsetY < 0)
|
||||||
|
{
|
||||||
|
ROS_ERROR("Input cloud doesn't have \"y\" field!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(offsetZ < 0)
|
||||||
|
{
|
||||||
|
ROS_ERROR("Input cloud doesn't have \"z\" field!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(input.height == 0)
|
||||||
|
{
|
||||||
|
ROS_ERROR("Input cloud height is zero!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(input.width == 0)
|
||||||
|
{
|
||||||
|
ROS_ERROR("Input cloud width is zero!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool timeOnColumns = input.width > input.height;
|
||||||
|
|
||||||
|
// Get latest timestamp
|
||||||
|
ros::Time firstStamp;
|
||||||
|
ros::Time lastStamp;
|
||||||
|
if(timeDatatype == 6) // UINT32
|
||||||
|
{
|
||||||
|
unsigned int nsec = *((const unsigned int*)(&input.data[0]+offsetTime));
|
||||||
|
firstStamp = input.header.stamp+ros::Duration(0, nsec);
|
||||||
|
nsec = *((const unsigned int*)(&input.data[timeOnColumns?(input.width-1)*input.point_step:(input.height-1)*input.row_step]+offsetTime));
|
||||||
|
lastStamp = input.header.stamp+ros::Duration(0, nsec);
|
||||||
|
}
|
||||||
|
else if(timeDatatype == 7) // FLOAT32
|
||||||
|
{
|
||||||
|
float sec = *((const float*)(&input.data[0]+offsetTime));
|
||||||
|
firstStamp = input.header.stamp+ros::Duration().fromSec(sec);
|
||||||
|
sec = *((const float*)(&input.data[timeOnColumns?(input.width-1)*input.point_step:(input.height-1)*input.row_step]+offsetTime));
|
||||||
|
lastStamp = input.header.stamp+ros::Duration().fromSec(sec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ROS_ERROR("Not supported time datatype %d!", timeDatatype);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lastStamp <= firstStamp)
|
||||||
|
{
|
||||||
|
ROS_ERROR("First and last stamps in the scan are the same!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string errorMsg;
|
||||||
|
if(listener != 0 &&
|
||||||
|
waitForTransform>0.0 &&
|
||||||
|
!listener->waitForTransform(
|
||||||
|
input.header.frame_id,
|
||||||
|
firstStamp,
|
||||||
|
input.header.frame_id,
|
||||||
|
lastStamp,
|
||||||
|
fixedFrameId,
|
||||||
|
ros::Duration(waitForTransform),
|
||||||
|
ros::Duration(0.01),
|
||||||
|
&errorMsg))
|
||||||
|
{
|
||||||
|
ROS_ERROR("Could not estimate motion of %s accordingly to fixed frame %s between stamps %f and %f! (%s)",
|
||||||
|
input.header.frame_id.c_str(),
|
||||||
|
fixedFrameId.c_str(),
|
||||||
|
firstStamp.toSec(),
|
||||||
|
lastStamp.toSec(),
|
||||||
|
errorMsg.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtabmap::Transform firstPose;
|
||||||
|
rtabmap::Transform lastPose;
|
||||||
|
double scanTime = 0;
|
||||||
|
if(slerp)
|
||||||
|
{
|
||||||
|
if(listener != 0)
|
||||||
|
{
|
||||||
|
firstPose = rtabmap_ros::getTransform(
|
||||||
|
input.header.frame_id,
|
||||||
|
fixedFrameId,
|
||||||
|
firstStamp,
|
||||||
|
input.header.stamp,
|
||||||
|
*listener,
|
||||||
|
0);
|
||||||
|
lastPose = rtabmap_ros::getTransform(
|
||||||
|
input.header.frame_id,
|
||||||
|
fixedFrameId,
|
||||||
|
lastStamp,
|
||||||
|
input.header.stamp,
|
||||||
|
*listener,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float vx,vy,vz, vroll,vpitch,vyaw;
|
||||||
|
velocity.getTranslationAndEulerAngles(vx,vy,vz, vroll,vpitch,vyaw);
|
||||||
|
|
||||||
|
// We need three poses:
|
||||||
|
// 1- The pose of base frame in odom frame at first stamp
|
||||||
|
// 2- The pose of base frame in odom frame at msg stamp
|
||||||
|
// 3- The pose of base frame in odom frame at last stamp
|
||||||
|
UASSERT(firstStamp.toSec() >= previousStamp);
|
||||||
|
UASSERT(lastStamp.toSec() > previousStamp);
|
||||||
|
double dt1 = firstStamp.toSec() - previousStamp;
|
||||||
|
double dt2 = input.header.stamp.toSec() - previousStamp;
|
||||||
|
double dt3 = lastStamp.toSec() - previousStamp;
|
||||||
|
|
||||||
|
rtabmap::Transform p1(vx*dt1, vy*dt1, vz*dt1, vroll*dt1, vpitch*dt1, vyaw*dt1);
|
||||||
|
rtabmap::Transform p2(vx*dt2, vy*dt2, vz*dt2, vroll*dt2, vpitch*dt2, vyaw*dt2);
|
||||||
|
rtabmap::Transform p3(vx*dt3, vy*dt3, vz*dt3, vroll*dt3, vpitch*dt3, vyaw*dt3);
|
||||||
|
|
||||||
|
// First and last poses are relative to stamp of the msg
|
||||||
|
firstPose = p2.inverse() * p1;
|
||||||
|
lastPose = p2.inverse() * p3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(firstPose.isNull())
|
||||||
|
{
|
||||||
|
ROS_ERROR("Could not get transform of %s accordingly to %s between stamps %f and %f!",
|
||||||
|
input.header.frame_id.c_str(),
|
||||||
|
fixedFrameId.empty()?"velocity":fixedFrameId.c_str(),
|
||||||
|
firstStamp.toSec(),
|
||||||
|
input.header.stamp.toSec());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(lastPose.isNull())
|
||||||
|
{
|
||||||
|
ROS_ERROR("Could not get transform of %s accordingly to %s between stamps %f and %f!",
|
||||||
|
input.header.frame_id.c_str(),
|
||||||
|
fixedFrameId.empty()?"velocity":fixedFrameId.c_str(),
|
||||||
|
lastStamp.toSec(),
|
||||||
|
input.header.stamp.toSec());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
scanTime = lastStamp.toSec() - firstStamp.toSec();
|
||||||
|
}
|
||||||
|
//else tf will be used to get more accurate transforms
|
||||||
|
|
||||||
|
output = input;
|
||||||
|
ros::Time stamp;
|
||||||
|
UTimer processingTime;
|
||||||
|
if(timeOnColumns)
|
||||||
|
{
|
||||||
|
// ouster point cloud:
|
||||||
|
// t1 t2 ...
|
||||||
|
// ring1 ring1 ...
|
||||||
|
// ring2 ring2 ...
|
||||||
|
// ring3 ring4 ...
|
||||||
|
// ring4 ring3 ...
|
||||||
|
for(size_t u=0; u<output.width; ++u)
|
||||||
|
{
|
||||||
|
if(timeDatatype == 6) // UINT32
|
||||||
|
{
|
||||||
|
unsigned int nsec = *((const unsigned int*)(&output.data[u*output.point_step]+offsetTime));
|
||||||
|
stamp = input.header.stamp+ros::Duration(0, nsec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float sec = *((const float*)(&output.data[u*output.point_step]+offsetTime));
|
||||||
|
stamp = input.header.stamp+ros::Duration().fromSec(sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
rtabmap::Transform transform;
|
||||||
|
if(slerp)
|
||||||
|
{
|
||||||
|
transform = firstPose.interpolate((stamp-firstStamp).toSec() / scanTime, lastPose);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
transform = rtabmap_ros::getTransform(
|
||||||
|
output.header.frame_id,
|
||||||
|
fixedFrameId,
|
||||||
|
stamp,
|
||||||
|
output.header.stamp,
|
||||||
|
*listener,
|
||||||
|
0);
|
||||||
|
if(transform.isNull())
|
||||||
|
{
|
||||||
|
ROS_ERROR("Could not get transform of %s accordingly to %s between stamps %f and %f!",
|
||||||
|
output.header.frame_id.c_str(),
|
||||||
|
fixedFrameId.c_str(),
|
||||||
|
stamp.toSec(),
|
||||||
|
output.header.stamp.toSec());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t v=0; v<input.height; ++v)
|
||||||
|
{
|
||||||
|
unsigned char * dataPtr = &output.data[v*output.row_step + u*output.point_step];
|
||||||
|
float & x = *((float*)(dataPtr+offsetX));
|
||||||
|
float & y = *((float*)(dataPtr+offsetY));
|
||||||
|
float & z = *((float*)(dataPtr+offsetZ));
|
||||||
|
pcl::PointXYZ pt(x,y,z);
|
||||||
|
pt = rtabmap::util3d::transformPoint(pt, transform);
|
||||||
|
x = pt.x;
|
||||||
|
y = pt.y;
|
||||||
|
z = pt.z;
|
||||||
|
|
||||||
|
// set delta stamp to zero so that on downstream they know the cloud is deskewed
|
||||||
|
if(timeDatatype == 6) // UINT32
|
||||||
|
{
|
||||||
|
*((unsigned int*)(dataPtr+offsetTime)) = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*((float*)(dataPtr+offsetTime)) = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // time on rows
|
||||||
|
{
|
||||||
|
// velodyne point cloud:
|
||||||
|
// t1 ring1 ring2 ring3 ring4
|
||||||
|
// t2 ring1 ring2 ring3 ring4
|
||||||
|
// t3 ring1 ring2 ring3 ring4
|
||||||
|
// t4 ring1 ring2 ring3 ring4
|
||||||
|
// ... ... ... ... ...
|
||||||
|
for(size_t v=0; v<output.height; ++v)
|
||||||
|
{
|
||||||
|
if(timeDatatype == 6) // UINT32
|
||||||
|
{
|
||||||
|
unsigned int nsec = *((const unsigned int*)(&output.data[v*output.row_step]+offsetTime));
|
||||||
|
stamp = input.header.stamp+ros::Duration(0, nsec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float sec = *((const float*)(&output.data[v*output.row_step]+offsetTime));
|
||||||
|
stamp = input.header.stamp+ros::Duration().fromSec(sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
rtabmap::Transform transform;
|
||||||
|
if(slerp)
|
||||||
|
{
|
||||||
|
transform = firstPose.interpolate((stamp-firstStamp).toSec() / scanTime, lastPose);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
transform = rtabmap_ros::getTransform(
|
||||||
|
output.header.frame_id,
|
||||||
|
fixedFrameId,
|
||||||
|
stamp,
|
||||||
|
output.header.stamp,
|
||||||
|
*listener,
|
||||||
|
0);
|
||||||
|
if(transform.isNull())
|
||||||
|
{
|
||||||
|
ROS_ERROR("Could not get transform of %s accordingly to %s between stamps %f and %f!",
|
||||||
|
output.header.frame_id.c_str(),
|
||||||
|
fixedFrameId.c_str(),
|
||||||
|
stamp.toSec(),
|
||||||
|
output.header.stamp.toSec());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t u=0; u<input.width; ++u)
|
||||||
|
{
|
||||||
|
unsigned char * dataPtr = &output.data[v*output.row_step + u*output.point_step];
|
||||||
|
float & x = *((float*)(dataPtr+offsetX));
|
||||||
|
float & y = *((float*)(dataPtr+offsetY));
|
||||||
|
float & z = *((float*)(dataPtr+offsetZ));
|
||||||
|
pcl::PointXYZ pt(x,y,z);
|
||||||
|
pt = rtabmap::util3d::transformPoint(pt, transform);
|
||||||
|
x = pt.x;
|
||||||
|
y = pt.y;
|
||||||
|
z = pt.z;
|
||||||
|
|
||||||
|
// set delta stamp to zero so that on downstream they know the cloud is deskewed
|
||||||
|
if(timeDatatype == 6) // UINT32
|
||||||
|
{
|
||||||
|
*((unsigned int*)(dataPtr+offsetTime)) = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*((float*)(dataPtr+offsetTime)) = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ROS_DEBUG("Lidar deskewing time=%fs", processingTime.elapsed());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deskew(
|
||||||
|
const sensor_msgs::PointCloud2 & input,
|
||||||
|
sensor_msgs::PointCloud2 & output,
|
||||||
|
const std::string & fixedFrameId,
|
||||||
|
tf::TransformListener & listener,
|
||||||
|
double waitForTransform,
|
||||||
|
bool slerp)
|
||||||
|
{
|
||||||
|
return deskew_impl(input, output, fixedFrameId, &listener, waitForTransform, slerp, rtabmap::Transform(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deskew(
|
||||||
|
const sensor_msgs::PointCloud2 & input,
|
||||||
|
sensor_msgs::PointCloud2 & output,
|
||||||
|
double previousStamp,
|
||||||
|
const rtabmap::Transform & velocity)
|
||||||
|
{
|
||||||
|
return deskew_impl(input, output, "", 0, 0, true, velocity, previousStamp);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -423,6 +423,15 @@ Transform OdometryROS::getTransform(const std::string & fromFrameId, const std::
|
|||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtabmap::Transform OdometryROS::velocityGuess() const
|
||||||
|
{
|
||||||
|
if(odometry_)
|
||||||
|
{
|
||||||
|
return odometry_->getVelocityGuess();
|
||||||
|
}
|
||||||
|
return rtabmap::Transform();
|
||||||
|
}
|
||||||
|
|
||||||
void OdometryROS::callbackIMU(const sensor_msgs::ImuConstPtr& msg)
|
void OdometryROS::callbackIMU(const sensor_msgs::ImuConstPtr& msg)
|
||||||
{
|
{
|
||||||
if(!this->isPaused())
|
if(!this->isPaused())
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <sensor_msgs/LaserScan.h>
|
#include <sensor_msgs/LaserScan.h>
|
||||||
#include <sensor_msgs/PointCloud2.h>
|
#include <sensor_msgs/PointCloud2.h>
|
||||||
#include <pcl_conversions/pcl_conversions.h>
|
#include <pcl_conversions/pcl_conversions.h>
|
||||||
|
#include <pcl_ros/transforms.h>
|
||||||
|
|
||||||
#include "rtabmap_ros/MsgConversion.h"
|
#include "rtabmap_ros/MsgConversion.h"
|
||||||
#include "rtabmap_ros/PluginInterface.h"
|
#include "rtabmap_ros/PluginInterface.h"
|
||||||
@@ -68,6 +69,8 @@ public:
|
|||||||
scanNormalK_(0),
|
scanNormalK_(0),
|
||||||
scanNormalRadius_(0.0),
|
scanNormalRadius_(0.0),
|
||||||
scanNormalGroundUp_(0.0),
|
scanNormalGroundUp_(0.0),
|
||||||
|
deskewing_(false),
|
||||||
|
deskewingSlerp_(false),
|
||||||
plugin_loader_("rtabmap_ros", "rtabmap_ros::PluginInterface"),
|
plugin_loader_("rtabmap_ros", "rtabmap_ros::PluginInterface"),
|
||||||
scanReceived_(false),
|
scanReceived_(false),
|
||||||
cloudReceived_(false)
|
cloudReceived_(false)
|
||||||
@@ -96,6 +99,8 @@ private:
|
|||||||
pnh.param("scan_normal_k", scanNormalK_, scanNormalK_);
|
pnh.param("scan_normal_k", scanNormalK_, scanNormalK_);
|
||||||
pnh.param("scan_normal_radius", scanNormalRadius_, scanNormalRadius_);
|
pnh.param("scan_normal_radius", scanNormalRadius_, scanNormalRadius_);
|
||||||
pnh.param("scan_normal_ground_up", scanNormalGroundUp_, scanNormalGroundUp_);
|
pnh.param("scan_normal_ground_up", scanNormalGroundUp_, scanNormalGroundUp_);
|
||||||
|
pnh.param("deskewing", deskewing_, deskewing_);
|
||||||
|
pnh.param("deskewing_slerp", deskewingSlerp_, deskewingSlerp_);
|
||||||
|
|
||||||
if (pnh.hasParam("plugins"))
|
if (pnh.hasParam("plugins"))
|
||||||
{
|
{
|
||||||
@@ -142,6 +147,8 @@ private:
|
|||||||
NODELET_INFO("IcpOdometry: scan_normal_k = %d", scanNormalK_);
|
NODELET_INFO("IcpOdometry: scan_normal_k = %d", scanNormalK_);
|
||||||
NODELET_INFO("IcpOdometry: scan_normal_radius = %f m", scanNormalRadius_);
|
NODELET_INFO("IcpOdometry: scan_normal_radius = %f m", scanNormalRadius_);
|
||||||
NODELET_INFO("IcpOdometry: scan_normal_ground_up = %f", scanNormalGroundUp_);
|
NODELET_INFO("IcpOdometry: scan_normal_ground_up = %f", scanNormalGroundUp_);
|
||||||
|
NODELET_INFO("IcpOdometry: deskewing = %s", deskewing_?"true":"false");
|
||||||
|
NODELET_INFO("IcpOdometry: deskewing_slerp = %s", deskewingSlerp_?"true":"false");
|
||||||
|
|
||||||
scan_sub_ = nh.subscribe("scan", queueSize, &ICPOdometry::callbackScan, this);
|
scan_sub_ = nh.subscribe("scan", queueSize, &ICPOdometry::callbackScan, this);
|
||||||
cloud_sub_ = nh.subscribe("scan_cloud", queueSize, &ICPOdometry::callbackCloud, this);
|
cloud_sub_ = nh.subscribe("scan_cloud", queueSize, &ICPOdometry::callbackCloud, this);
|
||||||
@@ -328,7 +335,7 @@ private:
|
|||||||
// make sure the frame of the laser is updated too
|
// make sure the frame of the laser is updated too
|
||||||
Transform localScanTransform = getTransform(this->frameId(),
|
Transform localScanTransform = getTransform(this->frameId(),
|
||||||
scanMsg->header.frame_id,
|
scanMsg->header.frame_id,
|
||||||
scanMsg->header.stamp + ros::Duration().fromSec(scanMsg->ranges.size()*scanMsg->time_increment));
|
scanMsg->header.stamp);
|
||||||
if(localScanTransform.isNull())
|
if(localScanTransform.isNull())
|
||||||
{
|
{
|
||||||
ROS_ERROR("TF of received laser scan topic at time %fs is not set, aborting odometry update.", scanMsg->header.stamp.toSec());
|
ROS_ERROR("TF of received laser scan topic at time %fs is not set, aborting odometry update.", scanMsg->header.stamp.toSec());
|
||||||
@@ -338,7 +345,35 @@ private:
|
|||||||
//transform in frameId_ frame
|
//transform in frameId_ frame
|
||||||
sensor_msgs::PointCloud2 scanOut;
|
sensor_msgs::PointCloud2 scanOut;
|
||||||
laser_geometry::LaserProjection projection;
|
laser_geometry::LaserProjection projection;
|
||||||
projection.transformLaserScanToPointCloud(scanMsg->header.frame_id, *scanMsg, scanOut, this->tfListener());
|
|
||||||
|
if(deskewing_ && !guessFrameId().empty())
|
||||||
|
{
|
||||||
|
projection.transformLaserScanToPointCloud(deskewing_&&!guessFrameId().empty()?guessFrameId():scanMsg->header.frame_id, *scanMsg, scanOut, this->tfListener());
|
||||||
|
|
||||||
|
sensor_msgs::PointCloud2 scanOutDeskewed;
|
||||||
|
if(!pcl_ros::transformPointCloud(scanMsg->header.frame_id, scanOut, scanOutDeskewed, this->tfListener()))
|
||||||
|
{
|
||||||
|
ROS_ERROR("Cannot transform back projected scan from \"%s\" frame to \"%s\" frame at time %fs.",
|
||||||
|
guessFrameId().c_str(), scanMsg->header.frame_id.c_str(), scanMsg->header.stamp.toSec());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scanOut = scanOutDeskewed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
projection.projectLaser(*scanMsg, scanOut, -1.0, laser_geometry::channel_option::Intensity | laser_geometry::channel_option::Timestamp);
|
||||||
|
|
||||||
|
if(previousStamp() > 0 && !velocityGuess().isNull())
|
||||||
|
{
|
||||||
|
// deskew with constant velocity model
|
||||||
|
sensor_msgs::PointCloud2 scanOutDeskewed;
|
||||||
|
if(!deskew(scanOut, scanOutDeskewed, previousStamp(), velocityGuess()))
|
||||||
|
{
|
||||||
|
ROS_ERROR("Failed to deskew input cloud, aborting odometry update!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool hasIntensity = false;
|
bool hasIntensity = false;
|
||||||
for(unsigned int i=0; i<scanOut.fields.size(); ++i)
|
for(unsigned int i=0; i<scanOut.fields.size(); ++i)
|
||||||
@@ -531,6 +566,28 @@ private:
|
|||||||
cloudMsg = *pointCloudMsg;
|
cloudMsg = *pointCloudMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(deskewing_)
|
||||||
|
{
|
||||||
|
if(!guessFrameId().empty())
|
||||||
|
{
|
||||||
|
// deskew with TF
|
||||||
|
if(!deskew(*pointCloudMsg, cloudMsg, guessFrameId(), tfListener(), waitForTransformDuration(), deskewingSlerp_))
|
||||||
|
{
|
||||||
|
ROS_ERROR("Failed to deskew input cloud, aborting odometry update!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(previousStamp() > 0 && !velocityGuess().isNull())
|
||||||
|
{
|
||||||
|
// deskew with constant velocity model
|
||||||
|
if(!deskew(*pointCloudMsg, cloudMsg, previousStamp(), velocityGuess()))
|
||||||
|
{
|
||||||
|
ROS_ERROR("Failed to deskew input cloud, aborting odometry update!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaserScan scan;
|
LaserScan scan;
|
||||||
bool hasNormals = false;
|
bool hasNormals = false;
|
||||||
bool hasIntensity = false;
|
bool hasIntensity = false;
|
||||||
@@ -760,6 +817,8 @@ private:
|
|||||||
int scanNormalK_;
|
int scanNormalK_;
|
||||||
double scanNormalRadius_;
|
double scanNormalRadius_;
|
||||||
double scanNormalGroundUp_;
|
double scanNormalGroundUp_;
|
||||||
|
bool deskewing_;
|
||||||
|
bool deskewingSlerp_;
|
||||||
std::vector<boost::shared_ptr<rtabmap_ros::PluginInterface> > plugins_;
|
std::vector<boost::shared_ptr<rtabmap_ros::PluginInterface> > plugins_;
|
||||||
pluginlib::ClassLoader<rtabmap_ros::PluginInterface> plugin_loader_;
|
pluginlib::ClassLoader<rtabmap_ros::PluginInterface> plugin_loader_;
|
||||||
bool scanReceived_ = false;
|
bool scanReceived_ = false;
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
|
||||||
|
#include <ros/ros.h>
|
||||||
|
#include <pluginlib/class_list_macros.h>
|
||||||
|
#include <nodelet/nodelet.h>
|
||||||
|
|
||||||
|
#include <tf/transform_listener.h>
|
||||||
|
|
||||||
|
#include <sensor_msgs/PointCloud2.h>
|
||||||
|
#include <sensor_msgs/LaserScan.h>
|
||||||
|
|
||||||
|
#include <laser_geometry/laser_geometry.h>
|
||||||
|
|
||||||
|
#include <pcl_ros/transforms.h>
|
||||||
|
|
||||||
|
#include <rtabmap/core/util3d_transforms.h>
|
||||||
|
#include <rtabmap/utilite/UTimer.h>
|
||||||
|
#include <rtabmap_ros/MsgConversion.h>
|
||||||
|
|
||||||
|
namespace rtabmap_ros
|
||||||
|
{
|
||||||
|
|
||||||
|
class LidarDeskewing : public nodelet::Nodelet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LidarDeskewing() :
|
||||||
|
waitForTransformDuration_(0.01),
|
||||||
|
slerp_(false),
|
||||||
|
tfListener_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~LidarDeskewing()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void onInit()
|
||||||
|
{
|
||||||
|
tfListener_ = new tf::TransformListener();
|
||||||
|
ros::NodeHandle & nh = getNodeHandle();
|
||||||
|
ros::NodeHandle & pnh = getPrivateNodeHandle();
|
||||||
|
|
||||||
|
pnh.param("fixed_frame_id", fixedFrameId_, fixedFrameId_);
|
||||||
|
pnh.param("wait_for_transform", waitForTransformDuration_, waitForTransformDuration_);
|
||||||
|
pnh.param("slerp", slerp_, slerp_);
|
||||||
|
|
||||||
|
NODELET_INFO("fixed_frame_id: %s", fixedFrameId_.c_str());
|
||||||
|
NODELET_INFO("wait_for_transform: %fs", waitForTransformDuration_);
|
||||||
|
NODELET_INFO("slerp: %s", slerp_?"true":"false");
|
||||||
|
|
||||||
|
if(fixedFrameId_.empty())
|
||||||
|
{
|
||||||
|
NODELET_FATAL("fixed_frame_id parameter cannot be empty!");
|
||||||
|
}
|
||||||
|
|
||||||
|
pubScan_ = nh.advertise<sensor_msgs::PointCloud2>(nh.resolveName("input_scan") + "/deskewed", 1);
|
||||||
|
pubCloud_ = nh.advertise<sensor_msgs::PointCloud2>(nh.resolveName("input_cloud") + "/deskewed", 1);
|
||||||
|
subScan_ = nh.subscribe("input_scan", 1, &LidarDeskewing::callbackScan, this);
|
||||||
|
subCloud_ = nh.subscribe("input_cloud", 1, &LidarDeskewing::callbackCloud, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callbackScan(const sensor_msgs::LaserScanConstPtr & msg)
|
||||||
|
{
|
||||||
|
sensor_msgs::PointCloud2 scanOut;
|
||||||
|
laser_geometry::LaserProjection projection;
|
||||||
|
projection.transformLaserScanToPointCloud(fixedFrameId_, *msg, scanOut, *tfListener_);
|
||||||
|
|
||||||
|
sensor_msgs::PointCloud2 scanOutDeskewed;
|
||||||
|
if(!pcl_ros::transformPointCloud(msg->header.frame_id, scanOut, scanOutDeskewed, *tfListener_))
|
||||||
|
{
|
||||||
|
ROS_ERROR("Cannot transform back projected scan from \"%s\" frame to \"%s\" frame at time %fs.",
|
||||||
|
fixedFrameId_.c_str(), msg->header.frame_id.c_str(), msg->header.stamp.toSec());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pubScan_.publish(scanOutDeskewed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callbackCloud(const sensor_msgs::PointCloud2ConstPtr & msg)
|
||||||
|
{
|
||||||
|
sensor_msgs::PointCloud2 msgDeskewed;
|
||||||
|
if(deskew(*msg, msgDeskewed, fixedFrameId_, *tfListener_, waitForTransformDuration_, slerp_))
|
||||||
|
{
|
||||||
|
pubCloud_.publish(msgDeskewed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Just republish the msg to not breakdown downstream
|
||||||
|
// A warning should be already shown (see deskew() source code)
|
||||||
|
ROS_WARN("deskewing failed! returning possible skewed cloud!");
|
||||||
|
pubCloud_.publish(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ros::Publisher pubScan_;
|
||||||
|
ros::Publisher pubCloud_;
|
||||||
|
ros::Subscriber subScan_;
|
||||||
|
ros::Subscriber subCloud_;
|
||||||
|
std::string fixedFrameId_;
|
||||||
|
double waitForTransformDuration_;
|
||||||
|
bool slerp_;
|
||||||
|
tf::TransformListener * tfListener_;
|
||||||
|
};
|
||||||
|
|
||||||
|
PLUGINLIB_EXPORT_CLASS(rtabmap_ros::LidarDeskewing, nodelet::Nodelet);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user