mirror of
https://github.com/huggingface/lerobot.git
synced 2026-05-14 16:19:45 +00:00
Delete test files
This commit is contained in:
@@ -1,439 +0,0 @@
|
||||
from pathlib import Path
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from concurrent import futures
|
||||
from typing import Dict
|
||||
import threading
|
||||
|
||||
import grpc
|
||||
from logging import getLogger
|
||||
|
||||
import torch.multiprocessing as mp
|
||||
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
||||
from lerobot.datasets.utils import hw_to_dataset_features
|
||||
from lerobot.robots.reachy2 import Reachy2Robot, Reachy2RobotConfig
|
||||
from lerobot.teleoperators.reachy2_fake_teleoperator import Reachy2FakeTeleoperator, Reachy2FakeTeleoperatorConfig
|
||||
from lerobot.utils.utils import log_say
|
||||
from lerobot.record import record_loop
|
||||
|
||||
from data_acquisition_api.data_acquisition_pb2 import (
|
||||
ActionAck,
|
||||
EpisodeRating,
|
||||
SessionParams,
|
||||
Dataset,
|
||||
DatasetList,
|
||||
DatasetPushState,
|
||||
)
|
||||
from data_acquisition_api.data_acquisition_pb2_grpc import add_DataAcquisitionServiceServicer_to_server
|
||||
from google.protobuf.empty_pb2 import Empty
|
||||
|
||||
|
||||
class DataAcquisitionServicer():
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
self._logger = getLogger(__name__)
|
||||
self.play_sound = True
|
||||
|
||||
self.thread: threading.Thread = None
|
||||
self.events: Dict = {}
|
||||
self.events["exit_early"] = False
|
||||
|
||||
self.setup_over = False
|
||||
|
||||
self.dataset_list_path = Path("datasets.json")
|
||||
self.robot: Reachy2Robot = None
|
||||
self.task: str = None
|
||||
self.dataset: LeRobotDataset = None
|
||||
|
||||
self.fps: int
|
||||
self.episode_duration: int
|
||||
self.break_time_duration: int
|
||||
|
||||
self.episode_recording_in_progress: bool = False
|
||||
self.episode_saved: bool = False
|
||||
self.episode_recorded_in_session: bool = False
|
||||
|
||||
self.run_compute_stats: bool = True
|
||||
|
||||
def register_to_server(self, server):
|
||||
add_DataAcquisitionServiceServicer_to_server(self, server)
|
||||
|
||||
def GetDatasetList(self, request: Empty, context: grpc.ServicerContext) -> DatasetList:
|
||||
dataset_list = self.read_datasets_from_json(self.dataset_list_path)
|
||||
return dataset_list
|
||||
|
||||
def RemoveDataset(self, request: Dataset, context: grpc.ServicerContext) -> ActionAck:
|
||||
self.remove_dataset_by_name(request.dataset_name, self.dataset_list_path)
|
||||
return ActionAck(success_ack=True)
|
||||
|
||||
def AddDataset(self, request: Dataset, context: grpc.ServicerContext) -> ActionAck:
|
||||
# Create a new dataset
|
||||
dataset = self.create_dataset(request.dataset_name, DatasetPushState.PUSHED, 0)
|
||||
# Add it to the JSON file
|
||||
self.add_dataset_to_json_file(dataset, self.dataset_list_path)
|
||||
return ActionAck(success_ack=True)
|
||||
|
||||
def UpdateDataset(self, request: Dataset, context: grpc.ServicerContext) -> ActionAck:
|
||||
self.update_dataset(request, self.dataset_list_path)
|
||||
return ActionAck(success_ack=True)
|
||||
|
||||
def ClearAllDatasets(self, request: Empty, context: grpc.ServicerContext) -> ActionAck:
|
||||
# Clear all datasets from the JSON file
|
||||
self.clear_all_datasets(self.dataset_list_path)
|
||||
return ActionAck(success_ack=True)
|
||||
|
||||
def RemoveSession(self, request: SessionParams, context: grpc.ServicerContext) -> ActionAck:
|
||||
self.delete_folder(request.session_name)
|
||||
return ActionAck(success_ack=True)
|
||||
|
||||
def ClearAllSessions(self, request: Empty, context: grpc.ServicerContext) -> ActionAck:
|
||||
pass
|
||||
|
||||
# Session and Episode Management with LeRobot
|
||||
|
||||
def StartSession(self, request: SessionParams, context: grpc.ServicerContext) -> ActionAck:
|
||||
self._logger.error(f"Starting session with params: {request}")
|
||||
try:
|
||||
self.setup_recording_session(request, context)
|
||||
self.setup_over = True
|
||||
ack = ActionAck(success_ack=True)
|
||||
except Exception as e:
|
||||
self._logger.error(f"Error starting session: {e}")
|
||||
ack = ActionAck(success_ack=False)
|
||||
return ack
|
||||
|
||||
def StopSession(self, request: Empty, context: grpc.ServicerContext) -> ActionAck:
|
||||
self._logger.error("Stopping session")
|
||||
try:
|
||||
if self.thread and self.thread.is_alive():
|
||||
self.thread.join() # Wait for the thread to finish
|
||||
log_say("Stop recording", play_sounds=self.play_sound, blocking=True)
|
||||
self.robot.disconnect()
|
||||
ack = ActionAck(success_ack=True)
|
||||
self._logger.error("Session stopped")
|
||||
except Exception as e:
|
||||
self._logger.error(f"Error stopping session: {e}")
|
||||
ack = ActionAck(success_ack=False)
|
||||
return ack
|
||||
|
||||
def StartEpisode(self, request: Empty, context: grpc.ServicerContext) -> ActionAck:
|
||||
self._logger.error("Starting episode")
|
||||
try:
|
||||
if self.episode_recorded_in_session and not self.episode_saved:
|
||||
self._logger.error("Episode not saved. Clearing episode buffer.")
|
||||
self.dataset.clear_episode_buffer()
|
||||
if not self.setup_over:
|
||||
raise RuntimeError("Setup not completed. Please call StartSession first.")
|
||||
if self.episode_recording_in_progress:
|
||||
raise RuntimeError("Episode recording already in progress. Please stop it before starting a new one.")
|
||||
self.episode_saved = False
|
||||
self.thread = threading.Thread(target=self.record_episode)
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
self.episode_recorded_in_session = True
|
||||
ack = ActionAck(success_ack=True)
|
||||
self._logger.error("Episode started")
|
||||
except Exception as e:
|
||||
self._logger.error(f"Error starting episode: {e}")
|
||||
ack = ActionAck(success_ack=False)
|
||||
return ack
|
||||
|
||||
def StopEpisode(self, request: Empty, context: grpc.ServicerContext) -> ActionAck:
|
||||
self._logger.error("Stopping episode")
|
||||
try:
|
||||
self.events["exit_early"] = True
|
||||
if self.thread and self.thread.is_alive():
|
||||
self.thread.join() # Wait for the thread to finish
|
||||
self.events["exit_early"] = False
|
||||
ack = ActionAck(success_ack=True)
|
||||
self._logger.error("Episode stopped")
|
||||
except Exception as e:
|
||||
self._logger.error(f"Error stopping session: {e}")
|
||||
ack = ActionAck(success_ack=False)
|
||||
return ack
|
||||
|
||||
def SaveEpisode(self, request: EpisodeRating, context: grpc.ServicerContext) -> ActionAck:
|
||||
self._logger.error("Saving episode")
|
||||
try:
|
||||
if self.episode_recording_in_progress:
|
||||
raise RuntimeError("Episode recording in progress. Please stop it before saving.")
|
||||
self.dataset.save_episode()
|
||||
self.episode_saved = True
|
||||
ack = ActionAck(success_ack=True)
|
||||
self._logger.error("Episode saved")
|
||||
except Exception as e:
|
||||
self._logger.error(f"Error saving episode: {e}")
|
||||
ack = ActionAck(success_ack=False)
|
||||
return ack
|
||||
|
||||
def UploadSession(self, request: Empty, context: grpc.ServicerContext) -> ActionAck:
|
||||
try:
|
||||
if self.episode_recording_in_progress:
|
||||
raise RuntimeError("Episode recording in progress. Please stop it before uploading.")
|
||||
self.dataset.push_to_hub()
|
||||
ack = ActionAck(success_ack=True)
|
||||
self._logger.error("Session uploaded")
|
||||
except Exception as e:
|
||||
self._logger.error(f"Error uploading session: {e}")
|
||||
ack = ActionAck(success_ack=False)
|
||||
return ack
|
||||
|
||||
def setup_recording_session(
|
||||
self,
|
||||
request: SessionParams,
|
||||
context: grpc.ServicerContext,
|
||||
):
|
||||
# Create the robot and teleoperator configurations
|
||||
self.robot_config = Reachy2RobotConfig(
|
||||
ip_address=request.robot.ip_address,
|
||||
id=request.robot.robot_id,
|
||||
use_external_commands=True,
|
||||
)
|
||||
self.teleop_config = Reachy2FakeTeleoperatorConfig(
|
||||
ip_address=request.robot.ip_address,
|
||||
)
|
||||
|
||||
# Initialize the robot and teleoperator
|
||||
self.robot = Reachy2Robot(self.robot_config)
|
||||
self.teleop = Reachy2FakeTeleoperator(self.teleop_config)
|
||||
|
||||
self.fps = request.fps if request.HasField("fps") else 30
|
||||
self.task = request.task_description
|
||||
self.episode_duration = request.episode_duration
|
||||
self.break_time_duration = request.break_time_duration
|
||||
|
||||
# Configure the dataset features
|
||||
action_features = hw_to_dataset_features(self.robot.action_features, "action")
|
||||
obs_features = hw_to_dataset_features(self.robot.observation_features, "observation")
|
||||
dataset_features = {**action_features, **obs_features}
|
||||
|
||||
print(f"Dataset name: {request.dataset_name}")
|
||||
|
||||
if request.resume:
|
||||
self.dataset = LeRobotDataset(
|
||||
request.dataset_name,
|
||||
root="/home/demo/.cache/huggingface/lerobot/" + request.dataset_name,
|
||||
)
|
||||
if hasattr(self.robot, "cameras") and len(self.robot.cameras) > 0:
|
||||
self.dataset.start_image_writer()
|
||||
else:
|
||||
# Create the dataset
|
||||
self.dataset = LeRobotDataset.create(
|
||||
repo_id=request.dataset_name,
|
||||
fps=self.fps,
|
||||
features=dataset_features,
|
||||
robot_type=self.robot.name,
|
||||
use_videos=True,
|
||||
image_writer_threads=4,
|
||||
)
|
||||
|
||||
current_dataset = self.create_dataset(request.dataset_name, DatasetPushState.LOCAL_ONLY, 0)
|
||||
self.add_dataset_to_json_file(current_dataset, self.dataset_list_path)
|
||||
|
||||
# Connect the robot and teleoperator
|
||||
if not self.robot.is_connected:
|
||||
self.robot.connect()
|
||||
if not self.teleop.is_connected:
|
||||
self.teleop.connect()
|
||||
|
||||
def record_episode(
|
||||
self,
|
||||
display_cameras: bool = False,
|
||||
play_sounds: bool = True,
|
||||
):
|
||||
self.episode_recording_in_progress = True
|
||||
record_loop(
|
||||
robot=self.robot,
|
||||
events=self.events,
|
||||
fps=self.fps,
|
||||
teleop=self.teleop,
|
||||
dataset=self.dataset,
|
||||
control_time_s=self.episode_duration,
|
||||
single_task=self.task,
|
||||
display_data=True,
|
||||
)
|
||||
self.episode_recording_in_progress = False
|
||||
|
||||
def create_dataset(self, dataset_name: str, pushed: DatasetPushState, nb_episodes: int) -> Dataset:
|
||||
dataset = Dataset()
|
||||
dataset.dataset_name = dataset_name
|
||||
dataset.pushed = pushed
|
||||
dataset.nb_episodes = nb_episodes
|
||||
return dataset
|
||||
|
||||
def dataset_to_dict(self, dataset: Dataset) -> dict:
|
||||
"""Convert Dataset proto to a dict manually."""
|
||||
return {
|
||||
"dataset_name": dataset.dataset_name,
|
||||
"pushed": DatasetPushState.Name(dataset.pushed),
|
||||
"nb_episodes": dataset.nb_episodes
|
||||
}
|
||||
|
||||
def add_dataset_to_json_file(self, dataset: Dataset, json_filename: str):
|
||||
if os.path.exists(json_filename):
|
||||
with open(json_filename, "r") as f:
|
||||
try:
|
||||
data = json.load(f)
|
||||
if not isinstance(data, list):
|
||||
print("Warning: JSON root is not a list, resetting.")
|
||||
data = []
|
||||
except json.JSONDecodeError:
|
||||
print("Warning: Invalid JSON file, resetting.")
|
||||
data = []
|
||||
else:
|
||||
data = []
|
||||
|
||||
# Add the new dataset
|
||||
data.append(self.dataset_to_dict(dataset))
|
||||
|
||||
# Save back to file
|
||||
with open(json_filename, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
def update_dataset(self, dataset: Dataset, json_filename: str):
|
||||
"""Update the 'pushed' field of a dataset in the JSON file."""
|
||||
if not os.path.exists(json_filename):
|
||||
print(f"File '{json_filename}' does not exist.")
|
||||
return
|
||||
|
||||
with open(json_filename, "r") as f:
|
||||
try:
|
||||
data = json.load(f)
|
||||
if not isinstance(data, list):
|
||||
print("Warning: JSON root is not a list. No update performed.")
|
||||
return
|
||||
except json.JSONDecodeError:
|
||||
print("Warning: JSON file is invalid. No update performed.")
|
||||
return
|
||||
|
||||
dataset_found = False
|
||||
for d in data:
|
||||
if d.get("dataset_name") == dataset.dataset_name:
|
||||
d["pushed"] = DatasetPushState.Name(dataset.pushed) # Convert enum to string
|
||||
dataset_found = True
|
||||
break
|
||||
|
||||
if not dataset_found:
|
||||
print(f"No dataset with name '{dataset.dataset_name}' found.")
|
||||
return
|
||||
|
||||
with open(json_filename, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
print(f"Dataset '{dataset.dataset_name}' updated successfully.")
|
||||
|
||||
def dict_to_dataset(self, d: Dict) -> Dataset:
|
||||
"""Convert a dictionary to a Dataset proto."""
|
||||
dataset = Dataset()
|
||||
dataset.dataset_name = d.get("dataset_name", "")
|
||||
|
||||
pushed_str = d.get("pushed", "UNKNOWN")
|
||||
if isinstance(pushed_str, str):
|
||||
# Convert string to enum value
|
||||
dataset.pushed = DatasetPushState.Value(pushed_str)
|
||||
elif isinstance(pushed_str, int):
|
||||
# Already an integer value
|
||||
dataset.pushed = pushed_str
|
||||
else:
|
||||
dataset.pushed = DatasetPushState.UNKNOWN
|
||||
|
||||
dataset.nb_episodes = d.get("nb_episodes", 0)
|
||||
return dataset
|
||||
|
||||
def read_datasets_from_json(self, json_filename: str) -> DatasetList:
|
||||
dataset_list = DatasetList()
|
||||
|
||||
if not os.path.exists(json_filename):
|
||||
print(f"File {json_filename} does not exist.")
|
||||
return dataset_list # Empty list
|
||||
|
||||
with open(json_filename, "r") as f:
|
||||
try:
|
||||
data = json.load(f)
|
||||
if not isinstance(data, list):
|
||||
print("Warning: JSON root is not a list. Ignored.")
|
||||
return dataset_list
|
||||
except json.JSONDecodeError:
|
||||
print("Warning: Invalid JSON file. Ignored.")
|
||||
return dataset_list
|
||||
|
||||
for d in data:
|
||||
dataset = self.dict_to_dataset(d)
|
||||
dataset_list.datasets.append(dataset)
|
||||
|
||||
return dataset_list
|
||||
|
||||
def remove_dataset_by_name(self, dataset_name: str, json_filename: str):
|
||||
"""Remove a dataset with a given name from the JSON file."""
|
||||
if not os.path.exists(json_filename):
|
||||
print(f"File {json_filename} does not exist.")
|
||||
return
|
||||
|
||||
with open(json_filename, "r") as f:
|
||||
try:
|
||||
data = json.load(f)
|
||||
if not isinstance(data, list):
|
||||
print("Warning: JSON root is not a list. No action taken.")
|
||||
return
|
||||
except json.JSONDecodeError:
|
||||
print("Warning: Invalid JSON file. No action taken.")
|
||||
return
|
||||
|
||||
original_length = len(data)
|
||||
# Filter out datasets with the matching name
|
||||
data = [d for d in data if d.get("dataset_name") != dataset_name]
|
||||
|
||||
if len(data) == original_length:
|
||||
print(f"No dataset found with name '{dataset_name}'. No action taken.")
|
||||
return
|
||||
|
||||
# Save the modified list back to the file
|
||||
with open(json_filename, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
print(f"Dataset '{dataset_name}' removed successfully.")
|
||||
|
||||
def clear_all_datasets(self, json_filename: str):
|
||||
"""Clear all datasets from the JSON file."""
|
||||
if not os.path.exists(json_filename):
|
||||
print(f"File {json_filename} does not exist. Nothing to clear.")
|
||||
return
|
||||
|
||||
# Overwrite the file with an empty list
|
||||
with open(json_filename, "w") as f:
|
||||
json.dump([], f, indent=2)
|
||||
print(f"All datasets have been cleared from '{json_filename}'.")
|
||||
|
||||
def delete_folder(self, folder_path: str):
|
||||
"""Delete an entire folder and its content."""
|
||||
if not os.path.exists(folder_path):
|
||||
print(f"Folder '{folder_path}' does not exist.")
|
||||
return
|
||||
|
||||
if not os.path.isdir(folder_path):
|
||||
print(f"Path '{folder_path}' is not a folder.")
|
||||
return
|
||||
|
||||
try:
|
||||
shutil.rmtree(folder_path)
|
||||
print(f"Folder '{folder_path}' and all its contents have been deleted.")
|
||||
except Exception as e:
|
||||
print(f"Error while deleting folder '{folder_path}': {e}")
|
||||
|
||||
|
||||
def main():
|
||||
mp.set_start_method('spawn', force=True)
|
||||
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
|
||||
|
||||
data_acquisition_servicer = DataAcquisitionServicer()
|
||||
data_acquisition_servicer.register_to_server(server)
|
||||
server.add_insecure_port('[::]:50062')
|
||||
server.start()
|
||||
server.wait_for_termination()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,116 +0,0 @@
|
||||
from lerobot.cameras.opencv.configuration_opencv import OpenCVCameraConfig
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
||||
from lerobot.datasets.utils import hw_to_dataset_features
|
||||
from lerobot.policies.act.modeling_act import ACTPolicy
|
||||
from lerobot.robots.reachy2 import Reachy2Robot, Reachy2RobotConfig
|
||||
from lerobot.utils.control_utils import init_keyboard_listener
|
||||
from lerobot.utils.utils import log_say
|
||||
# from lerobot.utils.visualization_utils import _init_rerun
|
||||
from lerobot.record import record_loop
|
||||
from reachy2_sdk import ReachySDK
|
||||
import numpy as np
|
||||
import time
|
||||
|
||||
|
||||
NUM_EPISODES = 2
|
||||
FPS = 15
|
||||
EPISODE_TIME_SEC = 4
|
||||
TASK_DESCRIPTION = "Grab a cube with Reachy 2"
|
||||
|
||||
|
||||
# Create the robot configuration
|
||||
robot_config = Reachy2RobotConfig(
|
||||
ip_address="192.168.0.199",
|
||||
id="reachy2-pvt02",
|
||||
with_mobile_base=False,
|
||||
)
|
||||
|
||||
# Initialize the robot
|
||||
robot = Reachy2Robot(robot_config)
|
||||
# Instantiate a client for starting and intermediate positions
|
||||
# reachy = ReachySDK(robot_config.ip_address)
|
||||
|
||||
# Initialize the policy
|
||||
policy = ACTPolicy.from_pretrained("pepijn223/grab_cube_2")
|
||||
|
||||
# Get initial dataset first episode
|
||||
initial_dataset = LeRobotDataset(repo_id="glannuzel/grab_cube_2", episodes=[0])
|
||||
actions = initial_dataset.hf_dataset.select_columns("action")
|
||||
|
||||
# Configure the dataset features
|
||||
action_features = hw_to_dataset_features(robot.action_features, "action")
|
||||
obs_features = hw_to_dataset_features(robot.observation_features, "observation")
|
||||
dataset_features = {**action_features, **obs_features}
|
||||
|
||||
# Create the dataset
|
||||
dataset = LeRobotDataset.create(
|
||||
repo_id="glannuzel/eval_grab_cube_2.1",
|
||||
fps=FPS,
|
||||
features=dataset_features,
|
||||
robot_type=robot.name,
|
||||
use_videos=True,
|
||||
image_writer_threads=4,
|
||||
)
|
||||
|
||||
# Initialize the keyboard listener and rerun visualization
|
||||
_, events = init_keyboard_listener()
|
||||
# _init_rerun(session_name="recording")
|
||||
|
||||
# Connect the robot
|
||||
robot.connect()
|
||||
|
||||
# Go to the initial pose
|
||||
action_array = actions[0]["action"]
|
||||
action = {}
|
||||
for i, name in enumerate(dataset.features["action"]["names"]):
|
||||
action[name] = action_array[i].item()
|
||||
|
||||
neck_goal = [action["neck_roll.pos"], action["neck_pitch.pos"], action["neck_yaw.pos"]]
|
||||
r_arm_goal = [action["r_shoulder_pitch.pos"],
|
||||
action["r_shoulder_roll.pos"],
|
||||
action["r_elbow_yaw.pos"],
|
||||
action["r_elbow_pitch.pos"],
|
||||
action["r_wrist_roll.pos"],
|
||||
action["r_wrist_pitch.pos"],
|
||||
action["r_wrist_yaw.pos"]]
|
||||
l_arm_goal = [action["l_shoulder_pitch.pos"],
|
||||
action["l_shoulder_roll.pos"],
|
||||
action["l_elbow_yaw.pos"],
|
||||
action["l_elbow_pitch.pos"],
|
||||
action["l_wrist_roll.pos"],
|
||||
action["l_wrist_pitch.pos"],
|
||||
action["l_wrist_yaw.pos"]]
|
||||
|
||||
robot.reachy.head.goto(neck_goal)
|
||||
robot.reachy.r_arm.goto(r_arm_goal)
|
||||
robot.reachy.r_arm.gripper.goto(100.0)
|
||||
robot.reachy.l_arm.goto(l_arm_goal, wait=True)
|
||||
time.sleep(1.0)
|
||||
|
||||
for episode_idx in range(NUM_EPISODES):
|
||||
log_say(f"Running inference, recording eval episode {episode_idx + 1} of {NUM_EPISODES}")
|
||||
|
||||
# Run the policy inference loop
|
||||
record_loop(
|
||||
robot=robot,
|
||||
events=events,
|
||||
fps=FPS,
|
||||
policy=policy,
|
||||
dataset=dataset,
|
||||
control_time_s=EPISODE_TIME_SEC,
|
||||
single_task=TASK_DESCRIPTION,
|
||||
display_data=False,
|
||||
)
|
||||
|
||||
# Set the robot back to the initial pose
|
||||
robot.reachy.head.goto(neck_goal)
|
||||
robot.reachy.r_arm.goto(r_arm_goal)
|
||||
robot.reachy.r_arm.gripper.goto(100.0)
|
||||
robot.reachy.l_arm.goto(l_arm_goal, wait=True)
|
||||
time.sleep(1.0)
|
||||
|
||||
dataset.save_episode()
|
||||
|
||||
# Clean up
|
||||
robot.disconnect()
|
||||
dataset.push_to_hub()
|
||||
@@ -1,60 +0,0 @@
|
||||
import time
|
||||
|
||||
from lerobot.robots.reachy2 import Reachy2Robot, Reachy2RobotConfig
|
||||
|
||||
# {lerobot_keys: reachy2_sdk_keys}
|
||||
REACHY2_JOINTS = {
|
||||
"neck_yaw.pos": "head.neck.yaw",
|
||||
"neck_pitch.pos": "head.neck.pitch",
|
||||
"neck_roll.pos": "head.neck.roll",
|
||||
"r_shoulder_pitch.pos": "r_arm.shoulder.pitch",
|
||||
"r_shoulder_roll.pos": "r_arm.shoulder.roll",
|
||||
"r_elbow_yaw.pos": "r_arm.elbow.yaw",
|
||||
"r_elbow_pitch.pos": "r_arm.elbow.pitch",
|
||||
"r_wrist_roll.pos": "r_arm.wrist.roll",
|
||||
"r_wrist_pitch.pos": "r_arm.wrist.pitch",
|
||||
"r_wrist_yaw.pos": "r_arm.wrist.yaw",
|
||||
"r_gripper.pos": "r_arm.gripper",
|
||||
"l_shoulder_pitch.pos": "l_arm.shoulder.pitch",
|
||||
"l_shoulder_roll.pos": "l_arm.shoulder.roll",
|
||||
"l_elbow_yaw.pos": "l_arm.elbow.yaw",
|
||||
"l_elbow_pitch.pos": "l_arm.elbow.pitch",
|
||||
"l_wrist_roll.pos": "l_arm.wrist.roll",
|
||||
"l_wrist_pitch.pos": "l_arm.wrist.pitch",
|
||||
"l_wrist_yaw.pos": "l_arm.wrist.yaw",
|
||||
"l_gripper.pos": "l_arm.gripper",
|
||||
"l_antenna.pos": "head.l_antenna",
|
||||
"r_antenna.pos": "head.r_antenna",
|
||||
}
|
||||
|
||||
REACHY2_VEL = {
|
||||
"mobile_base.vx": "vx",
|
||||
"mobile_base.vy": "vy",
|
||||
"mobile_base.vtheta": "vtheta",
|
||||
}
|
||||
|
||||
|
||||
robot_config = Reachy2RobotConfig(ip_address="localhost")
|
||||
robot = Reachy2Robot(robot_config)
|
||||
|
||||
robot.connect()
|
||||
|
||||
print(f"is_connected(): {robot.is_connected}\n")
|
||||
|
||||
print(f"_get_state(): {robot._get_state()}\n")
|
||||
|
||||
obs = robot.get_observation()
|
||||
print(f"get_observation(): {obs}\n")
|
||||
print(f"observation_features: {robot.observation_features}\n")
|
||||
print(f"action_features: {robot.action_features}\n")
|
||||
|
||||
|
||||
def get_action(robot):
|
||||
pos_dict = {k: robot.reachy.joints[v].present_position for k, v in REACHY2_JOINTS.items()}
|
||||
vel_dict = {k: robot.reachy.mobile_base.odometry[v] for k, v in REACHY2_VEL.items()}
|
||||
return {**pos_dict, **vel_dict}
|
||||
|
||||
|
||||
action = get_action(robot)
|
||||
time.sleep(5)
|
||||
robot.send_action(action)
|
||||
@@ -1,104 +0,0 @@
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
||||
from lerobot.datasets.utils import hw_to_dataset_features
|
||||
from lerobot.robots.reachy2 import Reachy2Robot, Reachy2RobotConfig
|
||||
from lerobot.teleoperators.reachy2_fake_teleoperator import Reachy2FakeTeleoperator, Reachy2FakeTeleoperatorConfig
|
||||
from lerobot.utils.control_utils import init_keyboard_listener
|
||||
from lerobot.utils.utils import log_say
|
||||
from lerobot.utils.visualization_utils import _init_rerun
|
||||
from lerobot.record import record_loop
|
||||
|
||||
import time
|
||||
|
||||
NUM_EPISODES = 35
|
||||
FPS = 20
|
||||
EPISODE_TIME_SEC = 10
|
||||
RESET_TIME_SEC = 5
|
||||
TASK_DESCRIPTION = "Grab a cube in Mujoco simulation"
|
||||
|
||||
# Create the robot and teleoperator configurations
|
||||
robot_config = Reachy2RobotConfig(
|
||||
ip_address="192.168.0.199",
|
||||
id="test_reachy",
|
||||
with_mobile_base=False,
|
||||
)
|
||||
teleop_config = Reachy2FakeTeleoperatorConfig(
|
||||
ip_address="192.168.0.199",
|
||||
with_mobile_base=False,
|
||||
)
|
||||
|
||||
# Initialize the robot and teleoperator
|
||||
robot = Reachy2Robot(robot_config)
|
||||
teleop = Reachy2FakeTeleoperator(teleop_config)
|
||||
|
||||
# Configure the dataset features
|
||||
action_features = hw_to_dataset_features(robot.action_features, "action")
|
||||
obs_features = hw_to_dataset_features(robot.observation_features, "observation")
|
||||
dataset_features = {**action_features, **obs_features}
|
||||
|
||||
# Create the dataset
|
||||
dataset = LeRobotDataset.create(
|
||||
repo_id="glannuzel/grab_cube",
|
||||
fps=FPS,
|
||||
features=dataset_features,
|
||||
robot_type=robot.name,
|
||||
use_videos=True,
|
||||
image_writer_threads=4,
|
||||
)
|
||||
|
||||
# Initialize the keyboard listener and rerun visualization
|
||||
_, events = init_keyboard_listener()
|
||||
# _init_rerun(session_name="recording")
|
||||
|
||||
# Connect the robot and teleoperator
|
||||
robot.connect()
|
||||
teleop.connect()
|
||||
|
||||
episode_idx = 0
|
||||
while episode_idx < NUM_EPISODES and not events["stop_recording"]:
|
||||
start_time = time.time()
|
||||
log_say(f"Recording episode {episode_idx + 1} of {NUM_EPISODES}")
|
||||
|
||||
print("########### RECORDING ###########")
|
||||
|
||||
record_loop(
|
||||
robot=robot,
|
||||
events=events,
|
||||
fps=FPS,
|
||||
teleop=teleop,
|
||||
dataset=dataset,
|
||||
control_time_s=EPISODE_TIME_SEC,
|
||||
single_task=TASK_DESCRIPTION,
|
||||
display_data=False,
|
||||
)
|
||||
|
||||
# Reset the environment if not stopping or re-recording
|
||||
if not events["stop_recording"] and (episode_idx < NUM_EPISODES - 1 or events["rerecord_episode"]):
|
||||
log_say("Reset the environment")
|
||||
|
||||
print("------------- RESETTING -------------")
|
||||
record_loop(
|
||||
robot=robot,
|
||||
events=events,
|
||||
fps=FPS,
|
||||
teleop=teleop,
|
||||
control_time_s=RESET_TIME_SEC,
|
||||
single_task=TASK_DESCRIPTION,
|
||||
display_data=False,
|
||||
)
|
||||
|
||||
if events["rerecord_episode"]:
|
||||
log_say("Re-recording episode")
|
||||
events["rerecord_episode"] = False
|
||||
events["exit_early"] = False
|
||||
dataset.clear_episode_buffer()
|
||||
continue
|
||||
|
||||
# episode_idx = NUM_EPISODES
|
||||
dataset.save_episode()
|
||||
episode_idx += 1
|
||||
print(time.time()-start_time)
|
||||
|
||||
# Clean up
|
||||
log_say("Stop recording")
|
||||
robot.disconnect()
|
||||
dataset.push_to_hub()
|
||||
@@ -1,65 +0,0 @@
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
||||
from lerobot.robots.reachy2 import Reachy2Robot, Reachy2RobotConfig
|
||||
from lerobot.utils.robot_utils import busy_wait
|
||||
import time
|
||||
|
||||
|
||||
# Create the robot configuration
|
||||
robot_config = Reachy2RobotConfig(
|
||||
ip_address="192.168.0.199",
|
||||
id="reachy2-pvt02",
|
||||
)
|
||||
|
||||
# Initialize the robot
|
||||
robot = Reachy2Robot(robot_config)
|
||||
|
||||
# Create the dataset
|
||||
dataset = LeRobotDataset(repo_id="glannuzel/store-rubiks-cube", episodes=[0])
|
||||
actions = dataset.hf_dataset.select_columns("action")
|
||||
|
||||
# Connect the robot
|
||||
robot.connect()
|
||||
|
||||
# Go smoothly to the first action
|
||||
action_array = actions[0]["action"]
|
||||
action = {}
|
||||
for i, name in enumerate(dataset.features["action"]["names"]):
|
||||
action[name] = action_array[i].item()
|
||||
|
||||
neck_goal = [action["neck_roll.pos"], action["neck_pitch.pos"], action["neck_yaw.pos"]]
|
||||
r_arm_goal = [action["r_shoulder_pitch.pos"],
|
||||
action["r_shoulder_roll.pos"],
|
||||
action["r_elbow_yaw.pos"],
|
||||
action["r_elbow_pitch.pos"],
|
||||
action["r_wrist_roll.pos"],
|
||||
action["r_wrist_pitch.pos"],
|
||||
action["r_wrist_yaw.pos"]]
|
||||
l_arm_goal = [action["l_shoulder_pitch.pos"],
|
||||
action["l_shoulder_roll.pos"],
|
||||
action["l_elbow_yaw.pos"],
|
||||
action["l_elbow_pitch.pos"],
|
||||
action["l_wrist_roll.pos"],
|
||||
action["l_wrist_pitch.pos"],
|
||||
action["l_wrist_yaw.pos"]]
|
||||
|
||||
robot.reachy.head.goto(neck_goal)
|
||||
robot.reachy.r_arm.goto(r_arm_goal)
|
||||
robot.reachy.r_arm.gripper.goto(100.0, percentage=True)
|
||||
robot.reachy.l_arm.gripper.goto(100.0, percentage=True)
|
||||
robot.reachy.l_arm.goto(l_arm_goal, wait=True)
|
||||
|
||||
for idx in range(dataset.num_frames):
|
||||
start_episode_t = time.perf_counter()
|
||||
|
||||
action_array = actions[idx]["action"]
|
||||
|
||||
action = {}
|
||||
for i, name in enumerate(dataset.features["action"]["names"]):
|
||||
action[name] = action_array[i].item()
|
||||
|
||||
robot.send_action(action)
|
||||
dt_s = time.perf_counter() - start_episode_t
|
||||
busy_wait(1 / dataset.fps - dt_s)
|
||||
|
||||
# Clean up
|
||||
robot.disconnect()
|
||||
@@ -1,46 +0,0 @@
|
||||
import threading
|
||||
|
||||
from lerobot.robots.reachy2 import Reachy2Robot, Reachy2RobotConfig
|
||||
from lerobot.scripts.server.configs import RobotClientConfig
|
||||
from lerobot.scripts.server.helpers import visualize_action_queue_size
|
||||
from lerobot.scripts.server.robot_client import RobotClient
|
||||
|
||||
robot_config = Reachy2RobotConfig(
|
||||
ip_address="192.168.0.199",
|
||||
id="reachy2-pvt02",
|
||||
with_mobile_base=False,
|
||||
with_l_arm=False,
|
||||
with_neck=False,
|
||||
with_antennas=False,
|
||||
)
|
||||
|
||||
# 3. Create client configuration
|
||||
client_cfg = RobotClientConfig(
|
||||
robot=robot_config,
|
||||
server_address="localhost:8080",
|
||||
policy_device="cuda",
|
||||
policy_type="act",
|
||||
pretrained_name_or_path="CarolinePascal/pick_and_place_bottle",
|
||||
chunk_size_threshold=0.5,
|
||||
actions_per_chunk=20, # make sure this is less than the max actions of the policy
|
||||
)
|
||||
|
||||
# 4. Create and start client
|
||||
client = RobotClient(client_cfg)
|
||||
|
||||
# 5. Specify the task
|
||||
task = "Don't do anything, stay still"
|
||||
|
||||
if client.start():
|
||||
# Start action receiver thread
|
||||
action_receiver_thread = threading.Thread(target=client.receive_actions, daemon=True)
|
||||
action_receiver_thread.start()
|
||||
|
||||
try:
|
||||
# Run the control loop
|
||||
client.control_loop(task)
|
||||
except KeyboardInterrupt:
|
||||
client.stop()
|
||||
action_receiver_thread.join()
|
||||
# (Optionally) plot the action queue size
|
||||
visualize_action_queue_size(client.action_queue_size)
|
||||
Reference in New Issue
Block a user