Skip to content

Commit 3cc4351

Browse files
committed
Added OccupancyGrid class definition and Dataset Analyser code
-- Added occupancygrid class with member function prototypes -- Added dataset analyser to read the pose and determine the range of the occupancy grid from the laser scan data. -- Dataset Analyser gives the optimal grid origin, voxel resolution and grid dimensions needed for the mapping.
1 parent 68148ea commit 3cc4351

File tree

6 files changed

+329
-7
lines changed

6 files changed

+329
-7
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ target_include_directories(visualizer INTERFACE src/visualizer)
2424
target_link_libraries(visualizer INTERFACE Open3D::Open3D Eigen3::Eigen)
2525

2626
#will have occupancy grid logic
27-
add_executable(occupancy_grid_main src/main.cpp)
27+
add_executable(occupancy_grid_main src/main.cpp src/data_analyser.cpp)
2828

2929
target_link_libraries(occupancy_grid_main PRIVATE dataloader visualizer)
3030

analyser.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
## Build and Execution Output
2+
3+
```bash
4+
saiga@sai-Ideapad:~/Downloads/ModernCppProject2025$ cmake --build build
5+
Consolidate compiler generated dependencies of target dataloader
6+
[ 40%] Built target dataloader
7+
Consolidate compiler generated dependencies of target occupancy_grid_main
8+
[ 60%] Building CXX object CMakeFiles/occupancy_grid_main.dir/src/main.cpp.o
9+
[ 80%] Linking CXX executable occupancy_grid_main
10+
[100%] Built target occupancy_grid_main
11+
saiga@sai-Ideapad:~/Downloads/ModernCppProject2025$ cd build/
12+
saiga@sai-Ideapad:~/Downloads/ModernCppProject2025/build$ ./occupancy_grid_main ../src/Data
13+
Loading dataset...
14+
Found 6770 scans
15+
16+
=== ANALYZING WORKSPACE ===
17+
Analyzing 6770 poses...
18+
Processed 0/6770 scans...
19+
Processed 677/6770 scans...
20+
Processed 1354/6770 scans...
21+
Processed 2031/6770 scans...
22+
Processed 2708/6770 scans...
23+
Processed 3385/6770 scans...
24+
Processed 4062/6770 scans...
25+
Processed 4739/6770 scans...
26+
Processed 5416/6770 scans...
27+
Processed 6093/6770 scans...
28+
=== WORKSPACE ANALYSIS ===
29+
Robot trajectory bounds:
30+
Min: -135.352 -219.638 -7.10028
31+
Max: 141.576 124.18 40.7033
32+
Range: 276.928 343.819 47.8035
33+
Total poses: 6770
34+
Total points processed: 432690151
35+
36+
=== OPTIMAL GRID CONFIGURATION ===
37+
Origin: -163.1 -254.1 -11.9
38+
Dimensions: 3324×4126×574
39+
Resolution: 0.1m (10cm voxels)
40+
Total voxels: 7872308976
41+
Estimated memory: 30030.5 MB
42+
Grid covers: [-163.1 -254.1 -11.9] to [169.3 158.5 45.5]
43+
WARNING: Large memory usage! Consider:
44+
- Increasing resolution (e.g., 0.5m instead of 0.2m)
45+
- Reducing safety margin
46+
- Processing subset of data first
47+
48+
=== APPLYING OPTIMIZATIONS ===
49+
Auto-adjusted resolution to: 0.2m
50+
Optimized memory usage: 2892.82 MB
51+
52+
=== OPTIMIZED GRID CONFIGURATION ===
53+
Origin: -149.2 -236.9 -9.5
54+
Dimensions: 1524×1892×263
55+
Resolution: 0.2m (20cm per voxels)
56+
Total voxels: 758336304
57+
Estimated memory: 2892.82 MB
58+
Grid covers: [-149.2 -236.9 -9.5] to [155.6 141.5 43.1]
59+
60+
=== STARTING MAPPING ===
61+
saiga@sai-Ideapad:~/Downloads/ModernCppProject2025/build$ cd ..
62+
```

