codroid-robot-sdk 2.1.4__tar.gz → 2.1.5__tar.gz

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 (54) hide show
  1. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/PKG-INFO +1 -1
  2. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/RELEASE_NOTES.md +19 -0
  3. codroid_robot_sdk-2.1.4/docs/CodroidPython-SDK-Manual-v2.1.4-en.md → codroid_robot_sdk-2.1.5/docs/CodroidPython-SDK-Manual-v2.1.5-en.md +1 -1
  4. codroid_robot_sdk-2.1.4/docs/CodroidPython-SDK-Manual-v2.1.4-zh.md → codroid_robot_sdk-2.1.5/docs/CodroidPython-SDK-Manual-v2.1.5-zh.md +2 -2
  5. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/docs/README.md +1 -1
  6. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/Codroid.py +46 -16
  7. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/__about__.py +1 -1
  8. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/async_tcp_client.py +2 -1
  9. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/cri_realtime_packet_parser.py +17 -17
  10. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/define.py +6 -2
  11. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/.gitignore +0 -0
  12. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/LICENSE.txt +0 -0
  13. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/README.md +0 -0
  14. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/docs/01-quick-start.md +0 -0
  15. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/docs/02-concepts.md +0 -0
  16. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/docs/03-api-reference-codroidclient.md +0 -0
  17. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/docs/04-api-reference-motion.md +0 -0
  18. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/docs/05-api-reference-types.md +0 -0
  19. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/docs/06-api-reference-cri.md +0 -0
  20. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/docs/07-api-reference-io-register.md +0 -0
  21. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/docs/08-api-reference-utilities.md +0 -0
  22. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/docs/CodroidPython-SDK-Manual-v2.1.4-zh.pdf +0 -0
  23. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/01_basic_usage.py +0 -0
  24. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/02_run_script.py +0 -0
  25. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/03_run_project.py +0 -0
  26. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/04_global_value.py +0 -0
  27. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/05_rs485.py +0 -0
  28. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/06_jog_mode.py +0 -0
  29. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/07_move_to.py +0 -0
  30. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/08_move.py +0 -0
  31. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/09_move_path.py +0 -0
  32. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/10_control_commands.py +0 -0
  33. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/11_io_demo.py +0 -0
  34. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/12_register_demo.py +0 -0
  35. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/13_cri_realtime.py +0 -0
  36. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/14_robot_parameters.py +0 -0
  37. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/15_sync_motion.py +0 -0
  38. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/codroid_cri_test.py +0 -0
  39. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/examples/codroid_test.py +0 -0
  40. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/pyproject.toml +0 -0
  41. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/__init__.py +0 -0
  42. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/client.py +0 -0
  43. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/console.py +0 -0
  44. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/console_utf8.py +0 -0
  45. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/cri_realtime_dispatcher.py +0 -0
  46. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/exceptions.py +0 -0
  47. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/publish.py +0 -0
  48. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/py.typed +0 -0
  49. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/robot_motion.py +0 -0
  50. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/robot_settings.py +0 -0
  51. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/trajectory.py +0 -0
  52. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/types.py +0 -0
  53. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/src/codroid/utils.py +0 -0
  54. {codroid_robot_sdk-2.1.4 → codroid_robot_sdk-2.1.5}/update1.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codroid-robot-sdk
3
- Version: 2.1.4
3
+ Version: 2.1.5
4
4
  Summary: Codroid robot controller Python SDK (TCP/CRI, typed motion API)
5
5
  Project-URL: Documentation, https://guybod.github.io/CodroidSDK/
6
6
  Project-URL: Issues, https://github.com/guybod/CodroidSDK/issues
@@ -1,5 +1,24 @@
1
1
  # Codroid Python SDK 版本说明
2
2
 
