File size: 9,363 Bytes
51defdc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | # SPDX-License-Identifier: Apache-2.0
"""Compile-time constants of the METEOR port (``meteor_v157c3Z.onnx``, AutowareFoundation/meteor@v1.0).
Every value here is either a static shape of the deployed graph or a constant baked into it; the source of each is
the ONNX node it comes from (``research/meteor/SPEC.md``, cited as ``S:<line>``) or METEOR's runtime
(``tier4/METEOR`` @ ``dc193a81e995``, ``deploy/cpp``). Changing one of them means a different graph, a new trace and
a new image (PLAN.md section 0.4, class COMPILE). Host post-processing defaults (class RT-host) live in
``host/postprocess.py`` (``PostConfig``); load-time options (``input_norm``, ``depth_mean_bins``) in
:class:`MeteorConfig`.
"""
from __future__ import annotations
import math
from dataclasses import dataclass, field
from typing import Tuple
__all__ = [
"ONNX_FILE", "ONNX_SHA256", "PARAM_YAML", "HF_REPO", "HF_TAG", "HF_REVISION", "METEOR_UPSTREAM_COMMIT",
"CAMERAS", "N_CAMS", "IMG_H", "IMG_W", "FEAT_H", "FEAT_W", "FPN_C", "CTX_C", "BEV_C", "BEV_H", "BEV_W",
"BEV_RES", "BEV_XF", "BEV_YH", "LIFT_H", "LIFT_W", "LIFT_RES", "DET_H", "DET_W", "DET_RES", "DEPTH_BINS",
"DEPTH_MIN", "DEPTH_STEP", "DEPTH_LOG_MAX", "LANE_CLASSES", "SEG2D_CLASSES", "DET2D_CLASSES", "DET3D_CLASSES",
"OCC_CLASSES", "TL_CLASSES", "DET2D_STRIDES", "PAINT_CLASSES", "OCC_CROP", "RISK_CROP", "EGO_MODES", "EGO_STEPS",
"EGO_DT", "EGO_DIM", "TRAJ_C", "DONOR", "IMAGENET_MEAN", "IMAGENET_STD", "INPUT_NORMS", "DEPTH_MEAN_BINS",
"MeteorConfig", "default_config", "depth_bin_centres",
]
# ---- artifact pins (S:99-118) ---------------------------------------------------------------------------------------
ONNX_FILE = "meteor_v157c3Z.onnx"
ONNX_SHA256 = "50397b4df76d33df2a71e8063b38d164399255008e1f0758c2a83675a200b1a7"
PARAM_YAML = "meteor_v157.param.yaml"
HF_REPO = "AutowareFoundation/meteor"
HF_TAG = "v1.0"
HF_REVISION = "01a5f6d71df5ecbbb5853ec600825481d57b9c6b"
METEOR_UPSTREAM_COMMIT = "dc193a81e9959de57b751e269127ae9b67e3f300" # tier4/METEOR, defines pre/post (no Autoware pkg)
# ---- cameras and image branch (param.yaml:10, S:136-140) ------------------------------------------------------------
CAMERAS: Tuple[str, ...] = ("CAM_FRONT_WIDE", "CAM_FRONT_LEFT", "CAM_FRONT_RIGHT", "CAM_BACK_WIDE", "CAM_BACK_LEFT",
"CAM_BACK_RIGHT", "CAM_FRONT_NARROW", "CAM_BACK_NARROW")
N_CAMS = 8
IMG_H, IMG_W = 432, 768 # network input per camera (the graph normalises u by 767, v by 431)
FEAT_H, FEAT_W = 108, 192 # stride-4 FPN map
FPN_C = 160 # FPN width
CTX_C = 96 # painted context channels lifted to the BEV
# the absent-camera donor rule of training (bevlane/dataset.py:17-21): zero image + the donor's K and pose
DONOR = {"CAM_BACK_NARROW": "CAM_BACK_WIDE", "CAM_FRONT_NARROW": "CAM_FRONT_WIDE"}
# ---- BEV (model.py:34-47, 82-89, 877-879; S:155-160) ----------------------------------------------------------------
BEV_C = 96
BEV_H, BEV_W = 800, 500 # rows: x = +80 .. -80 m, cols: y = +50 .. -50 m
BEV_RES = 0.2
BEV_XF, BEV_YH = 80.0, 50.0 # forward extent, half width
LIFT_H, LIFT_W = 400, 250 # 0.4 m lift grid (V52, LIFT_DIV 2): x 79.8 .. -79.8, y 49.8 .. -49.8
LIFT_RES = 0.4
DET_H, DET_W = 400, 250 # 3D detection / traj / stationary / risk grids
DET_RES = 0.4
# ---- depth bins (S:325-336; SPEC section 10 risk 2) -----------------------------------------------------------------
DEPTH_BINS = 64
DEPTH_MIN, DEPTH_STEP = 1.0, 1.25 # LINEAR bins of the lift and the trained head: 1.0 + 1.25 * b metres
DEPTH_LOG_MAX = 4.378896236419678 # ln(79.75) as the float32 constant of /Div_1: depth_mean's log-spaced centres
LIFT_MIN_Z = 0.5 # camera z clamp and validity threshold
LIFT_MAX_DIST = 90.0 # validity: Euclidean range from the camera < 90 m
LIFT_BIN_CLIP = 62.9999 # clip of the continuous bin index (/net/Clip_4, float32 62.99990081787109)
LIFT_W_EPS = 0.05 # + 0.05 on the interpolated bin probability (/net/Add_10)
LIFT_DEN_EPS = 1e-4 # max(sum_n w, 1e-4) (/net/Clip_6)
# ---- heads -----------------------------------------------------------------------------------------------------
LANE_CLASSES = ("bg", "road", "sidewalk", "crosswalk", "laneline", "stopline", "road_edge", "marking", "parking")
SEG2D_CLASSES = ("bg", "obstacle", "car", "truck", "bus", "motorcycle", "bicycle", "pedestrian", "marking",
"traffic_light", "sign", "road", "sidewalk", "lane_stop_line", "crosswalk", "unused", "wall",
"building", "vegetation", "sky", "pole") # comlops-21cls-autolabel-2504.csv (0, 15 unused)
DET2D_CLASSES = ("obs", "car", "truck", "bus", "bicycle", "motorcycle", "pedestrian", "road_paint", "traffic_light",
"traffic_sign") # comlops-instance-2510.csv
DET3D_CLASSES = ("vehicle", "vru")
OCC_CLASSES = ("free", "obstacle", "vehicle", "two_wheeler", "pedestrian", "road", "sidewalk", "vegetation",
"building_wall", "pole_sign_light") # extract_occ.py:4-8; 16 z-bins of 0.4 m from -1 m
OCC_Z_BINS = 16
TL_CLASSES = ("none", "green", "yellow", "red")
DET2D_STRIDES = (4, 8, 16)
PAINT_CLASSES = (2, 3, 4, 5, 6, 7, 8, 13) # seg2d classes painted into the context (/net/Gather indices; checked)
SEG2D_CLAMP = 30.0 # +-30 clamp, ONLY before the seg2d argmax (the paint softmax is unclamped)
OCC_CROP = ((200, 600), (50, 450)) # rows, cols of the RAW BEV (/net/Slice_8, Slice_9)
RISK_CROP = ((200, 600), (125, 375)) # rows, cols of the FUSED BEV (/net/Slice_11, Slice_12)
TGATE_SCALE = 4.0 # fused = bev + tfuse(bev * 4 * softmax(tgate)[0]) (/net/Mul_12)
# ---- planner (S:315, 4.4) --------------------------------------------------------------------------------------
EGO_MODES, EGO_STEPS, EGO_DT = 3, 6, 0.5
EGO_DIM = 42 # [0:36] 3 paths x 6 x (x, y); [36:39] mode logits; 39 steer, 40 accel, 41 brake
TRAJ_C = 39 # per-cell agent futures: 3 modes x 6 x (dx, dy) + 3 mode logits
V0_SCALE = 15.0 # kin_delta input [v0 / 15, 0, ..., 0] (7 wide)
KIN_DIM = 7
DEC_SPEED_MAX = 25.0 # decoupled decoder: v = min(softplus(.), 25)
RISK_X_HALF, RISK_Y_HALF = 40.0, 25.0 # risk grid +-40 m x +-25 m at 0.2 m (400 x 250)
E2E_LN_EPS = 1e-5 # LayerNormalization(139) epsilon (float32 9.99999974738e-06)
E2E_SCALE = 3.0 # ego[:36] += 3 * tanh(mlp(LN(...)))
SEG_REFINE_CLAMP, SEG_REFINE_SCALE = 20.0, 8.0 # lane_logit = clamp(lane_pre, +-20) + 8 tanh(res / 8)
BOX_HM_CLAMP, BOX_REG_CLAMP, BOX_REFINE_SCALE = 15.0, 20.0, 6.0
# ---- input normalisation (D12; S:209-229, section 10 risk 1) ---------------------------------------------------------
IMAGENET_MEAN = (0.485, 0.456, 0.406)
IMAGENET_STD = (0.229, 0.224, 0.225)
INPUT_NORMS = ("onnx", "imagenet") # onnx: x / 255 as released (default); imagenet: (x / 255 - mean) / std
DEPTH_MEAN_BINS = ("log", "linear") # depth_mean bin centres: log as exported (default, parity) or linear (trained)
def depth_bin_centres(kind: str = "log"):
"""The 64 bin centres of ``depth_mean`` in metres: ``log`` = exp(r * ln(79.75) / 63) in float32 exactly as the
graph (/Mul_1 -> /Exp), ``linear`` = 1 + 1.25 r (the bins the head and the lift were trained with)."""
import numpy as np
r = np.arange(DEPTH_BINS, dtype=np.float32)
if kind == "log":
step = np.float32(DEPTH_LOG_MAX) / np.float32(DEPTH_BINS - 1)
return np.exp(r * step + np.float32(0.0)).astype(np.float32)
if kind == "linear":
return (np.float32(DEPTH_MIN) + np.float32(DEPTH_STEP) * r).astype(np.float32)
raise ValueError(f"depth_mean bins must be one of {DEPTH_MEAN_BINS}, not {kind!r}")
@dataclass(frozen=True)
class MeteorConfig:
"""Load-time options of the reference (and of the TT port, ``METEOR_INPUT_NORM`` / ``METEOR_DEPTH_MEAN_BINS``).
``input_norm="onnx"`` reproduces the released graph (only /255; D12 default, validated against ORT);
``"imagenet"`` adds the training normalisation after /255 (SPEC section 10 risk 1; an in-memory patched ONNX is
its oracle). ``depth_mean_bins="log"`` keeps ONNX parity, ``"linear"`` gives metric depth as trained."""
input_norm: str = "onnx"
depth_mean_bins: str = "log"
cameras: Tuple[str, ...] = field(default=CAMERAS)
def check(self) -> "MeteorConfig":
if self.input_norm not in INPUT_NORMS:
raise ValueError(f"input_norm must be one of {INPUT_NORMS}, not {self.input_norm!r}")
if self.depth_mean_bins not in DEPTH_MEAN_BINS:
raise ValueError(f"depth_mean_bins must be one of {DEPTH_MEAN_BINS}, not {self.depth_mean_bins!r}")
if tuple(self.cameras) != CAMERAS:
raise ValueError("the camera order is fixed by training (param.yaml:10)")
return self
def default_config(**overrides) -> MeteorConfig:
return MeteorConfig(**overrides).check()
# sanity: the log centres span 1 .. 79.75 m (S:183)
assert abs(math.exp(DEPTH_LOG_MAX) - 79.75) < 1e-4
|