3 from message_filters
import ApproximateTimeSynchronizer, Subscriber
4 from sensor_msgs.msg
import PointCloud2, PointField, NavSatFix
5 from nav_msgs.msg
import Odometry
6 from cslam_common_interfaces.msg
import KeyframeOdom, KeyframePointCloud
7 from cslam_common_interfaces.msg
import LocalDescriptorsRequest, LocalPointCloudDescriptors, InterRobotLoopClosure, IntraRobotLoopClosure, LocalKeyframeMatch
8 from cslam_common_interfaces.msg
import VizPointCloud
9 import cslam.lidar_pr.icp_utils
as icp_utils
11 from rclpy.node
import Node
12 from rclpy.clock
import Clock
13 from rclpy.qos
import QoSProfile, ReliabilityPolicy, HistoryPolicy, DurabilityPolicy
14 from diagnostic_msgs.msg
import KeyValue
17 """Front-End data handler for lidar data
23 qos_profile = QoSProfile(
24 reliability=ReliabilityPolicy.BEST_EFFORT,
25 history=HistoryPolicy.KEEP_LAST,
29 tss = ApproximateTimeSynchronizer( [ Subscriber(self.
nodenode, PointCloud2, self.
paramsparams[
"frontend.pointcloud_topic"], qos_profile=qos_profile),
30 Subscriber(self.
nodenode, Odometry, self.
paramsparams[
"frontend.odom_topic"])], 100, self.
paramsparams[
"frontend.pointcloud_odom_approx_time_sync_s"] )
53 period_ms = self.
paramsparams[
"frontend.map_manager_process_period_ms"]
62 if self.
paramsparams[
"evaluation.enable_logs"]:
64 KeyValue,
'log_info', 100)
67 if self.
paramsparams[
"evaluation.enable_gps_recording"]:
73 """Sensor data callback
76 pc_msg (sensor_msgs/Pointcloud2): point cloud
77 odom_msg (nav_msgs/Odometry): odometry estimate
79 if (odom_msg.pose.covariance[0] > 1000):
80 self.
nodenode.get_logger().warn(
"Odom tracking failed, skipping frame")
83 if self.
paramsparams[
"evaluation.enable_gps_recording"]:
92 """Callback for local descriptors request
94 out_msg = LocalPointCloudDescriptors()
96 out_msg.keyframe_id = request.keyframe_id
97 out_msg.robot_id = self.
paramsparams[
"robot_id"]
98 out_msg.matches_robot_id = request.matches_robot_id
99 out_msg.matches_keyframe_id = request.matches_keyframe_id
102 if self.
paramsparams[
"evaluation.enable_logs"]:
107 """Callback for local descriptors from other robots
110 for i
in range(len(msg.matches_robot_id)):
111 if msg.matches_robot_id[i] == self.
paramsparams[
"robot_id"]:
112 frame_ids.append(msg.matches_keyframe_id[i])
113 for frame_id
in frame_ids:
116 out_msg = InterRobotLoopClosure()
117 out_msg.robot0_id = self.
paramsparams[
"robot_id"]
118 out_msg.robot0_keyframe_id = frame_id
119 out_msg.robot1_id = msg.robot_id
120 out_msg.robot1_keyframe_id = msg.keyframe_id
122 out_msg.success =
True
123 out_msg.transform = transform
125 out_msg.success =
False
129 """Callback for local keyframe match for intra-robot loop closures
134 out_msg = IntraRobotLoopClosure()
135 out_msg.keyframe0_id = msg.keyframe0_id
136 out_msg.keyframe1_id = msg.keyframe1_id
138 out_msg.success =
True
139 out_msg.transform = transform
141 out_msg.success =
False
145 """Compute the squared distance between two odometry messages
147 return (odom0.pose.pose.position.x - odom1.pose.pose.position.x)**2 + (odom0.pose.pose.position.y - odom1.pose.pose.position.y)**2 + (odom0.pose.pose.position.z - odom1.pose.pose.position.z)**2
150 """Check if we should generate a new keyframe
153 msg (nav_msgs/Odometry): odometry msg
156 bool: flag indicating if a new keyframe should be created.
162 if dist > self.
paramsparams[
"frontend.keyframe_generation_ratio_distance"]**2:
169 """ Process new sensor data
175 if self.
paramsparams[
"evaluation.enable_gps_recording"]:
181 except Exception
as e:
183 self.
nodenode.get_logger().info(
"Failure to downsample point cloud to voxel size {}. {}".format(self.
paramsparams[
"frontend.voxel_size"], e))
186 msg_pointcloud = KeyframePointCloud()
191 msg_odom = KeyframeOdom()
193 msg_odom.odom = data[1]
194 if self.
paramsparams[
"evaluation.enable_gps_recording"]:
197 if self.
paramsparams[
"visualization.enable"]:
198 viz_msg = VizPointCloud()
199 viz_msg.robot_id = self.
paramsparams[
"robot_id"]
201 viz_msg.pointcloud = msg_pointcloud.pointcloud
205 if __name__ ==
'__main__':
207 rclpy.init(args=
None)
208 node = Node(
"map_manager")
209 node.declare_parameters(
211 parameters=[(
'frontend.pointcloud_topic',
'pointcloud'),
212 (
'frontend.odom_topic',
'odom'),
213 (
'frontend.map_manager_process_period_ms', 100),
214 (
'frontend.voxel_size', 0.5),
215 (
'frontend.registration_min_inliers', 60),
216 (
'frontend.keyframe_generation_ratio_distance', 0.5),
217 (
'frontend.pointcloud_odom_approx_time_sync_s', 0.1),
219 (
'evaluation.enable_logs',
False),
220 (
'evaluation.enable_gps_recording',
False),
221 (
'evaluation.gps_topic',
""),
222 (
'evaluation.gps_topic',
""),
223 (
'visualization.enable',
False),
226 params[
'frontend.pointcloud_topic'] = node.get_parameter(
227 'frontend.pointcloud_topic').value
228 params[
'frontend.odom_topic'] = node.get_parameter(
229 'frontend.odom_topic').value
230 params[
'frontend.map_manager_process_period_ms'] = node.get_parameter(
231 'frontend.map_manager_process_period_ms').value
232 params[
'frontend.voxel_size'] = node.get_parameter(
233 'frontend.voxel_size').value
234 params[
'frontend.registration_min_inliers'] = node.get_parameter(
235 'frontend.registration_min_inliers').value
236 params[
'frontend.keyframe_generation_ratio_distance'] = node.get_parameter(
237 'frontend.keyframe_generation_ratio_distance').value
238 params[
'frontend.pointcloud_odom_approx_time_sync_s'] = node.get_parameter(
239 'frontend.pointcloud_odom_approx_time_sync_s').value
240 params[
'robot_id'] = node.get_parameter(
242 params[
"evaluation.enable_logs"] = node.get_parameter(
243 'evaluation.enable_logs').value
244 params[
"evaluation.enable_gps_recording"] = node.get_parameter(
245 'evaluation.enable_gps_recording').value
246 params[
"evaluation.gps_topic"] = node.get_parameter(
247 'evaluation.gps_topic').value
248 params[
"visualization.enable"] = node.get_parameter(
249 'visualization.enable').value
251 node.get_logger().info(
'Initialization done.')
def odom_distance_squared(self, odom0, odom1)
def gps_callback(self, gps_msg)
def generate_new_keyframe(self, msg)
log_local_descriptors_cumulative_communication
keyframe_pointcloud_publisher
pointcloud_descriptors_publisher
def receive_local_descriptors(self, msg)
inter_robot_loop_closure_publisher
send_local_descriptors_subscriber
def send_local_descriptors_request(self, request)
intra_robot_loop_closure_publisher
def __init__(self, node, params)
local_keyframe_match_subscriber
def receive_local_keyframe_match(self, msg)
def lidar_callback(self, pc_msg, odom_msg)
def process_new_sensor_data(self)
pointcloud_descriptors_subscriber
def downsample_ros_pointcloud(pc_msg, voxel_size)
def open3d_to_ros(open3d_cloud)
def compute_transform(src, dst, voxel_size, min_inliers)