[skip ci] feat: implement rerun v0.1
This commit is contained in:
parent
1f7ddc1d76
commit
4f3eaff2bd
|
@ -0,0 +1,31 @@
|
||||||
|
# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
from lerobot.common.cameras.configs import CameraConfig
|
||||||
|
from lerobot.common.cameras.opencv.configuration_opencv import OpenCVCameraConfig
|
||||||
|
from lerobot.common.robots.config import RobotConfig
|
||||||
|
|
||||||
|
|
||||||
|
@RobotConfig.register_subclass("dummy")
|
||||||
|
@dataclass
|
||||||
|
class DummyConfig(RobotConfig):
|
||||||
|
id = "dummy"
|
||||||
|
|
||||||
|
cameras: dict[str, CameraConfig] = field(
|
||||||
|
default_factory=lambda: {
|
||||||
|
"cam": OpenCVCameraConfig(camera_index=0, fps=30, width=1280, height=720),
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,101 @@
|
||||||
|
# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from lerobot.common.cameras.utils import make_cameras_from_configs
|
||||||
|
from lerobot.common.constants import OBS_STATE
|
||||||
|
from lerobot.common.errors import DeviceAlreadyConnectedError, DeviceNotConnectedError
|
||||||
|
|
||||||
|
from ..robot import Robot
|
||||||
|
from .configuration_dummy import DummyConfig
|
||||||
|
|
||||||
|
|
||||||
|
class Dummy(Robot):
|
||||||
|
config_class = DummyConfig
|
||||||
|
name = "dummy"
|
||||||
|
|
||||||
|
def __init__(self, config: DummyConfig):
|
||||||
|
super().__init__(config)
|
||||||
|
self.cameras = make_cameras_from_configs(config.cameras)
|
||||||
|
self.is_connected = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_feature(self) -> dict:
|
||||||
|
logging.warning("Dummy has nothing to send.")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def action_feature(self) -> dict:
|
||||||
|
logging.warning("Dummy has nothing to send.")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def camera_features(self) -> dict[str, dict]:
|
||||||
|
cam_ft = {
|
||||||
|
"cam": {
|
||||||
|
"shape": (480, 640, 3),
|
||||||
|
"names": ["height", "width", "channels"],
|
||||||
|
"info": None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cam_ft
|
||||||
|
|
||||||
|
def connect(self) -> None:
|
||||||
|
if self.is_connected:
|
||||||
|
raise DeviceAlreadyConnectedError(
|
||||||
|
"Dummy is already connected. Do not run `robot.connect()` twice."
|
||||||
|
)
|
||||||
|
|
||||||
|
logging.info("Connecting cameras.")
|
||||||
|
for cam in self.cameras.values():
|
||||||
|
cam.connect()
|
||||||
|
|
||||||
|
self.is_connected = True
|
||||||
|
|
||||||
|
def calibrate(self) -> None:
|
||||||
|
logging.warning("Dummy has nothing to calibrate.")
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_observation(self) -> dict[str, np.ndarray]:
|
||||||
|
if not self.is_connected:
|
||||||
|
raise DeviceNotConnectedError("Dummy is not connected. You need to run `robot.connect()`.")
|
||||||
|
|
||||||
|
obs_dict = {}
|
||||||
|
|
||||||
|
for cam_key, cam in self.cameras.items():
|
||||||
|
frame = cam.async_read()
|
||||||
|
obs_dict[f"{OBS_STATE}.{cam_key}"] = frame
|
||||||
|
|
||||||
|
return obs_dict
|
||||||
|
|
||||||
|
def send_action(self, action: np.ndarray) -> np.ndarray:
|
||||||
|
logging.warning("Dummy has nothing to send.")
|
||||||
|
|
||||||
|
def print_logs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
if not self.is_connected:
|
||||||
|
raise DeviceNotConnectedError(
|
||||||
|
"Dummy is not connected. You need to run `robot.connect()` before disconnecting."
|
||||||
|
)
|
||||||
|
for cam in self.cameras.values():
|
||||||
|
cam.disconnect()
|
||||||
|
self.is_connected = False
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if getattr(self, "is_connected", False):
|
||||||
|
self.disconnect()
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import rerun as rr
|
||||||
|
|
||||||
|
from lerobot.common.robots.dummy.configuration_dummy import DummyConfig
|
||||||
|
from lerobot.common.robots.dummy.dummy import Dummy
|
||||||
|
from lerobot.common.teleoperators.so100 import SO100Teleop, SO100TeleopConfig
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
logging.info("Configuring Devices")
|
||||||
|
leader_arm_config = SO100TeleopConfig(port="/dev/tty.usbmodem58760434171")
|
||||||
|
leader_arm = SO100Teleop(leader_arm_config)
|
||||||
|
|
||||||
|
robot_config = DummyConfig()
|
||||||
|
robot = Dummy(robot_config)
|
||||||
|
|
||||||
|
logging.info("Connecting SO100 Devices")
|
||||||
|
leader_arm.connect()
|
||||||
|
|
||||||
|
logging.info("Connecting Dummy")
|
||||||
|
robot.connect()
|
||||||
|
|
||||||
|
rr.init("rerun_dummy_data")
|
||||||
|
rr.spawn(memory_limit="50%") # add a bluprint config
|
||||||
|
logging.info("Starting...")
|
||||||
|
i = 0
|
||||||
|
while i < 10000:
|
||||||
|
arm_action = leader_arm.get_action()
|
||||||
|
observation = robot.get_observation()
|
||||||
|
|
||||||
|
for j in range(arm_action.size):
|
||||||
|
rr.log(f"arm_action_{j}", rr.Scalar(arm_action[j]))
|
||||||
|
|
||||||
|
for k, v in observation.items():
|
||||||
|
rr.log(k, rr.Image(v))
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
robot.disconnect()
|
||||||
|
leader_arm.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in New Issue