src/data_analyser.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include "data_analyser.hpp"
2+
#include <iostream>
3+
#include <algorithm>
4+
5+
WorkspaceBounds DataAnalyzer::analyzeDataset(const dataloader::Dataset& dataset,
6+
bool include_sensor_points) {
7+
// Initialize bounds with extreme values
8+
Eigen::Vector3d min_coords(1e9, 1e9, 1e9);
9+
Eigen::Vector3d max_coords(-1e9, -1e9, -1e9);
10+
size_t total_points = 0;
11+
12+
std::cout << "Analyzing " << dataset.size() << " poses..." << std::endl;
13+
14+
for (size_t idx = 0; idx < dataset.size(); ++idx) {
15+
const auto& [pose, sensor_points] = dataset[idx];
16+
17+
// Extract robot position from pose matrix (translation part)
18+
Eigen::Vector3d robot_pos(pose(0,3), pose(1,3), pose(2,3));
19+
20+
// Update bounds with robot position
21+
min_coords = min_coords.cwiseMin(robot_pos);
22+
max_coords = max_coords.cwiseMax(robot_pos);
23+
24+
if (include_sensor_points) {
25+
// Transform sensor points to world frame and include in bounds
26+
for (const auto& sensor_point : sensor_points) {
27+
// Manual transformation
28+
Eigen::Vector4d point_homogeneous;
29+
point_homogeneous(0) = sensor_point(0);
30+
point_homogeneous(1) = sensor_point(1);
31+
point_homogeneous(2) = sensor_point(2);
32+
point_homogeneous(3) = 1.0;
33+
34+
Eigen::Vector4d world_point_h = pose * point_homogeneous;
35+
Eigen::Vector3d world_point = world_point_h.head<3>();
36+
37+
// Update bounds
38+
min_coords = min_coords.cwiseMin(world_point);
39+
max_coords = max_coords.cwiseMax(world_point);
40+
total_points++;
41+
}
42+
}
43+
44+
// Progress indicator
45+
if (idx % (dataset.size() / 10) == 0) {
46+
std::cout << "Processed " << idx << "/" << dataset.size() << " scans..." << std::endl;
47+
}
48+
}
49+
50+
Eigen::Vector3d range = max_coords - min_coords;
51+
52+
return {min_coords, max_coords, range, dataset.size(), total_points};
53+
}
54+
55+
Eigen::Vector3d DataAnalyzer::estimateOptimalOrigin(const WorkspaceBounds& bounds,
56+
double safety_margin) {
57+
// Add safety margin around the workspace
58+
Eigen::Vector3d margin = bounds.range * safety_margin;
59+
Eigen::Vector3d optimal_origin = bounds.min_coords - margin;
60+
61+
// Optional: Round to nice numbers for easier debugging
62+
for (int i = 0; i < 3; ++i) {
63+
optimal_origin(i) = std::floor(optimal_origin(i) * 10.0) / 10.0; // Round to 0.1m
64+
}
65+
66+
return optimal_origin;
67+
}
68+
69+
std::array<size_t, 3> DataAnalyzer::estimateGridDimensions(const WorkspaceBounds& bounds,
70+
double resolution,
71+
double safety_margin) {
72+
// Calculate total range including margins
73+
Eigen::Vector3d margin = bounds.range * safety_margin;
74+
Eigen::Vector3d total_range = bounds.range + 2.0 * margin; // margin on both sides
75+
76+
std::array<size_t, 3> dimensions;
77+
for (int i = 0; i < 3; ++i) {
78+
dimensions[i] = static_cast<size_t>(std::ceil(total_range(i) / resolution));
79+
80+
// Ensure minimum dimensions
81+
dimensions[i] = std::max(dimensions[i], static_cast<size_t>(10));
82+
}
83+
84+
return dimensions;
85+
}
86+
87+

src/data_analyser.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#pragma once
2+
#include <Eigen/Core>
3+
#include "dataloader.hpp"
4+
#include <iostream>
5+
6+
struct WorkspaceBounds {
7+
Eigen::Vector3d min_coords;
8+
Eigen::Vector3d max_coords;
9+
Eigen::Vector3d range;
10+
size_t total_poses;
11+
size_t total_points;
12+
13+
void printSummary() const {
14+
std::cout << "=== WORKSPACE ANALYSIS ===" << std::endl;
15+
std::cout << "Robot trajectory bounds:" << std::endl;
16+
std::cout << " Min: " << min_coords.transpose() << std::endl;
17+
std::cout << " Max: " << max_coords.transpose() << std::endl;
18+
std::cout << " Range: " << range.transpose() << std::endl;
19+
std::cout << "Total poses: " << total_poses << std::endl;
20+
std::cout << "Total points processed: " << total_points << std::endl;
21+
}
22+
};
23+
24+
class DataAnalyzer {
25+
public:
26+
static WorkspaceBounds analyzeDataset(const dataloader::Dataset& dataset,
27+
bool include_sensor_points = true);
28+
static Eigen::Vector3d estimateOptimalOrigin(const WorkspaceBounds& bounds,
29+
double safety_margin = 0.15);
30+
static std::array<size_t, 3> estimateGridDimensions(const WorkspaceBounds& bounds,
31+
double resolution,
32+
double safety_margin = 0.15);
33+
};

