feat: update examples and README with streaming support and uint32 force types

- Change CForce3D fx/fy/fz from int16 to uint32 to match hardware
- Add independent C++ example with command and streaming modes
- Rewrite Python example with threaded streaming (read_loop + consumer pattern)
- Add ROS2 C++ publisher/subscriber examples
- Update README with streaming APIs, ROS2 docs, data type definitions, and full FFI table
- Add CHANGELOG
This commit is contained in:
lenn
2026-05-08 17:41:46 +08:00
parent c195234771
commit 705375085f
15 changed files with 903 additions and 149 deletions

152
README.md
View File

@@ -1,6 +1,6 @@
# Eskin Finger SDK
E-Skin 手指力传感器 Rust SDK提供串口通信、寄存器读写、流式采集能力支持 Rust / C/C++ / Python 调用。
E-Skin 手指力传感器 Rust SDK提供串口通信、寄存器读写、流式采集能力支持 Rust / C/C++ / Python / ROS2 调用。
## 目录结构
@@ -22,8 +22,10 @@ include/
eskin_ffi.h — C/C++ 头文件
example/
cpp/main.cpp C++ 使用示例
python/ — Python 使用示例
cpp/main.cpp — 独立 C++ 使用示例(含流式采集)
python/eskin_ffi.py — Python FFI 包装器
python/example.py — Python 使用示例(含流式采集)
ros-cpp/ — ROS2 C++ 示例publisher/subscriber
```
---
@@ -51,6 +53,20 @@ println!("Wrote {} bytes", count);
device.close().unwrap();
```
### 流式采集Rust
```rust
// 启动流式采集
device.start_stream().unwrap();
// 读取采样数据
let sample = device.read_sample(Some(std::time::Duration::from_millis(200))).unwrap();
println!("force: fx={} fy={} fz={}", sample.force.fx, sample.force.fy, sample.force.fz);
// 停止采集
device.stop_stream().unwrap();
```
---
## 使用 C/C++
@@ -69,26 +85,46 @@ cargo build --release
### 编译 C++ 示例
```bash
g++ example/cpp/main.cpp -I include -L target/release -leskin_finger_sdk -o example_cpp
g++ -std=c++17 example/cpp/main.cpp -I include -L target/release -leskin_finger_sdk -lpthread -o example_cpp
LD_LIBRARY_PATH=target/release ./example_cpp
```
### C++ 代码示例
完整示例见 `example/cpp/main.cpp`,包含 Command 模式和 Streaming 模式:
```cpp
#include "eskin_ffi.h"
#include <cstdio>
#include <cstring>
#include <thread>
#include <mutex>
#include <queue>
int main() {
// 打开设备
EskinDeviceHandle dev = eskin_open("/dev/ttyUSB0", nullptr);
if (!dev) return 1;
uint8_t buf[256];
uint32_t actual;
if (eskin_read_register(dev, 0x0000, 4, buf, sizeof(buf), &actual) == ESkinSuccess) {
printf("Serial: %02X %02X %02X %02X\n", buf[0], buf[1], buf[2], buf[3]);
// Command 模式:读取设备信息
char hw_buf[64] = {};
uint32_t hw_len = 0;
eskin_read_hdw_version(dev, hw_buf, sizeof(hw_buf), &hw_len);
printf("Hardware version: %.*s\n", (int)hw_len, hw_buf);
// Streaming 模式:持续采集力数据
eskin_start_stream(dev);
CFingerSample sample;
memset(&sample, 0, sizeof(sample));
if (eskin_read_sample(dev, 200, &sample) == ESkinSuccess) {
printf("force: fx=%u fy=%u fz=%u\n",
sample.combined_force.force.fx,
sample.combined_force.force.fy,
sample.combined_force.force.fz);
}
eskin_stop_stream(dev);
eskin_close(dev);
return 0;
}
@@ -108,26 +144,80 @@ cd example/python
python3 example.py
```
> **注意:** Python 示例默认从当前目录加载 `libeskin_finger_sdk.so`,请确保 `.so` 文件已复制到 `example/python/` 目录下,或修改 `example.py` 中的 `LIB_PATH` 指向正确的路径。
> **注意:** Python 示例默认从当前目录加载 `libeskin_finger_sdk.so`,请确保 `.so` 文件已复制到 `example/python/` 目录下,或修改 `eskin_ffi.py` 中的 `LIB_PATH` 指向正确的路径。
### Python 代码示例
完整示例见 `example/python/example.py`,包含 Command 模式和 Streaming 模式:
```python
from eskin_ffi import EskinDevice
with EskinDevice("libeskin_finger_sdk.so") as dev:
with EskinDevice() as dev:
dev.open("/dev/ttyUSB0")
# 读取硬件版本
# Command 模式:读取设备信息
print(f"Hardware version: {dev.read_hdw_version()}")
# 读取矩阵尺寸
print(f"Matrix: {dev.read_matrix_row()} x {dev.read_matrix_col()}")
# 读寄存器
data = dev.read_register(0x0000, 4)
print(f"Serial: {data.hex()}")
# Streaming 模式:持续采集力数据
dev.start_stream()
for _ in range(10):
sample = dev.read_sample(timeout_ms=200)
f = sample.combined_force.force
print(f"fx={f.fx} fy={f.fy} fz={f.fz}")
dev.stop_stream()
```
# 写寄存器
dev.write_register(0x0030, bytes([0x01, 0x00, 0x00, 0x00]))
---
## ROS2 示例
ROS2 C++ 示例位于 `example/ros-cpp/`,包含:
- `eskin_publisher.cpp` — 独立线程读取力数据,定时发布到 `/comb_force` topic`std_msgs/UInt32`
- `eskin_subscriber.cpp` — 订阅 `/comb_force` topic 并打印
- `CMakeLists.txt` / `package.xml` — ROS2 包配置
### 构建
```bash
cd your_ros2_ws/src
ln -s /path/to/eskin-finger-sdk/example/ros-cpp eskin_example
cd .. && colcon build --packages-select eskin_example
```
### 运行
```bash
# 终端1启动 publisher指定设备路径
ros2 run eskin_example eskin_publisher /dev/ttyUSB0
# 终端2订阅数据
ros2 run eskin_example eskin_subscriber
```
---
## 数据类型
```c
typedef struct {
uint32_t fx; // X 轴力uint32
uint32_t fy; // Y 轴力uint32
uint32_t fz; // Z 轴力uint32
} CForce3D;
typedef struct {
uint32_t module; // 模块编号
CForce3D force; // 三维力
} CCombinedForce;
typedef struct {
uint64_t timestamp_us; // 时间戳(微秒)
uint32_t sequence; // 序列号
CCombinedForce combined_force; // 组合力数据
} CFingerSample;
```
---
@@ -150,6 +240,10 @@ with EskinDevice("libeskin_finger_sdk.so") as dev:
| `eskin_write_device_config2(handle, enable, return_count)` | 写入设备配置寄存器2 |
| `eskin_write_matrix_row(handle, row, return_count)` | 写入矩阵行数 |
| `eskin_write_matrix_col(handle, col, return_count)` | 写入矩阵列数 |
| `eskin_start_stream(handle)` | 启动流式采集 |
| `eskin_stop_stream(handle)` | 停止流式采集 |
| `eskin_read_sample(handle, timeout_ms, out)` | 读取一个采样数据 |
| `eskin_get_mode(handle, out)` | 查询当前设备模式0=Command, 1=Streaming |
---
@@ -209,17 +303,17 @@ cargo test protocol::tests # 仅协议层
## 架构
```text
User / C / Python
↓ FFI
DeviceWrapper (handle)
EskinDeviceInner
↓ read_register / write_register
EskinProtocolCodec (encode/decode + CRC8)
SerialTransport (write/read bytes)
Hardware (UART)
User / C / Python / ROS2
↓ FFI
DeviceWrapper (handle)
EskinDeviceInner
↓ read_register / write_register / start_stream / read_sample
EskinProtocolCodec (encode/decode + CRC8)
SerialTransport (write/read bytes)
Hardware (UART)
```
详细设计见 `docs/PROGRESS.md``docs/ARCHITECTURE.md`