bevformer-p150 on Tenstorrent p150

Derived from changh95/bevformer-small-weights (commit 0e6d9d9), a re-hosting as safetensors of the official bevformer_small_epoch_24 checkpoint that the BEVFormer authors publish with fundamentalvision/BEVFormer: the BEVFormer-small network of Autoware's autoware_tensorrt_bevformer camera 3D detector, with unmodified weights. BEVFormer: Li et al., arXiv:2203.17270. See the original model card for training details.

BEVFormer-small, a 59.5M-parameter camera-only 3D detector (ResNet-101 with deformable convolutions in stages 3 and 4 and a one-level FPN, a 3-layer spatiotemporal BEV encoder over a 150x150 bird's-eye view that attends to the previous frame's BEV and to the six cameras, a 6-layer DETR decoder with 900 queries), run on six 1280x736 network inputs (about 1.97 TFLOPs per frame) through the graph of the TensorRT export Autoware deploys. Six surround-view camera images with their calibration and the ego pose go in. Out come 3D boxes with scores and velocities for the 10 nuScenes classes and the Autoware label of each, with the pre- and post-processing of the autoware_tensorrt_bevformer node (resize to 1600x900, normalisation, camera geometry, top-300 decode, label mapping). Each stream keeps its previous BEV on the device as temporal history.

Runs on Tenstorrent p150 — see the serving profiles below.

Packaged and published with tt-model-manager 0.1.0 (manifest schema 5.1).

At a glance

Hardware p150
License apache-2.0

Intended use

Camera-only 3D object detection for Autoware-style perception on a six-camera nuScenes-like rig: research, prototyping and offline processing of recorded drives, with the outputs of the autoware_tensorrt_bevformer node.

Prerequisites

  • The Tenstorrent CLI, tt: uv tool install tenstorrent (or use tt-model alone, see the Quickstart)
  • Docker
  • Tenstorrent hardware: p150

Quickstart

tt serve changh95/bevformer-p150

tt serve downloads the Docker image and the changh95/bevformer-small-weights at 0e6d9d9c7b8aca95820b264491e19a72d10f0608 weights (into your HF cache; they are not in the image), then starts the model's own HTTP server on port 20000 (or the next free port, if that one is busy). The first start compiles kernels for your device, which takes several minutes; the server is ready when it logs Application startup complete.

Without tt-cli: tt-model serve changh95/bevformer-p150.

Send the synthetic six-camera sample shipped in this repo to /predict, on the port tt serve reported. client.py (standard-library Python) builds the request from the six images and the shipped calibration preset (--reset: a fresh BEV history, as in the response below):

hf download changh95/bevformer-p150 --include "code/tt_bevformer/*" --local-dir bevformer-p150 && cd bevformer-p150
S=code/tt_bevformer/samples/synthetic_6cam
python3 code/tt_bevformer/server/client.py --calib-preset synthetic_6cam --reset --out req.json \
  --image CAM_FRONT=$S/CAM_FRONT.png --image CAM_FRONT_RIGHT=$S/CAM_FRONT_RIGHT.png --image CAM_FRONT_LEFT=$S/CAM_FRONT_LEFT.png \
  --image CAM_BACK=$S/CAM_BACK.png --image CAM_BACK_LEFT=$S/CAM_BACK_LEFT.png --image CAM_BACK_RIGHT=$S/CAM_BACK_RIGHT.png
curl -s localhost:20000/predict -H 'Content-Type: application/json' -d @req.json

Serving profiles

Select a different serving profile using --profile.

