feat(cameras): add Orbbec RGBD camera support via pyorbbecsdk2#3113
Open
orbbecwuxin wants to merge 13 commits intohuggingface:mainfrom
Open
feat(cameras): add Orbbec RGBD camera support via pyorbbecsdk2#3113orbbecwuxin wants to merge 13 commits intohuggingface:mainfrom
orbbecwuxin wants to merge 13 commits intohuggingface:mainfrom
Conversation
Implements OrbbecCamera and OrbbecCameraConfig for Orbbec depth cameras (Gemini 336 / 336L tested), mirroring the RealSense camera interface. Key features: - Color and depth streaming with frame-level synchronisation - Optional depth-to-color alignment via AlignFilter (align_depth=True) - Separate capture_depth_width/height tracking to handle differing color and depth sensor resolutions without validation errors - Lazy background thread for async_read() / async_read_depth() - read_latest() peek-buffer for high-frequency consumers - find_cameras() static method + lerobot-find-cameras orbbec CLI entry - Multi-format color conversion: RGB, BGR, YUYV, MJPG, NV12, NV21, I420 Bug fixes: - np.frombuffer(dtype=uint8) for color frames (was np.asanyarray causing 0-d object array from bytes input) - Rotation 90/270: swap output self.width/height, not capture dims - Error message backticks removed to match pytest match= regex All 32 unit tests pass (mock); hardware validated on 2x Orbbec cameras (Gemini 336 + Gemini 336L, USB 3.2) across 12 sync/async scenarios. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
….18 SDK patterns - Replace manual format conversion in _frame_to_rgb_image with FormatConvertFilter (matching SDK examples/utils.py); use np.frombuffer instead of np.asanyarray to avoid OOM when frame.get_data() returns bytes - Add hardware D2C alignment via config.set_align_mode(OBAlignMode.HW_MODE) + get_d2c_depth_profile_list (hw_d2c_align.py pattern); fall back to software AlignFilter when hardware mode unavailable - Enable pipeline.enable_frame_sync() for depth+color synchronisation - Merge _configure_capture_settings into _configure_pipeline; remove redundant state (_depth_profile_size, capture_depth_width/height, _has_color_sensor) - Split _postprocess_image into _postprocess_color / _postprocess_depth - Restore find_cameras sensor probing (has_color_sensor, has_depth_sensor, default profiles) for multi-camera setup support - Use device_list[idx] direct indexing (SDK 2.0.18 enumerate.py style) - Warmup loop uses new_frame_event.wait() instead of try-except-pass - Fix ruff/mypy/bandit: add FrameSet import, assert guards for _pipeline and stop_event, combine nested if, remove try-except-pass test(cameras/orbbec): update mocks to match refactored implementation - Add __getitem__ to _make_device_list so device_list[idx] works in tests - Update error message match patterns to align with new code messages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ove thread safety
…dependency versioning
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(cameras): add Orbbec RGBD camera support via pyorbbecsdk2
Type / Scope
cameras/orbbec,docs,requirementsSummary / Motivation
Adds full support for Orbbec Gemini series cameras (tested on Gemini 336 / 336L) to LeRobot, mirroring the existing RealSense camera interface. The driver is built on
pyorbbecsdk2(SDK 2.0.18) and uses a background thread for async frame capture. Key capabilities include color+depth stream synchronisation, hardware/software depth-to-color (D2C) alignment with automatic fallback, and multi-camera auto-discovery.Related issues
What changed
src/lerobot/cameras/orbbec/camera_orbbec.py:OrbbecCameraimplementation with sync/async read, color+depth frame sync, hardware D2C alignment (auto-falls back to softwareAlignFilterwhen unsupported), and multi-format color conversion (RGB/BGR/YUYV/MJPG/NV12/NV21/I420).src/lerobot/cameras/orbbec/configuration_orbbec.py:OrbbecCameraConfigwithD2CModeenum (HARDWARE/SOFTWARE); defaults toSOFTWAREfor maximum device compatibility.src/lerobot/cameras/orbbec/__init__.py: module exports.tests/cameras/test_orbbec.py: 32 unit tests (mock-based), covering 12 sync/async scenarios.src/lerobot/cameras/utils.py,scripts/lerobot_find_cameras.py: registerorbbeccamera type andlerobot-find-cameras orbbecCLI entry point.src/lerobot/scripts/lerobot_calibrate.py,lerobot_record.py,lerobot_teleoperate.py: addOrbbecCameraConfigimport.pyproject.toml,requirements-ubuntu.txt: addpyorbbecsdk2as an optional dependency.docs/source/cameras.mdx,docs/source/installation.mdx: add Orbbec installation instructions and udev rules.How was this tested (or how to run locally)
Tests added:
tests/cameras/test_orbbec.py(32 tests, all mock-based)Run unit tests:
Install Orbbec SDK:
pip install pyorbbecsdk2 # Configure udev rules (see docs/source/installation.mdx)Hardware validation (2 cameras: Gemini 336 + Gemini 336L, USB 3.2):
lerobot-find-cameras orbbec lerobot-teleoperate \ --robot.type=so101_follower \ --robot.port=/dev/so101_follower \ --robot.id=so101_follower \ --robot.cameras='{"handeye":{"type":"orbbec","index_or_serial_number":"<SN1>","width":640,"height":480,"fps":30,"use_depth":true,"align_depth":true,"d2c_mode":"hardware"},"up":{"type":"orbbec","index_or_serial_number":"<SN2>","width":640,"height":480,"fps":30,"use_depth":true,"align_depth":true,"d2c_mode":"hardware"}}' \ --teleop.type=so101_leader \ --teleop.port=/dev/so101_leader \ --teleop.id=so101_leader \ --display_data=trueChecklist (required before merge)
pre-commit run -a)pytest)Reviewer notes
_configure_pipeline(src/lerobot/cameras/orbbec/camera_orbbec.py): when the device does not support hardware D2C, the code automatically switches to a softwareAlignFilterto prevent runtime crashes.D2CModedefault was changed fromHARDWAREtoSOFTWAREfor broader device compatibility; hardware mode offers lower latency and can be explicitly enabled via config.