mirror of
https://github.com/huggingface/lerobot.git
synced 2026-05-12 15:19:43 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99cdb07dda | |||
| cbeb9ce00a | |||
| 09904e7797 | |||
| 8025ab0594 | |||
| 8039a76e77 | |||
| 3ebeb59cdc | |||
| a9cf770b99 | |||
| 037747da82 | |||
| 71f3cf30cd | |||
| cf75b75474 | |||
| c720a4a347 | |||
| ddfdf9aa76 | |||
| 84f06a86af | |||
| cafb956e15 | |||
| 0f19308152 | |||
| 6697ae789d | |||
| 0e5278f6b8 | |||
| 5a15a6a911 | |||
| c23472e376 | |||
| 63619619bf | |||
| ecfc8af9dd | |||
| c6c74b3093 | |||
| a5d3702927 | |||
| 574081ac02 | |||
| c5f66edff9 | |||
| 7f16e8cb09 | |||
| 0367955590 | |||
| 01c7c74070 |
@@ -173,7 +173,4 @@ outputs/
|
||||
|
||||
# Dev folders
|
||||
.cache/*
|
||||
*.stl
|
||||
*.urdf
|
||||
*.xml
|
||||
*.part
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2025 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.
|
||||
|
||||
"""
|
||||
Convert a joint-space OpenArms dataset to end-effector space.
|
||||
|
||||
For each frame, converts joint positions to EE poses (x, y, z, wx, wy, wz) using FK.
|
||||
Grippers are kept as-is. Applies to both observation.state and action.
|
||||
|
||||
Example usage:
|
||||
python examples/openarms/convert_joints_to_ee.py \
|
||||
--input-dataset lerobot-data-collection/rac_blackf0 \
|
||||
--output-repo-id my_user/rac_blackf0_ee \
|
||||
--output-dir ./outputs/rac_blackf0_ee
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from tqdm import tqdm
|
||||
|
||||
from lerobot.datasets.compute_stats import get_feature_stats
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDataset, LeRobotDatasetMetadata
|
||||
from lerobot.datasets.utils import write_info, write_stats
|
||||
from lerobot.model.kinematics import RobotKinematics
|
||||
from lerobot.utils.rotation import Rotation
|
||||
|
||||
DEFAULT_URDF = "src/lerobot/robots/openarms/urdf/openarm_bimanual_pybullet.urdf"
|
||||
DEFAULT_LEFT_EE_FRAME = "openarm_left_hand_tcp"
|
||||
DEFAULT_RIGHT_EE_FRAME = "openarm_right_hand_tcp"
|
||||
|
||||
LEFT_URDF_JOINTS = [f"openarm_left_joint{i}" for i in range(1, 8)]
|
||||
RIGHT_URDF_JOINTS = [f"openarm_right_joint{i}" for i in range(1, 8)]
|
||||
|
||||
JOINT_NAMES = [f"joint_{i}" for i in range(1, 8)]
|
||||
EE_COMPONENTS = ["x", "y", "z", "wx", "wy", "wz"]
|
||||
|
||||
|
||||
def compute_fk_for_arm(kinematics: RobotKinematics, joint_values: np.ndarray) -> dict[str, float]:
|
||||
"""Compute FK for one arm, returns EE pose as dict."""
|
||||
t = kinematics.forward_kinematics(joint_values)
|
||||
pos = t[:3, 3]
|
||||
rotvec = Rotation.from_matrix(t[:3, :3]).as_rotvec()
|
||||
return {
|
||||
"x": float(pos[0]),
|
||||
"y": float(pos[1]),
|
||||
"z": float(pos[2]),
|
||||
"wx": float(rotvec[0]),
|
||||
"wy": float(rotvec[1]),
|
||||
"wz": float(rotvec[2]),
|
||||
}
|
||||
|
||||
|
||||
def convert_joints_to_ee(
|
||||
values: np.ndarray,
|
||||
names: list[str],
|
||||
left_kin: RobotKinematics,
|
||||
right_kin: RobotKinematics,
|
||||
) -> tuple[np.ndarray, list[str]]:
|
||||
"""
|
||||
Convert joint values to EE values.
|
||||
|
||||
Args:
|
||||
values: Array of shape (N,) with joint values for one frame
|
||||
names: List of feature names corresponding to values
|
||||
left_kin: Left arm kinematics solver
|
||||
right_kin: Right arm kinematics solver
|
||||
|
||||
Returns:
|
||||
(new_values, new_names) with joints replaced by EE poses
|
||||
"""
|
||||
name_to_idx = {n: i for i, n in enumerate(names)}
|
||||
|
||||
new_values = []
|
||||
new_names = []
|
||||
|
||||
for prefix, kinematics in [("right", right_kin), ("left", left_kin)]:
|
||||
joint_vals = []
|
||||
for jname in JOINT_NAMES:
|
||||
key = f"{prefix}_{jname}.pos"
|
||||
if key in name_to_idx:
|
||||
joint_vals.append(values[name_to_idx[key]])
|
||||
|
||||
if len(joint_vals) == 7:
|
||||
ee_pose = compute_fk_for_arm(kinematics, np.array(joint_vals, dtype=float))
|
||||
for comp in EE_COMPONENTS:
|
||||
new_names.append(f"{prefix}_ee.{comp}")
|
||||
new_values.append(ee_pose[comp])
|
||||
|
||||
gripper_key = f"{prefix}_gripper.pos"
|
||||
if gripper_key in name_to_idx:
|
||||
new_names.append(f"{prefix}_ee.gripper_pos")
|
||||
new_values.append(values[name_to_idx[gripper_key]])
|
||||
|
||||
return np.array(new_values, dtype=np.float32), new_names
|
||||
|
||||
|
||||
def transform_feature_info(old_info: dict, new_names: list[str]) -> dict:
|
||||
"""Create new feature info with EE names instead of joint names."""
|
||||
return {
|
||||
"dtype": old_info.get("dtype", "float32"),
|
||||
"shape": (len(new_names),),
|
||||
"names": new_names,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Convert joint-space dataset to EE-space")
|
||||
parser.add_argument("--input-dataset", type=str, required=True, help="Input dataset repo ID")
|
||||
parser.add_argument("--output-repo-id", type=str, required=True, help="Output dataset repo ID")
|
||||
parser.add_argument("--output-dir", type=str, required=True, help="Output directory")
|
||||
parser.add_argument("--urdf", type=str, default=DEFAULT_URDF, help="Path to URDF file")
|
||||
parser.add_argument("--left-ee-frame", type=str, default=DEFAULT_LEFT_EE_FRAME)
|
||||
parser.add_argument("--right-ee-frame", type=str, default=DEFAULT_RIGHT_EE_FRAME)
|
||||
parser.add_argument("--push-to-hub", action="store_true", help="Push converted dataset to HF Hub")
|
||||
args = parser.parse_args()
|
||||
|
||||
output_dir = Path(args.output_dir)
|
||||
if output_dir.exists():
|
||||
shutil.rmtree(output_dir)
|
||||
|
||||
urdf_path = args.urdf
|
||||
if not Path(urdf_path).is_absolute():
|
||||
urdf_path = str(Path(__file__).parent.parent.parent / urdf_path)
|
||||
|
||||
print(f"Loading dataset: {args.input_dataset}")
|
||||
dataset = LeRobotDataset(args.input_dataset)
|
||||
|
||||
print(f"Initializing kinematics from {urdf_path}")
|
||||
left_kin = RobotKinematics(urdf_path, args.left_ee_frame, LEFT_URDF_JOINTS)
|
||||
right_kin = RobotKinematics(urdf_path, args.right_ee_frame, RIGHT_URDF_JOINTS)
|
||||
|
||||
action_info = dataset.meta.features.get("action", {})
|
||||
state_info = dataset.meta.features.get("observation.state", {})
|
||||
action_names = action_info.get("names", [])
|
||||
state_names = state_info.get("names", [])
|
||||
|
||||
print(f"Original action names ({len(action_names)}): {action_names[:8]}...")
|
||||
print(f"Original state names ({len(state_names)}): {state_names[:8]}...")
|
||||
|
||||
sample_action = np.zeros(len(action_names), dtype=np.float32)
|
||||
_, new_action_names = convert_joints_to_ee(sample_action, action_names, left_kin, right_kin)
|
||||
sample_state = np.zeros(len(state_names), dtype=np.float32)
|
||||
_, new_state_names = convert_joints_to_ee(sample_state, state_names, left_kin, right_kin)
|
||||
|
||||
print(f"New action names ({len(new_action_names)}): {new_action_names}")
|
||||
print(f"New state names ({len(new_state_names)}): {new_state_names}")
|
||||
|
||||
new_features = dataset.meta.features.copy()
|
||||
new_features["action"] = transform_feature_info(action_info, new_action_names)
|
||||
new_features["observation.state"] = transform_feature_info(state_info, new_state_names)
|
||||
|
||||
new_meta = LeRobotDatasetMetadata.create(
|
||||
repo_id=args.output_repo_id,
|
||||
fps=dataset.meta.fps,
|
||||
features=new_features,
|
||||
robot_type=dataset.meta.robot_type,
|
||||
root=output_dir,
|
||||
use_videos=len(dataset.meta.video_keys) > 0,
|
||||
)
|
||||
|
||||
data_dir = dataset.root / "data"
|
||||
parquet_files = sorted(data_dir.glob("*/*.parquet"))
|
||||
print(f"Processing {len(parquet_files)} parquet files...")
|
||||
|
||||
all_actions = []
|
||||
all_states = []
|
||||
|
||||
for src_path in tqdm(parquet_files, desc="Converting"):
|
||||
df = pd.read_parquet(src_path).reset_index(drop=True)
|
||||
|
||||
new_actions = []
|
||||
new_states = []
|
||||
|
||||
for idx in range(len(df)):
|
||||
action_vals = np.array(df.iloc[idx]["action"], dtype=np.float32)
|
||||
state_vals = np.array(df.iloc[idx]["observation.state"], dtype=np.float32)
|
||||
|
||||
new_action, _ = convert_joints_to_ee(action_vals, action_names, left_kin, right_kin)
|
||||
new_state, _ = convert_joints_to_ee(state_vals, state_names, left_kin, right_kin)
|
||||
|
||||
new_actions.append(new_action.tolist())
|
||||
new_states.append(new_state.tolist())
|
||||
all_actions.append(new_action)
|
||||
all_states.append(new_state)
|
||||
|
||||
df["action"] = new_actions
|
||||
df["observation.state"] = new_states
|
||||
|
||||
relative_path = src_path.relative_to(dataset.root)
|
||||
out_path = output_dir / relative_path
|
||||
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
df.to_parquet(out_path)
|
||||
|
||||
print("Computing statistics...")
|
||||
all_actions_arr = np.stack(all_actions)
|
||||
all_states_arr = np.stack(all_states)
|
||||
|
||||
stats = {}
|
||||
stats["action"] = get_feature_stats(all_actions_arr, axis=0, keepdims=True)
|
||||
stats["observation.state"] = get_feature_stats(all_states_arr, axis=0, keepdims=True)
|
||||
write_stats(stats, output_dir)
|
||||
|
||||
print("Updating metadata...")
|
||||
new_meta.info["total_episodes"] = dataset.meta.total_episodes
|
||||
new_meta.info["total_frames"] = dataset.meta.total_frames
|
||||
new_meta.info["total_tasks"] = dataset.meta.total_tasks
|
||||
write_info(new_meta.info, output_dir)
|
||||
|
||||
print("Copying episode metadata...")
|
||||
src_episodes_dir = dataset.root / "meta" / "episodes"
|
||||
dst_episodes_dir = output_dir / "meta" / "episodes"
|
||||
if src_episodes_dir.exists():
|
||||
shutil.copytree(src_episodes_dir, dst_episodes_dir, dirs_exist_ok=True)
|
||||
|
||||
print("Copying tasks metadata...")
|
||||
src_tasks = dataset.root / "meta" / "tasks.parquet"
|
||||
dst_tasks = output_dir / "meta" / "tasks.parquet"
|
||||
if src_tasks.exists():
|
||||
shutil.copy2(src_tasks, dst_tasks)
|
||||
|
||||
if dataset.meta.video_keys:
|
||||
print("Copying videos...")
|
||||
src_videos = dataset.root / "videos"
|
||||
dst_videos = output_dir / "videos"
|
||||
if src_videos.exists():
|
||||
shutil.copytree(src_videos, dst_videos, dirs_exist_ok=True)
|
||||
|
||||
print(f"\nDone! Dataset saved to: {output_dir}")
|
||||
print(f"Repo ID: {args.output_repo_id}")
|
||||
|
||||
if args.push_to_hub:
|
||||
print("\nPushing to Hub...")
|
||||
output_dataset = LeRobotDataset(args.output_repo_id, root=output_dir)
|
||||
output_dataset.push_to_hub()
|
||||
print(f"Pushed to: https://huggingface.co/datasets/{args.output_repo_id}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,650 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2025 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.
|
||||
|
||||
"""
|
||||
OpenArms End-Effector Policy Evaluation
|
||||
|
||||
Evaluates a policy trained on end-effector (EE) space by:
|
||||
1. Converting robot joint observations to EE poses (FK)
|
||||
2. Running policy inference with EE state
|
||||
3. Converting EE action output back to joint positions (IK)
|
||||
4. Sending joint commands to robot
|
||||
|
||||
Example usage:
|
||||
python examples/openarms/evaluate_ee.py
|
||||
python examples/openarms/evaluate_ee.py --model lerobot/my-ee-policy
|
||||
"""
|
||||
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
from lerobot.cameras.opencv.configuration_opencv import OpenCVCameraConfig
|
||||
from lerobot.configs.policies import PreTrainedConfig
|
||||
from lerobot.configs.train import TrainPipelineConfig
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
||||
from lerobot.datasets.pipeline_features import aggregate_pipeline_dataset_features, create_initial_features
|
||||
from lerobot.datasets.utils import build_dataset_frame, combine_feature_dicts
|
||||
from lerobot.model.kinematics import RobotKinematics
|
||||
from lerobot.policies.factory import make_policy, make_pre_post_processors
|
||||
from lerobot.processor import RobotAction, RobotObservation, RobotProcessorPipeline, make_default_processors
|
||||
from lerobot.utils.constants import ACTION, OBS_STR
|
||||
from lerobot.utils.control_utils import predict_action
|
||||
from lerobot.utils.relative_actions import (
|
||||
convert_state_to_relative,
|
||||
convert_from_relative_actions,
|
||||
PerTimestepNormalizer,
|
||||
)
|
||||
from lerobot.utils.utils import get_safe_torch_device
|
||||
from lerobot.utils.visualization_utils import init_rerun, log_rerun_data
|
||||
from lerobot.processor.converters import (
|
||||
robot_action_observation_to_transition,
|
||||
robot_action_to_transition,
|
||||
transition_to_robot_action,
|
||||
)
|
||||
from lerobot.robots.openarms.config_openarms_follower import OpenArmsFollowerConfig
|
||||
from lerobot.robots.openarms.openarms_follower import OpenArmsFollower
|
||||
from lerobot.robots.openarms.robot_kinematic_processor import (
|
||||
BimanualEEBoundsAndSafety,
|
||||
BimanualForwardKinematicsJointsToEE,
|
||||
BimanualInverseKinematicsEEToJoints,
|
||||
)
|
||||
from lerobot.teleoperators.openarms.config_openarms_leader import OpenArmsLeaderConfig
|
||||
from lerobot.teleoperators.openarms.openarms_leader import OpenArmsLeader
|
||||
from lerobot.utils.control_utils import init_keyboard_listener
|
||||
from lerobot.utils.robot_utils import precise_sleep
|
||||
from lerobot.utils.utils import log_say
|
||||
|
||||
# Configuration
|
||||
HF_MODEL_ID = "lerobot-data-collection/pi0_ee" # TODO: Replace with your EE-trained model
|
||||
HF_EVAL_DATASET_ID = "your-org/your-ee-eval-dataset" # TODO: Replace with your eval dataset
|
||||
TASK_DESCRIPTION = "ee-policy-task" # TODO: Replace with your task
|
||||
|
||||
NUM_EPISODES = 1
|
||||
FPS = 30
|
||||
EPISODE_TIME_SEC = 1000
|
||||
RESET_TIME_SEC = 60
|
||||
|
||||
# Robot CAN interfaces
|
||||
FOLLOWER_LEFT_PORT = "can0"
|
||||
FOLLOWER_RIGHT_PORT = "can1"
|
||||
|
||||
# Leader for manual resets (disabled by default)
|
||||
USE_LEADER_FOR_RESETS = False
|
||||
LEADER_LEFT_PORT = "can2"
|
||||
LEADER_RIGHT_PORT = "can3"
|
||||
|
||||
# Camera configuration
|
||||
CAMERA_CONFIG = {
|
||||
"left_wrist": OpenCVCameraConfig(index_or_path="/dev/video5", width=640, height=480, fps=FPS),
|
||||
"right_wrist": OpenCVCameraConfig(index_or_path="/dev/video1", width=640, height=480, fps=FPS),
|
||||
"base": OpenCVCameraConfig(index_or_path="/dev/video3", width=640, height=480, fps=FPS),
|
||||
}
|
||||
|
||||
# Kinematics configuration
|
||||
DEFAULT_URDF = "src/lerobot/robots/openarms/urdf/openarm_bimanual_pybullet.urdf"
|
||||
DEFAULT_LEFT_EE_FRAME = "openarm_left_hand_tcp"
|
||||
DEFAULT_RIGHT_EE_FRAME = "openarm_right_hand_tcp"
|
||||
|
||||
MOTOR_NAMES = ["joint_1", "joint_2", "joint_3", "joint_4", "joint_5", "joint_6", "joint_7", "gripper"]
|
||||
LEFT_URDF_JOINTS = [f"openarm_left_joint{i}" for i in range(1, 8)]
|
||||
RIGHT_URDF_JOINTS = [f"openarm_right_joint{i}" for i in range(1, 8)]
|
||||
|
||||
|
||||
def load_relative_config(model_path: Path | str) -> tuple[PerTimestepNormalizer | None, bool, bool]:
|
||||
"""Auto-detect relative action/state settings and load normalizer from checkpoint."""
|
||||
model_path = Path(model_path) if isinstance(model_path, str) else model_path
|
||||
normalizer = None
|
||||
use_relative_actions = False
|
||||
use_relative_state = False
|
||||
|
||||
# Try local path first
|
||||
if model_path.exists():
|
||||
stats_path = model_path / "relative_stats.pt"
|
||||
if stats_path.exists():
|
||||
normalizer = PerTimestepNormalizer.load(stats_path)
|
||||
use_relative_actions = True
|
||||
print(f" Loaded per-timestep stats from: {stats_path}")
|
||||
|
||||
config_path = model_path / "train_config.json"
|
||||
if config_path.exists():
|
||||
cfg = TrainPipelineConfig.from_pretrained(model_path)
|
||||
use_relative_actions = getattr(cfg, "use_relative_actions", use_relative_actions)
|
||||
use_relative_state = getattr(cfg, "use_relative_state", False)
|
||||
else:
|
||||
# Try hub
|
||||
try:
|
||||
from huggingface_hub import hf_hub_download
|
||||
try:
|
||||
stats_file = hf_hub_download(repo_id=str(model_path), filename="relative_stats.pt")
|
||||
normalizer = PerTimestepNormalizer.load(stats_file)
|
||||
use_relative_actions = True
|
||||
print(" Loaded per-timestep stats from hub")
|
||||
except Exception:
|
||||
pass # No stats file means no relative actions
|
||||
|
||||
try:
|
||||
config_file = hf_hub_download(repo_id=str(model_path), filename="train_config.json")
|
||||
cfg = TrainPipelineConfig.from_pretrained(Path(config_file).parent)
|
||||
use_relative_actions = getattr(cfg, "use_relative_actions", use_relative_actions)
|
||||
use_relative_state = getattr(cfg, "use_relative_state", False)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f" Warning: Could not load relative config: {e}")
|
||||
|
||||
return normalizer, use_relative_actions, use_relative_state
|
||||
|
||||
|
||||
def build_kinematics_pipelines(urdf_path: str, left_ee_frame: str, right_ee_frame: str):
|
||||
"""Build FK and IK pipelines for bimanual robot."""
|
||||
left_kinematics = RobotKinematics(
|
||||
urdf_path=urdf_path,
|
||||
target_frame_name=left_ee_frame,
|
||||
joint_names=LEFT_URDF_JOINTS,
|
||||
)
|
||||
right_kinematics = RobotKinematics(
|
||||
urdf_path=urdf_path,
|
||||
target_frame_name=right_ee_frame,
|
||||
joint_names=RIGHT_URDF_JOINTS,
|
||||
)
|
||||
|
||||
# Joints -> EE (Forward Kinematics)
|
||||
joints_to_ee = RobotProcessorPipeline[RobotAction, RobotAction](
|
||||
steps=[
|
||||
BimanualForwardKinematicsJointsToEE(
|
||||
left_kinematics=left_kinematics,
|
||||
right_kinematics=right_kinematics,
|
||||
motor_names=MOTOR_NAMES,
|
||||
),
|
||||
],
|
||||
to_transition=robot_action_to_transition,
|
||||
to_output=transition_to_robot_action,
|
||||
)
|
||||
|
||||
# EE -> Joints (Inverse Kinematics)
|
||||
ee_to_joints = RobotProcessorPipeline[tuple[RobotAction, RobotObservation], RobotAction](
|
||||
steps=[
|
||||
BimanualEEBoundsAndSafety(
|
||||
end_effector_bounds={"min": [-1.0, -1.0, -1.0], "max": [1.0, 1.0, 1.0]},
|
||||
max_ee_step_m=0.10,
|
||||
),
|
||||
BimanualInverseKinematicsEEToJoints(
|
||||
left_kinematics=left_kinematics,
|
||||
right_kinematics=right_kinematics,
|
||||
motor_names=MOTOR_NAMES,
|
||||
initial_guess_current_joints=True,
|
||||
),
|
||||
],
|
||||
to_transition=robot_action_observation_to_transition,
|
||||
to_output=transition_to_robot_action,
|
||||
)
|
||||
|
||||
return joints_to_ee, ee_to_joints
|
||||
|
||||
|
||||
def convert_obs_joints_to_ee(obs: dict, joints_to_ee_pipeline) -> dict:
|
||||
"""Convert joint observations to EE space."""
|
||||
# Extract joint positions from observation
|
||||
joint_positions = {}
|
||||
for key, value in obs.items():
|
||||
if key.startswith("observation.state.") and key.endswith(".pos"):
|
||||
# e.g., observation.state.left_joint_1.pos -> left_joint_1.pos
|
||||
motor_key = key.replace("observation.state.", "")
|
||||
joint_positions[motor_key] = value
|
||||
|
||||
if not joint_positions:
|
||||
return obs
|
||||
|
||||
# Apply FK to get EE poses
|
||||
ee_poses = joints_to_ee_pipeline(joint_positions)
|
||||
|
||||
# Build new observation with EE state
|
||||
new_obs = {}
|
||||
for key, value in obs.items():
|
||||
if not (key.startswith("observation.state.") and key.endswith(".pos")):
|
||||
new_obs[key] = value
|
||||
|
||||
# Add EE poses as state
|
||||
for key, value in ee_poses.items():
|
||||
new_obs[f"observation.state.{key}"] = value
|
||||
|
||||
return new_obs
|
||||
|
||||
|
||||
def convert_action_ee_to_joints(
|
||||
ee_action: dict,
|
||||
current_obs: dict,
|
||||
ee_to_joints_pipeline,
|
||||
) -> dict:
|
||||
"""Convert EE action to joint positions using IK."""
|
||||
# Extract EE components from action
|
||||
ee_action_dict = {}
|
||||
for key, value in ee_action.items():
|
||||
if "ee." in key:
|
||||
# e.g., action.left_ee.x -> left_ee.x
|
||||
ee_key = key.replace("action.", "")
|
||||
ee_action_dict[ee_key] = value
|
||||
|
||||
if not ee_action_dict:
|
||||
return ee_action
|
||||
|
||||
# Build current observation for IK (joint positions)
|
||||
current_joints = {}
|
||||
for key, value in current_obs.items():
|
||||
if key.startswith("observation.state.") and "joint" in key and key.endswith(".pos"):
|
||||
motor_key = key.replace("observation.state.", "")
|
||||
current_joints[motor_key] = value
|
||||
|
||||
# Apply IK
|
||||
joint_action = ee_to_joints_pipeline((ee_action_dict, current_joints))
|
||||
|
||||
# Format as action dict
|
||||
result = {}
|
||||
for key, value in joint_action.items():
|
||||
result[f"action.{key}"] = value
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def run_ee_inference_loop(
|
||||
robot: OpenArmsFollower,
|
||||
policy,
|
||||
preprocessor,
|
||||
postprocessor,
|
||||
joints_to_ee,
|
||||
ee_to_joints,
|
||||
dataset: LeRobotDataset,
|
||||
fps: int,
|
||||
duration_s: float,
|
||||
events: dict,
|
||||
task: str,
|
||||
use_relative_actions: bool = False,
|
||||
use_relative_state: bool = False,
|
||||
relative_normalizer: PerTimestepNormalizer | None = None,
|
||||
display_data: bool = True,
|
||||
):
|
||||
"""Run inference loop with EE conversion and optional UMI-style relative actions."""
|
||||
device = get_safe_torch_device(policy.config.device)
|
||||
|
||||
# Reset policy and processors
|
||||
policy.reset()
|
||||
preprocessor.reset()
|
||||
postprocessor.reset()
|
||||
|
||||
dt = 1.0 / fps
|
||||
timestamp = 0
|
||||
start_time = time.perf_counter()
|
||||
step = 0
|
||||
|
||||
mode_str = ""
|
||||
if use_relative_actions:
|
||||
mode_str += " [relative actions]"
|
||||
if use_relative_state:
|
||||
mode_str += " [relative state]"
|
||||
print(f"\nRunning EE inference for {duration_s}s...{mode_str}")
|
||||
|
||||
while timestamp < duration_s:
|
||||
loop_start = time.perf_counter()
|
||||
|
||||
if events.get("exit_early"):
|
||||
events["exit_early"] = False
|
||||
break
|
||||
|
||||
# 1. Get robot observation (joint positions)
|
||||
robot_obs = robot.get_observation()
|
||||
|
||||
# 2. Convert joint observation to EE space using FK
|
||||
joint_state = {}
|
||||
for key, value in robot_obs.items():
|
||||
if key.endswith(".pos"):
|
||||
joint_state[key] = value
|
||||
|
||||
ee_state = joints_to_ee(joint_state.copy())
|
||||
|
||||
# 3. Build observation frame with EE state for policy input
|
||||
# Filter to only EE keys (FK may include other keys in output)
|
||||
# Expected: left_ee.{x,y,z,wx,wy,wz,gripper_pos}, right_ee.{...} = 14 total
|
||||
ee_keys = sorted([k for k in ee_state.keys() if "_ee." in k])
|
||||
ee_values = [ee_state[k] for k in ee_keys]
|
||||
|
||||
# Debug: print on first step
|
||||
if step == 0:
|
||||
print(f" FK output keys ({len(ee_keys)}): {ee_keys}")
|
||||
state_feature = policy.config.input_features.get("observation.state")
|
||||
if state_feature:
|
||||
print(f" Policy expects state dim: {state_feature.shape[0]}")
|
||||
|
||||
# Store current EE position for relative action conversion (using same order)
|
||||
current_ee_pos = torch.tensor(ee_values)
|
||||
|
||||
# Convert to relative state if enabled (UMI-style)
|
||||
if use_relative_state:
|
||||
ee_state_tensor = torch.tensor(ee_values)
|
||||
relative_state = convert_state_to_relative(ee_state_tensor.unsqueeze(0))
|
||||
ee_values = [float(relative_state[0, i]) for i in range(len(ee_values))]
|
||||
|
||||
# Build observation dict for policy (images + state as numpy arrays)
|
||||
observation_frame = {}
|
||||
|
||||
# Add images - robot.cameras contains camera names as keys
|
||||
for cam_name in robot.cameras:
|
||||
if cam_name in robot_obs:
|
||||
observation_frame[f"observation.images.{cam_name}"] = robot_obs[cam_name]
|
||||
|
||||
# Add state as numpy array
|
||||
observation_frame["observation.state"] = np.array(ee_values, dtype=np.float32)
|
||||
|
||||
# 4. Run policy inference using predict_action
|
||||
action_tensor = predict_action(
|
||||
observation=observation_frame,
|
||||
policy=policy,
|
||||
device=device,
|
||||
preprocessor=preprocessor,
|
||||
postprocessor=postprocessor,
|
||||
use_amp=policy.config.use_amp,
|
||||
task=task,
|
||||
robot_type=robot.robot_type,
|
||||
)
|
||||
|
||||
# 5. Convert action tensor to dict using EE keys (not joint keys from eval dataset)
|
||||
action_tensor = action_tensor.squeeze(0).cpu()
|
||||
while action_tensor.dim() > 1:
|
||||
action_tensor = action_tensor[0]
|
||||
# Use the same EE keys we used for state (truncated to match policy's action dim)
|
||||
ee_action = {ee_keys[i]: float(action_tensor[i]) for i in range(len(action_tensor))}
|
||||
|
||||
# 6. Convert relative action back to absolute if needed
|
||||
if use_relative_actions:
|
||||
action_keys = sorted(ee_action.keys())
|
||||
action_vals = torch.tensor([ee_action[k] for k in action_keys])
|
||||
|
||||
# Unnormalize if we have a normalizer
|
||||
if relative_normalizer is not None:
|
||||
action_vals = relative_normalizer.unnormalize(action_vals.unsqueeze(0).unsqueeze(0))
|
||||
action_vals = action_vals.squeeze(0).squeeze(0)
|
||||
|
||||
# Convert from relative to absolute
|
||||
absolute_action = convert_from_relative_actions(action_vals.unsqueeze(0), current_ee_pos)
|
||||
|
||||
# Convert back to dict
|
||||
ee_action = {k: float(absolute_action[0, i]) for i, k in enumerate(action_keys)}
|
||||
|
||||
# 7. Convert EE action to joint positions using IK
|
||||
joint_action = ee_to_joints((ee_action.copy(), joint_state.copy()))
|
||||
|
||||
# 8. Send joint commands to robot
|
||||
robot.send_action(joint_action)
|
||||
|
||||
# 9. Save frame to dataset (save original robot obs + joint action)
|
||||
if dataset is not None:
|
||||
obs_frame = build_dataset_frame(dataset.features, robot_obs, prefix=OBS_STR)
|
||||
act_frame = build_dataset_frame(dataset.features, joint_action, prefix=ACTION)
|
||||
frame = {**obs_frame, **act_frame, "task": task}
|
||||
dataset.add_frame(frame)
|
||||
|
||||
# 10. Visualization
|
||||
if display_data:
|
||||
log_rerun_data(observation=robot_obs, action=joint_action)
|
||||
|
||||
# Progress logging
|
||||
step += 1
|
||||
if step % (fps * 5) == 0:
|
||||
elapsed = time.perf_counter() - start_time
|
||||
print(f" Step {step}, elapsed: {elapsed:.1f}s")
|
||||
|
||||
# Maintain loop rate
|
||||
loop_duration = time.perf_counter() - loop_start
|
||||
sleep_time = dt - loop_duration
|
||||
if sleep_time > 0:
|
||||
precise_sleep(sleep_time)
|
||||
|
||||
timestamp = time.perf_counter() - start_time
|
||||
|
||||
print(f" Completed {step} steps")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main evaluation function for EE policies."""
|
||||
print("=" * 70)
|
||||
print("OpenArms End-Effector Policy Evaluation")
|
||||
print("=" * 70)
|
||||
print(f"\nModel: {HF_MODEL_ID}")
|
||||
print(f"Dataset: {HF_EVAL_DATASET_ID}")
|
||||
print(f"Task: {TASK_DESCRIPTION}")
|
||||
print(f"Episodes: {NUM_EPISODES}")
|
||||
print(f"Episode Duration: {EPISODE_TIME_SEC}s")
|
||||
print("=" * 70)
|
||||
|
||||
# Resolve URDF path
|
||||
urdf_path = Path(__file__).parent.parent.parent / DEFAULT_URDF
|
||||
if not urdf_path.exists():
|
||||
raise FileNotFoundError(f"URDF not found: {urdf_path}")
|
||||
urdf_path = str(urdf_path)
|
||||
|
||||
# Build kinematics pipelines
|
||||
print("\n[1/5] Building kinematics pipelines...")
|
||||
joints_to_ee, ee_to_joints = build_kinematics_pipelines(
|
||||
urdf_path, DEFAULT_LEFT_EE_FRAME, DEFAULT_RIGHT_EE_FRAME
|
||||
)
|
||||
print(" FK and IK pipelines ready")
|
||||
|
||||
# Initialize robot
|
||||
print("\n[2/5] Connecting to robot...")
|
||||
follower_config = OpenArmsFollowerConfig(
|
||||
port_left=FOLLOWER_LEFT_PORT,
|
||||
port_right=FOLLOWER_RIGHT_PORT,
|
||||
can_interface="socketcan",
|
||||
id="openarms_follower",
|
||||
disable_torque_on_disconnect=True,
|
||||
max_relative_target=10.0,
|
||||
cameras=CAMERA_CONFIG,
|
||||
)
|
||||
follower = OpenArmsFollower(follower_config)
|
||||
follower.connect(calibrate=False)
|
||||
|
||||
if not follower.is_connected:
|
||||
raise RuntimeError("Robot failed to connect!")
|
||||
print(" Robot connected")
|
||||
|
||||
# Initialize leader for resets
|
||||
leader = None
|
||||
if USE_LEADER_FOR_RESETS:
|
||||
print("\n Connecting leader for resets...")
|
||||
leader_config = OpenArmsLeaderConfig(
|
||||
port_left=LEADER_LEFT_PORT,
|
||||
port_right=LEADER_RIGHT_PORT,
|
||||
can_interface="socketcan",
|
||||
id="openarms_leader",
|
||||
manual_control=False,
|
||||
)
|
||||
leader = OpenArmsLeader(leader_config)
|
||||
leader.connect(calibrate=False)
|
||||
|
||||
if leader.is_connected and leader.pin_robot is not None:
|
||||
leader.bus_right.enable_torque()
|
||||
leader.bus_left.enable_torque()
|
||||
print(" Leader connected with gravity compensation")
|
||||
|
||||
# Create dataset for saving evaluation data
|
||||
print(f"\n[3/5] Creating evaluation dataset...")
|
||||
teleop_action_processor, robot_action_processor, robot_observation_processor = make_default_processors()
|
||||
action_features_hw = {k: v for k, v in follower.action_features.items() if k.endswith(".pos")}
|
||||
|
||||
dataset_features = combine_feature_dicts(
|
||||
aggregate_pipeline_dataset_features(
|
||||
pipeline=teleop_action_processor,
|
||||
initial_features=create_initial_features(action=action_features_hw),
|
||||
use_videos=True,
|
||||
),
|
||||
aggregate_pipeline_dataset_features(
|
||||
pipeline=robot_observation_processor,
|
||||
initial_features=create_initial_features(observation=follower.observation_features),
|
||||
use_videos=True,
|
||||
),
|
||||
)
|
||||
|
||||
dataset_path = Path.home() / ".cache" / "huggingface" / "lerobot" / HF_EVAL_DATASET_ID
|
||||
if dataset_path.exists():
|
||||
print(f" Dataset exists at: {dataset_path}")
|
||||
if input(" Continue and overwrite? (y/n): ").strip().lower() != 'y':
|
||||
follower.disconnect()
|
||||
return
|
||||
|
||||
dataset = LeRobotDataset.create(
|
||||
repo_id=HF_EVAL_DATASET_ID,
|
||||
fps=FPS,
|
||||
features=dataset_features,
|
||||
robot_type=follower.name,
|
||||
use_videos=True,
|
||||
image_writer_processes=0,
|
||||
image_writer_threads=12,
|
||||
)
|
||||
print(" Dataset created")
|
||||
|
||||
# Load policy directly using from_pretrained to preserve original EE features
|
||||
# (make_policy would overwrite output_features with joint features from eval dataset)
|
||||
print(f"\n[4/5] Loading policy from {HF_MODEL_ID}...")
|
||||
from lerobot.policies.factory import get_policy_class
|
||||
|
||||
policy_config = PreTrainedConfig.from_pretrained(HF_MODEL_ID)
|
||||
policy_cls = get_policy_class(policy_config.type)
|
||||
policy = policy_cls.from_pretrained(HF_MODEL_ID)
|
||||
|
||||
# Load preprocessor/postprocessor from pretrained model
|
||||
# (uses the trained EE features, not joint features from eval dataset)
|
||||
preprocessor, postprocessor = make_pre_post_processors(
|
||||
policy_cfg=policy.config,
|
||||
pretrained_path=HF_MODEL_ID,
|
||||
preprocessor_overrides={
|
||||
"device_processor": {"device": str(policy.config.device)}
|
||||
},
|
||||
)
|
||||
print(" Policy loaded")
|
||||
print(f" State dim: {policy.config.input_features['observation.state'].shape[0]}")
|
||||
print(f" Action dim: {policy.config.output_features['action'].shape[0]}")
|
||||
|
||||
# Auto-detect relative action/state settings from checkpoint
|
||||
relative_normalizer, use_relative_actions, use_relative_state = load_relative_config(HF_MODEL_ID)
|
||||
|
||||
mode = "absolute"
|
||||
if use_relative_actions:
|
||||
mode = "relative actions + state" if use_relative_state else "relative actions only"
|
||||
print(f" Mode: {mode}")
|
||||
|
||||
# Initialize keyboard listener and visualization
|
||||
print("\n[5/5] Starting evaluation...")
|
||||
listener, events = init_keyboard_listener()
|
||||
init_rerun(session_name="openarms_eval_ee")
|
||||
|
||||
print("\nControls: ESC=stop, →=next episode, ←=rerecord")
|
||||
episode_idx = 0
|
||||
|
||||
try:
|
||||
while episode_idx < NUM_EPISODES and not events.get("stop_recording"):
|
||||
log_say(f"Episode {episode_idx + 1} of {NUM_EPISODES}")
|
||||
print(f"\n{'='*50}")
|
||||
print(f"Episode {episode_idx + 1}/{NUM_EPISODES}")
|
||||
print(f"{'='*50}")
|
||||
|
||||
input("\nPress ENTER to start episode...")
|
||||
events["exit_early"] = False
|
||||
|
||||
# Run inference with EE conversion
|
||||
run_ee_inference_loop(
|
||||
robot=follower,
|
||||
policy=policy,
|
||||
preprocessor=preprocessor,
|
||||
postprocessor=postprocessor,
|
||||
joints_to_ee=joints_to_ee,
|
||||
ee_to_joints=ee_to_joints,
|
||||
dataset=dataset,
|
||||
fps=FPS,
|
||||
duration_s=EPISODE_TIME_SEC,
|
||||
events=events,
|
||||
task=TASK_DESCRIPTION,
|
||||
use_relative_actions=use_relative_actions,
|
||||
use_relative_state=use_relative_state,
|
||||
relative_normalizer=relative_normalizer,
|
||||
)
|
||||
|
||||
# Handle re-recording
|
||||
if events.get("rerecord_episode", False):
|
||||
log_say("Re-recording episode")
|
||||
events["rerecord_episode"] = False
|
||||
events["exit_early"] = False
|
||||
dataset.clear_episode_buffer()
|
||||
continue
|
||||
|
||||
# Save episode if we have data
|
||||
if dataset.episode_buffer is not None and dataset.episode_buffer.get("size", 0) > 0:
|
||||
print(f" Saving episode {episode_idx + 1}...")
|
||||
dataset.save_episode()
|
||||
episode_idx += 1
|
||||
|
||||
events["exit_early"] = False
|
||||
|
||||
# Reset between episodes
|
||||
if episode_idx < NUM_EPISODES and not events.get("stop_recording"):
|
||||
if USE_LEADER_FOR_RESETS and leader and leader.is_connected:
|
||||
log_say("Reset environment using leader arms")
|
||||
print(f"\nManual reset ({RESET_TIME_SEC}s) - use leader arms...")
|
||||
|
||||
reset_start = time.perf_counter()
|
||||
while time.perf_counter() - reset_start < RESET_TIME_SEC:
|
||||
if events.get("exit_early") or events.get("stop_recording"):
|
||||
break
|
||||
|
||||
leader_action = leader.get_action()
|
||||
follower_action = {k: v for k, v in leader_action.items() if k.endswith(".pos")}
|
||||
if follower_action:
|
||||
follower.send_action(follower_action)
|
||||
time.sleep(1/FPS)
|
||||
else:
|
||||
input("\nReset environment and press ENTER...")
|
||||
|
||||
print(f"\n✓ Evaluation complete! {episode_idx} episodes recorded")
|
||||
log_say("Evaluation complete", blocking=True)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nEvaluation interrupted")
|
||||
|
||||
finally:
|
||||
if leader:
|
||||
if hasattr(leader, 'bus_right'):
|
||||
leader.bus_right.disable_torque()
|
||||
if hasattr(leader, 'bus_left'):
|
||||
leader.bus_left.disable_torque()
|
||||
leader.disconnect()
|
||||
|
||||
follower.disconnect()
|
||||
|
||||
if listener is not None:
|
||||
listener.stop()
|
||||
|
||||
# Finalize and push dataset
|
||||
dataset.finalize()
|
||||
print("Uploading to Hub...")
|
||||
dataset.push_to_hub(private=True)
|
||||
|
||||
print("✓ Done!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
OpenArms Policy Evaluation with Relative Actions
|
||||
|
||||
Two modes supported (based on training config):
|
||||
Mode 1: Relative actions only (use_relative_state=False)
|
||||
- Policy outputs relative action deltas
|
||||
- State input is absolute
|
||||
Mode 2: Relative actions + state (use_relative_state=True)
|
||||
- Policy outputs relative action deltas
|
||||
- State input is also converted to relative
|
||||
|
||||
Example usage:
|
||||
python examples/openarms/evaluate_relative.py
|
||||
"""
|
||||
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import torch
|
||||
|
||||
from lerobot.cameras.opencv.configuration_opencv import OpenCVCameraConfig
|
||||
from lerobot.configs.policies import PreTrainedConfig
|
||||
from lerobot.configs.train import TrainPipelineConfig
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
||||
from lerobot.datasets.pipeline_features import aggregate_pipeline_dataset_features, create_initial_features
|
||||
from lerobot.datasets.utils import build_dataset_frame, combine_feature_dicts
|
||||
from lerobot.policies.factory import make_policy, make_pre_post_processors
|
||||
from lerobot.processor import make_default_processors
|
||||
from lerobot.robots.openarms.config_openarms_follower import OpenArmsFollowerConfig
|
||||
from lerobot.robots.openarms.openarms_follower import OpenArmsFollower
|
||||
from lerobot.utils.constants import ACTION, OBS_STR
|
||||
from lerobot.utils.control_utils import init_keyboard_listener, predict_action
|
||||
from lerobot.utils.robot_utils import precise_sleep
|
||||
from lerobot.utils.utils import get_safe_torch_device
|
||||
from lerobot.utils.relative_actions import (
|
||||
convert_from_relative_actions_dict,
|
||||
convert_state_to_relative,
|
||||
PerTimestepNormalizer,
|
||||
)
|
||||
from lerobot.utils.utils import log_say
|
||||
from lerobot.utils.visualization_utils import init_rerun, log_rerun_data
|
||||
|
||||
|
||||
# Configuration
|
||||
HF_MODEL_ID = "your-org/your-relative-policy"
|
||||
HF_EVAL_DATASET_ID = "your-org/your-eval-dataset"
|
||||
TASK_DESCRIPTION = "your task description"
|
||||
|
||||
NUM_EPISODES = 1
|
||||
FPS = 30
|
||||
EPISODE_TIME_SEC = 1000
|
||||
|
||||
FOLLOWER_LEFT_PORT = "can0"
|
||||
FOLLOWER_RIGHT_PORT = "can1"
|
||||
|
||||
CAMERA_CONFIG = {
|
||||
"left_wrist": OpenCVCameraConfig(index_or_path="/dev/video5", width=640, height=480, fps=FPS),
|
||||
"right_wrist": OpenCVCameraConfig(index_or_path="/dev/video1", width=640, height=480, fps=FPS),
|
||||
"base": OpenCVCameraConfig(index_or_path="/dev/video3", width=640, height=480, fps=FPS),
|
||||
}
|
||||
|
||||
|
||||
def load_relative_config(model_path: Path | str) -> tuple[PerTimestepNormalizer | None, bool]:
|
||||
"""Load normalizer and relative_state setting from checkpoint."""
|
||||
model_path = Path(model_path) if isinstance(model_path, str) else model_path
|
||||
normalizer = None
|
||||
use_relative_state = False
|
||||
|
||||
# Try local path first
|
||||
if model_path.exists():
|
||||
stats_path = model_path / "relative_stats.pt"
|
||||
if stats_path.exists():
|
||||
normalizer = PerTimestepNormalizer.load(stats_path)
|
||||
print(f"Loaded per-timestep stats from: {stats_path}")
|
||||
|
||||
config_path = model_path / "train_config.json"
|
||||
if config_path.exists():
|
||||
cfg = TrainPipelineConfig.from_pretrained(model_path)
|
||||
use_relative_state = getattr(cfg, "use_relative_state", False)
|
||||
else:
|
||||
# Try hub
|
||||
try:
|
||||
from huggingface_hub import hf_hub_download
|
||||
stats_file = hf_hub_download(repo_id=str(model_path), filename="relative_stats.pt")
|
||||
normalizer = PerTimestepNormalizer.load(stats_file)
|
||||
print("Loaded per-timestep stats from hub")
|
||||
|
||||
config_file = hf_hub_download(repo_id=str(model_path), filename="train_config.json")
|
||||
cfg = TrainPipelineConfig.from_pretrained(Path(config_file).parent)
|
||||
use_relative_state = getattr(cfg, "use_relative_state", False)
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not load relative config: {e}")
|
||||
|
||||
return normalizer, use_relative_state
|
||||
|
||||
|
||||
def inference_loop_relative(
|
||||
robot,
|
||||
policy,
|
||||
preprocessor,
|
||||
postprocessor,
|
||||
dataset,
|
||||
events,
|
||||
fps: int,
|
||||
control_time_s: float,
|
||||
single_task: str,
|
||||
display_data: bool = True,
|
||||
state_key: str = "observation.state",
|
||||
relative_normalizer: PerTimestepNormalizer | None = None,
|
||||
use_relative_state: bool = False,
|
||||
):
|
||||
"""
|
||||
Inference loop for relative action policies.
|
||||
|
||||
If use_relative_state=True, also converts observation state to relative.
|
||||
"""
|
||||
device = get_safe_torch_device(policy.config.device)
|
||||
timestamp = 0
|
||||
start_t = time.perf_counter()
|
||||
|
||||
while timestamp < control_time_s:
|
||||
loop_start = time.perf_counter()
|
||||
|
||||
if events["exit_early"] or events["stop_recording"]:
|
||||
break
|
||||
|
||||
obs = robot.get_observation()
|
||||
observation_frame = build_dataset_frame(dataset.features, obs, prefix=OBS_STR)
|
||||
current_pos = {k: v for k, v in obs.items() if k.endswith(".pos")}
|
||||
|
||||
# Convert state to relative if using full UMI mode
|
||||
if use_relative_state and state_key in observation_frame:
|
||||
state_tensor = observation_frame[state_key]
|
||||
if isinstance(state_tensor, torch.Tensor):
|
||||
observation_frame[state_key] = convert_state_to_relative(state_tensor)
|
||||
|
||||
# Policy inference (outputs action tensor)
|
||||
action_tensor = predict_action(
|
||||
observation=observation_frame,
|
||||
policy=policy,
|
||||
device=device,
|
||||
preprocessor=preprocessor,
|
||||
postprocessor=postprocessor,
|
||||
use_amp=policy.config.use_amp,
|
||||
task=single_task,
|
||||
robot_type=robot.robot_type,
|
||||
)
|
||||
|
||||
# Unnormalize relative actions if normalizer exists
|
||||
if relative_normalizer is not None:
|
||||
# action_tensor shape: [1, action_dim] or [action_dim]
|
||||
if action_tensor.dim() == 1:
|
||||
action_tensor = action_tensor.unsqueeze(0).unsqueeze(0) # [1, 1, action_dim]
|
||||
elif action_tensor.dim() == 2:
|
||||
action_tensor = action_tensor.unsqueeze(1) # [batch, 1, action_dim]
|
||||
action_tensor = relative_normalizer.unnormalize(action_tensor)
|
||||
|
||||
# Flatten to 1D: take first timestep if chunks, squeeze batch dims
|
||||
while action_tensor.dim() > 1:
|
||||
action_tensor = action_tensor[0]
|
||||
|
||||
# Manually convert to dict (tensor_to_robot_action expects specific shape)
|
||||
action_names = dataset.features[ACTION]["names"]
|
||||
relative_action = {name: float(action_tensor[i]) for i, name in enumerate(action_names)}
|
||||
|
||||
# Convert relative to absolute
|
||||
absolute_action = convert_from_relative_actions_dict(relative_action, current_pos)
|
||||
|
||||
robot.send_action(absolute_action)
|
||||
|
||||
if dataset is not None:
|
||||
action_frame = build_dataset_frame(dataset.features, absolute_action, prefix=ACTION)
|
||||
frame = {**observation_frame, **action_frame, "task": single_task}
|
||||
dataset.add_frame(frame)
|
||||
|
||||
if display_data:
|
||||
log_rerun_data(observation=obs, action=absolute_action)
|
||||
|
||||
dt = time.perf_counter() - loop_start
|
||||
precise_sleep(1 / fps - dt)
|
||||
timestamp = time.perf_counter() - start_t
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print(" OpenArms Evaluation - Relative Actions")
|
||||
print("=" * 60)
|
||||
print(f"\nModel: {HF_MODEL_ID}")
|
||||
print(f"Dataset: {HF_EVAL_DATASET_ID}")
|
||||
print(f"Episodes: {NUM_EPISODES}, Duration: {EPISODE_TIME_SEC}s")
|
||||
|
||||
# Load relative action config
|
||||
relative_normalizer, use_relative_state = load_relative_config(HF_MODEL_ID)
|
||||
mode = "actions + state" if use_relative_state else "actions only"
|
||||
print(f"Mode: relative {mode}")
|
||||
|
||||
# Setup robot
|
||||
follower_config = OpenArmsFollowerConfig(
|
||||
port_left=FOLLOWER_LEFT_PORT,
|
||||
port_right=FOLLOWER_RIGHT_PORT,
|
||||
can_interface="socketcan",
|
||||
id="openarms_follower",
|
||||
disable_torque_on_disconnect=True,
|
||||
max_relative_target=10.0,
|
||||
cameras=CAMERA_CONFIG,
|
||||
)
|
||||
|
||||
follower = OpenArmsFollower(follower_config)
|
||||
follower.connect(calibrate=False)
|
||||
|
||||
if not follower.is_connected:
|
||||
raise RuntimeError("Robot failed to connect!")
|
||||
|
||||
teleop_action_processor, robot_action_processor, robot_observation_processor = make_default_processors()
|
||||
action_features_hw = {k: v for k, v in follower.action_features.items() if k.endswith(".pos")}
|
||||
|
||||
dataset_features = combine_feature_dicts(
|
||||
aggregate_pipeline_dataset_features(
|
||||
pipeline=teleop_action_processor,
|
||||
initial_features=create_initial_features(action=action_features_hw),
|
||||
use_videos=True,
|
||||
),
|
||||
aggregate_pipeline_dataset_features(
|
||||
pipeline=robot_observation_processor,
|
||||
initial_features=create_initial_features(observation=follower.observation_features),
|
||||
use_videos=True,
|
||||
),
|
||||
)
|
||||
|
||||
dataset_path = Path.home() / ".cache" / "huggingface" / "lerobot" / HF_EVAL_DATASET_ID
|
||||
if dataset_path.exists():
|
||||
print(f"\nDataset exists at: {dataset_path}")
|
||||
if input("Continue? (y/n): ").strip().lower() != 'y':
|
||||
follower.disconnect()
|
||||
return
|
||||
|
||||
dataset = LeRobotDataset.create(
|
||||
repo_id=HF_EVAL_DATASET_ID,
|
||||
fps=FPS,
|
||||
features=dataset_features,
|
||||
robot_type=follower.name,
|
||||
use_videos=True,
|
||||
image_writer_processes=0,
|
||||
image_writer_threads=12,
|
||||
)
|
||||
|
||||
policy_config = PreTrainedConfig.from_pretrained(HF_MODEL_ID)
|
||||
policy_config.pretrained_path = HF_MODEL_ID
|
||||
policy = make_policy(policy_config, ds_meta=dataset.meta)
|
||||
|
||||
preprocessor, postprocessor = make_pre_post_processors(
|
||||
policy_cfg=policy.config,
|
||||
pretrained_path=HF_MODEL_ID,
|
||||
dataset_stats=dataset.meta.stats,
|
||||
preprocessor_overrides={"device_processor": {"device": str(policy.config.device)}},
|
||||
)
|
||||
|
||||
listener, events = init_keyboard_listener()
|
||||
init_rerun(session_name="openarms_eval_relative")
|
||||
episode_idx = 0
|
||||
|
||||
print("\nControls: ESC=stop, →=next episode, ←=rerecord")
|
||||
|
||||
try:
|
||||
while episode_idx < NUM_EPISODES and not events["stop_recording"]:
|
||||
log_say(f"Episode {episode_idx + 1} of {NUM_EPISODES}")
|
||||
|
||||
inference_loop_relative(
|
||||
robot=follower,
|
||||
policy=policy,
|
||||
preprocessor=preprocessor,
|
||||
postprocessor=postprocessor,
|
||||
dataset=dataset,
|
||||
events=events,
|
||||
fps=FPS,
|
||||
control_time_s=EPISODE_TIME_SEC,
|
||||
single_task=TASK_DESCRIPTION,
|
||||
display_data=True,
|
||||
relative_normalizer=relative_normalizer,
|
||||
use_relative_state=use_relative_state,
|
||||
)
|
||||
|
||||
if events.get("rerecord_episode", False):
|
||||
log_say("Re-recording")
|
||||
events["rerecord_episode"] = False
|
||||
events["exit_early"] = False
|
||||
dataset.clear_episode_buffer()
|
||||
continue
|
||||
|
||||
if dataset.episode_buffer is not None and dataset.episode_buffer.get("size", 0) > 0:
|
||||
print(f"Saving episode {episode_idx + 1}...")
|
||||
dataset.save_episode()
|
||||
episode_idx += 1
|
||||
|
||||
events["exit_early"] = False
|
||||
|
||||
if not events["stop_recording"] and episode_idx < NUM_EPISODES:
|
||||
input("Press ENTER for next episode...")
|
||||
|
||||
print(f"\nDone! {episode_idx} episodes recorded")
|
||||
log_say("Complete", blocking=True)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nInterrupted")
|
||||
|
||||
finally:
|
||||
follower.disconnect()
|
||||
if listener is not None:
|
||||
listener.stop()
|
||||
dataset.finalize()
|
||||
print("Uploading to Hub...")
|
||||
dataset.push_to_hub(private=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -33,11 +33,6 @@ Example usage:
|
||||
python examples/openarms/evaluate_with_rtc.py \
|
||||
--rtc.execution_horizon=12 \
|
||||
--rtc.max_guidance_weight=10.0
|
||||
|
||||
# With action interpolation (policy at 30Hz, robot at 50Hz)
|
||||
python examples/openarms/evaluate_with_rtc.py \
|
||||
--action_interpolation_enabled=true \
|
||||
--control_hz=50
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -87,8 +82,6 @@ DEFAULT_FPS = 30
|
||||
DEFAULT_EPISODE_TIME_SEC = 300
|
||||
DEFAULT_RESET_TIME_SEC = 60
|
||||
|
||||
DEFAULT_CONTROL_HZ = 50
|
||||
|
||||
DEFAULT_FOLLOWER_LEFT_PORT = "can0"
|
||||
DEFAULT_FOLLOWER_RIGHT_PORT = "can1"
|
||||
|
||||
@@ -174,9 +167,6 @@ class OpenArmsRTCEvalConfig(HubMixin):
|
||||
record_dataset: bool = True
|
||||
push_to_hub: bool = True
|
||||
|
||||
action_interpolation_enabled: bool = False
|
||||
control_hz: float = DEFAULT_CONTROL_HZ
|
||||
|
||||
use_torch_compile: bool = False
|
||||
torch_compile_backend: str = "inductor"
|
||||
torch_compile_mode: str = "default"
|
||||
@@ -319,11 +309,6 @@ def get_actions_thread(
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def _interpolate_actions(prev_action: Tensor, next_action: Tensor, alpha: float) -> Tensor:
|
||||
"""Linear interpolation between two action tensors."""
|
||||
return prev_action + alpha * (next_action - prev_action)
|
||||
|
||||
|
||||
def actor_thread(
|
||||
robot: RobotWrapper,
|
||||
robot_action_processor,
|
||||
@@ -339,101 +324,49 @@ def actor_thread(
|
||||
"""Thread function to execute actions on the robot."""
|
||||
try:
|
||||
logger.info("[ACTOR] Starting actor thread")
|
||||
logger.info(f"[ACTOR] interpolation={cfg.action_interpolation_enabled}, control_hz={cfg.control_hz}")
|
||||
|
||||
action_count = 0
|
||||
action_interval = 1.0 / cfg.fps
|
||||
action_keys = [k for k in robot.action_features.keys() if k.endswith(".pos")]
|
||||
|
||||
if cfg.action_interpolation_enabled:
|
||||
control_interval = 1.0 / cfg.control_hz
|
||||
interp_steps = int(cfg.control_hz / cfg.fps)
|
||||
else:
|
||||
control_interval = 1.0 / cfg.fps
|
||||
interp_steps = 1
|
||||
|
||||
prev_action: Tensor | None = None
|
||||
current_action: Tensor | None = None
|
||||
interp_step = 0
|
||||
last_dataset_frame_time = 0.0
|
||||
|
||||
while not shutdown_event.is_set():
|
||||
if not episode_active.is_set():
|
||||
prev_action = None
|
||||
current_action = None
|
||||
interp_step = 0
|
||||
time.sleep(0.01)
|
||||
continue
|
||||
|
||||
start_time = time.perf_counter()
|
||||
action = action_queue.get()
|
||||
|
||||
if cfg.action_interpolation_enabled:
|
||||
if interp_step == 0 or current_action is None:
|
||||
new_action = action_queue.get()
|
||||
if new_action is not None:
|
||||
prev_action = current_action if current_action is not None else new_action.cpu()
|
||||
current_action = new_action.cpu()
|
||||
interp_step = 0
|
||||
if action is not None:
|
||||
action = action.cpu()
|
||||
|
||||
if current_action is not None:
|
||||
if prev_action is not None and interp_steps > 1:
|
||||
alpha = (interp_step + 1) / interp_steps
|
||||
action_to_send = _interpolate_actions(prev_action, current_action, alpha)
|
||||
else:
|
||||
action_to_send = current_action
|
||||
action_dict = {}
|
||||
for i, key in enumerate(action_keys):
|
||||
if i < len(action):
|
||||
action_dict[key] = action[i].item()
|
||||
|
||||
action_dict = {}
|
||||
for i, key in enumerate(action_keys):
|
||||
if i < len(action_to_send):
|
||||
action_dict[key] = action_to_send[i].item()
|
||||
action_processed = robot_action_processor((action_dict, None))
|
||||
robot.send_action(action_processed)
|
||||
|
||||
action_processed = robot_action_processor((action_dict, None))
|
||||
robot.send_action(action_processed)
|
||||
action_count += 1
|
||||
if cfg.record_dataset and dataset is not None:
|
||||
with dataset_lock:
|
||||
obs = robot.get_observation()
|
||||
obs_processed = robot_observation_processor(obs)
|
||||
action_for_dataset = teleop_action_processor((action_dict, None))
|
||||
|
||||
interp_step = (interp_step + 1) % interp_steps
|
||||
frame = {}
|
||||
for key, value in obs_processed.items():
|
||||
frame[f"observation.{key}"] = value
|
||||
for key, value in action_for_dataset.items():
|
||||
frame[f"action.{key}"] = value
|
||||
frame["task"] = cfg.task
|
||||
|
||||
if cfg.record_dataset and dataset is not None:
|
||||
if time.perf_counter() - last_dataset_frame_time >= (1.0 / cfg.fps):
|
||||
last_dataset_frame_time = time.perf_counter()
|
||||
with dataset_lock:
|
||||
obs = robot.get_observation()
|
||||
obs_processed = robot_observation_processor(obs)
|
||||
action_for_dataset = teleop_action_processor((action_dict, None))
|
||||
frame = {}
|
||||
for key, value in obs_processed.items():
|
||||
frame[f"observation.{key}"] = value
|
||||
for key, value in action_for_dataset.items():
|
||||
frame[f"action.{key}"] = value
|
||||
frame["task"] = cfg.task
|
||||
dataset.add_frame(frame)
|
||||
else:
|
||||
action = action_queue.get()
|
||||
if action is not None:
|
||||
action = action.cpu()
|
||||
action_dict = {}
|
||||
for i, key in enumerate(action_keys):
|
||||
if i < len(action):
|
||||
action_dict[key] = action[i].item()
|
||||
dataset.add_frame(frame)
|
||||
|
||||
action_processed = robot_action_processor((action_dict, None))
|
||||
robot.send_action(action_processed)
|
||||
action_count += 1
|
||||
|
||||
if cfg.record_dataset and dataset is not None:
|
||||
with dataset_lock:
|
||||
obs = robot.get_observation()
|
||||
obs_processed = robot_observation_processor(obs)
|
||||
action_for_dataset = teleop_action_processor((action_dict, None))
|
||||
frame = {}
|
||||
for key, value in obs_processed.items():
|
||||
frame[f"observation.{key}"] = value
|
||||
for key, value in action_for_dataset.items():
|
||||
frame[f"action.{key}"] = value
|
||||
frame["task"] = cfg.task
|
||||
dataset.add_frame(frame)
|
||||
action_count += 1
|
||||
|
||||
dt_s = time.perf_counter() - start_time
|
||||
sleep_time = max(0, control_interval - dt_s - 0.001)
|
||||
sleep_time = max(0, action_interval - dt_s - 0.001)
|
||||
if sleep_time > 0:
|
||||
time.sleep(sleep_time)
|
||||
|
||||
@@ -501,9 +434,6 @@ def main(cfg: OpenArmsRTCEvalConfig):
|
||||
print(f"RTC Enabled: {cfg.rtc.enabled}")
|
||||
print(f"RTC Execution Horizon: {cfg.rtc.execution_horizon}")
|
||||
print(f"RTC Max Guidance Weight: {cfg.rtc.max_guidance_weight}")
|
||||
print(f"Action Interpolation: {cfg.action_interpolation_enabled}")
|
||||
if cfg.action_interpolation_enabled:
|
||||
print(f"Control Hz: {cfg.control_hz}")
|
||||
print(f"Device: {cfg.device}")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2025 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.
|
||||
|
||||
"""
|
||||
OpenArms End-Effector Replay Example with Visualization
|
||||
|
||||
Replays a dataset recorded with absolute joint positions by:
|
||||
1. Converting joint positions to EE poses using FK
|
||||
2. Converting EE poses back to joint positions using IK
|
||||
3. Sending joint commands to the robot OR visualizing in simulation
|
||||
|
||||
Supports three modes:
|
||||
- real: Send commands to physical robot
|
||||
- sim: Visualize in simulation only (no robot required)
|
||||
- both: Real robot + visualization
|
||||
|
||||
Example usage:
|
||||
python examples/openarms/replay_ee.py --mode sim
|
||||
python examples/openarms/replay_ee.py --mode real
|
||||
python examples/openarms/replay_ee.py --mode both --visualizer meshcat
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import time
|
||||
from os.path import dirname, expanduser
|
||||
|
||||
import numpy as np
|
||||
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
||||
from lerobot.model.kinematics import RobotKinematics
|
||||
from lerobot.processor import RobotAction, RobotObservation, RobotProcessorPipeline
|
||||
from lerobot.processor.converters import (
|
||||
robot_action_observation_to_transition,
|
||||
robot_action_to_transition,
|
||||
transition_to_robot_action,
|
||||
)
|
||||
from lerobot.robots.openarms.robot_kinematic_processor import (
|
||||
BimanualEEBoundsAndSafety,
|
||||
BimanualForwardKinematicsJointsToEE,
|
||||
BimanualInverseKinematicsEEToJoints,
|
||||
)
|
||||
from lerobot.utils.constants import ACTION
|
||||
from lerobot.utils.robot_utils import precise_sleep
|
||||
|
||||
|
||||
# Default configuration
|
||||
DEFAULT_EPISODE_IDX = 0
|
||||
DEFAULT_DATASET = "lerobot-data-collection/rac_blackf0"
|
||||
DEFAULT_URDF = "src/lerobot/robots/openarms/urdf/openarm_bimanual_pybullet.urdf"
|
||||
DEFAULT_LEFT_EE_FRAME = "openarm_left_hand_tcp"
|
||||
DEFAULT_RIGHT_EE_FRAME = "openarm_right_hand_tcp"
|
||||
|
||||
# Motor names as used in the dataset actions (e.g., left_joint_1.pos)
|
||||
MOTOR_NAMES = ["joint_1", "joint_2", "joint_3", "joint_4", "joint_5", "joint_6", "joint_7", "gripper"]
|
||||
|
||||
# URDF joint names (no underscore between "joint" and number)
|
||||
LEFT_URDF_JOINTS = [f"openarm_left_joint{i}" for i in range(1, 8)]
|
||||
RIGHT_URDF_JOINTS = [f"openarm_right_joint{i}" for i in range(1, 8)]
|
||||
|
||||
|
||||
class MeshcatVisualizer:
|
||||
"""Lightweight URDF visualizer using pinocchio + meshcat."""
|
||||
|
||||
def __init__(self, urdf_path: str):
|
||||
import pinocchio as pin
|
||||
from pinocchio.visualize import MeshcatVisualizer as PinMeshcat
|
||||
|
||||
urdf_dir = dirname(urdf_path)
|
||||
self.model, self.collision_model, self.visual_model = pin.buildModelsFromUrdf(
|
||||
urdf_path, urdf_dir, pin.JointModelFreeFlyer()
|
||||
)
|
||||
self.data = self.model.createData()
|
||||
|
||||
self.viz = PinMeshcat(self.model, self.collision_model, self.visual_model)
|
||||
self.viz.initViewer(open=True)
|
||||
self.viz.loadViewerModel()
|
||||
|
||||
# Build joint name mapping: dataset name -> pinocchio joint index
|
||||
# Dataset uses: left_joint_1, right_joint_2, etc.
|
||||
# URDF uses: openarm_left_joint1, openarm_right_joint2, etc.
|
||||
self.joint_map = {}
|
||||
for jid in range(1, self.model.njoints):
|
||||
urdf_name = self.model.names[jid] # e.g., "openarm_left_joint1"
|
||||
# Extract side and number
|
||||
if "left_joint" in urdf_name:
|
||||
num = urdf_name.split("joint")[-1] # "1"
|
||||
dataset_name = f"left_joint_{num}"
|
||||
self.joint_map[dataset_name] = jid
|
||||
elif "right_joint" in urdf_name:
|
||||
num = urdf_name.split("joint")[-1]
|
||||
dataset_name = f"right_joint_{num}"
|
||||
self.joint_map[dataset_name] = jid
|
||||
|
||||
print(f" Meshcat viewer opened (mapped {len(self.joint_map)} joints)")
|
||||
print(f" Joint map: {list(self.joint_map.keys())[:4]}...")
|
||||
print(" Waiting for meshcat to load...")
|
||||
time.sleep(3) # Give meshcat time to load meshes
|
||||
self._first_update = True
|
||||
|
||||
def update(self, joint_positions: dict[str, float]):
|
||||
"""Update visualization with new joint positions."""
|
||||
if self._first_update:
|
||||
pos_keys = [k for k in joint_positions.keys() if k.endswith(".pos")]
|
||||
print(f" First update keys: {pos_keys[:4]}...")
|
||||
# Print sample values
|
||||
for k in pos_keys[:2]:
|
||||
print(f" {k} = {joint_positions[k]:.2f}")
|
||||
|
||||
# Build configuration vector (base pose + joints)
|
||||
# Free flyer base: [x, y, z, qx, qy, qz, qw]
|
||||
q = np.zeros(self.model.nq)
|
||||
q[3:7] = [0, 0, 0, 1] # Identity quaternion
|
||||
|
||||
matched = 0
|
||||
# Map joint positions using pre-built mapping
|
||||
for name, pos in joint_positions.items():
|
||||
if not name.endswith(".pos"):
|
||||
continue
|
||||
joint_name = name.removesuffix(".pos") # e.g., "left_joint_1"
|
||||
|
||||
jid = self.joint_map.get(joint_name)
|
||||
if jid is not None:
|
||||
idx = self.model.idx_qs[jid]
|
||||
if idx < len(q):
|
||||
q[idx] = np.deg2rad(pos)
|
||||
matched += 1
|
||||
|
||||
if self._first_update:
|
||||
print(f" Matched {matched} joints, q[7:14] = {q[7:14]}")
|
||||
self._first_update = False
|
||||
|
||||
self.viz.display(q)
|
||||
|
||||
|
||||
class RerunVisualizer:
|
||||
"""Rerun-based visualizer for plots and EE trajectories."""
|
||||
|
||||
def __init__(self, urdf_path: str = None, session_name: str = "openarms_replay"):
|
||||
import rerun as rr
|
||||
self.rr = rr
|
||||
rr.init(session_name)
|
||||
rr.spawn(memory_limit="10%")
|
||||
print(" Rerun viewer spawned (plots only, use --visualizer meshcat for 3D robot)")
|
||||
|
||||
def update(self, joint_positions: dict[str, float], ee_poses: dict[str, float], frame_idx: int):
|
||||
"""Log joint positions and EE poses."""
|
||||
self.rr.set_time("frame", sequence=frame_idx)
|
||||
|
||||
# Log EE positions as colored spheres
|
||||
for prefix, color in [("left", [255, 100, 100]), ("right", [100, 100, 255])]:
|
||||
x, y, z = ee_poses.get(f"{prefix}_ee.x"), ee_poses.get(f"{prefix}_ee.y"), ee_poses.get(f"{prefix}_ee.z")
|
||||
if None not in (x, y, z):
|
||||
self.rr.log(f"ee/{prefix}", self.rr.Points3D([[x, y, z]], colors=[color], radii=[0.02]))
|
||||
|
||||
# Log joint positions as time series
|
||||
for name, pos in joint_positions.items():
|
||||
if name.endswith(".pos"):
|
||||
self.rr.log(f"joints/{name}", self.rr.Scalars(pos))
|
||||
|
||||
# Log EE poses as time series
|
||||
for name, val in ee_poses.items():
|
||||
self.rr.log(f"ee_plots/{name}", self.rr.Scalars(val))
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="OpenArms EE Replay with Visualization")
|
||||
parser.add_argument("--mode", choices=["real", "sim", "both"], default="sim",
|
||||
help="Execution mode: real (robot), sim (visualization), both")
|
||||
parser.add_argument("--visualizer", choices=["meshcat", "rerun", "none"], default="meshcat",
|
||||
help="Visualization backend (meshcat shows 3D robot, rerun shows plots)")
|
||||
parser.add_argument("--dataset", type=str, default=DEFAULT_DATASET,
|
||||
help="Dataset repo ID")
|
||||
parser.add_argument("--episode", type=int, default=DEFAULT_EPISODE_IDX,
|
||||
help="Episode index to replay")
|
||||
parser.add_argument("--urdf", type=str, default=DEFAULT_URDF,
|
||||
help="Path to URDF file")
|
||||
parser.add_argument("--left-ee-frame", type=str, default=DEFAULT_LEFT_EE_FRAME,
|
||||
help="Left arm end-effector frame name in URDF")
|
||||
parser.add_argument("--right-ee-frame", type=str, default=DEFAULT_RIGHT_EE_FRAME,
|
||||
help="Right arm end-effector frame name in URDF")
|
||||
parser.add_argument("--port-left", type=str, default="can2",
|
||||
help="CAN port for left arm")
|
||||
parser.add_argument("--port-right", type=str, default="can3",
|
||||
help="CAN port for right arm")
|
||||
parser.add_argument("--speed", type=float, default=1.0,
|
||||
help="Playback speed multiplier")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
use_robot = args.mode in ["real", "both"]
|
||||
use_viz = args.mode in ["sim", "both"] and args.visualizer != "none"
|
||||
|
||||
print("=" * 70)
|
||||
print("OpenArms EE Replay (FK -> IK Pipeline)")
|
||||
print("=" * 70)
|
||||
print(f"\nMode: {args.mode}")
|
||||
print(f"Visualizer: {args.visualizer}")
|
||||
print(f"Dataset: {args.dataset}")
|
||||
print(f"Episode: {args.episode}")
|
||||
print(f"Speed: {args.speed}x")
|
||||
print("=" * 70)
|
||||
|
||||
robot = None
|
||||
viz = None
|
||||
|
||||
# Resolve URDF path (handle relative and ~ paths)
|
||||
from pathlib import Path
|
||||
urdf_path = args.urdf
|
||||
if urdf_path.startswith("~"):
|
||||
urdf_path = expanduser(urdf_path)
|
||||
elif not Path(urdf_path).is_absolute():
|
||||
# Relative to workspace root
|
||||
urdf_path = str(Path(__file__).parent.parent.parent / urdf_path)
|
||||
|
||||
# Initialize robot if needed
|
||||
if use_robot:
|
||||
from lerobot.robots.openarms.config_openarms_follower import OpenArmsFollowerConfig
|
||||
from lerobot.robots.openarms.openarms_follower import OpenArmsFollower
|
||||
|
||||
print("\n[1/5] Initializing robot...")
|
||||
robot_config = OpenArmsFollowerConfig(
|
||||
port_left=args.port_left,
|
||||
port_right=args.port_right,
|
||||
can_interface="socketcan",
|
||||
id="openarms_follower",
|
||||
disable_torque_on_disconnect=True,
|
||||
max_relative_target=10.0,
|
||||
)
|
||||
robot = OpenArmsFollower(robot_config)
|
||||
else:
|
||||
print("\n[1/5] Skipping robot (sim mode)")
|
||||
|
||||
# Initialize visualizer if needed
|
||||
if use_viz:
|
||||
print(f"\n[2/5] Initializing {args.visualizer} visualizer...")
|
||||
if args.visualizer == "meshcat":
|
||||
viz = MeshcatVisualizer(urdf_path)
|
||||
elif args.visualizer == "rerun":
|
||||
viz = RerunVisualizer(urdf_path)
|
||||
else:
|
||||
print("\n[2/5] Skipping visualization")
|
||||
|
||||
# Initialize kinematics with URDF joint names
|
||||
print("\n[3/5] Initializing kinematics solvers...")
|
||||
|
||||
left_kinematics = RobotKinematics(
|
||||
urdf_path=urdf_path,
|
||||
target_frame_name=args.left_ee_frame,
|
||||
joint_names=LEFT_URDF_JOINTS,
|
||||
)
|
||||
right_kinematics = RobotKinematics(
|
||||
urdf_path=urdf_path,
|
||||
target_frame_name=args.right_ee_frame,
|
||||
joint_names=RIGHT_URDF_JOINTS,
|
||||
)
|
||||
|
||||
# Build pipelines - use motor names without gripper for the processor
|
||||
motor_names_no_gripper = [n for n in MOTOR_NAMES if n != "gripper"]
|
||||
|
||||
joints_to_ee = RobotProcessorPipeline[RobotAction, RobotAction](
|
||||
steps=[
|
||||
BimanualForwardKinematicsJointsToEE(
|
||||
left_kinematics=left_kinematics,
|
||||
right_kinematics=right_kinematics,
|
||||
motor_names=MOTOR_NAMES,
|
||||
),
|
||||
],
|
||||
to_transition=robot_action_to_transition,
|
||||
to_output=transition_to_robot_action,
|
||||
)
|
||||
|
||||
ee_to_joints = RobotProcessorPipeline[tuple[RobotAction, RobotObservation], RobotAction](
|
||||
steps=[
|
||||
BimanualEEBoundsAndSafety(
|
||||
end_effector_bounds={"min": [-1.0, -1.0, -1.0], "max": [1.0, 1.0, 1.0]},
|
||||
max_ee_step_m=0.10,
|
||||
),
|
||||
BimanualInverseKinematicsEEToJoints(
|
||||
left_kinematics=left_kinematics,
|
||||
right_kinematics=right_kinematics,
|
||||
motor_names=MOTOR_NAMES,
|
||||
initial_guess_current_joints=False,
|
||||
),
|
||||
],
|
||||
to_transition=robot_action_observation_to_transition,
|
||||
to_output=transition_to_robot_action,
|
||||
)
|
||||
|
||||
# Load dataset
|
||||
print(f"\n[4/5] Loading dataset '{args.dataset}'...")
|
||||
dataset = LeRobotDataset(args.dataset, episodes=[args.episode])
|
||||
episode_frames = dataset.hf_dataset.filter(lambda x: x["episode_index"] == args.episode)
|
||||
|
||||
if len(episode_frames) == 0:
|
||||
raise ValueError(f"No frames found for episode {args.episode}")
|
||||
|
||||
print(f" Found {len(episode_frames)} frames at {dataset.fps} FPS")
|
||||
|
||||
action_features = dataset.features.get(ACTION, {})
|
||||
action_names = action_features.get("names", [])
|
||||
actions = episode_frames.select_columns(ACTION)
|
||||
|
||||
# Connect robot if needed
|
||||
if use_robot:
|
||||
print("\n[5/5] Connecting to robot...")
|
||||
robot.connect(calibrate=False)
|
||||
if not robot.is_connected:
|
||||
raise RuntimeError("Robot failed to connect!")
|
||||
else:
|
||||
print("\n[5/5] Skipping robot connection (sim mode)")
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print(f"Ready to replay! Mode: {args.mode}")
|
||||
print("=" * 70)
|
||||
|
||||
if use_robot:
|
||||
input("\nPress ENTER to start...")
|
||||
else:
|
||||
print("\nStarting visualization playback...")
|
||||
time.sleep(1)
|
||||
|
||||
# Simulated observation for sim-only mode
|
||||
sim_obs = {f"{prefix}_{motor}.pos": 0.0
|
||||
for prefix in ["left", "right"]
|
||||
for motor in MOTOR_NAMES}
|
||||
|
||||
try:
|
||||
for idx in range(len(episode_frames)):
|
||||
loop_start = time.perf_counter()
|
||||
|
||||
# Get observation
|
||||
if use_robot:
|
||||
robot_obs = robot.get_observation()
|
||||
else:
|
||||
robot_obs = sim_obs.copy()
|
||||
|
||||
# Build joint action from dataset
|
||||
action_array = actions[idx][ACTION]
|
||||
joint_action = {}
|
||||
for i, name in enumerate(action_names):
|
||||
if name.endswith(".pos"):
|
||||
joint_action[name] = float(action_array[i])
|
||||
|
||||
# Convert: joints -> EE (FK)
|
||||
ee_action = joints_to_ee(joint_action.copy())
|
||||
|
||||
# Convert: EE -> joints (IK)
|
||||
final_joint_action = ee_to_joints((ee_action.copy(), robot_obs))
|
||||
|
||||
# Update simulated observation for next iteration
|
||||
if not use_robot:
|
||||
sim_obs.update(final_joint_action)
|
||||
|
||||
# Send to robot
|
||||
if use_robot:
|
||||
robot.send_action(final_joint_action)
|
||||
|
||||
# Update visualization with ORIGINAL dataset trajectory
|
||||
if viz:
|
||||
if isinstance(viz, MeshcatVisualizer):
|
||||
viz.update(joint_action) # Use original, not FK->IK reconstructed
|
||||
elif isinstance(viz, RerunVisualizer):
|
||||
viz.update(joint_action, ee_action, idx)
|
||||
|
||||
# Maintain replay rate
|
||||
loop_duration = time.perf_counter() - loop_start
|
||||
dt_s = (1.0 / dataset.fps / args.speed) - loop_duration
|
||||
if dt_s > 0:
|
||||
precise_sleep(dt_s)
|
||||
|
||||
if (idx + 1) % 100 == 0:
|
||||
progress = (idx + 1) / len(episode_frames) * 100
|
||||
print(f"Progress: {idx + 1}/{len(episode_frames)} ({progress:.1f}%)")
|
||||
|
||||
print(f"\n✓ Replayed {len(episode_frames)} frames")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nReplay interrupted")
|
||||
finally:
|
||||
if use_robot and robot:
|
||||
print("\nDisconnecting robot...")
|
||||
robot.disconnect()
|
||||
print("✓ Done!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python
|
||||
"""Unify all tasks in a dataset to a single task."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
||||
from lerobot.datasets.utils import write_tasks
|
||||
|
||||
|
||||
def unify_tasks(repo_id: str, new_task: str):
|
||||
"""Set all episodes to use a single task."""
|
||||
print(f"Loading dataset: {repo_id}")
|
||||
dataset = LeRobotDataset(repo_id)
|
||||
root = dataset.root
|
||||
|
||||
print(f"Current tasks: {list(dataset.meta.tasks['task']) if dataset.meta.tasks is not None else []}")
|
||||
|
||||
# 1. Update tasks.parquet to have only one task
|
||||
tasks_df = pd.DataFrame({"task": [new_task]})
|
||||
write_tasks(tasks_df, root)
|
||||
print(f"Set single task: '{new_task}'")
|
||||
|
||||
# 2. Update all data parquet files to set task_index=0
|
||||
data_dir = root / "data"
|
||||
parquet_files = sorted(data_dir.glob("*/*.parquet"))
|
||||
for parquet_path in parquet_files:
|
||||
df = pd.read_parquet(parquet_path)
|
||||
df["task_index"] = 0
|
||||
df.to_parquet(parquet_path)
|
||||
print(f"Updated: {parquet_path.relative_to(root)}")
|
||||
|
||||
# 3. Update info.json
|
||||
info_path = root / "info.json"
|
||||
with open(info_path) as f:
|
||||
info = json.load(f)
|
||||
info["total_tasks"] = 1
|
||||
with open(info_path, "w") as f:
|
||||
json.dump(info, f, indent=2)
|
||||
|
||||
print(f"\nDone! All {dataset.meta.total_episodes} episodes now use task: '{new_task}'")
|
||||
print(f"\nTo push: huggingface-cli upload {repo_id} {root} --repo-type dataset")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Unify all tasks in a dataset to a single task")
|
||||
parser.add_argument("--repo_id", type=str, required=True, help="Dataset repo_id")
|
||||
parser.add_argument("--task", type=str, required=True, help="New task description")
|
||||
args = parser.parse_args()
|
||||
|
||||
unify_tasks(args.repo_id, args.task)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -66,6 +66,17 @@ class TrainPipelineConfig(HubMixin):
|
||||
eval: EvalConfig = field(default_factory=EvalConfig)
|
||||
wandb: WandBConfig = field(default_factory=WandBConfig)
|
||||
|
||||
# UMI-style relative actions with per-timestep normalization
|
||||
# Mode 1: use_relative_actions=True, use_relative_state=False
|
||||
# - Actions: relative to current position + per-timestep normalized
|
||||
# - State: absolute (unchanged)
|
||||
# Mode 2: use_relative_actions=True, use_relative_state=True (full UMI)
|
||||
# - Actions: relative to current position + per-timestep normalized
|
||||
# - State: relative to current position (provides velocity info)
|
||||
# Stats are computed automatically from first 1000 batches at training start
|
||||
use_relative_actions: bool = False
|
||||
use_relative_state: bool = False
|
||||
|
||||
# RA-BC (Reward-Aligned Behavior Cloning) parameters
|
||||
use_rabc: bool = False # Enable reward-weighted training
|
||||
rabc_progress_path: str | None = None # Path to precomputed SARM progress parquet file
|
||||
|
||||
@@ -16,5 +16,16 @@
|
||||
|
||||
from .config_openarms_follower import OpenArmsFollowerConfig
|
||||
from .openarms_follower import OpenArmsFollower
|
||||
from .robot_kinematic_processor import (
|
||||
BimanualEEBoundsAndSafety,
|
||||
BimanualForwardKinematicsJointsToEE,
|
||||
BimanualInverseKinematicsEEToJoints,
|
||||
)
|
||||
|
||||
__all__ = ["OpenArmsFollower", "OpenArmsFollowerConfig"]
|
||||
__all__ = [
|
||||
"OpenArmsFollower",
|
||||
"OpenArmsFollowerConfig",
|
||||
"BimanualForwardKinematicsJointsToEE",
|
||||
"BimanualInverseKinematicsEEToJoints",
|
||||
"BimanualEEBoundsAndSafety",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2025 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.
|
||||
|
||||
"""
|
||||
Kinematic processor steps for bimanual OpenArms robot.
|
||||
|
||||
Provides FK/IK conversions for both left and right arms.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
from lerobot.configs.types import FeatureType, PipelineFeatureType, PolicyFeature
|
||||
from lerobot.model.kinematics import RobotKinematics
|
||||
from lerobot.processor import (
|
||||
ProcessorStep,
|
||||
ProcessorStepRegistry,
|
||||
RobotAction,
|
||||
RobotActionProcessorStep,
|
||||
TransitionKey,
|
||||
)
|
||||
from lerobot.utils.rotation import Rotation
|
||||
|
||||
|
||||
def compute_bimanual_fk(
|
||||
joints: dict[str, Any],
|
||||
left_kinematics: RobotKinematics,
|
||||
right_kinematics: RobotKinematics,
|
||||
motor_names: list[str],
|
||||
) -> dict[str, Any]:
|
||||
"""Compute FK for both arms, converting joint positions to EE poses."""
|
||||
result = {}
|
||||
|
||||
for prefix, kinematics in [("left", left_kinematics), ("right", right_kinematics)]:
|
||||
motor_joint_values = []
|
||||
for name in motor_names:
|
||||
key = f"{prefix}_{name}.pos"
|
||||
if key in joints:
|
||||
motor_joint_values.append(joints[key])
|
||||
|
||||
if not motor_joint_values:
|
||||
continue
|
||||
|
||||
q = np.array(motor_joint_values, dtype=float)
|
||||
t = kinematics.forward_kinematics(q)
|
||||
pos = t[:3, 3]
|
||||
tw = Rotation.from_matrix(t[:3, :3]).as_rotvec()
|
||||
|
||||
gripper_key = f"{prefix}_gripper.pos"
|
||||
gripper_pos = joints.get(gripper_key, 0.0)
|
||||
|
||||
result[f"{prefix}_ee.x"] = float(pos[0])
|
||||
result[f"{prefix}_ee.y"] = float(pos[1])
|
||||
result[f"{prefix}_ee.z"] = float(pos[2])
|
||||
result[f"{prefix}_ee.wx"] = float(tw[0])
|
||||
result[f"{prefix}_ee.wy"] = float(tw[1])
|
||||
result[f"{prefix}_ee.wz"] = float(tw[2])
|
||||
result[f"{prefix}_ee.gripper_pos"] = float(gripper_pos)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@ProcessorStepRegistry.register("bimanual_forward_kinematics_joints_to_ee")
|
||||
@dataclass
|
||||
class BimanualForwardKinematicsJointsToEE(RobotActionProcessorStep):
|
||||
"""
|
||||
Converts joint positions to end-effector poses for both arms using FK.
|
||||
|
||||
Input action keys: {prefix}_{motor}.pos (e.g., "right_joint_1.pos", "left_joint_2.pos")
|
||||
Output action keys: {prefix}_ee.{x,y,z,wx,wy,wz,gripper_pos}
|
||||
"""
|
||||
left_kinematics: RobotKinematics
|
||||
right_kinematics: RobotKinematics
|
||||
motor_names: list[str] # e.g., ["joint_1", "joint_2", ..., "joint_7", "gripper"]
|
||||
|
||||
def action(self, action: RobotAction) -> RobotAction:
|
||||
ee_poses = compute_bimanual_fk(
|
||||
action, self.left_kinematics, self.right_kinematics, self.motor_names
|
||||
)
|
||||
|
||||
# Remove joint positions, keep EE poses
|
||||
for prefix in ["left", "right"]:
|
||||
for name in self.motor_names:
|
||||
action.pop(f"{prefix}_{name}.pos", None)
|
||||
|
||||
action.update(ee_poses)
|
||||
return action
|
||||
|
||||
def transform_features(
|
||||
self, features: dict[PipelineFeatureType, dict[str, PolicyFeature]]
|
||||
) -> dict[PipelineFeatureType, dict[str, PolicyFeature]]:
|
||||
for prefix in ["left", "right"]:
|
||||
for name in self.motor_names:
|
||||
features[PipelineFeatureType.ACTION].pop(f"{prefix}_{name}.pos", None)
|
||||
for k in ["x", "y", "z", "wx", "wy", "wz", "gripper_pos"]:
|
||||
features[PipelineFeatureType.ACTION][f"{prefix}_ee.{k}"] = PolicyFeature(
|
||||
type=FeatureType.ACTION, shape=(1,)
|
||||
)
|
||||
return features
|
||||
|
||||
|
||||
@ProcessorStepRegistry.register("bimanual_inverse_kinematics_ee_to_joints")
|
||||
@dataclass
|
||||
class BimanualInverseKinematicsEEToJoints(RobotActionProcessorStep):
|
||||
"""
|
||||
Converts EE poses to joint positions for both arms using IK.
|
||||
|
||||
Input action keys: {prefix}_ee.{x,y,z,wx,wy,wz,gripper_pos}
|
||||
Output action keys: {prefix}_{motor}.pos
|
||||
"""
|
||||
left_kinematics: RobotKinematics
|
||||
right_kinematics: RobotKinematics
|
||||
motor_names: list[str]
|
||||
initial_guess_current_joints: bool = True
|
||||
|
||||
q_curr_left: np.ndarray | None = field(default=None, init=False, repr=False)
|
||||
q_curr_right: np.ndarray | None = field(default=None, init=False, repr=False)
|
||||
|
||||
def action(self, action: RobotAction) -> RobotAction:
|
||||
observation = self.transition.get(TransitionKey.OBSERVATION)
|
||||
if observation is None:
|
||||
raise ValueError("Observation required for IK")
|
||||
observation = observation.copy()
|
||||
|
||||
for prefix, kinematics in [("left", self.left_kinematics), ("right", self.right_kinematics)]:
|
||||
x = action.pop(f"{prefix}_ee.x", None)
|
||||
y = action.pop(f"{prefix}_ee.y", None)
|
||||
z = action.pop(f"{prefix}_ee.z", None)
|
||||
wx = action.pop(f"{prefix}_ee.wx", None)
|
||||
wy = action.pop(f"{prefix}_ee.wy", None)
|
||||
wz = action.pop(f"{prefix}_ee.wz", None)
|
||||
gripper_pos = action.pop(f"{prefix}_ee.gripper_pos", None)
|
||||
|
||||
if None in (x, y, z, wx, wy, wz, gripper_pos):
|
||||
continue
|
||||
|
||||
# Get current joint positions from observation
|
||||
q_raw = np.array([
|
||||
float(observation.get(f"{prefix}_{name}.pos", 0.0))
|
||||
for name in self.motor_names if name != "gripper"
|
||||
], dtype=float)
|
||||
|
||||
q_curr_attr = f"q_curr_{prefix}"
|
||||
if self.initial_guess_current_joints:
|
||||
setattr(self, q_curr_attr, q_raw)
|
||||
else:
|
||||
if getattr(self, q_curr_attr) is None:
|
||||
setattr(self, q_curr_attr, q_raw)
|
||||
|
||||
t_des = np.eye(4, dtype=float)
|
||||
t_des[:3, :3] = Rotation.from_rotvec([wx, wy, wz]).as_matrix()
|
||||
t_des[:3, 3] = [x, y, z]
|
||||
|
||||
q_target = kinematics.inverse_kinematics(getattr(self, q_curr_attr), t_des)
|
||||
setattr(self, q_curr_attr, q_target)
|
||||
|
||||
for i, name in enumerate(self.motor_names):
|
||||
if name != "gripper":
|
||||
action[f"{prefix}_{name}.pos"] = float(q_target[i])
|
||||
else:
|
||||
action[f"{prefix}_gripper.pos"] = float(gripper_pos)
|
||||
|
||||
return action
|
||||
|
||||
def transform_features(
|
||||
self, features: dict[PipelineFeatureType, dict[str, PolicyFeature]]
|
||||
) -> dict[PipelineFeatureType, dict[str, PolicyFeature]]:
|
||||
for prefix in ["left", "right"]:
|
||||
for k in ["x", "y", "z", "wx", "wy", "wz", "gripper_pos"]:
|
||||
features[PipelineFeatureType.ACTION].pop(f"{prefix}_ee.{k}", None)
|
||||
for name in self.motor_names:
|
||||
features[PipelineFeatureType.ACTION][f"{prefix}_{name}.pos"] = PolicyFeature(
|
||||
type=FeatureType.ACTION, shape=(1,)
|
||||
)
|
||||
return features
|
||||
|
||||
def reset(self):
|
||||
self.q_curr_left = None
|
||||
self.q_curr_right = None
|
||||
|
||||
|
||||
@ProcessorStepRegistry.register("bimanual_ee_bounds_and_safety")
|
||||
@dataclass
|
||||
class BimanualEEBoundsAndSafety(RobotActionProcessorStep):
|
||||
"""
|
||||
Clips EE poses to bounds and limits step size for both arms.
|
||||
"""
|
||||
end_effector_bounds: dict # {"min": [x,y,z], "max": [x,y,z]}
|
||||
max_ee_step_m: float = 0.05
|
||||
|
||||
_last_pos_left: np.ndarray | None = field(default=None, init=False, repr=False)
|
||||
_last_pos_right: np.ndarray | None = field(default=None, init=False, repr=False)
|
||||
|
||||
def action(self, action: RobotAction) -> RobotAction:
|
||||
for prefix in ["left", "right"]:
|
||||
x = action.get(f"{prefix}_ee.x")
|
||||
y = action.get(f"{prefix}_ee.y")
|
||||
z = action.get(f"{prefix}_ee.z")
|
||||
|
||||
if None in (x, y, z):
|
||||
continue
|
||||
|
||||
pos = np.array([x, y, z], dtype=float)
|
||||
pos = np.clip(pos, self.end_effector_bounds["min"], self.end_effector_bounds["max"])
|
||||
|
||||
last_pos_attr = f"_last_pos_{prefix}"
|
||||
last_pos = getattr(self, last_pos_attr)
|
||||
if last_pos is not None:
|
||||
dpos = pos - last_pos
|
||||
n = float(np.linalg.norm(dpos))
|
||||
if n > self.max_ee_step_m and n > 0:
|
||||
pos = last_pos + dpos * (self.max_ee_step_m / n)
|
||||
|
||||
setattr(self, last_pos_attr, pos)
|
||||
|
||||
action[f"{prefix}_ee.x"] = float(pos[0])
|
||||
action[f"{prefix}_ee.y"] = float(pos[1])
|
||||
action[f"{prefix}_ee.z"] = float(pos[2])
|
||||
|
||||
return action
|
||||
|
||||
def reset(self):
|
||||
self._last_pos_left = None
|
||||
self._last_pos_right = None
|
||||
|
||||
def transform_features(
|
||||
self, features: dict[PipelineFeatureType, dict[str, PolicyFeature]]
|
||||
) -> dict[PipelineFeatureType, dict[str, PolicyFeature]]:
|
||||
return features
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||
*.arrow filter=lfs diff=lfs merge=lfs -text
|
||||
*.bin filter=lfs diff=lfs merge=lfs -text
|
||||
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
||||
*.ftz filter=lfs diff=lfs merge=lfs -text
|
||||
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||
*.h5 filter=lfs diff=lfs merge=lfs -text
|
||||
*.joblib filter=lfs diff=lfs merge=lfs -text
|
||||
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
||||
*.lz4 filter=lfs diff=lfs merge=lfs -text
|
||||
*.mds filter=lfs diff=lfs merge=lfs -text
|
||||
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
||||
*.model filter=lfs diff=lfs merge=lfs -text
|
||||
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
||||
*.npy filter=lfs diff=lfs merge=lfs -text
|
||||
*.npz filter=lfs diff=lfs merge=lfs -text
|
||||
*.onnx filter=lfs diff=lfs merge=lfs -text
|
||||
*.ot filter=lfs diff=lfs merge=lfs -text
|
||||
*.parquet filter=lfs diff=lfs merge=lfs -text
|
||||
*.pb filter=lfs diff=lfs merge=lfs -text
|
||||
*.pickle filter=lfs diff=lfs merge=lfs -text
|
||||
*.pkl filter=lfs diff=lfs merge=lfs -text
|
||||
*.pt filter=lfs diff=lfs merge=lfs -text
|
||||
*.pth filter=lfs diff=lfs merge=lfs -text
|
||||
*.rar filter=lfs diff=lfs merge=lfs -text
|
||||
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
||||
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
||||
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
||||
*.tar filter=lfs diff=lfs merge=lfs -text
|
||||
*.tflite filter=lfs diff=lfs merge=lfs -text
|
||||
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||
*.wasm filter=lfs diff=lfs merge=lfs -text
|
||||
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
*.zst filter=lfs diff=lfs merge=lfs -text
|
||||
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
||||
# Audio files - uncompressed
|
||||
*.pcm filter=lfs diff=lfs merge=lfs -text
|
||||
*.sam filter=lfs diff=lfs merge=lfs -text
|
||||
*.raw filter=lfs diff=lfs merge=lfs -text
|
||||
# Audio files - compressed
|
||||
*.aac filter=lfs diff=lfs merge=lfs -text
|
||||
*.flac filter=lfs diff=lfs merge=lfs -text
|
||||
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||
# Image files - uncompressed
|
||||
*.bmp filter=lfs diff=lfs merge=lfs -text
|
||||
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.tiff filter=lfs diff=lfs merge=lfs -text
|
||||
# Image files - compressed
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
*.webp filter=lfs diff=lfs merge=lfs -text
|
||||
# Video files - compressed
|
||||
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
||||
*.webm filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/collision/link3_symp.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/collision/link4_symp.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/collision/link5_symp.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/visual/link0.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/visual/link1.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/visual/link2.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/visual/link3.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/visual/link4.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/visual/link5.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/visual/link6.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/arm/v10/visual/link7.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/body/v10/collision/body_link0_symp.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/body/v10/visual/body_link0.dae filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/body/v10/visual/body_link0.stl filter=lfs diff=lfs merge=lfs -text
|
||||
meshes/ee/openarm_hand/visual/finger.stl filter=lfs diff=lfs merge=lfs -text
|
||||
@@ -0,0 +1,35 @@
|
||||
# Copyright 2025 Enactic, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(openarm_description)
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
# the following line skips the linter which checks for copyrights
|
||||
# comment the line when a copyright and license is added to all source files
|
||||
set(ament_cmake_copyright_FOUND TRUE)
|
||||
# the following line skips cpplint (only works in a git repo)
|
||||
# comment the line when this package is in a git repo and when
|
||||
# a copyright and license is added to all source files
|
||||
set(ament_cmake_cpplint_FOUND TRUE)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
endif()
|
||||
|
||||
install(DIRECTORY launch meshes rviz urdf config
|
||||
DESTINATION share/${PROJECT_NAME})
|
||||
|
||||
ament_package()
|
||||
@@ -0,0 +1,133 @@
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official email address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
hi_public@reazon.jp.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
@@ -0,0 +1,19 @@
|
||||
# How to contribute
|
||||
|
||||
## Did you find a bug?
|
||||
|
||||
Please report it to [GitHub Issues](https://github.com/enactic/openarm_description/issues/new?template=1-bug-report.yml)!
|
||||
|
||||
## Did you have a feature request?
|
||||
|
||||
Please share it to [GitHub Issues](https://github.com/enactic/openarm_description/issues/new?template=2-feature-request.yml)!
|
||||
|
||||
## Did you write a patch?
|
||||
|
||||
Please open a pull request with it!
|
||||
|
||||
Please make sure to review [our license](https://github.com/enactic/openarm_description/blob/main/LICENSE.txt) before you open a pull request.
|
||||
|
||||
## Others?
|
||||
|
||||
Please share it on [Discord](https://discord.gg/FsZaZ4z3We) or [GitHub Discussions](https://github.com/enactic/openarm_description/discussions)!
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Robot Description files for OpenArm
|
||||
|
||||
This package contains description files to generate OpenArm URDFs (Universal Robot Description Files). See [documentation](https://docs.openarm.dev/software/description) for details.
|
||||
|
||||
## Related links
|
||||
|
||||
- 📚 Read the [documentation](https://docs.openarm.dev/software/description)
|
||||
- 💬 Join the community on [Discord](https://discord.gg/FsZaZ4z3We)
|
||||
- 📬 Contact us through <openarm@enactic.ai>
|
||||
|
||||
## License
|
||||
|
||||
[Apache License 2.0](LICENSE.txt)
|
||||
|
||||
Copyright 2025 Enactic, Inc.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
All participation in the OpenArm project is governed by our
|
||||
[Code of Conduct](CODE_OF_CONDUCT.md).
|
||||
@@ -0,0 +1,135 @@
|
||||
link0:
|
||||
origin:
|
||||
x: -0.0009483362816297526
|
||||
y: 0.0001580207020448382
|
||||
z: 0.03076860287587199
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
mass: 1.1432284943239561
|
||||
inertia:
|
||||
xx: 0.001128
|
||||
xy: -4.0e-06
|
||||
xz: -3.3e-05
|
||||
yy: 0.000962
|
||||
yz: -7.0e-06
|
||||
zz: 0.00147
|
||||
|
||||
link1:
|
||||
origin:
|
||||
x: 0.0011467657911800769
|
||||
y: 3.319987657026362e-05
|
||||
z: 0.05395284380736254
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
mass: 1.1416684646202298
|
||||
inertia:
|
||||
xx: 0.001567
|
||||
xy: -1.0e-06
|
||||
xz: -2.9e-05
|
||||
yy: 0.001273
|
||||
yz: 1e-06
|
||||
zz: 0.001016
|
||||
|
||||
link2:
|
||||
origin:
|
||||
x: 0.00839629182351943
|
||||
y: -2.0145102027597523e-08
|
||||
z: 0.03256649300522363
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
mass: 0.2775092746011571
|
||||
inertia:
|
||||
xx: 0.000359
|
||||
xy: 1e-06
|
||||
xz: -0.000109
|
||||
yy: 0.000376
|
||||
yz: 1e-06
|
||||
zz: 0.000232
|
||||
|
||||
link3:
|
||||
origin:
|
||||
x: -0.002104752099628911
|
||||
y: 0.0005549085042607548
|
||||
z: 0.09047470545721961
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
mass: 1.073863338202347
|
||||
inertia:
|
||||
xx: 0.004372
|
||||
xy: 1e-06
|
||||
xz: 1.1e-05
|
||||
yy: 0.004319
|
||||
yz: -3.6e-05
|
||||
zz: 0.000661
|
||||
|
||||
link4:
|
||||
origin:
|
||||
x: -0.0029006831074562967
|
||||
y: -0.03030575826634669
|
||||
z: 0.06339637422196209
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
mass: 0.6348534566833373
|
||||
inertia:
|
||||
xx: 0.000623
|
||||
xy: -1.0e-06
|
||||
xz: -1.9e-05
|
||||
yy: 0.000511
|
||||
yz: 3.8e-05
|
||||
zz: 0.000334
|
||||
|
||||
link5:
|
||||
origin:
|
||||
x: -0.003049665024221911
|
||||
y: 0.0008866902457326625
|
||||
z: 0.043079803024980934
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
mass: 0.6156588026168502
|
||||
inertia:
|
||||
xx: 0.000423
|
||||
xy: -8.0e-06
|
||||
xz: 6.0e-06
|
||||
yy: 0.000445
|
||||
yz: -6.0e-06
|
||||
zz: 0.000324
|
||||
|
||||
link6:
|
||||
origin:
|
||||
x: -0.037136587005447405
|
||||
y: 0.00033230528343419053
|
||||
z: -9.498374522309838e-05
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
mass: 0.475202773187987
|
||||
inertia:
|
||||
xx: 0.000143
|
||||
xy: 1e-06
|
||||
xz: 1e-06
|
||||
yy: 0.000157
|
||||
yz: 1e-06
|
||||
zz: 0.000159
|
||||
|
||||
link7:
|
||||
origin:
|
||||
x: 6.875510271106056e-05
|
||||
y: 0.01266175250761268
|
||||
z: 0.06951945409987448
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
mass: 0.4659771327380578
|
||||
inertia:
|
||||
xx: 0.000639
|
||||
xy: 1e-06
|
||||
xz: 1e-06
|
||||
yy: 0.000497
|
||||
yz: 8.9e-05
|
||||
zz: 0.000342
|
||||
@@ -0,0 +1,62 @@
|
||||
joint1:
|
||||
limit:
|
||||
lower: -1.396263
|
||||
upper: 3.490659
|
||||
velocity: 16.754666
|
||||
effort: 40
|
||||
|
||||
joint2:
|
||||
limit:
|
||||
lower: -1.745329
|
||||
upper: 1.745329
|
||||
velocity: 16.754666
|
||||
effort: 40
|
||||
|
||||
joint3:
|
||||
limit:
|
||||
lower: -1.570796
|
||||
upper: 1.570796
|
||||
velocity: 5.445426
|
||||
effort: 27
|
||||
|
||||
joint4:
|
||||
limit:
|
||||
lower: 0.0
|
||||
upper: 2.443461
|
||||
velocity: 5.445426
|
||||
effort: 27
|
||||
|
||||
joint5:
|
||||
limit:
|
||||
lower: -1.570796
|
||||
upper: 1.570796
|
||||
velocity: 20.943946
|
||||
effort: 7
|
||||
|
||||
joint6:
|
||||
limit:
|
||||
lower: -0.785398
|
||||
upper: 0.785398
|
||||
velocity: 20.943946
|
||||
effort: 7
|
||||
|
||||
joint7:
|
||||
limit:
|
||||
lower: -1.570796
|
||||
upper: 1.570796
|
||||
velocity: 20.943946
|
||||
effort: 7
|
||||
|
||||
# joint_gripper1:
|
||||
# limit:
|
||||
# lower: 0.0
|
||||
# upper: 0.043
|
||||
# velocity: 100
|
||||
# effort: 100
|
||||
|
||||
# joint_gripper2:
|
||||
# limit:
|
||||
# lower: 0.0
|
||||
# upper: 0.043
|
||||
# velocity: 100
|
||||
# effort: 100
|
||||
@@ -0,0 +1,89 @@
|
||||
joint1:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0625
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint2:
|
||||
kinematic:
|
||||
x: -0.0301
|
||||
y: 0.0
|
||||
z: 0.06
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint3:
|
||||
kinematic:
|
||||
x: 0.0301
|
||||
y: 0.0
|
||||
z: 0.06625
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint4:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: 0.0315
|
||||
z: 0.20375
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint5:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: -0.0315
|
||||
z: 0.0955
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint6:
|
||||
kinematic:
|
||||
x: 0.0375
|
||||
y: 0.0
|
||||
z: 0.1205
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
# joint7:
|
||||
# kinematic:
|
||||
# x: -0.0375
|
||||
# y: 0.0205
|
||||
# z: 0.0
|
||||
# roll: 0
|
||||
# pitch: 0
|
||||
# yaw: 0
|
||||
|
||||
joint7:
|
||||
kinematic:
|
||||
x: -0.0375
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
# joint8:
|
||||
# kinematic:
|
||||
# x: 1e-06
|
||||
# y: -0.098
|
||||
# z: 0.114501
|
||||
# roll: 0
|
||||
# pitch: 0
|
||||
# yaw: 0
|
||||
|
||||
joint8:
|
||||
kinematic:
|
||||
x: 1e-6
|
||||
y: 0.0205
|
||||
z: 0.0
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
@@ -0,0 +1,80 @@
|
||||
link0:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
|
||||
link1:
|
||||
kinematic:
|
||||
x: -0.0
|
||||
y: 0.0
|
||||
z: -0.0625
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
|
||||
link2:
|
||||
kinematic:
|
||||
x: 0.0301
|
||||
y: 0.0
|
||||
z: -0.1225
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
|
||||
link3:
|
||||
kinematic:
|
||||
x: -0.0
|
||||
y: -0.0
|
||||
z: -0.18875
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
|
||||
link4:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: -0.0315
|
||||
z: -0.3425
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
|
||||
link5:
|
||||
kinematic:
|
||||
x: -0.0
|
||||
y: -0.0
|
||||
z: -0.438
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
|
||||
link6:
|
||||
kinematic:
|
||||
x: -0.0375
|
||||
y: -0.0
|
||||
z: -0.5585
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
|
||||
# link7:
|
||||
# kinematic:
|
||||
# x: 0.0
|
||||
# y: -0.0205
|
||||
# z: -0.5585
|
||||
# roll: 0.0
|
||||
# pitch: 0.0
|
||||
# yaw: 0.0
|
||||
|
||||
link7:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: -0.0
|
||||
z: -0.5585
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
@@ -0,0 +1,71 @@
|
||||
joint1:
|
||||
kinematic_offset:
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint2:
|
||||
kinematic_offset:
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
roll: 1.57079632679
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint3:
|
||||
kinematic_offset:
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint4:
|
||||
kinematic_offset:
|
||||
x: -0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint5:
|
||||
kinematic_offset:
|
||||
x: 0.0
|
||||
y: -0.0
|
||||
z: 0.0
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint6:
|
||||
kinematic_offset:
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint7:
|
||||
kinematic_offset:
|
||||
x: -0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
|
||||
joint8:
|
||||
kinematic_offset:
|
||||
x: 0.0
|
||||
y: -0.0
|
||||
z: 0.0
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
@@ -0,0 +1,14 @@
|
||||
body_link0:
|
||||
origin:
|
||||
xyz: 0.0 0.0 0.0
|
||||
rpy: 0.0 0.0 0.0
|
||||
mass: 13.89
|
||||
inertia:
|
||||
xx: 1.653
|
||||
xy: 0.0
|
||||
xz: 0.0
|
||||
yy: 1.653
|
||||
yz: 0.0
|
||||
zz: 0.051
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
body_link0:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
@@ -0,0 +1,8 @@
|
||||
body_link0:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
@@ -0,0 +1,70 @@
|
||||
hand:
|
||||
origin:
|
||||
x: 0.0
|
||||
y: 0.002
|
||||
z: 0.03
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
mass: 0.35
|
||||
inertia:
|
||||
xx: 0.0002473
|
||||
yy: 1.763e-05
|
||||
zz: 0.0002521
|
||||
xy: 1e-06
|
||||
xz: 1e-06
|
||||
yz: 1e-06
|
||||
|
||||
left_finger:
|
||||
origin:
|
||||
x: 0.0064528
|
||||
y: 0.017020
|
||||
z: 0.0219685
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
mass: 0.03602545343277134
|
||||
inertia:
|
||||
xx: 2.3749999999999997e-06
|
||||
yy: 2.3749999999999997e-06
|
||||
zz: 7.5e-07
|
||||
xy: 1e-06
|
||||
xz: 1e-06
|
||||
yz: 1e-06
|
||||
|
||||
right_finger:
|
||||
origin:
|
||||
x: 0.0064528
|
||||
y: -0.017020
|
||||
z: 0.0219685
|
||||
roll: 0
|
||||
pitch: 0
|
||||
yaw: 0
|
||||
mass: 0.03602545343277134
|
||||
inertia:
|
||||
xx: 2.3749999999999997e-06
|
||||
yy: 2.3749999999999997e-06
|
||||
zz: 7.5e-07
|
||||
xy: 1e-06
|
||||
xz: 1e-06
|
||||
yz: 1e-06
|
||||
|
||||
# left_finger: &finger
|
||||
# origin:
|
||||
# x: 00064528
|
||||
# y: 0.017020
|
||||
# z: 0.0219685
|
||||
# roll: 0
|
||||
# pitch: 0
|
||||
# yaw: 0
|
||||
# mass: 0.03602545343277134
|
||||
# inertia:
|
||||
# xx: 2.3749999999999997e-06
|
||||
# yy: 2.3749999999999997e-06
|
||||
# zz: 7.5e-07
|
||||
# xy: 0
|
||||
# xz: 0
|
||||
# yz: 0
|
||||
# right_finger: *finger
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# hand:
|
||||
# kinematic:
|
||||
# x: 0.0
|
||||
# y: -0.0205
|
||||
# z: -0.5585
|
||||
# roll: 0.0
|
||||
# pitch: 0.0
|
||||
# yaw: 0.0
|
||||
|
||||
hand:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: 0.00
|
||||
z: -0.6585
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
|
||||
# left_finger:
|
||||
# kinematic:
|
||||
# x: 0.0
|
||||
# y: 0.0775
|
||||
# z: -0.673001
|
||||
# roll: 0.0
|
||||
# pitch: 0.0
|
||||
# yaw: 0.0
|
||||
|
||||
# left_finger:
|
||||
# kinematic:
|
||||
# x: 0.0
|
||||
# y: 0.053
|
||||
# z: -0.673001
|
||||
# roll: 0.0
|
||||
# pitch: 0.0
|
||||
# yaw: 0.0
|
||||
|
||||
# right_finger:
|
||||
# kinematic:
|
||||
# x: 0.0
|
||||
# y: 0.143
|
||||
# z: -0.673001
|
||||
# roll: 0.0
|
||||
# pitch: 0.0
|
||||
# yaw: 0.0
|
||||
|
||||
left_finger:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: -0.05
|
||||
z: -0.673001
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
|
||||
right_finger:
|
||||
kinematic:
|
||||
x: 0.0
|
||||
y: 0.05
|
||||
z: -0.673001
|
||||
roll: 0.0
|
||||
pitch: 0.0
|
||||
yaw: 0.0
|
||||
@@ -0,0 +1,9 @@
|
||||
# Development
|
||||
|
||||
## How to release
|
||||
|
||||
```bash
|
||||
git clone git@github.com:enactic/openarm_description.git
|
||||
cd openarm_description
|
||||
dev/release.sh ${VERSION} # e.g. dev/release.sh 1.0.0
|
||||
```
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2025 Enactic, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
set -eu
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 version"
|
||||
echo " e.g.: $0 1.0.0"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
version="$1"
|
||||
|
||||
base_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
cd "${base_dir}"
|
||||
|
||||
if [ "${RELEASE_CHECK_ORIGIN:-yes}" = "yes" ]; then
|
||||
git_origin_url="$(git remote get-url origin)"
|
||||
if [ "${git_origin_url}" != "git@github.com:enactic/openarm_description.git" ]; then
|
||||
echo "This script must be ran with working copy of enactic/openarm_description."
|
||||
echo "The origin's URL: ${git_origin_url}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${RELEASE_PULL:-yes}" = "yes" ]; then
|
||||
echo "Ensure using the latest commit"
|
||||
git checkout main
|
||||
git pull --ff-only
|
||||
fi
|
||||
|
||||
if [ "${RELEASE_TAG:-yes}" = "yes" ]; then
|
||||
echo "Tag"
|
||||
git tag -a -m "OpenArm Description ${version}" "${version}"
|
||||
git push origin "${version}"
|
||||
fi
|
||||
@@ -0,0 +1,120 @@
|
||||
# Copyright 2025 Enactic, Inc.
|
||||
#
|
||||
# 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 os
|
||||
import xacro
|
||||
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
|
||||
from launch import LaunchDescription, LaunchContext
|
||||
from launch.actions import DeclareLaunchArgument, OpaqueFunction
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def robot_state_publisher_spawner(context: LaunchContext, arm_type, ee_type, bimanual):
|
||||
arm_type_str = context.perform_substitution(arm_type)
|
||||
ee_type_str = context.perform_substitution(ee_type)
|
||||
bimanual_str = context.perform_substitution(bimanual)
|
||||
|
||||
xacro_path = os.path.join(
|
||||
get_package_share_directory("openarm_description"),
|
||||
"urdf", "robot", f"{arm_type_str}.urdf.xacro"
|
||||
)
|
||||
|
||||
robot_description = xacro.process_file(
|
||||
xacro_path,
|
||||
mappings={
|
||||
"arm_type": arm_type_str,
|
||||
"ee_type": ee_type_str,
|
||||
"bimanual": bimanual_str,
|
||||
}
|
||||
).toprettyxml(indent=" ")
|
||||
|
||||
return [
|
||||
Node(
|
||||
package="robot_state_publisher",
|
||||
executable="robot_state_publisher",
|
||||
name="robot_state_publisher",
|
||||
output="screen",
|
||||
parameters=[{"robot_description": robot_description}],
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def rviz_spawner(context: LaunchContext, bimanual):
|
||||
bimanual_str = context.perform_substitution(bimanual)
|
||||
|
||||
rviz_config_file = "bimanual.rviz" if bimanual_str.lower() == "true" else "arm_only.rviz"
|
||||
rviz_config_path = os.path.join(
|
||||
get_package_share_directory("openarm_description"),
|
||||
"rviz", rviz_config_file
|
||||
)
|
||||
|
||||
return [
|
||||
Node(
|
||||
package="rviz2",
|
||||
executable="rviz2",
|
||||
name="rviz2",
|
||||
arguments=["--display-config", rviz_config_path],
|
||||
output="screen"
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
arm_type_arg = DeclareLaunchArgument(
|
||||
"arm_type",
|
||||
description="Type of arm to visualize (e.g., v10)"
|
||||
)
|
||||
|
||||
ee_type_arg = DeclareLaunchArgument(
|
||||
"ee_type",
|
||||
default_value="openarm_hand",
|
||||
description="Type of end-effector to attach (e.g., openarm_hand or none)"
|
||||
)
|
||||
|
||||
bimanual_arg = DeclareLaunchArgument(
|
||||
"bimanual",
|
||||
default_value="false",
|
||||
description="Whether to use bimanual configuration"
|
||||
)
|
||||
|
||||
arm_type = LaunchConfiguration("arm_type")
|
||||
ee_type = LaunchConfiguration("ee_type")
|
||||
bimanual = LaunchConfiguration("bimanual")
|
||||
|
||||
robot_state_publisher_loader = OpaqueFunction(
|
||||
function=robot_state_publisher_spawner,
|
||||
args=[arm_type, ee_type, bimanual]
|
||||
)
|
||||
|
||||
rviz_loader = OpaqueFunction(
|
||||
function=rviz_spawner,
|
||||
args=[bimanual]
|
||||
)
|
||||
|
||||
return LaunchDescription([
|
||||
arm_type_arg,
|
||||
ee_type_arg,
|
||||
bimanual_arg,
|
||||
robot_state_publisher_loader,
|
||||
Node(
|
||||
package="joint_state_publisher_gui",
|
||||
executable="joint_state_publisher_gui",
|
||||
name="joint_state_publisher_gui"
|
||||
),
|
||||
rviz_loader,
|
||||
])
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:baf52578e1d9e6225f3818cae82b6074a0b948d3cef8e9a3e6dfafca78507590
|
||||
size 40284
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:066113d13d5cc85098609003bc7ebb73c570015350877f5ed7162ef1b6601852
|
||||
size 17784
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:382ab32e4ae0880e8a1512e7a6ca6ce1f478a6c125db4efa977429ffb1d6b02a
|
||||
size 13384
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:00c908cefab152c00416a570a48bf9aafed1549085f19ff2d882dc3f355d9f59
|
||||
size 156984
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b54883b8c7c96268a68a5879f95998a53ad0b0c4fe74325fad63a6caef669c73
|
||||
size 1139984
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:678a2802906eff7b45a836d2f34a2d8e51def50b6599376968f888e05c72739e
|
||||
size 751484
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:95529bec23733476dfdbbb266c7db0d25a473a568de73c8337a82440fe4a9ac3
|
||||
size 30084
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:434f207f21f75f5f0bd604e390b8e5bc7b62b619265222846770e06b3f9b5cfb
|
||||
size 23884
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c77bdc9419947088e1dfc452e29c6092cc7b02b239ff4f2f5be3d77e393af185
|
||||
size 4148234
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2a182c7fd4d25226aff6d9e2dd9d1008a85fce7df8e16525722a5c5053f8b055
|
||||
size 5741534
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:de1b190a56c16dea14546fb8e22c86eccabc2ca5e054819630c1932592381745
|
||||
size 4543534
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8d47d75d0c47a65021708ba63cb73162bf7c3b1d14e9cfc70dfa47336034ac76
|
||||
size 4978834
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8ca8149f2ce8b1b102270ec0b1a4b75c3e6f98c09b084430a450adce808607e1
|
||||
size 4944684
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:30ea7abfdd3661b315f897bb82a5f34fd966357b358e890e155e928e931ea975
|
||||
size 6322984
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e49f91279f109baecb9ff54f5041eeb4514f757ba6daa65c3ea01fb1991967e4
|
||||
size 4818434
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a91593b67d2dec16d1dfb6f1305df3ddcd214cdef02e97d5aba30bc633e775b2
|
||||
size 5114784
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6c18bbf7e86b03e3faf802e61e8eb438b38dcbcf146d97cffe6e808c65e9a72a
|
||||
size 293284
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a05f59379dcf694e5ff336b1ee3a47ea504a2425a2fa774af1b14e096d9a239a
|
||||
size 10783948
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7ab0afa9a26bebbf5c6149894c41d96622caa649555ae7c5f0f2548f86148d91
|
||||
size 7955034
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8e96e1314618cf434908f70df78f68dd2b049c03538964e8d41fc99abe41564d
|
||||
size 13284
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8e5d373ebbd3fd001b506058644062ad71a68f1ced5ca5d5ed0f6de20137956b
|
||||
size 18284
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:25c115c7c55a422f30ad11581dc21576dc8fc4e09e659890772d86fe82ec04d7
|
||||
size 432484
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,649 @@
|
||||
<?xml version="1.0" ?>
|
||||
<!-- =================================================================================== -->
|
||||
<!-- | This document was autogenerated by xacro from urdf/robot/v10.urdf.xacro | -->
|
||||
<!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
|
||||
<!-- =================================================================================== -->
|
||||
<robot name="openarm">
|
||||
<!-- Is the robot being simulated in gazebo? -->
|
||||
<!-- <xacro:arg name="gazebo" default="false" /> -->
|
||||
<link name="world"/>
|
||||
<joint name="openarm_body_world_joint" type="fixed">
|
||||
<parent link="world"/>
|
||||
<child link="openarm_body_link0"/>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
</joint>
|
||||
<link name="openarm_body_link0">
|
||||
<visual name="openarm_body_link0_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/body/v10/visual/body_link0.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_body_link0_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/body/v10/collision/body_link0_symp.stl" scale="0.001 0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
<mass value="13.89"/>
|
||||
<inertia ixx="1.653" ixy="0.0" ixz="0.0" iyy="1.653" iyz="0.0" izz="0.051"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_openarm_body_link0_joint" type="fixed">
|
||||
<parent link="openarm_body_link0"/>
|
||||
<child link="openarm_left_link0"/>
|
||||
<origin rpy="-1.5708 0 0" xyz="0.0 0.031 0.698"/>
|
||||
</joint>
|
||||
<link name="openarm_left_link0">
|
||||
<visual name="openarm_left_link0_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link0.dae" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link0_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link0_symp.stl" scale="0.001 -0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0009483362816297526 -0.0001580207020448382 0.03076860287587199"/>
|
||||
<mass value="1.1432284943239561"/>
|
||||
<inertia ixx="0.001128" ixy="-4e-06" ixz="-3.3e-05" iyy="0.000962" iyz="-7e-06" izz="0.00147"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<link name="openarm_left_link1">
|
||||
<visual name="openarm_left_link1_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 0.0 -0.0625"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link1.dae" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link1_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 0.0 -0.0625"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link1_symp.stl" scale="0.001 -0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0011467657911800769 -3.319987657026362e-05 0.05395284380736254"/>
|
||||
<mass value="1.1416684646202298"/>
|
||||
<inertia ixx="0.001567" ixy="-1e-06" ixz="-2.9e-05" iyy="0.001273" iyz="1e-06" izz="0.001016"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint1" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0 0.0 0.0625"/>
|
||||
<parent link="openarm_left_link0"/>
|
||||
<child link="openarm_left_link1"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="40" lower="-3.490659" upper="1.3962629999999998" velocity="16.754666"/>
|
||||
</joint>
|
||||
<link name="openarm_left_link2">
|
||||
<visual name="openarm_left_link2_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0301 0.0 -0.1225"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link2.dae" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link2_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0301 0.0 -0.1225"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link2_symp.stl" scale="0.001 -0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.00839629182351943 2.0145102027597523e-08 0.03256649300522363"/>
|
||||
<mass value="0.2775092746011571"/>
|
||||
<inertia ixx="0.000359" ixy="1e-06" ixz="-0.000109" iyy="0.000376" iyz="1e-06" izz="0.000232"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint2" type="revolute">
|
||||
<origin rpy="-1.57079632679 0 0" xyz="-0.0301 0.0 0.06"/>
|
||||
<parent link="openarm_left_link1"/>
|
||||
<child link="openarm_left_link2"/>
|
||||
<axis xyz="-1 0 0"/>
|
||||
<limit effort="40" lower="-3.3161253267948965" upper="0.17453267320510335" velocity="16.754666"/>
|
||||
</joint>
|
||||
<link name="openarm_left_link3">
|
||||
<visual name="openarm_left_link3_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.18875"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link3.dae" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link3_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.18875"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link3_symp.stl" scale="0.001 -0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.002104752099628911 -0.0005549085042607548 0.09047470545721961"/>
|
||||
<mass value="1.073863338202347"/>
|
||||
<inertia ixx="0.004372" ixy="1e-06" ixz="1.1e-05" iyy="0.004319" iyz="-3.6e-05" izz="0.000661"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint3" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0301 0.0 0.06625"/>
|
||||
<parent link="openarm_left_link2"/>
|
||||
<child link="openarm_left_link3"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="27" lower="-1.570796" upper="1.570796" velocity="5.445426"/>
|
||||
</joint>
|
||||
<link name="openarm_left_link4">
|
||||
<visual name="openarm_left_link4_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0315 -0.3425"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link4.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link4_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0315 -0.3425"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link4_symp.stl" scale="0.001 0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0029006831074562967 -0.03030575826634669 0.06339637422196209"/>
|
||||
<mass value="0.6348534566833373"/>
|
||||
<inertia ixx="0.000623" ixy="-1e-06" ixz="-1.9e-05" iyy="0.000511" iyz="3.8e-05" izz="0.000334"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint4" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="-0.0 0.0315 0.20375"/>
|
||||
<parent link="openarm_left_link3"/>
|
||||
<child link="openarm_left_link4"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit effort="27" lower="0.0" upper="2.443461" velocity="5.445426"/>
|
||||
</joint>
|
||||
<link name="openarm_left_link5">
|
||||
<visual name="openarm_left_link5_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.438"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link5.dae" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link5_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.438"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link5_symp.stl" scale="0.001 -0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.003049665024221911 -0.0008866902457326625 0.043079803024980934"/>
|
||||
<mass value="0.6156588026168502"/>
|
||||
<inertia ixx="0.000423" ixy="-8e-06" ixz="6e-06" iyy="0.000445" iyz="-6e-06" izz="0.000324"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint5" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0 -0.0315 0.0955"/>
|
||||
<parent link="openarm_left_link4"/>
|
||||
<child link="openarm_left_link5"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946"/>
|
||||
</joint>
|
||||
<link name="openarm_left_link6">
|
||||
<visual name="openarm_left_link6_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0375 -0.0 -0.5585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link6.dae" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link6_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0375 -0.0 -0.5585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link6_symp.stl" scale="0.001 -0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.037136587005447405 -0.00033230528343419053 -9.498374522309838e-05"/>
|
||||
<mass value="0.475202773187987"/>
|
||||
<inertia ixx="0.000143" ixy="1e-06" ixz="1e-06" iyy="0.000157" iyz="1e-06" izz="0.000159"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint6" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0375 0.0 0.1205"/>
|
||||
<parent link="openarm_left_link5"/>
|
||||
<child link="openarm_left_link6"/>
|
||||
<axis xyz="1 0 0"/>
|
||||
<limit effort="7" lower="-0.785398" upper="0.785398" velocity="20.943946"/>
|
||||
</joint>
|
||||
<link name="openarm_left_link7">
|
||||
<visual name="openarm_left_link7_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0 -0.5585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link7.dae" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link7_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0 -0.5585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link7_symp.stl" scale="0.001 -0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="6.875510271106056e-05 -0.01266175250761268 0.06951945409987448"/>
|
||||
<mass value="0.4659771327380578"/>
|
||||
<inertia ixx="0.000639" ixy="1e-06" ixz="1e-06" iyy="0.000497" iyz="8.9e-05" izz="0.000342"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint7" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="-0.0375 0.0 0.0"/>
|
||||
<parent link="openarm_left_link6"/>
|
||||
<child link="openarm_left_link7"/>
|
||||
<axis xyz="0 -1 0"/>
|
||||
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946"/>
|
||||
</joint>
|
||||
<!-- <link name="${prefix}link8"/>
|
||||
|
||||
<joint name="${prefix}joint8" type="fixed">
|
||||
<xacro:openarm-kinematics name="joint8" config="${kinematics}" />
|
||||
<parent link="${prefix}link7" />
|
||||
<child link="${prefix}link8" />
|
||||
</joint> -->
|
||||
<joint name="openarm_right_openarm_body_link0_joint" type="fixed">
|
||||
<parent link="openarm_body_link0"/>
|
||||
<child link="openarm_right_link0"/>
|
||||
<origin rpy="1.5708 0 0" xyz="0.0 -0.031 0.698"/>
|
||||
</joint>
|
||||
<link name="openarm_right_link0">
|
||||
<visual name="openarm_right_link0_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link0.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link0_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link0_symp.stl" scale="0.001 0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0009483362816297526 0.0001580207020448382 0.03076860287587199"/>
|
||||
<mass value="1.1432284943239561"/>
|
||||
<inertia ixx="0.001128" ixy="-4e-06" ixz="-3.3e-05" iyy="0.000962" iyz="-7e-06" izz="0.00147"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<link name="openarm_right_link1">
|
||||
<visual name="openarm_right_link1_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 0.0 -0.0625"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link1.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link1_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 0.0 -0.0625"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link1_symp.stl" scale="0.001 0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0011467657911800769 3.319987657026362e-05 0.05395284380736254"/>
|
||||
<mass value="1.1416684646202298"/>
|
||||
<inertia ixx="0.001567" ixy="-1e-06" ixz="-2.9e-05" iyy="0.001273" iyz="1e-06" izz="0.001016"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint1" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0 0.0 0.0625"/>
|
||||
<parent link="openarm_right_link0"/>
|
||||
<child link="openarm_right_link1"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="40" lower="-1.396263" upper="3.490659" velocity="16.754666"/>
|
||||
</joint>
|
||||
<link name="openarm_right_link2">
|
||||
<visual name="openarm_right_link2_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0301 0.0 -0.1225"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link2.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link2_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0301 0.0 -0.1225"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link2_symp.stl" scale="0.001 0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.00839629182351943 -2.0145102027597523e-08 0.03256649300522363"/>
|
||||
<mass value="0.2775092746011571"/>
|
||||
<inertia ixx="0.000359" ixy="1e-06" ixz="-0.000109" iyy="0.000376" iyz="1e-06" izz="0.000232"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint2" type="revolute">
|
||||
<origin rpy="1.57079632679 0 0" xyz="-0.0301 0.0 0.06"/>
|
||||
<parent link="openarm_right_link1"/>
|
||||
<child link="openarm_right_link2"/>
|
||||
<axis xyz="-1 0 0"/>
|
||||
<limit effort="40" lower="-0.17453267320510335" upper="3.3161253267948965" velocity="16.754666"/>
|
||||
</joint>
|
||||
<link name="openarm_right_link3">
|
||||
<visual name="openarm_right_link3_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.18875"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link3.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link3_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.18875"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link3_symp.stl" scale="0.001 0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.002104752099628911 0.0005549085042607548 0.09047470545721961"/>
|
||||
<mass value="1.073863338202347"/>
|
||||
<inertia ixx="0.004372" ixy="1e-06" ixz="1.1e-05" iyy="0.004319" iyz="-3.6e-05" izz="0.000661"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint3" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0301 0.0 0.06625"/>
|
||||
<parent link="openarm_right_link2"/>
|
||||
<child link="openarm_right_link3"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="27" lower="-1.570796" upper="1.570796" velocity="5.445426"/>
|
||||
</joint>
|
||||
<link name="openarm_right_link4">
|
||||
<visual name="openarm_right_link4_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0315 -0.3425"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link4.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link4_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0315 -0.3425"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link4_symp.stl" scale="0.001 0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0029006831074562967 -0.03030575826634669 0.06339637422196209"/>
|
||||
<mass value="0.6348534566833373"/>
|
||||
<inertia ixx="0.000623" ixy="-1e-06" ixz="-1.9e-05" iyy="0.000511" iyz="3.8e-05" izz="0.000334"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint4" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="-0.0 0.0315 0.20375"/>
|
||||
<parent link="openarm_right_link3"/>
|
||||
<child link="openarm_right_link4"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit effort="27" lower="0.0" upper="2.443461" velocity="5.445426"/>
|
||||
</joint>
|
||||
<link name="openarm_right_link5">
|
||||
<visual name="openarm_right_link5_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.438"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link5.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link5_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.438"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link5_symp.stl" scale="0.001 0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.003049665024221911 0.0008866902457326625 0.043079803024980934"/>
|
||||
<mass value="0.6156588026168502"/>
|
||||
<inertia ixx="0.000423" ixy="-8e-06" ixz="6e-06" iyy="0.000445" iyz="-6e-06" izz="0.000324"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint5" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0 -0.0315 0.0955"/>
|
||||
<parent link="openarm_right_link4"/>
|
||||
<child link="openarm_right_link5"/>
|
||||
<axis xyz="0 0 1"/>
|
||||
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946"/>
|
||||
</joint>
|
||||
<link name="openarm_right_link6">
|
||||
<visual name="openarm_right_link6_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0375 -0.0 -0.5585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link6.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link6_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0375 -0.0 -0.5585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link6_symp.stl" scale="0.001 0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.037136587005447405 0.00033230528343419053 -9.498374522309838e-05"/>
|
||||
<mass value="0.475202773187987"/>
|
||||
<inertia ixx="0.000143" ixy="1e-06" ixz="1e-06" iyy="0.000157" iyz="1e-06" izz="0.000159"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint6" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0375 0.0 0.1205"/>
|
||||
<parent link="openarm_right_link5"/>
|
||||
<child link="openarm_right_link6"/>
|
||||
<axis xyz="1 0 0"/>
|
||||
<limit effort="7" lower="-0.785398" upper="0.785398" velocity="20.943946"/>
|
||||
</joint>
|
||||
<link name="openarm_right_link7">
|
||||
<visual name="openarm_right_link7_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0 -0.5585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link7.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link7_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0 -0.5585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link7_symp.stl" scale="0.001 0.001 0.001"/>
|
||||
<!-- <mesh filename="./meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="6.875510271106056e-05 0.01266175250761268 0.06951945409987448"/>
|
||||
<mass value="0.4659771327380578"/>
|
||||
<inertia ixx="0.000639" ixy="1e-06" ixz="1e-06" iyy="0.000497" iyz="8.9e-05" izz="0.000342"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint7" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="-0.0375 0.0 0.0"/>
|
||||
<parent link="openarm_right_link6"/>
|
||||
<child link="openarm_right_link7"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946"/>
|
||||
</joint>
|
||||
<!-- <link name="${prefix}link8"/>
|
||||
|
||||
<joint name="${prefix}joint8" type="fixed">
|
||||
<xacro:openarm-kinematics name="joint8" config="${kinematics}" />
|
||||
<parent link="${prefix}link7" />
|
||||
<child link="${prefix}link8" />
|
||||
</joint> -->
|
||||
<link name="openarm_left_hand">
|
||||
<visual name="openarm_left_hand_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.6585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/hand.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_hand_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.6585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/hand.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0 0.002 0.03"/>
|
||||
<mass value="0.35"/>
|
||||
<inertia ixx="0.0002473" ixy="1e-06" ixz="1e-06" iyy="1.763e-05" iyz="1e-06" izz="0.0002521"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<!-- <link name="${ee_prefix}hand"/> -->
|
||||
<!-- <joint name="${ee_prefix}hand_joint" type="fixed"> -->
|
||||
<joint name="left_openarm_hand_joint" type="fixed">
|
||||
<parent link="openarm_left_link7"/>
|
||||
<child link="openarm_left_hand"/>
|
||||
<origin rpy="0 0 0" xyz="0 -0.0 0.1001"/>
|
||||
</joint>
|
||||
<!-- Define the hand_tcp frame -->
|
||||
<link name="openarm_left_hand_tcp"/>
|
||||
<joint name="openarm_left_hand_tcp_joint" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 -0.0 0.08"/>
|
||||
<parent link="openarm_left_hand"/>
|
||||
<child link="openarm_left_hand_tcp"/>
|
||||
</joint>
|
||||
<link name="openarm_left_left_finger">
|
||||
<visual name="openarm_left_left_finger_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.05 -0.723001"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/finger.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_left_finger_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.05 -0.723001"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/finger.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0064528 0.01702 0.0219685"/>
|
||||
<mass value="0.03602545343277134"/>
|
||||
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<link name="openarm_left_right_finger">
|
||||
<visual name="openarm_left_right_finger_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.05 -0.723001"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/finger.dae" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_right_finger_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.05 -0.723001"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/finger.stl" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0064528 -0.01702 0.0219685"/>
|
||||
<mass value="0.03602545343277134"/>
|
||||
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_finger_joint1" type="prismatic">
|
||||
<parent link="openarm_left_hand"/>
|
||||
<child link="openarm_left_right_finger"/>
|
||||
<origin rpy="0 0 0" xyz="0 -0.006 0.015"/>
|
||||
<axis xyz="0 -1 0"/>
|
||||
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0"/>
|
||||
</joint>
|
||||
<joint name="openarm_left_finger_joint2" type="prismatic">
|
||||
<parent link="openarm_left_hand"/>
|
||||
<child link="openarm_left_left_finger"/>
|
||||
<origin rpy="0 0 0" xyz="0 0.006 0.015"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0"/>
|
||||
<mimic joint="openarm_left_finger_joint1"/>
|
||||
</joint>
|
||||
<link name="openarm_right_hand">
|
||||
<visual name="openarm_right_hand_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.6585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/hand.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_hand_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.6585"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/hand.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0 0.002 0.03"/>
|
||||
<mass value="0.35"/>
|
||||
<inertia ixx="0.0002473" ixy="1e-06" ixz="1e-06" iyy="1.763e-05" iyz="1e-06" izz="0.0002521"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<!-- <link name="${ee_prefix}hand"/> -->
|
||||
<!-- <joint name="${ee_prefix}hand_joint" type="fixed"> -->
|
||||
<joint name="right_openarm_hand_joint" type="fixed">
|
||||
<parent link="openarm_right_link7"/>
|
||||
<child link="openarm_right_hand"/>
|
||||
<origin rpy="0 0 0" xyz="0 -0.0 0.1001"/>
|
||||
</joint>
|
||||
<!-- Define the hand_tcp frame -->
|
||||
<link name="openarm_right_hand_tcp"/>
|
||||
<joint name="openarm_right_hand_tcp_joint" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 -0.0 0.08"/>
|
||||
<parent link="openarm_right_hand"/>
|
||||
<child link="openarm_right_hand_tcp"/>
|
||||
</joint>
|
||||
<link name="openarm_right_left_finger">
|
||||
<visual name="openarm_right_left_finger_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.05 -0.723001"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/finger.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_left_finger_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.05 -0.723001"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/finger.stl" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0064528 0.01702 0.0219685"/>
|
||||
<mass value="0.03602545343277134"/>
|
||||
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<link name="openarm_right_right_finger">
|
||||
<visual name="openarm_right_right_finger_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.05 -0.723001"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/finger.dae" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_right_finger_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.05 -0.723001"/>
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/finger.stl" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0064528 -0.01702 0.0219685"/>
|
||||
<mass value="0.03602545343277134"/>
|
||||
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_finger_joint1" type="prismatic">
|
||||
<parent link="openarm_right_hand"/>
|
||||
<child link="openarm_right_right_finger"/>
|
||||
<origin rpy="0 0 0" xyz="0 -0.006 0.015"/>
|
||||
<axis xyz="0 -1 0"/>
|
||||
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0"/>
|
||||
</joint>
|
||||
<joint name="openarm_right_finger_joint2" type="prismatic">
|
||||
<parent link="openarm_right_hand"/>
|
||||
<child link="openarm_right_left_finger"/>
|
||||
<origin rpy="0 0 0" xyz="0 0.006 0.015"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0"/>
|
||||
<mimic joint="openarm_right_finger_joint1"/>
|
||||
</joint>
|
||||
</robot>
|
||||
@@ -0,0 +1,618 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<robot name="openarm">
|
||||
<link name="world" />
|
||||
<joint name="openarm_body_world_joint" type="fixed">
|
||||
<parent link="world" />
|
||||
<child link="openarm_body_link0" />
|
||||
<origin rpy="0 0 0" xyz="0 0 0" />
|
||||
</joint>
|
||||
<link name="openarm_body_link0">
|
||||
<visual name="openarm_body_link0_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/body/v10/visual/body_link0.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_body_link0_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/body/v10/collision/body_link0_symp.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
||||
<mass value="13.89" />
|
||||
<inertia ixx="1.653" ixy="0.0" ixz="0.0" iyy="1.653" iyz="0.0" izz="0.051" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_openarm_body_link0_joint" type="fixed">
|
||||
<parent link="openarm_body_link0" />
|
||||
<child link="openarm_left_link0" />
|
||||
<origin rpy="-1.5708 0 0" xyz="0.0 0.031 0.698" />
|
||||
</joint>
|
||||
<link name="openarm_left_link0">
|
||||
<visual name="openarm_left_link0_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link0.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link0_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link0_symp.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0009483362816297526 -0.0001580207020448382 0.03076860287587199" />
|
||||
<mass value="1.1432284943239561" />
|
||||
<inertia ixx="0.001128" ixy="-4e-06" ixz="-3.3e-05" iyy="0.000962" iyz="-7e-06" izz="0.00147" />
|
||||
</inertial>
|
||||
</link>
|
||||
<link name="openarm_left_link1">
|
||||
<visual name="openarm_left_link1_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 0.0 -0.0625" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link1.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link1_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 0.0 -0.0625" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link1_symp.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0011467657911800769 -3.319987657026362e-05 0.05395284380736254" />
|
||||
<mass value="1.1416684646202298" />
|
||||
<inertia ixx="0.001567" ixy="-1e-06" ixz="-2.9e-05" iyy="0.001273" iyz="1e-06" izz="0.001016" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint1" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0 0.0 0.0625" />
|
||||
<parent link="openarm_left_link0" />
|
||||
<child link="openarm_left_link1" />
|
||||
<axis xyz="0 0 1" />
|
||||
<limit effort="40" lower="-3.490659" upper="1.3962629999999998" velocity="16.754666" />
|
||||
</joint>
|
||||
<link name="openarm_left_link2">
|
||||
<visual name="openarm_left_link2_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0301 0.0 -0.1225" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link2.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link2_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0301 0.0 -0.1225" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link2_symp.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.00839629182351943 2.0145102027597523e-08 0.03256649300522363" />
|
||||
<mass value="0.2775092746011571" />
|
||||
<inertia ixx="0.000359" ixy="1e-06" ixz="-0.000109" iyy="0.000376" iyz="1e-06" izz="0.000232" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint2" type="revolute">
|
||||
<origin rpy="-1.57079632679 0 0" xyz="-0.0301 0.0 0.06" />
|
||||
<parent link="openarm_left_link1" />
|
||||
<child link="openarm_left_link2" />
|
||||
<axis xyz="-1 0 0" />
|
||||
<limit effort="40" lower="-3.3161253267948965" upper="0.17453267320510335" velocity="16.754666" />
|
||||
</joint>
|
||||
<link name="openarm_left_link3">
|
||||
<visual name="openarm_left_link3_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.18875" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link3.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link3_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.18875" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link3_symp.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.002104752099628911 -0.0005549085042607548 0.09047470545721961" />
|
||||
<mass value="1.073863338202347" />
|
||||
<inertia ixx="0.004372" ixy="1e-06" ixz="1.1e-05" iyy="0.004319" iyz="-3.6e-05" izz="0.000661" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint3" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0301 0.0 0.06625" />
|
||||
<parent link="openarm_left_link2" />
|
||||
<child link="openarm_left_link3" />
|
||||
<axis xyz="0 0 1" />
|
||||
<limit effort="27" lower="-1.570796" upper="1.570796" velocity="5.445426" />
|
||||
</joint>
|
||||
<link name="openarm_left_link4">
|
||||
<visual name="openarm_left_link4_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0315 -0.3425" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link4.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link4_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0315 -0.3425" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link4_symp.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0029006831074562967 -0.03030575826634669 0.06339637422196209" />
|
||||
<mass value="0.6348534566833373" />
|
||||
<inertia ixx="0.000623" ixy="-1e-06" ixz="-1.9e-05" iyy="0.000511" iyz="3.8e-05" izz="0.000334" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint4" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="-0.0 0.0315 0.20375" />
|
||||
<parent link="openarm_left_link3" />
|
||||
<child link="openarm_left_link4" />
|
||||
<axis xyz="0 1 0" />
|
||||
<limit effort="27" lower="0.0" upper="2.443461" velocity="5.445426" />
|
||||
</joint>
|
||||
<link name="openarm_left_link5">
|
||||
<visual name="openarm_left_link5_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.438" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link5.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link5_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.438" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link5_symp.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.003049665024221911 -0.0008866902457326625 0.043079803024980934" />
|
||||
<mass value="0.6156588026168502" />
|
||||
<inertia ixx="0.000423" ixy="-8e-06" ixz="6e-06" iyy="0.000445" iyz="-6e-06" izz="0.000324" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint5" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0 -0.0315 0.0955" />
|
||||
<parent link="openarm_left_link4" />
|
||||
<child link="openarm_left_link5" />
|
||||
<axis xyz="0 0 1" />
|
||||
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946" />
|
||||
</joint>
|
||||
<link name="openarm_left_link6">
|
||||
<visual name="openarm_left_link6_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0375 -0.0 -0.5585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link6.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link6_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0375 -0.0 -0.5585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link6_symp.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.037136587005447405 -0.00033230528343419053 -9.498374522309838e-05" />
|
||||
<mass value="0.475202773187987" />
|
||||
<inertia ixx="0.000143" ixy="1e-06" ixz="1e-06" iyy="0.000157" iyz="1e-06" izz="0.000159" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint6" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0375 0.0 0.1205" />
|
||||
<parent link="openarm_left_link5" />
|
||||
<child link="openarm_left_link6" />
|
||||
<axis xyz="1 0 0" />
|
||||
<limit effort="7" lower="-0.785398" upper="0.785398" velocity="20.943946" />
|
||||
</joint>
|
||||
<link name="openarm_left_link7">
|
||||
<visual name="openarm_left_link7_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0 -0.5585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link7.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_link7_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0 -0.5585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link7_symp.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="6.875510271106056e-05 -0.01266175250761268 0.06951945409987448" />
|
||||
<mass value="0.4659771327380578" />
|
||||
<inertia ixx="0.000639" ixy="1e-06" ixz="1e-06" iyy="0.000497" iyz="8.9e-05" izz="0.000342" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_joint7" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="-0.0375 0.0 0.0" />
|
||||
<parent link="openarm_left_link6" />
|
||||
<child link="openarm_left_link7" />
|
||||
<axis xyz="0 -1 0" />
|
||||
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946" />
|
||||
</joint>
|
||||
<joint name="openarm_right_openarm_body_link0_joint" type="fixed">
|
||||
<parent link="openarm_body_link0" />
|
||||
<child link="openarm_right_link0" />
|
||||
<origin rpy="1.5708 0 0" xyz="0.0 -0.031 0.698" />
|
||||
</joint>
|
||||
<link name="openarm_right_link0">
|
||||
<visual name="openarm_right_link0_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link0.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link0_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link0_symp.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0009483362816297526 0.0001580207020448382 0.03076860287587199" />
|
||||
<mass value="1.1432284943239561" />
|
||||
<inertia ixx="0.001128" ixy="-4e-06" ixz="-3.3e-05" iyy="0.000962" iyz="-7e-06" izz="0.00147" />
|
||||
</inertial>
|
||||
</link>
|
||||
<link name="openarm_right_link1">
|
||||
<visual name="openarm_right_link1_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 0.0 -0.0625" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link1.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link1_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 0.0 -0.0625" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link1_symp.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0011467657911800769 3.319987657026362e-05 0.05395284380736254" />
|
||||
<mass value="1.1416684646202298" />
|
||||
<inertia ixx="0.001567" ixy="-1e-06" ixz="-2.9e-05" iyy="0.001273" iyz="1e-06" izz="0.001016" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint1" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0 0.0 0.0625" />
|
||||
<parent link="openarm_right_link0" />
|
||||
<child link="openarm_right_link1" />
|
||||
<axis xyz="0 0 1" />
|
||||
<limit effort="40" lower="-1.396263" upper="3.490659" velocity="16.754666" />
|
||||
</joint>
|
||||
<link name="openarm_right_link2">
|
||||
<visual name="openarm_right_link2_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0301 0.0 -0.1225" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link2.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link2_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0301 0.0 -0.1225" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link2_symp.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.00839629182351943 -2.0145102027597523e-08 0.03256649300522363" />
|
||||
<mass value="0.2775092746011571" />
|
||||
<inertia ixx="0.000359" ixy="1e-06" ixz="-0.000109" iyy="0.000376" iyz="1e-06" izz="0.000232" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint2" type="revolute">
|
||||
<origin rpy="1.57079632679 0 0" xyz="-0.0301 0.0 0.06" />
|
||||
<parent link="openarm_right_link1" />
|
||||
<child link="openarm_right_link2" />
|
||||
<axis xyz="-1 0 0" />
|
||||
<limit effort="40" lower="-0.17453267320510335" upper="3.3161253267948965" velocity="16.754666" />
|
||||
</joint>
|
||||
<link name="openarm_right_link3">
|
||||
<visual name="openarm_right_link3_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.18875" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link3.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link3_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.18875" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link3_symp.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.002104752099628911 0.0005549085042607548 0.09047470545721961" />
|
||||
<mass value="1.073863338202347" />
|
||||
<inertia ixx="0.004372" ixy="1e-06" ixz="1.1e-05" iyy="0.004319" iyz="-3.6e-05" izz="0.000661" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint3" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0301 0.0 0.06625" />
|
||||
<parent link="openarm_right_link2" />
|
||||
<child link="openarm_right_link3" />
|
||||
<axis xyz="0 0 1" />
|
||||
<limit effort="27" lower="-1.570796" upper="1.570796" velocity="5.445426" />
|
||||
</joint>
|
||||
<link name="openarm_right_link4">
|
||||
<visual name="openarm_right_link4_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0315 -0.3425" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link4.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link4_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0315 -0.3425" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link4_symp.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0029006831074562967 -0.03030575826634669 0.06339637422196209" />
|
||||
<mass value="0.6348534566833373" />
|
||||
<inertia ixx="0.000623" ixy="-1e-06" ixz="-1.9e-05" iyy="0.000511" iyz="3.8e-05" izz="0.000334" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint4" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="-0.0 0.0315 0.20375" />
|
||||
<parent link="openarm_right_link3" />
|
||||
<child link="openarm_right_link4" />
|
||||
<axis xyz="0 1 0" />
|
||||
<limit effort="27" lower="0.0" upper="2.443461" velocity="5.445426" />
|
||||
</joint>
|
||||
<link name="openarm_right_link5">
|
||||
<visual name="openarm_right_link5_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.438" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link5.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link5_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.438" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link5_symp.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.003049665024221911 0.0008866902457326625 0.043079803024980934" />
|
||||
<mass value="0.6156588026168502" />
|
||||
<inertia ixx="0.000423" ixy="-8e-06" ixz="6e-06" iyy="0.000445" iyz="-6e-06" izz="0.000324" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint5" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0 -0.0315 0.0955" />
|
||||
<parent link="openarm_right_link4" />
|
||||
<child link="openarm_right_link5" />
|
||||
<axis xyz="0 0 1" />
|
||||
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946" />
|
||||
</joint>
|
||||
<link name="openarm_right_link6">
|
||||
<visual name="openarm_right_link6_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0375 -0.0 -0.5585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link6.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link6_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.0375 -0.0 -0.5585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link6_symp.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="-0.037136587005447405 0.00033230528343419053 -9.498374522309838e-05" />
|
||||
<mass value="0.475202773187987" />
|
||||
<inertia ixx="0.000143" ixy="1e-06" ixz="1e-06" iyy="0.000157" iyz="1e-06" izz="0.000159" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint6" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.0375 0.0 0.1205" />
|
||||
<parent link="openarm_right_link5" />
|
||||
<child link="openarm_right_link6" />
|
||||
<axis xyz="1 0 0" />
|
||||
<limit effort="7" lower="-0.785398" upper="0.785398" velocity="20.943946" />
|
||||
</joint>
|
||||
<link name="openarm_right_link7">
|
||||
<visual name="openarm_right_link7_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0 -0.5585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/visual/link7.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_link7_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0 -0.5585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/arm/v10/collision/link7_symp.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="6.875510271106056e-05 0.01266175250761268 0.06951945409987448" />
|
||||
<mass value="0.4659771327380578" />
|
||||
<inertia ixx="0.000639" ixy="1e-06" ixz="1e-06" iyy="0.000497" iyz="8.9e-05" izz="0.000342" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_joint7" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="-0.0375 0.0 0.0" />
|
||||
<parent link="openarm_right_link6" />
|
||||
<child link="openarm_right_link7" />
|
||||
<axis xyz="0 1 0" />
|
||||
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946" />
|
||||
</joint>
|
||||
<link name="openarm_left_hand">
|
||||
<visual name="openarm_left_hand_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.6585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/hand.dae" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_hand_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.6585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/hand.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0 0.002 0.03" />
|
||||
<mass value="0.35" />
|
||||
<inertia ixx="0.0002473" ixy="1e-06" ixz="1e-06" iyy="1.763e-05" iyz="1e-06" izz="0.0002521" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="left_openarm_hand_joint" type="fixed">
|
||||
<parent link="openarm_left_link7" />
|
||||
<child link="openarm_left_hand" />
|
||||
<origin rpy="0 0 0" xyz="0 -0.0 0.1001" />
|
||||
</joint>
|
||||
<link name="openarm_left_hand_tcp">
|
||||
<inertial>
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<mass value="0.001" />
|
||||
<inertia ixx="0.000001" ixy="0.0" ixz="0.0" iyy="0.000001" iyz="0.0" izz="0.000001" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_hand_tcp_joint" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 -0.0 0.08" />
|
||||
<parent link="openarm_left_hand" />
|
||||
<child link="openarm_left_hand_tcp" />
|
||||
</joint>
|
||||
<link name="openarm_left_left_finger">
|
||||
<visual name="openarm_left_left_finger_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.05 -0.723001" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/finger.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_left_finger_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.05 -0.723001" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/finger.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0064528 0.01702 0.0219685" />
|
||||
<mass value="0.03602545343277134" />
|
||||
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07" />
|
||||
</inertial>
|
||||
</link>
|
||||
<link name="openarm_left_right_finger">
|
||||
<visual name="openarm_left_right_finger_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.05 -0.723001" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/finger.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_left_right_finger_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.05 -0.723001" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/finger.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0064528 -0.01702 0.0219685" />
|
||||
<mass value="0.03602545343277134" />
|
||||
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_left_finger_joint1" type="prismatic">
|
||||
<parent link="openarm_left_hand" />
|
||||
<child link="openarm_left_right_finger" />
|
||||
<origin rpy="0 0 0" xyz="0 -0.006 0.015" />
|
||||
<axis xyz="0 -1 0" />
|
||||
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0" />
|
||||
</joint>
|
||||
<joint name="openarm_left_finger_joint2" type="prismatic">
|
||||
<parent link="openarm_left_hand" />
|
||||
<child link="openarm_left_left_finger" />
|
||||
<origin rpy="0 0 0" xyz="0 0.006 0.015" />
|
||||
<axis xyz="0 1 0" />
|
||||
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0" />
|
||||
<mimic joint="openarm_left_finger_joint1" />
|
||||
</joint>
|
||||
<link name="openarm_right_hand">
|
||||
<visual name="openarm_right_hand_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.6585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/hand.dae" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_hand_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.6585" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/hand.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0 0.002 0.03" />
|
||||
<mass value="0.35" />
|
||||
<inertia ixx="0.0002473" ixy="1e-06" ixz="1e-06" iyy="1.763e-05" iyz="1e-06" izz="0.0002521" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="right_openarm_hand_joint" type="fixed">
|
||||
<parent link="openarm_right_link7" />
|
||||
<child link="openarm_right_hand" />
|
||||
<origin rpy="0 0 0" xyz="0 -0.0 0.1001" />
|
||||
</joint>
|
||||
<link name="openarm_right_hand_tcp">
|
||||
<inertial>
|
||||
<origin xyz="0 0 0" rpy="0 0 0" />
|
||||
<mass value="0.001" />
|
||||
<inertia ixx="0.000001" ixy="0.0" ixz="0.0" iyy="0.000001" iyz="0.0" izz="0.000001" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_hand_tcp_joint" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 -0.0 0.08" />
|
||||
<parent link="openarm_right_hand" />
|
||||
<child link="openarm_right_hand_tcp" />
|
||||
</joint>
|
||||
<link name="openarm_right_left_finger">
|
||||
<visual name="openarm_right_left_finger_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.05 -0.723001" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/finger.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_left_finger_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.05 -0.723001" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/finger.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0064528 0.01702 0.0219685" />
|
||||
<mass value="0.03602545343277134" />
|
||||
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07" />
|
||||
</inertial>
|
||||
</link>
|
||||
<link name="openarm_right_right_finger">
|
||||
<visual name="openarm_right_right_finger_visual">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.05 -0.723001" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/visual/finger.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="openarm_right_right_finger_collision">
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.05 -0.723001" />
|
||||
<geometry>
|
||||
<mesh filename="./meshes/ee/openarm_hand/collision/finger.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.0064528 -0.01702 0.0219685" />
|
||||
<mass value="0.03602545343277134" />
|
||||
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07" />
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="openarm_right_finger_joint1" type="prismatic">
|
||||
<parent link="openarm_right_hand" />
|
||||
<child link="openarm_right_right_finger" />
|
||||
<origin rpy="0 0 0" xyz="0 -0.006 0.015" />
|
||||
<axis xyz="0 -1 0" />
|
||||
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0" />
|
||||
</joint>
|
||||
<joint name="openarm_right_finger_joint2" type="prismatic">
|
||||
<parent link="openarm_right_hand" />
|
||||
<child link="openarm_right_left_finger" />
|
||||
<origin rpy="0 0 0" xyz="0 0.006 0.015" />
|
||||
<axis xyz="0 1 0" />
|
||||
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0" />
|
||||
<mimic joint="openarm_right_finger_joint1" />
|
||||
</joint>
|
||||
</robot>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<!--
|
||||
Copyright 2025 Enactic, Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<package format="3">
|
||||
<name>openarm_description</name>
|
||||
<version>1.0.0</version>
|
||||
<description>URDF with optional configuration parameters for OpenArm</description>
|
||||
<maintainer email="openarm_dev@enactic.ai">Enactic, Inc.</maintainer>
|
||||
<license>Apache-2.0</license>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<test_depend>ament_cmake_pytest</test_depend>
|
||||
|
||||
<depend>joint_state_publisher_gui</depend>
|
||||
<depend>ros_gz</depend>
|
||||
<depend>realsense2_description</depend>
|
||||
<exec_depend>xacro</exec_depend>
|
||||
<exec_depend>rviz2</exec_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,185 @@
|
||||
Panels:
|
||||
- Class: rviz_common/Displays
|
||||
Help Height: 138
|
||||
Name: Displays
|
||||
Property Tree Widget:
|
||||
Expanded:
|
||||
- /Global Options1
|
||||
Splitter Ratio: 0.5
|
||||
Tree Height: 758
|
||||
- Class: rviz_common/Views
|
||||
Expanded:
|
||||
- /Current View1
|
||||
Name: Views
|
||||
Splitter Ratio: 0.5
|
||||
Visualization Manager:
|
||||
Class: ""
|
||||
Displays:
|
||||
- Alpha: 0.5
|
||||
Cell Size: 1
|
||||
Class: rviz_default_plugins/Grid
|
||||
Color: 160; 160; 164
|
||||
Enabled: true
|
||||
Line Style:
|
||||
Line Width: 0.029999999329447746
|
||||
Value: Lines
|
||||
Name: Grid
|
||||
Normal Cell Count: 0
|
||||
Offset:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Plane: XY
|
||||
Plane Cell Count: 10
|
||||
Reference Frame: <Fixed Frame>
|
||||
Value: true
|
||||
- Alpha: 1
|
||||
Class: rviz_default_plugins/RobotModel
|
||||
Collision Enabled: false
|
||||
Description File: ""
|
||||
Description Source: Topic
|
||||
Description Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /robot_description
|
||||
Enabled: true
|
||||
Links:
|
||||
All Links Enabled: true
|
||||
Expand Joint Details: false
|
||||
Expand Link Details: false
|
||||
Expand Tree: false
|
||||
Link Tree Style: Links in Alphabetic Order
|
||||
openarm_hand:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_hand_tcp:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
openarm_left_finger:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_link0:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_link1:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_link2:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_link3:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_link4:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_link5:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_link6:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_link7:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_link8:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
openarm_right_finger:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
Mass Properties:
|
||||
Inertia: false
|
||||
Mass: false
|
||||
Name: RobotModel
|
||||
TF Prefix: ""
|
||||
Update Interval: 0
|
||||
Value: true
|
||||
Visual Enabled: true
|
||||
- Class: rviz_default_plugins/TF
|
||||
Enabled: false
|
||||
Frame Timeout: 15
|
||||
Frames:
|
||||
All Enabled: true
|
||||
Marker Scale: 1
|
||||
Name: TF
|
||||
Show Arrows: true
|
||||
Show Axes: true
|
||||
Show Names: false
|
||||
Tree:
|
||||
{}
|
||||
Update Interval: 0
|
||||
Value: false
|
||||
Enabled: true
|
||||
Global Options:
|
||||
Background Color: 48; 48; 48
|
||||
Fixed Frame: openarm_link0
|
||||
Frame Rate: 30
|
||||
Name: root
|
||||
Tools:
|
||||
- Class: rviz_default_plugins/MoveCamera
|
||||
Transformation:
|
||||
Current:
|
||||
Class: rviz_default_plugins/TF
|
||||
Value: true
|
||||
Views:
|
||||
Current:
|
||||
Class: rviz_default_plugins/Orbit
|
||||
Distance: 2.424237012863159
|
||||
Enable Stereo Rendering:
|
||||
Stereo Eye Separation: 0.05999999865889549
|
||||
Stereo Focal Distance: 1
|
||||
Swap Stereo Eyes: false
|
||||
Value: false
|
||||
Focal Point:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Focal Shape Fixed Size: true
|
||||
Focal Shape Size: 0.05000000074505806
|
||||
Invert Z Axis: false
|
||||
Name: Current View
|
||||
Near Clip Distance: 0.009999999776482582
|
||||
Pitch: 0.33000001311302185
|
||||
Target Frame: <Fixed Frame>
|
||||
Value: Orbit (rviz)
|
||||
Yaw: 5.5
|
||||
Saved: ~
|
||||
Window Geometry:
|
||||
Displays:
|
||||
collapsed: false
|
||||
Height: 1922
|
||||
Hide Left Dock: false
|
||||
Hide Right Dock: false
|
||||
QMainWindow State: 000000ff00000000fd0000000100000000000001da000006f0fc0200000002fb000000100044006900730070006c0061007900730100000069000003e40000017800fffffffb0000000a005600690065007700730100000459000003000000012300ffffff00000afa000006f000000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
|
||||
Views:
|
||||
collapsed: false
|
||||
Width: 3296
|
||||
X: 0
|
||||
Y: 64
|
||||
@@ -0,0 +1,257 @@
|
||||
Panels:
|
||||
- Class: rviz_common/Displays
|
||||
Help Height: 138
|
||||
Name: Displays
|
||||
Property Tree Widget:
|
||||
Expanded:
|
||||
- /Global Options1
|
||||
Splitter Ratio: 0.5
|
||||
Tree Height: 139
|
||||
- Class: rviz_common/Views
|
||||
Expanded:
|
||||
- /Current View1
|
||||
Name: Views
|
||||
Splitter Ratio: 0.5
|
||||
Visualization Manager:
|
||||
Class: ""
|
||||
Displays:
|
||||
- Alpha: 0.5
|
||||
Cell Size: 1
|
||||
Class: rviz_default_plugins/Grid
|
||||
Color: 160; 160; 164
|
||||
Enabled: true
|
||||
Line Style:
|
||||
Line Width: 0.029999999329447746
|
||||
Value: Lines
|
||||
Name: Grid
|
||||
Normal Cell Count: 0
|
||||
Offset:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Plane: XY
|
||||
Plane Cell Count: 10
|
||||
Reference Frame: <Fixed Frame>
|
||||
Value: true
|
||||
- Alpha: 1
|
||||
Class: rviz_default_plugins/RobotModel
|
||||
Collision Enabled: false
|
||||
Description File: ""
|
||||
Description Source: Topic
|
||||
Description Topic:
|
||||
Depth: 5
|
||||
Durability Policy: Volatile
|
||||
History Policy: Keep Last
|
||||
Reliability Policy: Reliable
|
||||
Value: /robot_description
|
||||
Enabled: true
|
||||
Links:
|
||||
All Links Enabled: true
|
||||
Expand Joint Details: false
|
||||
Expand Link Details: false
|
||||
Expand Tree: false
|
||||
Link Tree Style: Links in Alphabetic Order
|
||||
openarm_left_hand:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_left_hand_tcp:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
openarm_left_left_finger:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_left_link0:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_left_link1:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_left_link2:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_left_link3:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_left_link4:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_left_link5:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_left_link6:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_left_link7:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_left_link8:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
openarm_left_right_finger:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_hand:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_hand_tcp:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
openarm_right_left_finger:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_link0:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_link1:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_link2:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_link3:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_link4:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_link5:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_link6:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_link7:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
openarm_right_link8:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
openarm_right_right_finger:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
v10_body_link0:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Value: true
|
||||
world:
|
||||
Alpha: 1
|
||||
Show Axes: false
|
||||
Show Trail: false
|
||||
Mass Properties:
|
||||
Inertia: false
|
||||
Mass: false
|
||||
Name: RobotModel
|
||||
TF Prefix: ""
|
||||
Update Interval: 0
|
||||
Value: true
|
||||
Visual Enabled: true
|
||||
- Class: rviz_default_plugins/TF
|
||||
Enabled: false
|
||||
Frame Timeout: 15
|
||||
Frames:
|
||||
All Enabled: true
|
||||
Marker Scale: 1
|
||||
Name: TF
|
||||
Show Arrows: true
|
||||
Show Axes: true
|
||||
Show Names: false
|
||||
Tree:
|
||||
{}
|
||||
Update Interval: 0
|
||||
Value: false
|
||||
Enabled: true
|
||||
Global Options:
|
||||
Background Color: 48; 48; 48
|
||||
Fixed Frame: world
|
||||
Frame Rate: 30
|
||||
Name: root
|
||||
Tools:
|
||||
- Class: rviz_default_plugins/MoveCamera
|
||||
Transformation:
|
||||
Current:
|
||||
Class: rviz_default_plugins/TF
|
||||
Value: true
|
||||
Views:
|
||||
Current:
|
||||
Class: rviz_default_plugins/Orbit
|
||||
Distance: 5
|
||||
Enable Stereo Rendering:
|
||||
Stereo Eye Separation: 0.05999999865889549
|
||||
Stereo Focal Distance: 1
|
||||
Swap Stereo Eyes: false
|
||||
Value: false
|
||||
Focal Point:
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Focal Shape Fixed Size: true
|
||||
Focal Shape Size: 0.05000000074505806
|
||||
Invert Z Axis: false
|
||||
Name: Current View
|
||||
Near Clip Distance: 0.009999999776482582
|
||||
Pitch: 0.33000001311302185
|
||||
Target Frame: <Fixed Frame>
|
||||
Value: Orbit (rviz)
|
||||
Yaw: 5.5
|
||||
Saved: ~
|
||||
Window Geometry:
|
||||
Displays:
|
||||
collapsed: false
|
||||
Height: 826
|
||||
Hide Left Dock: false
|
||||
Hide Right Dock: false
|
||||
QMainWindow State: 000000ff00000000fd0000000100000000000001da000002a8fc0200000002fb000000100044006900730070006c0061007900730100000069000001790000017800fffffffb0000000a0056006900650077007301000001ee000001230000012300ffffff000002ca000002a800000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
|
||||
Views:
|
||||
collapsed: false
|
||||
Width: 1200
|
||||
X: 42
|
||||
Y: 64
|
||||
@@ -0,0 +1,118 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="openarm">
|
||||
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/arm/openarm_macro.xacro" />
|
||||
|
||||
<xacro:macro name="openarm_arm" params="arm_type arm_prefix:='' no_prefix:=false description_pkg:='openarm_description' connected_to:='base' xyz:='0 0 0' rpy:='0 0 0' joint_limits inertials kinematics kinematics_link kinematics_offset:=none " >
|
||||
|
||||
<xacro:property name="prefix" value="${'' if no_prefix else 'openarm' + '_' + arm_prefix}" />
|
||||
|
||||
<xacro:if value="${arm_prefix == 'right_'}">
|
||||
<xacro:property name="reflect" value="1" />
|
||||
</xacro:if>
|
||||
|
||||
<xacro:unless value="${arm_prefix == 'right_'}">
|
||||
<xacro:property name="reflect" value="-1" />
|
||||
</xacro:unless>
|
||||
|
||||
<xacro:unless value="${connected_to == ''}">
|
||||
<joint name="${prefix}${connected_to}_joint" type="fixed">
|
||||
<parent link="${connected_to}"/>
|
||||
<child link="${prefix}link0"/>
|
||||
<origin rpy="${rpy}" xyz="${xyz}"/>
|
||||
</joint>
|
||||
</xacro:unless>
|
||||
|
||||
<xacro:link_with_sc no_prefix="${no_prefix}" name="link0" kinematics_link="${kinematics_link}" inertials="${inertials}" reflect="${reflect}"/>
|
||||
|
||||
<xacro:link_with_sc no_prefix="${no_prefix}" name="link1" kinematics_link="${kinematics_link}" inertials="${inertials}" reflect="${reflect}"/>
|
||||
|
||||
<joint name="${prefix}joint1" type="revolute">
|
||||
<xacro:openarm-kinematics name="joint1" config="${kinematics}" reflect="${reflect}"/>
|
||||
<parent link="${prefix}link0" />
|
||||
<child link="${prefix}link1" />
|
||||
<axis xyz="0 0 1" />
|
||||
<xacro:openarm-limits name="joint1" config="${joint_limits}" offset="${-2.094396 if arm_prefix=='left_' else 0}"/>
|
||||
</joint>
|
||||
|
||||
<!-- for bimanual offset -->
|
||||
<xacro:link_with_sc no_prefix="${no_prefix}" name="link2" kinematics_link="${kinematics_link}" inertials="${inertials}" reflect="${reflect}"/>
|
||||
|
||||
<xacro:property name="limit_offset_joint2" value="0" />
|
||||
|
||||
<xacro:if value="${prefix.find('right_') != -1}">
|
||||
<xacro:property name="limit_offset_joint2" value="${pi/2}" />
|
||||
</xacro:if>
|
||||
|
||||
<xacro:if value="${prefix.find('left_') != -1}">
|
||||
<xacro:property name="limit_offset_joint2" value="${-pi/2}" />
|
||||
</xacro:if>
|
||||
|
||||
<joint name="${prefix}joint2" type="revolute">
|
||||
<xacro:openarm-kinematics name="joint2" config="${kinematics}" offset="${kinematics_offset}" reflect="${reflect}"/>
|
||||
<parent link="${prefix}link1" />
|
||||
<child link="${prefix}link2" />
|
||||
<axis xyz="-1 0 0" />
|
||||
<xacro:openarm-limits name="joint2" config="${joint_limits}" reflect="${reflect}" offset="${limit_offset_joint2}"/>
|
||||
</joint>
|
||||
|
||||
<xacro:link_with_sc no_prefix="${no_prefix}" name="link3" kinematics_link="${kinematics_link}" inertials="${inertials}" reflect="${reflect}"/>
|
||||
|
||||
<joint name="${prefix}joint3" type="revolute">
|
||||
<xacro:openarm-kinematics name="joint3" config="${kinematics}" />
|
||||
<parent link="${prefix}link2" />
|
||||
<child link="${prefix}link3" />
|
||||
<axis xyz="0 0 1" />
|
||||
<xacro:openarm-limits name="joint3" config="${joint_limits}" />
|
||||
</joint>
|
||||
|
||||
<xacro:link_with_sc no_prefix="${no_prefix}" name="link4" kinematics_link="${kinematics_link}" />
|
||||
|
||||
<joint name="${prefix}joint4" type="revolute">
|
||||
<xacro:openarm-kinematics name="joint4" config="${kinematics}" />
|
||||
<parent link="${prefix}link3" />
|
||||
<child link="${prefix}link4" />
|
||||
<axis xyz="0 1 0" />
|
||||
<xacro:openarm-limits name="joint4" config="${joint_limits}" />
|
||||
</joint>
|
||||
|
||||
<xacro:link_with_sc no_prefix="${no_prefix}" name="link5" kinematics_link="${kinematics_link}" inertials="${inertials}" reflect="${reflect}" />
|
||||
|
||||
<joint name="${prefix}joint5" type="revolute">
|
||||
<xacro:openarm-kinematics name="joint5" config="${kinematics}" />
|
||||
<parent link="${prefix}link4" />
|
||||
<child link="${prefix}link5" />
|
||||
<axis xyz="0 0 1" />
|
||||
<xacro:openarm-limits name="joint5" config="${joint_limits}" />
|
||||
</joint>
|
||||
|
||||
<xacro:link_with_sc no_prefix="${no_prefix}" name="link6" kinematics_link="${kinematics_link}" inertials="${inertials}" reflect="${reflect}"/>
|
||||
|
||||
<joint name="${prefix}joint6" type="revolute">
|
||||
<xacro:openarm-kinematics name="joint6" config="${kinematics}" />
|
||||
<parent link="${prefix}link5" />
|
||||
<child link="${prefix}link6" />
|
||||
<axis xyz="1 0 0" />
|
||||
<xacro:openarm-limits name="joint6" config="${joint_limits}" />
|
||||
</joint>
|
||||
|
||||
<xacro:link_with_sc no_prefix="${no_prefix}" name="link7" kinematics_link="${kinematics_link}" inertials="${inertials}" reflect="${reflect}"/>
|
||||
|
||||
<joint name="${prefix}joint7" type="revolute">
|
||||
<xacro:openarm-kinematics name="joint7" config="${kinematics}" />
|
||||
<parent link="${prefix}link6"/>
|
||||
<child link="${prefix}link7"/>
|
||||
<axis xyz="0 ${reflect} 0"/>
|
||||
<xacro:openarm-limits name="joint7" config="${joint_limits}" />
|
||||
</joint>
|
||||
|
||||
<!-- <link name="${prefix}link8"/>
|
||||
|
||||
<joint name="${prefix}joint8" type="fixed">
|
||||
<xacro:openarm-kinematics name="joint8" config="${kinematics}" />
|
||||
<parent link="${prefix}link7" />
|
||||
<child link="${prefix}link8" />
|
||||
</joint> -->
|
||||
|
||||
</xacro:macro>
|
||||
</robot>
|
||||
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<xacro:macro name="openarm-inertials" params="name inertials:=^ reflect:=1">
|
||||
<xacro:unless value="${name in inertials}">
|
||||
${xacro.warning('No inertia properties defined for: ' + name)}
|
||||
</xacro:unless>
|
||||
<xacro:if value="${name in inertials}">
|
||||
<xacro:property name="I" value="${inertials[name]}" lazy_eval="false" />
|
||||
<inertial>
|
||||
<origin
|
||||
xyz="${I.origin.x} ${reflect * I.origin.y} ${I.origin.z}"
|
||||
rpy="${I.origin.roll} ${I.origin.pitch} ${I.origin.yaw}" />
|
||||
<mass value="${I.mass}" />
|
||||
<xacro:if value="${'inertia' in I}">
|
||||
<xacro:property name="inert" value="${I.inertia}" />
|
||||
<inertia ixx="${inert.xx}" ixy="${inert.xy}" ixz="${inert.xz}"
|
||||
iyy="${inert.yy}" iyz="${inert.yz}" izz="${inert.zz}" />
|
||||
</xacro:if>
|
||||
</inertial>
|
||||
</xacro:if>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="link_with_sc" params="name no_prefix:='false' rpy:='0 0 0' kinematics_link:='' reflect:=1 inertials:=^">
|
||||
<!-- <xacro:property name="prefix" value="${'' if no_prefix else arm_prefix + arm_type + '_'}" /> -->
|
||||
<xacro:property name="prefix" value="${'' if no_prefix else 'openarm' + '_' + arm_prefix}" />
|
||||
<link name="${prefix}${name}">
|
||||
<visual name="${prefix}${name}_visual">
|
||||
<xacro:openarm-kinematics-link config="${kinematics_link}" name="${name}" />
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/arm/${arm_type}/visual/${name}.dae" scale="0.001 ${0.001*reflect} 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="${prefix}${name}_collision">
|
||||
<xacro:openarm-kinematics-link config="${kinematics_link}" name="${name}" />
|
||||
<geometry>
|
||||
<mesh filename="package://openarm_description/meshes/arm/${arm_type}/collision/${name}_symp.stl" scale="0.001 ${0.001*reflect} 0.001" />
|
||||
<!-- <mesh filename="package://openarm_description/meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<xacro:openarm-inertials name="${name}" inertials="${inertials}" reflect="${reflect}" />
|
||||
</link>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="transmission-openarm-state" params="arm_type:=fer">
|
||||
<transmission name="${arm_prefix}${arm_type}_openarm_state">
|
||||
<type>openarm_hw/openarmStateInterface</type>
|
||||
<joint name="${arm_prefix}${arm_type}_joint1"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></joint>
|
||||
<joint name="${arm_prefix}${arm_type}_joint2"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></joint>
|
||||
<joint name="${arm_prefix}${arm_type}_joint3"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></joint>
|
||||
<joint name="${arm_prefix}${arm_type}_joint4"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></joint>
|
||||
<joint name="${arm_prefix}${arm_type}_joint5"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></joint>
|
||||
<joint name="${arm_prefix}${arm_type}_joint6"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></joint>
|
||||
<joint name="${arm_prefix}${arm_type}_joint7"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></joint>
|
||||
|
||||
<actuator name="${arm_prefix}${arm_type}_motor1"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></actuator>
|
||||
<actuator name="${arm_prefix}${arm_type}_motor2"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></actuator>
|
||||
<actuator name="${arm_prefix}${arm_type}_motor3"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></actuator>
|
||||
<actuator name="${arm_prefix}${arm_type}_motor4"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></actuator>
|
||||
<actuator name="${arm_prefix}${arm_type}_motor5"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></actuator>
|
||||
<actuator name="${arm_prefix}${arm_type}_motor6"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></actuator>
|
||||
<actuator name="${arm_prefix}${arm_type}_motor7"><hardwareInterface>openarm_hw/openarmStateInterface</hardwareInterface></actuator>
|
||||
</transmission>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="transmission-openarm-model" params="arm_type:=v10 root:=v10_joint1 tip:=v10_joint7">
|
||||
<transmission name="${arm_prefix}${arm_type}_openarm_model">
|
||||
<type>openarm_hw/openarmModelInterface</type>
|
||||
<joint name="${root}">
|
||||
<role>root</role>
|
||||
<hardwareInterface>openarm_hw/openarmModelInterface</hardwareInterface>
|
||||
</joint>
|
||||
<joint name="${tip}">
|
||||
<role>tip</role>
|
||||
<hardwareInterface>openarm_hw/openarmModelInterface</hardwareInterface>
|
||||
</joint>
|
||||
<actuator name="${root}_motor_root"><hardwareInterface>openarm_hw/openarmModelInterface</hardwareInterface></actuator>
|
||||
<actuator name="${tip}_motor_tip" ><hardwareInterface>openarm_hw/openarmModelInterface</hardwareInterface></actuator>
|
||||
</transmission>
|
||||
</xacro:macro>
|
||||
<xacro:macro name="inertia-cylinder" params="mass radius h">
|
||||
<inertial>
|
||||
<mass value="${mass}" />
|
||||
<inertia ixx="${1./12 * mass * (3 * radius**2 + h**2)}" ixy = "0" ixz = "0"
|
||||
iyy="${1./12 * mass * (3 * radius**2 + h**2)}" iyz = "0"
|
||||
izz="${1./2 * mass * radius**2}" />
|
||||
</inertial>
|
||||
</xacro:macro>
|
||||
|
||||
|
||||
<xacro:macro name="openarm-limits" params="name config reflect:=1 offset:=0">
|
||||
<xacro:property name="limits" value="${config[name]['limit']}"/>
|
||||
<xacro:property name="raw_lower" value="${limits.lower * reflect + offset}" />
|
||||
<xacro:property name="raw_upper" value="${limits.upper * reflect + offset}" />
|
||||
|
||||
<xacro:property name="lower" value="0.0" />
|
||||
<xacro:property name="upper" value="0.0" />
|
||||
|
||||
<xacro:if value="${raw_lower < raw_upper}">
|
||||
<xacro:property name="lower" value="${raw_lower}" />
|
||||
<xacro:property name="upper" value="${raw_upper}" />
|
||||
</xacro:if>
|
||||
|
||||
<xacro:unless value="${raw_lower < raw_upper}">
|
||||
<xacro:property name="lower" value="${raw_upper}" />
|
||||
<xacro:property name="upper" value="${raw_lower}" />
|
||||
</xacro:unless>
|
||||
|
||||
<limit
|
||||
lower="${lower}"
|
||||
upper="${upper}"
|
||||
effort="${limits.effort}"
|
||||
velocity="${limits.velocity}" />
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="openarm-kinematics" params="name config offset:=none reflect:=1">
|
||||
<xacro:property name="kinematics" value="${config[name]['kinematic']}" lazy_eval="false" />
|
||||
<xacro:if value="${offset != 'none'}">
|
||||
<xacro:property name="offset" value="${offset[name]['kinematic_offset']}" lazy_eval="false" />
|
||||
<origin
|
||||
xyz="${kinematics.x + offset.x} ${kinematics.y + offset.y} ${kinematics.z + offset.z}"
|
||||
rpy="${reflect*(kinematics.roll + offset.roll)} ${reflect*(kinematics.pitch + offset.pitch)} ${reflect*(kinematics.yaw + offset.yaw)}" />
|
||||
</xacro:if>
|
||||
<xacro:unless value="${offset != 'none'}">
|
||||
<origin
|
||||
xyz="${kinematics.x} ${kinematics.y} ${kinematics.z}"
|
||||
rpy="${reflect*kinematics.roll} ${reflect*kinematics.pitch} ${reflect*kinematics.yaw}" />
|
||||
</xacro:unless>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="openarm-kinematics-link" params="config name">
|
||||
<xacro:property name="kinematics_link" value="${config[name]['kinematic']}" lazy_eval="false" />
|
||||
<origin rpy="${kinematics_link.roll} ${kinematics_link.pitch} ${kinematics_link.yaw}"
|
||||
xyz="${kinematics_link.x} ${kinematics_link.y} ${kinematics_link.z}" />
|
||||
</xacro:macro>
|
||||
|
||||
</robot>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="openarm_body">
|
||||
|
||||
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/body/openarm_body_macro.xacro" />
|
||||
|
||||
<xacro:macro name="openarm_body"
|
||||
params="body_type
|
||||
body_prefix:=''
|
||||
no_prefix:=false
|
||||
description_pkg:='openarm_description'
|
||||
connected_to:='world'
|
||||
xyz:='0 0 0'
|
||||
rpy:='0 0 0'
|
||||
inertials
|
||||
kinematics
|
||||
kinematics_link">
|
||||
|
||||
<!-- <xacro:property name="prefix" value="${'' if no_prefix else body_prefix + 'body_'}" /> -->
|
||||
<xacro:property name="prefix" value="${'' if no_prefix else 'openarm_' + 'body_'}" />
|
||||
|
||||
<xacro:unless value="${not connected_to}">
|
||||
<link name="${connected_to}" />
|
||||
<joint name="${prefix}${connected_to}_joint" type="fixed">
|
||||
<parent link="${connected_to}" />
|
||||
<child link="${prefix}link0" />
|
||||
<origin xyz="${xyz}" rpy="${rpy}" />
|
||||
</joint>
|
||||
</xacro:unless>
|
||||
|
||||
<xacro:body_link_with_sc
|
||||
name="body_link0"
|
||||
no_prefix="${no_prefix}"
|
||||
kinematics_link="${kinematics_link}" />
|
||||
</xacro:macro>
|
||||
|
||||
</robot>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<xacro:macro name="openarm-body-inertials" params="name inertials:=^">
|
||||
<xacro:unless value="${name in inertials}">${xacro.warning('No inertia properties defined for: ' + name)}</xacro:unless>
|
||||
<xacro:if value="${name in inertials}">
|
||||
<!-- Access inertia properties of link 'name' -->
|
||||
<xacro:property name="link_inertials" value="${inertials[name]}" lazy_eval="false" />
|
||||
<inertial>
|
||||
<origin rpy="${link_inertials.origin.rpy}" xyz="${link_inertials.origin.xyz}" />
|
||||
<mass value="${link_inertials.mass}" />
|
||||
<xacro:if value="${'inertia' in link_inertials}">
|
||||
<xacro:property name="I" value="${link_inertials.inertia}" />
|
||||
<inertia ixx="${I.xx}" ixy="${I.xy}" ixz="${I.xz}" iyy="${I.yy}" iyz="${I.yz}" izz="${I.zz}" />
|
||||
</xacro:if>
|
||||
</inertial>
|
||||
</xacro:if>
|
||||
</xacro:macro>
|
||||
|
||||
|
||||
<xacro:macro name="body_link_with_sc" params="name no_prefix:='false' rpy:='0 0 0' kinematics_link">
|
||||
<!-- <xacro:property name="prefix" value="${'' if no_prefix else body_prefix + body_type + '_'}" /> -->
|
||||
<xacro:property name="prefix" value="${'' if no_prefix else 'openarm' + '_' + body_prefix}" />
|
||||
<link name="${prefix}${name}">
|
||||
<visual name="${prefix}${name}_visual">
|
||||
<xacro:openarm-body-kinematics config="${kinematics_link}" name="${name}" />
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/body/${body_type}/visual/${name}.dae" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="${prefix}${name}_collision">
|
||||
<xacro:openarm-body-kinematics config="${kinematics_link}" name="${name}" />
|
||||
<geometry>
|
||||
<mesh filename="package://openarm_description/meshes/body/${body_type}/collision/${name}_symp.stl" scale="0.001 0.001 0.001" />
|
||||
<!-- <mesh filename="package://openarm_description/meshes/arm/${arm_type}/collision/${name}.stl" scale="0.001 0.001 0.001" /> -->
|
||||
</geometry>
|
||||
</collision>
|
||||
<xacro:openarm-body-inertials name="${name}" />
|
||||
</link>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="openarm-body-kinematics" params="config name">
|
||||
<xacro:property name="kinematics" value="${config[name]['kinematic']}" lazy_eval="false" />
|
||||
<origin rpy="${kinematics.roll} ${kinematics.pitch} ${kinematics.yaw}"
|
||||
xyz="${kinematics.x} ${kinematics.y} ${kinematics.z}" />
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="openarm-body-kinematics-link" params="config name">
|
||||
<xacro:property name="kinematics_link" value="${config[name]['kinematic']}" lazy_eval="false" />
|
||||
<origin rpy="${kinematics_link.roll} ${kinematics_link.pitch} ${kinematics_link.yaw}"
|
||||
xyz="${kinematics_link.x} ${kinematics_link.y} ${kinematics_link.z}" />
|
||||
</xacro:macro>
|
||||
|
||||
|
||||
</robot>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="ee_with_one_link">
|
||||
<!-- safety_distance: Minimum safety distance in [m] by which the collision volumes are expanded and which is enforced during robot motions -->
|
||||
<xacro:macro name="ee_with_one_link" params="connected_to:='' arm_type arm_prefix:='' ee_type ee_inertials rpy_ee:='0 0 0' xyz_ee:='0 0 0' tcp_xyz:='0 0 0' tcp_rpy:='0 0 0' description_pkg:=openarm_description with_sc:=false">
|
||||
<xacro:property name="ee_prefix" default="robot_"/>
|
||||
<xacro:unless value="${arm_type == ''}">
|
||||
<xacro:property name="ee_prefix" value="openarm_${arm_prefix}" />
|
||||
</xacro:unless>
|
||||
<xacro:unless value="${connected_to == ''}">
|
||||
<joint name="${ee_prefix}${ee_type}_joint" type="fixed">
|
||||
<parent link="${connected_to}" />
|
||||
<child link="${ee_prefix}${ee_type}" />
|
||||
<origin xyz="${xyz_ee}" rpy="${rpy_ee}" />
|
||||
</joint>
|
||||
</xacro:unless>
|
||||
|
||||
<xacro:ee_link_with_sc name="${ee_type}"/>
|
||||
|
||||
<!-- Define the ${ee_type}_tcp frame -->
|
||||
<link name="${ee_prefix}${ee_type}_tcp" />
|
||||
|
||||
<joint name="${ee_prefix}${ee_type}_tcp_joint" type="fixed">
|
||||
<origin xyz="${tcp_xyz}" rpy="${tcp_rpy}" />
|
||||
<parent link="${ee_prefix}${ee_type}" />
|
||||
<child link="${ee_prefix}${ee_type}_tcp" />
|
||||
</joint>
|
||||
|
||||
</xacro:macro>
|
||||
</robot>
|
||||
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="hand">
|
||||
<!-- safety_distance: Minimum safety distance in [m] by which the collision volumes are expanded and which is enforced during robot motions -->
|
||||
<xacro:macro name="openarm_hand" params="connected_to:='' arm_type arm_prefix:='' ee_type ee_inertials ee_kinematics_link rpy_ee:='0 0 0' xyz_ee:='0 0 0' tcp_xyz:='0 0 0' tcp_rpy:='0 0 0' description_pkg:=openarm_description">
|
||||
|
||||
<xacro:property name="ee_prefix" default=""/>
|
||||
|
||||
<xacro:unless value="${arm_type == ''}">
|
||||
<!-- <xacro:property name="ee_prefix" value="${arm_prefix}${arm_type}_" /> -->
|
||||
<xacro:property name="ee_prefix" value="openarm_${arm_prefix}" />
|
||||
</xacro:unless>
|
||||
|
||||
|
||||
<xacro:ee_link_with_sc name="hand" prefix="${ee_prefix}" ee_kinematics_link="${ee_kinematics_link}"/>
|
||||
<!-- <link name="${ee_prefix}hand"/> -->
|
||||
|
||||
<xacro:unless value="${connected_to == ''}">
|
||||
<!-- <joint name="${ee_prefix}hand_joint" type="fixed"> -->
|
||||
<joint name="${arm_prefix}openarm_hand_joint" type="fixed">
|
||||
<parent link="${connected_to}" />
|
||||
<child link="${ee_prefix}hand" />
|
||||
<origin xyz="${xyz_ee}" rpy="${rpy_ee}" />
|
||||
</joint>
|
||||
</xacro:unless>
|
||||
|
||||
<!-- Define the hand_tcp frame -->
|
||||
<link name="${ee_prefix}hand_tcp" />
|
||||
|
||||
<joint name="${ee_prefix}hand_tcp_joint" type="fixed">
|
||||
<origin xyz="${tcp_xyz}" rpy="${tcp_rpy}" />
|
||||
<parent link="${ee_prefix}hand" />
|
||||
<child link="${ee_prefix}hand_tcp" />
|
||||
</joint>
|
||||
|
||||
<link name="${ee_prefix}left_finger">
|
||||
<visual name="${ee_prefix}left_finger_visual">
|
||||
<xacro:openarm-ee-kinematics-link config="${ee_kinematics_link}" name="left_finger" />
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/ee/${ee_type}/visual/finger.dae" scale="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="${ee_prefix}left_finger_collision">
|
||||
<xacro:openarm-ee-kinematics-link config="${ee_kinematics_link}" name="left_finger" />
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/ee/${ee_type}/collision/finger.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<xacro:ee-inertials name="left_finger"/>
|
||||
</link>
|
||||
|
||||
<link name="${ee_prefix}right_finger">
|
||||
<visual name="${ee_prefix}right_finger_visual">
|
||||
<xacro:openarm-ee-kinematics-link config="${ee_kinematics_link}" name="right_finger" />
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/ee/${ee_type}/visual/finger.dae" scale="0.001 -0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="${ee_prefix}right_finger_collision">
|
||||
<xacro:openarm-ee-kinematics-link config="${ee_kinematics_link}" name="right_finger" />
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/ee/${ee_type}/collision/finger.stl" scale="0.001 -0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<xacro:ee-inertials name="right_finger"/>
|
||||
</link>
|
||||
|
||||
<joint name="${ee_prefix}finger_joint1" type="prismatic">
|
||||
<parent link="${ee_prefix}hand" />
|
||||
<child link="${ee_prefix}right_finger" />
|
||||
<origin xyz="0 -0.006 0.015" rpy="0 0 0" />
|
||||
<axis xyz="0 -1 0" />
|
||||
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0" />
|
||||
</joint>
|
||||
|
||||
<joint name="${ee_prefix}finger_joint2" type="prismatic">
|
||||
<parent link="${ee_prefix}hand" />
|
||||
<child link="${ee_prefix}left_finger" />
|
||||
<origin xyz="0 0.006 0.015" rpy="0 0 0" />
|
||||
<axis xyz="0 1 0" />
|
||||
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0" />
|
||||
<mimic joint="${ee_prefix}finger_joint1" />
|
||||
</joint>
|
||||
|
||||
</xacro:macro>
|
||||
</robot>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<!-- Where is the end-effector connected to, if different from the robot flange? -->
|
||||
<xacro:arg name="special_connection" default="" />
|
||||
|
||||
<!-- Position offset between ee and parent frame -->
|
||||
<xacro:arg name="xyz_ee" default="0 -0.0 0.1001" />
|
||||
|
||||
<!-- Rotation offset between ee and parent frame -->
|
||||
<xacro:arg name="rpy_ee" default= "0 0 0" />
|
||||
|
||||
<!-- Position offset between ee frame and tcp frame -->
|
||||
<xacro:arg name="tcp_xyz" default="0 -0.0 0.08" />
|
||||
|
||||
<!-- Rotation offset between ee frame and tcp frame -->
|
||||
<xacro:arg name="tcp_rpy" default="0 0 0" />
|
||||
|
||||
<!-- Is the robot being simulated in gazebo? -->
|
||||
<!-- <xacro:arg name="gazebo" default="false" /> -->
|
||||
|
||||
<!-- Name of the description package -->
|
||||
<xacro:arg name="description_pkg" default="openarm_description" />
|
||||
|
||||
</robot>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
|
||||
<xacro:macro name="ee-inertials" params="name ee_inertials:=^">
|
||||
<xacro:unless value="${name in ee_inertials}">
|
||||
${xacro.warning('No inertia properties defined for: ' + name)}
|
||||
</xacro:unless>
|
||||
<xacro:if value="${name in ee_inertials}">
|
||||
<xacro:property name="link_inertials" value="${ee_inertials[name]}" lazy_eval="false" />
|
||||
<xacro:property name="xyz" value="${link_inertials.origin.x} ${link_inertials.origin.y} ${link_inertials.origin.z}" />
|
||||
<xacro:property name="rpy" value="${link_inertials.origin.roll} ${link_inertials.origin.pitch} ${link_inertials.origin.yaw}" />
|
||||
<inertial>
|
||||
<origin xyz="${xyz}" rpy="${rpy}" />
|
||||
<mass value="${link_inertials.mass}" />
|
||||
<xacro:if value="${'inertia' in link_inertials}">
|
||||
<xacro:property name="I" value="${link_inertials.inertia}" />
|
||||
<inertia ixx="${I.xx}" ixy="${I.xy}" ixz="${I.xz}"
|
||||
iyy="${I.yy}" iyz="${I.yz}" izz="${I.zz}" />
|
||||
</xacro:if>
|
||||
</inertial>
|
||||
</xacro:if>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="ee_link_with_sc" params="name prefix rpy:='0 0 0' ee_kinematics_link">
|
||||
<link name="${prefix}${name}">
|
||||
<visual name="${prefix}${name}_visual">
|
||||
<xacro:openarm-ee-kinematics-link config="${ee_kinematics_link}" name="${name}" />
|
||||
<geometry>
|
||||
<mesh filename="package://${description_pkg}/meshes/ee/${ee_type}/visual/${name}.dae" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision name="${prefix}${name}_collision">
|
||||
<xacro:openarm-ee-kinematics-link config="${ee_kinematics_link}" name="${name}" />
|
||||
<geometry>
|
||||
<mesh filename="package://openarm_description/meshes/ee/${ee_type}/collision/${name}.stl" scale="0.001 0.001 0.001" />
|
||||
</geometry>
|
||||
</collision>
|
||||
<xacro:ee-inertials name="${name}" />
|
||||
</link>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="openarm-ee-kinematics" params="config name">
|
||||
<xacro:property name="kinematics" value="${config[name]['kinematic']}" lazy_eval="false" />
|
||||
<origin rpy="${kinematics.roll} ${kinematics.pitch} ${kinematics.yaw}"
|
||||
xyz="${kinematics.x} ${kinematics.y} ${kinematics.z}" />
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:macro name="openarm-ee-kinematics-link" params="config name">
|
||||
<xacro:property name="kinematics_link" value="${config[name]['kinematic']}" lazy_eval="false" />
|
||||
<origin rpy="${kinematics_link.roll} ${kinematics_link.pitch} ${kinematics_link.yaw}"
|
||||
xyz="${kinematics_link.x} ${kinematics_link.y} ${kinematics_link.z}" />
|
||||
</xacro:macro>
|
||||
|
||||
</robot>
|
||||
@@ -0,0 +1,197 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
<xacro:macro name="openarm_robot"
|
||||
params="arm_type
|
||||
joint_limits
|
||||
kinematics
|
||||
kinematics_link
|
||||
kinematics_offset
|
||||
inertials
|
||||
body_type
|
||||
body_inertials
|
||||
body_kinematics
|
||||
body_kinematics_link
|
||||
ee_kinematics_link
|
||||
parent:='world'
|
||||
xyz:='0 0 0'
|
||||
rpy:='0 0 0'
|
||||
hand:='false'
|
||||
ee_type:=''
|
||||
ros2_control:=false
|
||||
can_interface:=''
|
||||
use_fake_hardware:=false
|
||||
fake_sensor_commands:=false
|
||||
no_prefix:='false'
|
||||
arm_prefix:=''
|
||||
body_prefix:=''
|
||||
arm_connected_to='base'
|
||||
body_connected_to='world'
|
||||
bimanual:=false
|
||||
right_arm_base_xyz='0 0 0'
|
||||
left_arm_base_xyz='0 0 0'
|
||||
right_arm_base_rpy='0 0 0'
|
||||
left_arm_base_rpy='0 0 0'
|
||||
left_can_interface:='can1'
|
||||
right_can_interface:='can0'
|
||||
left_arm_prefix:='left_'
|
||||
right_arm_prefix:='right_'
|
||||
can_fd:='true'
|
||||
"
|
||||
>
|
||||
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/arm/openarm_macro.xacro" />
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/arm/openarm_arm.xacro" />
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/body/openarm_body_macro.xacro" />
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/body/openarm_body.xacro" />
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/ee/openarm_hand.xacro" />
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/ee/openarm_hand_macro.xacro" />
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/ros2_control/openarm.ros2_control.xacro" />
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/ros2_control/openarm.bimanual.ros2_control.xacro" />
|
||||
<xacro:if value="${ee_type != 'none'}">
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/ee/${ee_type}_arguments.xacro" />
|
||||
</xacro:if>
|
||||
|
||||
<xacro:property name="arm_prefix_modified" value="${'' if arm_prefix == '' else arm_prefix + '_'}" />
|
||||
<xacro:property name="body_prefix_modified" value="${'' if body_prefix == '' else body_prefix + '_'}" />
|
||||
|
||||
<xacro:if value="${bimanual}">
|
||||
|
||||
<xacro:openarm_body
|
||||
body_type="${body_type}"
|
||||
body_prefix="${body_prefix_modified}"
|
||||
no_prefix="${no_prefix}"
|
||||
description_pkg="openarm_description"
|
||||
connected_to="${body_connected_to}"
|
||||
xyz="${xyz}"
|
||||
rpy="${rpy}"
|
||||
inertials="${body_inertials}"
|
||||
kinematics="${body_kinematics}"
|
||||
kinematics_link="${body_kinematics_link}" />
|
||||
|
||||
<!-- left arm -->
|
||||
<xacro:openarm_arm
|
||||
arm_type="${arm_type}"
|
||||
arm_prefix="left_"
|
||||
no_prefix="false"
|
||||
description_pkg="openarm_description"
|
||||
connected_to="openarm_body_link0"
|
||||
xyz="${left_arm_base_xyz}"
|
||||
rpy="${left_arm_base_rpy}"
|
||||
joint_limits="${joint_limits}"
|
||||
inertials="${inertials}"
|
||||
kinematics="${kinematics}"
|
||||
kinematics_link="${kinematics_link}"
|
||||
kinematics_offset="${kinematics_offset}" />
|
||||
|
||||
<!-- right arm -->
|
||||
<xacro:openarm_arm
|
||||
arm_type="${arm_type}"
|
||||
arm_prefix="right_"
|
||||
no_prefix="false"
|
||||
description_pkg="openarm_description"
|
||||
connected_to="openarm_body_link0"
|
||||
xyz="${right_arm_base_xyz}"
|
||||
rpy="${right_arm_base_rpy}"
|
||||
joint_limits="${joint_limits}"
|
||||
inertials="${inertials}"
|
||||
kinematics="${kinematics}"
|
||||
kinematics_link="${kinematics_link}"
|
||||
kinematics_offset="${kinematics_offset}" />
|
||||
|
||||
<!-- ros2_control hardware interfaces for bimanual setup -->
|
||||
<xacro:if value="${ros2_control}">
|
||||
<xacro:openarm_bimanual_ros2_control
|
||||
arm_type="${arm_type}"
|
||||
left_can_interface="${left_can_interface}"
|
||||
right_can_interface="${right_can_interface}"
|
||||
use_fake_hardware="${use_fake_hardware}"
|
||||
fake_sensor_commands="${fake_sensor_commands}"
|
||||
left_arm_prefix="${left_arm_prefix}"
|
||||
right_arm_prefix="${right_arm_prefix}"
|
||||
hand="${hand}"/>
|
||||
</xacro:if>
|
||||
|
||||
<xacro:if value="$(eval hand and ee_type == 'openarm_hand')">
|
||||
<xacro:openarm_hand
|
||||
connected_to="openarm_left_link7"
|
||||
arm_type="${arm_type}"
|
||||
arm_prefix="left_"
|
||||
ee_type="${ee_type}"
|
||||
ee_kinematics_link="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/hand/openarm_hand/kinematics_link.yaml')}"
|
||||
ee_inertials="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/hand/openarm_hand/inertials.yaml')}"
|
||||
rpy_ee="$(arg rpy_ee)"
|
||||
xyz_ee="$(arg xyz_ee)"
|
||||
tcp_xyz="$(arg tcp_xyz)"
|
||||
tcp_rpy="$(arg tcp_rpy)"
|
||||
description_pkg="$(arg description_pkg)"
|
||||
/>
|
||||
<xacro:openarm_hand
|
||||
connected_to="openarm_right_link7"
|
||||
arm_type="${arm_type}"
|
||||
arm_prefix="right_"
|
||||
ee_type="${ee_type}"
|
||||
ee_kinematics_link="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/hand/openarm_hand/kinematics_link.yaml')}"
|
||||
ee_inertials="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/hand/openarm_hand/inertials.yaml')}"
|
||||
rpy_ee="$(arg rpy_ee)"
|
||||
xyz_ee="$(arg xyz_ee)"
|
||||
tcp_xyz="$(arg tcp_xyz)"
|
||||
tcp_rpy="$(arg tcp_rpy)"
|
||||
description_pkg="$(arg description_pkg)"
|
||||
/>
|
||||
</xacro:if>
|
||||
</xacro:if>
|
||||
|
||||
<xacro:if value="${not bimanual}">
|
||||
|
||||
<xacro:openarm_arm
|
||||
arm_type="${arm_type}"
|
||||
arm_prefix="${arm_prefix_modified}"
|
||||
no_prefix="${no_prefix}"
|
||||
xyz="${xyz}"
|
||||
rpy="${rpy}"
|
||||
joint_limits="${joint_limits}"
|
||||
kinematics="${kinematics}"
|
||||
kinematics_link="${kinematics_link}"
|
||||
inertials="${inertials}"
|
||||
connected_to=""
|
||||
/>
|
||||
|
||||
<xacro:if value="${ros2_control}">
|
||||
|
||||
<xacro:openarm_arm_ros2_control
|
||||
arm_type="${arm_type}"
|
||||
arm_prefix="${arm_prefix_modified}"
|
||||
can_interface="${can_interface}"
|
||||
use_fake_hardware="${use_fake_hardware}"
|
||||
fake_sensor_commands="${fake_sensor_commands}"
|
||||
hand="${hand}"
|
||||
bimanual="false"
|
||||
can_fd="${can_fd}"/>
|
||||
</xacro:if>
|
||||
|
||||
<xacro:if value="${hand and ee_type == 'openarm_hand'}">
|
||||
|
||||
<xacro:property name="special_connection" value="$(arg special_connection)" />
|
||||
<!-- <xacro:property name="connection" value="${special_connection if special_connection != '' else arm_type + '_link7'}" /> -->
|
||||
<xacro:property name="connection" value="${special_connection if special_connection != '' else 'openarm' + '_link7'}" />
|
||||
|
||||
<xacro:if value="$(eval hand and ee_type == 'openarm_hand')">
|
||||
<xacro:openarm_hand
|
||||
connected_to="${arm_prefix_modified}${connection}"
|
||||
arm_type="${arm_type}"
|
||||
arm_prefix="${arm_prefix_modified}"
|
||||
ee_type="${ee_type}"
|
||||
ee_kinematics_link="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/hand/openarm_hand/kinematics_link.yaml')}"
|
||||
ee_inertials="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/hand/openarm_hand/inertials.yaml')}"
|
||||
rpy_ee="$(arg rpy_ee)"
|
||||
xyz_ee="$(arg xyz_ee)"
|
||||
tcp_xyz="$(arg tcp_xyz)"
|
||||
tcp_rpy="$(arg tcp_rpy)"
|
||||
description_pkg="$(arg description_pkg)"/>
|
||||
</xacro:if>
|
||||
</xacro:if>
|
||||
|
||||
</xacro:if>
|
||||
|
||||
</xacro:macro>
|
||||
</robot>
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="openarm">
|
||||
|
||||
<xacro:include filename="/Users/michel_aractingi/code/openarm_description/urdf/robot/openarm_robot.xacro"/>
|
||||
|
||||
<xacro:arg name="arm_type" default="v10" />
|
||||
|
||||
<xacro:arg name="body_type" default="v10" />
|
||||
|
||||
<xacro:arg name="ee_type" default="openarm_hand" />
|
||||
|
||||
<xacro:arg name="no_prefix" default="false"/>
|
||||
|
||||
<xacro:arg name="hand" default="true" />
|
||||
|
||||
<xacro:if value="$(eval ee_type == 'none')">
|
||||
<xacro:property name="hand" value="false"/>
|
||||
</xacro:if>
|
||||
|
||||
<xacro:arg name="parent" default="world" />
|
||||
|
||||
<xacro:arg name="xyz" default="0 0 0" />
|
||||
|
||||
<xacro:arg name="rpy" default="0 0 0" />
|
||||
|
||||
<xacro:arg name="ros2_control" default="false" />
|
||||
|
||||
<xacro:arg name="can_interface" default="can0" />
|
||||
|
||||
<xacro:arg name="left_can_interface" default="can1" />
|
||||
|
||||
<xacro:arg name="right_can_interface" default="can0" />
|
||||
|
||||
<xacro:arg name="left_arm_prefix" default="left_" />
|
||||
|
||||
<xacro:arg name="right_arm_prefix" default="right_" />
|
||||
|
||||
<xacro:arg name="use_fake_hardware" default="false" />
|
||||
|
||||
<xacro:arg name="fake_sensor_commands" default="false" />
|
||||
|
||||
<xacro:arg name="bimanual" default="true" />
|
||||
|
||||
<xacro:arg name="right_arm_base_xyz" default="0.0 -0.031 0.698" />
|
||||
<xacro:arg name="right_arm_base_rpy" default="1.5708 0 0" />
|
||||
|
||||
<xacro:arg name="left_arm_base_xyz" default="0.0 0.031 0.698" />
|
||||
<xacro:arg name="left_arm_base_rpy" default="-1.5708 0 0" />
|
||||
|
||||
<xacro:arg name="can_fd" default="true" />
|
||||
|
||||
<xacro:openarm_robot
|
||||
arm_type="v10"
|
||||
body_type="v10"
|
||||
joint_limits="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config//arm/v10/joint_limits.yaml')}"
|
||||
inertials="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/arm/v10/inertials.yaml')}"
|
||||
kinematics="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/arm/v10/kinematics.yaml')}"
|
||||
kinematics_link="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/arm/v10/kinematics_link.yaml')}"
|
||||
kinematics_offset="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/arm/v10/kinematics_offset.yaml')}"
|
||||
body_inertials="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/body/v10/inertials.yaml')}"
|
||||
body_kinematics="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/body/v10/kinematics.yaml')}"
|
||||
body_kinematics_link="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/body/v10/kinematics.yaml')}"
|
||||
ee_kinematics_link="${xacro.load_yaml('/Users/michel_aractingi/code/openarm_description/config/hand/openarm_hand/kinematics_link.yaml')}"
|
||||
hand="$(arg hand)"
|
||||
ee_type="$(arg ee_type)"
|
||||
ros2_control="$(arg ros2_control)"
|
||||
can_interface="$(arg can_interface)"
|
||||
left_can_interface="$(arg left_can_interface)"
|
||||
right_can_interface="$(arg right_can_interface)"
|
||||
left_arm_prefix="$(arg left_arm_prefix)"
|
||||
right_arm_prefix="$(arg right_arm_prefix)"
|
||||
use_fake_hardware="$(arg use_fake_hardware)"
|
||||
fake_sensor_commands="$(arg fake_sensor_commands)"
|
||||
no_prefix="$(arg no_prefix)"
|
||||
arm_prefix=""
|
||||
body_prefix=""
|
||||
arm_connected_to="base"
|
||||
body_connected_to="world"
|
||||
bimanual="$(arg bimanual)"
|
||||
right_arm_base_xyz="$(arg right_arm_base_xyz)"
|
||||
left_arm_base_xyz="$(arg left_arm_base_xyz)"
|
||||
right_arm_base_rpy="$(arg right_arm_base_rpy)"
|
||||
left_arm_base_rpy="$(arg left_arm_base_rpy)"
|
||||
can_fd="$(arg can_fd)"
|
||||
/>
|
||||
|
||||
</robot>
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<xacro:macro name="openarm_bimanual_ros2_control"
|
||||
params="arm_type
|
||||
left_can_interface:=^|can1
|
||||
right_can_interface:=^|can0
|
||||
use_fake_hardware:=^|false
|
||||
fake_sensor_commands:=^|false
|
||||
hand:=^|false
|
||||
left_arm_prefix:=^|left_
|
||||
right_arm_prefix:=^|right_
|
||||
can_fd:=^|true">
|
||||
|
||||
<!-- Left Arm Hardware Interface -->
|
||||
<ros2_control name="openarm_left_hardware_interface" type="system">
|
||||
<hardware>
|
||||
<param name="arm_type">${arm_type}</param>
|
||||
<xacro:if value="${use_fake_hardware}">
|
||||
<plugin>fake_components/GenericSystem</plugin>
|
||||
<param name="fake_sensor_commands">${fake_sensor_commands}</param>
|
||||
<param name="state_following_offset">0.0</param>
|
||||
</xacro:if>
|
||||
<xacro:unless value="${use_fake_hardware}">
|
||||
<plugin>openarm_hardware/OpenArm_v10HW</plugin>
|
||||
<param name="can_interface">${left_can_interface}</param>
|
||||
<param name="arm_prefix">${left_arm_prefix}</param>
|
||||
<param name="hand">${hand}</param>
|
||||
<param name="can_fd">${can_fd}</param>
|
||||
</xacro:unless>
|
||||
</hardware>
|
||||
|
||||
<xacro:macro name="configure_joint" params="joint_name initial_position">
|
||||
<joint name="${joint_name}">
|
||||
<command_interface name="position"/>
|
||||
<command_interface name="velocity"/>
|
||||
<command_interface name="effort"/>
|
||||
|
||||
<state_interface name="position">
|
||||
<param name="initial_value">${initial_position}</param>
|
||||
</state_interface>
|
||||
|
||||
<state_interface name="velocity">
|
||||
<param name="initial_value">0.0</param>
|
||||
</state_interface>
|
||||
|
||||
<state_interface name="effort">
|
||||
<param name="initial_value">0.0</param>
|
||||
</state_interface>
|
||||
</joint>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:configure_joint joint_name="openarm_${left_arm_prefix}joint1" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${left_arm_prefix}joint2" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${left_arm_prefix}joint3" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${left_arm_prefix}joint4" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${left_arm_prefix}joint5" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${left_arm_prefix}joint6" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${left_arm_prefix}joint7" initial_position="0.0"/>
|
||||
|
||||
<xacro:if value="${hand}">
|
||||
<xacro:configure_joint joint_name="openarm_${left_arm_prefix}finger_joint1" initial_position="0.0" />
|
||||
</xacro:if>
|
||||
</ros2_control>
|
||||
|
||||
<!-- Right Arm Hardware Interface -->
|
||||
<ros2_control name="openarm_right_hardware_interface" type="system">
|
||||
<hardware>
|
||||
<param name="arm_type">${arm_type}</param>
|
||||
<xacro:if value="${use_fake_hardware}">
|
||||
<plugin>fake_components/GenericSystem</plugin>
|
||||
<param name="fake_sensor_commands">${fake_sensor_commands}</param>
|
||||
<param name="state_following_offset">0.0</param>
|
||||
</xacro:if>
|
||||
<xacro:unless value="${use_fake_hardware}">
|
||||
<plugin>openarm_hardware/OpenArm_v10HW</plugin>
|
||||
<param name="can_interface">${right_can_interface}</param>
|
||||
<param name="arm_prefix">${right_arm_prefix}</param>
|
||||
<param name="hand">${hand}</param>
|
||||
<param name="can_fd">${can_fd}</param>
|
||||
</xacro:unless>
|
||||
</hardware>
|
||||
|
||||
<xacro:configure_joint joint_name="openarm_${right_arm_prefix}joint1" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${right_arm_prefix}joint2" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${right_arm_prefix}joint3" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${right_arm_prefix}joint4" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${right_arm_prefix}joint5" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${right_arm_prefix}joint6" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${right_arm_prefix}joint7" initial_position="0.0"/>
|
||||
|
||||
<xacro:if value="${hand}">
|
||||
<xacro:configure_joint joint_name="openarm_${right_arm_prefix}finger_joint1" initial_position="0.0" />
|
||||
</xacro:if>
|
||||
</ros2_control>
|
||||
|
||||
</xacro:macro>
|
||||
|
||||
</robot>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
|
||||
<xacro:macro name="openarm_arm_ros2_control"
|
||||
params="arm_type
|
||||
can_interface
|
||||
use_fake_hardware:=^|false
|
||||
fake_sensor_commands:=^|false
|
||||
hand:=^|false
|
||||
gazebo_effort:=^|false
|
||||
arm_prefix:=''
|
||||
bimanual:=false
|
||||
can_fd:=^|true">
|
||||
|
||||
<ros2_control name="openarm_hardware_interface" type="system">
|
||||
<hardware>
|
||||
<param name="arm_type">${arm_type}</param>
|
||||
<param name="prefix">${arm_prefix}</param>
|
||||
<xacro:if value="${use_fake_hardware}">
|
||||
<plugin>fake_components/GenericSystem</plugin>
|
||||
<param name="fake_sensor_commands">${fake_sensor_commands}</param>
|
||||
<param name="state_following_offset">0.0</param>
|
||||
</xacro:if>
|
||||
<xacro:if value="${use_fake_hardware == 0}">
|
||||
<plugin>openarm_hardware/OpenArm_${arm_type}HW</plugin>
|
||||
<param name="can_interface">${can_interface}</param>
|
||||
<param name="arm_prefix">${arm_prefix}</param>
|
||||
<param name="hand">${hand}</param>
|
||||
<param name="can_fd">${can_fd}</param>
|
||||
</xacro:if>
|
||||
</hardware>
|
||||
|
||||
<xacro:macro name="configure_joint" params="joint_name initial_position">
|
||||
<joint name="${joint_name}">
|
||||
<!--
|
||||
deactivated for gazebo velocity and position interface due to a bug
|
||||
https://github.com/ros-controls/gz_ros2_control/issues/343
|
||||
|
||||
Command Interfaces -->
|
||||
<command_interface name="position"/>
|
||||
<command_interface name="velocity"/>
|
||||
<command_interface name="effort"/>
|
||||
|
||||
<!-- State Interfaces -->
|
||||
<state_interface name="position">
|
||||
<param name="initial_value">${initial_position}</param>
|
||||
</state_interface>
|
||||
|
||||
<state_interface name="velocity">
|
||||
<param name="initial_value">0.0</param>
|
||||
</state_interface>
|
||||
|
||||
<state_interface name="effort">
|
||||
<param name="initial_value">0.0</param>
|
||||
</state_interface>
|
||||
|
||||
</joint>
|
||||
</xacro:macro>
|
||||
|
||||
<xacro:configure_joint joint_name="openarm_${arm_prefix}joint1" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${arm_prefix}joint2" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${arm_prefix}joint3" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${arm_prefix}joint4" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${arm_prefix}joint5" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${arm_prefix}joint6" initial_position="0.0"/>
|
||||
<xacro:configure_joint joint_name="openarm_${arm_prefix}joint7" initial_position="0.0"/>
|
||||
|
||||
<xacro:if value="${hand}">
|
||||
<xacro:configure_joint joint_name="openarm_${arm_prefix}finger_joint1" initial_position="0.0" />
|
||||
</xacro:if>
|
||||
|
||||
</ros2_control>
|
||||
|
||||
</xacro:macro>
|
||||
|
||||
|
||||
</robot>
|
||||
@@ -13,6 +13,7 @@
|
||||
# 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 gc
|
||||
import logging
|
||||
import time
|
||||
from contextlib import nullcontext
|
||||
@@ -46,6 +47,11 @@ from lerobot.utils.train_utils import (
|
||||
save_checkpoint,
|
||||
update_last_checkpoint,
|
||||
)
|
||||
from lerobot.utils.relative_actions import (
|
||||
convert_to_relative_actions,
|
||||
compute_relative_action_stats,
|
||||
PerTimestepNormalizer,
|
||||
)
|
||||
from lerobot.utils.utils import (
|
||||
format_big_number,
|
||||
has_method,
|
||||
@@ -298,6 +304,41 @@ def train(cfg: TrainPipelineConfig, accelerator: Accelerator | None = None):
|
||||
device=device,
|
||||
)
|
||||
|
||||
# Compute per-timestep normalizer for relative actions
|
||||
relative_normalizer = None
|
||||
if cfg.use_relative_actions:
|
||||
mode = "actions + state" if cfg.use_relative_state else "actions only"
|
||||
cfg.output_dir.mkdir(parents=True, exist_ok=True)
|
||||
stats_path = cfg.output_dir / "relative_stats.pt"
|
||||
|
||||
if is_main_process:
|
||||
logging.info(colored(f"Relative mode: {mode}", "cyan", attrs=["bold"]))
|
||||
|
||||
if stats_path.exists():
|
||||
logging.info(f"Loading pre-computed stats from: {stats_path}")
|
||||
else:
|
||||
logging.info("Computing per-timestep stats (first 1000 batches)...")
|
||||
logging.info("Using fresh dataset to avoid video decoder state issues...")
|
||||
# Create separate dataset instance to avoid corrupting main dataset's video decoders
|
||||
stats_dataset = make_dataset(cfg)
|
||||
temp_loader = torch.utils.data.DataLoader(
|
||||
stats_dataset, batch_size=cfg.batch_size, shuffle=False, num_workers=0
|
||||
)
|
||||
mean, std = compute_relative_action_stats(temp_loader, num_batches=1000)
|
||||
del temp_loader, stats_dataset
|
||||
gc.collect()
|
||||
torch.save({"mean": mean, "std": std}, stats_path)
|
||||
logging.info(f"Saved stats to: {stats_path}")
|
||||
|
||||
# Poll for stats file instead of using NCCL barrier (avoids timeout during long computation)
|
||||
if not is_main_process:
|
||||
while not stats_path.exists():
|
||||
time.sleep(5)
|
||||
|
||||
data = torch.load(stats_path, weights_only=True, map_location="cpu")
|
||||
relative_normalizer = PerTimestepNormalizer(data["mean"], data["std"])
|
||||
accelerator.wait_for_everyone() # Sync after everyone has loaded
|
||||
|
||||
step = 0 # number of policy updates (forward + backward + optim)
|
||||
|
||||
if cfg.resume:
|
||||
@@ -385,6 +426,13 @@ def train(cfg: TrainPipelineConfig, accelerator: Accelerator | None = None):
|
||||
start_time = time.perf_counter()
|
||||
batch = next(dl_iter)
|
||||
batch = preprocessor(batch)
|
||||
|
||||
# Convert to relative actions (and optionally state) if enabled
|
||||
if cfg.use_relative_actions:
|
||||
batch = convert_to_relative_actions(batch, convert_state=cfg.use_relative_state)
|
||||
if relative_normalizer is not None:
|
||||
batch["action"] = relative_normalizer.normalize(batch["action"])
|
||||
|
||||
train_tracker.dataloading_s = time.perf_counter() - start_time
|
||||
|
||||
train_tracker, output_dict = update_policy(
|
||||
@@ -439,6 +487,9 @@ def train(cfg: TrainPipelineConfig, accelerator: Accelerator | None = None):
|
||||
preprocessor=preprocessor,
|
||||
postprocessor=postprocessor,
|
||||
)
|
||||
# Save relative action stats with checkpoint
|
||||
if relative_normalizer is not None:
|
||||
relative_normalizer.save(checkpoint_dir / "relative_stats.pt")
|
||||
update_last_checkpoint(checkpoint_dir)
|
||||
if wandb_logger:
|
||||
wandb_logger.log_policy(checkpoint_dir)
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
UMI-style relative actions with per-timestep normalization.
|
||||
|
||||
Two modes supported:
|
||||
Mode 1: Relative actions only (use_relative_state=False)
|
||||
- Actions converted to relative, state stays absolute
|
||||
Mode 2: Relative actions + state (use_relative_state=True, full UMI)
|
||||
- Both actions and state converted to relative
|
||||
|
||||
Per-timestep normalization (TRI LBM / BEHAVIOR style):
|
||||
Training: action_norm[t] = (action_rel[t] - mean[t]) / std[t]
|
||||
Inference: action_rel[t] = action_norm[t] * std[t] + mean[t]
|
||||
"""
|
||||
|
||||
import torch
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class PerTimestepNormalizer:
|
||||
"""Per-timestep normalization using precomputed dataset statistics."""
|
||||
|
||||
def __init__(self, mean: torch.Tensor, std: torch.Tensor, eps: float = 1e-8):
|
||||
self.mean = mean
|
||||
self.std = std
|
||||
self.eps = eps
|
||||
self._cache = {} # Cache for device/dtype converted tensors
|
||||
|
||||
def _get_stats(self, device, dtype):
|
||||
"""Get cached stats for device/dtype, or create and cache them."""
|
||||
key = (device, dtype)
|
||||
if key not in self._cache:
|
||||
self._cache[key] = (
|
||||
self.mean.to(device, dtype),
|
||||
self.std.to(device, dtype),
|
||||
)
|
||||
return self._cache[key]
|
||||
|
||||
def normalize(self, x: torch.Tensor) -> torch.Tensor:
|
||||
mean, std = self._get_stats(x.device, x.dtype)
|
||||
if x.dim() == 3 and mean.dim() == 2:
|
||||
mean, std = mean.unsqueeze(0), std.unsqueeze(0)
|
||||
return (x - mean) / (std + self.eps)
|
||||
|
||||
def unnormalize(self, x: torch.Tensor) -> torch.Tensor:
|
||||
mean, std = self._get_stats(x.device, x.dtype)
|
||||
if x.dim() == 3 and mean.dim() == 2:
|
||||
mean, std = mean.unsqueeze(0), std.unsqueeze(0)
|
||||
return x * (std + self.eps) + mean
|
||||
|
||||
def save(self, path: Path | str):
|
||||
path = Path(path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
torch.save({"mean": self.mean.cpu(), "std": self.std.cpu(), "eps": self.eps}, path)
|
||||
|
||||
@classmethod
|
||||
def load(cls, path: Path | str) -> "PerTimestepNormalizer":
|
||||
data = torch.load(path, weights_only=True, map_location="cpu")
|
||||
return cls(data["mean"], data["std"], data.get("eps", 1e-8))
|
||||
|
||||
|
||||
def compute_relative_action_stats(
|
||||
dataloader,
|
||||
state_key: str = "observation.state",
|
||||
num_batches: int | None = None,
|
||||
) -> tuple[torch.Tensor, torch.Tensor]:
|
||||
"""Compute per-timestep mean/std from relative actions."""
|
||||
all_rel = []
|
||||
for i, batch in enumerate(dataloader):
|
||||
if num_batches is not None and i >= num_batches:
|
||||
break
|
||||
action, state = batch["action"], batch[state_key]
|
||||
current_pos = state[:, -1, :] if state.dim() == 3 else state
|
||||
min_dim = min(action.shape[-1], current_pos.shape[-1])
|
||||
rel = action.clone()
|
||||
rel[..., :min_dim] -= current_pos[:, None, :min_dim]
|
||||
all_rel.append(rel)
|
||||
|
||||
all_rel = torch.cat(all_rel, dim=0)
|
||||
return all_rel.mean(dim=0), all_rel.std(dim=0).clamp(min=1e-6)
|
||||
|
||||
|
||||
def convert_to_relative(
|
||||
batch: dict,
|
||||
state_key: str = "observation.state",
|
||||
convert_state: bool = True,
|
||||
) -> dict:
|
||||
"""
|
||||
Convert actions (and optionally state) to relative.
|
||||
|
||||
Args:
|
||||
batch: Training batch with "action" and state_key
|
||||
state_key: Key for observation state
|
||||
convert_state: If True, also convert state to relative (full UMI mode)
|
||||
"""
|
||||
if "action" not in batch or state_key not in batch:
|
||||
return batch
|
||||
|
||||
action = batch["action"]
|
||||
state = batch[state_key]
|
||||
batch = batch.copy()
|
||||
|
||||
# Get current position as reference
|
||||
current_pos = state[:, -1, :] if state.dim() == 3 else state
|
||||
|
||||
# Convert state if requested
|
||||
if convert_state:
|
||||
if state.dim() == 3:
|
||||
batch[state_key] = state - current_pos[:, None, :]
|
||||
else:
|
||||
batch[state_key] = torch.zeros_like(state)
|
||||
|
||||
# Convert actions to relative
|
||||
min_dim = min(action.shape[-1], current_pos.shape[-1])
|
||||
rel_action = action.clone()
|
||||
rel_action[..., :min_dim] -= current_pos[:, None, :min_dim]
|
||||
batch["action"] = rel_action
|
||||
|
||||
return batch
|
||||
|
||||
|
||||
# Backward compatibility alias
|
||||
convert_to_relative_actions = convert_to_relative
|
||||
|
||||
|
||||
def convert_state_to_relative(state: torch.Tensor) -> torch.Tensor:
|
||||
"""Convert state to relative (for inference with use_relative_state=True)."""
|
||||
if state.dim() == 1:
|
||||
return torch.zeros_like(state)
|
||||
current_pos = state[-1, :] if state.dim() == 2 else state[:, -1, :]
|
||||
if state.dim() == 2:
|
||||
return state - current_pos[None, :]
|
||||
return state - current_pos[:, None, :]
|
||||
|
||||
|
||||
def convert_from_relative_actions(
|
||||
relative_actions: torch.Tensor,
|
||||
current_pos: torch.Tensor,
|
||||
) -> torch.Tensor:
|
||||
"""Convert relative actions back to absolute for robot execution."""
|
||||
current_pos = current_pos.to(relative_actions.device, relative_actions.dtype)
|
||||
min_dim = min(relative_actions.shape[-1], current_pos.shape[-1])
|
||||
absolute = relative_actions.clone()
|
||||
|
||||
if relative_actions.dim() == 2:
|
||||
absolute[..., :min_dim] += current_pos[:min_dim]
|
||||
elif relative_actions.dim() == 3:
|
||||
absolute[..., :min_dim] += current_pos[None, None, :min_dim]
|
||||
else:
|
||||
absolute[..., :min_dim] += current_pos[:min_dim]
|
||||
|
||||
return absolute
|
||||
|
||||
|
||||
def convert_from_relative_actions_dict(
|
||||
relative_actions: dict[str, float],
|
||||
current_pos: dict[str, float],
|
||||
) -> dict[str, float]:
|
||||
"""Convert relative actions back to absolute (dict version for inference)."""
|
||||
return {k: v + current_pos.get(k, 0.0) for k, v in relative_actions.items()}
|
||||
@@ -0,0 +1,229 @@
|
||||
import importlib
|
||||
import os
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from safetensors.torch import load_file
|
||||
|
||||
from .utils import require_package
|
||||
|
||||
|
||||
def run_command(cmd, module, args):
|
||||
module = importlib.import_module(f"lerobot.scripts.{module}")
|
||||
with patch("sys.argv", [cmd] + args):
|
||||
module.main()
|
||||
|
||||
|
||||
def lerobot_train(args):
|
||||
return run_command(cmd="lerobot-train", module="lerobot_train", args=args)
|
||||
|
||||
|
||||
def lerobot_record(args):
|
||||
return run_command(cmd="lerobot-record", module="lerobot_record", args=args)
|
||||
|
||||
|
||||
def resolve_model_id_for_peft_training(policy_type):
|
||||
"""PEFT training needs pretrained models, this finds the pretrained model of a policy type for PEFT training."""
|
||||
if policy_type == "smolvla":
|
||||
return "lerobot/smolvla_base"
|
||||
|
||||
raise ValueError(f"No pretrained model known for {policy_type}. PEFT training will not work.")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("policy_type", ["smolvla"])
|
||||
@require_package("peft")
|
||||
def test_peft_training_push_to_hub_works(policy_type, tmp_path):
|
||||
"""Ensure that push to hub stores PEFT only the adapter, not the full model weights."""
|
||||
output_dir = tmp_path / f"output_{policy_type}"
|
||||
upload_folder_contents = set()
|
||||
|
||||
model_id = resolve_model_id_for_peft_training(policy_type)
|
||||
|
||||
def mock_upload_folder(*args, **kwargs):
|
||||
folder_path = kwargs["folder_path"]
|
||||
# we include more than is actually uploaded since we ignore {allow,ignore}_patterns of upload_folders()
|
||||
upload_folder_contents.update(os.listdir(folder_path))
|
||||
return MagicMock()
|
||||
|
||||
with (
|
||||
patch("huggingface_hub.HfApi.create_repo"),
|
||||
patch("huggingface_hub.HfApi.upload_folder", mock_upload_folder),
|
||||
):
|
||||
lerobot_train(
|
||||
[
|
||||
f"--policy.path={model_id}",
|
||||
"--policy.push_to_hub=true",
|
||||
"--policy.repo_id=foo/bar",
|
||||
"--policy.input_features=null",
|
||||
"--policy.output_features=null",
|
||||
"--peft.method=LORA",
|
||||
"--dataset.repo_id=lerobot/pusht",
|
||||
"--dataset.episodes=[0, 1]",
|
||||
"--steps=1",
|
||||
f"--output_dir={output_dir}",
|
||||
]
|
||||
)
|
||||
|
||||
assert "adapter_model.safetensors" in upload_folder_contents
|
||||
assert "config.json" in upload_folder_contents
|
||||
assert "adapter_config.json" in upload_folder_contents
|
||||
|
||||
|
||||
@pytest.mark.parametrize("policy_type", ["smolvla"])
|
||||
@require_package("peft")
|
||||
def test_peft_training_works(policy_type, tmp_path):
|
||||
"""Check whether the standard case of fine-tuning a (partially) pre-trained policy with PEFT works."""
|
||||
output_dir = tmp_path / f"output_{policy_type}"
|
||||
model_id = resolve_model_id_for_peft_training(policy_type)
|
||||
|
||||
lerobot_train(
|
||||
[
|
||||
f"--policy.path={model_id}",
|
||||
"--policy.push_to_hub=false",
|
||||
"--policy.input_features=null",
|
||||
"--policy.output_features=null",
|
||||
"--peft.method=LORA",
|
||||
"--dataset.repo_id=lerobot/pusht",
|
||||
"--dataset.episodes=[0, 1]",
|
||||
"--steps=1",
|
||||
f"--output_dir={output_dir}",
|
||||
]
|
||||
)
|
||||
|
||||
policy_dir = output_dir / "checkpoints" / "last" / "pretrained_model"
|
||||
|
||||
for file in ["adapter_config.json", "adapter_model.safetensors", "config.json"]:
|
||||
assert (policy_dir / file).exists()
|
||||
|
||||
# This is the default case where we train a pre-trained policy from scratch with new data.
|
||||
# We assume that we target policy-specific modules but fully fine-tune action and state projections
|
||||
# so these must be part of the trained state dict.
|
||||
state_dict = load_file(policy_dir / "adapter_model.safetensors")
|
||||
|
||||
adapted_keys = [
|
||||
"state_proj",
|
||||
"action_in_proj",
|
||||
"action_out_proj",
|
||||
"action_time_mlp_in",
|
||||
"action_time_mlp_out",
|
||||
]
|
||||
|
||||
found_keys = [
|
||||
module_key
|
||||
for module_key in adapted_keys
|
||||
for state_dict_key in state_dict
|
||||
if f".{module_key}." in state_dict_key
|
||||
]
|
||||
|
||||
assert set(found_keys) == set(adapted_keys)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("policy_type", ["smolvla"])
|
||||
@require_package("peft")
|
||||
def test_peft_training_params_are_fewer(policy_type, tmp_path):
|
||||
"""Check whether the standard case of fine-tuning a (partially) pre-trained policy with PEFT works."""
|
||||
output_dir = tmp_path / f"output_{policy_type}"
|
||||
model_id = resolve_model_id_for_peft_training(policy_type)
|
||||
|
||||
def dummy_update_policy(
|
||||
train_metrics, policy, batch, optimizer, grad_clip_norm: float, accelerator, **kwargs
|
||||
):
|
||||
params_total = sum(p.numel() for p in policy.parameters())
|
||||
params_trainable = sum(p.numel() for p in policy.parameters() if p.requires_grad)
|
||||
|
||||
assert params_total > params_trainable
|
||||
|
||||
return train_metrics, {}
|
||||
|
||||
with patch("lerobot.scripts.lerobot_train.update_policy", dummy_update_policy):
|
||||
lerobot_train(
|
||||
[
|
||||
f"--policy.path={model_id}",
|
||||
"--policy.push_to_hub=false",
|
||||
"--policy.input_features=null",
|
||||
"--policy.output_features=null",
|
||||
"--peft.method=LORA",
|
||||
"--dataset.repo_id=lerobot/pusht",
|
||||
"--dataset.episodes=[0, 1]",
|
||||
"--steps=1",
|
||||
f"--output_dir={output_dir}",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class DummyRobot:
|
||||
name = "dummy"
|
||||
cameras = []
|
||||
action_features = {"foo": 1.0, "bar": 2.0}
|
||||
observation_features = {"obs1": 1.0, "obs2": 2.0}
|
||||
is_connected = True
|
||||
|
||||
def connect(self, *args):
|
||||
pass
|
||||
|
||||
def disconnect(self):
|
||||
pass
|
||||
|
||||
|
||||
def dummy_make_robot_from_config(*args, **kwargs):
|
||||
return DummyRobot()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("policy_type", ["smolvla"])
|
||||
@require_package("peft")
|
||||
def test_peft_record_loads_policy(policy_type, tmp_path):
|
||||
"""Train a policy with PEFT and attempt to load it with `lerobot-record`."""
|
||||
from peft import PeftModel
|
||||
|
||||
output_dir = tmp_path / f"output_{policy_type}"
|
||||
model_id = resolve_model_id_for_peft_training(policy_type)
|
||||
|
||||
lerobot_train(
|
||||
[
|
||||
f"--policy.path={model_id}",
|
||||
"--policy.push_to_hub=false",
|
||||
"--policy.input_features=null",
|
||||
"--policy.output_features=null",
|
||||
"--peft.method=LORA",
|
||||
"--dataset.repo_id=lerobot/pusht",
|
||||
"--dataset.episodes=[0, 1]",
|
||||
"--steps=1",
|
||||
f"--output_dir={output_dir}",
|
||||
]
|
||||
)
|
||||
|
||||
policy_dir = output_dir / "checkpoints" / "last" / "pretrained_model"
|
||||
dataset_dir = tmp_path / "eval_pusht"
|
||||
single_task = "move the table"
|
||||
loaded_policy = None
|
||||
|
||||
def dummy_record_loop(*args, **kwargs):
|
||||
nonlocal loaded_policy
|
||||
|
||||
if "dataset" not in kwargs:
|
||||
return
|
||||
|
||||
dataset = kwargs["dataset"]
|
||||
dataset.add_frame({"task": single_task})
|
||||
loaded_policy = kwargs["policy"]
|
||||
|
||||
with (
|
||||
patch("lerobot.scripts.lerobot_record.make_robot_from_config", dummy_make_robot_from_config),
|
||||
# disable record loop since we're only interested in successful loading of the policy.
|
||||
patch("lerobot.scripts.lerobot_record.record_loop", dummy_record_loop),
|
||||
# disable speech output
|
||||
patch("lerobot.utils.utils.say"),
|
||||
):
|
||||
lerobot_record(
|
||||
[
|
||||
f"--policy.path={policy_dir}",
|
||||
"--robot.type=so101_follower",
|
||||
"--robot.port=/dev/null",
|
||||
"--dataset.repo_id=lerobot/eval_pusht",
|
||||
f'--dataset.single_task="{single_task}"',
|
||||
f"--dataset.root={dataset_dir}",
|
||||
"--dataset.push_to_hub=false",
|
||||
]
|
||||
)
|
||||
|
||||
assert isinstance(loaded_policy, PeftModel)
|
||||
Reference in New Issue
Block a user