File size: 6,616 Bytes
4d9b003 | 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 | # SPDX-License-Identifier: Apache-2.0
"""A stand-in for :class:`tt_diffusion_planner.api.DiffusionPlanner` with no device and no weights, for the host tests
of the server and the smoke test. It quacks like the real model (``validate_params``, ``__call__``, ``info``,
``device_info``, ``close``) and runs the bundle's REAL host code around a fake network: the inputs are checked
against ``INPUT_SCHEMA`` and pre-processed by ``host.prepare`` (normalization from the v5.0 statistics embedded below),
the "network" extrapolates every agent's current state at a constant normalised speed, and ``host.make_output``
post-processes it, so the output depends on the input deterministically and exercises the real post-processing."""
from __future__ import annotations
from typing import Any, Dict, Optional
import numpy as np
from tt_diffusion_planner import io as tio
from tt_diffusion_planner.api import DiffusionPlanner, Output
from tt_diffusion_planner.host import pipeline as hp
from tt_diffusion_planner.reference import config as C
from tt_diffusion_planner.reference.weights import Normalization
ETH_P150 = {"dispatch": "eth", "dispatch_requested": "eth", "fallback": None, "grid": "12x10", "grid_x": 12,
"grid_y": 10, "cores": 120, "arch": "blackhole", "eth_patch": True, "device_id": 0,
"num_command_queues": DiffusionPlanner.DEVICE_DEFAULTS["num_command_queues"]}
def v5_normalization() -> Normalization:
"""The v5.0 ``observation_normalizer`` / ``state_normalizer`` (HF diffusion_planner.param.json; SPEC 3.7), so the
stub needs no weights download."""
def mv(mean, std):
return np.asarray(mean, np.float32), np.asarray(std, np.float32)
lane_std = [20.0] * 8 + [1.0] * 25
obs = {"ego_agent_past": mv([10, 0, 0, 0], [20, 20, 1, 1]),
"ego_current_state": mv([10] + [0] * 9, [20, 20, 1, 1, 20, 20, 20, 20, 1, 1]),
"neighbor_agents_past": mv([10] + [0] * 10, [20, 20, 1, 1, 20, 20, 20, 20, 1, 1, 1]),
"static_objects": mv([10] + [0] * 9, [20, 20, 1, 1, 20, 20, 1, 1, 1, 1]),
"lanes": mv([10] + [0] * 32, lane_std), "route_lanes": mv([10] + [0] * 32, lane_std),
"lanes_speed_limit": mv([0], [20]), "route_lanes_speed_limit": mv([0], [20]),
"polygons": mv([10, 0, 0], [20, 20, 1]), "line_strings": mv([10, 0, 0, 0], [20, 20, 1, 1]),
"goal_pose": mv([10, 0, 0, 0], [20, 20, 1, 1])}
mean = np.tile(np.float32([10, 0, 0, 0]), (C.MAX_NUM_AGENTS, 1, 1))
std = np.tile(np.float32([20, 20, 1, 1]), (C.MAX_NUM_AGENTS, 1, 1))
return Normalization(obs, mean, std, 5)
class StubModel:
"""``StubModel(cfg)`` is a valid ``app.state.ttaw.model_factory`` (``cfg`` = ``config_from_env`` output)."""
device_info: Dict[str, Any] = ETH_P150
validate_params = DiffusionPlanner.validate_params
normalization = v5_normalization()
def __init__(self, cfg: Optional[Dict[str, Any]] = None):
self.cfg = dict(cfg or {})
self.closed = False
@property
def info(self) -> Dict[str, Any]:
cls = DiffusionPlanner
return {"model": cls.MODEL_NAME, "variant": self.cfg.get("variant") or cls.DEFAULT_VARIANT,
"weights": {"repo": self.cfg.get("model_id") or cls.DEFAULT_REPO,
"revision": self.cfg.get("revision") or cls.DEFAULT_REVISION},
"device": self.device_info, "input_kind": cls.INPUT_KIND, "point_fields": list(cls.POINT_FIELDS),
"camera_order": list(cls.CAMERA_ORDER), "labels": list(cls.LABELS),
"runtime_params": {k: v[3] for k, v in cls.RUNTIME_PARAMS.items()},
"input_schema": {k: {"shape": list(s), "dtype": "float32"} for k, (s, _) in cls.INPUT_SCHEMA.items()}}
@staticmethod
def fake_network(prep: hp.Prepared) -> Dict[str, np.ndarray]:
"""Every agent moves along its current heading at 0.25 normalised units (5 m) per second."""
cs = prep.decoder.current_states # [321, 4] normalised
t = np.arange(C.OUTPUT_T + 1, dtype=np.float32)[None, :, None] * np.float32(0.025)
x0 = np.repeat(cs[:, None, :], C.OUTPUT_T + 1, axis=1).astype(np.float32)
x0[..., 0:1] += t * cs[:, None, 2:3]
x0[..., 1:2] += t * cs[:, None, 3:4]
logit = np.array([-4.0, 1.0 + float(np.tanh(cs[0, 0])), 0.5, 0.25, 0.0], np.float32)
return {"final_x0": x0, "logit": logit}
def __call__(self, points: Any = None, *, inputs: Any = None, **params: Any) -> Output:
p = self.validate_params(params)
if points is not None:
raise tio.InputError("this model takes only `inputs` (the planner tensors), not ['points']")
if inputs is None:
raise tio.InputError("this model needs `inputs`: the 15 planner tensors of INPUT_SCHEMA")
arrays = tio.load_inputs(inputs)
prep = hp.prepare(arrays, self.normalization.observation)
raw = self.fake_network(prep)
return hp.make_output(raw["final_x0"], raw["logit"], prep, self.normalization, p,
model=DiffusionPlanner.MODEL_NAME, timing_ms={"device": 1.0})
def close(self) -> None:
self.closed = True
def sample_inputs(seed: int = 0, n_neighbors: int = 3) -> Dict[str, np.ndarray]:
"""A small synthetic planner scene in the raw (pre-normalization) layout of INPUT_SCHEMA."""
rng = np.random.default_rng(seed)
raw = {k: np.zeros(s, np.float32) for k, s in C.INPUT_SHAPES.items()}
t = np.arange(C.INPUT_T + 1, dtype=np.float32)
raw["ego_agent_past"][0, :, 0] = (t - C.INPUT_T) * 0.8
raw["ego_agent_past"][0, :, 2] = 1.0
raw["ego_current_state"][0] = (0, 0, 1, 0, 8.0, 0, 0.2, 0, 0, 0)
for i in range(n_neighbors):
x0, y0, v = float(rng.uniform(5, 40)), float(rng.choice([-3.5, 0.0, 3.5])), float(rng.uniform(2, 10))
nb = raw["neighbor_agents_past"][0, i]
nb[:, 0], nb[:, 1], nb[:, 2] = x0 + (t - C.INPUT_T) * v * 0.1, y0, 1.0
nb[:, 4], nb[:, 6], nb[:, 7], nb[:, 8] = v, 1.8, 4.5, 1.0
for k in range(4):
lane = raw["lanes"][0, k]
lane[:, 0], lane[:, 1] = np.linspace(-20 + 20 * k, 20 * k, 20), 0.0
lane[:19, 2] = lane[1:, 0] - lane[:-1, 0]
lane[:, 5], lane[:, 7], lane[:, 12] = 1.75, -1.75, 1.0
raw["lanes_speed_limit"][0, k, 0] = 13.9
raw["route_lanes"][0, :4] = raw["lanes"][0, :4]
raw["route_lanes_speed_limit"][0, :4] = 13.9
raw["goal_pose"][0] = (150.0, 0.0, 1.0, 0.0)
raw["ego_shape"][0] = (2.79, 4.89, 1.896)
raw["turn_indicators"][:] = 1.0
return raw
|