profile hardware mesh description
fast (default) p150 P150 reduced precision (bf16 weights, bfp8 in the backbone's third stage, HiFi2); judged by task-level agreement on nuScenes mini_val
accurate p150 P150 full precision (fp32 weights, HiFi4); meets the per-stage PCC thresholds

Capabilities

This package exposes the model's own HTTP server on port 20000, not an OpenAI-compatible chat API — its request and response shapes are the model's own. See the author's notes for the payload it expects.

POST /predict takes one frame of six camera images as JSON and returns its 3D boxes. GET /health and GET /info report the server and device state (/info lists the calibration presets); GET /v1/models is a stub, not a chat API.

request field meaning
images the six cameras CAM_FRONT, CAM_FRONT_RIGHT, CAM_FRONT_LEFT, CAM_BACK, CAM_BACK_LEFT, CAM_BACK_RIGHT, each once, any order: {"camera": "<name>", "data": "<base64 PNG, JPEG or .npy>"}; any size (resized to 1600x900 like the node)
calibration per camera intrinsics (K of the image as sent) and T_ref_from_camera (camera optical frame to base_link or LIDAR_TOP), or {"preset": "<name>"} (shipped: synthetic_6cam)
stream (optional) id (default "default"), reset (false), timestamp_s, T_world_from_ego (the ego pose that aligns the previous BEV). A new id, reset or a gap above 2 s restarts the history; up to 4 ids keep their own
ego (optional) CAN dynamics in base_link: accel, rotation_rate, velocity (missing = 0)
params (optional) score_threshold (default 0.2), max_num (300), physical_boxes (false)
output_format (optional) json (default) or npz (adds the arrays as a lossless base64 npz)

Response for the sample with the default fast profile (three of the six detections shown, meta arrays elided):

{"model": "bevformer-p150", "frame_id": "LIDAR_TOP", "num_detections": 6,
 "detections": [
   {"label": "CAR", "label_id": 0, "score": 0.9785, "center": [-0.331, 11.844, -2.98], "size": [1.842, 4.218, 1.505], "yaw": -0.6172, "velocity": [-0.0, 0.004]},
   {"label": "CAR", "label_id": 0, "score": 0.9766, "center": [-3.585, 25.779, -3.873], "size": [2.065, 4.951, 1.866], "yaw": 0.3894, "velocity": [-0.0, 0.003]},
   {"label": "PEDESTRIAN", "label_id": 8, "score": 0.971, "center": [-3.468, 8.908, -2.803], "size": [0.776, 0.839, 1.8], "yaw": 1.5032, "velocity": [-0.001, -0.016]}],
 "meta": {"class_names": ["car", "truck", "construction_vehicle", "bus", "trailer", "barrier", "motorcycle", "bicycle", "pedestrian", "traffic_cone"],
          "box_convention": "autoware", "rot": ["..."], "query": ["..."], "stream": {"id": "default", "fresh": true, "use_prev_bev": 0.0}},
 "timing_ms": {"decode": 1911.3, "preprocess": 4046.9, "device": 57.3, "postprocess": 1.5, "total": 6018.1}}
  • Boxes are in the network's nuScenes-style LIDAR_TOP frame (x right, y forward, about 1.84 m above the road on the default virtual mount), the frame the Autoware node publishes; center is the gravity centre in metres. By default size and yaw follow the node's message: size = [w, l, h] and yaw = the heading minus 90 degrees; params: {"physical_boxes": true} gives [l, w, h] and the heading of the length axis.
  • label is the Autoware label (bus, trailer and construction_vehicle become TRUCK; barrier and traffic_cone UNKNOWN); label_id is the network class in meta.class_names. velocity is [vx, vy] in m/s.
  • The timing above is the first request after a boot; later requests of the same sample take about 613 ms on the server (see Expected performance).
  • A Python API with the same outputs (tt_bevformer.BEVFormer) is described in code/PYTHON.md; it needs a tt-metal build with the two patches in this repo's patches/ directory.

Example output

BEVFormer-small on a Tenstorrent p150: 3D boxes on the six cameras and the bird's-eye view of nuScenes scene-0916, a Singapore parking lot

3D boxes (score ≥ 0.4) computed on the p150 for 20 consecutive key-frames of nuScenes scene-0916 (Singapore, 2 Hz, shown at 4 frames/s), projected into the six cameras and drawn in the bird's-eye view, also as MP4. The video was rendered from the fp32-weight (HiFi4) outputs of this port's initial version, the precision of the accurate profile, not from the fast default. Non-commercial use only: rendered from the nuScenes dataset, © Motional AD Inc., CC BY-NC-SA 4.0 and the nuScenes Terms of Use; Motional does not endorse this work. Attributions for every file in media/: media/ATTRIBUTION.md.

Expected performance

The two serve profiles differ in precision. fast (the default) uses bf16 weights (bfp8 in the backbone's third stage), HiFi2 math and a bfp8 residual stream in the backbone; accurate (--profile accurate) uses fp32 weights and HiFi4 math. Both keep bf16 activations and fp32 accumulation. Agreement with the fp32 CPU reference of the same network (same weights, same pre- and post-processing):

eval fast (default) accurate fp32 CPU reference frames
nuScenes mini_val mAP / NDS (official devkit, test-time decode) 0.3574 / 0.3994 0.3539 / 0.3993 0.3527 / 0.3984 81
|ΔmAP| / |ΔNDS| against the reference, nuScenes mini_val 0.0047 / 0.0010 0.0012 / 0.0009 – 81
Boxes matched to the reference (recall / precision), nuScenes mini_val 0.964 / 0.978 0.987 / 0.995 5,921 boxes 81
Boxes matched to the reference (recall / precision / max score difference), PandaSet 019 0.952 / 0.971 / 0.156 0.983 / 1.000 / 0.049 – 3
Boxes matched to the reference (recall / precision), nuScenes scene-1077 0.967 / 1.000 – – 5
Boxes matched to the reference, shipped synthetic sample (max score difference) 6 of 6 (0.0046) 6 of 6 (0.0005) 6 1
Per-stage output PCC, PandaSet frame: backbone stage 4 / BEV features (threshold 0.999) 0.9954 / 0.99896 0.99946 / 0.99986 – 1
Per-stage output PCC, last decoder layer: class scores (threshold 0.995) / boxes (0.9999) – 0.99980 / 0.999994 – 1

fast is accepted on task-level agreement (|ΔmAP| and |ΔNDS| ≤ 0.02, recall and precision ≥ 0.90 on nuScenes mini_val, scenes 0103 and 0916, history reset at each scene start); it does not meet the per-stage PCC thresholds that accurate meets. A box counts as matched when a box of the same class lies within 1 m (nuScenes) or 0.5 m (PandaSet, the sample). The mini_val figures are indicative (3 of the 10 classes do not occur); the checkpoint's published full nuScenes val result is 0.370 mAP / 0.479 NDS, not re-measured here. Scene-1077 is a training scene of the weights, so its row measures agreement, not accuracy.

speed (batch 1, warm) profile input median throughput
Device time per frame, back-to-back trace replays fast six 1280x736 network inputs 49.3 ms 20.3 frames/s
Device time per frame, back-to-back trace replays accurate six 1280x736 network inputs 79.6 ms 12.6 frames/s
Python model() call (resize, pre-processing, device, decode) fast six 1600x900 images 59.0 ms 17 frames/s
Python model() call accurate six 1600x900 images 88.9 ms 11 frames/s
Python model() call, one frame / a 2 Hz sequence fast six 1920x1080 images (PandaSet) 71.0 / 73.6 ms 14 frames/s
Python model() call, one frame / a 2 Hz sequence accurate six 1920x1080 images (PandaSet) 101.9 / 104.2 ms 9.8 / 9.6 frames/s
Served POST /predict, server timing_ms.total fast shipped sample (six PNGs, 13.7 MB, in 18.3 MB of JSON) / six 1920x1080 JPEGs 613 / 189-192 ms –
Served POST /predict, server timing_ms.total accurate shipped sample / six 1920x1080 JPEGs 645 / 218-222 ms –

Measured with this package's code on one p150 in an 8-vCPU AMD EPYC-Rome host: code/scripts/bench.py (p50 of 30 warm iterations) and, inside the container, 2,000 (fast) and 200 (accurate) served requests round-robin over the sample and three PandaSet frames. The served times are host-bound: decoding and pre-processing the base64 request take most of the call (the device part of a fast request is about 57 ms).

Limitations

  • Do not use it to drive or control a vehicle or as any safety function: it is not a certified Autoware component and has no functional-safety qualification. No Autoware launch file starts the BEVFormer node by default.
  • Do not use its outputs as a BEVFormer accuracy benchmark: the mini_val figures cover 81 key-frames of 2 scenes, and nuScenes mini_val is also the set on which the fast precision settings were chosen. The held-out checks are 3 PandaSet frames and 5 frames of a nuScenes training scene.
  • Not a ROS 2 node: it serves HTTP and Python only. Autoware's own bevformer_small.onnx is not obtainable; the package runs the official checkpoint through a re-implementation of the TensorRT export graph that Autoware's file comes from. Outputs were compared with the fp32 CPU reference, not with the node's fp16 TensorRT engine.
  • Batch 1, one frame per request; requests are served one at a time. Fixed shapes: six cameras in the nuScenes slots, 1280x736 network input, a 150x150 BEV grid (±51.2 m), 900 queries, up to 4 stream histories.
  • A rig whose cameras each see more than 6,144 BEV cells is refused with HTTP 400; the Autoware node accepts any rig.
  • One p150 only. The image runs device dispatch on the chip's Ethernet cores, so multi-chip meshes are not supported.
  • fast (the default) is reduced precision: against the fp32 reference, scores move by up to 0.156 on held-out PandaSet frames (0.049 with accurate), so a box near the 0.2 score threshold appears or disappears more often. Use --profile accurate when per-box parity with the fp32 network matters.
  • The nuScenes rows feed the pre-processed network input; the served path runs the same pre-processing on the device from the uint8 camera images, and matches it bit for bit in a kernel test.
  • The weights were trained on nuScenes only (Boston, Singapore). Other rigs need a virtual LIDAR_TOP frame (the default calibration provides one) and lose accuracy, on CPU and p150 alike: on PandaSet sequence 019, frame 40, 22 of the 63 published boxes match one of the 78 annotations within 50 m. The network has no time-step input; send frames at about 2 Hz, the key-frame rate it was trained on.
  • The first request after a boot takes about 6 s on the host, and the first frame of a new camera image size compiles a host resize kernel in the background (about 6 s, slower resizes until then).

Licensing

  • Weights: changh95/bevformer-small-weights at commit 0e6d9d9c7b8aca95820b264491e19a72d10f0608 (branch main), Apache-2.0 per its model card: the tensors of the official bevformer_small_epoch_24.pth published by the BEVFormer authors (fundamentalvision/BEVFormer, Apache-2.0), unchanged. Downloaded by tt serve at that commit, not redistributed here. The weights were trained on nuScenes (CC BY-NC-SA 4.0; commercial use of nuScenes needs a licence from Motional): read the weights card's training-data notice before commercial use.
  • Pre- and post-processing ported from autoware_universe perception/autoware_tensorrt_bevformer (Apache-2.0). The network graph follows DerryHub/BEVFormer_tensorrt (Apache-2.0); no code was copied from it.
  • Port and serving code (code/) and the tt-metal patches in patches/: Apache-2.0.
  • Sample code/tt_bevformer/samples/synthetic_6cam* and the calibration preset synthetic_6cam: generated by this package (code/scripts/make_sample.py, no third-party data), Apache-2.0.
  • Demo media in media/ are renders, not part of the package. The demo video and media/*_NC.* come from nuScenes: non-commercial use only, CC BY-NC-SA 4.0, © Motional AD Inc. The PandaSet renders: CC BY 4.0 and the PandaSet Dataset Terms (Scale AI and Hesai). Neither dataset owner endorses this work. Full attributions: media/ATTRIBUTION.md.

Related packages

  • The Autoware collection: the other Autoware perception and planning models packaged for the p150.

Feedback

Questions or problems with this package: open a discussion at https://huggingface.co/changh95/bevformer-p150/discussions — that is what reaches its author. A problem with the tt tooling itself: tt report issue (collects your environment and opens a prefilled issue against tenstorrent/tt-cli). Product feedback: [email protected].

Provenance

The exact sources the image was built from — code/ in this repo is byte-identical to the model code inside the image:

component built from
tt-metal 44d66500520fda9f2c7060c0f6b41ec48f7ab37e (dirty tree — the image includes uncommitted changes)
code/ digest d0ced57726403512 (sha256, first 16 hex digits)
built 2026-10-17T00:08:08+00:00 by tt-model 0.1.0
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for changh95/bevformer-p150

Finetuned
(1)
this model

Collection including changh95/bevformer-p150

Paper for changh95/bevformer-p150