tactile-pressure-sdk 2.0.0__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.
- tactile_pressure_sdk-2.0.0/PKG-INFO +1311 -0
- tactile_pressure_sdk-2.0.0/README.md +1292 -0
- tactile_pressure_sdk-2.0.0/pyproject.toml +33 -0
- tactile_pressure_sdk-2.0.0/setup.cfg +4 -0
- tactile_pressure_sdk-2.0.0/tactile_pressure_sdk.egg-info/PKG-INFO +1311 -0
- tactile_pressure_sdk-2.0.0/tactile_pressure_sdk.egg-info/SOURCES.txt +24 -0
- tactile_pressure_sdk-2.0.0/tactile_pressure_sdk.egg-info/dependency_links.txt +1 -0
- tactile_pressure_sdk-2.0.0/tactile_pressure_sdk.egg-info/requires.txt +6 -0
- tactile_pressure_sdk-2.0.0/tactile_pressure_sdk.egg-info/top_level.txt +1 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/__init__.py +61 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/api/__init__.py +1 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/api/base.py +35 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/api/calibration_api.py +196 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/api/config_api.py +249 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/api/device_api.py +92 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/api/pressure_api.py +86 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/client.py +159 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/exceptions.py +39 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/models.py +97 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/protocol/__init__.py +1 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/protocol/constants.py +118 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/protocol/crc16.py +44 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/protocol/modbus_rtu.py +435 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/transport/__init__.py +1 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/transport/crc16.py +48 -0
- tactile_pressure_sdk-2.0.0/tactile_sdk/transport/serial_transport.py +121 -0
|
@@ -0,0 +1,1311 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tactile-pressure-sdk
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: 玄雅 / Synria 科技触觉压力采集模块 Python SDK
|
|
5
|
+
Author: 玄雅 / Synria 科技
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: tactile,pressure,sensor,modbus,serial
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: pyserial>=3.5
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
17
|
+
Requires-Dist: ruff; extra == "dev"
|
|
18
|
+
Requires-Dist: mypy; extra == "dev"
|
|
19
|
+
|
|
20
|
+
# 玄雅 / Synria 科技 触觉压力采集模块 Python SDK
|
|
21
|
+
|
|
22
|
+
> 内部开发文档 · 面向 SDK 维护者与集成开发者
|
|
23
|
+
|
|
24
|
+
基于 Modbus RTU 协议的高性能 Python SDK,支持 60 点触觉压力传感器实时数据采集(实测最高 ~1480 Hz)。
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 目录
|
|
29
|
+
|
|
30
|
+
- [快速开始](#快速开始)
|
|
31
|
+
- [项目结构](#项目结构)
|
|
32
|
+
- [架构详解](#架构详解)
|
|
33
|
+
- [门面层 client.py](#门面层--clientpy)
|
|
34
|
+
- [API 层 api/](#api-层--api)
|
|
35
|
+
- [协议层 protocol/](#协议层--protocol)
|
|
36
|
+
- [传输层 transport/](#传输层--transport)
|
|
37
|
+
- [横切层 models / exceptions](#横切层--models--exceptions)
|
|
38
|
+
- [数据流全链路](#数据流全链路)
|
|
39
|
+
- [完整 API 参考](#完整-api-参考)
|
|
40
|
+
- [连接管理](#连接管理)
|
|
41
|
+
- [sdk.device — 设备信息](#sdkdevice--设备信息)
|
|
42
|
+
- [sdk.config — 参数配置](#sdkconfig--参数配置)
|
|
43
|
+
- [sdk.pressure — 压力读取](#sdkpressure--压力读取)
|
|
44
|
+
- [sdk.calibration — 标定操作](#sdkcalibration--标定操作)
|
|
45
|
+
- [数据模型](#数据模型)
|
|
46
|
+
- [异常体系](#异常体系)
|
|
47
|
+
- [协议常量参考](#协议常量参考)
|
|
48
|
+
- [示例脚本说明](#示例脚本说明)
|
|
49
|
+
- [硬件通信参数](#硬件通信参数)
|
|
50
|
+
- [常见问题](#常见问题)
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 快速开始
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install -r requirements.txt
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from tactile_sdk import TactilePressureSDK
|
|
62
|
+
|
|
63
|
+
with TactilePressureSDK("COM6", slave_address=1) as sdk:
|
|
64
|
+
# 读取设备信息
|
|
65
|
+
info = sdk.device.get_info()
|
|
66
|
+
print(info) # DeviceInfo(model='ST-00-01', ...)
|
|
67
|
+
|
|
68
|
+
# 读取一帧压力数据(标准路径,含完整错误处理)
|
|
69
|
+
sdk.config.set_pressure_value_type(1) # 1=标定值(mN), 0=AD原始值
|
|
70
|
+
frame = sdk.pressure.read_all()
|
|
71
|
+
print(frame.values) # List[int], 长度=60
|
|
72
|
+
print(frame.total_pressure) # 所有点之和
|
|
73
|
+
|
|
74
|
+
# 高频采集路径(不抛异常,失败返回 None)
|
|
75
|
+
values = sdk.pressure.read_fast()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 项目结构
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
Electronic-Skin-ML-main/
|
|
84
|
+
├── tactile_sdk/ ← SDK 主包
|
|
85
|
+
│ ├── __init__.py ← 公开接口出口(用户 import 的总入口)
|
|
86
|
+
│ ├── client.py ← 门面层:TactilePressureSDK
|
|
87
|
+
│ ├── exceptions.py ← 横切层:统一异常类型
|
|
88
|
+
│ ├── models.py ← 横切层:业务数据模型 (dataclass)
|
|
89
|
+
│ │
|
|
90
|
+
│ ├── api/ ← API 层:按业务领域拆分
|
|
91
|
+
│ │ ├── __init__.py
|
|
92
|
+
│ │ ├── base.py ← BaseAPI:持有 modbus + 共享地址
|
|
93
|
+
│ │ ├── device_api.py ← DeviceAPI:设备信息读写、地址管理
|
|
94
|
+
│ │ ├── config_api.py ← ConfigAPI:传感器工作参数配置
|
|
95
|
+
│ │ ├── pressure_api.py ← PressureAPI:压力数据读取
|
|
96
|
+
│ │ └── calibration_api.py ← CalibrationAPI:标定工作流
|
|
97
|
+
│ │
|
|
98
|
+
│ ├── protocol/ ← 协议层:Modbus RTU 帧构建与解析
|
|
99
|
+
│ │ ├── __init__.py
|
|
100
|
+
│ │ ├── constants.py ← 功能码、寄存器地址、枚举常量
|
|
101
|
+
│ │ ├── crc16.py ← CRC16 算法(Modbus 协议规范的一部分)
|
|
102
|
+
│ │ └── modbus_rtu.py ← 帧构建/发送/接收/CRC校验/解析
|
|
103
|
+
│ │
|
|
104
|
+
│ └── transport/ ← 传输层:串口物理 I/O
|
|
105
|
+
│ ├── __init__.py
|
|
106
|
+
│ ├── serial_transport.py ← pyserial 封装,字节级 I/O
|
|
107
|
+
│ └── crc16.py ← 转发模块(向后兼容,实现在 protocol/)
|
|
108
|
+
│
|
|
109
|
+
├── examples/ ← 10 个完整示例脚本
|
|
110
|
+
├── pyproject.toml ← 包元数据与构建配置
|
|
111
|
+
├── requirements.txt ← 依赖(仅 pyserial)
|
|
112
|
+
└── actual.moduluscali.moduluscali.csv ← 出厂标定数据备份
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 架构详解
|
|
118
|
+
|
|
119
|
+
本 SDK 采用**严格四层架构**,每一层只与相邻层通信,职责单一、互不越界。
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
用户代码
|
|
123
|
+
│
|
|
124
|
+
▼
|
|
125
|
+
┌─────────────────────────────────────┐
|
|
126
|
+
│ 门面层 TactilePressureSDK │ client.py
|
|
127
|
+
│ sdk.device / .config / .pressure │
|
|
128
|
+
│ / .calibration │
|
|
129
|
+
└────────────────┬────────────────────┘
|
|
130
|
+
│ 调用
|
|
131
|
+
▼
|
|
132
|
+
┌─────────────────────────────────────┐
|
|
133
|
+
│ API 层 DeviceAPI / ConfigAPI │ api/
|
|
134
|
+
│ PressureAPI / Calibration│
|
|
135
|
+
│ API(继承自 BaseAPI) │
|
|
136
|
+
└────────────────┬────────────────────┘
|
|
137
|
+
│ 调用
|
|
138
|
+
▼
|
|
139
|
+
┌─────────────────────────────────────┐
|
|
140
|
+
│ 协议层 ModbusRTU │ protocol/
|
|
141
|
+
│ 帧构建 · 发送接收 · CRC · 解析 │
|
|
142
|
+
└────────────────┬────────────────────┘
|
|
143
|
+
│ 调用
|
|
144
|
+
▼
|
|
145
|
+
┌─────────────────────────────────────┐
|
|
146
|
+
│ 传输层 SerialTransport │ transport/
|
|
147
|
+
│ 字节级 write / read / flush │
|
|
148
|
+
└─────────────────────────────────────┘
|
|
149
|
+
|
|
150
|
+
横切层(各层均可引用):
|
|
151
|
+
exceptions.py ← 统一异常体系,屏蔽底层 pyserial 异常
|
|
152
|
+
models.py ← 业务数据结构,解耦字节格式与业务含义
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### 门面层 — `client.py`
|
|
158
|
+
|
|
159
|
+
**唯一对外入口**,使用 **Facade 模式**把四个领域 API 组合为一个 `TactilePressureSDK` 对象。
|
|
160
|
+
|
|
161
|
+
`__init__` 中按依赖顺序创建各层实例:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
SerialTransport(port, baudrate, timeout)
|
|
165
|
+
└─→ ModbusRTU(transport, send_wait_secs)
|
|
166
|
+
├─→ DeviceAPI(modbus, addr_ref)
|
|
167
|
+
├─→ ConfigAPI(modbus, addr_ref)
|
|
168
|
+
├─→ PressureAPI(modbus, addr_ref)
|
|
169
|
+
└─→ CalibrationAPI(modbus, addr_ref)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**共享地址容器 `addr_ref`**:四个 API 实例共享同一个单元素列表 `[slave_address]`,`BaseAPI._slave_address` 是操作该列表的 property。因此调用 `sdk.device.set_address(new_addr)` 后,其余三个 API 会**立即同步**,不存在地址不一致问题。
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
# client.py 内部
|
|
176
|
+
self._addr_ref: list = [slave_address]
|
|
177
|
+
self.device = DeviceAPI(self._modbus, self._addr_ref)
|
|
178
|
+
self.config = ConfigAPI(self._modbus, self._addr_ref)
|
|
179
|
+
self.pressure = PressureAPI(self._modbus, self._addr_ref)
|
|
180
|
+
self.calibration = CalibrationAPI(self._modbus, self._addr_ref)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
# base.py 内部
|
|
185
|
+
@property
|
|
186
|
+
def _slave_address(self) -> int:
|
|
187
|
+
return self._addr_ref[0] # 读共享容器
|
|
188
|
+
|
|
189
|
+
@_slave_address.setter
|
|
190
|
+
def _slave_address(self, value: int) -> None:
|
|
191
|
+
self._addr_ref[0] = value # 写共享容器,全局同步
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
### API 层 — `api/`
|
|
197
|
+
|
|
198
|
+
按业务领域分为 4 个类,**只调用 `self._modbus` 的高层方法**,不关心 Modbus 帧结构。
|
|
199
|
+
|
|
200
|
+
| 类 | 文件 | 核心职责 |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| `DeviceAPI` | device_api.py | 读取设备型号/协议版本/固件版本;读写 Modbus 地址 |
|
|
203
|
+
| `ConfigAPI` | config_api.py | 压力值类型、采样频率、AD 屏蔽值、点面积、归零控制 |
|
|
204
|
+
| `PressureAPI` | pressure_api.py | 标准读取(返回 `PressureFrame`)和高频快速读取 |
|
|
205
|
+
| `CalibrationAPI` | calibration_api.py | 拟合点写入、标定模式切换、执行标定、清除标定 |
|
|
206
|
+
|
|
207
|
+
所有子类继承 `BaseAPI`,共享:
|
|
208
|
+
- `self._modbus` — `ModbusRTU` 实例,负责实际通信
|
|
209
|
+
- `self._slave_address` — 当前目标从设备地址(通过共享容器保持同步)
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### 协议层 — `protocol/`
|
|
214
|
+
|
|
215
|
+
**知道 Modbus RTU 协议格式,不知道业务含义,不知道串口参数。**
|
|
216
|
+
|
|
217
|
+
#### `modbus_rtu.py` — 核心流程
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
API 层调用高层方法
|
|
221
|
+
│
|
|
222
|
+
▼
|
|
223
|
+
_build_frame(slave, func, data) → bytes(地址+功能码+数据+CRC)
|
|
224
|
+
│
|
|
225
|
+
▼
|
|
226
|
+
transport.reset_input_buffer() → 清空接收缓冲区
|
|
227
|
+
transport.write(frame) → 发送帧
|
|
228
|
+
│
|
|
229
|
+
▼
|
|
230
|
+
_read_response() 或 _read_response_fast_0x42() → 读取响应字节
|
|
231
|
+
│
|
|
232
|
+
▼
|
|
233
|
+
_parse_response(bytes, expected_func)
|
|
234
|
+
├─ len(response) < 4? → CommunicationError
|
|
235
|
+
├─ CRC 不匹配? → CommunicationError
|
|
236
|
+
├─ 功能码带 0x80 异常掩码? → ProtocolError
|
|
237
|
+
└─ 功能码不一致? → ProtocolError
|
|
238
|
+
│
|
|
239
|
+
▼
|
|
240
|
+
返回数据域 bytes(去掉地址、功能码、CRC)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### 两条读取路径
|
|
244
|
+
|
|
245
|
+
| 路径 | 方法 | 适用功能码 | 特点 |
|
|
246
|
+
|---|---|---|---|
|
|
247
|
+
| 标准路径 | `_read_response()` | 0x03 / 0x06 / 0x41 | 发送后等待 `send_wait_secs`(默认 5ms),动态推断帧长 |
|
|
248
|
+
| 快速路径 | `_read_response_fast_0x42()` | 0x42(压力读取) | 纯轮询无 sleep,最大化吞吐,实测 ~1480 Hz |
|
|
249
|
+
|
|
250
|
+
#### `constants.py` — 协议常量
|
|
251
|
+
|
|
252
|
+
集中管理所有魔法数字,禁止在其他层散落:
|
|
253
|
+
|
|
254
|
+
- `FunctionCode`:`READ_HOLDING_REGISTERS(0x03)` / `WRITE_SINGLE_REGISTER(0x06)` / `READ_SPECIFIC_INFO(0x41)` / `READ_ALL_PRESSURE_VALUES(0x42)`
|
|
255
|
+
- `RegisterAddress`:所有寄存器地址枚举(0x0001–0x0070)
|
|
256
|
+
- `CalibrationMode`:`SINGLE_POINT(100)` / `ALL_POINTS(101)`
|
|
257
|
+
- `InfoIndex`:0x41 信息索引
|
|
258
|
+
- `ExceptionCode`:Modbus 标准异常码
|
|
259
|
+
- 边界常量:`MIN_SLAVE_ADDRESS(1)` / `MAX_SLAVE_ADDRESS(247)` / `BROADCAST_ADDRESS(0)` / `DEFAULT_BAUDRATE(4_000_000)` / `DEFAULT_TIMEOUT(1.0)` / `DEFAULT_SEND_WAIT_SECS(0.005)`
|
|
260
|
+
|
|
261
|
+
#### `crc16.py` — CRC16 算法
|
|
262
|
+
|
|
263
|
+
CRC16 是 Modbus 协议规范的一部分,因此放在 `protocol/` 而非 `transport/`。
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
calculate_crc16(data: bytes) -> bytes # 返回 2 字节小端 CRC
|
|
267
|
+
verify_crc16(data: bytes, crc: bytes) -> bool
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
> `transport/crc16.py` 是向后兼容的转发模块,直接 re-export `protocol/crc16`。
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
### 传输层 — `transport/`
|
|
275
|
+
|
|
276
|
+
**只管字节进出,完全不知道 Modbus 协议。**
|
|
277
|
+
|
|
278
|
+
`SerialTransport` 是对 pyserial `serial.Serial` 的轻量封装:
|
|
279
|
+
|
|
280
|
+
| 方法/属性 | 说明 |
|
|
281
|
+
|---|---|
|
|
282
|
+
| `open()` | 打开串口(已打开时为空操作) |
|
|
283
|
+
| `close()` | 关闭串口 |
|
|
284
|
+
| `is_open` | 属性,bool |
|
|
285
|
+
| `write(data: bytes)` | 发送字节,立即 flush |
|
|
286
|
+
| `read(size: int)` | 从缓冲区读取最多 size 字节 |
|
|
287
|
+
| `reset_input_buffer()` | 清空接收缓冲区 |
|
|
288
|
+
| `in_waiting` | 属性,当前缓冲区可读字节数 |
|
|
289
|
+
|
|
290
|
+
所有 pyserial 的 `SerialException` 都被转换为 `DeviceConnectionError` 或 `CommunicationError`,不向上层暴露底层依赖。
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
### 横切层 — `models` / `exceptions`
|
|
295
|
+
|
|
296
|
+
#### `models.py` — 业务数据模型
|
|
297
|
+
|
|
298
|
+
4 个 `dataclass`,是 API 层与用户代码之间的**数据契约**,使用户拿到类型清晰的对象而非裸字节或裸字典。
|
|
299
|
+
|
|
300
|
+
> `models.py` 不在运行时依赖任何其他模块(`CalibrationMode` 的导入受 `TYPE_CHECKING` 保护),保持模型层独立。
|
|
301
|
+
|
|
302
|
+
#### `exceptions.py` — 统一异常体系
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
TactileSdkError(基类)
|
|
306
|
+
├── DeviceConnectionError 串口打不开 / 连接意外断开
|
|
307
|
+
├── CommunicationError 超时 / CRC 失败 / 帧不完整
|
|
308
|
+
├── ProtocolError 功能码异常 / Modbus 异常响应码
|
|
309
|
+
├── ValidationError 参数越界(地址/频率/压力值等)
|
|
310
|
+
└── CalibrationError 标定操作失败(通常包装上述两种)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
捕获建议:
|
|
314
|
+
- 日常使用:捕获 `TactileSdkError`(一网打尽)
|
|
315
|
+
- 精细处理:按子类分别处理(例如区分连接失败和通信失败)
|
|
316
|
+
- 高频采集:使用 `read_fast()` — 内部吞掉所有异常,失败返回 `None`
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## 数据流全链路
|
|
321
|
+
|
|
322
|
+
以 `sdk.pressure.read_all()` 为例,完整追踪一次调用:
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
1. 用户调用
|
|
326
|
+
sdk.pressure.read_all()
|
|
327
|
+
|
|
328
|
+
2. PressureAPI(api/pressure_api.py)
|
|
329
|
+
ts = time.perf_counter()
|
|
330
|
+
values = self._modbus.read_all_pressure_values(self._slave_address)
|
|
331
|
+
return PressureFrame(values=values, timestamp=ts)
|
|
332
|
+
|
|
333
|
+
3. ModbusRTU(protocol/modbus_rtu.py)
|
|
334
|
+
frame = _build_frame(addr, 0x42, b"") # [addr, 0x42, CRC_lo, CRC_hi]
|
|
335
|
+
transport.reset_input_buffer()
|
|
336
|
+
transport.write(frame) # 发送 4 字节请求帧
|
|
337
|
+
response = _read_response_fast_0x42() # 轮询接收响应
|
|
338
|
+
data_bytes = _parse_response(response, 0x42)
|
|
339
|
+
# 验证 CRC + 功能码
|
|
340
|
+
return struct.unpack("<60H", data_bytes[2:]) # 小端解析 60 个 uint16
|
|
341
|
+
|
|
342
|
+
4. SerialTransport(transport/serial_transport.py)
|
|
343
|
+
serial.Serial.write(frame)
|
|
344
|
+
serial.Serial.flush()
|
|
345
|
+
...(硬件串口通信)...
|
|
346
|
+
serial.Serial.read(n) → bytes
|
|
347
|
+
|
|
348
|
+
5. 设备响应帧结构(0x42)
|
|
349
|
+
[addr][0x42][len_lo][len_hi][value0_lo][value0_hi]...[value59_hi][CRC_lo][CRC_hi]
|
|
350
|
+
1B 1B 2B(小端) 60×2=120 字节数据 2B CRC
|
|
351
|
+
|
|
352
|
+
6. 返回给用户
|
|
353
|
+
PressureFrame(values=[0,0,512,...], timestamp=12345.678)
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## 完整 API 参考
|
|
359
|
+
|
|
360
|
+
### 连接管理
|
|
361
|
+
|
|
362
|
+
```python
|
|
363
|
+
sdk = TactilePressureSDK(
|
|
364
|
+
port="COM6", # 串口名:Windows "COM6",Linux "/dev/ttyUSB0"
|
|
365
|
+
slave_address=1, # Modbus 从设备地址(1–247),默认 1
|
|
366
|
+
baudrate=4_000_000, # 波特率,默认 4,000,000
|
|
367
|
+
timeout=1.0, # 读超时秒数,默认 1.0
|
|
368
|
+
send_wait_secs=0.005, # 发送后等待响应开始的延迟,默认 0.005
|
|
369
|
+
# 低延迟设备可适当减小,提高标准命令速度
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
sdk.connect() # 打开串口(已打开时为空操作)
|
|
373
|
+
sdk.disconnect() # 关闭串口
|
|
374
|
+
sdk.is_connected # 属性 bool,当前串口是否已打开
|
|
375
|
+
|
|
376
|
+
# 推荐用上下文管理器(自动 connect / disconnect)
|
|
377
|
+
with TactilePressureSDK("COM6") as sdk:
|
|
378
|
+
...
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
### `sdk.device` — 设备信息
|
|
384
|
+
|
|
385
|
+
#### 读取设备身份信息
|
|
386
|
+
|
|
387
|
+
```python
|
|
388
|
+
info: DeviceInfo = sdk.device.get_info()
|
|
389
|
+
# 一次调用读取全部(内部发送 4 次 0x41 请求)
|
|
390
|
+
# info.device_model → "ST-00-01"
|
|
391
|
+
# info.protocol_number → "YF-e0-000001"
|
|
392
|
+
# info.protocol_version → "v1.2"
|
|
393
|
+
# info.app_version → "v1.0.1"
|
|
394
|
+
|
|
395
|
+
# 也可单独读取
|
|
396
|
+
sdk.device.get_model() # → str
|
|
397
|
+
sdk.device.get_protocol_number() # → str
|
|
398
|
+
sdk.device.get_protocol_version() # → str
|
|
399
|
+
sdk.device.get_app_version() # → str
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### Modbus 地址管理
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
sdk.device.get_address() → int
|
|
406
|
+
# 从寄存器 0x0001 读取当前 Modbus 地址(1–247)
|
|
407
|
+
|
|
408
|
+
sdk.device.set_address(new_address: int, *, use_broadcast: bool = True)
|
|
409
|
+
# 修改设备 Modbus 地址
|
|
410
|
+
# use_broadcast=True(默认):通过广播地址发送(设备不返回响应)
|
|
411
|
+
# → SDK 内所有 API 实例的地址同步更新
|
|
412
|
+
# use_broadcast=False:通过当前地址发送(有响应确认,但修改后设备立即失效)
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
> **注意**:修改地址后设备立即使用新地址,代码中 `slave_address` 同步更新,无需重新创建 SDK 实例。
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
### `sdk.config` — 参数配置
|
|
420
|
+
|
|
421
|
+
#### 压力值类型
|
|
422
|
+
|
|
423
|
+
```python
|
|
424
|
+
sdk.config.get_pressure_value_type() → int
|
|
425
|
+
sdk.config.set_pressure_value_type(value_type: int)
|
|
426
|
+
# 0 = ADC 原始值(0–65535)
|
|
427
|
+
# 1 = 标定后压力值(mN,需先完成标定)
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
#### AD 屏蔽值
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
sdk.config.get_ad_mask_value() → int
|
|
434
|
+
sdk.config.set_ad_mask_value(mask_value: int)
|
|
435
|
+
# 低于此 AD 值的压力点输出 0(视为无压力)
|
|
436
|
+
# 用于过滤传感器底噪,范围 0–65535
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### 主动上传
|
|
440
|
+
|
|
441
|
+
```python
|
|
442
|
+
sdk.config.get_auto_upload_flag() → bool
|
|
443
|
+
sdk.config.set_auto_upload_flag(enable: bool)
|
|
444
|
+
# 设备主动周期性上报数据(无需主动轮询)
|
|
445
|
+
# 与主动采集(read_fast)二选一使用
|
|
446
|
+
|
|
447
|
+
sdk.config.get_auto_upload_frequency() → int # 50–200 Hz
|
|
448
|
+
sdk.config.set_auto_upload_frequency(frequency: int)
|
|
449
|
+
# 有效范围 50–200 Hz,超出抛 ValidationError
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### 压力点信息
|
|
453
|
+
|
|
454
|
+
```python
|
|
455
|
+
sdk.config.get_pressure_point_count() → int # 只读,典型值 60
|
|
456
|
+
sdk.config.get_sensor_point_area() → float # 单位 mm²,精度 0.1
|
|
457
|
+
sdk.config.set_sensor_point_area(area_mm2: float)
|
|
458
|
+
# 范围 0–6553.5 mm²,精度 0.1 mm²
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
#### 归零控制
|
|
462
|
+
|
|
463
|
+
```python
|
|
464
|
+
sdk.config.get_auto_zero_enable() → Optional[bool]
|
|
465
|
+
# True=已启用,False=已禁用,None=固件不支持读取该寄存器
|
|
466
|
+
sdk.config.set_auto_zero_enable(enable: bool)
|
|
467
|
+
# 上电自动归零使能(只写寄存器),重启后生效
|
|
468
|
+
|
|
469
|
+
sdk.config.trigger_dynamic_zero()
|
|
470
|
+
# 立即将当前压力输出归零(建议无负载时调用)
|
|
471
|
+
|
|
472
|
+
sdk.config.reset_dynamic_zero()
|
|
473
|
+
# 撤销动态归零,恢复出厂零点
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
### `sdk.pressure` — 压力读取
|
|
479
|
+
|
|
480
|
+
SDK 提供两条读取路径,根据使用场景选择:
|
|
481
|
+
|
|
482
|
+
#### 标准路径(含完整错误处理)
|
|
483
|
+
|
|
484
|
+
```python
|
|
485
|
+
frame: PressureFrame = sdk.pressure.read_all()
|
|
486
|
+
# 功能码 0x42,快速路径读取,包装为 PressureFrame
|
|
487
|
+
# frame.values → List[int],长度 = 压力点总数
|
|
488
|
+
# frame.timestamp → float,time.perf_counter() 值
|
|
489
|
+
# frame.point_count → int,压力点数量
|
|
490
|
+
# frame.total_pressure→ int,所有点之和(mN)
|
|
491
|
+
|
|
492
|
+
# 失败时抛出 CommunicationError
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
适用场景:单次读取、调试、需要时间戳、需要明确错误原因。
|
|
496
|
+
|
|
497
|
+
#### 高频快速路径(热循环专用)
|
|
498
|
+
|
|
499
|
+
```python
|
|
500
|
+
values: Optional[List[int]] = sdk.pressure.read_fast()
|
|
501
|
+
# 成功:返回 List[int]
|
|
502
|
+
# 失败:返回 None(内部吞掉所有异常,不打断循环)
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
适用场景:100 Hz+ 连续采集循环,实测最高 ~1480 Hz。
|
|
506
|
+
|
|
507
|
+
```python
|
|
508
|
+
# 典型高频采集模式
|
|
509
|
+
import time
|
|
510
|
+
|
|
511
|
+
TARGET_HZ = 200
|
|
512
|
+
interval = 1.0 / TARGET_HZ
|
|
513
|
+
next_t = time.perf_counter()
|
|
514
|
+
|
|
515
|
+
with TactilePressureSDK("COM6") as sdk:
|
|
516
|
+
sdk.config.set_pressure_value_type(1)
|
|
517
|
+
while True:
|
|
518
|
+
now = time.perf_counter()
|
|
519
|
+
if now < next_t:
|
|
520
|
+
time.sleep(next_t - now)
|
|
521
|
+
values = sdk.pressure.read_fast()
|
|
522
|
+
if values is not None:
|
|
523
|
+
process(values) # 你的处理逻辑
|
|
524
|
+
next_t += interval
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
### `sdk.calibration` — 标定操作
|
|
530
|
+
|
|
531
|
+
标定建立 **ADC 原始值 → 实际压力(mN)** 的映射关系,每个压力点支持最多 **11 个拟合点**(分段线性插值)。
|
|
532
|
+
|
|
533
|
+
#### 标定模式
|
|
534
|
+
|
|
535
|
+
| `CalibrationMode` | 值 | 说明 |
|
|
536
|
+
|---|---|---|
|
|
537
|
+
| `SINGLE_POINT` | 100 | 仅标定当前选中的一个压力点 |
|
|
538
|
+
| `ALL_POINTS` | 101 | 对所有压力点统一应用同一组拟合曲线 |
|
|
539
|
+
|
|
540
|
+
```python
|
|
541
|
+
from tactile_sdk import CalibrationMode
|
|
542
|
+
|
|
543
|
+
sdk.calibration.get_mode() → CalibrationMode
|
|
544
|
+
sdk.calibration.set_mode(CalibrationMode.ALL_POINTS)
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
#### 拟合点与压力点选择
|
|
548
|
+
|
|
549
|
+
```python
|
|
550
|
+
sdk.calibration.get_fitting_point() → int # 当前选中的拟合点编号(1–11)
|
|
551
|
+
sdk.calibration.set_fitting_point(point: int) # 选择拟合点,范围 1–11
|
|
552
|
+
|
|
553
|
+
sdk.calibration.get_pressure_point() → int # 当前选中的压力点(单点模式用)
|
|
554
|
+
sdk.calibration.set_pressure_point(point: int) # 选择要标定的压力点编号
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
#### 执行标定
|
|
558
|
+
|
|
559
|
+
```python
|
|
560
|
+
sdk.calibration.get_fitting_point_ad() → int # 读取当前拟合点已记录的 AD 值
|
|
561
|
+
sdk.calibration.set_fitting_point_pressure(pressure_mn: int)
|
|
562
|
+
# 设置当前拟合点对应的已知压力值(mN),范围 0–65535
|
|
563
|
+
|
|
564
|
+
sdk.calibration.calibrate(*, use_sample: bool = True, ad_value: Optional[int] = None)
|
|
565
|
+
# use_sample=True(默认):写入 65535,设备实时采样 ADC 并记录
|
|
566
|
+
# use_sample=False: 写入 0,使用寄存器中已有的 AD 值
|
|
567
|
+
# ad_value=1500: 直接指定具体 AD 值(精确控制,0–65535)
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
#### 查看当前状态
|
|
571
|
+
|
|
572
|
+
```python
|
|
573
|
+
status: CalibrationStatus = sdk.calibration.get_status()
|
|
574
|
+
# status.mode → CalibrationMode
|
|
575
|
+
# status.pressure_point → int
|
|
576
|
+
# status.fitting_point → int
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
#### 清除标定(不可逆)
|
|
580
|
+
|
|
581
|
+
```python
|
|
582
|
+
sdk.calibration.clear()
|
|
583
|
+
# 清除所有标定数据,恢复出厂状态
|
|
584
|
+
# ⚠️ 清除后如需恢复,运行 06_demo_recover_calibration.py 可由固件还原出厂标定
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
#### 标准标定流程示例
|
|
588
|
+
|
|
589
|
+
```python
|
|
590
|
+
from tactile_sdk import TactilePressureSDK, CalibrationMode
|
|
591
|
+
|
|
592
|
+
with TactilePressureSDK("COM6") as sdk:
|
|
593
|
+
|
|
594
|
+
# —— 全部标定(推荐:对所有 60 个点统一建曲线)——
|
|
595
|
+
sdk.calibration.set_mode(CalibrationMode.ALL_POINTS)
|
|
596
|
+
|
|
597
|
+
fitting_plan = [
|
|
598
|
+
(1, 0), # 拟合点1:施加 0 mN(空载)
|
|
599
|
+
(2, 200), # 拟合点2:施加 200 mN
|
|
600
|
+
(3, 500),
|
|
601
|
+
(4, 1000),
|
|
602
|
+
]
|
|
603
|
+
for fitting_point, pressure_mn in fitting_plan:
|
|
604
|
+
sdk.calibration.set_fitting_point(fitting_point)
|
|
605
|
+
sdk.calibration.set_fitting_point_pressure(pressure_mn)
|
|
606
|
+
input(f"请施加 {pressure_mn} mN 后按 Enter 采样...")
|
|
607
|
+
sdk.calibration.calibrate(use_sample=True)
|
|
608
|
+
ad = sdk.calibration.get_fitting_point_ad()
|
|
609
|
+
print(f"拟合点{fitting_point}: {pressure_mn} mN → AD={ad}")
|
|
610
|
+
|
|
611
|
+
# —— 单点标定(仅标定压力点 #5)——
|
|
612
|
+
sdk.calibration.set_mode(CalibrationMode.SINGLE_POINT)
|
|
613
|
+
sdk.calibration.set_pressure_point(5) # 选定压力点 5
|
|
614
|
+
sdk.calibration.set_fitting_point(1) # 选定拟合点 1
|
|
615
|
+
sdk.calibration.set_fitting_point_pressure(0)
|
|
616
|
+
sdk.calibration.calibrate() # 采样
|
|
617
|
+
|
|
618
|
+
# —— 手动指定 AD 值(离线/回放标定)——
|
|
619
|
+
sdk.calibration.set_fitting_point(2)
|
|
620
|
+
sdk.calibration.set_fitting_point_pressure(1000)
|
|
621
|
+
sdk.calibration.calibrate(ad_value=2284) # 直接写入历史 AD 值
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
#### 出厂标定参考数据
|
|
625
|
+
|
|
626
|
+
存储于 `actual.moduluscali.moduluscali.csv`。出厂标定参数因设备批次/型号而异,如需恢复请运行 `06_demo_recover_calibration.py`,由固件自动还原匹配本设备的出厂参数。
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## 数据模型
|
|
631
|
+
|
|
632
|
+
### `DeviceInfo`
|
|
633
|
+
|
|
634
|
+
```python
|
|
635
|
+
@dataclass
|
|
636
|
+
class DeviceInfo:
|
|
637
|
+
device_model: str # 设备型号,如 "ST-00-01"
|
|
638
|
+
protocol_number: str # 协议编号,如 "YF-e0-000001"
|
|
639
|
+
protocol_version: str # 协议版本,如 "v1.2"
|
|
640
|
+
app_version: str # 固件 App 版本,如 "v1.0.1"
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### `PressureFrame`
|
|
644
|
+
|
|
645
|
+
```python
|
|
646
|
+
@dataclass
|
|
647
|
+
class PressureFrame:
|
|
648
|
+
values: List[int] # 各点压力值,长度 = 压力点总数
|
|
649
|
+
timestamp: Optional[float] # 采样时刻(time.perf_counter()),可为 None
|
|
650
|
+
|
|
651
|
+
# 计算属性
|
|
652
|
+
point_count: int # len(values)
|
|
653
|
+
total_pressure: int # sum(values)
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
### `FittingPoint`
|
|
657
|
+
|
|
658
|
+
```python
|
|
659
|
+
@dataclass
|
|
660
|
+
class FittingPoint:
|
|
661
|
+
index: int # 拟合点编号(1–11)
|
|
662
|
+
pressure_mn: int # 已知压力值(mN)
|
|
663
|
+
ad_value: int # ADC 原始采样值(0–65535)
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### `CalibrationStatus`
|
|
667
|
+
|
|
668
|
+
```python
|
|
669
|
+
@dataclass
|
|
670
|
+
class CalibrationStatus:
|
|
671
|
+
mode: CalibrationMode # SINGLE_POINT 或 ALL_POINTS
|
|
672
|
+
pressure_point: int # 当前选定的压力点编号
|
|
673
|
+
fitting_point: int # 当前选定的拟合点编号(1–11)
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## 异常体系
|
|
679
|
+
|
|
680
|
+
```python
|
|
681
|
+
from tactile_sdk import (
|
|
682
|
+
TactileSdkError, # 基类,捕获所有 SDK 异常
|
|
683
|
+
DeviceConnectionError, # 串口打不开 / 意外断开
|
|
684
|
+
CommunicationError, # 超时 / CRC 失败 / 帧不完整
|
|
685
|
+
ProtocolError, # 功能码异常 / Modbus 异常响应码
|
|
686
|
+
ValidationError, # 参数越界(地址/频率/压力值等)
|
|
687
|
+
CalibrationError, # 标定操作失败(通常包装上述异常)
|
|
688
|
+
)
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
```python
|
|
692
|
+
# 推荐的异常处理模式
|
|
693
|
+
try:
|
|
694
|
+
sdk.connect()
|
|
695
|
+
frame = sdk.pressure.read_all()
|
|
696
|
+
except DeviceConnectionError as e:
|
|
697
|
+
print(f"串口连接失败: {e}") # 检查串口号、驱动
|
|
698
|
+
except CommunicationError as e:
|
|
699
|
+
print(f"通信失败: {e}") # 检查线缆、地址、波特率
|
|
700
|
+
except ProtocolError as e:
|
|
701
|
+
print(f"协议错误: {e}") # 通常是固件版本不匹配
|
|
702
|
+
except ValidationError as e:
|
|
703
|
+
print(f"参数错误: {e}") # 检查传入参数范围
|
|
704
|
+
except TactileSdkError as e:
|
|
705
|
+
print(f"SDK 其他错误: {e}")
|
|
706
|
+
finally:
|
|
707
|
+
sdk.disconnect()
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
---
|
|
711
|
+
|
|
712
|
+
## 协议常量参考
|
|
713
|
+
|
|
714
|
+
```python
|
|
715
|
+
from tactile_sdk.protocol.constants import (
|
|
716
|
+
FunctionCode,
|
|
717
|
+
RegisterAddress,
|
|
718
|
+
CalibrationMode,
|
|
719
|
+
InfoIndex,
|
|
720
|
+
ExceptionCode,
|
|
721
|
+
BROADCAST_ADDRESS, # 0x00,广播地址,写后设备不回复
|
|
722
|
+
MIN_SLAVE_ADDRESS, # 1
|
|
723
|
+
MAX_SLAVE_ADDRESS, # 247
|
|
724
|
+
DEFAULT_BAUDRATE, # 4_000_000
|
|
725
|
+
DEFAULT_TIMEOUT, # 1.0 秒
|
|
726
|
+
DEFAULT_SEND_WAIT_SECS, # 0.005 秒
|
|
727
|
+
CALIBRATION_COMMAND_SAMPLE, # 65535,触发采样
|
|
728
|
+
CLEAR_CALIBRATION_COMMAND, # 119,清除标定
|
|
729
|
+
)
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
### 保持寄存器地址速查
|
|
733
|
+
|
|
734
|
+
| 寄存器地址 | 枚举名 | 读/写 | 说明 |
|
|
735
|
+
|---|---|---|---|
|
|
736
|
+
| 0x0001 | `DEVICE_ADDRESS` | 读/写 | Modbus 从设备地址(1–247) |
|
|
737
|
+
| 0x000B | `AUTO_UPLOAD_FLAG` | 读/写 | 主动上传使能(0/1) |
|
|
738
|
+
| 0x000C | `AUTO_UPLOAD_FREQUENCY` | 读/写 | 主动上传频率(50–200 Hz) |
|
|
739
|
+
| 0x000D | `PRESSURE_VALUE_TYPE` | 读/写 | 输出类型(0=AD,1=mN) |
|
|
740
|
+
| 0x000E | `AD_MASK_VALUE` | 读/写 | AD 屏蔽阈值 |
|
|
741
|
+
| 0x000F | `PRESSURE_POINT_COUNT` | 只读 | 压力点总数(60) |
|
|
742
|
+
| 0x0010 | `SENSOR_POINT_AREA` | 读/写 | 单点面积(×0.1 mm²) |
|
|
743
|
+
| 0x0011 | `PRESSURE_AUTO_ZERO_ENABLE` | 只写 | 上电自动归零使能 |
|
|
744
|
+
| 0x0012 | `PRESSURE_DYNAMIC_ZERO` | 只写 | 1=触发归零,2=重置归零 |
|
|
745
|
+
| 0x0064 | `FITTING_POINT` | 读/写 | 当前拟合点编号(1–11) |
|
|
746
|
+
| 0x0065 | `FITTING_POINT_AD_VALUE` | 只读 | 当前拟合点 AD 值 |
|
|
747
|
+
| 0x0066 | `FITTING_POINT_PRESSURE_VALUE` | 读/写 | 当前拟合点压力值(mN) |
|
|
748
|
+
| 0x0067 | `PRESSURE_POINT` | 读/写 | 当前压力点编号(单点标定) |
|
|
749
|
+
| 0x0068 | `CALIBRATION_MODE` | 读/写 | 标定模式(100/101) |
|
|
750
|
+
| 0x0069 | `CALIBRATION` | 只写 | 标定控制(65535=采样,0=使用已有AD,其他=指定AD) |
|
|
751
|
+
| 0x0070 | `CLEAR_CALIBRATION` | 只写 | 写入 119 清除所有标定 |
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## 示例脚本说明
|
|
756
|
+
|
|
757
|
+
所有示例位于 `examples/`,**串口号统一设为 `COM6`**,运行前请根据实际情况修改顶部的 `PORT` 变量。
|
|
758
|
+
`SLAVE_ADDRESS` 须与设备拨码开关一致(出厂默认为 `1`)。
|
|
759
|
+
|
|
760
|
+
```bash
|
|
761
|
+
cd examples
|
|
762
|
+
python 01_demo_quickstart.py
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
### 01_demo_quickstart.py — 快速入门
|
|
768
|
+
|
|
769
|
+
**适用场景**:初次使用 SDK,验证设备是否正常连接。
|
|
770
|
+
|
|
771
|
+
**运行方式**:运行后自动打印信息并退出。
|
|
772
|
+
|
|
773
|
+
```bash
|
|
774
|
+
python 01_demo_quickstart.py
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
**输出内容**:
|
|
778
|
+
- 设备型号、协议编号/版本、App 固件版本
|
|
779
|
+
- 设备 Modbus 地址、压力点总数、主动上传频率
|
|
780
|
+
- 压力值类型(标定值 / AD 原始值)、传感器点面积、AD 屏蔽值
|
|
781
|
+
|
|
782
|
+
---
|
|
783
|
+
|
|
784
|
+
### 02_demo_calibration.py — 手动输入标定数据
|
|
785
|
+
|
|
786
|
+
**适用场景**:已通过实验/仪器获得各压力点对应的 AD 值,手动写入设备标定表。
|
|
787
|
+
|
|
788
|
+
**运行方式**:交互式输入,确认后写入设备。
|
|
789
|
+
|
|
790
|
+
```bash
|
|
791
|
+
python 02_demo_calibration.py
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
**操作流程**:
|
|
795
|
+
1. 脚本提示逐行输入标定点,格式为 `压力(mN) AD值`,例如:
|
|
796
|
+
```
|
|
797
|
+
标定点 1: 0 10
|
|
798
|
+
标定点 2: 100 559
|
|
799
|
+
标定点 3: 500 1729
|
|
800
|
+
标定点 4: ← 直接回车结束输入
|
|
801
|
+
```
|
|
802
|
+
2. 支持 1–11 个标定点,压力范围 0–1000 mN,AD 值范围 0–65535。
|
|
803
|
+
3. 输入完毕后显示汇总表并确认(`y`),脚本以**全部标定模式**写入所有压力点。
|
|
804
|
+
|
|
805
|
+
**注意**:写入后会覆盖当前标定数据。如需恢复出厂标定,运行 `06_demo_recover_calibration.py`。
|
|
806
|
+
|
|
807
|
+
---
|
|
808
|
+
|
|
809
|
+
### 03_demo_configuration.py — 设备参数配置
|
|
810
|
+
|
|
811
|
+
**适用场景**:调整设备工作参数,或修改 Modbus 地址。
|
|
812
|
+
|
|
813
|
+
**运行方式**:自动执行读写示例,设备地址修改步骤含交互确认。
|
|
814
|
+
|
|
815
|
+
```bash
|
|
816
|
+
python 03_demo_configuration.py
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
**操作内容**:
|
|
820
|
+
1. **读取当前配置**:设备地址、压力值类型、AD 屏蔽值。
|
|
821
|
+
2. **设置压力值类型**:`1` = 标定值(mN),`0` = AD 原始值(调试用)。
|
|
822
|
+
3. **设置 AD 屏蔽值**:低于此阈值的 AD 采样视为无压力(噪声过滤),示例设为 100。
|
|
823
|
+
4. **修改 Modbus 地址**(可选,需手动输入 `y` 确认):将设备地址改为 1–247 之间的新值,修改后须用新地址重新连接。
|
|
824
|
+
|
|
825
|
+
---
|
|
826
|
+
|
|
827
|
+
### 04_demo_read_pressure.py — 高速连续读取
|
|
828
|
+
|
|
829
|
+
**适用场景**:实时监测压力分布,以固定目标频率持续采样。
|
|
830
|
+
|
|
831
|
+
**运行方式**:Ctrl+C 停止。
|
|
832
|
+
|
|
833
|
+
```bash
|
|
834
|
+
python 04_demo_read_pressure.py
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
**行为说明**:
|
|
838
|
+
- 目标采样率 200 Hz(可修改 `TARGET_HZ`),通过 sleep 精确控制帧间隔。
|
|
839
|
+
- 每帧打印长度为 60 的压力值列表。
|
|
840
|
+
- 每秒额外输出一行性能统计:
|
|
841
|
+
```
|
|
842
|
+
>>> 统计: 实际采样率=198.3Hz | 成功=198 | 失败=0 | 错误率=0.00%
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
### 05_demo_record_pressure.py — 数据记录到 CSV
|
|
848
|
+
|
|
849
|
+
**适用场景**:采集一段时间的压力数据用于后续分析。
|
|
850
|
+
|
|
851
|
+
**运行方式**:交互式配置后开始记录,Ctrl+C 可提前停止。
|
|
852
|
+
|
|
853
|
+
```bash
|
|
854
|
+
python 05_demo_record_pressure.py
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
**操作流程**:
|
|
858
|
+
1. 选择采样率:10 / 50 / 100 / 200 Hz 或自定义。
|
|
859
|
+
2. 输入记录时长(秒),或留空后按 Ctrl+C 手动停止。
|
|
860
|
+
3. 输入文件名(默认按时间戳自动生成,如 `pressure_data_20260426_214712.csv`)。
|
|
861
|
+
4. 确认配置摘要后开始记录,实时显示已采集帧数和当前总压力。
|
|
862
|
+
|
|
863
|
+
**CSV 格式**:
|
|
864
|
+
```
|
|
865
|
+
时间戳, 相对时间(秒), 压力点1, 压力点2, ..., 压力点60
|
|
866
|
+
1746000000.123, 0.000, 0, 128, 256, ...
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
### 06_demo_recover_calibration.py — 恢复出厂标定
|
|
872
|
+
|
|
873
|
+
**适用场景**:标定数据被破坏或自定义标定有误,需恢复出厂基线。
|
|
874
|
+
|
|
875
|
+
**运行方式**:含安全确认提示,确认后执行。
|
|
876
|
+
|
|
877
|
+
```bash
|
|
878
|
+
python 06_demo_recover_calibration.py
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
**行为说明**:
|
|
882
|
+
1. 读取恢复**前**各拟合点的 AD 值,供对比。
|
|
883
|
+
2. 显示 ⚠️ 警告并等待用户输入 `y` 确认。
|
|
884
|
+
3. 向固件发送恢复命令(寄存器 `0x0070` ← 119),由固件自动将 ADC–mN 对应关系还原为出厂烧录参数。
|
|
885
|
+
4. 读取恢复**后**各拟合点的 AD 值,显示变更数量。
|
|
886
|
+
|
|
887
|
+
**优势**:恢复逻辑在固件内完成,Python 无需知道具体参数值,对任何批次/型号的设备均有效。
|
|
888
|
+
|
|
889
|
+
**注意**:此操作不可逆,运行后将覆盖当前所有自定义标定数据。
|
|
890
|
+
|
|
891
|
+
---
|
|
892
|
+
|
|
893
|
+
### 07_demo_test_fps.py — 最大采样率压测
|
|
894
|
+
|
|
895
|
+
**适用场景**:摸底当前硬件条件(串口芯片、USB 延迟、CPU 负载)下的 FPS 上限。
|
|
896
|
+
|
|
897
|
+
**运行方式**:Ctrl+C 停止。
|
|
898
|
+
|
|
899
|
+
```bash
|
|
900
|
+
python 07_demo_test_fps.py
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
**行为说明**:
|
|
904
|
+
- 以全速无限循环调用 `pressure.read_fast()`,不做任何限速。
|
|
905
|
+
- 每秒打印一次性能统计:
|
|
906
|
+
```
|
|
907
|
+
实际频率: 1480.2 Hz | 点数: 60 | 成功: 1480 | 失败: 0 | 错误率: 0.00%
|
|
908
|
+
```
|
|
909
|
+
- 与 `04_demo_read_pressure.py` 的区别:本脚本不限速(测极限),04 脚本固定目标频率(实际采集用)。
|
|
910
|
+
|
|
911
|
+
---
|
|
912
|
+
|
|
913
|
+
### 08_demo_zero_baseline.py — 手动动态归零
|
|
914
|
+
|
|
915
|
+
**适用场景**:在设备运行中消除当前基线偏移,效果等同于重新插拔。执行前建议确保传感器表面无负载。
|
|
916
|
+
|
|
917
|
+
**运行方式**:含交互提示。
|
|
918
|
+
|
|
919
|
+
```bash
|
|
920
|
+
python 08_demo_zero_baseline.py
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
**流程**:
|
|
924
|
+
1. 打印归零前全部 60 点压力值及总压力
|
|
925
|
+
2. 按回车触发 `trigger_dynamic_zero()`:发送硬件命令 → 等待 90ms → 读取 60 点存为软件基线
|
|
926
|
+
3. 打印归零后全部 60 点压力值(预期全为 0)
|
|
927
|
+
4. 后续所有读取自动逐点减去该基线(`output[i] = max(0, raw[i] - baseline[i])`)
|
|
928
|
+
|
|
929
|
+
**注意**:软件基线仅在当次 SDK 连接生命周期内有效,断开重连后自动清除。如需撤销归零,运行 `09_demo_baseline_initialization.py`。
|
|
930
|
+
|
|
931
|
+
---
|
|
932
|
+
|
|
933
|
+
### 09_demo_baseline_initialization.py — 重置动态归零
|
|
934
|
+
|
|
935
|
+
**适用场景**:撤销之前的动态归零,将压力值恢复为上电时的硬件零点状态(原始偏移量)。
|
|
936
|
+
|
|
937
|
+
**运行方式**:直接运行,无交互提示。
|
|
938
|
+
|
|
939
|
+
```bash
|
|
940
|
+
python 09_demo_baseline_initialization.py
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
**流程**:
|
|
944
|
+
1. 打印重置前总压力(当前归零后状态)
|
|
945
|
+
2. 调用 `reset_dynamic_zero()`:发送硬件命令 + 立即清除 Python 软件基线
|
|
946
|
+
3. 打印重置后总压力(已恢复为上电硬件基线的原始偏移量)
|
|
947
|
+
|
|
948
|
+
**注意**:重置后压力值会恢复到上电时固件自动归零后的硬件基线状态,而非完全原始的 AD 零点。
|
|
949
|
+
|
|
950
|
+
---
|
|
951
|
+
|
|
952
|
+
### 10_demo_read_calibration.py — 读取当前标定参数
|
|
953
|
+
|
|
954
|
+
**适用场景**:查看设备当前存储的 11 个拟合点标定参数,用于验证标定是否正确写入或被意外覆盖。
|
|
955
|
+
|
|
956
|
+
**运行方式**:直接运行,无交互提示。
|
|
957
|
+
|
|
958
|
+
```bash
|
|
959
|
+
python 10_demo_read_calibration.py
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
**输出内容**:
|
|
963
|
+
- `pressure_value_type`:当前输出模式(0 = AD 原始值,1 = 标定值 mN)
|
|
964
|
+
- 11 个拟合点各自的 AD 值与对应压力值(mN)
|
|
965
|
+
|
|
966
|
+
**示例输出**:
|
|
967
|
+
```
|
|
968
|
+
pressure_value_type = 1 (0=AD 值, 1=标定值 mN)
|
|
969
|
+
|
|
970
|
+
点位 AD 值 pressure (mN)
|
|
971
|
+
------------------------------------
|
|
972
|
+
point 1: AD = 10, pressure = 0 mN
|
|
973
|
+
point 2: AD = 559, pressure = 100 mN
|
|
974
|
+
...
|
|
975
|
+
point 11: AD = 2284, pressure = 1000 mN
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
**注意**:读取的是设备可读写存储区(Flash/EEPROM)里的当前值。运行 `06_demo_recover_calibration.py` 或固件 `clear()` 命令会覆盖这些值;真实出厂标定数据备份在 `actual.moduluscali.moduluscali.csv`。
|
|
979
|
+
|
|
980
|
+
---
|
|
981
|
+
|
|
982
|
+
> `examples/sdk/` 目录是旧版单文件 SDK(历史遗留),已被本包替代,**仅保留作参考,不应在新代码中使用**。
|
|
983
|
+
|
|
984
|
+
---
|
|
985
|
+
|
|
986
|
+
## 硬件通信参数
|
|
987
|
+
|
|
988
|
+
| 参数 | 值 |
|
|
989
|
+
|------|---|
|
|
990
|
+
| 通信协议 | Modbus RTU |
|
|
991
|
+
| 波特率 | 4,000,000 bps |
|
|
992
|
+
| 数据位 | 8 |
|
|
993
|
+
| 校验位 | 无 (None) |
|
|
994
|
+
| 停止位 | 1 |
|
|
995
|
+
| 从设备地址范围 | 1–247 |
|
|
996
|
+
| 广播地址 | 0(写地址时使用,设备不返回响应) |
|
|
997
|
+
| 压力点数量 | 60 |
|
|
998
|
+
| 压力数据格式 | uint16 × 60,小端序(每帧 120 字节数据域) |
|
|
999
|
+
| ADC 分辨率 | 12 位(0–4095)/ 16 位(0–65535)视固件 |
|
|
1000
|
+
| 实测最大采样率 | ~1480 Hz(read_fast 全速无限制) |
|
|
1001
|
+
| 推荐采样率 | 100–200 Hz |
|
|
1002
|
+
|
|
1003
|
+
---
|
|
1004
|
+
|
|
1005
|
+
## 常见问题
|
|
1006
|
+
|
|
1007
|
+
**Q:串口拒绝访问(PermissionError / Access Denied)?**
|
|
1008
|
+
> 其他程序(串口调试助手、另一个 Python 进程)已占用该串口。关闭后重试。
|
|
1009
|
+
|
|
1010
|
+
**Q:压力值全为 0?**
|
|
1011
|
+
> 1. 确认输出类型:`sdk.config.set_pressure_value_type(1)` 切换为标定值模式。
|
|
1012
|
+
> 2. 检查 AD 屏蔽值:`sdk.config.get_ad_mask_value()` 若过高会过滤小信号。
|
|
1013
|
+
> 3. 运行 `06_demo_recover_calibration.py` 恢复出厂标定后再测试。
|
|
1014
|
+
|
|
1015
|
+
**Q:实际采样率达不到目标?**
|
|
1016
|
+
> 1. 使用 `sdk.pressure.read_fast()` 而非 `read_all()`。
|
|
1017
|
+
> 2. 减少循环内的打印、写文件等 I/O 操作(用环形缓冲后处理)。
|
|
1018
|
+
> 3. 确认波特率为 4,000,000。
|
|
1019
|
+
> 4. 使用 FTDI 芯片的 USB 转串口适配器(避免 CH340 的延迟问题)。
|
|
1020
|
+
> 5. 如响应延迟较低,可减小 `send_wait_secs`:`TactilePressureSDK("COM6", send_wait_secs=0.002)`。
|
|
1021
|
+
|
|
1022
|
+
**Q:修改设备地址后连不上?**
|
|
1023
|
+
> `set_address()` 会同步更新 SDK 内部所有 API 实例的地址,无需重新创建实例。若已断开连接,重新连接时传入新地址即可。
|
|
1024
|
+
|
|
1025
|
+
**Q:标定后压力仍不准?**
|
|
1026
|
+
> 先运行 `06_demo_recover_calibration.py` 恢复出厂标定,确认基线准确后再执行自定义标定。
|
|
1027
|
+
|
|
1028
|
+
**Q:`get_auto_zero_enable()` 返回 `None`?**
|
|
1029
|
+
> 该寄存器(0x0011)在部分固件版本中为只写,读取时设备返回 `ILLEGAL_DATA_ADDRESS` 异常码,SDK 捕获后返回 `None`。这是正常行为,不影响 `set_auto_zero_enable()` 的写入功能。
|
|
1030
|
+
|
|
1031
|
+
**Q:如何向协议层添加新功能码?**
|
|
1032
|
+
> 1. 在 `protocol/constants.py` 的 `FunctionCode` 和 `RegisterAddress` 中添加常量。
|
|
1033
|
+
> 2. 在 `protocol/modbus_rtu.py` 中添加对应的公共方法(参照 `read_holding_registers` 的模式)。
|
|
1034
|
+
> 3. 在对应的 `api/*.py` 中添加高层业务方法调用协议层新方法。
|
|
1035
|
+
> 4. 如需对外暴露新数据结构,在 `models.py` 添加 dataclass,并在 `__init__.py` 的 `__all__` 中导出。
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
---
|
|
1039
|
+
|
|
1040
|
+
## 目录
|
|
1041
|
+
|
|
1042
|
+
- [主要特性](#主要特性)
|
|
1043
|
+
- [系统要求](#系统要求)
|
|
1044
|
+
- [安装](#安装)
|
|
1045
|
+
- [快速开始](#快速开始)
|
|
1046
|
+
- [示例脚本](#示例脚本)
|
|
1047
|
+
- [API 文档](#api-文档)
|
|
1048
|
+
- [设备与通信参数](#设备与通信参数)
|
|
1049
|
+
- [常见问题](#常见问题)
|
|
1050
|
+
|
|
1051
|
+
---
|
|
1052
|
+
|
|
1053
|
+
## 主要特性
|
|
1054
|
+
|
|
1055
|
+
- **高速采集**:`read_pressure_fast()` 实测最高 ~1480 Hz,200 Hz 精确控制零失帧
|
|
1056
|
+
- **完整功能**:设备信息读取、参数配置、传感器标定、归零、数据记录
|
|
1057
|
+
- **易用接口**:支持上下文管理器(`with` 语句)自动管理连接
|
|
1058
|
+
- **可靠通信**:Modbus RTU 协议 + CRC16 校验,确保数据完整性
|
|
1059
|
+
- **跨平台**:支持 Windows、Linux、macOS
|
|
1060
|
+
|
|
1061
|
+
---
|
|
1062
|
+
|
|
1063
|
+
## 系统要求
|
|
1064
|
+
|
|
1065
|
+
- Python 3.7+
|
|
1066
|
+
- pyserial
|
|
1067
|
+
|
|
1068
|
+
---
|
|
1069
|
+
|
|
1070
|
+
## 安装
|
|
1071
|
+
|
|
1072
|
+
```bash
|
|
1073
|
+
pip install -r requirements.txt
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
---
|
|
1077
|
+
|
|
1078
|
+
## 快速开始
|
|
1079
|
+
|
|
1080
|
+
### 1. 确认串口号
|
|
1081
|
+
|
|
1082
|
+
| 操作系统 | 串口名称示例 |
|
|
1083
|
+
|---------|------------|
|
|
1084
|
+
| Windows | `COM3`、`COM6` 等(设备管理器查看) |
|
|
1085
|
+
| Linux | `/dev/ttyUSB0`、`/dev/ttyACM0` |
|
|
1086
|
+
| macOS | `/dev/tty.usbserial-*` |
|
|
1087
|
+
|
|
1088
|
+
### 2. 连接设备并读取信息
|
|
1089
|
+
|
|
1090
|
+
```python
|
|
1091
|
+
from sdk import TactilePressureSDK
|
|
1092
|
+
|
|
1093
|
+
with TactilePressureSDK(port="COM6", slave_address=1) as sdk:
|
|
1094
|
+
info = sdk.get_device_info()
|
|
1095
|
+
print(f"设备型号: {info['device_model']}")
|
|
1096
|
+
print(f"压力点数: {sdk.get_pressure_point_count()}") # 60
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
### 3. 读取一帧压力数据
|
|
1100
|
+
|
|
1101
|
+
```python
|
|
1102
|
+
with TactilePressureSDK(port="COM6", slave_address=1) as sdk:
|
|
1103
|
+
sdk.set_pressure_value_type(1) # 1=标定值(mN),0=AD 原始值
|
|
1104
|
+
values = sdk.read_pressure_fast() # 返回长度为 60 的列表,失败返回 None
|
|
1105
|
+
print(values)
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
### 4. 以 200 Hz 精确采集
|
|
1109
|
+
|
|
1110
|
+
```python
|
|
1111
|
+
import time
|
|
1112
|
+
from sdk import TactilePressureSDK
|
|
1113
|
+
|
|
1114
|
+
with TactilePressureSDK(port="COM6", slave_address=1) as sdk:
|
|
1115
|
+
sdk.set_pressure_value_type(1)
|
|
1116
|
+
interval = 1.0 / 200.0
|
|
1117
|
+
next_t = time.perf_counter()
|
|
1118
|
+
|
|
1119
|
+
while True:
|
|
1120
|
+
now = time.perf_counter()
|
|
1121
|
+
if now < next_t:
|
|
1122
|
+
time.sleep(next_t - now)
|
|
1123
|
+
data = sdk.read_pressure_fast()
|
|
1124
|
+
if data is not None:
|
|
1125
|
+
print(data) # 60 个压力点(mN)
|
|
1126
|
+
next_t += interval
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
---
|
|
1130
|
+
|
|
1131
|
+
## 示例脚本
|
|
1132
|
+
|
|
1133
|
+
所有示例位于 `examples/` 目录,**串口号已统一设置为 `COM6`**,请根据实际情况修改。
|
|
1134
|
+
|
|
1135
|
+
| 文件名 | 功能简介 |
|
|
1136
|
+
|--------|--------|
|
|
1137
|
+
| 01_demo_quickstart.py | 连接设备,读取设备信息和基本配置寄存器 |
|
|
1138
|
+
| 02_demo_calibration.py | 传感器标定:单点标定、多拟合点全部标定、清除标定 |
|
|
1139
|
+
| 03_demo_configuration.py | 读取并修改设备配置:压力值类型、AD 屏蔽值、设备地址 |
|
|
1140
|
+
| 04_demo_read_pressure.py | 以 200 Hz 精确采样,连续打印 60 点压力数组,每秒统计采样率 |
|
|
1141
|
+
| 05_demo_record_pressure.py | 交互式配置采样率和时长,将压力数据保存为 CSV 文件 |
|
|
1142
|
+
| 06_demo_recover_calibration.py | 向固件发送恢复命令,由设备自动还原出厂标定参数(兼容任意批次/型号) |
|
|
1143
|
+
| 07_demo_test_fps.py | 全速压测,测试当前硬件条件下的最大实际采样率(实测 ~1480 Hz) |
|
|
1144
|
+
| 08_demo_zero_baseline.py | 手动动态归零:触发后将当前 60 点存为软件基线,后续读取自动补偿 |
|
|
1145
|
+
| 09_demo_baseline_initialization.py | 重置动态归零:清除软件基线,压力值恢复为上电时的硬件零点状态 |
|
|
1146
|
+
|
|
1147
|
+
### 运行方式
|
|
1148
|
+
|
|
1149
|
+
```bash
|
|
1150
|
+
cd examples
|
|
1151
|
+
python 01_demo_quickstart.py
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
> **注意**:04、07 为持续运行脚本,按 `Ctrl+C` 停止;05、08 包含交互式提示。
|
|
1155
|
+
|
|
1156
|
+
---
|
|
1157
|
+
|
|
1158
|
+
## API 文档
|
|
1159
|
+
|
|
1160
|
+
### 连接管理
|
|
1161
|
+
|
|
1162
|
+
```python
|
|
1163
|
+
sdk = TactilePressureSDK(port, slave_address, baudrate=4000000, timeout=1.0)
|
|
1164
|
+
sdk.connect() # 连接设备
|
|
1165
|
+
sdk.disconnect() # 断开连接
|
|
1166
|
+
sdk.is_connected() # 返回 bool
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
### 设备信息
|
|
1170
|
+
|
|
1171
|
+
| 方法 | 返回类型 | 说明 |
|
|
1172
|
+
|------|---------|------|
|
|
1173
|
+
| `get_device_info()` | dict | 包含型号、协议编号/版本、App 版本 |
|
|
1174
|
+
| `get_device_model()` | str | 设备型号 |
|
|
1175
|
+
| `get_protocol_number()` | str | 协议编号 |
|
|
1176
|
+
| `get_protocol_version()` | str | 协议版本 |
|
|
1177
|
+
| `get_app_version()` | str | App 版本 |
|
|
1178
|
+
|
|
1179
|
+
### 压力值读取
|
|
1180
|
+
|
|
1181
|
+
#### `read_pressure_fast()` 推荐
|
|
1182
|
+
|
|
1183
|
+
```python
|
|
1184
|
+
values = sdk.read_pressure_fast()
|
|
1185
|
+
# 成功:返回 List[int],长度 = 压力点总数(60)
|
|
1186
|
+
# 失败:返回 None,不抛异常
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
适用于生产环境和高频采集。
|
|
1190
|
+
|
|
1191
|
+
#### `read_all_pressure_values()`
|
|
1192
|
+
|
|
1193
|
+
```python
|
|
1194
|
+
values = sdk.read_all_pressure_values()
|
|
1195
|
+
# 成功:返回 List[int]
|
|
1196
|
+
# 失败:抛出 ModbusRTUError
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
适用于开发调试,需要详细错误信息时使用。
|
|
1200
|
+
|
|
1201
|
+
### 设备配置
|
|
1202
|
+
|
|
1203
|
+
| 方法 | 说明 | 参数 |
|
|
1204
|
+
|------|------|------|
|
|
1205
|
+
| `get/set_pressure_value_type(type)` | 压力值输出类型 | 0=AD 原始值,1=标定值(mN) |
|
|
1206
|
+
| `get/set_ad_mask_value(value)` | AD 屏蔽阈值,低于此值视为无压力 | 0~4095 |
|
|
1207
|
+
| `get/set_auto_upload_flag(enable)` | 主动上传开关 | True / False |
|
|
1208
|
+
| `get/set_auto_upload_frequency(freq)` | 主动上传频率 | 50~200 Hz |
|
|
1209
|
+
| `get/set_sensor_point_area(area)` | 传感器点面积 | 单位:mm² |
|
|
1210
|
+
| `get_pressure_point_count()` | 压力点总数(只读) | 如 60 |
|
|
1211
|
+
| `get/set_device_address(addr)` | Modbus 从设备地址 | 1~247(修改需广播) |
|
|
1212
|
+
|
|
1213
|
+
### 标定功能
|
|
1214
|
+
|
|
1215
|
+
每个压力点支持最多 **11 个拟合点**(索引 1~11)。
|
|
1216
|
+
|
|
1217
|
+
#### 标定模式
|
|
1218
|
+
|
|
1219
|
+
| 模式值 | 含义 |
|
|
1220
|
+
|--------|------|
|
|
1221
|
+
| 100 | 单点标定:只标定当前选中的压力点 |
|
|
1222
|
+
| 101 | 全部标定:所有压力点应用同一组拟合曲线 |
|
|
1223
|
+
|
|
1224
|
+
#### 标定流程示例
|
|
1225
|
+
|
|
1226
|
+
```python
|
|
1227
|
+
# 单点标定:对压力点1的拟合点1标定 1000 mN
|
|
1228
|
+
sdk.set_calibration_mode(100)
|
|
1229
|
+
sdk.set_pressure_point(1)
|
|
1230
|
+
sdk.set_fitting_point(1)
|
|
1231
|
+
sdk.set_fitting_point_pressure_value(1000) # 施加已知压力后执行
|
|
1232
|
+
sdk.calibrate(use_sample=True) # 采样当前 AD 值并写入
|
|
1233
|
+
ad = sdk.get_fitting_point_ad_value() # 读回确认
|
|
1234
|
+
```
|
|
1235
|
+
|
|
1236
|
+
```python
|
|
1237
|
+
# 全部标定:对所有压力点写入多个拟合点
|
|
1238
|
+
sdk.set_calibration_mode(101)
|
|
1239
|
+
for fitting_point, pressure_mN in [(1,0),(2,500),(3,1000),(4,2000)]:
|
|
1240
|
+
sdk.set_fitting_point(fitting_point)
|
|
1241
|
+
sdk.set_fitting_point_pressure_value(pressure_mN)
|
|
1242
|
+
sdk.calibrate(use_sample=True)
|
|
1243
|
+
```
|
|
1244
|
+
|
|
1245
|
+
#### 标定相关方法
|
|
1246
|
+
|
|
1247
|
+
| 方法 | 说明 |
|
|
1248
|
+
|------|------|
|
|
1249
|
+
| `get/set_calibration_mode(mode)` | 标定模式(100 单点 / 101 全部) |
|
|
1250
|
+
| `get/set_pressure_point(point)` | 当前操作的压力点编号 |
|
|
1251
|
+
| `get/set_fitting_point(point)` | 当前操作的拟合点编号(1~11) |
|
|
1252
|
+
| `set_fitting_point_pressure_value(mN)` | 设置该拟合点对应的压力值 |
|
|
1253
|
+
| `get_fitting_point_ad_value()` | 读取该拟合点的 AD 值 |
|
|
1254
|
+
| `calibrate(use_sample, ad_value)` | 执行标定;use_sample=True 使用采样值 |
|
|
1255
|
+
| `clear_calibration()` | 清除所有标定,恢复出厂状态(不可逆) |
|
|
1256
|
+
|
|
1257
|
+
#### 出厂标定参考数据
|
|
1258
|
+
|
|
1259
|
+
存储于 `actual.moduluscali.moduluscali.csv`。出厂标定参数因设备批次/型号而异,如需恢复请运行 `06_demo_recover_calibration.py`,由固件自动还原匹配本设备的出厂参数。
|
|
1260
|
+
|
|
1261
|
+
### 归零功能
|
|
1262
|
+
|
|
1263
|
+
| 方法 | 说明 | 注意 |
|
|
1264
|
+
|------|------|------|
|
|
1265
|
+
| `config.trigger_dynamic_zero()` | 触发动态归零;采集 60 点软件基线,后续读取均自动逐点扣减 | 建议无负载时执行;通过 Python 层实现,效果等同重新插拔 |
|
|
1266
|
+
| `config.reset_dynamic_zero()` | 清除软件基线,恢复为上电硬件零点状态 | 即时生效 |
|
|
1267
|
+
|
|
1268
|
+
> 上电自动归零为固件强制行为,每次上电自动执行,无对应 SDK 控制方法。
|
|
1269
|
+
|
|
1270
|
+
---
|
|
1271
|
+
|
|
1272
|
+
## 设备与通信参数
|
|
1273
|
+
|
|
1274
|
+
| 参数 | 值 |
|
|
1275
|
+
|------|---|
|
|
1276
|
+
| 通信协议 | Modbus RTU |
|
|
1277
|
+
| 波特率 | 4,000,000 bps |
|
|
1278
|
+
| 数据位 | 8 |
|
|
1279
|
+
| 校验位 | 无 |
|
|
1280
|
+
| 停止位 | 1 |
|
|
1281
|
+
| 从设备地址范围 | 1~247 |
|
|
1282
|
+
| 广播地址 | 0(用于修改设备地址) |
|
|
1283
|
+
| 压力点数量 | 60 |
|
|
1284
|
+
| 压力数据类型 | uint16(2字节/点) |
|
|
1285
|
+
| AD 分辨率 | 12位(0~4095) |
|
|
1286
|
+
| 实测最大采样率 | ~1480 Hz(全速无限制) |
|
|
1287
|
+
| 推荐采样率 | 100~200 Hz |
|
|
1288
|
+
|
|
1289
|
+
---
|
|
1290
|
+
|
|
1291
|
+
## 常见问题
|
|
1292
|
+
|
|
1293
|
+
**Q:串口拒绝访问(PermissionError)?**
|
|
1294
|
+
> 其他程序已占用该串口。关闭串口调试工具或其他 Python 脚本后重试。
|
|
1295
|
+
|
|
1296
|
+
**Q:压力值全为 0?**
|
|
1297
|
+
> 1. 确认压力值类型:`set_pressure_value_type(1)` 切换为标定值模式。
|
|
1298
|
+
> 2. 检查 AD 屏蔽值 `get_ad_mask_value()`,过高会过滤小压力信号。
|
|
1299
|
+
> 3. 运行 `06_demo_recover_calibration.py` 恢复出厂标定后重试。
|
|
1300
|
+
|
|
1301
|
+
**Q:实际采样率达不到目标?**
|
|
1302
|
+
> 1. 使用 `read_pressure_fast()` 而非 `read_all_pressure_values()`。
|
|
1303
|
+
> 2. 减少循环内的打印、写文件等耗时操作。
|
|
1304
|
+
> 3. 确认波特率为 4,000,000。
|
|
1305
|
+
> 4. 使用高性能 USB 转串口适配器(推荐 FTDI 芯片)。
|
|
1306
|
+
|
|
1307
|
+
**Q:修改设备地址后连不上?**
|
|
1308
|
+
> 将代码中 `slave_address` 改为新地址后重新连接,原地址立即失效。
|
|
1309
|
+
|
|
1310
|
+
**Q:标定后压力仍不准?**
|
|
1311
|
+
> 先运行 `06_demo_recover_calibration.py` 恢复出厂标定,再重新执行自定义标定流程。
|