### Build and Install GroundGrid ROS Package Source: https://context7.com/dcmlr/groundgrid/llms.txt Instructions for cloning the GroundGrid repository into a catkin workspace, building it in Release mode, and sourcing the workspace. ```bash cd ~/catkin_ws/src git clone https://github.com/dcmlr/groundgrid.git catkin build -DCMAKE_BUILD_TYPE=Release groundgrid source ~/catkin_ws/devel/setup.bash ``` -------------------------------- ### Build GroundGrid with Catkin Source: https://github.com/dcmlr/groundgrid/blob/main/README.md Use this command to build the GroundGrid package with Catkin, specifying a Release build type. ```bash catkin build -DCMAKE_BUILD_TYPE=Release groundgrid ``` -------------------------------- ### Launch KITTI Playback Source: https://github.com/dcmlr/groundgrid/blob/main/README.md Launch the GroundGrid KITTI playback node to visualize segmentation results in RViz. Specify the dataset directory and sequence number. ```bash roslaunch groundgrid KITTIPlayback.launch directory:=/path/to/the/SemanticKITTI/dataset sequence:=0 ``` -------------------------------- ### Launch SemanticKITTI Playback Bash Snippet Source: https://context7.com/dcmlr/groundgrid/llms.txt Replays a SemanticKITTI odometry sequence, visualizing the segmented cloud and terrain map in RViz. Supports custom directory, sequence, playback rate, and pause options. ```bash # Play sequence 00 from the SemanticKITTI dataset roslaunch groundgrid KITTIPlayback.launch \ directory:=/data/SemanticKITTI \ sequence:=0 # Arguments # directory – path to the dataset root (must contain sequences//) # sequence – zero-padded sequence number (0–21) # rate – playback speed multiplier (default: 1.0) # paused – start paused (default: false) # RViz will open automatically showing: # /groundgrid/segmented_cloud – colour-coded ground/obstacle cloud # /groundgrid/grid_map – terrain height map ``` -------------------------------- ### Launch SemanticKITTI Evaluation Bash Snippet Source: https://context7.com/dcmlr/groundgrid/llms.txt Replays a SemanticKITTI sequence and quantitatively measures segmentation performance, printing per-class statistics and final results. ```bash roslaunch groundgrid KITTIEvaluate.launch \ directory:=/data/SemanticKITTI \ sequence:=0 # Example final output (sequence 00, 4540 clouds): # Precision 96.05% # Recall 98.70% # F1 97.35% # Accuracy 97.24% # IoUg 94.84% # # Per-class breakdown (nonground % | ground % | nonground count | total): # road 0.07% 99.93% 68465 95649669 # sidewalk 0.91% 99.09% 716154 78601664 # car 94.42% 5.58% 43078880 45622648 # building 97.33% 2.67% 117586234 120810401 # vegetation 93.43% 6.57% 121595505 130139604 ``` -------------------------------- ### GroundSegmentation::init Source: https://context7.com/dcmlr/groundgrid/llms.txt Precomputes an expected point density table as a function of range, which is used to determine if enough points have been observed for reliable ground estimation. This must be called once before any `filter_cloud` calls. ```APIDOC ## `GroundSegmentation::init` — Precompute Expected Point Density Table Builds a `cellCount × cellCount` Eigen matrix of expected LiDAR points per cell per ring as a function of range. This table is used later in `detect_ground_patch` to decide whether enough points were observed for reliable ground estimation. Must be called once before any `filter_cloud` calls. ### Method ```cpp void init(const ros::NodeHandle& nh, float dimension, float resolution) ``` ### Parameters * **nh** (const ros::NodeHandle&): ROS NodeHandle. * **dimension** (float): The side length of the map in meters. * **resolution** (float): The size of each cell in meters. ### Request Example ```cpp #include #include ros::NodeHandle nh; groundgrid::GroundSegmentation gs; const float resolution = 0.33f; // meters per cell (matches GroundGrid::mResolution) const float dimension = 120.0f; // map side length in meters (matches GroundGrid::mDimension) gs.init(nh, dimension, resolution); // Internally computes: expectedPoints(i,j) = atan(1/dist) / verticalPointAngDist // for each cell (i,j), where dist is the Euclidean distance from the map center. // Cells near the sensor origin have high expected counts; distant cells have low counts. // Eigen parallel processing is also enabled here (Eigen::initParallel()). ``` ``` -------------------------------- ### ROS Service: /kitti_player/NextCloud Python Snippet Source: https://context7.com/dcmlr/groundgrid/llms.txt Advances the SemanticKITTI playback by one frame using the NextCloud ROS service. Used for synchronous processing and evaluation. ```python import rospy from groundgrid.srv import NextCloud rospy.init_node('my_node') rospy.wait_for_service('/kitti_player/NextCloud') next_cloud = rospy.ServiceProxy('/kitti_player/NextCloud', NextCloud) # Advance one frame at a time for _ in range(10): try: next_cloud() # triggers sendCloud + sendPosition for the current frame rospy.sleep(0.1) except rospy.ServiceException as e: rospy.logerr("Service call failed: %s", e) ``` -------------------------------- ### Initialize GroundSegmentation with Expected Point Density Table Source: https://context7.com/dcmlr/groundgrid/llms.txt Precomputes a table of expected LiDAR points per cell per ring as a function of range. This table is crucial for reliable ground estimation in `detect_ground_patch`. Must be called once before any `filter_cloud` calls. Internally enables Eigen parallel processing. ```cpp #include #include ros::NodeHandle nh; groundgrid::GroundSegmentation gs; const float resolution = 0.33f; // meters per cell (matches GroundGrid::mResolution) const float dimension = 120.0f; // map side length in meters (matches GroundGrid::mDimension) gs.init(nh, dimension, resolution); // Internally computes: expectedPoints(i,j) = atan(1/dist) / verticalPointAngDist // for each cell (i,j), where dist is the Euclidean distance from the map center. // Cells near the sensor origin have high expected counts; distant cells have low counts. // Eigen parallel processing is also enabled here (Eigen::initParallel()). ``` -------------------------------- ### Launch GroundGrid Standalone Node Bash Snippet Source: https://context7.com/dcmlr/groundgrid/llms.txt Launches GroundGrid as a standalone ROS node. Can be configured to use a custom point cloud topic or run as a nodelet. ```bash # Default: subscribes to /sensors/velodyne_points roslaunch groundgrid GroundGrid.launch # Custom topic remapping roslaunch groundgrid GroundGrid.launch point_cloud_topic:=/lidar/points # As a nodelet (requires a running nodelet manager named core_nodelet_manager) USE_NODELETS=true roslaunch groundgrid GroundGrid.launch ``` -------------------------------- ### Initialize Sliding Grid Map with GroundGrid::initGroundGrid Source: https://context7.com/dcmlr/groundgrid/llms.txt Initializes the GroundGrid system with the first odometry message. This sets up the grid map centered on the vehicle's initial position and defines its layers. Ensure necessary headers are included. ```cpp #include #include // Simulate first odometry message nav_msgs::Odometry::Ptr odom(new nav_msgs::Odometry()); odom->header.frame_id = "map"; odom->header.stamp = ros::Time::now(); odom->pose.pose.position.x = 100.0; // easting odom->pose.pose.position.y = 200.0; // northing odom->pose.pose.position.z = 0.5; // vehicle height above ground groundgrid::GroundGrid gg; gg.initGroundGrid(odom); // Output: // [INFO] Created map with size 120.00 x 120.00 m (364 x 364 cells). // // Grid map layers initialized: // "points" → all zeros // "ground" → constant 0.5 (current vehicle z) // "groundpatch" → constant 1e-7 (near-zero confidence) // "minGroundHeight" → constant 100.0 // "maxGroundHeight" → constant -100.0 ``` -------------------------------- ### Apply Dynamic Reconfigure Parameters to GroundGrid and GroundSegmentation Source: https://context7.com/dcmlr/groundgrid/llms.txt Updates tunable algorithm parameters at runtime. Both GroundGrid and GroundSegmentation must be configured with the same config object. Adjusts segmentation thresholds, patch detection, and performance settings. ```cpp #include #include #include groundgrid::GroundGrid gg; groundgrid::GroundSegmentation gs; groundgrid::GroundGridConfig cfg; // Segmentation thresholds cfg.miminum_point_height_threshold = 0.3; // [m] points within 0.3 m of ground → ground cfg.minimum_point_height_obstacle_threshold = 0.1; // [m] hard floor for obstacle threshold cfg.outlier_tolerance = 0.1; // [m] tolerance for outlier detection // Patch detection cfg.distance_factor = 0.0001; // variance threshold scales with dist² cfg.minimum_distance_factor = 0.0005; cfg.patch_size_change_distance = 20.0; // [m] beyond this, use 5×5 patches cfg.ground_patch_detection_minimum_point_count_threshold = 0.25; // Performance cfg.thread_count = 8; cfg.max_ring = 64; // ignore Velodyne rings above this gg.setConfig(cfg); gs.setConfig(cfg); ``` -------------------------------- ### Launch KITTI Ground Segmentation Evaluation Source: https://github.com/dcmlr/groundgrid/blob/main/README.md Launch the GroundGrid KITTI evaluation node to assess ground segmentation performance. Specify the dataset directory and sequence number. Results are displayed periodically and upon termination. ```bash roslaunch groundgrid KITTIEvaluate.launch directory:=/path/to/the/SemanticKITTI/dataset sequence:=0 ``` -------------------------------- ### GroundGrid::initGroundGrid Source: https://context7.com/dcmlr/groundgrid/llms.txt Initializes the sliding grid map for GroundGrid. This function creates a `grid_map::GridMap` object centered at the vehicle's initial odometry position. It sets up five specific layers: `points`, `ground`, `groundpatch`, `minGroundHeight`, and `maxGroundHeight`. The initialization is triggered automatically upon receiving the first odometry message. ```APIDOC ## GroundGrid::initGroundGrid — Initialize the Sliding Grid Map Creates a 120 × 120 m `grid_map::GridMap` centered at the vehicle's first odometry position with five named layers: `points`, `ground`, `groundpatch`, `minGroundHeight`, `maxGroundHeight`. This is called automatically on the first odometry message received. ```cpp #include #include // Simulate first odometry message nav_msgs::Odometry::Ptr odom(new nav_msgs::Odometry()); odom->header.frame_id = "map"; odom->header.stamp = ros::Time::now(); odom->pose.pose.position.x = 100.0; // easting odom->pose.pose.position.y = 200.0; // northing odom->pose.pose.position.z = 0.5; // vehicle height above ground groundgrid::GroundGrid gg; gg.initGroundGrid(odom); // Output: // [INFO] Created map with size 120.00 x 120.00 m (364 x 364 cells). // // Grid map layers initialized: // "points" → all zeros // "ground" → constant 0.5 (current vehicle z) // "groundpatch" → constant 1e-7 (near-zero confidence) // "minGroundHeight" → constant 100.0 // "maxGroundHeight" → constant -100.0 ``` ``` -------------------------------- ### GroundGrid::setConfig Source: https://context7.com/dcmlr/groundgrid/llms.txt Updates all tunable algorithm parameters at runtime via the ROS dynamic reconfigure server. Both `GroundGrid` and `GroundSegmentation` must have `setConfig` called with the same config object. ```APIDOC ## `GroundGrid::setConfig` — Apply Dynamic Reconfigure Parameters Updates all tunable algorithm parameters at runtime via the ROS dynamic reconfigure server. Both `GroundGrid` and `GroundSegmentation` must have `setConfig` called with the same config object. ### Method ```cpp void setConfig(const groundgrid::GroundGridConfig& config) ``` ### Parameters * **config** (const groundgrid::GroundGridConfig&): A configuration object containing the tunable parameters. ### Request Example ```cpp #include #include #include groundgrid::GroundGrid gg; groundgrid::GroundSegmentation gs; groundgrid::GroundGridConfig cfg; // Segmentation thresholds cfg.miminum_point_height_threshold = 0.3; // [m] points within 0.3 m of ground → ground cfg.minimum_point_height_obstacle_threshold = 0.1; // [m] hard floor for obstacle threshold cfg.outlier_tolerance = 0.1; // [m] tolerance for outlier detection // Patch detection cfg.distance_factor = 0.0001; // variance threshold scales with dist² cfg.minimum_distance_factor = 0.0005; cfg.patch_size_change_distance = 20.0; // [m] beyond this, use 5×5 patches cfg.ground_patch_detection_minimum_point_count_threshold = 0.25; // Performance cfg.thread_count = 8; cfg.max_ring = 64; // ignore Velodyne rings above this gg.setConfig(cfg); gs.setConfig(cfg); ``` ``` -------------------------------- ### Update GroundGrid Parameters Programmatically Source: https://context7.com/dcmlr/groundgrid/llms.txt Use the dynamic_reconfigure client to update GroundGrid parameters. Ensure the client is initialized with the correct namespace and a suitable timeout. ```python from dynamic_reconfigure.client import Client client = Client('/groundgrid', timeout=5) client.update_configuration({ # Ground/obstacle height thresholds 'miminum_point_height_threshold': 0.3, # [m] max height above ground → ground 'minimum_point_height_obstacle_threshold': 0.1, # [m] hard minimum obstacle threshold 'outlier_tolerance': 0.1, # [m] tolerance for below-ground outliers # Patch detection geometry 'patch_size_change_distance': 20.0, # [m] 33 inside, 55 outside 'distance_factor': 0.0001, 'minimum_distance_factor': 0.0005, # Point count thresholds 'ground_patch_detection_minimum_point_count_threshold': 0.25, 'point_count_cell_variance_threshold': 10, # cells with ≥10 pts use own variance 'occupied_cells_point_count_factor': 20, # confidence = min(pts/factor, 1.0) 'occupied_cells_decrease_factor': 5.0, # confidence decay rate # Sensor / performance 'max_ring': 64, # Velodyne HDL-64E; use 128 for VLS-128 'thread_count': 8, # worker threads for parallel processing # Outlier detection 'min_outlier_detection_ground_confidence': 1.25, 'groundpatch_detection_minimum_threshold': 0.01, }) ``` -------------------------------- ### Multi-threaded Point Cloud Insertion into Grid Map Source: https://context7.com/dcmlr/groundgrid/llms.txt The `insert_cloud` function is used internally by `filter_cloud` for parallel point rasterization. Each thread processes a slice of the point cloud, accumulating statistics and identifying points to be re-added after segmentation. ```cpp // Called internally by filter_cloud via std::thread; shown here for clarity. // Each thread processes a different cloud slice for lock-free parallel insertion. std::vector> point_index; std::vector> ignored; std::vector outliers; // Thread 0 processes points [0, N/8) gs.insert_cloud(cloud, /*start=*/0, /*end=*/cloud->points.size()/8, cloudOrigin, point_index, ignored, outliers, map); // After all threads finish and results are merged: // point_index → valid points mapped to their grid cell indices // ignored → close-range or high-ring points to re-add post-segmentation // outliers → points classified as below-ground outliers (intensity 49) ``` -------------------------------- ### GroundSegmentation::insert_cloud Source: https://context7.com/dcmlr/groundgrid/llms.txt Inserts a contiguous slice of the point cloud into the grid map, accumulating running statistics. This function is designed for multi-threaded execution. ```APIDOC ## GroundSegmentation::insert_cloud ### Description Rasterizes points from a given slice of the point cloud into the grid map, updating cell statistics. Points too close or with high ring indices are marked as 'ignored' for later processing. Outlier detection is also performed. ### Method C++ Function Call (typically called internally by `filter_cloud` via `std::thread`) ### Parameters - **cloud** (*pcl::PointCloud::Ptr*) - The input point cloud. - **start** (*size_t*) - The starting index of the point cloud slice to process. - **end** (*size_t*) - The ending index (exclusive) of the point cloud slice to process. - **cloudOrigin** (*PCLPoint*) - The origin of the LiDAR sensor in the "map" frame. - **point_index** (*std::vector>&*) - Output vector to store indices of valid points mapped to grid cells. - **ignored** (*std::vector>&*) - Output vector to store indices of points marked as 'ignored'. - **outliers** (*std::vector&*) - Output vector to store indices of points classified as below-ground outliers. - **map** (*grid_map::GridMap&*) - The grid map object to insert points into. ``` -------------------------------- ### Update GroundGrid Map with Vehicle Motion Source: https://context7.com/dcmlr/groundgrid/llms.txt Moves the grid map to follow the vehicle and resets newly revealed cells. If the vehicle has not moved, the map is returned unchanged. Requires initialization before subsequent calls. ```cpp #include #include groundgrid::GroundGrid gg; nav_msgs::Odometry::Ptr odom_init(new nav_msgs::Odometry()); odom_init->pose.pose.position.x = 0.0; odom_init->pose.pose.position.y = 0.0; odom_init->pose.pose.position.z = 0.4; odom_init->header.stamp = ros::Time::now(); // First call initializes the map std::shared_ptr map = gg.update(odom_init); // Subsequent call with new position shifts the map nav_msgs::Odometry::Ptr odom_new(new nav_msgs::Odometry()); odom_new->pose.pose.position.x = 5.0; // moved 5 m east odom_new->pose.pose.position.y = 0.0; odom_new->pose.pose.position.z = 0.42; odom_new->header.stamp = ros::Time::now(); map = gg.update(odom_new); // Cells that entered the map from the east are reset using the TF-derived // ground height. Cells that left the map (west edge) are discarded. // map->getPosition() now returns (5.0, 0.0) ``` -------------------------------- ### GroundSegmentation::spiral_ground_interpolation C++ Snippet Source: https://context7.com/dcmlr/groundgrid/llms.txt Internal function for ground height interpolation. Blends observed height with neighbor averages using confidence weights. Confidence decreases over time, causing unobserved cells to defer to neighbors. ```cpp // Called internally by filter_cloud after detect_ground_patches. geometry_msgs::TransformStamped toBase; // TF: "base_link" frame → map gs.spiral_ground_interpolation(map, toBase); // Effect on map layers: // Center cell (vehicle position): // map["groundpatch"](center,center) = 1.0 (full confidence) // map["ground"](center,center) = vehicle_z from TF // // Each outer cell: // new_height = (1 - confidence) * neighbour_avg + confidence * old_height // confidence -= confidence / occupied_cells_decrease_factor (default: /5) // // Cells with no observed points (confidence ≈ 0) fully inherit neighbour heights, effectively extrapolating the terrain across occluded or sparse regions. ``` -------------------------------- ### GroundGrid Citation Source: https://github.com/dcmlr/groundgrid/blob/main/README.md Bibliographic information for citing the GroundGrid article in IEEE Robotics and Automation Letters. ```bibtex @article{steinke2024groundgrid, author={Steinke, Nicolai and Goehring, Daniel and Rojas, Raúl}, journal={IEEE Robotics and Automation Letters}, title={GroundGrid: LiDAR Point Cloud Ground Segmentation and Terrain Estimation}, year={2024}, volume={9}, number={1}, pages={420-426}, keywords={Sensors;Point cloud compression;Estimation;Laser radar;Image segmentation;Task analysis;Robot sensing systems;Range Sensing;Mapping;Field Robots}, doi={10.1109/LRA.2023.3333233}} ``` -------------------------------- ### GroundSegmentation::filter_cloud Source: https://context7.com/dcmlr/groundgrid/llms.txt The main entry point for per-scan processing. It rasterizes the incoming Velodyne cloud into the grid map, runs parallel ground patch detection, performs spiral interpolation, and then classifies every point. Returns a new point cloud with the same points but with `intensity` overwritten: `49` for ground and `99` for non-ground/obstacle. ```APIDOC ## GroundSegmentation::filter_cloud ### Description Segments ground from obstacles in a point cloud. This is the primary function for processing individual scans. ### Method C++ Function Call ### Parameters - **cloud** (*pcl::PointCloud::Ptr*) - Input point cloud, assumed to be transformed into the "map" frame. - **cloudOrigin** (*PCLPoint*) - The origin of the LiDAR sensor in the "map" frame. - **mapToBase** (*geometry_msgs::TransformStamped*) - Transformation from the "map" frame to the "base_link" frame. - **map_ptr** (*grid_map::GridMap::Ptr*) - Pointer to the grid map object. ### Return Value - (*pcl::PointCloud::Ptr*) - A new point cloud with the same points as the input, but with `intensity` values updated to `49` (ground) or `99` (non-ground/obstacle). ``` -------------------------------- ### GroundSegmentation::detect_ground_patches Source: https://context7.com/dcmlr/groundgrid/llms.txt Performs parallel ground patch detection across quadrants of the grid map. It updates the ground height and confidence layers. ```APIDOC ## GroundSegmentation::detect_ground_patches ### Description Detects ground patches in parallel across different sections (quadrants) of the grid map. It updates the `ground` height and `groundpatch` confidence layers based on neighborhood analysis. ### Method C++ Function Call (typically called internally by `filter_cloud` via `std::thread`) ### Parameters - **map** (*grid_map::GridMap&*) - The grid map object containing height and variance data. - **section** (*unsigned short*) - The specific section (quadrant) of the map to process (0-3). ``` -------------------------------- ### Filter LiDAR Cloud for Ground vs. Obstacles Source: https://context7.com/dcmlr/groundgrid/llms.txt Use `filter_cloud` to process a Velodyne point cloud, classifying points as ground (intensity 49) or non-ground (intensity 99). Ensure the input cloud is in the 'map' frame and provide the sensor's origin and TF transform. ```cpp #include #include #include typedef velodyne_pointcloud::PointXYZIR PCLPoint; groundgrid::GroundSegmentation gs; // ... gs.init(...) and gs.setConfig(...) called previously ... // Input cloud already transformed to the "map" frame pcl::PointCloud::Ptr cloud(new pcl::PointCloud); // ... populate cloud from sensor_msgs::PointCloud2 via pcl::fromROSMsg ... // Origin of the LiDAR sensor in map frame PCLPoint cloudOrigin; cloudOrigin.x = 5.1f; cloudOrigin.y = 0.2f; cloudOrigin.z = 1.73f; geometry_msgs::TransformStamped mapToBase; // from TF: "map" → "base_link" pcl::PointCloud::Ptr segmented = gs.filter_cloud(cloud, cloudOrigin, mapToBase, *map_ptr); // Inspect classification int ground_count = 0, obstacle_count = 0; for (const auto& pt : segmented->points) { if (pt.intensity == 49) ++ground_count; if (pt.intensity == 99) ++obstacle_count; } // Example output for a typical urban scan: // ground_count ≈ 180000 // obstacle_count ≈ 40000 ``` -------------------------------- ### Access GridMap Layers in C++ Source: https://context7.com/dcmlr/groundgrid/llms.txt Convert a ROS GridMap message to a GridMap object to access its layers. Use `isInside` to check if a position is within the map boundaries before accessing layer data. ```cpp // Access a layer from a received GridMap message #include void gridMapCallback(const grid_map_msgs::GridMap& msg) { grid_map::GridMap map; grid_map::GridMapRosConverter::fromMessage(msg, map); grid_map::Position pos(3.0, 1.5); // query at (x=3, y=1.5) in map frame if (map.isInside(pos)) { float ground_height = map.atPosition("ground", pos); // e.g. 0.12 m float confidence = map.atPosition("groundpatch", pos); // e.g. 0.87 float obstacle_count = map.atPosition("points", pos); // e.g. 3.0 } } ``` -------------------------------- ### Parallel Ground Patch Detection in Grid Map Source: https://context7.com/dcmlr/groundgrid/llms.txt The `detect_ground_patches` function is called internally by `filter_cloud` to perform parallel ground classification across four quadrants of the grid map. It updates the 'ground' height and 'groundpatch' confidence layers. ```cpp // Called internally by filter_cloud; four threads launched simultaneously: std::vector threads; for (unsigned short section = 0; section < 4; ++section) threads.push_back(std::thread( &groundgrid::GroundSegmentation::detect_ground_patches, &gs, std::ref(map), section)); for (auto& t : threads) t.join(); // Section mapping: // section 0 → top-left quadrant // section 1 → top-right quadrant // section 2 → bottom-left quadrant // section 3 → bottom-right quadrant // // After this step: // map["ground"] → estimated ground height per cell // map["groundpatch"] → confidence [0, 1] per cell // map["variance"] → per-cell point height variance ``` -------------------------------- ### GroundGrid::update Source: https://context7.com/dcmlr/groundgrid/llms.txt Moves the grid map to follow the vehicle, resets newly revealed cells to the interpolated ground height, and returns the updated map pointer. If the vehicle has not moved, the map is returned unchanged. ```APIDOC ## `GroundGrid::update` — Shift Grid Map with Vehicle Motion Moves the grid map to follow the vehicle, resets newly revealed cells to the interpolated ground height derived from a TF lookup (`base_link` → `map`), and returns the updated map pointer. If the vehicle has not moved, the map is returned unchanged. ### Method ```cpp std::shared_ptr update(const nav_msgs::Odometry::ConstPtr& odom) ``` ### Parameters * **odom** (const nav_msgs::Odometry::ConstPtr&): Odometry message containing the vehicle's current pose. ### Request Example ```cpp #include #include groundgrid::GroundGrid gg; nav_msgs::Odometry::Ptr odom_init(new nav_msgs::Odometry()); odom_init->pose.pose.position.x = 0.0; odom_init->pose.pose.position.y = 0.0; odom_init->pose.pose.position.z = 0.4; odom_init->header.stamp = ros::Time::now(); // First call initializes the map std::shared_ptr map = gg.update(odom_init); // Subsequent call with new position shifts the map nav_msgs::Odometry::Ptr odom_new(new nav_msgs::Odometry()); odom_new->pose.pose.position.x = 5.0; // moved 5 m east odom_new->pose.pose.position.y = 0.0; odom_new->pose.pose.position.z = 0.42; odom_new->header.stamp = ros::Time::now(); map = gg.update(odom_new); // Cells that entered the map from the east are reset using the TF-derived // ground height. Cells that left the map (west edge) are discarded. // map->getPosition() now returns (5.0, 0.0) ``` ### Response * **std::shared_ptr** : A pointer to the updated grid map. ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.