5.9 KiB
5.9 KiB
Train Measurement System - Developer Guide
Architecture Overview
This is a configuration-driven, plugin-based point cloud measurement system for train inspection. The core design principle: measurements are defined in YAML, not hardcoded in Python.
Key Design Decisions
Why configuration-driven?
- Users need to measure 20+ different features per train scan
- Measurement locations are consistent across scans but vary by train model
- Non-programmers should be able to add new measurement tasks by editing YAML
Why plugin-based measurement types?
- Three fundamentally different measurement algorithms: plane-to-plane, point-to-plane, point-to-point
- Each type has different input requirements (ROI bboxes vs exact coordinates)
- Easy to add new types (e.g., circle-to-circle) without modifying core logic
Why single large point cloud input?
- Train scans are 100M points covering the entire vehicle
- ROI extraction happens at runtime based on bbox parameters
- Avoids pre-segmentation and maintains spatial context
Common Development Tasks
Running measurements
conda activate pcc-experiment
cd train_measurement_system
python main.py
Results: results/measurement_results_YYYYMMDD_HHMMSS.xlsx
Visualizations: visualizations/*.ply
Adding a new measurement task
- Edit
config/measurements.yaml- add task definition with appropriate type - Set
enabled: trueand provide required parameters for that type - Run
python main.py- the task will execute automatically - Check Excel results - verify mean, std dev, inlier ratio
- Inspect PLY files - confirm ROI extraction is correct
Adding a new measurement type
- Create
src/measurements/your_type.pyinheriting fromBaseMeasurement - Implement
execute() -> MeasurementResultwith your algorithm - Register in
src/measurements/__init__.py- add to__all__ - Update
src/task_manager.py- add branch in_create_measurement_task() - Document in README.md - add config example and parameter descriptions
Architecture Flow
YAML Config → TaskManager → [Measurement Plugin] → Excel + PLY
↓
PointCloudLoader (cached)
↓
ROIExtractor / PlaneFitter (shared utilities)
TaskManager orchestrates everything:
- Loads point cloud once, caches in memory
- Iterates through enabled tasks
- Instantiates appropriate measurement class based on
typefield - Collects results and delegates to ExcelExporter
Measurement plugins are stateless:
- Receive full point cloud + task config in constructor
- Extract ROI, run algorithm, return
MeasurementResult - No shared state between tasks
Critical Implementation Details
Coordinate System
- Point cloud uses global coordinates (mm units)
- Z-axis is vertical (up positive)
- ROI bboxes are in global coordinates
- No coordinate transformations applied during extraction
RANSAC Plane Fitting
inlier_ratioin results indicates ROI quality, not measurement accuracy- 100% inlier ratio = ROI perfectly contains only the target plane
- Low inlier ratio (<70%) = ROI includes multiple surfaces or noise
- Automatic downsampling: ROIs >10k points are sampled to 10k before SVD fitting (prevents memory explosion)
Point-to-Plane Distance
- Two modes:
exact_points(KDTree nearest neighbor) vsbbox_filtered(extract then filter) exact_pointsis faster but requires knowing precise coordinatesbbox_filteredsupports filters likemax_abs_coordinatefor finding extreme points
Plane-to-Plane Distance
- Unidirectional sampling: samples points from source plane, measures to target plane
- Not symmetric: swapping source/target may give different results if planes aren't parallel
- Standard deviation indicates parallelism quality
Testing Strategy
When modifying measurement algorithms:
- Disable all tasks except one test case (
enabled: false) - Run and verify Excel output matches expected values
- Open PLY files in CloudCompare/MeshLab to visually verify ROI extraction
- Check console output for warnings (low inlier ratio, insufficient points)
When adding new measurement types:
- Start with a simple test case with known ground truth
- Verify the
MeasurementResult.datadictionary contains all required fields - Ensure visualization saves the relevant points (not the entire ROI)
- Test with edge cases: empty ROI, single point, coplanar points
Common Pitfalls
- Forgetting to set
enabled: true- task silently skipped - Wrong coordinate order - YAML uses
[x, y, z], some tools use[z, y, x] - Bbox too large - includes multiple surfaces, low inlier ratio
- Bbox too small - insufficient points for robust fitting
- Missing
visualization_dir- no PLY files generated (optional but recommended) - Modifying cached point cloud - reload by restarting Python process
Performance Notes
- Point cloud loading: ~10-30 seconds for 100M points (cached after first load)
- Single plane-to-plane task: ~5-10 seconds (ROI extraction + RANSAC + sampling)
- Bottleneck is usually RANSAC iterations, not point cloud I/O
- Visualization (PLY save) adds ~1-2 seconds per task
Extension Points
Want to add CAD-based ROI extraction?
- Modify
roi_extractor.pyto accept plane parameters from CAD - Keep bbox-based extraction as fallback
- Add
roi_source: "cad"vs"bbox"in config
Want to add statistical outlier removal?
- Add preprocessing step in measurement plugins before RANSAC
- Make it optional via
params.remove_outliers: true
Want to support multiple point cloud files?
- Modify
task_manager.pyto accept per-taskpointcloud_fileoverride - Update
pointcloud_loader.pyto cache multiple files by path
Want to add 2D projections or cross-sections?
- Create new measurement type
cross_section_profile - Use
roi_extractorto get 3D points, then project to 2D plane - Output profile data as additional sheet in Excel