file

#1
by pr0tos - opened

lerobot-record
--robot.type=so101_follower
--robot.port=/dev/tty.usbmodem5AAF2198231
--robot.id=my_awesome_follower_arm_2
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30},
right: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30},
}"
--teleop.type=so101_leader
--teleop.port=/dev/tty.usbmodem5AAF2190341
--teleop.id=my_awesome_leader_arm_2
--display_data=true
--dataset.repo_id=kd-forge/eval_debug_recovery
--dataset.num_episodes=10
--dataset.single_task="Your task description"
--dataset.streaming_encoding=true
--dataset.encoder_threads=2
--policy.path=kd-forge/act_aim-bolt-nut_55ep

#!/usr/bin/env python3
"""Send a single goal position to one joint (SO follower or bi_so follower).

Run from repo root with package on PYTHONPATH, e.g.:
uv run python examples/send_joint_position.py --port /dev/ttyUSB0 --joint shoulder_pan --position 0.0

Bimanual (joint name must include left_ or right_ prefix):
uv run python examples/send_joint_position.py --robot bi_so_follower \
--left-port /dev/ttyUSB0 --right-port /dev/ttyUSB1 --joint left_elbow_flex --position -20

Joint names (one arm): shoulder_pan, shoulder_lift, elbow_flex, wrist_flex, wrist_roll, gripper
You can pass shoulder_pan or shoulder_pan.pos. Position is in degrees if use_degrees=True (default).
"""

from future import annotations

import argparse
import sys

def _joint_key(name: str) -> str:
name = name.strip()
return name if name.endswith(".pos") else f"{name}.pos"

def main() -> None:
p = argparse.ArgumentParser(description=doc, formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument("--robot", choices=("so100_follower", "bi_so_follower"), default="so100_follower")
p.add_argument("--port", default=None, help="Serial port (single arm)")
p.add_argument("--left-port", default=None)
p.add_argument("--right-port", default=None)
p.add_argument("--id", default="test_joint", help="Robot id for calibration file")
p.add_argument("--joint", required=True, help="Motor key, e.g. shoulder_pan or left_shoulder_pan.pos")
p.add_argument("--position", type=float, required=True, help="Goal position (degrees by default)")
p.add_argument("--no-calibrate", action="store_true", help="connect(calibrate=False)")
args = p.parse_args()

key = _joint_key(args.joint)

from lerobot.robots.bi_so_follower.bi_so_follower import BiSOFollower
from lerobot.robots.bi_so_follower.config_bi_so_follower import BiSOFollowerConfig
from lerobot.robots.so_follower.config_so_follower import SOFollowerConfig, SOFollowerRobotConfig
from lerobot.robots.so_follower.so_follower import SOFollower

calibrate = not args.no_calibrate

if args.robot == "so100_follower":
    if not args.port:
        print("error: --port is required for so100_follower", file=sys.stderr)
        sys.exit(2)
    cfg = SOFollowerRobotConfig(port=args.port, id=args.id, cameras={})
    robot = SOFollower(cfg)
else:
    if not args.left_port or not args.right_port:
        print("error: --left-port and --right-port are required for bi_so_follower", file=sys.stderr)
        sys.exit(2)
    if not (key.startswith("left_") or key.startswith("right_")):
        print(
            "error: for bi_so_follower use joint names like left_shoulder_pan or right_wrist_roll",
            file=sys.stderr,
        )
        sys.exit(2)
    cfg = BiSOFollowerConfig(
        id=args.id,
        left_arm_config=SOFollowerConfig(port=args.left_port, cameras={}),
        right_arm_config=SOFollowerConfig(port=args.right_port, cameras={}),
    )
    robot = BiSOFollower(cfg)

robot.connect(calibrate=calibrate)
try:
    sent = robot.send_action({key: args.position})
    print(f"sent {key}={args.position!r} -> effective {sent}")
finally:
    robot.disconnect()

if name == "main":
main()

Sign up or log in to comment