mirror of
https://github.com/huggingface/lerobot.git
synced 2026-05-22 20:19:43 +00:00
fix(smolvla2): instantiate CameraConfig subclasses from JSON dicts
``--robot.cameras`` parses the JSON into ``dict[str, dict]``, but ``RobotConfig`` expects ``dict[str, CameraConfig]`` — each inner value must be the actual ``CameraConfig`` subclass instance for the chosen backend (e.g. ``OpenCVCameraConfig``). Passing raw dicts blew up in ``RobotConfig.__post_init__`` with ``AttributeError: 'dict' object has no attribute 'width'`` when it iterated cameras and tried to read attributes. Look up the right subclass per-camera by its ``"type"`` field via ``CameraConfig.get_choice_class(...)`` (mirroring the lazy-import dance we already do for ``RobotConfig``: eagerly walk ``lerobot.cameras``'s submodules so the registry is populated before lookup). Construct an instance with the rest of the dict's fields. On an unknown camera type, raise a clean ``ValueError`` listing the available choices. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -494,11 +494,52 @@ def _build_robot(
|
|||||||
kwargs["id"] = robot_id
|
kwargs["id"] = robot_id
|
||||||
if robot_cameras_json:
|
if robot_cameras_json:
|
||||||
try:
|
try:
|
||||||
kwargs["cameras"] = json.loads(robot_cameras_json)
|
cameras_raw = json.loads(robot_cameras_json)
|
||||||
except json.JSONDecodeError as exc:
|
except json.JSONDecodeError as exc:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"--robot.cameras must be a JSON object, got {robot_cameras_json!r}: {exc}"
|
f"--robot.cameras must be a JSON object, got {robot_cameras_json!r}: {exc}"
|
||||||
) from exc
|
) from exc
|
||||||
|
# ``RobotConfig`` expects ``cameras: dict[str, CameraConfig]`` —
|
||||||
|
# each inner value must be an actual ``CameraConfig`` subclass
|
||||||
|
# instance, not a raw dict. Look up the matching subclass via
|
||||||
|
# ``CameraConfig.get_choice_class(<type>)`` (registered by
|
||||||
|
# ``@CameraConfig.register_subclass`` decorators on each camera
|
||||||
|
# backend's config) and instantiate it. Mirror the lazy-import
|
||||||
|
# pattern from above so the registry is populated.
|
||||||
|
import lerobot.cameras as _cameras_pkg # noqa: PLC0415
|
||||||
|
from lerobot.cameras import CameraConfig # noqa: PLC0415
|
||||||
|
|
||||||
|
for _modinfo in pkgutil.iter_modules(_cameras_pkg.__path__):
|
||||||
|
if _modinfo.name.startswith("_"):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
importlib.import_module(f"lerobot.cameras.{_modinfo.name}")
|
||||||
|
except Exception as exc: # noqa: BLE001
|
||||||
|
logger.debug("could not import lerobot.cameras.%s: %s", _modinfo.name, exc)
|
||||||
|
|
||||||
|
cameras: dict[str, Any] = {}
|
||||||
|
for cam_name, cam_dict in cameras_raw.items():
|
||||||
|
if not isinstance(cam_dict, dict):
|
||||||
|
raise ValueError(
|
||||||
|
f"camera {cam_name!r} value must be a dict, got {cam_dict!r}"
|
||||||
|
)
|
||||||
|
cam_dict = dict(cam_dict) # don't mutate caller's parsed JSON
|
||||||
|
cam_type = cam_dict.pop("type", None)
|
||||||
|
if cam_type is None:
|
||||||
|
raise ValueError(
|
||||||
|
f"camera {cam_name!r} is missing a 'type' field "
|
||||||
|
f"(e.g. 'opencv', 'intelrealsense')"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
cam_cls = CameraConfig.get_choice_class(cam_type)
|
||||||
|
except KeyError as exc:
|
||||||
|
available = sorted(CameraConfig._choice_registry.keys())
|
||||||
|
raise ValueError(
|
||||||
|
f"camera {cam_name!r}: unknown type {cam_type!r}. "
|
||||||
|
f"Available choices: {available}"
|
||||||
|
) from exc
|
||||||
|
cameras[cam_name] = cam_cls(**cam_dict)
|
||||||
|
kwargs["cameras"] = cameras
|
||||||
if robot_max_relative_target:
|
if robot_max_relative_target:
|
||||||
# Accept either a bare float (uniform cap) or a JSON object
|
# Accept either a bare float (uniform cap) or a JSON object
|
||||||
# (per-motor cap). Matches ``RobotConfig.max_relative_target``'s
|
# (per-motor cap). Matches ``RobotConfig.max_relative_target``'s
|
||||||
|
|||||||
Reference in New Issue
Block a user