3
+ ## 2.1.5 — 隐患修复
4
+
5
+ ### Bug Fixes
6
+
7
+ - **修复 `RunStep` 发错指令**:发送 `"project/run"` 改为 `"project/runStep"`,之前会导致单步调试变成完整运行
8
+ - **修复 `CriData` 返回可变引用**:改为返回深拷贝,与 C# 行为一致,避免 UDP 线程更新时主线程读到不一致数据
9
+ - **修复 `_id_counter` 竞态**:加 `threading.Lock` 保护,避免多线程并发发指令时 ID 冲突
10
+ - **修复 `GetDi`/`GetDo` 缺少端口校验**:与 `SetDo`/`SetAo` 一致,传入非法端口时抛出 `CodroidError`
11
+ - **修复 `_dispatch_publish` 静默吞异常**:publish handler 的异常现在会输出到日志
12
+
13
+ ### 改进
14
+
15
+ - **`StartCriControl` 参数校验**:新增 `filter_type`(0-3)、`duration`(1-16 且整除 1000)、`start_buffer`(1-100)校验
16
+ - **便捷运动方法补 `relative_blend` 参数**:`MovJ`、`MovL`、`MovC`、`MovCircle` 及其 `*Sync` 变体、`MotionPath` 构建方法全部支持 `relative_blend`,与 C# 对齐
17
+ - **`CposToApos` 默认参考关节改为当前关节角度**:`rj=None` 时优先使用 `CriData.JointPosition`,CRI 未启动时兜底 `[20,20,20,20,20,20]`,与 C# 行为一致
18
+ - **`CriStreamHandler.parse_packet()` 舍位方式修正**:从银行家舍位改为四舍五入(AwayFromZero),与 C# `Math.Round` 行为一致
19
+
20
+ ---
21
+
3
22
  ## 2.1.4 — blend 参数改为可选 + 欧拉角判定修复
4
23
 
5
24
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  # CodroidPython SDK Manual
2
2
 
3
- **Version:** 2.1.4 | **Package:** `codroid-robot-sdk` | **Python:** 3.7+
3
+ **Version:** 2.1.5 | **Package:** `codroid-robot-sdk` | **Python:** 3.7+
4
4
 
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  # CodroidPython SDK 手册
2
2
 
3
- **版本:** 2.1.4 | **包名:** `codroid-robot-sdk` | **Python:** 3.7+
3
+ **版本:** 2.1.5 | **包名:** `codroid-robot-sdk` | **Python:** 3.7+
4
4
 
5
5
  ---
6
6
 
@@ -2059,7 +2059,7 @@ with CodroidClient(host="192.168.8.136") as robot:
2059
2059
  #### blend / relative_blend
2060
2060
 
2061
2061
  - `blend`:平滑半径。与 `relative_blend` 互斥——同时传入时 `relative_blend` 无效。不传表示无过渡。
2062
- - `relative_blend`:相对平滑比(0–1)。与 `blend` 互斥——同时传入时此参数无效。
2062
+ - `relative_blend`:相对平滑比(0–100)。与 `blend` 互斥——同时传入时此参数无效。
2063
2063
 
2064
2064
  #### coor / tool
2065
2065
 
@@ -1,6 +1,6 @@
1
1
  # CodroidPython SDK 手册
2
2
 
3
- **版本:** 2.1.4 | **包名:** `codroid-robot-sdk` | **Python:** 3.7+
3
+ **版本:** 2.1.5 | **包名:** `codroid-robot-sdk` | **Python:** 3.7+
4
4
 
5
5
  ---
6
6
 
@@ -5,6 +5,7 @@
5
5
  """
6
6
  from __future__ import annotations
7
7
 
8
+ import copy
8
9
  import threading
9
10
  import socket
10
11
  import time
@@ -35,6 +36,7 @@ class CodroidSession:
35
36
  """
36
37
  self._net = JsonStreamClient(host, port)
37
38
  self._id_counter = 1
39
+ self._id_lock = threading.Lock()
38
40
  self.debug = False
39
41
  # --- 新增属性 ---
40
42
  self.local_ip = local_ip
@@ -55,9 +57,11 @@ class CodroidSession:
55
57
  Returns:
56
58
  CommonResponse: 响应对象 / Response object.
