Files
massive-cloud/alg/delivery_v1.2.1/underframe_deflection/cli.py
lipan 29f1d45d66 feat: 算法升级、整车配准集成与性能分析文档
算法更新: delivery v1.2.1、平整度v1.5、测距v2.2、整车配准v1.3
适配器: vehicle-registration 新增、各算法 manifest 和 adapter 更新
后端: taskCadFiles/source 字段迁移、derivedPointClouds 注册、取消任务联动
前端: ElapsedTime 时长显示、两步算法流程、canRun 逻辑修复
文档: 新增5份算法性能分析报告
2026-06-25 08:22:20 +00:00

200 lines
6.2 KiB
Python

"""Command-line entry point installed by ``pyproject.toml``.
The DEFAULT mode is single-beam measurement: the ROI(s) already isolate one
beam each, the cloud is cropped and measured directly (no registration, no
dual-beam tracking). Provide ``--roi`` for one beam, or ``--left-roi`` and
``--right-roi`` to measure both in one invocation.
The legacy dual-beam pipeline (registration + left/right tracking on a
car-bottom slab) is still available via ``--mode dual``.
"""
from __future__ import annotations
import argparse
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Measure underframe longitudinal beam deflection from a point cloud.",
)
parser.add_argument(
"--mode",
default="single",
choices=["single", "dual"],
help="'single' (default): ROI isolates one beam, measure directly. "
"'dual': legacy car-bottom slab with left+right tracking + registration.",
)
parser.add_argument(
"--input-cloud",
required=True,
help="Path to input scan point cloud (.ply or .xyz). Large binary PLYs "
"are stream-cropped by the ROI in single mode.",
)
parser.add_argument(
"--out-dir",
required=True,
help="Output directory for report.json/.xlsx, CSV, and PNG.",
)
# --- single mode ---
parser.add_argument(
"--roi",
default=None,
help="Single mode: ROI JSON isolating ONE beam (native frame "
"x=length, y=height, z=width).",
)
parser.add_argument(
"--left-roi",
default=None,
help="Single mode: ROI JSON for the left beam (use with --right-roi).",
)
parser.add_argument(
"--right-roi",
default=None,
help="Single mode: ROI JSON for the right beam (use with --left-roi).",
)
parser.add_argument(
"--side",
default=None,
help="Single mode label for --roi (left/right); inferred from the ROI's "
"lateral z (x-forward: z>0 left, z<0 right) if omitted.",
)
parser.add_argument(
"--tolerance",
type=float,
default=None,
help="Single mode --roi: pass/fail tolerance in mm. Default by side "
"(left 7, right 12).",
)
parser.add_argument(
"--left-tolerance",
type=float,
default=None,
help="Both-beam mode: left-beam tolerance in mm (default 7).",
)
parser.add_argument(
"--right-tolerance",
type=float,
default=None,
help="Both-beam mode: right-beam tolerance in mm (default 12).",
)
parser.add_argument(
"--enable-registration",
action="store_true",
help="Single mode: rigidly align the cloud to --cad before measuring. "
"Default OFF (cloud assumed already registered). Requires --cad as a "
"sampled .ply/.stl/.obj (.stp must be converted first).",
)
# --- shared ---
parser.add_argument(
"--config",
default=None,
help="Optional JSON config override file.",
)
parser.add_argument(
"--skip-excel",
action="store_true",
help="Skip writing the .xlsx report.",
)
parser.add_argument(
"--skip-plot",
action="store_true",
help="Single mode: skip the PNG plot.",
)
# --- dual (legacy) mode ---
parser.add_argument(
"--cad",
default=None,
help="Dual mode: CAD reference (.ply/.xyz/.stl/.obj) for registration.",
)
parser.add_argument(
"--profile",
default="auto",
choices=["auto", "M", "TC", "TP", "custom"],
help="Dual mode: profile for scale-related tuning.",
)
parser.add_argument(
"--skip-registration",
action="store_true",
help="Dual mode: bypass ICP even when --cad is supplied.",
)
parser.add_argument(
"--no-bilateral",
action="store_true",
help="Dual mode: disable bilateral denoising.",
)
parser.add_argument(
"--input-axis",
default="y_up",
choices=["z_up", "y_up"],
help="Dual mode: height axis convention (default y_up).",
)
return parser.parse_args()
def _load_config(path):
if not path:
return None
import json
from pathlib import Path
return json.loads(Path(path).read_text(encoding="utf-8"))
def main() -> None:
args = parse_args()
if args.mode == "single":
from .single_beam_pipeline import run_single_beam_pipeline, run_both_beams
config = _load_config(args.config)
if args.left_roi and args.right_roi:
run_both_beams(
input_cloud=args.input_cloud,
left_roi=args.left_roi,
right_roi=args.right_roi,
out_dir=args.out_dir,
config=config,
cad_path=args.cad,
enable_registration=args.enable_registration,
left_tolerance_mm=args.left_tolerance,
right_tolerance_mm=args.right_tolerance,
skip_excel=args.skip_excel,
skip_plot=args.skip_plot,
)
elif args.roi:
run_single_beam_pipeline(
input_cloud=args.input_cloud,
roi_path=args.roi,
out_dir=args.out_dir,
side=args.side,
tolerance_mm=args.tolerance,
config=config,
cad_path=args.cad,
enable_registration=args.enable_registration,
skip_excel=args.skip_excel,
skip_plot=args.skip_plot,
)
else:
raise SystemExit(
"single mode requires --roi (one beam) or both --left-roi and --right-roi."
)
return
# dual (legacy) mode
from .pipeline import run_measurement_pipeline
run_measurement_pipeline(
input_cloud=args.input_cloud,
out_dir=args.out_dir,
config_path=args.config,
profile=args.profile,
cad_path=args.cad,
roi_path=args.roi,
skip_registration=args.skip_registration,
enable_bilateral=not args.no_bilateral,
skip_excel=args.skip_excel,
input_axis=args.input_axis,
)
if __name__ == "__main__":
main()