src/main.cpp

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,90 @@
1-
#include<iostream>
2-
using std::cout;
3-
using std::endl;
1+
#include <iostream>
2+
#include "occupancy_grid.hpp"
3+
#include "data_analyser.hpp"
44

5-
int main() {
6-
cout << "Hello World!" << endl;
7-
}
5+
int main(int argc, char* argv[]) {
6+
if (argc != 2) {
7+
std::cerr << "Usage: " << argv[0] << " <data_directory>" << std::endl;
8+
return -1;
9+
}
10+
11+
const std::string data_dir = argv[1];
12+
13+
// Load dataset
14+
std::cout << "Loading dataset..." << std::endl;
15+
dataloader::Dataset dataset(data_dir);
16+
std::cout << "Found " << dataset.size() << " scans" << std::endl;
17+
18+
// Analyze workspace to determine optimal grid parameters
19+
std::cout << "\n=== ANALYZING WORKSPACE ===" << std::endl;
20+
WorkspaceBounds bounds = DataAnalyzer::analyzeDataset(dataset, true);
21+
bounds.printSummary();
22+
23+
// Determine optimal parameters
24+
double resolution = 0.1; // 10cm voxels - adjust as needed
25+
double safety_margin = 0.10; // 10% margin around workspace
26+
27+
Eigen::Vector3d optimal_origin = DataAnalyzer::estimateOptimalOrigin(bounds, safety_margin);
28+
std::array<size_t, 3> grid_dims = DataAnalyzer::estimateGridDimensions(bounds, resolution, safety_margin);
29+
30+
// Calculate memory usage
31+
size_t total_voxels = grid_dims[0] * grid_dims[1] * grid_dims[2];
32+
double memory_mb = (total_voxels * sizeof(float)) / (1024.0 * 1024.0);
33+
34+
std::cout << "\n=== OPTIMAL GRID CONFIGURATION ===" << std::endl;
35+
std::cout << "Origin: " << optimal_origin.transpose() << std::endl;
36+
std::cout << "Dimensions: " << grid_dims[0] << "×" << grid_dims[1] << "×" << grid_dims[2] << std::endl;
37+
std::cout << "Resolution: " << resolution << "m (" << (resolution*100) << "cm voxels)" << std::endl;
38+
std::cout << "Total voxels: " << total_voxels << std::endl;
39+
std::cout << "Estimated memory: " << memory_mb << " MB" << std::endl;
40+
std::cout << "Grid covers: [" << optimal_origin.transpose() << "] to ["
41+
<< (optimal_origin + Eigen::Vector3d(grid_dims[0]*resolution, grid_dims[1]*resolution, grid_dims[2]*resolution)).transpose() << "]" << std::endl;
42+
43+
// Warn if memory usage is too high
44+
if (memory_mb > 5000) { // > 5GB
45+
std::cout << "WARNING: Large memory usage! Consider:" << std::endl;
46+
std::cout << " - Increasing resolution (e.g., 0.2m instead of 0.1m)" << std::endl;
47+
std::cout << " - Reducing safety margin" << std::endl;
48+
std::cout << " - Processing subset of data first" << std::endl;
49+
std::cout << "\n=== APPLYING OPTIMIZATIONS ===" << std::endl;
50+
51+
// Auto-adjust resolution
52+
double target_memory_gb = 5.0; // Target 2GB
53+
double volume = bounds.range.x() * bounds.range.y() * bounds.range.z();
54+
double target_voxels = (target_memory_gb * 1024 * 1024 * 1024) / sizeof(float);
55+
double optimized_resolution = std::pow(volume / target_voxels, 1.0/3.0);
56+
57+
resolution = std::max(optimized_resolution, 0.2); // Minimum 20cm
58+
safety_margin = 0.05; // Reduced safety margin
59+
60+
std::cout << "Auto-adjusted resolution to: " << resolution << "m" << std::endl;
61+
62+
// Recalculate with optimized parameters
63+
optimal_origin = DataAnalyzer::estimateOptimalOrigin(bounds, safety_margin);
64+
grid_dims = DataAnalyzer::estimateGridDimensions(bounds, resolution, safety_margin);
65+
66+
// Recalculate memory
67+
total_voxels = grid_dims[0] * grid_dims[1] * grid_dims[2];
68+
memory_mb = (total_voxels * sizeof(float)) / (1024.0 * 1024.0);
69+
70+
std::cout << "Optimized memory usage: " << memory_mb << " MB" << std::endl;
71+
72+
std::cout << "\n=== OPTIMIZED GRID CONFIGURATION ===" << std::endl;
73+
std::cout << "Origin: " << optimal_origin.transpose() << std::endl;
74+
std::cout << "Dimensions: " << grid_dims[0] << "×" << grid_dims[1] << "×" << grid_dims[2] << std::endl;
75+
std::cout << "Resolution: " << resolution << "m (" << (resolution*100) << "cm per voxels)" << std::endl;
76+
std::cout << "Total voxels: " << total_voxels << std::endl;
77+
std::cout << "Estimated memory: " << memory_mb << " MB" << std::endl;
78+
std::cout << "Grid covers: [" << optimal_origin.transpose() << "] to ["
79+
<< (optimal_origin + Eigen::Vector3d(grid_dims[0]*resolution, grid_dims[1]*resolution, grid_dims[2]*resolution)).transpose() << "]" << std::endl;
80+
}
81+
82+
83+
84+
// Create optimally sized grid
85+
//OccupancyGrid grid(grid_dims, resolution, optimal_origin);
86+
87+
std::cout << "\n=== STARTING MAPPING ===" << std::endl;
88+
89+
return 0;
90+
}

