feat: initial commit - massive-cloud point cloud processing system

This commit is contained in:
2026-05-27 09:51:42 +00:00
commit 16811100d1
391 changed files with 78338 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
# 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
```bash
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
1. **Edit `config/measurements.yaml`** - add task definition with appropriate type
2. **Set `enabled: true`** and provide required parameters for that type
3. **Run `python main.py`** - the task will execute automatically
4. **Check Excel results** - verify mean, std dev, inlier ratio
5. **Inspect PLY files** - confirm ROI extraction is correct
### Adding a new measurement type
1. **Create `src/measurements/your_type.py`** inheriting from `BaseMeasurement`
2. **Implement `execute() -> MeasurementResult`** with your algorithm
3. **Register in `src/measurements/__init__.py`** - add to `__all__`
4. **Update `src/task_manager.py`** - add branch in `_create_measurement_task()`
5. **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 `type` field
- 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_ratio` in 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) vs `bbox_filtered` (extract then filter)
- `exact_points` is faster but requires knowing precise coordinates
- `bbox_filtered` supports filters like `max_abs_coordinate` for 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:**
1. Disable all tasks except one test case (`enabled: false`)
2. Run and verify Excel output matches expected values
3. Open PLY files in CloudCompare/MeshLab to visually verify ROI extraction
4. Check console output for warnings (low inlier ratio, insufficient points)
**When adding new measurement types:**
1. Start with a simple test case with known ground truth
2. Verify the `MeasurementResult.data` dictionary contains all required fields
3. Ensure visualization saves the relevant points (not the entire ROI)
4. 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.py` to 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.py` to accept per-task `pointcloud_file` override
- Update `pointcloud_loader.py` to cache multiple files by path
**Want to add 2D projections or cross-sections?**
- Create new measurement type `cross_section_profile`
- Use `roi_extractor` to get 3D points, then project to 2D plane
- Output profile data as additional sheet in Excel