Skip to content

Commit f8a8cf1

Browse files
committed
[FEAT][V1.0] Added occupancy mapping pipeline code with optimisations
-- Added basic mapping pipeline code linking bresenham algo with ray trace function -- Added function to process lidar scan points - converts from sensor frame to world frame and then performs ray trace -- Added compiler optimisations for faster floating point additions. -- Optimised emplace back logic in bresenham algo -- Increased unordered set size to avoid hash collisions -- Added timing across entire pipeline -- Limited mapping and processing pipeline to first 10% of entire lidar point cloud dataset. -- Updated occupancy probability logic code -- Reduced the average scan time from 450ms to 350ms
1 parent 612e04c commit f8a8cf1

File tree

9 files changed

+312
-39
lines changed

9 files changed

+312
-39
lines changed

CMakeLists.txt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,21 @@ 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 src/occupancy_grid.cpp src/data_analyser.cpp)
27+
add_executable(occupancy_grid_main src/main.cpp src/occupancy_grid.cpp src/data_analyser.cpp src/scan_processor.cpp)
2828

2929
target_link_libraries(occupancy_grid_main PRIVATE dataloader visualizer)
3030

3131
target_compile_options(occupancy_grid_main PRIVATE -Wall -Wextra)
3232

33-
target_compile_features(occupancy_grid_main PRIVATE cxx_std_17)
33+
target_compile_features(occupancy_grid_main PRIVATE cxx_std_17)
34+
35+
# Release build optimizations
36+
if(CMAKE_BUILD_TYPE STREQUAL "Release")
37+
target_compile_options(occupancy_grid_main PRIVATE
38+
-O3 # Maximum optimization
39+
-DNDEBUG # Disable assertions
40+
-march=native # CPU-specific optimization
41+
-ffast-math # Faster floating-point math
42+
-funroll-loops # Loop unrolling
43+
)
44+
endif()

analyser.md

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Robot trajectory bounds:
3232
Range: 276.928 343.819 47.8035
3333
Total poses: 6770
3434
Total points processed: 432690151
35+
Dataset Analysis time: 443.096 seconds
3536

3637
=== OPTIMAL GRID CONFIGURATION ===
3738
Origin: -163.1 -254.1 -11.9
@@ -41,7 +42,7 @@ Total voxels: 7872308976
4142
Estimated memory: 30030.5 MB
4243
Grid covers: [-163.1 -254.1 -11.9] to [169.3 158.5 45.5]
4344
WARNING: Large memory usage! Consider:
44-
- Increasing resolution (e.g., 0.5m instead of 0.2m)
45+
- Increasing resolution (e.g., 0.2m instead of 0.1m)
4546
- Reducing safety margin
4647
- Processing subset of data first
4748

@@ -56,6 +57,12 @@ Resolution: 0.2m (20cm per voxels)
5657
Total voxels: 758336304
5758
Estimated memory: 2892.82 MB
5859
Grid covers: [-149.2 -236.9 -9.5] to [155.6 141.5 43.1]
60+
OccupancyGrid3D initialized:
61+
Dimensions: 1524×1892×263
62+
Resolution: 0.2m
63+
Origin: -149.2 -236.9 -9.5
64+
Total voxels: 758336304
65+
Grid creation time: 1.56853 seconds
5966

6067
=== STARTING MAPPING ===
6168
saiga@sai-Ideapad:~/Downloads/ModernCppProject2025/build$ cd ..
@@ -81,3 +88,109 @@ Coordinate Conversion Test:
8188

