Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 88 additions & 2 deletions dimos/mapping/ray_tracing/rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,19 @@ fn find_misses_along_ray(
let shadow_sq = shadow_depth.powi(2);
let grace_sq = grace_depth.powi(2);

let ray_len = (dx * dx + dy * dy + dz * dz).sqrt();
let t_max = 1.0 + shadow_depth / ray_len.max(f32::EPSILON);

let mut past_endpoint = false;
loop {
let t_enter = tx.min(ty).min(tz);
if t_enter > t_max {
return;
}
if t_enter >= 1.0 {
past_endpoint = true;
}

if tx < ty {
if tx < tz {
x += step_x;
Expand All @@ -353,6 +364,12 @@ fn find_misses_along_ray(
continue;
}

// don't remove points in the same xy plane as the hit, unless the plane only walks that plane
// we do this to preserve floors, which is more important than some missed points
if origin_voxel.2 != endpoint.2 && z == endpoint.2 {
continue;
}
Comment thread
aclauer marked this conversation as resolved.

let cx = x as f32 * voxel_size + half;
let cy = y as f32 * voxel_size + half;
let cz = z as f32 * voxel_size + half;
Expand Down Expand Up @@ -625,7 +642,7 @@ mod tests {
let origin_voxel = world_to_voxel(origin.0, origin.1, origin.2, inv);
let endpoint = world_to_voxel(end.0, end.1, end.2, inv);

let expected: AHashSet<VoxelKey> = [
let walked: AHashSet<VoxelKey> = [
(1, 0, 0),
(1, 1, 0),
(1, 1, 1),
Expand All @@ -638,7 +655,7 @@ mod tests {
.into_iter()
.collect();
let mut map_voxels: AHashMap<VoxelKey, i32> = AHashMap::new();
for v in &expected {
for v in &walked {
map_voxels.insert(*v, 1);
}

Expand All @@ -655,6 +672,13 @@ mod tests {
endpoint,
);

// z-slab protection skips voxels in the endpoint's z-slab (z=1) when the
// ray crosses z-slabs.
let expected: AHashSet<VoxelKey> = walked
.iter()
.filter(|v| v.2 != endpoint.2)
.copied()
.collect();
assert_eq!(misses, expected);
}

Expand Down Expand Up @@ -850,6 +874,68 @@ mod tests {
assert!(cloud_points(&local).contains(&voxel_center(10, 10, 10)));
}

/// Test how bad the planar ray clipping is.
/// For example, points on floors can be counted as misses because they are close to the same ray as the hit.
#[test]
fn ground_clipping_single_ray() {
let voxel_size = 0.1_f32;
let lidar_height = 1.0_f32;
let cfg = Config {
voxel_size,
max_range: 50.0,
ray_subsample: 1,
shadow_depth: 0.2,
grace_depth: 0.2,
min_health: 0,
max_health: 1,
};
let inv = 1.0 / voxel_size;

// Cover the full range we will probe, plus a little for shadow.
let max_x = 25.0_f32;
let n_ground = (max_x / voxel_size).ceil() as i32;

let ranges: Vec<f32> = (1..=20).map(|i| i as f32).collect();
let mut table = format!(
"voxel_size={voxel_size} lidar_height={lidar_height} grace={} shadow={}\n\
range_m ground_voxels_in_row clipped clipped_pct\n",
cfg.grace_depth, cfg.shadow_depth
);
let mut total_clipped = 0usize;
for &range in &ranges {
let mut map = VoxelMap::default();
for i in 0..n_ground {
let x = (i as f32) * voxel_size + voxel_size * 0.5;
let key = world_to_voxel(x, 0.0, 0.0, inv);
map.voxels.insert(key, cfg.max_health);
}
let n_before = map.voxels.len();

let origin = (0.0_f32, 0.0_f32, lidar_height);
let hits = vec![(range, 0.0_f32, 0.0_f32)];
update_map(&mut map, origin, &hits, &cfg);

let n_after_ground: usize = (0..n_ground)
.filter(|i| {
let x = (*i as f32) * voxel_size + voxel_size * 0.5;
let key = world_to_voxel(x, 0.0, 0.0, inv);
map.voxels.contains_key(&key)
})
.count();
let clipped = n_before - n_after_ground;
let pct = 100.0 * clipped as f32 / n_before as f32;
table.push_str(&format!(
"{range:>6.1} {n_before:>20} {clipped:>7} {pct:>10.1}\n"
));
total_clipped += clipped;
}
eprint!("{table}");
assert!(
total_clipped == 0,
"planar grace regressed, ground voxels clipped:\n{table}"
);
}

#[test]
fn two_misses_needed_when_max_health_is_two() {
let cfg = Config {
Expand Down
Loading