Initial commit: RealSense D435 API server
Drop-in replacement for the Nuwa-HP60C camera container, exposing RGB +
aligned depth + intrinsics via REST/WebSocket so the existing
pestello-pose-detector pipeline can consume frames without changes.
Key adaptations for D4xx on USB 2.0 (this robot has no USB 3 port exposed
externally — see STATUS.md):
- pyrealsense2 built from source; the librealsense v2.55.1 install rule
doesn't ship the main .so, so we copy it from the build dir and add an
__init__.py that re-exports the submodule.
- Color stream uses YUYV format on USB 2.x (BGR8/RGB8 time out on this
bus); converted to BGR via cv2.cvtColor in capture().
- rs.align is skipped on USB 2 (incompatible with YUYV); color is
cv2-resized to depth shape for approximate per-pixel alignment.
- Driver auto-detects usb_type_descriptor and falls back to the
full-resolution BGR8 + rs.align pipeline when on USB 3.
Tested: /api/{health,status,discover,rgb,depth,depth/color,capture,
intrinsics} all return 200 with valid payloads at 640x480.
This commit is contained in:
commit
07d95792e3
58 files changed
+6987
No files matched your search
@@ -0,0 +1,73 @@
|
||||
"""Smoke tests for the RealSense D435 service layer.
|
||||
|
||||
These tests exercise the dataclasses + the driver in its un-initialized
|
||||
state. Tests that need real hardware are skipped at runtime.
|
||||
"""
|
||||
import pytest
|
||||
import numpy as np
|
||||
|
||||
|
||||
def _camera_module():
|
||||
try:
|
||||
return __import__("src.backend.services.camera", fromlist=["*"])
|
||||
except ImportError as exc:
|
||||
pytest.skip(f"pyrealsense2 not importable: {exc}")
|
||||
|
||||
|
||||
class TestCameraIntrinsics:
|
||||
def test_creation(self):
|
||||
mod = _camera_module()
|
||||
i = mod.CameraIntrinsics(
|
||||
fx_ir=383.0, fy_ir=383.0, cx_ir=320.0, cy_ir=240.0,
|
||||
fx_rgb=610.0, fy_rgb=610.0, cx_rgb=320.0, cy_rgb=240.0,
|
||||
)
|
||||
assert i.fx_ir == 383.0
|
||||
assert i.fx_rgb == 610.0
|
||||
|
||||
|
||||
class TestCameraFrame:
|
||||
def test_creation(self):
|
||||
mod = _camera_module()
|
||||
rgb = np.zeros((480, 640, 3), dtype=np.uint8)
|
||||
depth = np.zeros((480, 640), dtype=np.uint16)
|
||||
f = mod.CameraFrame(rgb=rgb, depth=depth, timestamp=1234.5)
|
||||
assert f.rgb.shape == (480, 640, 3)
|
||||
assert f.depth.shape == (480, 640)
|
||||
assert f.timestamp == 1234.5
|
||||
|
||||
|
||||
class TestDiscoveredDevice:
|
||||
def test_creation(self):
|
||||
mod = _camera_module()
|
||||
d = mod.DiscoveredDevice(index=0, vid=0x8086, pid=0x0B07, serial="ABC123", bus=1, device=5)
|
||||
assert d.vid == 0x8086
|
||||
assert d.serial == "ABC123"
|
||||
assert d.index == 0
|
||||
|
||||
|
||||
class TestRealSenseD435:
|
||||
def test_defaults(self):
|
||||
mod = _camera_module()
|
||||
cam = mod.RealSenseD435()
|
||||
assert not cam.is_open()
|
||||
assert not cam.is_initialized()
|
||||
assert cam.intrinsics is None
|
||||
|
||||
def test_capture_when_closed(self):
|
||||
mod = _camera_module()
|
||||
cam = mod.RealSenseD435()
|
||||
assert cam.capture() is None
|
||||
assert cam.get_rgb() is None
|
||||
assert cam.get_depth() is None
|
||||
|
||||
def test_discover_requires_init(self):
|
||||
mod = _camera_module()
|
||||
cam = mod.RealSenseD435()
|
||||
with pytest.raises(RuntimeError, match="not initialized"):
|
||||
cam.discover()
|
||||
|
||||
def test_open_requires_init(self):
|
||||
mod = _camera_module()
|
||||
cam = mod.RealSenseD435()
|
||||
# open() returns False rather than raising when not initialized.
|
||||
assert cam.open(0) is False
|
||||
Reference in new issue
Block a user