99
1010from kiss_slam .slam import KissSLAM
1111from kiss_slam .config import load_config
12+ from kiss_slam .occupancy_mapper import OccupancyGridMapper
13+ from map_closures import map_closures
1214
13- from kiss_slam_ros .conversions import pc2_to_numpy , build_transform , build_odometry , matrix_to_pose
15+ from kiss_slam_ros .conversions import pc2_to_numpy , build_transform , build_odometry , build_map , matrix_to_pose
1416from rcl_interfaces .msg import ParameterDescriptor , SetParametersResult
1517from rclpy .parameter import Parameter
1618from std_msgs .msg import Header
1719from tf2_ros import TransformBroadcaster
1820from sensor_msgs .msg import PointCloud2
19- from nav_msgs .msg import Odometry
21+ from nav_msgs .msg import Odometry , OccupancyGrid
2022
2123class KissSLAMNode (Node ):
2224
@@ -52,9 +54,15 @@ def __init__(self):
5254
5355 # Publishers
5456 self .odom_publisher = self .create_publisher (Odometry , 'odom' , 10 )
57+ self .map_publisher = self .create_publisher (OccupancyGrid , 'map' , 10 )
5558 self .tf_broadcaster = TransformBroadcaster (self )
5659
57- self .slam = KissSLAM (load_config (None ))
60+ self .slam_config = load_config (None )
61+ self .slam = KissSLAM (self .slam_config )
62+ self .mapper = OccupancyGridMapper (self .slam_config .occupancy_mapper )
63+ self .ref_ground_alignment = None
64+ self .min_voxel_idx = (0 , 0 )
65+
5866 self .add_on_set_parameters_callback (self .parameter_callback )
5967
6068 def parameter_callback (self , params : list ):
@@ -81,7 +89,8 @@ def cloud_callback(self, in_msg: PointCloud2):
8189 :param in_msg: New points to injest
8290 :type in_msg: PointCloud2
8391 """
84- self .slam .process_scan (pc2_to_numpy (in_msg ), np .empty ((0 ,)))
92+ pcd = pc2_to_numpy (in_msg )
93+ self .slam .process_scan (pcd , np .empty ((0 ,)))
8594
8695 pose = matrix_to_pose (self .slam .poses [- 1 ])
8796 header = Header ()
@@ -93,6 +102,31 @@ def cloud_callback(self, in_msg: PointCloud2):
93102
94103 odom = build_odometry (header , pose , self .position_covariance , self .orientation_covariance )
95104 self .odom_publisher .publish (odom )
105+
106+ self .update_mapper (pcd )
107+ map_msg = build_map (header , self .occupancy_2d , self .min_voxel_idx , self .slam_config .occupancy_mapper .resolution )
108+ self .map_publisher .publish (map_msg )
109+
110+ def update_mapper (self , pcd : np .ndarray ):
111+ """Use a point cloud to update the node's OccupancyGridMapper and store the new 2d grid.
112+ Also update the minimum active voxel xy idx so the map origin can be placed.
113+
114+ :param pcd: Latest point cloud received
115+ :type pcd: np.ndarray
116+ """
117+ if self .ref_ground_alignment is None :
118+ self .ref_ground_alignment = map_closures .align_map_to_local_ground (
119+ self .slam .voxel_grid .open3d_pcd_with_normals ().point .positions .cpu ().numpy (),
120+ self .slam_config .odometry .mapping .voxel_size ,
121+ )
122+
123+ self .mapper .integrate_frame (
124+ pcd , self .ref_ground_alignment @ self .slam .poses [- 1 ]
125+ )
126+ self .mapper .compute_3d_occupancy_information ()
127+ self .mapper .compute_2d_occupancy_information ()
128+ self .occupancy_2d = self .mapper .occupancy_grid
129+ self .min_voxel_idx = (self .mapper .lower_bound [0 ], self .mapper .lower_bound [1 ])
96130
97131
98132def main (args = None ):
@@ -102,9 +136,6 @@ def main(args=None):
102136
103137 rclpy .spin (kiss )
104138
105- # Destroy the node explicitly
106- # (optional - otherwise it will be done automatically
107- # when the garbage collector destroys the node object)
108139 kiss .destroy_node ()
109140 rclpy .shutdown ()
110141
0 commit comments