Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP lidar fusion #162

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ members = [
"components/sources/cu_wt901",
"components/sources/cu_rp_encoder",
"components/tasks/cu_aligner",
"components/tasks/cu_lidar_fusion",
"components/tasks/cu_pid",
"components/testing/cu_udp_inject",
"examples/cu_caterpillar",
Expand Down Expand Up @@ -117,5 +118,8 @@ derive_more = { version = "1", features = ["full"] }
# used across testing
tempfile = "3.14.0"

# 0.21 is crappy out at the time of writing with some other dependencies
rerun = "0.20.3"

# [profile.release]
# lto = true
18 changes: 18 additions & 0 deletions components/tasks/cu_lidar_fusion/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "cu-lidar-fusion"
description = "Fusion Lidar - Lidar for Copper"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
keywords.workspace = true
categories.workspace = true
homepage.workspace = true
repository.workspace = true

[dependencies]
cu29 = { workspace = true }
cu-sensor-payloads = { path = "../../payloads/cu_sensor_payloads" }

[dev-dependencies]
rerun = { workspace = true }
199 changes: 199 additions & 0 deletions components/tasks/cu_lidar_fusion/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
use cu29::prelude::*;
use cu_sensor_payloads::PointCloudSoa;

pub fn fuse_lidar<const LS: usize, const RS: usize, const OS: usize>(
left: &PointCloudSoa<LS>,
right: &PointCloudSoa<RS>,
result: &mut PointCloudSoa<OS>,
) -> CuResult<()> {
left.iter().chain(right.iter()).for_each(|l| result.push(l));
Ok(())
}

pub fn fuse_lidar_fast<const LS: usize, const RS: usize, const OS: usize>(
left: &PointCloudSoa<LS>,
right: &PointCloudSoa<RS>,
result: &mut PointCloudSoa<OS>,
) -> CuResult<()> {
let left_size = left.len();
let right_size = right.len();

// favor reading linearity for better prefetching
result.tov[0..left_size].copy_from_slice(&left.tov[..left_size]);
result.x[0..left_size].copy_from_slice(&left.x[..left_size]);
result.y[0..left_size].copy_from_slice(&left.y[..left_size]);
result.z[0..left_size].copy_from_slice(&left.z[..left_size]);
result.i[0..left_size].copy_from_slice(&left.i[..left_size]);

result.tov[left_size..].copy_from_slice(&right.tov[..right_size]);
result.x[left_size..].copy_from_slice(&right.x[..right_size]);
result.y[left_size..].copy_from_slice(&right.y[..right_size]);
result.z[left_size..].copy_from_slice(&right.z[..right_size]);
result.i[left_size..].copy_from_slice(&right.i[..right_size]);

result.len = left_size + right_size;

Ok(())
}

// Both need to be sorted by tov, the result will be sorted by tov
pub fn mergesort_lidar<const LS: usize, const RS: usize, const OS: usize>(
left: &PointCloudSoa<LS>,
right: &PointCloudSoa<RS>,
result: &mut PointCloudSoa<OS>,
) {
let mut left_index = 0;
let mut right_index = 0;
let mut result_index = 0;

while left_index < left.len() && right_index < right.len() {
if left.tov[left_index] < right.tov[right_index] {
result.push(left.get(left_index));
left_index += 1;
} else {
result.push(right.get(right_index));
right_index += 1;
}
result_index += 1;
}

while left_index < left.len() {
result.push(left.get(left_index));
left_index += 1;
result_index += 1;
}

while right_index < right.len() {
result.push(right.get(right_index));
right_index += 1;
result_index += 1;
}
}

#[cfg(test)]
mod tests {
use super::*;
use cu_sensor_payloads::PointCloud;
type Cube = PointCloudSoa<72>;
type Sphere = PointCloudSoa<561>;
type Result = PointCloudSoa<{ 72 + 561 }>;
use rerun::Position3D;

fn generate_cube() -> Cube {
let mut cube = Cube::default();
let steps = 5;

let edges = [
// Far face
[(-1.0f32, -1.0f32, -1.0f32), (1.0f32, -1.0f32, -1.0f32)], // Bottom
[(-1.0f32, -1.0f32, -1.0f32), (-1.0f32, 1.0f32, -1.0f32)], // Left
[(1.0f32, -1.0f32, -1.0f32), (1.0f32, 1.0f32, -1.0f32)], // Right
[(-1.0f32, 1.0f32, -1.0f32), (1.0f32, 1.0f32, -1.0f32)], // Top
// Near face
[(-1.0f32, -1.0f32, 1.0f32), (1.0f32, -1.0f32, 1.0f32)], // Bottom
[(-1.0f32, -1.0f32, 1.0f32), (-1.0f32, 1.0f32, 1.0f32)], // Left
[(1.0f32, -1.0f32, 1.0f32), (1.0f32, 1.0f32, 1.0f32)], // Right
[(-1.0f32, 1.0f32, 1.0f32), (1.0f32, 1.0f32, 1.0f32)], // Top
// Connecting edges
[(-1.0f32, -1.0f32, -1.0f32), (-1.0f32, -1.0f32, 1.0f32)],
[(1.0f32, -1.0f32, -1.0f32), (1.0f32, -1.0f32, 1.0f32)],
[(-1.0f32, 1.0f32, -1.0f32), (-1.0f32, 1.0f32, 1.0f32)],
[(1.0f32, 1.0f32, -1.0f32), (1.0f32, 1.0f32, 1.0f32)],
];

let mut ts: CuTime = (72 * 2).into();

for &[(x1, y1, z1), (x2, y2, z2)] in &edges {
for step in 0..=steps {
let t = step as f32 / steps as f32;
let x = x1 + t * (x2 - x1);
let y = y1 + t * (y2 - y1);
let z = z1 + t * (z2 - z1);
ts -= 2u64.into(); // put it in reverse in time to test the ordering
cube.push(PointCloud::new(ts, x, y, z, 1.0f32, None));
}
}

cube
}

fn generate_sphere() -> Sphere {
let mut sphere = Sphere::default();
let num_latitudes = 10;
let num_longitudes = 50;
let mut ts: CuTime = 0.into();

for lat_step in 0..=num_latitudes {
let theta = std::f32::consts::PI * (lat_step as f32 / num_latitudes as f32); // Latitude angle
let sin_theta = theta.sin();
let cos_theta = theta.cos();

for lon_step in 0..=num_longitudes {
let phi = 2.0 * std::f32::consts::PI * (lon_step as f32 / num_longitudes as f32); // Longitude angle
let sin_phi = phi.sin();
let cos_phi = phi.cos();

let x = sin_theta * cos_phi;
let y = sin_theta * sin_phi;
let z = cos_theta;
ts += 1u64.into();
sphere.push(PointCloud::new(ts, x, y, z, 1.0f32, None));
}
}

sphere
}

#[test]
fn test_simple_fusion() {
let l = generate_cube();
let r = generate_sphere();
let mut res = Result::default();
let rec = rerun::RecordingStreamBuilder::new("Ply Visualizer")
.spawn()
.map_err(|e| CuError::new_with_cause("Failed to spawn rerun stream", e))
.unwrap();
fuse_lidar(&l, &r, &mut res).unwrap();

// push res to rerun
let points: Vec<Position3D> = res
.iter()
.map(|p| Position3D::new(p.x.0.value, p.y.0.value, p.z.0.value))
.collect();

rec.log("points", &rerun::Points3D::new(points)).unwrap();

assert_eq!(res.len(), l.len() + r.len());
}

#[test]
fn test_ordering() {
let mut l = generate_cube();
l.sort();
// assert that all tov are in order
for i in 1..l.len() {
assert!(l.tov[i] > l.tov[i - 1]);
}

let mut s = generate_sphere();
s.sort();
// assert that all tov are in order
for i in 1..s.len() {
assert!(s.tov[i] > s.tov[i - 1]);
}
}
#[test]
fn test_mergesort() {
let mut l = generate_cube();
l.sort();
let mut s = generate_sphere();
s.sort();

let mut res = Result::default();
mergesort_lidar(&l, &s, &mut res);
// asset that all tov are in order
for i in 1..res.len() {
assert!(res.tov[i] > res.tov[i - 1]);
}
}
}
2 changes: 1 addition & 1 deletion examples/cu_pointclouds/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repository.workspace = true
[dependencies]
cu29 = { workspace = true }
cu29-helpers = { workspace = true }
rerun = { workspace = true }
tempfile = "3.14.0"
cu-hesai = { path = "../../components/sources/cu_hesai", version = "0.5.1" }
cu-udp-inject = { path = "../../components/testing/cu_udp_inject" }
rerun = "0.20.3"
Loading