8289
=== BASIC FUNCTIONALITY TEST COMPLETED ===
8390
```
91+
92+
### Run with Timing Stats
93+
94+
```bash
95+
saiga@sai-Ideapad:~/Downloads/ModernCppProject2025/build$ ./occupancy_grid_main ../src/Data
96+
Loading dataset...
97+
Found 6770 scans
98+
99+
=== ANALYZING WORKSPACE ===
100+
Analyzing 6770 poses...
101+
Processed 0/6770 scans...
102+
Processed 338/6770 scans...
103+
Processed 676/6770 scans...
104+
Processed 1014/6770 scans...
105+
Processed 1352/6770 scans...
106+
Processed 1690/6770 scans...
107+
Processed 2028/6770 scans...
108+
Processed 2366/6770 scans...
109+
Processed 2704/6770 scans...
110+
Processed 3042/6770 scans...
111+
Processed 3380/6770 scans...
112+
Processed 3718/6770 scans...
113+
Processed 4056/6770 scans...
114+
Processed 4394/6770 scans...
115+
Processed 4732/6770 scans...
116+
Processed 5070/6770 scans...
117+
Processed 5408/6770 scans...
118+
Processed 5746/6770 scans...
119+
Processed 6084/6770 scans...
120+
Processed 6422/6770 scans...
121+
Processed 6760/6770 scans...
122+
=== WORKSPACE ANALYSIS ===
123+
Robot trajectory bounds:
124+
Min: -135.352 -219.638 -7.10028
125+
Max: 141.576 124.18 40.7033
126+
Range: 276.928 343.819 47.8035
127+
Total poses: 6770
128+
Total points processed: 432690151
129+
Dataset Analysis time: 22.9543 seconds
130+
131+
=== OPTIMAL GRID CONFIGURATION ===
132+
Origin: -163.1 -254.1 -11.9
133+
Dimensions: 3324×4126×574
134+
Resolution: 0.1m (10cm voxels)
135+
Total voxels: 7872308976
136+
Estimated memory: 30030.5 MB
137+
Grid covers: [-163.1 -254.1 -11.9] to [169.3 158.5 45.5]
138+
WARNING: Large memory usage! Consider:
139+
- Increasing resolution (e.g., 0.2m instead of 0.1m)
140+
- Reducing safety margin
141+
- Processing subset of data first
142+
143+
=== APPLYING OPTIMIZATIONS ===
144+
Auto-adjusted resolution to: 0.5m
145+
Optimized memory usage: 148.276 MB
146+
147+
=== OPTIMIZED GRID CONFIGURATION ===
148+
Origin: -138.2 -223.1 -7.6
149+
Dimensions: 565×702×98
150+
Resolution: 0.5m (50cm per voxels)
151+
Total voxels: 38869740
152+
Estimated memory: 148.276 MB
153+
Grid covers: [-138.2 -223.1 -7.6] to [144.3 127.9 41.4]
154+
155+
=== STARTING MAPPING ===
156+
OccupancyGrid3D initialized:
157+
Dimensions: 565×702×98
158+
Resolution: 0.5m
159+
Origin: -138.2 -223.1 -7.6
160+
Total voxels: 38869740
161+
Grid creation time: 0.0459019 seconds
162+
163+
=== PROCESSING LIDAR SCANS ===
164+
Scan completed. Best rayTrace: 0.088 us, Worst rayTrace: 11.628 us
165+
Mapped :: 0/6770 scans...
166+
Scan completed. Best rayTrace: 0.099 us, Worst rayTrace: 13.404 us
167+
Scan completed. Best rayTrace: 0.033 us, Worst rayTrace: 13.938 us
168+
Scan completed. Best rayTrace: 0.034 us, Worst rayTrace: 13.828 us
169+
Scan completed. Best rayTrace: 0.033 us, Worst rayTrace: 11.217 us
170+
Scan completed. Best rayTrace: 0.035 us, Worst rayTrace: 13.52 us
171+
Scan completed. Best rayTrace: 0.031 us, Worst rayTrace: 52.029 us
172+
Scan completed. Best rayTrace: 0.031 us, Worst rayTrace: 16.557 us
173+
Scan completed. Best rayTrace: 0.033 us, Worst rayTrace: 22.091 us
174+
Scan completed. Best rayTrace: 0.055 us, Worst rayTrace: 137.093 us
175+
Scan completed. Best rayTrace: 0.033 us, Worst rayTrace: 160.792 us
176+
Scan completed. Best rayTrace: 0.033 us, Worst rayTrace: 173.393 us
177+
Scan completed. Best rayTrace: 0.034 us, Worst rayTrace: 217.114 us
178+
Scan completed. Best rayTrace: 0.034 us, Worst rayTrace: 405.679 us
179+
Scan completed. Best rayTrace: 0.034 us, Worst rayTrace: 115.71 us
180+
Scan completed. Best rayTrace: 0.034 us, Worst rayTrace: 155.032 us
181+
Scan completed. Best rayTrace: 0.034 us, Worst rayTrace: 167.785 us
182+
Mapped :: 677/6770 scans...
183+
Mapping completed!
184+
=== SCAN STATISTICS ===
185+
Total scans processed: 678
186+
Total execution time: 240.746 seconds
187+
Best scan time: 0.0372507 seconds Worst scan time: 0.615129 seconds
188+
Average time per scan: 354.904 ms
189+
Scan rate: 2.81624 scans/second
190+
=== EXTRACTING OCCUPIED VOXELS ===
191+
Extracted 40985 occupied voxels from 40985 tracked voxels (vs 38869740 total voxels)
192+
Voxel extraction time: 0.00129689 seconds.
193+
Visualising Voxels using open3D ===
194+
Visualizing 40985 occupied voxels...
195+
Visualization time: 54.764 seconds.
196+
```

src/data_analyser.cpp

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,10 @@ WorkspaceBounds DataAnalyzer::analyzeDataset(const dataloader::Dataset& dataset,
2222
max_coords = max_coords.cwiseMax(robot_pos);
2323

2424
if (include_sensor_points) {
25+
const Eigen::Matrix3d rotation = pose.block<3,3>(0,0);
2526
// Transform sensor points to world frame and include in bounds
2627
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>();
28+
Eigen::Vector3d world_point = rotation * sensor_point + robot_pos;
3629

3730
// Update bounds
3831
min_coords = min_coords.cwiseMin(world_point);
@@ -42,7 +35,7 @@ WorkspaceBounds DataAnalyzer::analyzeDataset(const dataloader::Dataset& dataset,
4235
}
4336

4437
// Progress indicator
45-
if (idx % (dataset.size() / 10) == 0) {
38+
if (idx % (dataset.size() / 20) == 0) {
4639
std::cout << "Processed " << idx << "/" << dataset.size() << " scans..." << std::endl;
4740
}
4841
}
@@ -85,3 +78,5 @@ std::array<size_t, 3> DataAnalyzer::estimateGridDimensions(const WorkspaceBounds
8578
}
8679

8780

81+
82+

src/data_analyser.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ class DataAnalyzer {
3030
static std::array<size_t, 3> estimateGridDimensions(const WorkspaceBounds& bounds,
3131
double resolution,
3232
double safety_margin = 0.15);
33+
3334
};

src/main.cpp

Lines changed: 103 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,36 @@
11
#include <iostream>
2+
#include <chrono>
3+
#include <algorithm>
4+
#include <vector>
5+
#include <numeric>
6+
#include <functional>
27
#include "occupancy_grid.hpp"
38
#include "data_analyser.hpp"
9+
#include "scan_processor.hpp"
10+
#include "visualizer.hpp"
11+
//#define TEST
12+
13+
constexpr float OCCUPANCY_THRESHOLD = 0.5f;
14+
15+
void printTimingStats(const std::vector<double>& scan_times, double total_time) {
16+
std::cout << "=== SCAN STATISTICS ===" << std::endl;
17+
if (scan_times.empty()) {
18+
19+
std::cout << "No scan data available!" << std::endl;
20+
return;
21+
}
22+
23+
double avg_scan_time = std::reduce(scan_times.begin(), scan_times.end(), 0.0,std::plus<double>()) / scan_times.size();
24+
const auto [min,max] = std::minmax_element(begin(scan_times), end(scan_times));
25+
std::cout << "Total scans processed: " << scan_times.size() << std::endl;
26+
std::cout << "Total execution time: " << total_time << " seconds" << std::endl;
27+
std::cout << "Best scan time: " << *min << " seconds " << "Worst scan time: " << *max << " seconds " << std::endl;
28+
std::cout << "Average time per scan: " << avg_scan_time * 1000 << " ms" << std::endl;
29+
std::cout << "Scan rate: " << scan_times.size() / total_time << " scans/second" << std::endl;
30+
}
31+
32+
433

5-
#define TEST
634

735
int main(int argc, char* argv[]) {
836

@@ -13,7 +41,7 @@ int main(int argc, char* argv[]) {
1341
}
1442

1543
const std::string data_dir = argv[1];
16-
44+
auto start_analys_time = std::chrono::high_resolution_clock::now();
1745
// Load dataset
1846
std::cout << "Loading dataset..." << std::endl;
1947
dataloader::Dataset dataset(data_dir);
@@ -23,7 +51,9 @@ int main(int argc, char* argv[]) {
2351
std::cout << "\n=== ANALYZING WORKSPACE ===" << std::endl;
2452
WorkspaceBounds bounds = DataAnalyzer::analyzeDataset(dataset, true);
2553
bounds.printSummary();
26-
54+
auto end_analys_time = std::chrono::high_resolution_clock::now();
55+
double total_analysis_time = std::chrono::duration<double>(end_analys_time - start_analys_time).count();
56+
std::cout << "Dataset Analysis time: " << total_analysis_time << " seconds " << std::endl;
2757
// Determine optimal parameters
2858
double resolution = 0.1; // 10cm voxels - adjust as needed
2959
double safety_margin = 0.10; // 10% margin around workspace
@@ -58,8 +88,8 @@ int main(int argc, char* argv[]) {
5888
double target_voxels = (target_memory_gb * 1024 * 1024 * 1024) / sizeof(float);
5989
double optimized_resolution = std::pow(volume / target_voxels, 1.0/3.0);
6090

61-
resolution = std::max(optimized_resolution, 0.2); // Minimum 20cm
62-
safety_margin = 0.05; // Reduced safety margin
91+
resolution = std::max(optimized_resolution, 0.5); // Minimum 50cm
92+
safety_margin = 0.01; // Reduced safety margin
6393

6494
std::cout << "Auto-adjusted resolution to: " << resolution << "m" << std::endl;
6595

@@ -83,12 +113,75 @@ int main(int argc, char* argv[]) {
83113
<< (optimal_origin + Eigen::Vector3d(grid_dims[0]*resolution, grid_dims[1]*resolution, grid_dims[2]*resolution)).transpose() << "]" << std::endl;
84114
}
85115

86-
87-
88-
// Create optimally sized grid
89-
//OccupancyGrid grid(grid_dims, resolution, optimal_origin);
90-
91116
std::cout << "\n=== STARTING MAPPING ===" << std::endl;
117+
auto start_time = std::chrono::high_resolution_clock::now();
118+
// Create optimally sized grid
119+
OccupancyGrid grid(grid_dims, resolution, optimal_origin);
120+
auto end_time = std::chrono::high_resolution_clock::now();
121+
double total_time = std::chrono::duration<double>(end_time - start_time).count();
122+
std::cout << "Grid creation time: " << total_time << " seconds " << std::endl;
123+
124+
std::cout << "\n=== PROCESSING LIDAR SCANS ===" << std::endl;
125+
std::vector<double> scan_times;
126+
scan_times.reserve(dataset.size());
127+
128+
auto mapping_start = std::chrono::high_resolution_clock::now();
129+
130+
// test var
131+
int scan_count = 0;
132+
// Process each scan
133+
for (size_t i = 0; i < dataset.size(); ++i) {
134+
auto scan_start = std::chrono::high_resolution_clock::now();
135+
136+
// Load pose and pointcloud from the dataset
137+
const auto& [pose, pointcloud] = dataset[i];
138+
139+
// Process the scan using your function
140+
ScanProcessor::processLidarScan(grid, pose, pointcloud);
141+
142+
auto scan_end = std::chrono::high_resolution_clock::now();
143+
double scan_time = std::chrono::duration<double>(scan_end - scan_start).count();
144+
scan_times.emplace_back(scan_time);
145+
146+
// Progress indicator
147+
if (i % (dataset.size() / 10) == 0) {
148+
std::cout << "Mapped :: " << i << "/" << dataset.size() << " scans..." << std::endl;
149+
scan_count++;
150+
if (scan_count > 1) {
151+
// TODO: fix this issue: for now: restrict the scan processing to 10% of lidar dataset, as the scan time reaches 250 seconds.
152+
break;
153+
}
154+
}
155+
}
156+
157+
auto mapping_end = std::chrono::high_resolution_clock::now();
158+
double total_mapping_time = std::chrono::duration<double>(mapping_end - mapping_start).count();
159+
160+
std::cout << "Mapping completed!" << std::endl;
161+
printTimingStats(scan_times, total_mapping_time);
162+
163+
std::cout << "=== EXTRACTING OCCUPIED VOXELS ===" << std::endl;
164+
auto extraction_start = std::chrono::high_resolution_clock::now();
165+
Vector3dVector occupied_voxels = grid.getOccupiedVoxels(OCCUPANCY_THRESHOLD);
166+
auto extraction_end = std::chrono::high_resolution_clock::now();
167+
double extraction_time = std::chrono::duration<double>(extraction_end - extraction_start).count();
168+
std::cout << "Voxel extraction time: " << extraction_time << " seconds." << std::endl;
169+
170+
if(!occupied_voxels.empty()) {
171+
172+
std::cout << "Visualising Voxels using open3D ===" << std::endl;
173+
std::cout << "Visualizing " << occupied_voxels.size() << " occupied voxels..." << std::endl;
174+
auto viz_start = std::chrono::high_resolution_clock::now();
175+
visualize(occupied_voxels); // Calls open3d API
176+
auto viz_end = std::chrono::high_resolution_clock::now();
177+
double viz_time = std::chrono::duration<double>(viz_end - viz_start).count();
178+
std::cout << "Visualization time: " << viz_time << " seconds." << std::endl;
179+
} else {
180+
std::cout << "No occupied voxels found! Check mapping pipeline." << std::endl;
181+
}
182+
183+
184+
92185
#else
93186
// Test with small grid first
94187
std::cout << "=== TESTING BASIC FUNCTIONALITY ===" << std::endl;

0 commit comments

Comments
 (0)