hex-zmq-servers 0.3.3__py3-none-any.whl → 0.3.4__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.
Files changed (37) hide show
  1. hex_zmq_servers/__init__.py +5 -3
  2. hex_zmq_servers/cam/berxel/cam_berxel.py +8 -10
  3. hex_zmq_servers/cam/berxel/cam_berxel_cli.py +1 -0
  4. hex_zmq_servers/cam/cam_base.py +40 -10
  5. hex_zmq_servers/cam/dummy/cam_dummy.py +7 -7
  6. hex_zmq_servers/cam/dummy/cam_dummy_cli.py +1 -0
  7. hex_zmq_servers/cam/realsense/cam_realsense.py +9 -9
  8. hex_zmq_servers/cam/realsense/cam_realsense_cli.py +1 -0
  9. hex_zmq_servers/cam/rgb/cam_rgb.py +33 -7
  10. hex_zmq_servers/cam/rgb/cam_rgb_cli.py +11 -0
  11. hex_zmq_servers/config/cam_rgb.json +8 -0
  12. hex_zmq_servers/config/mujoco_archer_y6.json +1 -1
  13. hex_zmq_servers/config/mujoco_e3_desktop.json +1 -1
  14. hex_zmq_servers/config/robot_hexarm.json +1 -1
  15. hex_zmq_servers/device_base.py +3 -2
  16. hex_zmq_servers/mujoco/archer_y6/model/scene.xml +1 -1
  17. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6.py +28 -22
  18. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_cli.py +42 -0
  19. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_srv.py +16 -14
  20. hex_zmq_servers/mujoco/e3_desktop/model/scene.xml +1 -1
  21. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop.py +50 -40
  22. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_cli.py +148 -33
  23. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_srv.py +39 -35
  24. hex_zmq_servers/mujoco/mujoco_base.py +101 -64
  25. hex_zmq_servers/robot/dummy/robot_dummy.py +11 -7
  26. hex_zmq_servers/robot/dummy/robot_dummy_cli.py +1 -0
  27. hex_zmq_servers/robot/gello/robot_gello.py +11 -7
  28. hex_zmq_servers/robot/gello/robot_gello_cli.py +1 -0
  29. hex_zmq_servers/robot/hexarm/robot_hexarm.py +56 -22
  30. hex_zmq_servers/robot/hexarm/robot_hexarm_cli.py +1 -0
  31. hex_zmq_servers/robot/robot_base.py +40 -10
  32. hex_zmq_servers/zmq_base.py +92 -31
  33. {hex_zmq_servers-0.3.3.dist-info → hex_zmq_servers-0.3.4.dist-info}/METADATA +1 -1
  34. {hex_zmq_servers-0.3.3.dist-info → hex_zmq_servers-0.3.4.dist-info}/RECORD +37 -37
  35. {hex_zmq_servers-0.3.3.dist-info → hex_zmq_servers-0.3.4.dist-info}/WHEEL +0 -0
  36. {hex_zmq_servers-0.3.3.dist-info → hex_zmq_servers-0.3.4.dist-info}/licenses/LICENSE +0 -0
  37. {hex_zmq_servers-0.3.3.dist-info → hex_zmq_servers-0.3.4.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@
7
7
  ################################################################
8
8
 
9
9
  import numpy as np
10
- from hex_zmq_servers.zmq_base import HexSafeValue
10
+ from collections import deque
11
11
 
12
12
  try:
13
13
  from ..mujoco_base import HexMujocoServerBase
@@ -31,7 +31,7 @@ NET_CONFIG = {
31
31
  }
32
32
 
33
33
  MUJOCO_CONFIG = {
34
- "states_rate": 500,
34
+ "states_rate": 1000,
35
35
  "img_rate": 30,
36
36
  "headless": False,
37
37
  "sens_ts": True,
@@ -53,32 +53,32 @@ class HexMujocoE3DesktopServer(HexMujocoServerBase):
53
53
  # values
54
54
  self._cmds_left_seq = -1
55
55
  self._cmds_right_seq = -1
56
- self._states_left_value = HexSafeValue()
57
- self._states_right_value = HexSafeValue()
58
- self._states_obj_value = HexSafeValue()
59
- self._cmds_left_value = HexSafeValue()
60
- self._cmds_right_value = HexSafeValue()
61
- self._rgb_head_value = HexSafeValue()
62
- self._depth_head_value = HexSafeValue()
63
- self._rgb_left_value = HexSafeValue()
64
- self._depth_left_value = HexSafeValue()
65
- self._rgb_right_value = HexSafeValue()
66
- self._depth_right_value = HexSafeValue()
56
+ self._states_left_queue = deque(maxlen=10)
57
+ self._states_right_queue = deque(maxlen=10)
58
+ self._states_obj_queue = deque(maxlen=10)
59
+ self._cmds_left_queue = deque(maxlen=10)
60
+ self._cmds_right_queue = deque(maxlen=10)
61
+ self._rgb_head_queue = deque(maxlen=10)
62
+ self._depth_head_queue = deque(maxlen=10)
63
+ self._rgb_left_queue = deque(maxlen=10)
64
+ self._depth_left_queue = deque(maxlen=10)
65
+ self._rgb_right_queue = deque(maxlen=10)
66
+ self._depth_right_queue = deque(maxlen=10)
67
67
 
68
68
  def work_loop(self):
69
69
  try:
70
70
  self._device.work_loop([
71
- self._states_left_value,
72
- self._states_right_value,
73
- self._states_obj_value,
74
- self._cmds_left_value,
75
- self._cmds_right_value,
76
- self._rgb_head_value,
77
- self._depth_head_value,
78
- self._rgb_left_value,
79
- self._depth_left_value,
80
- self._rgb_right_value,
81
- self._depth_right_value,
71
+ self._states_left_queue,
72
+ self._states_right_queue,
73
+ self._states_obj_queue,
74
+ self._cmds_left_queue,
75
+ self._cmds_right_queue,
76
+ self._rgb_head_queue,
77
+ self._depth_head_queue,
78
+ self._rgb_left_queue,
79
+ self._depth_left_queue,
80
+ self._rgb_right_queue,
81
+ self._depth_right_queue,
82
82
  self._stop_event,
83
83
  ])
84
84
  finally:
@@ -94,16 +94,18 @@ class HexMujocoE3DesktopServer(HexMujocoServerBase):
94
94
  # get robot name
95
95
  robot_name = recv_hdr["cmd"].split("_")[2]
96
96
  if robot_name == "left":
97
- value = self._states_left_value
97
+ queue = self._states_left_queue
98
98
  elif robot_name == "right":
99
- value = self._states_right_value
99
+ queue = self._states_right_queue
100
100
  elif robot_name == "obj":
101
- value = self._states_obj_value
101
+ queue = self._states_obj_queue
102
102
  else:
103
103
  raise ValueError(f"unknown robot name: {robot_name}")
104
104
 
105
105
  try:
106
- ts, count, states = value.get()
106
+ ts, count, states = queue.popleft()
107
+ except IndexError:
108
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
107
109
  except Exception as e:
108
110
  print(f"\033[91m{recv_hdr['cmd']} failed: {e}\033[0m")
109
111
  return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
@@ -129,10 +131,10 @@ class HexMujocoE3DesktopServer(HexMujocoServerBase):
129
131
  # get robot name
130
132
  robot_name = recv_hdr["cmd"].split("_")[2]
131
133
  if robot_name == "left":
132
- value = self._cmds_left_value
134
+ queue = self._cmds_left_queue
133
135
  cmds_seq = self._cmds_left_seq
134
136
  elif robot_name == "right":
135
- value = self._cmds_right_value
137
+ queue = self._cmds_right_queue
136
138
  cmds_seq = self._cmds_right_seq
137
139
  else:
138
140
  raise ValueError(f"unknown robot name: {robot_name}")
@@ -144,7 +146,7 @@ class HexMujocoE3DesktopServer(HexMujocoServerBase):
144
146
  self._cmds_left_seq = cmds_seq
145
147
  elif robot_name == "right":
146
148
  self._cmds_right_seq = cmds_seq
147
- value.set((recv_hdr["ts"], seq, recv_buf))
149
+ queue.append((recv_hdr["ts"], seq, recv_buf))
148
150
  return self.no_ts_hdr(recv_hdr, True), None
149
151
  else:
150
152
  return self.no_ts_hdr(recv_hdr, False), None
@@ -163,16 +165,18 @@ class HexMujocoE3DesktopServer(HexMujocoServerBase):
163
165
  depth_flag = split_cmd[1] == "depth"
164
166
  camera_name = split_cmd[2]
165
167
  if camera_name == "head":
166
- value = self._rgb_head_value if not depth_flag else self._depth_head_value
168
+ queue = self._rgb_head_queue if not depth_flag else self._depth_head_queue
167
169
  elif camera_name == "left":
168
- value = self._rgb_left_value if not depth_flag else self._depth_left_value
170
+ queue = self._rgb_left_queue if not depth_flag else self._depth_left_queue
169
171
  elif camera_name == "right":
170
- value = self._rgb_right_value if not depth_flag else self._depth_right_value
172
+ queue = self._rgb_right_queue if not depth_flag else self._depth_right_queue
171
173
  else:
172
174
  raise ValueError(f"unknown camera name: {camera_name}")
173
175
 
174
176
  try:
175
- ts, count, img = value.get()
177
+ ts, count, img = queue.popleft()
178
+ except IndexError:
179
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
176
180
  except Exception as e:
177
181
  print(f"\033[91m{recv_hdr['cmd']} failed: {e}\033[0m")
178
182
  return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
@@ -8,12 +8,12 @@
8
8
 
9
9
  import threading
10
10
  import numpy as np
11
+ from collections import deque
11
12
  from abc import abstractmethod
12
13
 
13
14
  from ..device_base import HexDeviceBase
14
15
  from ..zmq_base import (
15
16
  hex_zmq_ts_now,
16
- HexSafeValue,
17
17
  HexZMQClientBase,
18
18
  HexZMQServerBase,
19
19
  )
@@ -88,7 +88,7 @@ class HexMujocoBase(HexDeviceBase):
88
88
  return normed_rads
89
89
 
90
90
  @abstractmethod
91
- def work_loop(self, hex_values: list[HexSafeValue | threading.Event]):
91
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
92
92
  raise NotImplementedError(
93
93
  "`work_loop` should be implemented by the child class")
94
94
 
@@ -107,6 +107,13 @@ class HexMujocoClientBase(HexZMQClientBase):
107
107
  self._cmds_seq = 0
108
108
  self._rgb_seq = 0
109
109
  self._depth_seq = 0
110
+ self._states_queue = {
111
+ "robot": deque(maxlen=10),
112
+ "obj": deque(maxlen=10),
113
+ }
114
+ self._rgb_queue = deque(maxlen=10)
115
+ self._depth_queue = deque(maxlen=10)
116
+ self._cmds_queue = deque(maxlen=10)
110
117
 
111
118
  def __del__(self):
112
119
  HexZMQClientBase.__del__(self)
@@ -133,7 +140,77 @@ class HexMujocoClientBase(HexZMQClientBase):
133
140
  _, limits = self.request({"cmd": "get_limits"})
134
141
  return limits
135
142
 
136
- def get_states(self, robot_name: str | None = None):
143
+ def get_states(self, robot_name: str | None = None, newest: bool = False):
144
+ try:
145
+ return self._states_queue[robot_name].popleft(
146
+ ) if not newest else self._states_queue[robot_name][-1]
147
+ except IndexError:
148
+ return None, None
149
+ except KeyError:
150
+ print(f"\033[91munknown robot name: {robot_name}\033[0m")
151
+ return None, None
152
+
153
+ def get_rgb(self, camera_name: str | None = None, newest: bool = False):
154
+ try:
155
+ return self._rgb_queue.popleft(
156
+ ) if not newest else self._rgb_queue[-1]
157
+ except IndexError:
158
+ return None, None
159
+
160
+ def get_depth(self, camera_name: str | None = None, newest: bool = False):
161
+ try:
162
+ return self._depth_queue.popleft(
163
+ ) if not newest else self._depth_queue[-1]
164
+ except IndexError:
165
+ return None, None
166
+
167
+ def set_cmds(self, cmds: np.ndarray):
168
+ self._cmds_queue.append(cmds)
169
+
170
+ def get_intri(self):
171
+ intri_hdr, intri = self.request({"cmd": "get_intri"})
172
+ return intri_hdr, intri
173
+
174
+ def _get_rgb_inner(self, camera_name: str | None = None):
175
+ return self._process_frame(camera_name, False)
176
+
177
+ def _get_depth_inner(self, camera_name: str | None = None):
178
+ return self._process_frame(camera_name, True)
179
+
180
+ def _process_frame(
181
+ self,
182
+ camera_name: str | None = None,
183
+ depth_flag: bool = False,
184
+ ):
185
+ req_cmd = f"get_{'depth' if depth_flag else 'rgb'}"
186
+ if camera_name is not None:
187
+ req_cmd += f"_{camera_name}"
188
+
189
+ hdr, img = self.request({
190
+ "cmd":
191
+ req_cmd,
192
+ "args": (1 + (self._depth_seq if depth_flag else self._rgb_seq)) %
193
+ self._max_seq_num,
194
+ })
195
+
196
+ try:
197
+ cmd = hdr["cmd"]
198
+ if cmd == f"{req_cmd}_ok":
199
+ if depth_flag:
200
+ self._depth_seq = hdr["args"]
201
+ else:
202
+ self._rgb_seq = hdr["args"]
203
+ return hdr, img
204
+ else:
205
+ return None, None
206
+ except KeyError:
207
+ print(f"\033[91m{hdr['cmd']} requires `cmd`\033[0m")
208
+ return None, None
209
+ except Exception as e:
210
+ print(f"\033[91m__process_frame failed: {e}\033[0m")
211
+ return None, None
212
+
213
+ def _get_states_inner(self, robot_name: str | None = None):
137
214
  req_cmd = "get_states"
138
215
  if robot_name is not None:
139
216
  req_cmd += f"_{robot_name}"
@@ -156,7 +233,7 @@ class HexMujocoClientBase(HexZMQClientBase):
156
233
  print(f"\033[91m{req_cmd} failed: {e}\033[0m")
157
234
  return None, None
158
235
 
159
- def set_cmds(
236
+ def _set_cmds_inner(
160
237
  self,
161
238
  cmds: np.ndarray,
162
239
  ) -> bool:
@@ -184,71 +261,28 @@ class HexMujocoClientBase(HexZMQClientBase):
184
261
  print(f"\033[91m{req_cmd} failed: {e}\033[0m")
185
262
  return False
186
263
 
187
- def get_intri(self):
188
- intri_hdr, intri = self.request({"cmd": "get_intri"})
189
- return intri_hdr, intri
190
-
191
- def get_rgb(self, camera_name: str | None = None):
192
- return self._process_frame(camera_name, False)
193
-
194
- def get_depth(self, camera_name: str | None = None):
195
- return self._process_frame(camera_name, True)
196
-
197
- def _process_frame(
198
- self,
199
- camera_name: str | None = None,
200
- depth_flag: bool = False,
201
- ):
202
- req_cmd = f"get_{'depth' if depth_flag else 'rgb'}"
203
- if camera_name is not None:
204
- req_cmd += f"_{camera_name}"
205
-
206
- hdr, img = self.request({
207
- "cmd":
208
- req_cmd,
209
- "args": (1 + (self._depth_seq if depth_flag else self._rgb_seq)) %
210
- self._max_seq_num,
211
- })
212
-
213
- try:
214
- cmd = hdr["cmd"]
215
- if cmd == f"{req_cmd}_ok":
216
- if depth_flag:
217
- self._depth_seq = hdr["args"]
218
- else:
219
- self._rgb_seq = hdr["args"]
220
- return hdr, img
221
- else:
222
- return None, None
223
- except KeyError:
224
- print(f"\033[91m{hdr['cmd']} requires `cmd`\033[0m")
225
- return None, None
226
- except Exception as e:
227
- print(f"\033[91m__process_frame failed: {e}\033[0m")
228
- return None, None
229
-
230
264
 
231
265
  class HexMujocoServerBase(HexZMQServerBase):
232
266
 
233
267
  def __init__(self, net_config: dict = NET_CONFIG):
234
268
  HexZMQServerBase.__init__(self, net_config)
235
269
  self._device: HexDeviceBase = None
236
- self._states_value = HexSafeValue()
237
- self._obj_pose_value = HexSafeValue()
238
- self._cmds_value = HexSafeValue()
270
+ self._states_queue = deque(maxlen=10)
271
+ self._obj_pose_queue = deque(maxlen=10)
272
+ self._cmds_queue = deque(maxlen=10)
239
273
  self._cmds_seq = -1
240
- self._rgb_value = HexSafeValue()
241
- self._depth_value = HexSafeValue()
274
+ self._rgb_queue = deque(maxlen=10)
275
+ self._depth_queue = deque(maxlen=10)
242
276
  self._seq_clear_flag = False
243
277
 
244
278
  def work_loop(self):
245
279
  try:
246
280
  self._device.work_loop([
247
- self._states_value,
248
- self._obj_pose_value,
249
- self._cmds_value,
250
- self._rgb_value,
251
- self._depth_value,
281
+ self._states_queue,
282
+ self._obj_pose_queue,
283
+ self._cmds_queue,
284
+ self._rgb_queue,
285
+ self._depth_queue,
252
286
  self._stop_event,
253
287
  ])
254
288
  finally:
@@ -266,7 +300,9 @@ class HexMujocoServerBase(HexZMQServerBase):
266
300
  return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
267
301
 
268
302
  try:
269
- ts, count, states = self._states_value.get()
303
+ ts, count, states = self._states_queue.popleft()
304
+ except IndexError:
305
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
270
306
  except Exception as e:
271
307
  print(f"\033[91m{recv_hdr['cmd']} failed: {e}\033[0m")
272
308
  return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
@@ -292,7 +328,7 @@ class HexMujocoServerBase(HexZMQServerBase):
292
328
  delta = (seq - self._cmds_seq) % self._max_seq_num
293
329
  if delta >= 0 and delta < 1e6:
294
330
  self._cmds_seq = seq
295
- self._cmds_value.set((recv_hdr["ts"], seq, recv_buf))
331
+ self._cmds_queue.append((recv_hdr["ts"], seq, recv_buf))
296
332
  return self.no_ts_hdr(recv_hdr, True), None
297
333
  else:
298
334
  return self.no_ts_hdr(recv_hdr, False), None
@@ -309,13 +345,14 @@ class HexMujocoServerBase(HexZMQServerBase):
309
345
  # get camera config
310
346
  split_cmd = recv_hdr["cmd"].split("_")
311
347
  depth_flag = split_cmd[1] == "depth"
312
- if depth_flag:
313
- value = self._depth_value
314
- else:
315
- value = self._rgb_value
316
348
 
317
349
  try:
318
- ts, count, img = value.get()
350
+ if depth_flag:
351
+ ts, count, img = self._depth_queue.popleft()
352
+ else:
353
+ ts, count, img = self._rgb_queue.popleft()
354
+ except IndexError:
355
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
319
356
  except Exception as e:
320
357
  print(f"\033[91m{recv_hdr['cmd']} failed: {e}\033[0m")
321
358
  return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
@@ -8,13 +8,13 @@
8
8
 
9
9
  import threading
10
10
  import numpy as np
11
+ from collections import deque
11
12
 
12
13
  from ..robot_base import HexRobotBase
13
14
  from ...zmq_base import (
14
15
  hex_zmq_ts_now,
15
16
  hex_zmq_ts_delta_ms,
16
17
  HexRate,
17
- HexSafeValue,
18
18
  )
19
19
  from ...hex_launch import hex_log, HEX_LOG_LEVEL
20
20
 
@@ -47,10 +47,10 @@ class HexRobotDummy(HexRobotBase):
47
47
  def __del__(self):
48
48
  HexRobotBase.__del__(self)
49
49
 
50
- def work_loop(self, hex_values: list[HexSafeValue | threading.Event]):
51
- states_value = hex_values[0]
52
- cmds_value = hex_values[1]
53
- stop_event = hex_values[2]
50
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
51
+ states_queue = hex_queues[0]
52
+ cmds_queue = hex_queues[1]
53
+ stop_event = hex_queues[2]
54
54
 
55
55
  dummy_states = np.zeros((self._dofs[0], 3))
56
56
  states_count = 0
@@ -58,11 +58,15 @@ class HexRobotDummy(HexRobotBase):
58
58
  rate = HexRate(1000)
59
59
  while self._working.is_set() and not stop_event.is_set():
60
60
  # states
61
- states_value.set((hex_zmq_ts_now(), states_count, dummy_states))
61
+ states_queue.append((hex_zmq_ts_now(), states_count, dummy_states))
62
62
  states_count = (states_count + 1) % self._max_seq_num
63
63
 
64
64
  # cmds
65
- cmds_pack = cmds_value.get(timeout_s=-1.0)
65
+ cmds_pack = None
66
+ try:
67
+ cmds_pack = cmds_queue.popleft()
68
+ except IndexError:
69
+ pass
66
70
  if cmds_pack is not None:
67
71
  ts, seq, cmds = cmds_pack
68
72
  if seq != last_cmds_seq:
@@ -24,3 +24,4 @@ class HexRobotDummyClient(HexRobotClientBase):
24
24
  net_config: dict = NET_CONFIG,
25
25
  ):
26
26
  HexRobotClientBase.__init__(self, net_config)
27
+ self._wait_for_working()
@@ -11,13 +11,13 @@ import subprocess
11
11
  import time
12
12
  import threading
13
13
  import numpy as np
14
+ from collections import deque
14
15
 
15
16
  from ..robot_base import HexRobotBase
16
17
  from ...zmq_base import (
17
18
  hex_zmq_ts_now,
18
19
  hex_zmq_ts_delta_ms,
19
20
  HexRate,
20
- HexSafeValue,
21
21
  )
22
22
  from ...hex_launch import hex_log, HEX_LOG_LEVEL
23
23
  from dynamixel_sdk.group_sync_read import GroupSyncRead
@@ -104,10 +104,10 @@ class HexRobotGello(HexRobotBase):
104
104
  # start work loop
105
105
  self._working.set()
106
106
 
107
- def work_loop(self, hex_values: list[HexSafeValue | threading.Event]):
108
- states_value = hex_values[0]
109
- cmds_value = hex_values[1]
110
- stop_event = hex_values[2]
107
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
108
+ states_queue = hex_queues[0]
109
+ cmds_queue = hex_queues[1]
110
+ stop_event = hex_queues[2]
111
111
 
112
112
  states_count = 0
113
113
  last_cmds_seq = -1
@@ -116,11 +116,15 @@ class HexRobotGello(HexRobotBase):
116
116
  # states
117
117
  ts, states = self.__get_states()
118
118
  if states is not None:
119
- states_value.set((ts, states_count, states))
119
+ states_queue.append((ts, states_count, states))
120
120
  states_count = (states_count + 1) % self._max_seq_num
121
121
 
122
122
  # cmds
123
- cmds_pack = cmds_value.get(timeout_s=-1.0)
123
+ cmds_pack = None
124
+ try:
125
+ cmds_pack = cmds_queue.popleft()
126
+ except IndexError:
127
+ pass
124
128
  if cmds_pack is not None:
125
129
  ts, seq, cmds = cmds_pack
126
130
  if seq != last_cmds_seq:
@@ -24,3 +24,4 @@ class HexRobotGelloClient(HexRobotClientBase):
24
24
  net_config: dict = NET_CONFIG,
25
25
  ):
26
26
  HexRobotClientBase.__init__(self, net_config)
27
+ self._wait_for_working()
@@ -9,13 +9,13 @@
9
9
  import time
10
10
  import threading
11
11
  import numpy as np
12
+ from collections import deque
12
13
 
13
14
  from ..robot_base import HexRobotBase
14
15
  from ...zmq_base import (
15
16
  hex_zmq_ts_now,
16
17
  hex_zmq_ts_delta_ms,
17
18
  HexRate,
18
- HexSafeValue,
19
19
  )
20
20
  from ...hex_launch import hex_log, HEX_LOG_LEVEL
21
21
  from hex_device import HexDeviceApi, MotorBase
@@ -73,6 +73,10 @@ class HexRobotHexarm(HexRobotBase):
73
73
  self.__arm_archer: MotorBase | None = None
74
74
  self.__gripper: MotorBase | None = None
75
75
 
76
+ # buffer
77
+ self.__arm_state_buffer: dict | None = None
78
+ self.__gripper_state_buffer: dict | None = None
79
+
76
80
  # open device
77
81
  self.__hex_api = HexDeviceApi(
78
82
  ws_url=f"ws://{device_ip}:{device_port}",
@@ -122,26 +126,30 @@ class HexRobotHexarm(HexRobotBase):
122
126
  # start work loop
123
127
  self._working.set()
124
128
 
125
- def work_loop(self, hex_values: list[HexSafeValue | threading.Event]):
126
- states_value = hex_values[0]
127
- cmds_value = hex_values[1]
128
- stop_event = hex_values[2]
129
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
130
+ states_queue = hex_queues[0]
131
+ cmds_queue = hex_queues[1]
132
+ stop_event = hex_queues[2]
129
133
 
130
134
  last_states_ts = hex_zmq_ts_now()
131
135
  states_count = 0
132
136
  last_cmds_seq = -1
133
- rate = HexRate(1000)
137
+ rate = HexRate(2000)
134
138
  while self._working.is_set() and not stop_event.is_set():
135
139
  # states
136
140
  ts, states = self.__get_states()
137
141
  if states is not None:
138
- if hex_zmq_ts_delta_ms(ts, last_states_ts) > 1.0:
142
+ if hex_zmq_ts_delta_ms(ts, last_states_ts) > 1e-6:
139
143
  last_states_ts = ts
140
- states_value.set((ts, states_count, states))
144
+ states_queue.append((ts, states_count, states))
141
145
  states_count = (states_count + 1) % self._max_seq_num
142
146
 
143
147
  # cmds
144
- cmds_pack = cmds_value.get(timeout_s=-1.0)
148
+ cmds_pack = None
149
+ try:
150
+ cmds_pack = cmds_queue.popleft()
151
+ except IndexError:
152
+ pass
145
153
  if cmds_pack is not None:
146
154
  ts, seq, cmds = cmds_pack
147
155
  if seq != last_cmds_seq:
@@ -160,22 +168,47 @@ class HexRobotHexarm(HexRobotBase):
160
168
  return None, None
161
169
 
162
170
  # (arm_dofs, 3) # pos vel eff
163
- arm_states_dict = self.__arm_archer.get_simple_motor_status()
164
- pos = arm_states_dict['pos']
165
- vel = arm_states_dict['vel']
166
- eff = arm_states_dict['eff']
167
- ts = arm_states_dict['ts']
171
+ if self.__arm_state_buffer is None:
172
+ self.__arm_state_buffer = self.__arm_archer.get_simple_motor_status(
173
+ )
168
174
 
169
175
  # (gripper_dofs, 3) # pos vel eff
170
- if self.__gripper is not None:
171
- gripper_states_dict = self.__gripper.get_simple_motor_status()
172
- pos += gripper_states_dict['pos']
173
- vel += gripper_states_dict['vel']
174
- eff += gripper_states_dict['eff']
176
+ if self.__gripper is not None and self.__gripper_state_buffer is None:
177
+ self.__gripper_state_buffer = self.__gripper.get_simple_motor_status(
178
+ )
179
+
180
+ arm_ready = self.__arm_state_buffer is not None
181
+ gripper_ready = self.__gripper is None or self.__gripper_state_buffer is not None
182
+ if arm_ready and gripper_ready:
183
+ arm_ts = self.__arm_state_buffer['ts']
184
+ gripper_ts = self.__gripper_state_buffer[
185
+ 'ts'] if self.__gripper is not None else arm_ts
186
+
187
+ delta_ms = hex_zmq_ts_delta_ms(arm_ts, gripper_ts)
188
+ if np.fabs(delta_ms) < 1e-6:
189
+ pos = self.__arm_state_buffer['pos']
190
+ vel = self.__arm_state_buffer['vel']
191
+ eff = self.__arm_state_buffer['eff']
192
+
193
+ if self.__gripper is not None:
194
+ pos = np.concatenate(
195
+ [pos, self.__gripper_state_buffer['pos']])
196
+ vel = np.concatenate(
197
+ [vel, self.__gripper_state_buffer['vel']])
198
+ eff = np.concatenate(
199
+ [eff, self.__gripper_state_buffer['eff']])
200
+
201
+ state = np.array([pos, vel, eff]).T
202
+ self.__arm_state_buffer, self.__gripper_state_buffer = None, None
203
+ return arm_ts if self.__sens_ts else hex_zmq_ts_now(), state
204
+ elif delta_ms > 0.0:
205
+ self.__gripper_state_buffer = None
206
+ return None, None
207
+ else:
208
+ self.__arm_state_buffer = None
209
+ return None, None
175
210
 
176
- pos, vel, eff = np.asarray(pos), np.asarray(vel), np.asarray(eff)
177
- return ts if self.__sens_ts else hex_zmq_ts_now(), np.array(
178
- [pos, vel, eff]).T
211
+ return None, None
179
212
 
180
213
  def __set_cmds(self, cmds: np.ndarray) -> bool:
181
214
  # cmds: (n)
@@ -254,5 +287,6 @@ class HexRobotHexarm(HexRobotBase):
254
287
  return
255
288
  self._working.clear()
256
289
  self.__arm_archer.stop()
290
+ time.sleep(0.5)
257
291
  self.__hex_api.close()
258
292
  hex_log(HEX_LOG_LEVEL["info"], "HexRobotHexarm closed")
@@ -32,3 +32,4 @@ class HexRobotHexarmClient(HexRobotClientBase):
32
32
  net_config: dict = NET_CONFIG,
33
33
  ):
34
34
  HexRobotClientBase.__init__(self, net_config)
35
+ self._wait_for_working()