173 lines
4.6 KiB
Python
173 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
g1_arm_actions_cli.py
|
|
|
|
Run Unitree G1 Arm ActionClient actions directly on the robot from CLI.
|
|
|
|
Changes from your request:
|
|
- Network interface is OPTIONAL (no longer required).
|
|
If you pass it, it will use it; otherwise it uses default DDS interface.
|
|
|
|
Usage:
|
|
# list actions
|
|
python3 g1_arm_actions_cli.py list
|
|
|
|
# run by id
|
|
python3 g1_arm_actions_cli.py 6
|
|
|
|
# run by name (quotes recommended)
|
|
python3 g1_arm_actions_cli.py "face wave"
|
|
|
|
# specify interface explicitly (optional)
|
|
python3 g1_arm_actions_cli.py "shake hand" --iface enp3s0
|
|
|
|
# force auto release
|
|
python3 g1_arm_actions_cli.py "hug" --auto-release --sleep 2
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
import time
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
from unitree_sdk2py.core.channel import ChannelFactoryInitialize
|
|
from unitree_sdk2py.g1.arm.g1_arm_action_client import G1ArmActionClient, action_map
|
|
|
|
|
|
@dataclass
|
|
class TestOption:
|
|
name: str
|
|
id: int
|
|
|
|
|
|
OPTION_LIST = [
|
|
TestOption(name="release arm", id=0),
|
|
TestOption(name="shake hand", id=1),
|
|
TestOption(name="high five", id=2),
|
|
TestOption(name="hug", id=3),
|
|
TestOption(name="high wave", id=4),
|
|
TestOption(name="clap", id=5),
|
|
TestOption(name="face wave", id=6),
|
|
TestOption(name="left kiss", id=7),
|
|
TestOption(name="heart", id=8),
|
|
TestOption(name="right heart", id=9),
|
|
TestOption(name="hands up", id=10),
|
|
TestOption(name="x-ray", id=11),
|
|
TestOption(name="right hand up", id=12),
|
|
TestOption(name="reject", id=13),
|
|
TestOption(name="right kiss", id=14),
|
|
TestOption(name="two-hand kiss", id=15),
|
|
]
|
|
|
|
# Actions that your original script auto-released after ~2s
|
|
DEFAULT_AUTO_RELEASE_IDS = {1, 2, 3, 8, 9, 10, 11, 12, 13}
|
|
|
|
|
|
def find_option(query: str) -> Optional[TestOption]:
|
|
q = query.strip().lower()
|
|
|
|
# try id
|
|
try:
|
|
as_int = int(q)
|
|
for opt in OPTION_LIST:
|
|
if opt.id == as_int:
|
|
return opt
|
|
except ValueError:
|
|
pass
|
|
|
|
# try name
|
|
for opt in OPTION_LIST:
|
|
if opt.name.lower() == q:
|
|
return opt
|
|
|
|
return None
|
|
|
|
|
|
def print_list():
|
|
print("\nAvailable actions:")
|
|
for opt in OPTION_LIST:
|
|
print(f" {opt.id:>2} {opt.name}")
|
|
print("")
|
|
|
|
|
|
def execute_action(client: G1ArmActionClient, opt: TestOption):
|
|
if opt.name not in action_map:
|
|
raise RuntimeError(f"Action '{opt.name}' not found in action_map. Check SDK version.")
|
|
print(f"[RUN] {opt.name} (id={opt.id})")
|
|
client.ExecuteAction(action_map.get(opt.name))
|
|
|
|
|
|
def init_dds(iface: Optional[str]):
|
|
# If iface provided, use it; otherwise default interface.
|
|
if iface:
|
|
ChannelFactoryInitialize(0, iface)
|
|
else:
|
|
ChannelFactoryInitialize(0)
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument(
|
|
"action",
|
|
help='Action id/name, or "list". Examples: 6 | "face wave" | list',
|
|
)
|
|
ap.add_argument("--iface", default=None, help="Optional network interface, e.g. enp3s0/eth0/wlan0")
|
|
ap.add_argument("--timeout", type=float, default=10.0, help="Action client timeout (seconds)")
|
|
ap.add_argument(
|
|
"--auto-release",
|
|
action="store_true",
|
|
help="After action, run 'release arm' automatically (like your demo for some actions).",
|
|
)
|
|
ap.add_argument(
|
|
"--sleep",
|
|
type=float,
|
|
default=2.0,
|
|
help="Seconds to wait before auto-release (only used with auto-release).",
|
|
)
|
|
ap.add_argument(
|
|
"--no-prompt",
|
|
action="store_true",
|
|
help="Skip the 'Press Enter to continue' safety prompt.",
|
|
)
|
|
args = ap.parse_args()
|
|
|
|
if args.action.strip().lower() == "list":
|
|
print_list()
|
|
return
|
|
|
|
opt = find_option(args.action)
|
|
if not opt:
|
|
print(f"[ERR] No matching action for: {args.action!r}")
|
|
print_list()
|
|
sys.exit(2)
|
|
|
|
print("WARNING: Please ensure there are no obstacles around the robot while running this script.")
|
|
if not args.no_prompt:
|
|
input("Press Enter to continue... ")
|
|
|
|
# DDS init (optional iface)
|
|
init_dds(args.iface)
|
|
|
|
# Arm action client
|
|
client = G1ArmActionClient()
|
|
client.SetTimeout(args.timeout)
|
|
client.Init()
|
|
|
|
execute_action(client, opt)
|
|
|
|
# Decide auto-release behavior
|
|
do_auto_release = args.auto_release or (opt.id in DEFAULT_AUTO_RELEASE_IDS)
|
|
|
|
if do_auto_release and opt.id != 0:
|
|
time.sleep(max(0.0, args.sleep))
|
|
print("[AUTO] release arm")
|
|
client.ExecuteAction(action_map.get("release arm"))
|
|
|
|
time.sleep(0.5)
|
|
print("[DONE]")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|