hex-zmq-servers 0.3.2__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 (45) hide show
  1. hex_zmq_servers/__init__.py +45 -20
  2. hex_zmq_servers/cam/__init__.py +29 -5
  3. hex_zmq_servers/cam/berxel/cam_berxel.py +8 -10
  4. hex_zmq_servers/cam/berxel/cam_berxel_cli.py +1 -0
  5. hex_zmq_servers/cam/cam_base.py +40 -10
  6. hex_zmq_servers/cam/dummy/cam_dummy.py +7 -7
  7. hex_zmq_servers/cam/dummy/cam_dummy_cli.py +1 -0
  8. hex_zmq_servers/cam/realsense/__init__.py +17 -0
  9. hex_zmq_servers/cam/realsense/cam_realsense.py +157 -0
  10. hex_zmq_servers/cam/realsense/cam_realsense_cli.py +31 -0
  11. hex_zmq_servers/cam/realsense/cam_realsense_srv.py +75 -0
  12. hex_zmq_servers/cam/rgb/__init__.py +17 -0
  13. hex_zmq_servers/cam/rgb/cam_rgb.py +133 -0
  14. hex_zmq_servers/cam/rgb/cam_rgb_cli.py +41 -0
  15. hex_zmq_servers/cam/rgb/cam_rgb_srv.py +75 -0
  16. hex_zmq_servers/config/cam_realsense.json +15 -0
  17. hex_zmq_servers/config/cam_rgb.json +26 -0
  18. hex_zmq_servers/config/mujoco_archer_y6.json +2 -1
  19. hex_zmq_servers/config/mujoco_e3_desktop.json +6 -1
  20. hex_zmq_servers/config/robot_hexarm.json +1 -1
  21. hex_zmq_servers/device_base.py +3 -2
  22. hex_zmq_servers/mujoco/archer_y6/model/robot.xml +6 -6
  23. hex_zmq_servers/mujoco/archer_y6/model/scene.xml +1 -1
  24. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6.py +74 -39
  25. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_cli.py +42 -0
  26. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_srv.py +16 -14
  27. hex_zmq_servers/mujoco/e3_desktop/model/robot.xml +12 -12
  28. hex_zmq_servers/mujoco/e3_desktop/model/scene.xml +1 -1
  29. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop.py +138 -70
  30. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_cli.py +148 -33
  31. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_srv.py +39 -35
  32. hex_zmq_servers/mujoco/mujoco_base.py +101 -64
  33. hex_zmq_servers/robot/dummy/robot_dummy.py +11 -7
  34. hex_zmq_servers/robot/dummy/robot_dummy_cli.py +1 -0
  35. hex_zmq_servers/robot/gello/robot_gello.py +11 -7
  36. hex_zmq_servers/robot/gello/robot_gello_cli.py +1 -0
  37. hex_zmq_servers/robot/hexarm/robot_hexarm.py +56 -22
  38. hex_zmq_servers/robot/hexarm/robot_hexarm_cli.py +1 -0
  39. hex_zmq_servers/robot/robot_base.py +40 -10
  40. hex_zmq_servers/zmq_base.py +97 -33
  41. {hex_zmq_servers-0.3.2.dist-info → hex_zmq_servers-0.3.4.dist-info}/METADATA +7 -6
  42. {hex_zmq_servers-0.3.2.dist-info → hex_zmq_servers-0.3.4.dist-info}/RECORD +45 -35
  43. {hex_zmq_servers-0.3.2.dist-info → hex_zmq_servers-0.3.4.dist-info}/WHEEL +0 -0
  44. {hex_zmq_servers-0.3.2.dist-info → hex_zmq_servers-0.3.4.dist-info}/licenses/LICENSE +0 -0
  45. {hex_zmq_servers-0.3.2.dist-info → hex_zmq_servers-0.3.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,17 @@
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 .cam_rgb import HexCamRGB
10
+ from .cam_rgb_cli import HexCamRGBClient
11
+ from .cam_rgb_srv import HexCamRGBServer
12
+
13
+ __all__ = [
14
+ "HexCamRGB",
15
+ "HexCamRGBClient",
16
+ "HexCamRGBServer",
17
+ ]
@@ -0,0 +1,133 @@
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 platform
10
+ import cv2
11
+ import threading
12
+ import numpy as np
13
+ from collections import deque
14
+
15
+ from ..cam_base import HexCamBase
16
+ from ...zmq_base import (
17
+ hex_zmq_ts_now,
18
+ HexRate,
19
+ )
20
+ from ...hex_launch import hex_log, HEX_LOG_LEVEL
21
+
22
+ CAMERA_CONFIG = {
23
+ "cam_path": "/dev/video0",
24
+ "resolution": [640, 480],
25
+ "crop": [0, 640, 0, 480],
26
+ "exposure": 100,
27
+ "temperature": 4000,
28
+ "frame_rate": 30,
29
+ "sens_ts": True,
30
+ }
31
+
32
+
33
+ class HexCamRGB(HexCamBase):
34
+
35
+ def __init__(
36
+ self,
37
+ camera_config: dict = CAMERA_CONFIG,
38
+ ):
39
+ HexCamBase.__init__(self)
40
+
41
+ try:
42
+ self.__cam_path = camera_config["cam_path"]
43
+ self.__resolution = camera_config["resolution"]
44
+ self.__crop = camera_config["crop"]
45
+ self.__exposure = camera_config["exposure"]
46
+ self.__temperature = camera_config["temperature"]
47
+ self.__frame_rate = camera_config["frame_rate"]
48
+ self.__sens_ts = camera_config["sens_ts"]
49
+ except KeyError as ke:
50
+ missing_key = ke.args[0]
51
+ raise ValueError(
52
+ f"camera_config is not valid, missing key: {missing_key}")
53
+
54
+ # variables
55
+ # camera variables
56
+ self.__cap = cv2.VideoCapture(self.__cam_path)
57
+ # camera variables
58
+ self.__intri = np.zeros(4)
59
+
60
+ # open device
61
+ self.__cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*"MJPG"))
62
+ self.__cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.__resolution[0])
63
+ self.__cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.__resolution[1])
64
+ self.__cap.set(cv2.CAP_PROP_FPS, self.__frame_rate)
65
+ ae_open = 1.0 if platform.system() == "Windows" else 3.0
66
+ ae_close = 0.0 if platform.system() == "Windows" else 1.0
67
+ ae_value = 10000 * (2**self.__exposure) if platform.system(
68
+ ) == "Windows" else self.__exposure
69
+ self.__cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, ae_open)
70
+ if self.__exposure != 0:
71
+ self.__cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, ae_close)
72
+ self.__cap.set(cv2.CAP_PROP_EXPOSURE, ae_value)
73
+ self.__cap.set(cv2.CAP_PROP_AUTO_WB, 1)
74
+ if self.__temperature != 0:
75
+ self.__cap.set(cv2.CAP_PROP_AUTO_WB, 0)
76
+ self.__cap.set(cv2.CAP_PROP_WB_TEMPERATURE, self.__temperature)
77
+
78
+ print("#############################")
79
+ print(
80
+ f"# Resolution: ({self.__cap.get(cv2.CAP_PROP_FRAME_WIDTH)}, {self.__cap.get(cv2.CAP_PROP_FRAME_HEIGHT)})"
81
+ )
82
+ print(f"# FPS: {self.__cap.get(cv2.CAP_PROP_FPS)}")
83
+ four_cc_int = int(self.__cap.get(cv2.CAP_PROP_FOURCC))
84
+ four_cc_str = chr(four_cc_int & 0xff) + chr(
85
+ (four_cc_int >> 8) & 0xff) + chr((four_cc_int >> 16) & 0xff) + chr(
86
+ (four_cc_int >> 24) & 0xff)
87
+ print(f"# FourCC: {four_cc_str}")
88
+ print(f"# Auto Exposure: {self.__cap.get(cv2.CAP_PROP_AUTO_EXPOSURE)}")
89
+ print(f"# Exposure: {self.__cap.get(cv2.CAP_PROP_EXPOSURE)}")
90
+ print(f"# Auto WB: {self.__cap.get(cv2.CAP_PROP_AUTO_WB)}")
91
+ print(
92
+ f"# WB Temperature: {self.__cap.get(cv2.CAP_PROP_WB_TEMPERATURE)}")
93
+ print("#############################")
94
+
95
+ # start work loop
96
+ self._working.set()
97
+
98
+ def get_intri(self) -> np.ndarray:
99
+ self._wait_for_working()
100
+ return self.__intri
101
+
102
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
103
+ rgb_queue = hex_queues[0]
104
+ depth_queue = hex_queues[1]
105
+ stop_event = hex_queues[2]
106
+
107
+ rgb_count = 0
108
+ depth_queue.append((hex_zmq_ts_now(), 0,
109
+ np.zeros((self.__resolution[1], self.__resolution[0]),
110
+ dtype=np.uint16)))
111
+ rate = HexRate(self.__frame_rate * 5)
112
+ while self._working.is_set() and not stop_event.is_set():
113
+ # read frame
114
+ ret, frame = self.__cap.read()
115
+
116
+ # collect rgb frame
117
+ if ret:
118
+ frame = frame[self.__crop[2]:self.__crop[3],
119
+ self.__crop[0]:self.__crop[1]]
120
+ rgb_queue.append((hex_zmq_ts_now(), rgb_count, frame))
121
+ rgb_count = (rgb_count + 1) % self._max_seq_num
122
+
123
+ rate.sleep()
124
+
125
+ # close
126
+ self.close()
127
+
128
+ def close(self):
129
+ if not self._working.is_set():
130
+ return
131
+ self._working.clear()
132
+ self.__cap.release()
133
+ hex_log(HEX_LOG_LEVEL["info"], "HexCamRGB closed")
@@ -0,0 +1,41 @@
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 ..cam_base import HexCamClientBase
10
+ from ...zmq_base import HexRate
11
+
12
+ NET_CONFIG = {
13
+ "ip": "127.0.0.1",
14
+ "port": 12345,
15
+ "client_timeout_ms": 200,
16
+ "server_timeout_ms": 1_000,
17
+ "server_num_workers": 4,
18
+ }
19
+
20
+
21
+ class HexCamRGBClient(HexCamClientBase):
22
+
23
+ def __init__(
24
+ self,
25
+ net_config: dict = NET_CONFIG,
26
+ ):
27
+ HexCamClientBase.__init__(self, net_config)
28
+ self._wait_for_working()
29
+
30
+ def get_intri(self):
31
+ intri_hdr, intri = self.request({"cmd": "get_intri"})
32
+ return intri_hdr, intri
33
+
34
+ def _recv_loop(self):
35
+ rate = HexRate(200)
36
+ while self._recv_flag:
37
+ hdr, img = self._get_rgb_inner()
38
+ if hdr is not None:
39
+ self._rgb_queue.append((hdr, img))
40
+ rate.sleep()
41
+
@@ -0,0 +1,75 @@
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 numpy as np
10
+
11
+ try:
12
+ from ..cam_base import HexCamServerBase
13
+ from .cam_rgb import HexCamRGB
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.cam.cam_base import HexCamServerBase
22
+ from hex_zmq_servers.cam.rgb.cam_rgb import HexCamRGB
23
+
24
+ NET_CONFIG = {
25
+ "ip": "127.0.0.1",
26
+ "port": 12345,
27
+ "client_timeout_ms": 200,
28
+ "server_timeout_ms": 1_000,
29
+ "server_num_workers": 4,
30
+ }
31
+
32
+ CAMERA_CONFIG = {
33
+ "cam_path": "/dev/video0",
34
+ "resolution": [640, 480],
35
+ "frame_rate": 30,
36
+ "sens_ts": True,
37
+ }
38
+
39
+
40
+ class HexCamRGBServer(HexCamServerBase):
41
+
42
+ def __init__(
43
+ self,
44
+ net_config: dict = NET_CONFIG,
45
+ params_config: dict = CAMERA_CONFIG,
46
+ ):
47
+ HexCamServerBase.__init__(self, net_config)
48
+
49
+ # camera
50
+ self._device = HexCamRGB(params_config)
51
+
52
+ def _process_request(self, recv_hdr: dict, recv_buf: np.ndarray):
53
+ if recv_hdr["cmd"] == "is_working":
54
+ return self.no_ts_hdr(recv_hdr, self._device.is_working()), None
55
+ elif recv_hdr["cmd"] == "get_intri":
56
+ intri = self._device.get_intri()
57
+ return self.no_ts_hdr(recv_hdr, intri is not None), intri
58
+ elif recv_hdr["cmd"] == "get_rgb":
59
+ return self._get_frame(recv_hdr, False)
60
+ elif recv_hdr["cmd"] == "get_depth":
61
+ return self._get_frame(recv_hdr, True)
62
+ else:
63
+ raise ValueError(f"unknown command: {recv_hdr['cmd']}")
64
+
65
+
66
+ if __name__ == "__main__":
67
+ import argparse, json
68
+ from hex_zmq_servers.zmq_base import hex_server_helper
69
+
70
+ parser = argparse.ArgumentParser()
71
+ parser.add_argument("--cfg", type=str, required=True)
72
+ args = parser.parse_args()
73
+ cfg = json.loads(args.cfg)
74
+
75
+ hex_server_helper(cfg, HexCamRGBServer)
@@ -0,0 +1,15 @@
1
+ {
2
+ "net": {
3
+ "ip": "127.0.0.1",
4
+ "port": 12345,
5
+ "client_timeout_ms": 200,
6
+ "server_timeout_ms": 1000,
7
+ "server_num_workers": 4
8
+ },
9
+ "params": {
10
+ "serial_number": "243422073194",
11
+ "resolution": [640, 480],
12
+ "frame_rate": 30,
13
+ "sens_ts": true
14
+ }
15
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "net": {
3
+ "ip": "127.0.0.1",
4
+ "port": 12345,
5
+ "client_timeout_ms": 200,
6
+ "server_timeout_ms": 1000,
7
+ "server_num_workers": 4
8
+ },
9
+ "params": {
10
+ "cam_path": "/dev/video0",
11
+ "resolution": [
12
+ 640,
13
+ 480
14
+ ],
15
+ "crop": [
16
+ 0,
17
+ 640,
18
+ 0,
19
+ 480
20
+ ],
21
+ "exposure": 70,
22
+ "temperature": 0,
23
+ "frame_rate": 30,
24
+ "sens_ts": true
25
+ }
26
+ }
@@ -7,7 +7,7 @@
7
7
  "server_num_workers": 4
