git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
176 lines
5.8 KiB
Rust
176 lines
5.8 KiB
Rust
//! Configuration types for the perception pipeline.
|
|
//!
|
|
//! Provides tuning knobs for scene-graph construction, obstacle detection,
|
|
//! and the top-level perception configuration that bundles them together.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Scene-graph configuration
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Tuning parameters for scene-graph construction from point clouds.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct SceneGraphConfig {
|
|
/// Maximum distance between two points to be considered part of the same
|
|
/// cluster (metres).
|
|
pub cluster_radius: f64,
|
|
/// Minimum number of points required to form a valid cluster / object.
|
|
pub min_cluster_size: usize,
|
|
/// Hard cap on the number of objects the builder will emit.
|
|
pub max_objects: usize,
|
|
/// Maximum centre-to-centre distance for two objects to be connected by an
|
|
/// edge in the scene graph (metres).
|
|
pub edge_distance_threshold: f64,
|
|
}
|
|
|
|
impl Default for SceneGraphConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
cluster_radius: 0.5,
|
|
min_cluster_size: 3,
|
|
max_objects: 256,
|
|
edge_distance_threshold: 5.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Obstacle configuration
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Tuning parameters for the obstacle detector.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct ObstacleConfig {
|
|
/// Minimum number of points that must fall inside a cluster to be
|
|
/// considered an obstacle.
|
|
pub min_obstacle_size: usize,
|
|
/// Maximum range from the robot within which obstacles are detected
|
|
/// (metres).
|
|
pub max_detection_range: f64,
|
|
/// Extra padding added around every detected obstacle (metres).
|
|
pub safety_margin: f64,
|
|
}
|
|
|
|
impl Default for ObstacleConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
min_obstacle_size: 3,
|
|
max_detection_range: 20.0,
|
|
safety_margin: 0.2,
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Top-level perception configuration
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Aggregated configuration for the full perception pipeline.
|
|
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
|
pub struct PerceptionConfig {
|
|
pub scene_graph: SceneGraphConfig,
|
|
pub obstacle: ObstacleConfig,
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_scene_graph_config_defaults() {
|
|
let cfg = SceneGraphConfig::default();
|
|
assert!((cfg.cluster_radius - 0.5).abs() < f64::EPSILON);
|
|
assert_eq!(cfg.min_cluster_size, 3);
|
|
assert_eq!(cfg.max_objects, 256);
|
|
assert!((cfg.edge_distance_threshold - 5.0).abs() < f64::EPSILON);
|
|
}
|
|
|
|
#[test]
|
|
fn test_obstacle_config_defaults() {
|
|
let cfg = ObstacleConfig::default();
|
|
assert_eq!(cfg.min_obstacle_size, 3);
|
|
assert!((cfg.max_detection_range - 20.0).abs() < f64::EPSILON);
|
|
assert!((cfg.safety_margin - 0.2).abs() < f64::EPSILON);
|
|
}
|
|
|
|
#[test]
|
|
fn test_perception_config_defaults() {
|
|
let cfg = PerceptionConfig::default();
|
|
assert_eq!(cfg.scene_graph, SceneGraphConfig::default());
|
|
assert_eq!(cfg.obstacle, ObstacleConfig::default());
|
|
}
|
|
|
|
#[test]
|
|
fn test_scene_graph_config_serde_roundtrip() {
|
|
let cfg = SceneGraphConfig {
|
|
cluster_radius: 1.0,
|
|
min_cluster_size: 5,
|
|
max_objects: 128,
|
|
edge_distance_threshold: 3.0,
|
|
};
|
|
let json = serde_json::to_string(&cfg).unwrap();
|
|
let restored: SceneGraphConfig = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(cfg, restored);
|
|
}
|
|
|
|
#[test]
|
|
fn test_obstacle_config_serde_roundtrip() {
|
|
let cfg = ObstacleConfig {
|
|
min_obstacle_size: 10,
|
|
max_detection_range: 50.0,
|
|
safety_margin: 0.5,
|
|
};
|
|
let json = serde_json::to_string(&cfg).unwrap();
|
|
let restored: ObstacleConfig = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(cfg, restored);
|
|
}
|
|
|
|
#[test]
|
|
fn test_perception_config_serde_roundtrip() {
|
|
let cfg = PerceptionConfig::default();
|
|
let json = serde_json::to_string_pretty(&cfg).unwrap();
|
|
let restored: PerceptionConfig = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(cfg, restored);
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_clone_equality() {
|
|
let a = PerceptionConfig::default();
|
|
let b = a.clone();
|
|
assert_eq!(a, b);
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_debug_format() {
|
|
let cfg = PerceptionConfig::default();
|
|
let dbg = format!("{:?}", cfg);
|
|
assert!(dbg.contains("PerceptionConfig"));
|
|
assert!(dbg.contains("SceneGraphConfig"));
|
|
assert!(dbg.contains("ObstacleConfig"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_custom_perception_config() {
|
|
let cfg = PerceptionConfig {
|
|
scene_graph: SceneGraphConfig {
|
|
cluster_radius: 2.0,
|
|
min_cluster_size: 10,
|
|
max_objects: 64,
|
|
edge_distance_threshold: 10.0,
|
|
},
|
|
obstacle: ObstacleConfig {
|
|
min_obstacle_size: 5,
|
|
max_detection_range: 100.0,
|
|
safety_margin: 1.0,
|
|
},
|
|
};
|
|
assert!((cfg.scene_graph.cluster_radius - 2.0).abs() < f64::EPSILON);
|
|
assert_eq!(cfg.obstacle.min_obstacle_size, 5);
|
|
}
|
|
}
|