Skip to content

Commit c7630e5

Browse files
committed
feat: cartesian control
1 parent b07c6f0 commit c7630e5

File tree

6 files changed

+183
-34
lines changed

6 files changed

+183
-34
lines changed

extensions/rcs_xarm7/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ dependencies = [
1313
readme = "README.md"
1414
maintainers = [
1515
{ name = "Tobias Jülg", email = "[email protected]" },
16+
{ name = "Ken Nakahara", email = "[email protected]" },
1617
]
1718
authors = [
1819
{ name = "Tobias Jülg", email = "[email protected]" },
19-
{ name = "Pierre Krack", email = "[email protected]" },
2020
{ name = "Ken Nakahara", email = "[email protected]" },
2121
]
2222
requires-python = ">=3.10"

extensions/rcs_xarm7/src/rcs_xarm7/creators.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,18 @@
3333
class RCSXArm7EnvCreator(RCSHardwareEnvCreator):
3434
def __call__( # type: ignore
3535
self,
36+
control_mode: ControlMode,
3637
ip: str,
37-
urdf_path: str,
3838
calibration_dir: PathLike | str | None = None,
3939
camera_set: HardwareCameraSet | None = None,
4040
max_relative_movement: float | tuple[float, float] | None = None,
4141
relative_to: RelativeTo = RelativeTo.LAST_STEP,
4242
) -> gym.Env:
4343
if isinstance(calibration_dir, str):
4444
calibration_dir = Path(calibration_dir)
45-
robot = XArm7(ip=ip, urdf_path=urdf_path)
46-
env: gym.Env = RobotEnv(robot, ControlMode.JOINTS)
45+
robot = XArm7(ip=ip)
46+
# env: gym.Env = RobotEnv(robot, ControlMode.JOINTS, home_on_reset=True)
47+
env: gym.Env = RobotEnv(robot, control_mode, home_on_reset=True)
4748

4849
if camera_set is not None:
4950
camera_set.start()
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import logging
2+
from time import sleep
3+
4+
from rcs._core.common import RobotPlatform
5+
from rcs.envs.base import ControlMode, RelativeTo
6+
from rcs_fr3.desk import ContextManager
7+
from rcs_xarm7.creators import RCSXArm7EnvCreator, XArm7SimEnvCreator
8+
9+
import rcs
10+
from rcs import sim
11+
import numpy as np
12+
13+
logger = logging.getLogger(__name__)
14+
logger.setLevel(logging.INFO)
15+
16+
ROBOT_IP = "192.168.1.245"
17+
# ROBOT_INSTANCE = RobotPlatform.SIMULATION
18+
ROBOT_INSTANCE = RobotPlatform.HARDWARE
19+
20+
21+
def main():
22+
23+
if ROBOT_INSTANCE == RobotPlatform.HARDWARE:
24+
env_rel = RCSXArm7EnvCreator()(
25+
control_mode=ControlMode.CARTESIAN_TQuat,
26+
ip=ROBOT_IP,
27+
relative_to=RelativeTo.LAST_STEP,
28+
max_relative_movement=np.deg2rad(3)
29+
)
30+
cm = env_rel.unwrapped.robot
31+
else:
32+
cm = ContextManager()
33+
cfg = sim.SimRobotConfig()
34+
cfg.robot_type = rcs.common.RobotType.XArm7
35+
cfg.actuators = ["1", "2", "3", "4", "5"]
36+
cfg.joints = ["1", "2", "3", "4", "5"]
37+
cfg.arm_collision_geoms = []
38+
cfg.attachment_site = "gripper"
39+
40+
grp_cfg = sim.SimGripperConfig()
41+
grp_cfg.actuator = "6"
42+
grp_cfg.joint = "6"
43+
grp_cfg.collision_geoms = []
44+
grp_cfg.collision_geoms_fingers = []
45+
46+
env_rel = XArm7SimEnvCreator()(
47+
control_mode=ControlMode.JOINTS,
48+
urdf_path="/home/tobi/coding/lerobot/so101_new_calib.urdf",
49+
robot_cfg=cfg,
50+
collision_guard=False,
51+
mjcf="/home/tobi/coding/lerobot/SO-ARM100/Simulation/XArm7/scene.xml",
52+
gripper_cfg=grp_cfg,
53+
# camera_set_cfg=default_mujoco_cameraset_cfg(),
54+
max_relative_movement=None,
55+
# max_relative_movement=10.0,
56+
# max_relative_movement=0.5,
57+
relative_to=RelativeTo.LAST_STEP,
58+
)
59+
env_rel.get_wrapper_attr("sim").open_gui()
60+
env_rel.reset()
61+
act = {"tquat": [0.1, 0, 0, 0, 0, 0, 1], "gripper": 0}
62+
obs, reward, terminated, truncated, info = env_rel.step(act)
63+
64+
with cm:
65+
for _ in range(10):
66+
for _ in range(10):
67+
# move 1cm in x direction (forward) and close gripper
68+
act = {"tquat": [0.01, 0, 0, 0, 0, 0, 1], "gripper": 0}
69+
obs, reward, terminated, truncated, info = env_rel.step(act)
70+
if truncated or terminated:
71+
logger.info("Truncated or terminated!")
72+
return
73+
print(obs)
74+
#sleep(2.0)
75+
for _ in range(10):
76+
# move 1cm in negative x direction (backward) and open gripper
77+
act = {"tquat": [-0.01, 0, 0, 0, 0, 0, 1], "gripper": 1}
78+
obs, reward, terminated, truncated, info = env_rel.step(act)
79+
if truncated or terminated:
80+
logger.info("Truncated or terminated!")
81+
return
82+
83+
84+
if __name__ == "__main__":
85+
main()

extensions/rcs_xarm7/src/rcs_xarm7/env_joint_control.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33

44
from rcs._core.common import RobotPlatform
55
from rcs.envs.base import ControlMode, RelativeTo
6+
from rcs_fr3.desk import ContextManager
67
from rcs_xarm7.creators import RCSXArm7EnvCreator, XArm7SimEnvCreator
78

89
import rcs
910
from rcs import sim
11+
import numpy as np
1012

1113
logger = logging.getLogger(__name__)
1214
logger.setLevel(logging.INFO)
@@ -20,11 +22,14 @@ def main():
2022

2123
if ROBOT_INSTANCE == RobotPlatform.HARDWARE:
2224
env_rel = RCSXArm7EnvCreator()(
25+
control_mode=ControlMode.JOINTS,
2326
ip=ROBOT_IP,
24-
urdf_path="/home/ken/Downloads/xarm7.urdf",
2527
relative_to=RelativeTo.LAST_STEP,
28+
max_relative_movement=np.deg2rad(3)
2629
)
30+
cm = env_rel.unwrapped.robot
2731
else:
32+
cm = ContextManager()
2833
cfg = sim.SimRobotConfig()
2934
cfg.robot_type = rcs.common.RobotType.XArm7
3035
cfg.actuators = ["1", "2", "3", "4", "5"]
@@ -53,20 +58,21 @@ def main():
5358
)
5459
env_rel.get_wrapper_attr("sim").open_gui()
5560