8
8
  },
9
9
  "params": {
10
- "states_rate": 500,
10
+ "states_rate": 1000,
11
11
  "img_rate": 30,
12
12
  "tau_ctrl": false,
13
13
  "mit_kp": [
@@ -28,6 +28,7 @@
28
28
  0.31,
29
29
  1.0
30
30
  ],
31
+ "cam_type": "empty",
31
32
  "headless": false,
32
33
  "sens_ts": true
33
34
  }
@@ -7,7 +7,7 @@
7
7
  "server_num_workers": 4
8
8
  },
9
9
  "params": {
10
- "states_rate": 500,
10
+ "states_rate": 1000,
11
11
  "img_rate": 30,
12
12
  "tau_ctrl": false,
13
13
  "mit_kp": [
@@ -28,6 +28,11 @@
28
28
  0.31,
29
29
  1.0
30
30
  ],
31
+ "cam_type": [
32
+ "empty",
33
+ "empty",
34
+ "empty"
35
+ ],
31
36
  "headless": false,
32
37
  "sens_ts": true
33
38
  }
@@ -9,7 +9,7 @@
9
9
  "params": {
10
10
  "device_ip": "172.18.8.161",
11
11
  "device_port": 8439,
12
- "control_hz": 500,
12
+ "control_hz": 1000,
13
13
  "arm_type": "archer_l6y",
14
14
  "use_gripper": true,
15
15
  "mit_kp": [
@@ -7,9 +7,10 @@
7
7
  ################################################################
8
8
 
9
9
  import threading
10
+ from collections import deque
10
11
  from abc import ABC, abstractmethod
11
12
 
12
- from .zmq_base import MAX_SEQ_NUM, HexSafeValue
13
+ from .zmq_base import MAX_SEQ_NUM
13
14
 
14
15
 
15
16
  class HexDeviceBase(ABC):
@@ -32,7 +33,7 @@ class HexDeviceBase(ABC):
32
33
  self._working.wait(0.1)
33
34
 
34
35
  @abstractmethod
35
- def work_loop(self, hex_values: list[HexSafeValue | threading.Event]):
36
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
36
37
  raise NotImplementedError(
37
38
  "`work_loop` should be implemented by the child class")
38
39
 
@@ -6,37 +6,37 @@
6
6
  # link 1
7
7
  <body name="link_1" pos="0 0 0.0665">
8
8
  <inertial pos="0.008 -0.0023 0.0213" quat="0.707107 0.707107 0 0" mass="0.2154" diaginertia="0.0003 0.0003 0.0002"/>
9
- <joint name="joint_1" pos="0 0 0" axis="0 0 1" range="-2.7 2.7" limited="true" armature="0.1" damping="3.0" frictionloss="1.0"/>
9
+ <joint name="joint_1" pos="0 0 0" axis="0 0 1" range="-2.7 2.7" limited="true" armature="0.01" damping="0.1" frictionloss="0.5"/>
10
10
  <geom type="mesh" rgba="0.92549 0.92549 0.92549 1" mesh="arm_link_1"/>
11
11
 
12
12
  # link 2
13
13
  <body name="link_2" pos="0.02 0 0.045">
14
14
  <inertial pos="0.0001 -0.0028 0.1317" quat="0.999789 0.0205404 0 0" mass="1.3123" diaginertia="0.018 0.0177288 0.000671225"/>
15
- <joint name="joint_2" pos="0 0 0" axis="0 1 0" range="-1.57 2.09" limited="true" armature="0.1" damping="3.0" frictionloss="1.0"/>
15
+ <joint name="joint_2" pos="0 0 0" axis="0 1 0" range="-1.57 2.09" limited="true" armature="0.01" damping="0.1" frictionloss="0.5"/>
16
16
  <geom type="mesh" rgba="1 1 1 1" mesh="arm_link_2"/>
17
17
 
18
18
  # link 3
19
19
  <body name="link_3" pos="0 0 0.264">
20
20
  <inertial pos="-0.051 0.0008 0.1447" quat="0.996909 0.0323182 -0.0688403 -0.019747" mass="1.1083" diaginertia="0.0115968 0.0112369 0.00136632"/>
21
- <joint name="joint_3" pos="0 0 0" axis="0 1 0" range="0 3.14" limited="true" armature="0.1" damping="3.0" frictionloss="1.0"/>
21
+ <joint name="joint_3" pos="0 0 0" axis="0 1 0" range="0 3.14" limited="true" armature="0.01" damping="0.1" frictionloss="0.5"/>
22
22
  <geom type="mesh" rgba="1 1 1 1" mesh="arm_link_3"/>
23
23
 
24
24
  # link 4
25
25
  <body name="link_4" pos="-0.06 0 0.245">
26
26
  <inertial pos="-0.0597 -0.001 0.0572" quat="0 0.92388 0 0.382683" mass="0.5398" diaginertia="0.0011 0.001 0.0005"/>
27
- <joint name="joint_4" pos="0 0 0" axis="0 1 0" range="-1.57 1.57" limited="true" armature="0.1" damping="3.0" frictionloss="1.0"/>
27
+ <joint name="joint_4" pos="0 0 0" axis="0 1 0" range="-1.57 1.57" limited="true" armature="0.01" damping="0.02" frictionloss="0.1"/>
28
28
  <geom type="mesh" rgba="0.792157 0.792157 0.792157 1" mesh="arm_link_4"/>
29
29
 
30
30
  # link 5
31
31
  <body name="link_5" pos="-0.0553 0 0.070">
32
32
  <inertial pos="0.0292 0.0001 -0.0107" quat="0.707107 0 0 0.707107" mass="0.3984" diaginertia="0.0003 0.0002 0.0002"/>
33
- <joint name="joint_5" pos="0 0 0" axis="-1 0 0" range="-1.57 1.57" limited="true" armature="0.1" damping="3.0" frictionloss="1.0"/>
33
+ <joint name="joint_5" pos="0 0 0" axis="-1 0 0" range="-1.57 1.57" limited="true" armature="0.01" damping="0.02" frictionloss="0.1"/>
34
34
  <geom type="mesh" rgba="0.898039 0.917647 0.929412 1" mesh="arm_link_5"/>
35
35
 
36
36
  # gripper base link
37
37
  <body name="gripper_base_link" pos="0.0553 0 0.029" quat="0.707107 0.0 -0.707107 0.0">
38
38
  <inertial pos="-0.0139 0.0001 0.0553" quat="0.567843 0.421372 0.421372 0.567843" mass="0.7651" diaginertia="0.0021 0.00143028 0.00106972"/>
39
- <joint name="joint_6" pos="0 0 0" axis="1 0 0" range="-1.57 1.57" limited="true" armature="0.1" damping="3.0" frictionloss="1.0"/>
39
+ <joint name="joint_6" pos="0 0 0" axis="1 0 0" range="-1.57 1.57" limited="true" armature="0.01" damping="0.02" frictionloss="0.1"/>
40
40
  <geom type="mesh" rgba="1 1 1 1" mesh="gripper_base_link"/>
41
41
  <geom pos="0.04 0 0.12" quat="0.6830127 0.6830127 0.1830127 -0.1830127" type="mesh" rgba="0.1 0.1 0.1 1" mesh="camera_link" />
42
42
  <camera name="end_camera" pos="0.066 0 0.105" quat="0.8660254 0.0 -0.5 0.0" fovy="50"/>
@@ -1,7 +1,7 @@
1
1
  <mujoco model="archer_y6 scene">
2
2
  <compiler angle="radian"/>
3
3
  <option integrator="implicitfast"/>
4
- <option timestep="2e-3"/>
4
+ <option timestep="1e-3"/>
5
5
 
6
6
  <statistic center="0.3 0 0.45" extent="0.8" meansize="0.05"/>
7
7
 
@@ -11,22 +11,24 @@ import copy
11
11
  import threading
12
12
  import cv2
13
13
  import numpy as np
14
+ from collections import deque
14
15
 
15
16
  import mujoco
16
17
  from mujoco import viewer
17
18
 
18
19
  from ..mujoco_base import HexMujocoBase
19
20
  from ...zmq_base import (
21
+ hex_ns_now,
20
22
  hex_zmq_ts_now,
23
+ ns_to_hex_zmq_ts,
21
24
  hex_zmq_ts_delta_ms,
22
25
  HexRate,
23
- HexSafeValue,
24
26
  )
25
27
  from ...hex_launch import hex_log, HEX_LOG_LEVEL
26
28
  from hex_robo_utils import HexCtrlUtilMitJoint as CtrlUtil
27
29
 
28
30
  MUJOCO_CONFIG = {
29
- "states_rate": 500,
31
+ "states_rate": 1000,
30
32
  "img_rate": 30,
31
33
  "tau_ctrl": False,
32
34
  "mit_kp": [200.0, 200.0, 200.0, 75.0, 15.0, 15.0, 20.0],
@@ -34,6 +36,12 @@ MUJOCO_CONFIG = {
34
36
  "headless": False,
35
37
  "sens_ts": True,
36
38
  }
39
+ CAMERA_CONFIG = {
40
+ "empty": (False, False),
41
+ "rgb": (True, False),
42
+ "berxel": (True, True),
43
+ "realsense": (True, True),
44
+ }
37
45
 
38
46
 
39
47
  class HexMujocoArcherY6(HexMujocoBase):
@@ -50,6 +58,7 @@ class HexMujocoArcherY6(HexMujocoBase):
50
58
  self.__tau_ctrl = mujoco_config["tau_ctrl"]
51
59
  self.__mit_kp = mujoco_config["mit_kp"]
52
60
  self.__mit_kd = mujoco_config["mit_kd"]
61
+ self.__cam_type = mujoco_config["cam_type"]
53
62
  self.__headless = mujoco_config["headless"]
54
63
  self.__sens_ts = mujoco_config["sens_ts"]
55
64
  except KeyError as ke:
@@ -95,20 +104,31 @@ class HexMujocoArcherY6(HexMujocoBase):
95
104
  self.__states_trig_thresh = int(self.__sim_rate / states_rate)
96
105
 
97
106
  # camera init
98
- width, height = 640, 400
99
- fovy_rad = self.__model.cam_fovy[0] * np.pi / 180.0
100
- focal = 0.5 * height / np.tan(fovy_rad / 2.0)
101
- self._intri = np.array([focal, focal, height / 2, height / 2])
102
- self.__rgb_cam = mujoco.Renderer(self.__model, height, width)
103
- self.__depth_cam = mujoco.Renderer(self.__model, height, width)
104
- self.__depth_cam.enable_depth_rendering()
105
107
  self.__img_trig_thresh = int(self.__sim_rate / img_rate)
108
+ self.__width, self.__height = 640, 400
109
+ fovy_rad = self.__model.cam_fovy[0] * np.pi / 180.0
110
+ focal = 0.5 * self.__height / np.tan(fovy_rad / 2.0)
111
+ self._intri = np.array(
112
+ [focal, focal, self.__height / 2, self.__height / 2])
113
+ self.__use_rgb, self.__use_depth = CAMERA_CONFIG.get(
114
+ self.__cam_type, (False, False))
115
+ self.__rgb_cam, self.__depth_cam = None, None
116
+ if self.__use_rgb:
117
+ self.__rgb_cam = mujoco.Renderer(self.__model, self.__height,
118
+ self.__width)
119
+ if self.__use_depth:
120
+ self.__depth_cam = mujoco.Renderer(self.__model, self.__height,
121
+ self.__width)
122
+ self.__depth_cam.enable_depth_rendering()
106
123
 
107
124
  # viewer init
108
125
  mujoco.mj_forward(self.__model, self.__data)
109
126
  if not self.__headless:
110
127
  self.__viewer = viewer.launch_passive(self.__model, self.__data)
111
128
 
129
+ # time init
130
+ self.__bias_ns = hex_ns_now() - self.__data.time * 1_000_000_000
131
+
112
132
  # start work loop
113
133
  self._working.set()
114
134
 
@@ -124,13 +144,13 @@ class HexMujocoArcherY6(HexMujocoBase):
124
144
  self.__viewer.sync()
125
145
  return True
126
146
 
127
- def work_loop(self, hex_values: list[HexSafeValue | threading.Event]):
128
- states_robot_value = hex_values[0]
129
- states_obj_value = hex_values[1]
130
- cmds_robot_value = hex_values[2]
131
- rgb_value = hex_values[3]
132
- depth_value = hex_values[4]
133
- stop_event = hex_values[5]
147
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
148
+ states_robot_queue = hex_queues[0]
149
+ states_obj_queue = hex_queues[1]
150
+ cmds_robot_queue = hex_queues[2]
151
+ rgb_queue = hex_queues[3]
152
+ depth_queue = hex_queues[4]
153
+ stop_event = hex_queues[5]
134
154
 
135
155
  last_states_ts = {"s": 0, "ns": 0}
136
156
  states_robot_count = 0
@@ -138,11 +158,18 @@ class HexMujocoArcherY6(HexMujocoBase):
138
158
  last_cmds_robot_seq = -1
139
159
  rgb_count = 0
140
160
  depth_count = 0
161
+ cmds_robot = None
141
162
 
142
163
  rate = HexRate(self.__sim_rate)
143
164
  states_trig_count = 0
144
165
  img_trig_count = 0
145
-
166
+ init_ts = self.__mujoco_ts() if self.__sens_ts else hex_zmq_ts_now()
167
+ rgb_queue.append((init_ts, 0,
168
+ np.zeros((self.__height, self.__width, 3),
169
+ dtype=np.uint8)))
170
+ depth_queue.append((init_ts, 0,
171
+ np.zeros((self.__height, self.__width),
172
+ dtype=np.uint16)))
146
173
  while self._working.is_set() and not stop_event.is_set():
147
174
  states_trig_count += 1
148
175
  if states_trig_count >= self.__states_trig_thresh:
@@ -151,45 +178,53 @@ class HexMujocoArcherY6(HexMujocoBase):
151
178
  # states
152
179
  ts, states_robot, states_obj = self.__get_states()
153
180
  if states_robot is not None:
154
- if hex_zmq_ts_delta_ms(ts, last_states_ts) > 1.0:
181
+ if hex_zmq_ts_delta_ms(ts, last_states_ts) > 1e-6:
155
182
  last_states_ts = ts
156
183
 
157
184
  # states robot
158
- states_robot_value.set(
185
+ states_robot_queue.append(
159
186
  (ts, states_robot_count, states_robot))
160
187
  states_robot_count = (states_robot_count +
161
188
  1) % self._max_seq_num
162
189
 
163
190
  # states obj
164
- states_obj_value.set(
191
+ states_obj_queue.append(
165
192
  (ts, states_obj_count, states_obj))
166
193
  states_obj_count = (states_obj_count +
167
194
  1) % self._max_seq_num
168
195
 
169
196
  # cmds
170
- cmds_robot_pack = cmds_robot_value.get(timeout_s=-1.0)
197
+ cmds_robot_pack = None
198
+ try:
199
+ cmds_robot_pack = cmds_robot_queue.popleft()
200
+ except IndexError:
201
+ pass
171
202
  if cmds_robot_pack is not None:
172
- ts, seq, cmds = cmds_robot_pack
203
+ ts, seq, cmds_robot_get = cmds_robot_pack
173
204
  if seq != last_cmds_robot_seq:
174
205
  last_cmds_robot_seq = seq
175
206
  if hex_zmq_ts_delta_ms(hex_zmq_ts_now(), ts) < 200.0:
176
- self.__set_cmds(cmds)
207
+ cmds_robot = cmds_robot_get.copy()
208
+ if cmds_robot is not None:
209
+ self.__set_cmds(cmds_robot)
177
210
 
178
211
  img_trig_count += 1
179
212
  if img_trig_count >= self.__img_trig_thresh:
180
213
  img_trig_count = 0
181
214
 
182
215
  # rgb
183
- ts, rgb_img = self.__get_rgb()
184
- if rgb_img is not None:
185
- rgb_value.set((ts, rgb_count, rgb_img))
186
- rgb_count = (rgb_count + 1) % self._max_seq_num
216
+ if self.__use_rgb:
217
+ ts, rgb_img = self.__get_rgb()
218
+ if rgb_img is not None:
219
+ rgb_queue.append((ts, rgb_count, rgb_img))
220
+ rgb_count = (rgb_count + 1) % self._max_seq_num
187
221
 
188
222
  # depth
189
- ts, depth_img = self.__get_depth()
190
- if depth_img is not None:
191
- depth_value.set((ts, depth_count, depth_img))
192
- depth_count = (depth_count + 1) % self._max_seq_num
223
+ if self.__use_depth:
224
+ ts, depth_img = self.__get_depth()
225
+ if depth_img is not None:
226
+ depth_queue.append((ts, depth_count, depth_img))
227
+ depth_count = (depth_count + 1) % self._max_seq_num
193
228
 
194
229
  # mujoco step
195
230
  mujoco.mj_step(self.__model, self.__data)
@@ -236,7 +271,8 @@ class HexMujocoArcherY6(HexMujocoBase):
236
271
  cmd_kp = cmds[:, 3].copy()
237
272
  cmd_kd = cmds[:, 4].copy()
238
273
  else:
239
- raise ValueError(f"The shape of cmds is invalid: {cmds.shape}")
274
+ raise ValueError(
275
+ f"The shape of cmds is invalid: {cmds.shape}")
240
276
  else:
241
277
  raise ValueError(f"The shape of cmds is invalid: {cmds.shape}")
242
278
  tar_pos = self._apply_pos_limits(
@@ -272,18 +308,17 @@ class HexMujocoArcherY6(HexMujocoBase):
272
308
  ), depth_img
273
309
 
274
310
  def __mujoco_ts(self):
275
- mujoco_ts = self.__data.time
276
- return {
277
- "s": int(mujoco_ts // 1),
278
- "ns": int((mujoco_ts % 1) * 1_000_000_000),
279
- }
311
+ mujoco_ts = self.__data.time * 1_000_000_000 + self.__bias_ns
312
+ return ns_to_hex_zmq_ts(mujoco_ts)
280
313
 
281
314
  def close(self):
282
315
  if not self._working.is_set():
283
316
  return
284
317
  self._working.clear()
285
- self.__rgb_cam.close()
286
- self.__depth_cam.close()
318
+ if self.__rgb_cam is not None:
319
+ self.__rgb_cam.close()
320
+ if self.__depth_cam is not None:
321
+ self.__depth_cam.close()
287
322
  if not self.__headless:
288
323
  self.__viewer.close()
289
324
  hex_log(HEX_LOG_LEVEL["info"], "HexMujocoArcherY6 closed")