Files
code-test-drone/utils/stereo_ranger/config.py
T

124 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""StereoConfig 与 JSON 配置加载(§9 配置参数全量表)。
全部参数配置外置、构造期一次加载并校验:缺项、类型错误、非法取值一律拒绝
进入运行期(fail-fast)。所有默认值均为 ⚠占位值,未整定前不得用于飞行决策
(§12.3 红线 1/2)。
域权威(实现决策):视差域 disparity_min_px / disparity_max_px 为权威,
距离域 z_min_m / z_max_m 由 Z = F·B/D 派生为只读属性——域判定使用视差
(§11.2 直接观测量),两套阈值不再冗余存储、避免互推舍入不一致(§6.4)。
"""
import json
import math
from dataclasses import dataclass, fields
from typing import Any, Dict
# JSON 加载时的整数型字段(其余按数值校验)
_INT_FIELDS = frozenset({"consistency_N_frames", "consistency_M_frames", "filter_reset_frames"})
@dataclass
class StereoConfig:
# ---- 几何组(来自标定 P1/P2,§9.1;⚠全部为占位值)----
fx_px: float = 960.0
fy_px: float = 960.0
cx_px: float = 960.0
cy_px: float = 540.0
baseline_m: float = 0.06
# ---- 匹配组(§9.2)----
tau_y_px: float = 2.0
tau_h_ratio: float = 0.25
disparity_min_px: float = 3.0
disparity_max_px: float = 57.6 # ⚠占位:F=960/B=0.06 下 ⇔ 近界 1.0 m
# ---- 有效性组(§9.3)----
max_delta_z_m: float = 2.0 # 误差预算 δZ(δD = 1px) 上限
consistency_N_frames: int = 3 # 连续跳变降置信度的帧数
consistency_M_frames: int = 5 # 连续异常判 invalid 的帧数
consistency_jump_px: float = 3.0 # ⚠补充项:§9 未列,§6.7 的视差跳变门限
# ---- 滤波组(§9.4)----
smooth_alpha: float = 0.3
jump_reset_m: float = 5.0
filter_reset_frames: int = 5
# ---- 目标隔离(§4:类别 + 框位置最近邻;⚠补充项)----
isolation_gate_px: float = 50.0
def __post_init__(self) -> None:
_require(self.fx_px > 0.0, "fx_px", "必须为正")
_require(self.fy_px > 0.0, "fy_px", "必须为正")
_require(self.cx_px >= 0.0, "cx_px", "必须非负")
_require(self.cy_px >= 0.0, "cy_px", "必须非负")
_require(self.baseline_m > 0.0, "baseline_m", "必须为正")
_require(self.tau_y_px > 0.0, "tau_y_px", "必须为正")
_require(self.tau_h_ratio > 0.0, "tau_h_ratio", "必须为正")
_require(
0.0 < self.disparity_min_px < self.disparity_max_px,
"disparity_min_px/disparity_max_px",
"须满足 0 < 下限 < 上限",
)
_require(self.max_delta_z_m > 0.0, "max_delta_z_m", "必须为正")
_require(self.consistency_N_frames >= 1, "consistency_N_frames", "必须 ≥ 1")
_require(
self.consistency_M_frames >= self.consistency_N_frames,
"consistency_M_frames",
"必须 ≥ consistency_N_frames",
)
_require(self.consistency_jump_px > 0.0, "consistency_jump_px", "必须为正")
_require(0.0 < self.smooth_alpha <= 1.0, "smooth_alpha", "须落在 (0, 1]")
_require(self.jump_reset_m > 0.0, "jump_reset_m", "必须为正")
_require(self.filter_reset_frames >= 1, "filter_reset_frames", "必须 ≥ 1")
_require(self.isolation_gate_px > 0.0, "isolation_gate_px", "必须为正")
@property
def focal_baseline_px_m(self) -> float:
"""F·B:构造期一次算好的常量,热路径只做乘除(§11.1)。"""
return self.fx_px * self.baseline_m
@property
def z_min_m(self) -> float:
"""近界(由 disparity_max_px 派生)。"""
return self.focal_baseline_px_m / self.disparity_max_px
@property
def z_max_m(self) -> float:
"""远界(由 disparity_min_px 派生)。"""
return self.focal_baseline_px_m / self.disparity_min_px
@property
def z_error_budget_max_m(self) -> float:
"""误差预算门限对应的最远距离 sqrt(δZ_max·F·B)(δD = 1px,§7.2)。"""
return math.sqrt(self.max_delta_z_m * self.focal_baseline_px_m)
def load_from_json(path: str) -> StereoConfig:
"""从 JSON 文件构建配置;缺项/未知字段/类型错误/非法取值一律抛 ValueError。"""
with open(path, encoding="utf-8") as handle:
data: Dict[str, Any] = json.load(handle)
if not isinstance(data, dict):
raise ValueError("配置非法: 顶层须为 JSON 对象")
known = {item.name for item in fields(StereoConfig)}
unknown = sorted(set(data) - known)
if unknown:
raise ValueError(f"未知配置字段: {unknown}")
missing = sorted(known - set(data))
if missing:
raise ValueError(f"缺少配置字段: {missing}")
for name, value in data.items():
if isinstance(value, bool):
raise ValueError(f"配置类型非法: {name} 须为数值")
if name in _INT_FIELDS:
if not isinstance(value, int):
raise ValueError(f"配置类型非法: {name} 须为整数")
elif not isinstance(value, (int, float)):
raise ValueError(f"配置类型非法: {name} 须为数值")
return StereoConfig(**data)
def _require(condition: bool, field_name: str, message: str) -> None:
if not condition:
raise ValueError(f"配置非法: {field_name} {message}")