-
Notifications
You must be signed in to change notification settings - Fork 8
Improve contact sensor data buffer and support save data to lerobot dataset #197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -31,8 +31,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from embodichain.utils import logger | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from embodichain.data.constants import EMBODICHAIN_DEFAULT_DATASET_ROOT | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from embodichain.lab.gym.utils.misc import is_stereocam | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from embodichain.utils.utility import get_right_name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from embodichain.data.enum import JointType | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from embodichain.lab.sim.sensors import Camera, ContactSensor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .manager_base import Functor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from .cfg import DatasetFunctorCfg | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -306,28 +305,37 @@ def _build_features(self) -> Dict: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for sensor_name, value in sensor_obs_space.items(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sensor = self._env.get_sensor(sensor_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| is_stereo = is_stereocam(sensor) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for frame_name, space in value.items(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # TODO: Support depth (uint16) and mask (also uint16 or uint8) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if frame_name not in ["color", "color_right"]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.log_error( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"Only support 'color' frame for vision sensors, but got '{frame_name}' in sensor '{sensor_name}'" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if isinstance(sensor, Camera): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| is_stereo = is_stereocam(sensor) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| features[f"{sensor_name}.{frame_name}"] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "dtype": "video" if self.use_videos else "image", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "shape": (sensor.cfg.height, sensor.cfg.width, 3), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "names": ["height", "width", "channel"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for frame_name, space in value.items(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # TODO: Support depth (uint16) and mask (also uint16 or uint8) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if frame_name not in ["color", "color_right"]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.log_error( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"Only support 'color' frame for vision sensors, but got '{frame_name}' in sensor '{sensor_name}'" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if is_stereo: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| features[f"{sensor_name}.{frame_name}_right"] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| features[f"{sensor_name}.{frame_name}"] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "dtype": "video" if self.use_videos else "image", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "shape": (sensor.cfg.height, sensor.cfg.width, 3), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "names": ["height", "width", "channel"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if is_stereo: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| features[f"{sensor_name}.{frame_name}_right"] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "dtype": "video" if self.use_videos else "image", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "shape": (sensor.cfg.height, sensor.cfg.width, 3), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "names": ["height", "width", "channel"], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif isinstance(sensor, ContactSensor): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for frame_name, space in value.items(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| features[f"{sensor_name}.{frame_name}"] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "dtype": str(space.dtype), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "shape": space.shape, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "names": frame_name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Add any extra features specified in observation space excluding 'robot' and 'sensor' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for key, space in self._env.single_observation_space.items(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if key in ["robot", "sensor"]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -338,12 +346,13 @@ def _build_features(self) -> Dict: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._add_nested_features(features, key, space) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| features[f"observation.{key}"] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| features[key] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "dtype": str(space.dtype), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "shape": space.shape, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "names": key, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._modify_feature_names(features) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return features | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _add_nested_features( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -380,6 +389,51 @@ def _add_nested_features( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "names": sub_key, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _modify_feature_names(self, features: dict[str, Any]) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Get feature names for an observation based on its functor config. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Note: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The `space` parameter is kept for API consistency but not used | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| directly, as the feature names are derived from the functor config | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and entity properties. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| For observations generated by `get_object_uid`, returns meaningful names: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - RigidObject: object UID names | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Articulation/Robot: link names | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| key: The observation space key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| space: The observation space. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| A list of feature names for the observation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+393
to
+409
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Get feature names for an observation based on its functor config. | |
| Note: | |
| The `space` parameter is kept for API consistency but not used | |
| directly, as the feature names are derived from the functor config | |
| and entity properties. | |
| For observations generated by `get_object_uid`, returns meaningful names: | |
| - RigidObject: object UID names | |
| - Articulation/Robot: link names | |
| Args: | |
| key: The observation space key. | |
| space: The observation space. | |
| Returns: | |
| A list of feature names for the observation. | |
| """Modify feature metadata in-place based on the functor configuration. | |
| This function adjusts the feature definitions stored in ``features``: | |
| - Ensures scalar features with empty shapes ``()`` are converted to ``(1,)``, | |
| as required by LeRobot. | |
| - For observations generated by :func:`get_object_uid`, assigns meaningful | |
| ``names`` values based on the corresponding asset: | |
| - :class:`RigidObject`: the asset UID is used as the feature name. | |
| - :class:`Articulation` / :class:`Robot`: link names are used as | |
| the feature names. | |
| Args: | |
| features: A mapping from feature keys to metadata dictionaries. This | |
| dictionary is modified in-place; no value is returned. |
Copilot
AI
Mar 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inline comment # Debug here to inspect contact sensor data looks like a leftover debugging note in production code. Consider removing it or converting it into a proper explanatory comment (e.g., describing why .cpu() is required) to avoid confusing future readers.
| ].cpu() # Debug here to inspect contact sensor data | |
| ].cpu() # Move contact sensor tensor to CPU for serialization |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra top-level observations are now stored in
featuresusing the raw observation-space key (e.g.features[key]), but nested Dict observations still use theobservation.{key}.{sub_key}prefix (see_add_nested_features). This creates an inconsistent schema within the same dataset (some extra obs underobservation.*, others not), which can break downstream consumers expecting a single convention. Consider either keeping theobservation.prefix for top-level extras as well, or removing it for nested extras so the naming is consistent.