56-
for _ in range(10):
57-
obs, info = env_rel.reset()
58-
for _ in range(100):
59-
# sample random relative action and execute it
60-
act = env_rel.action_space.sample()
61-
print(act)
62-
# act["gripper"] = 1.0
63-
obs, reward, terminated, truncated, info = env_rel.step(act)
64-
print(obs)
65-
if truncated or terminated:
66-
logger.info("Truncated or terminated!")
67-
return
68-
logger.info(act["gripper"])
69-
sleep(1.0)
61+
with cm:
62+
for _ in range(10):
63+
obs, info = env_rel.reset()
64+
for _ in range(3):
65+
# sample random relative action and execute it
66+
act = env_rel.action_space.sample()
67+
print(act)
68+
# act["gripper"] = 1.0
69+
obs, reward, terminated, truncated, info = env_rel.step(act)
70+
print(obs)
71+
if truncated or terminated:
72+
logger.info("Truncated or terminated!")
73+
return
74+
# logger.info(act["gripper"])
75+
sleep(2.0)
7076

7177

7278
if __name__ == "__main__":

extensions/rcs_xarm7/src/rcs_xarm7/hw.py

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,70 @@
1+
from dataclasses import dataclass, field
12
import typing
3+
from typing import List
24

35
import numpy as np
46
from xarm.wrapper import XArmAPI
57

68
from rcs import common
79

10+
from scipy.spatial.transform import Rotation as R
811

