9740bc64c9
Continuous Deployment / Deploy to Production (push) Blocked by required conditions
Continuous Deployment / Rollback Deployment (push) Blocked by required conditions
Continuous Deployment / Post-deployment Monitoring (push) Blocked by required conditions
Continuous Deployment / Notify Deployment Status (push) Blocked by required conditions
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier1) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (full-adr060) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (tdm-3node) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / Swarm Test (ADR-062) (push) Has been skipped
npm packages / tools/ruview-mcp (node 22) (push) Failing after 1s
nvsim-server → ghcr.io / build-and-publish (push) Failing after 1s
ruview-swarm CI guard / tests (full+train) (push) Failing after 2s
Bench Regression Guard / bench compile-verify (--no-run) (push) Failing after 0s
Bench Regression Guard / bench fast-run (informational, non-gating) (push) Has been skipped
Firmware CI / Verify version.txt matches release tag (push) Has been skipped
Dashboard a11y + cross-browser / a11y (push) Failing after 0s
nvsim Dashboard → GitHub Pages / build-and-deploy (push) Failing after 2s
Firmware CI / Build firmware (esp32s3 / 4mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / Build Espressif QEMU (push) Failing after 1s
Firmware QEMU Tests (ADR-061) / Fuzz Testing (ADR-061 Layer 6) (push) Failing after 1s
Continuous Deployment / Pre-deployment Checks (push) Has been skipped
Continuous Deployment / Deploy to Staging (push) Waiting to run
Firmware CI / Build firmware (esp32c6 / c6-4mb) (push) Failing after 15s
Firmware CI / Build firmware (esp32s3 / 8mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-max) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-min) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (default) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier0) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / NVS Matrix Generation (push) Failing after 1s
Security Scanning / Security Policy Compliance (push) Failing after 0s
Security Scanning / Security Report (push) Waiting to run
Security Scanning / Dependency Vulnerability Scan (push) Failing after 0s
Security Scanning / Static Application Security Testing (push) Failing after 1s
Security Scanning / Infrastructure Security Scan (push) Failing after 1s
Security Scanning / Secret Scanning (push) Failing after 1s
npm packages / harness/ruview (node 22) (push) Failing after 17s
Security Scanning / License Compliance Scan (push) Failing after 1s
Security Scanning / Container Security Scan (push) Failing after 4s
three.js demos → GitHub Pages / build-and-deploy (push) Failing after 1s
Verify Pipeline Determinism / Verify Pipeline Determinism (3.11) (push) Failing after 1s
Fix-Marker Regression Guard / Verify fix markers (push) Failing after 1s
ADR-115 MQTT integration tests / mqtt-integration (push) Failing after 1s
npm packages / harness/ruview (node 20) (push) Failing after 1s
npm packages / tools/ruview-mcp (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 22) (push) Failing after 1s
BFLD MQTT Integration / cargo test --features mqtt (live mosquitto) (push) Failing after 29s
ruview-swarm CI guard / build train_marl bin (push) Failing after 2s
ruview-swarm CI guard / clippy (-D warnings, --no-deps) (push) Failing after 3s
ruview-swarm CI guard / tests (ruflo) (push) Failing after 1s
ruview-swarm CI guard / tests (train) (push) Failing after 2s
ruview-swarm CI guard / tests (default) (push) Failing after 2s
Point Cloud Viewer → GitHub Pages / build-and-deploy (push) Failing after 8s
ruview-swarm CI guard / ITAR / publish guard (push) Failing after 0s
wifi-densepose sensing-server → Docker Hub + ghcr.io / build · push · smoke-test (push) Failing after 1s
144 lines
4.7 KiB
Python
144 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Export pose_v1.safetensors -> pose_v1.onnx.
|
|
|
|
Builds the same architecture as v2/crates/cog-pose-estimation/src/inference.rs
|
|
in PyTorch, loads the trained weights from safetensors, and runs a torch.onnx
|
|
export with a fixed [1, 56, 20] input. Then verifies the ONNX loads and
|
|
matches the torch output to within 1e-5.
|
|
"""
|
|
|
|
import json
|
|
import struct
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import torch
|
|
import torch.nn as nn
|
|
|
|
|
|
N_SUB = 56
|
|
N_FRAMES = 20
|
|
N_KP = 17
|
|
|
|
|
|
class PoseNet(nn.Module):
|
|
"""Mirrors inference.rs::PoseNet exactly."""
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.c1 = nn.Conv1d(N_SUB, 64, kernel_size=3, padding=1, dilation=1)
|
|
self.c2 = nn.Conv1d(64, 128, kernel_size=3, padding=2, dilation=2)
|
|
self.c3 = nn.Conv1d(128, 128, kernel_size=3, padding=4, dilation=4)
|
|
self.fc1 = nn.Linear(128, 256)
|
|
self.fc2 = nn.Linear(256, N_KP * 2)
|
|
|
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
# x: [B, 56, 20]
|
|
h = torch.relu(self.c1(x))
|
|
h = torch.relu(self.c2(h))
|
|
h = torch.relu(self.c3(h))
|
|
h = h.mean(dim=2) # [B, 128]
|
|
h = torch.relu(self.fc1(h))
|
|
h = torch.sigmoid(self.fc2(h))
|
|
return h
|
|
|
|
|
|
def load_safetensors(path: Path) -> dict[str, torch.Tensor]:
|
|
"""Pure-python safetensors reader. Avoids the safetensors pip dep."""
|
|
with path.open("rb") as f:
|
|
header_len = struct.unpack("<Q", f.read(8))[0]
|
|
header = json.loads(f.read(header_len).decode("utf-8"))
|
|
out: dict[str, torch.Tensor] = {}
|
|
for name, meta in header.items():
|
|
if name == "__metadata__":
|
|
continue
|
|
start, end = meta["data_offsets"]
|
|
shape = meta["shape"]
|
|
dtype = meta["dtype"]
|
|
assert dtype == "F32", f"unsupported dtype {dtype} for {name}"
|
|
f.seek(8 + header_len + start)
|
|
buf = f.read(end - start)
|
|
arr = np.frombuffer(buf, dtype=np.float32).copy().reshape(shape)
|
|
out[name] = torch.from_numpy(arr)
|
|
return out
|
|
|
|
|
|
def main() -> None:
|
|
weights_path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("pose_v1.safetensors")
|
|
out_path = Path(sys.argv[2]) if len(sys.argv) > 2 else Path("pose_v1.onnx")
|
|
|
|
if not weights_path.exists():
|
|
raise SystemExit(f"weights file not found: {weights_path}")
|
|
|
|
print(f"reading {weights_path}")
|
|
tensors = load_safetensors(weights_path)
|
|
print(f" found {len(tensors)} tensors: {sorted(tensors.keys())}")
|
|
|
|
model = PoseNet()
|
|
# Map safetensors names (enc.c1.weight, head.fc1.weight, ...) to module params
|
|
mapping = {
|
|
"enc.c1.weight": "c1.weight",
|
|
"enc.c1.bias": "c1.bias",
|
|
"enc.c2.weight": "c2.weight",
|
|
"enc.c2.bias": "c2.bias",
|
|
"enc.c3.weight": "c3.weight",
|
|
"enc.c3.bias": "c3.bias",
|
|
"head.fc1.weight": "fc1.weight",
|
|
"head.fc1.bias": "fc1.bias",
|
|
"head.fc2.weight": "fc2.weight",
|
|
"head.fc2.bias": "fc2.bias",
|
|
}
|
|
state = {dst: tensors[src] for src, dst in mapping.items()}
|
|
model.load_state_dict(state)
|
|
model.eval()
|
|
print(" weights loaded into PyTorch model")
|
|
|
|
# Sanity check forward
|
|
x = torch.zeros(1, N_SUB, N_FRAMES)
|
|
with torch.no_grad():
|
|
y = model(x)
|
|
print(f" zero-input forward: shape={tuple(y.shape)} sample={y[0, :4].tolist()}")
|
|
|
|
# Export to ONNX
|
|
torch.onnx.export(
|
|
model,
|
|
x,
|
|
out_path,
|
|
export_params=True,
|
|
opset_version=18,
|
|
do_constant_folding=True,
|
|
input_names=["csi_window"],
|
|
output_names=["keypoints"],
|
|
dynamic_axes={"csi_window": {0: "batch"}, "keypoints": {0: "batch"}},
|
|
)
|
|
print(f" wrote {out_path} ({out_path.stat().st_size} bytes)")
|
|
|
|
# Verify the ONNX file loads + matches torch output
|
|
try:
|
|
import onnx
|
|
import onnxruntime as ort
|
|
|
|
onnx_model = onnx.load(str(out_path))
|
|
onnx.checker.check_model(onnx_model)
|
|
print(" ONNX model checker: ok")
|
|
|
|
sess = ort.InferenceSession(str(out_path), providers=["CPUExecutionProvider"])
|
|
rng = np.random.default_rng(42)
|
|
x_np = rng.standard_normal((1, N_SUB, N_FRAMES), dtype=np.float32)
|
|
with torch.no_grad():
|
|
y_torch = model(torch.from_numpy(x_np)).numpy()
|
|
y_onnx = sess.run(["keypoints"], {"csi_window": x_np})[0]
|
|
max_abs = float(np.max(np.abs(y_torch - y_onnx)))
|
|
print(f" parity vs torch: max |torch - onnx| = {max_abs:.2e}")
|
|
assert max_abs < 1e-5, "ONNX output diverges from torch output"
|
|
print(" parity ok (<1e-5)")
|
|
except ImportError as e:
|
|
print(f" WARN: onnx/onnxruntime not installed, skipping verification: {e}")
|
|
|
|
print("\nDone.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|