57
59
  """
58
- self._id_counter += 1
60
+ with self._id_lock:
61
+ self._id_counter += 1
62
+ current_id = self._id_counter
59
63
  # 构造请求模型
60
- request = CodroidRequest(id=self._id_counter, ty=ty, db=db)
64
+ request = CodroidRequest(id=current_id, ty=ty, db=db)
61
65
 
62
66
  # 转换为字典并发送 (排除 db 为 None 的情况可根据具体接口微调)
63
67
  payload: Dict[str, Any] = {
@@ -173,8 +177,9 @@ class CodroidSession:
173
177
 
174
178
  @property
175
179
  def CriData(self) -> Optional[CriRealTimeData]:
176
- """最新 CRI 快照(C# ``CriData``,内部 ``cri_cache``)。"""
177
- return self.cri_cache
180
+ """最新 CRI 快照(线程安全深拷贝,与 C# ``CriData`` 行为一致)。"""
181
+ cache = self.cri_cache
182
+ return copy.deepcopy(cache) if cache is not None else None
178
183
 
179
184
  def WaitForCriData(self, timeout: float = 5.0) -> CriRealTimeData:
180
185
  """
@@ -298,7 +303,7 @@ class CodroidSession:
298
303
  """
299
304
  2.5 单步运行(C# ``RunStep``)。
300
305
  """
301
- return self._send_command("project/run", {"id": project_id})
306
+ return self._send_command("project/runStep", {"id": project_id})
302
307
 
303
308
  def PauseProject(self) -> CommonResponse:
304
309
  """
@@ -542,9 +547,10 @@ class CodroidSession:
542
547
  Returns:
543
548
  CommonResponse: 响应对象 / Response object.
544
549
  """
545
- # 处理默认参考关节角
550
+ # 处理默认参考关节角:优先用当前关节角度,CRI 未启动时兜底
546
551
  if rj is None:
547
- rj = [20.0, 20.0, 20.0, 20.0, 20.0, 20.0]
552
+ cri = self.CriData
553
+ rj = list(cri.joint_position) if cri is not None and len(cri.joint_position) >= 6 else [20.0, 20.0, 20.0, 20.0, 20.0, 20.0]
548
554
 
549
555
  db = {
550
556
  "cp": cp,
@@ -724,6 +730,7 @@ class CodroidSession:
724
730
  speed: float,
725
731
  acceleration: float,
726
732
  blend: Optional[float] = None,
733
+ relative_blend: Optional[float] = None,
727
734
  coor: Optional[Sequence[float]] = None,
728
735
  tool: Optional[Sequence[float]] = None,
729
736
  ) -> CommonResponse:
@@ -740,6 +747,7 @@ class CodroidSession:
740
747
  speed,
741
748
  acceleration,
742
749
  blend=blend,
750
+ relative_blend=relative_blend,
743
751
  coor=coor,
744
752
  tool=tool,
745
753
  )
@@ -749,6 +757,7 @@ class CodroidSession:
749
757
  speed,
750
758
  acceleration,
751
759
  blend=blend,
760
+ relative_blend=relative_blend,
752
761
  coor=coor,
753
762
  tool=tool,
754
763
  )
@@ -760,6 +769,7 @@ class CodroidSession:
760
769
  speed: float,
761
770
  acceleration: float,
762
771
  blend: Optional[float] = None,
772
+ relative_blend: Optional[float] = None,
763
773
  coor: Optional[Sequence[float]] = None,
764
774
  tool: Optional[Sequence[float]] = None,
765
775
  ) -> CommonResponse:
@@ -775,6 +785,7 @@ class CodroidSession:
775
785
  speed,
776
786
  acceleration,
777
787
  blend=blend,
788
+ relative_blend=relative_blend,
778
789
  coor=coor,
779
790
  tool=tool,
780
791
  )
@@ -784,6 +795,7 @@ class CodroidSession:
784
795
  speed,
785
796
  acceleration,
786
797
  blend=blend,
798
+ relative_blend=relative_blend,
787
799
  coor=coor,
788
800
  tool=tool,
789
801
  )
@@ -796,6 +808,7 @@ class CodroidSession:
796
808
  speed: float,
797
809
  acceleration: float,
798
810
  blend: Optional[float] = None,
811
+ relative_blend: Optional[float] = None,
799
812
  coor: Optional[Sequence[float]] = None,
800
813
  tool: Optional[Sequence[float]] = None,
801
814
  ) -> CommonResponse:
@@ -806,6 +819,7 @@ class CodroidSession:
806
819
  speed,
807
820
  acceleration,
808
821
  blend=blend,
822
+ relative_blend=relative_blend,
809
823
  coor=coor,
810
824
  tool=tool,
811
825
  )
@@ -819,6 +833,7 @@ class CodroidSession:
819
833
  speed: float,
820
834
  acceleration: float,
821
835
  blend: Optional[float] = None,
836
+ relative_blend: Optional[float] = None,
822
837
  coor: Optional[Sequence[float]] = None,
823
838
  tool: Optional[Sequence[float]] = None,
824
839
  ) -> CommonResponse:
@@ -830,6 +845,7 @@ class CodroidSession:
830
845
  speed,
831
846
  acceleration,
832
847
  blend=blend,
848
+ relative_blend=relative_blend,
833
849
  coor=coor,
834
850
  tool=tool,
835
851
  )
@@ -1013,6 +1029,7 @@ class CodroidSession:
1013
1029
  speed: float, acceleration: float,
1014
1030
  wait: Optional[MotionWaitOptions] = None,
1015
1031
  blend: Optional[float] = None,
1032
+ relative_blend: Optional[float] = None,
1016
1033
  coor: Optional[Sequence[float]] = None,
1017
1034
  tool: Optional[Sequence[float]] = None,
1018
1035
  ) -> bool:
@@ -1024,7 +1041,8 @@ class CodroidSession:
1024
1041
  speed: 速度。
1025
1042
  acceleration: 加速度。
1026
1043
  wait: 等待参数,None 使用默认值。
1027
- blend: 平滑半径。
1044
+ blend: 平滑半径。与 relative_blend 互斥。
1045
+ relative_blend: 相对平滑比(0-100)。与 blend 互斥。
1028
1046
  coor: 用户坐标系。
1029
1047
  tool: 工具坐标系。
1030
1048
 
@@ -1032,7 +1050,7 @@ class CodroidSession:
1032
1050
  bool: 到达目标返回 True。
1033
1051
  """
1034
1052
  options = wait or MotionWaitOptions()
1035
- self.MovJ(target, speed, acceleration, blend=blend, coor=coor, tool=tool)
1053
+ self.MovJ(target, speed, acceleration, blend=blend, relative_blend=relative_blend, coor=coor, tool=tool)
1036
1054
  if isinstance(target, JointPoint):
1037
1055
  predicate = (lambda jp: lambda data:
1038
1056
  self._max_abs_diff(data.joint_position, jp) <= options.joint_tolerance_deg
@@ -1050,6 +1068,7 @@ class CodroidSession:
1050
1068
  speed: float, acceleration: float,
1051
1069
  wait: Optional[MotionWaitOptions] = None,
1052
1070
  blend: Optional[float] = None,
1071
+ relative_blend: Optional[float] = None,
1053
1072
  coor: Optional[Sequence[float]] = None,
1054
1073
  tool: Optional[Sequence[float]] = None,
1055
1074
  ) -> bool:
@@ -1061,7 +1080,8 @@ class CodroidSession:
1061
1080
  speed: 速度。
1062
1081
  acceleration: 加速度。
1063
1082
  wait: 等待参数,None 使用默认值。
1064
- blend: 平滑半径。
1083
+ blend: 平滑半径。与 relative_blend 互斥。
1084
+ relative_blend: 相对平滑比(0-100)。与 blend 互斥。
1065
1085
  coor: 用户坐标系。
1066
1086
  tool: 工具坐标系。
1067
1087
 
@@ -1069,7 +1089,7 @@ class CodroidSession:
1069
1089
  bool: 到达目标返回 True。
1070
1090
  """
1071
1091
  options = wait or MotionWaitOptions()
1072
- self.MovL(target, speed, acceleration, blend=blend, coor=coor, tool=tool)
1092
+ self.MovL(target, speed, acceleration, blend=blend, relative_blend=relative_blend, coor=coor, tool=tool)
1073
1093
  if isinstance(target, JointPoint):
1074
1094
  predicate = (lambda jp: lambda data:
1075
1095
  self._max_abs_diff(data.joint_position, jp) <= options.joint_tolerance_deg
@@ -1088,6 +1108,7 @@ class CodroidSession:
1088
1108
  speed: float, acceleration: float,
1089
1109
  wait: Optional[MotionWaitOptions] = None,
1090
1110
  blend: Optional[float] = None,
1111
+ relative_blend: Optional[float] = None,
1091
1112
  coor: Optional[Sequence[float]] = None,
1092
1113
  tool: Optional[Sequence[float]] = None,
1093
1114
  ) -> bool:
@@ -1098,8 +1119,8 @@ class CodroidSession:
1098
1119
  bool: 到达目标返回 True。
1099
1120
  """
1100
1121
  options = wait or MotionWaitOptions()
1101
- self.MovC(middle, target, speed, acceleration, blend=blend, coor=coor, tool=tool)
1102
- inst = MoveInstruction.MovC(middle, target, speed, acceleration, blend=blend, coor=coor, tool=tool)
1122
+ self.MovC(middle, target, speed, acceleration, blend=blend, relative_blend=relative_blend, coor=coor, tool=tool)
1123
+ inst = MoveInstruction.MovC(middle, target, speed, acceleration, blend=blend, relative_blend=relative_blend, coor=coor, tool=tool)
1103
1124
  cmds = [inst.to_dict()]
1104
1125
  predicate = self._build_move_target_reached_predicate(cmds, options)
1105
1126
  self._wait_until_settled_by_cri(predicate, "MovCSync", options)
@@ -1113,6 +1134,7 @@ class CodroidSession:
1113
1134
  speed: float, acceleration: float,
1114
1135
  wait: Optional[MotionWaitOptions] = None,
1115
1136
  blend: Optional[float] = None,
1137
+ relative_blend: Optional[float] = None,
1116
1138
  coor: Optional[Sequence[float]] = None,
1117
1139
  tool: Optional[Sequence[float]] = None,
1118
1140
  ) -> bool:
@@ -1123,8 +1145,8 @@ class CodroidSession:
1123
1145
  bool: 到达目标返回 True。
1124
1146
  """
1125
1147
  options = wait or MotionWaitOptions()
1126
- self.MovCircle(middle, target, circle_num, speed, acceleration, blend=blend, coor=coor, tool=tool)
1127
- inst = MoveInstruction.MovCircle(middle, target, circle_num, speed, acceleration, blend=blend, coor=coor, tool=tool)
1148
+ self.MovCircle(middle, target, circle_num, speed, acceleration, blend=blend, relative_blend=relative_blend, coor=coor, tool=tool)
1149
+ inst = MoveInstruction.MovCircle(middle, target, circle_num, speed, acceleration, blend=blend, relative_blend=relative_blend, coor=coor, tool=tool)
1128
1150
  cmds = [inst.to_dict()]
1129
1151
  predicate = self._build_move_target_reached_predicate(cmds, options)
1130
1152
  self._wait_until_settled_by_cri(predicate, "MovCircleSync", options)
@@ -1267,6 +1289,7 @@ class CodroidSession:
1267
1289
  Returns:
1268
1290
  int: 0 或 1 / 0 or 1.
1269
1291
  """
1292
+ self._validate_io(IOType.DI, port)
1270
1293
  res = self.GetIoValues([{"type": IOType.DI, "port": port}])
1271
1294
  if res.db is not None and len(res.db) > 0:
1272
1295
  return int(res.db[0]["value"])
@@ -1283,6 +1306,7 @@ class CodroidSession:
1283
1306
  Returns:
1284
1307
  int: 0 或 1 / 0 or 1.
1285
1308
  """
1309
+ self._validate_io(IOType.DO, port)
1286
1310
  res = self.GetIoValues([{"type": IOType.DO, "port": port}])
1287
1311
  if res.db is not None and len(res.db) > 0:
1288
1312
  return int(res.db[0]["value"])
@@ -1497,10 +1521,16 @@ class CodroidSession:
1497
1521
  17.6 开启实时控制 / Start CRI control mode(C# ``StartCriControl``,``CRI/StartControl``)。
1498
1522
 
1499
1523
  Args:
1500
- filter_type: 滤波类型。
1524
+ filter_type: 滤波类型(0=关闭,1=平均,2=二阶低通,3=椭圆)。
1501
1525
  duration: 指令间隔 ms(1–16,且可整除 1000)。
1502
1526
  start_buffer: 启动缓冲点数(1–100)。
1503
1527
  """
1528
+ if int(filter_type) not in (0, 1, 2, 3):
1529
+ raise ValueError(f"filter_type 须为 0-3,收到 {int(filter_type)}")
1530
+ if not (1 <= duration <= 16) or 1000 % duration != 0:
1531
+ raise ValueError(f"duration 须为 1-16 且整除 1000,收到 {duration}")
1532
+ if not (1 <= start_buffer <= 100):
1533
+ raise ValueError(f"start_buffer 须为 1-100,收到 {start_buffer}")
1504
1534
  db = {
1505
1535
  "filterType": int(filter_type),
1506
1536
  "duration": duration,
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2026-present guybod <b13140185898@outlook.com>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "2.1.4"
4
+ __version__ = "2.1.5"
@@ -214,7 +214,8 @@ class TransportClient(JsonStreamClient):
214
214
  for handler in handlers:
215
215
  try:
216
216
  handler(ty, db, raw_json)
217
- except Exception:
217
+ except Exception as e:
218
+ logger.warning("publish handler error [%s]: %s", ty, e)
218
219
  continue
219
220
 
220
221
  def _fail_all_pending(self, exc: BaseException):
@@ -189,13 +189,13 @@ class CriStreamHandler:
189
189
  if self.mask & (1 << 8):
190
190
  fmt = f"<{self.joint_count}{self.float_fmt}"
191
191
  raw_jp = struct.unpack_from(fmt, data, offset)
192
- res.joint_pos = [round(math.degrees(q), 3) for q in raw_jp]
192
+ res.joint_pos = [_round_away_from_zero(math.degrees(q)) for q in raw_jp]
193
193
  offset += self.joint_count * self.float_size
194
194
 
195
195
  if self.mask & (1 << 9):
196
196
  fmt = f"<{self.joint_count}{self.float_fmt}"
197
197
  raw_jv = struct.unpack_from(fmt, data, offset)
198
- res.joint_vel = [round(math.degrees(q), 3) for q in raw_jv]
198
+ res.joint_vel = [_round_away_from_zero(math.degrees(q)) for q in raw_jv]
199
199
  offset += self.joint_count * self.float_size
200
200
 
201
201
  if self.mask & (1 << 10):
@@ -204,13 +204,13 @@ class CriStreamHandler:
204
204
  raw_cp = struct.unpack_from(fmt, data, offset)
205
205
 
206
206
  converted_cp = [
207
- round(raw_cp[0] * 1000.0, 3),
208
- round(raw_cp[1] * 1000.0, 3),
209
- round(raw_cp[2] * 1000.0, 3),
207
+ _round_away_from_zero(raw_cp[0] * 1000.0),
208
+ _round_away_from_zero(raw_cp[1] * 1000.0),
209
+ _round_away_from_zero(raw_cp[2] * 1000.0),
210
210
  ]
211
- converted_cp.extend([round(math.degrees(a), 3) for a in raw_cp[3:6]])
211
+ converted_cp.extend([_round_away_from_zero(math.degrees(a)) for a in raw_cp[3:6]])
212
212
  if count == 7:
213
- converted_cp.append(round(math.degrees(raw_cp[6]), 3))
213
+ converted_cp.append(_round_away_from_zero(math.degrees(raw_cp[6])))
214
214
 
215
215
  res.cartesian_pos = converted_cp
216
216
  offset += count * self.float_size
@@ -219,37 +219,37 @@ class CriStreamHandler:
219
219
  fmt = f"<{6}{self.float_fmt}"
220
220
  raw_cv = struct.unpack_from(fmt, data, offset)
221
221
  res.cartesian_vel = [
222
- round(raw_cv[0] * 1000.0, 3),
223
- round(raw_cv[1] * 1000.0, 3),
224
- round(raw_cv[2] * 1000.0, 3),
225
- round(math.degrees(raw_cv[3]), 3),
226
- round(math.degrees(raw_cv[4]), 3),
227
- round(math.degrees(raw_cv[5]), 3),
222
+ _round_away_from_zero(raw_cv[0] * 1000.0),
223
+ _round_away_from_zero(raw_cv[1] * 1000.0),
224
+ _round_away_from_zero(raw_cv[2] * 1000.0),
225
+ _round_away_from_zero(math.degrees(raw_cv[3])),
226
+ _round_away_from_zero(math.degrees(raw_cv[4])),
227
+ _round_away_from_zero(math.degrees(raw_cv[5])),
228
228
  ]
229
229
  offset += 6 * self.float_size
230
230
 
231
231
  if self.mask & (1 << 12):
232
232
  raw_tcp = struct.unpack_from(f"<{self.float_fmt}", data, offset)[0]
233
- res.tcp_speed = round(raw_tcp * 1000.0, 3)
233
+ res.tcp_speed = _round_away_from_zero(raw_tcp * 1000.0)
234
234
  offset += self.float_size
235
235
 
236
236
  if self.mask & (1 << 13):
237
237
  fmt = f"<{self.joint_count}{self.float_fmt}"
238
238
  raw_jt = struct.unpack_from(fmt, data, offset)
239
- res.joint_torque = [round(q, 3) for q in raw_jt]
239
+ res.joint_torque = [_round_away_from_zero(q) for q in raw_jt]
240
240
  offset += self.joint_count * self.float_size
241
241
 
242
242
  if self.mask & (1 << 14):
243
243
  fmt = f"<{self.joint_count}{self.float_fmt}"
244
244
  raw_et = struct.unpack_from(fmt, data, offset)
245
- res.external_torque = [round(q, 3) for q in raw_et]
245
+ res.external_torque = [_round_away_from_zero(q) for q in raw_et]
246
246
  offset += self.joint_count * self.float_size
247
247
 
248
248
  if self.mask & (1 << 15):
249
249
  if self.extra_axis_count > 0:
250
250
  fmt = f"<{self.extra_axis_count}{self.float_fmt}"
251
251
  raw_ep = struct.unpack_from(fmt, data, offset)
252
- res.extra_axis_pos = [round(q, 3) for q in raw_ep]
252
+ res.extra_axis_pos = [_round_away_from_zero(q) for q in raw_ep]
253
253
  offset += self.extra_axis_count * self.float_size
254
254
 
255
255
  except struct.error as e:
@@ -648,9 +648,10 @@ class MotionPath:
648
648
  speed: float,
649
649
  acc: float,
650
650
  blend: Optional[float] = None,
651
+ relative_blend: Optional[float] = None,
651
652
  ) -> MotionPath:
652
653
  """添加关节运动 movJ。"""
653
- return self._add_item(MotionType.MOVJ, target, speed, acc, blend)
654
+ return self._add_item(MotionType.MOVJ, target, speed, acc, blend, relative_blend=relative_blend)
654
655
 
655
656
  def MovL(
656
657
  self,
@@ -658,9 +659,10 @@ class MotionPath:
658
659
  speed: float,
659
660
  acc: float,
660
661
  blend: Optional[float] = None,
662
+ relative_blend: Optional[float] = None,
661
663
  ) -> MotionPath:
662
664
  """添加直线运动 movL。"""
663
- return self._add_item(MotionType.MOVL, target, speed, acc, blend)
665
+ return self._add_item(MotionType.MOVL, target, speed, acc, blend, relative_blend=relative_blend)
664
666
 
665
667
  def MovC(
666
668
  self,
@@ -669,6 +671,7 @@ class MotionPath:
669
671
  speed: float,
670
672
  acc: float,
671
673
  blend: Optional[float] = None,
674
+ relative_blend: Optional[float] = None,
672
675
  ) -> MotionPath:
673
676
  """添加圆弧运动 movC(目标与中间点均为 TCP)。"""
674
677
  target_mp = (
@@ -687,6 +690,7 @@ class MotionPath:
687
690
  speed,
688
691
  acc,
689
692
  blend,
693
+ relative_blend=relative_blend,
690
694
  middle=middle_mp,
691
695
  )
692
696