src/occupancy_grid.hpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#pragma once
2+
3+
#include <Eigen/Core>
4+
#include <array>
5+
#include <vector>
6+
7+
using Vector3dVector = std::vector<Eigen::Vector3d>;
8+
using Vector3d = Eigen::Vector3d;
9+
using Vector3i = Eigen::Vector3i;
10+
11+
//TODO: implement the member functions
12+
class OccupancyGrid {
13+
private:
14+
std::array<size_t, 3> voxel_dimensions;
15+
double voxel_resolution;
16+
// links grid coordinates with world coordinates
17+
Vector3d grid_origin;
18+
std::vector<float> grid_data;
19+
// connects the start and end point of lidar ray in voxel map
20+
std::vector<Vector3i> draw_bresenham3d_line(const Vector3i& start, const Vector3i& end) const;
21+
// updates the log odds aka grid data for the voxel
22+
void update_occupied(const Vector3i& idx);
23+
void update_free(const Vector3i& idx);
24+
// check if the index within the grid boundary
25+
bool is_InBounds(const Vector3i& idx) const;
26+
// converts voxel index 3D to 1D index to fetch grid data when required.
27+
size_t to_LinearIndex(const Vector3i& idx) const;
28+
public:
29+
OccupancyGrid(const std::array<size_t, 3>& dimensions, double resolution, const Vector3d& origin);
30+
31+
// TODO: implement transformations
32+
Vector3i world_to_grid(const Vector3d& world_coord) const;
33+
Vector3d grid_to_world(const Vector3i& grid_index) const;
34+
// to update occupancy probability along the lidar scan
35+
void rayTrace(const Vector3d& start, const Vector3d& end);
36+
// to return the voxels which are occupied either by obstacles or grid boundary
37+
Vector3dVector getOccupiedVoxels(float threshold = 0.0f) const;
38+
//to detect if the lidar ray is crossing the grid boundary
39+
Vector3i findGridBoundaryIntersection(const Vector3i& start, const Vector3i& end) const;
40+
};
41+
42+
43+
44+
// namespace dataloader
45+
// {
46+
// class Dataset
47+
// {
48+
// public:
49+
// Dataset(const std::string &data_dir);
50+
// std::size_t size() const { return pointcloud_files_.size(); }
51+
// PoseAndCloud operator[](const int idx) const;
52+
53+
// private:
54+
// std::vector<std::string> pointcloud_files_;
55+
// std::vector<Eigen::Matrix4d> poses_;
56+
// };
57+
// } // namespace dataloader

0 commit comments

Comments
 (0)