hex-zmq-servers 0.3.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hex_zmq_servers/__init__.py +173 -0
- hex_zmq_servers/cam/__init__.py +52 -0
- hex_zmq_servers/cam/berxel/__init__.py +17 -0
- hex_zmq_servers/cam/berxel/cam_berxel.py +282 -0
- hex_zmq_servers/cam/berxel/cam_berxel_cli.py +33 -0
- hex_zmq_servers/cam/berxel/cam_berxel_srv.py +79 -0
- hex_zmq_servers/cam/cam_base.py +189 -0
- hex_zmq_servers/cam/dummy/__init__.py +17 -0
- hex_zmq_servers/cam/dummy/cam_dummy.py +69 -0
- hex_zmq_servers/cam/dummy/cam_dummy_cli.py +29 -0
- hex_zmq_servers/cam/dummy/cam_dummy_srv.py +68 -0
- hex_zmq_servers/cam/realsense/__init__.py +17 -0
- hex_zmq_servers/cam/realsense/cam_realsense.py +159 -0
- hex_zmq_servers/cam/realsense/cam_realsense_cli.py +33 -0
- hex_zmq_servers/cam/realsense/cam_realsense_srv.py +78 -0
- hex_zmq_servers/cam/rgb/__init__.py +17 -0
- hex_zmq_servers/cam/rgb/cam_rgb.py +135 -0
- hex_zmq_servers/cam/rgb/cam_rgb_cli.py +43 -0
- hex_zmq_servers/cam/rgb/cam_rgb_srv.py +78 -0
- hex_zmq_servers/config/cam_berxel.json +18 -0
- hex_zmq_servers/config/cam_dummy.json +12 -0
- hex_zmq_servers/config/cam_realsense.json +17 -0
- hex_zmq_servers/config/cam_rgb.json +28 -0
- hex_zmq_servers/config/mujoco_archer_y6.json +37 -0
- hex_zmq_servers/config/mujoco_e3_desktop.json +41 -0
- hex_zmq_servers/config/robot_dummy.json +153 -0
- hex_zmq_servers/config/robot_gello.json +66 -0
- hex_zmq_servers/config/robot_hexarm.json +37 -0
- hex_zmq_servers/config/zmq_dummy.json +12 -0
- hex_zmq_servers/device_base.py +44 -0
- hex_zmq_servers/hex_launch.py +489 -0
- hex_zmq_servers/mujoco/__init__.py +28 -0
- hex_zmq_servers/mujoco/archer_y6/__init__.py +17 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/arm_base_link.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_1.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_2.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_3.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_4.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_5.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/assets.xml +17 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/camera_link.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_base_link.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_left_helper_link.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_left_link_1.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_left_link_2.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_right_helper_link.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_right_link_1.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_right_link_2.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/assets/table_link.STL +0 -0
- hex_zmq_servers/mujoco/archer_y6/model/robot.xml +95 -0
- hex_zmq_servers/mujoco/archer_y6/model/scene.xml +51 -0
- hex_zmq_servers/mujoco/archer_y6/model/setting.xml +37 -0
- hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6.py +325 -0
- hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_cli.py +71 -0
- hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_srv.py +148 -0
- hex_zmq_servers/mujoco/e3_desktop/__init__.py +17 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_base_link.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_1.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_2.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_3.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_4.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_5.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/assets.xml +18 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/camera_link.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/e3_desktop_base_link.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_base_link.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_left_helper_link.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_left_link_1.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_left_link_2.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_right_helper_link.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_right_link_1.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_right_link_2.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/assets/table_link.STL +0 -0
- hex_zmq_servers/mujoco/e3_desktop/model/robot.xml +188 -0
- hex_zmq_servers/mujoco/e3_desktop/model/scene.xml +53 -0
- hex_zmq_servers/mujoco/e3_desktop/model/setting.xml +72 -0
- hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop.py +449 -0
- hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_cli.py +289 -0
- hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_srv.py +244 -0
- hex_zmq_servers/mujoco/mujoco_base.py +425 -0
- hex_zmq_servers/robot/__init__.py +37 -0
- hex_zmq_servers/robot/dummy/__init__.py +17 -0
- hex_zmq_servers/robot/dummy/robot_dummy.py +94 -0
- hex_zmq_servers/robot/dummy/robot_dummy_cli.py +29 -0
- hex_zmq_servers/robot/dummy/robot_dummy_srv.py +82 -0
- hex_zmq_servers/robot/gello/__init__.py +17 -0
- hex_zmq_servers/robot/gello/robot_gello.py +366 -0
- hex_zmq_servers/robot/gello/robot_gello_cli.py +29 -0
- hex_zmq_servers/robot/gello/robot_gello_srv.py +93 -0
- hex_zmq_servers/robot/hexarm/__init__.py +47 -0
- hex_zmq_servers/robot/hexarm/robot_hexarm.py +292 -0
- hex_zmq_servers/robot/hexarm/robot_hexarm_cli.py +37 -0
- hex_zmq_servers/robot/hexarm/robot_hexarm_srv.py +87 -0
- hex_zmq_servers/robot/hexarm/urdf/archer_l6y/empty.urdf +206 -0
- hex_zmq_servers/robot/hexarm/urdf/archer_l6y/gp100.urdf +206 -0
- hex_zmq_servers/robot/hexarm/urdf/archer_l6y/gp100_handle.urdf +206 -0
- hex_zmq_servers/robot/hexarm/urdf/archer_l6y/gp100_p050.urdf +206 -0
- hex_zmq_servers/robot/hexarm/urdf/archer_l6y/gp100_p050_handle.urdf +206 -0
- hex_zmq_servers/robot/hexarm/urdf/archer_y6/empty.urdf +207 -0
- hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100.urdf +207 -0
- hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100_handle.urdf +207 -0
- hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100_p050.urdf +207 -0
- hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100_p050_handle.urdf +207 -0
- hex_zmq_servers/robot/robot_base.py +276 -0
- hex_zmq_servers/zmq_base.py +547 -0
- hex_zmq_servers-0.3.9.dist-info/METADATA +147 -0
- hex_zmq_servers-0.3.9.dist-info/RECORD +110 -0
- hex_zmq_servers-0.3.9.dist-info/WHEEL +5 -0
- hex_zmq_servers-0.3.9.dist-info/licenses/LICENSE +201 -0
- hex_zmq_servers-0.3.9.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding:utf-8 -*-
|
|
3
|
+
################################################################
|
|
4
|
+
# Copyright 2025 Dong Zhaorui. All rights reserved.
|
|
5
|
+
# Author: Dong Zhaorui 847235539@qq.com
|
|
6
|
+
# Date : 2025-09-12
|
|
7
|
+
################################################################
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
import time
|
|
12
|
+
import threading
|
|
13
|
+
import numpy as np
|
|
14
|
+
from collections import deque
|
|
15
|
+
|
|
16
|
+
from ..robot_base import HexRobotBase
|
|
17
|
+
from ...zmq_base import (
|
|
18
|
+
hex_zmq_ts_now,
|
|
19
|
+
hex_zmq_ts_delta_ms,
|
|
20
|
+
HexRate,
|
|
21
|
+
)
|
|
22
|
+
from ...hex_launch import hex_log, HEX_LOG_LEVEL
|
|
23
|
+
from dynamixel_sdk.group_sync_read import GroupSyncRead
|
|
24
|
+
from dynamixel_sdk.group_sync_write import GroupSyncWrite
|
|
25
|
+
from dynamixel_sdk.packet_handler import PacketHandler
|
|
26
|
+
from dynamixel_sdk.port_handler import PortHandler
|
|
27
|
+
from dynamixel_sdk.robotis_def import (
|
|
28
|
+
COMM_SUCCESS,
|
|
29
|
+
DXL_HIBYTE,
|
|
30
|
+
DXL_HIWORD,
|
|
31
|
+
DXL_LOBYTE,
|
|
32
|
+
DXL_LOWORD,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
DYNAMIXEL_TORQUE_ENABLE_ADDR = 64
|
|
36
|
+
DYNAMIXEL_TORQUE_ENABLE_VAL = 1
|
|
37
|
+
DYNAMIXEL_TORQUE_DISABLE_VAL = 0
|
|
38
|
+
DYNAMIXEL_GOAL_POSITION_ADDR = 116
|
|
39
|
+
DYNAMIXEL_GOAL_POSITION_LEN = 4
|
|
40
|
+
DYNAMIXEL_PRESENT_POSITION_ADDR = 132
|
|
41
|
+
DYNAMIXEL_PRESENT_POSITION_LEN = 4
|
|
42
|
+
|
|
43
|
+
ROBOT_CONFIG = {
|
|
44
|
+
"idxs": [0, 1, 2, 3, 4, 5, 6],
|
|
45
|
+
"invs": [1.0, -1.0, 1.0, 1.0, -1.0, -4.0],
|
|
46
|
+
"limits": [[[-2.7, 2.7], [-1.57, 2.09], [0, 3.14], [-1.57, 1.57],
|
|
47
|
+
[-1.57, 1.57], [-1.57, 1.57], [0.0, 1.0]]],
|
|
48
|
+
"device":
|
|
49
|
+
"/dev/ttyUSB0",
|
|
50
|
+
"baudrate":
|
|
51
|
+
115200,
|
|
52
|
+
"max_retries":
|
|
53
|
+
3,
|
|
54
|
+
"torque_enabled":
|
|
55
|
+
False,
|
|
56
|
+
"sens_ts":
|
|
57
|
+
True,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class HexRobotGello(HexRobotBase):
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
robot_config: dict = ROBOT_CONFIG,
|
|
66
|
+
realtime_mode: bool = False,
|
|
67
|
+
):
|
|
68
|
+
HexRobotBase.__init__(self, realtime_mode)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
self.__idxs = np.array(robot_config["idxs"])
|
|
72
|
+
self.__invs = np.array(robot_config["invs"])
|
|
73
|
+
self._limits = np.array(robot_config["limits"])
|
|
74
|
+
self.__device = robot_config["device"]
|
|
75
|
+
self.__baudrate = robot_config["baudrate"]
|
|
76
|
+
self.__max_retries = robot_config["max_retries"]
|
|
77
|
+
self.__torque_enabled = robot_config["torque_enabled"]
|
|
78
|
+
self.__sens_ts = robot_config["sens_ts"]
|
|
79
|
+
except KeyError as ke:
|
|
80
|
+
missing_key = ke.args[0]
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"robot_config is not valid, missing key: {missing_key}")
|
|
83
|
+
|
|
84
|
+
# variables
|
|
85
|
+
# gello variables
|
|
86
|
+
self.__servo_to_rad = np.pi / 2048
|
|
87
|
+
self.__port_handler = None
|
|
88
|
+
self.__packet_handler = None
|
|
89
|
+
self.__group_sync_read = None
|
|
90
|
+
self.__group_sync_write = None
|
|
91
|
+
self.__lock = threading.Lock()
|
|
92
|
+
# robot variables
|
|
93
|
+
self._dofs = [self.__idxs.shape[0]]
|
|
94
|
+
|
|
95
|
+
# open device
|
|
96
|
+
for attempt in range(self.__max_retries):
|
|
97
|
+
print(
|
|
98
|
+
f"Attempting to initialize Dynamixel driver (attempt {attempt + 1}/{self.__max_retries})"
|
|
99
|
+
)
|
|
100
|
+
if self.__try_open_device():
|
|
101
|
+
break
|
|
102
|
+
else:
|
|
103
|
+
time.sleep(1.0)
|
|
104
|
+
|
|
105
|
+
# start work loop
|
|
106
|
+
self._working.set()
|
|
107
|
+
|
|
108
|
+
def work_loop(self, hex_queues: list[deque | threading.Event]):
|
|
109
|
+
states_queue = hex_queues[0]
|
|
110
|
+
cmds_queue = hex_queues[1]
|
|
111
|
+
stop_event = hex_queues[2]
|
|
112
|
+
|
|
113
|
+
states_count = 0
|
|
114
|
+
last_cmds_seq = -1
|
|
115
|
+
rate = HexRate(1000)
|
|
116
|
+
while self._working.is_set() and not stop_event.is_set():
|
|
117
|
+
# states
|
|
118
|
+
ts, states = self.__get_states()
|
|
119
|
+
if states is not None:
|
|
120
|
+
states_queue.append((ts, states_count, states))
|
|
121
|
+
states_count = (states_count + 1) % self._max_seq_num
|
|
122
|
+
|
|
123
|
+
# cmds
|
|
124
|
+
cmds_pack = None
|
|
125
|
+
try:
|
|
126
|
+
cmds_pack = cmds_queue[
|
|
127
|
+
-1] if self._realtime_mode else cmds_queue.popleft()
|
|
128
|
+
except IndexError:
|
|
129
|
+
pass
|
|
130
|
+
if cmds_pack is not None:
|
|
131
|
+
ts, seq, cmds = cmds_pack
|
|
132
|
+
if seq != last_cmds_seq:
|
|
133
|
+
last_cmds_seq = seq
|
|
134
|
+
if hex_zmq_ts_delta_ms(hex_zmq_ts_now(), ts) < 200.0:
|
|
135
|
+
self.__set_cmds(cmds)
|
|
136
|
+
|
|
137
|
+
# sleep
|
|
138
|
+
rate.sleep()
|
|
139
|
+
|
|
140
|
+
# close
|
|
141
|
+
self.close()
|
|
142
|
+
|
|
143
|
+
def __get_states(self):
|
|
144
|
+
with self.__lock:
|
|
145
|
+
ts = hex_zmq_ts_now()
|
|
146
|
+
servo_values = np.zeros(self._dofs, dtype=int)
|
|
147
|
+
dxl_comm_result = self.__group_sync_read.txRxPacket()
|
|
148
|
+
if dxl_comm_result != COMM_SUCCESS:
|
|
149
|
+
print(f"warning, comm failed: {dxl_comm_result}")
|
|
150
|
+
return None, None
|
|
151
|
+
for i, dxl_id in enumerate(self.__idxs):
|
|
152
|
+
if self.__group_sync_read.isAvailable(
|
|
153
|
+
dxl_id, DYNAMIXEL_PRESENT_POSITION_ADDR,
|
|
154
|
+
DYNAMIXEL_PRESENT_POSITION_LEN):
|
|
155
|
+
servo_value = self.__group_sync_read.getData(
|
|
156
|
+
dxl_id, DYNAMIXEL_PRESENT_POSITION_ADDR,
|
|
157
|
+
DYNAMIXEL_PRESENT_POSITION_LEN)
|
|
158
|
+
servo_value = np.int32(np.uint32(servo_value))
|
|
159
|
+
servo_values[i] = servo_value
|
|
160
|
+
else:
|
|
161
|
+
raise RuntimeError(
|
|
162
|
+
f"Failed to get joint angles for Dynamixel with ID {dxl_id}"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
rads = self._apply_pos_limits(
|
|
166
|
+
servo_values * self.__servo_to_rad * self.__invs,
|
|
167
|
+
self._limits[:, 0],
|
|
168
|
+
self._limits[:, 1],
|
|
169
|
+
)
|
|
170
|
+
return ts if self.__sens_ts else hex_zmq_ts_now(), rads
|
|
171
|
+
|
|
172
|
+
def __set_cmds(self, cmds: np.ndarray):
|
|
173
|
+
if len(cmds) != len(self.__idxs):
|
|
174
|
+
print(
|
|
175
|
+
"\033[91mThe length of joint_angles must match the number of servos\033[0m"
|
|
176
|
+
)
|
|
177
|
+
return False
|
|
178
|
+
if not self.__torque_enabled:
|
|
179
|
+
print("\033[91mTorque must be enabled to set joint angles\033[0m")
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
for dxl_id, cmd_rad in zip(self.__idxs, cmds):
|
|
183
|
+
# Convert the angle to the appropriate value for the servo
|
|
184
|
+
position_value = int(cmd_rad / self.__servo_to_rad)
|
|
185
|
+
|
|
186
|
+
# Allocate goal position value into byte array
|
|
187
|
+
param_goal_position = [
|
|
188
|
+
DXL_LOBYTE(DXL_LOWORD(position_value)),
|
|
189
|
+
DXL_HIBYTE(DXL_LOWORD(position_value)),
|
|
190
|
+
DXL_LOBYTE(DXL_HIWORD(position_value)),
|
|
191
|
+
DXL_HIBYTE(DXL_HIWORD(position_value)),
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
# Add goal position value to the Syncwrite parameter storage
|
|
195
|
+
dxl_addparam_result = self.__group_sync_write.addParam(
|
|
196
|
+
dxl_id, param_goal_position)
|
|
197
|
+
if not dxl_addparam_result:
|
|
198
|
+
print(
|
|
199
|
+
f"\033[91mFailed to set joint angle for Dynamixel with ID {dxl_id}\033[0m"
|
|
200
|
+
)
|
|
201
|
+
return False
|
|
202
|
+
|
|
203
|
+
# Syncwrite goal position
|
|
204
|
+
dxl_comm_result = self.__group_sync_write.txPacket()
|
|
205
|
+
if dxl_comm_result != COMM_SUCCESS:
|
|
206
|
+
print(f"\033[91mFailed to syncwrite goal position\033[0m")
|
|
207
|
+
return False
|
|
208
|
+
|
|
209
|
+
# Clear syncwrite parameter storage
|
|
210
|
+
self.__group_sync_write.clearParam()
|
|
211
|
+
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
def __try_open_device(self):
|
|
215
|
+
if not self.__check_device_availability():
|
|
216
|
+
print("Port is busy, attempting to free it...")
|
|
217
|
+
if not self.__kill_processes_using_device():
|
|
218
|
+
print("Failed to free device, trying to fix permissions...")
|
|
219
|
+
self.__fix_device_permissions()
|
|
220
|
+
time.sleep(2)
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
self.__initialize_hardware()
|
|
224
|
+
print(
|
|
225
|
+
f"Successfully initialized Dynamixel driver on {self.__device}"
|
|
226
|
+
)
|
|
227
|
+
return True
|
|
228
|
+
except Exception as e:
|
|
229
|
+
print(f"Failed to initialize Dynamixel driver: {e}")
|
|
230
|
+
if "Permission denied" in str(e):
|
|
231
|
+
print(
|
|
232
|
+
"Please add permission to the device. For more details, please refer to the \033[0;31mhttps://docs.hexfellow.com\033[0m"
|
|
233
|
+
)
|
|
234
|
+
return False
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
def __check_device_availability(self):
|
|
238
|
+
try:
|
|
239
|
+
# Check if port exists
|
|
240
|
+
if not os.path.exists(self.__device):
|
|
241
|
+
print(f"Device {self.__device} does not exist")
|
|
242
|
+
return False
|
|
243
|
+
|
|
244
|
+
# Check for processes using the port
|
|
245
|
+
result = subprocess.run(["lsof", self.__device],
|
|
246
|
+
capture_output=True,
|
|
247
|
+
text=True)
|
|
248
|
+
|
|
249
|
+
if result.returncode == 0:
|
|
250
|
+
lines = result.stdout.strip().split("\n")
|
|
251
|
+
if len(lines) > 1: # Header + processes
|
|
252
|
+
print(
|
|
253
|
+
f"Device {self.__device} is being used by other processes:"
|
|
254
|
+
)
|
|
255
|
+
for line in lines[1:]:
|
|
256
|
+
print(f" {line}")
|
|
257
|
+
return False
|
|
258
|
+
return True
|
|
259
|
+
except Exception as e:
|
|
260
|
+
print(f"Error checking device availability: {e}")
|
|
261
|
+
return False
|
|
262
|
+
|
|
263
|
+
def __kill_processes_using_device(self):
|
|
264
|
+
try:
|
|
265
|
+
result = subprocess.run(
|
|
266
|
+
["fuser", "-k", self.__device],
|
|
267
|
+
capture_output=True,
|
|
268
|
+
text=True,
|
|
269
|
+
)
|
|
270
|
+
if result.returncode == 0:
|
|
271
|
+
print(f"Killed processes using {self.__device}")
|
|
272
|
+
time.sleep(1)
|
|
273
|
+
return True
|
|
274
|
+
return False
|
|
275
|
+
except Exception as e:
|
|
276
|
+
print(f"Error killing processes: {e}")
|
|
277
|
+
return False
|
|
278
|
+
|
|
279
|
+
def __fix_device_permissions(self):
|
|
280
|
+
try:
|
|
281
|
+
result = subprocess.run(["sudo", "chmod", "666", self.__device],
|
|
282
|
+
capture_output=True,
|
|
283
|
+
text=True)
|
|
284
|
+
if result.returncode == 0:
|
|
285
|
+
print(f"Fixed permissions for {self.__device}")
|
|
286
|
+
return True
|
|
287
|
+
return False
|
|
288
|
+
except Exception as e:
|
|
289
|
+
print(f"Error fixing device permissions: {e}")
|
|
290
|
+
return False
|
|
291
|
+
|
|
292
|
+
def __initialize_hardware(self):
|
|
293
|
+
# Check and prepare port before connection
|
|
294
|
+
self.__prepare_device()
|
|
295
|
+
|
|
296
|
+
# Initialize the port handler, packet handler, and group sync read/write
|
|
297
|
+
self.__port_handler = PortHandler(self.__device)
|
|
298
|
+
self.__packet_handler = PacketHandler(2.0)
|
|
299
|
+
self.__group_sync_read = GroupSyncRead(
|
|
300
|
+
self.__port_handler,
|
|
301
|
+
self.__packet_handler,
|
|
302
|
+
DYNAMIXEL_PRESENT_POSITION_ADDR,
|
|
303
|
+
DYNAMIXEL_PRESENT_POSITION_LEN,
|
|
304
|
+
)
|
|
305
|
+
self.__group_sync_write = GroupSyncWrite(
|
|
306
|
+
self.__port_handler,
|
|
307
|
+
self.__packet_handler,
|
|
308
|
+
DYNAMIXEL_GOAL_POSITION_ADDR,
|
|
309
|
+
DYNAMIXEL_GOAL_POSITION_LEN,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Open the port and set the baudrate
|
|
313
|
+
if not self.__port_handler.openPort():
|
|
314
|
+
raise RuntimeError("Failed to open the port")
|
|
315
|
+
|
|
316
|
+
if not self.__port_handler.setBaudRate(self.__baudrate):
|
|
317
|
+
raise RuntimeError(
|
|
318
|
+
f"Failed to change the baudrate, {self.__baudrate}")
|
|
319
|
+
|
|
320
|
+
# Add parameters for each Dynamixel servo to the group sync read
|
|
321
|
+
for dxl_id in self.__idxs:
|
|
322
|
+
if not self.__group_sync_read.addParam(dxl_id):
|
|
323
|
+
raise RuntimeError(
|
|
324
|
+
f"Failed to add parameter for Dynamixel with ID {dxl_id}")
|
|
325
|
+
|
|
326
|
+
# Disable torque for each Dynamixel servo
|
|
327
|
+
if self.__torque_enabled:
|
|
328
|
+
try:
|
|
329
|
+
self.__hardware_set_torque_mode(self.__torque_enabled)
|
|
330
|
+
except Exception as e:
|
|
331
|
+
print(f"device: {self.__device}, {e}")
|
|
332
|
+
|
|
333
|
+
def __prepare_device(self):
|
|
334
|
+
if not self.__check_device_availability():
|
|
335
|
+
print(
|
|
336
|
+
f"Device {self.__device} is not available, attempting to fix..."
|
|
337
|
+
)
|
|
338
|
+
self.__kill_processes_using_device()
|
|
339
|
+
self.__fix_device_permissions()
|
|
340
|
+
|
|
341
|
+
# Check again after fixing
|
|
342
|
+
if not self.__check_device_availability():
|
|
343
|
+
print(f"Warning: Device {self.__device} may still have issues")
|
|
344
|
+
|
|
345
|
+
def __hardware_set_torque_mode(self, enable: bool):
|
|
346
|
+
torque_value = DYNAMIXEL_TORQUE_ENABLE_VAL if enable else DYNAMIXEL_TORQUE_DISABLE_VAL
|
|
347
|
+
with self.__lock:
|
|
348
|
+
for dxl_id in self.__idxs:
|
|
349
|
+
dxl_comm_result, dxl_error = self.__packet_handler.write1ByteTxRx(
|
|
350
|
+
self.__port_handler, dxl_id, DYNAMIXEL_TORQUE_ENABLE_ADDR,
|
|
351
|
+
torque_value)
|
|
352
|
+
if dxl_comm_result != COMM_SUCCESS or dxl_error != 0:
|
|
353
|
+
print(dxl_comm_result)
|
|
354
|
+
print(dxl_error)
|
|
355
|
+
raise RuntimeError(
|
|
356
|
+
f"Failed to set torque mode for Dynamixel with ID {dxl_id}"
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
self.__torque_enabled = enable
|
|
360
|
+
|
|
361
|
+
def close(self):
|
|
362
|
+
if not self._working.is_set():
|
|
363
|
+
return
|
|
364
|
+
self._working.clear()
|
|
365
|
+
self.__port_handler.closePort()
|
|
366
|
+
hex_log(HEX_LOG_LEVEL["info"], "HexRobotGello closed")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding:utf-8 -*-
|
|
3
|
+
################################################################
|
|
4
|
+
# Copyright 2025 Dong Zhaorui. All rights reserved.
|
|
5
|
+
# Author: Dong Zhaorui 847235539@qq.com
|
|
6
|
+
# Date : 2025-09-14
|
|
7
|
+
################################################################
|
|
8
|
+
|
|
9
|
+
from ..robot_base import HexRobotClientBase
|
|
10
|
+
|
|
11
|
+
NET_CONFIG = {
|
|
12
|
+
"ip": "127.0.0.1",
|
|
13
|
+
"port": 12345,
|
|
14
|
+
"realtime_mode": False,
|
|
15
|
+
"deque_maxlen": 10,
|
|
16
|
+
"client_timeout_ms": 200,
|
|
17
|
+
"server_timeout_ms": 1_000,
|
|
18
|
+
"server_num_workers": 4,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class HexRobotGelloClient(HexRobotClientBase):
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
net_config: dict = NET_CONFIG,
|
|
27
|
+
):
|
|
28
|
+
HexRobotClientBase.__init__(self, net_config)
|
|
29
|
+
self._wait_for_working()
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding:utf-8 -*-
|
|
3
|
+
################################################################
|
|
4
|
+
# Copyright 2025 Dong Zhaorui. All rights reserved.
|
|
5
|
+
# Author: Dong Zhaorui 847235539@qq.com
|
|
6
|
+
# Date : 2025-09-14
|
|
7
|
+
################################################################
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from ..robot_base import HexRobotServerBase
|
|
13
|
+
from .robot_gello import HexRobotGello
|
|
14
|
+
except (ImportError, ValueError):
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
this_file = Path(__file__).resolve()
|
|
18
|
+
project_root = this_file.parents[3]
|
|
19
|
+
if str(project_root) not in sys.path:
|
|
20
|
+
sys.path.insert(0, str(project_root))
|
|
21
|
+
from hex_zmq_servers.robot.robot_base import HexRobotServerBase
|
|
22
|
+
from hex_zmq_servers.robot.gello.robot_gello import HexRobotGello
|
|
23
|
+
|
|
24
|
+
NET_CONFIG = {
|
|
25
|
+
"ip": "127.0.0.1",
|
|
26
|
+
"port": 12345,
|
|
27
|
+
"realtime_mode": False,
|
|
28
|
+
"deque_maxlen": 10,
|
|
29
|
+
"client_timeout_ms": 200,
|
|
30
|
+
"server_timeout_ms": 1_000,
|
|
31
|
+
"server_num_workers": 4,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
ROBOT_CONFIG = {
|
|
35
|
+
"idxs": [0, 1, 2, 3, 4, 5, 6],
|
|
36
|
+
"invs": [1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -4.0],
|
|
37
|
+
"limits": [[[-2.7, 2.7], [-1.57, 2.09], [0, 3.14], [-1.57, 1.57],
|
|
38
|
+
[-1.57, 1.57], [-1.57, 1.57], [0.0, 1.0]]],
|
|
39
|
+
"device":
|
|
40
|
+
"/dev/ttyUSB0",
|
|
41
|
+
"baudrate":
|
|
42
|
+
115200,
|
|
43
|
+
"max_retries":
|
|
44
|
+
3,
|
|
45
|
+
"torque_enabled":
|
|
46
|
+
False,
|
|
47
|
+
"sens_ts":
|
|
48
|
+
True,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class HexRobotGelloServer(HexRobotServerBase):
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
net_config: dict = NET_CONFIG,
|
|
57
|
+
params_config: dict = ROBOT_CONFIG,
|
|
58
|
+
):
|
|
59
|
+
HexRobotServerBase.__init__(self, net_config)
|
|
60
|
+
|
|
61
|
+
# robot
|
|
62
|
+
self._device = HexRobotGello(params_config,
|
|
63
|
+
net_config.get("realtime_mode", False))
|
|
64
|
+
|
|
65
|
+
def _process_request(self, recv_hdr: dict, recv_buf: np.ndarray):
|
|
66
|
+
if recv_hdr["cmd"] == "is_working":
|
|
67
|
+
return self.no_ts_hdr(recv_hdr, self._device.is_working()), None
|
|
68
|
+
elif recv_hdr["cmd"] == "seq_clear":
|
|
69
|
+
return self.no_ts_hdr(recv_hdr, self._seq_clear()), None
|
|
70
|
+
elif recv_hdr["cmd"] == "get_dofs":
|
|
71
|
+
dofs = self._device.get_dofs()
|
|
72
|
+
return self.no_ts_hdr(recv_hdr, dofs is not None), dofs
|
|
73
|
+
elif recv_hdr["cmd"] == "get_limits":
|
|
74
|
+
limits = self._device.get_limits()
|
|
75
|
+
return self.no_ts_hdr(recv_hdr, limits is not None), limits
|
|
76
|
+
elif recv_hdr["cmd"] == "get_states":
|
|
77
|
+
return self._get_states(recv_hdr)
|
|
78
|
+
elif recv_hdr["cmd"] == "set_cmds":
|
|
79
|
+
return self._set_cmds(recv_hdr, recv_buf)
|
|
80
|
+
else:
|
|
81
|
+
raise ValueError(f"unknown command: {recv_hdr['cmd']}")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
import argparse, json
|
|
86
|
+
from hex_zmq_servers.zmq_base import hex_server_helper
|
|
87
|
+
|
|
88
|
+
parser = argparse.ArgumentParser()
|
|
89
|
+
parser.add_argument("--cfg", type=str, required=True)
|
|
90
|
+
args = parser.parse_args()
|
|
91
|
+
cfg = json.loads(args.cfg)
|
|
92
|
+
|
|
93
|
+
hex_server_helper(cfg, HexRobotGelloServer)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding:utf-8 -*-
|
|
3
|
+
################################################################
|
|
4
|
+
# Copyright 2025 Dong Zhaorui. All rights reserved.
|
|
5
|
+
# Author: Dong Zhaorui 847235539@qq.com
|
|
6
|
+
# Date : 2025-09-12
|
|
7
|
+
################################################################
|
|
8
|
+
|
|
9
|
+
from .robot_hexarm import HexRobotHexarm
|
|
10
|
+
from .robot_hexarm_cli import HexRobotHexarmClient
|
|
11
|
+
from .robot_hexarm_srv import HexRobotHexarmServer
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
|
|
15
|
+
urdf_dir = os.path.join(os.path.dirname(__file__), "urdf")
|
|
16
|
+
HEXARM_URDF_PATH_DICT = {
|
|
17
|
+
"archer_y6_empty":
|
|
18
|
+
f"{urdf_dir}/archer_y6/empty.urdf",
|
|
19
|
+
"archer_y6_gp100":
|
|
20
|
+
f"{urdf_dir}/archer_y6/gp100.urdf",
|
|
21
|
+
"archer_y6_gp100_handle":
|
|
22
|
+
f"{urdf_dir}/archer_y6/gp100_handle.urdf",
|
|
23
|
+
"archer_y6_gp100_p050":
|
|
24
|
+
f"{urdf_dir}/archer_y6/gp100_p050.urdf",
|
|
25
|
+
"archer_y6_gp100_p050_handle":
|
|
26
|
+
f"{urdf_dir}/archer_y6/gp100_p050_handle.urdf",
|
|
27
|
+
"archer_l6y_empty":
|
|
28
|
+
f"{urdf_dir}/archer_l6y/empty.urdf",
|
|
29
|
+
"archer_l6y_gp100":
|
|
30
|
+
f"{urdf_dir}/archer_l6y/gp100.urdf",
|
|
31
|
+
"archer_l6y_gp100_handle":
|
|
32
|
+
f"{urdf_dir}/archer_l6y/gp100_handle.urdf",
|
|
33
|
+
"archer_l6y_gp100_p050":
|
|
34
|
+
f"{urdf_dir}/archer_l6y/gp100_p050.urdf",
|
|
35
|
+
"archer_l6y_gp100_p050_handle":
|
|
36
|
+
f"{urdf_dir}/archer_l6y/gp100_p050_handle.urdf",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
# path
|
|
41
|
+
"HEXARM_URDF_PATH_DICT",
|
|
42
|
+
|
|
43
|
+
# robot
|
|
44
|
+
"HexRobotHexarm",
|
|
45
|
+
"HexRobotHexarmClient",
|
|
46
|
+
"HexRobotHexarmServer",
|
|
47
|
+
]
|