@@ -26,6 +26,7 @@ limitations under the License.
2626#include " nvblox/mapper/mapper.h"
2727#include " nvblox/mesh/mesh.h"
2828#include " nvblox/primitives/scene.h"
29+ #include " nvblox/sensors/lidar.h"
2930#include " nvblox/tests/utils.h"
3031
3132using namespace nvblox ;
@@ -403,6 +404,64 @@ TEST(MapperTest, SaveAndLoad) {
403404 mapper2.freespace_layer ().numBlocks ());
404405}
405406
407+ TEST (MapperTest, IntegratePointcloud) {
408+ // Create a simple pointcloud
409+ Pointcloud pointcloud (MemoryType::kUnified );
410+ std::vector<Vector3f> points_host = {
411+ Vector3f (1 .0f , 0 .0f , 0 .0f ), Vector3f (2 .0f , 0 .0f , 0 .0f ),
412+ Vector3f (3 .0f , 0 .0f , 0 .0f ), Vector3f (1 .5f , 0 .5f , 0 .0f ),
413+ Vector3f (2 .5f , -0 .5f , 0 .0f ), Vector3f (2 .0f , 1 .0f , 0 .0f ),
414+ Vector3f (2 .0f , -1 .0f , 0 .0f ), Vector3f (3 .0f , 0 .5f , 0 .5f )};
415+ pointcloud.copyPointsFromAsync (points_host, CudaStreamOwning ());
416+
417+ // Create a Lidar sensor with valid dimensions
418+ const int num_azimuth_divisions = 1024 ;
419+ const int num_elevation_divisions = 16 ;
420+ const float min_valid_range_m = 0 .1f ;
421+ const float vertical_fov_rad = 30 .0f * M_PI / 180 .0f ;
422+ Lidar lidar (num_azimuth_divisions, num_elevation_divisions, min_valid_range_m,
423+ vertical_fov_rad);
424+
425+ // Create Mapper
426+ const float voxel_size_m = 0 .1f ;
427+ Mapper mapper (voxel_size_m, MemoryType::kDevice , ProjectiveLayerType::kTsdf );
428+
429+ // Integrate pointcloud without motion compensation
430+ Transform T_L_S = Transform::Identity ();
431+ bool use_lidar_motion_compensation = false ;
432+ mapper.integrateDepth (pointcloud, T_L_S, lidar,
433+ use_lidar_motion_compensation);
434+
435+ // Verify integration worked - should have allocated some blocks
436+ const int num_blocks_after_first = mapper.tsdf_layer ().numBlocks ();
437+ EXPECT_GT (num_blocks_after_first, 0 );
438+
439+ // Integrate pointcloud again with motion compensation from a different
440+ // location Motion compensation requires timestamps for each point
441+ const int64_t scan_duration_ms_value = 100 ;
442+ Time scan_duration_ms (scan_duration_ms_value);
443+ std::vector<Time> timestamps_ms (points_host.size ());
444+ for (size_t i = 0 ; i < points_host.size (); i++) {
445+ // Spread timestamps evenly across scan duration
446+ timestamps_ms[i] = Time (i * scan_duration_ms_value / points_host.size ());
447+ }
448+ pointcloud.copyTimestampsFromAsync (timestamps_ms, CudaStreamOwning ());
449+
450+ // Start from a different position to cover new space
451+ Transform T_L_S_scanStart2 = Transform::Identity ();
452+ T_L_S_scanStart2.translate (Vector3f (0 .0f , 3 .0f , 0 .0f )); // Start 3m away in y
453+ Transform T_L_S_scanEnd2 = Transform::Identity ();
454+ T_L_S_scanEnd2.translate (Vector3f (0 .0f , 4 .0f , 0 .0f )); // End 4m away in y
455+ use_lidar_motion_compensation = true ;
456+ mapper.integrateDepth (pointcloud, T_L_S_scanStart2, lidar,
457+ use_lidar_motion_compensation, T_L_S_scanEnd2,
458+ scan_duration_ms);
459+
460+ // Verify more blocks were allocated after second integration
461+ const int num_blocks_after_second = mapper.tsdf_layer ().numBlocks ();
462+ EXPECT_GT (num_blocks_after_second, num_blocks_after_first);
463+ }
464+
406465int main (int argc, char ** argv) {
407466 FLAGS_alsologtostderr = true ;
408467 google::InitGoogleLogging (argv[0 ]);
0 commit comments