9-
class XArm7(common.Robot):
10-
def __init__(self, ip: str, urdf_path: str):
11-
self.ik = common.RL(urdf_path=urdf_path)
12-
self._xarm = XArmAPI(ip=ip)
12+
13+
@dataclass(kw_only=True)
14+
class XArm7Config(common.RobotConfig):
15+
# some_custom_config: str = "default_value"
16+
payload_weight: float = 0.624
17+
payload_tcp: List[float] = field(default_factory=lambda: [-4.15, 5.24, 76.38])
18+
def __post_init__(self):
19+
super().__init__()
20+
21+
22+
class XArm7():
23+
def __init__(self, ip: str):
24+
self.ik = None #common.RL(urdf_path=urdf_path)
25+
self._config = XArm7Config()
26+
self._config.robot_platform = common.RobotPlatform.HARDWARE
27+
self._config.robot_type = common.RobotType.XArm7
28+
29+
self._xarm = XArmAPI(ip)
30+
self._xarm.set_mode(0)
31+
self._xarm.clean_error()
32+
self._xarm.clean_warn()
33+
self._xarm.motion_enable(enable=True)
34+
self._xarm.set_state(state=0)
35+
self._xarm.set_tcp_load(
36+
weight=self._config.payload_weight,
37+
center_of_gravity=self._config.payload_tcp,
38+
wait=True,
39+
)
1340

1441
# def get_base_pose_in_world_coordinates(self) -> Pose: ...
42+
1543
def get_cartesian_position(self) -> common.Pose:
16-
return self.ik.forward(self.get_joint_position())
44+
code, xyzrpy = self._xarm.get_position(is_radian=True)
45+
if code != 0:
46+
raise RuntimeError("couldn't get cartesian position from xarm")
47+
48+
translation = np.array(xyzrpy[:3], dtype=np.float64).reshape((3, 1)) * 0.001
49+
rpy = xyzrpy[3:]
50+
quat = R.from_euler('xyz', rpy).as_quat() # [x, y, z, w]
51+
quat = np.array(quat, dtype=np.float64).reshape((4, 1))
52+
53+
# return common.Pose(quaternion=quat, translation=translation)
54+
return common.Pose(rpy_vector=rpy, translation=translation)
1755

1856
def get_ik(self) -> common.IK | None:
1957
return self.ik
2058

2159
def get_joint_position(self) -> np.ndarray[tuple[typing.Literal[7]], np.dtype[np.float64]]: # type: ignore
22-
obs = self._hf_robot.get_servo_angle(is_radian=True)[1]
60+
obs = self._xarm.get_servo_angle(is_radian=True)[1]
2361
return np.array(obs, dtype=np.float64)
2462

25-
def get_parameters(self):
26-
a = common.RobotConfig()
27-
a.robot_platform = common.RobotPlatform.HARDWARE
28-
a.robot_type = common.RobotType.XArm7
29-
return a
63+
def get_parameters(self) -> XArm7Config:
64+
return self._config
65+
66+
def set_parameters(self, robot_cfg: XArm7Config) -> None:
67+
self._config = robot_cfg
3068

3169
def get_state(self) -> common.RobotState:
3270
return common.RobotState()
@@ -42,12 +80,19 @@ def reset(self) -> None:
4280
pass
4381

4482
def set_cartesian_position(self, pose: common.Pose) -> None:
45-
joints = self.ik.ik(pose, q0=self.get_joint_position())
46-
if joints is not None:
47-
self.set_joint_position(joints)
83+
x, y, z, roll, pitch, yaw = pose.xyzrpy()
84+
x_mm, y_mm, z_mm = 1000 * x, 1000 * y, 1000 * z
85+
self._xarm.set_position(x_mm, y_mm, z_mm, roll, pitch, yaw, is_radian=True, wait=True)
4886

4987
def set_joint_position(self, q: np.ndarray[tuple[typing.Literal[7]], np.dtype[np.float64]]) -> None: # type: ignore
50-
self._hf_robot.set_servo_angle(angle=q, is_radian=True, wait=True)
88+
self._xarm.set_servo_angle(angle=q, is_radian=True, wait=True)
89+
90+
def __enter__(self):
91+
return self
92+
93+
def __exit__(self, exc_type, exc_value, traceback):
94+
self._xarm.disconnect()
95+
5196

5297
# def to_pose_in_robot_coordinates(self, pose_in_world_coordinates: Pose) -> Pose: ...
5398
# def to_pose_in_world_coordinates(self, pose_in_robot_coordinates: Pose) -> Pose: ...
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from xarm.wrapper import XArmAPI
2+
import numpy as np
3+
import time
4+
xarm = XArmAPI(port="192.168.1.245")
5+
xarm.motion_enable(enable=True)
6+
xarm.clean_error()
7+
xarm.set_mode(0)
8+
xarm.set_state(state=0)
9+
# xarm.set_servo_angle(angle=np.zeros(7).tolist(), is_radian=True, wait=True)
10+
print(xarm.get_position())
11+
xarm.set_position(x=10, is_radian=True, relative=True, wait=True)
12+
print(xarm.get_position())

0 commit comments

Comments
 (0)