implement simultaneous localization and mapping (slam) with lidar scans -凯发k8网页登录
this example demonstrates how to implement the simultaneous localization and mapping (slam) algorithm on a collected series of lidar scans using pose graph optimization. the goal of this example is to build a map of the environment using the lidar scans and retrieve the trajectory of the robot.
to build the map of the environment, the slam algorithm incrementally processes the lidar scans and builds a pose graph that links these scans. the robot recognizes a previously-visited place through scan matching and may establish one or more loop closures along its moving path. the slam algorithm utilizes the loop closure information to update the map and adjust the estimated robot trajectory.
load laser scan data from file
load a down-sampled data set consisting of laser scans collected from a mobile robot in an indoor environment. the average displacement between every two scans is around 0.6 meters.
the offlineslamdata.mat
file contains the scans
variable, which contains all the laser scans used in this example.
load('offlineslamdata.mat');
a floor plan and approximate path of the robot are provided for illustrative purposes. this image shows the relative environment being mapped and the approximate trajectory of the robot.
run slam algorithm, construct optimized map and plot trajectory of the robot
create a object and set the map resolution and the max lidar range. this example uses a jackal™ robot from clearpath robotics™. the robot is equipped with a sick™ tim-511 laser scanner with a max range of 10 meters. set the max lidar range slightly smaller than the max scan range (8m), as the laser readings are less accurate near max range. set the grid map resolution to 20 cells per meter, which gives a 5cm precision.
maxlidarrange = 8; mapresolution = 20; slamalg = lidarslam(mapresolution, maxlidarrange);
the following loop closure parameters are set empirically. using higher loop closure threshold helps reject false positives in loop closure identification process. however, keep in mind that a high-score match may still be a bad match. for example, scans collected in an environment that has similar or repeated features are more likely to produce false positives. using a higher loop closure search radius allows the algorithm to search a wider range of the map around current pose estimate for loop closures.
slamalg.loopclosurethreshold = 210; slamalg.loopclosuresearchradius = 8;
observe the map building process with initial 10 scans
incrementally add scans to the slamalg
object. scan numbers are printed if added to the map. the object rejects scans if the distance between scans is too small. add the first 10 scans first to test your algorithm.
for i=1:10 [isscanaccepted, loopclosureinfo, optimizationinfo] = addscan(slamalg, scans{i}); if isscanaccepted fprintf('added scan %d \n', i); end end
added scan 1 added scan 2 added scan 3 added scan 4 added scan 5 added scan 6 added scan 7 added scan 8 added scan 9 added scan 10
reconstruct the scene by plotting the scans and poses tracked by the slamalg
.
figure; show(slamalg); title({'map of the environment','pose graph for initial 10 scans'});
observe the effect of loop closures and the optimization process
continue to add scans in a loop. loop closures should be automatically detected as the robot moves. pose graph optimization is performed whenever a loop closure is identified. the output optimizationinfo
has a field, isperformed
, that indicates when pose graph optimization occurs.
plot the scans and poses whenever a loop closure is identified and verify the results visually. this plot shows overlaid scans and an optimized pose graph for the first loop closure. a loop closure edge is added as a red link.
firsttimelcdetected = false; figure; for i=10:length(scans) [isscanaccepted, loopclosureinfo, optimizationinfo] = addscan(slamalg, scans{i}); if ~isscanaccepted continue; end % visualize the first detected loop closure, if you want to see the % complete map building process, remove the if condition below if optimizationinfo.isperformed && ~firsttimelcdetected show(slamalg, 'poses', 'off'); hold on; show(slamalg.posegraph); hold off; firsttimelcdetected = true; drawnow end end title('first loop closure');
visualize the constructed map and trajectory of the robot
plot the final built map after all scans are added to the slamalg
object. though the previous for
loop only plotted the initial closure, all the scans were added.
figure show(slamalg); title({'final built map of the environment', 'trajectory of the robot'});
visually inspect the built map compared to the original floor plan
an image of the scans and pose graph is overlaid on the original floorplan. you can see that the map matches the original floor plan well after adding all the scans and optimizing the pose graph.
build occupancy grid map
the optimized scans and poses can be used to generate a , which represents the environment as a probabilistic occupancy grid.
[scans, optimizedposes] = scansandposes(slamalg); map = buildmap(scans, optimizedposes, mapresolution, maxlidarrange);
visualize the occupancy grid map populated with the laser scans and the optimized pose graph.
figure; show(map); hold on show(slamalg.posegraph, 'ids', 'off'); hold off title('occupancy grid map built using lidar slam');