update docs and downstream sync
This commit is contained in:
+175
-139
@@ -1,66 +1,19 @@
|
|||||||
# orisys API 文档
|
---
|
||||||
|
title: API Reference
|
||||||
|
description: Orisys SDK Python API 中文参考
|
||||||
|
---
|
||||||
|
|
||||||
本文件提供了 `orisys` 套件的中文文档
|
# API Reference
|
||||||
|
|
||||||
## 准备 Python 开发环境
|
本文件提供 `orisys` 套件的 Python API 中文参考。安装、CUDA、CuPy 与运行示例的基础步骤请参考 [快速开始](./get_started.md)。
|
||||||
推荐下载 Anaconda,并使用Python 3.10
|
|
||||||
```
|
|
||||||
# 创建并激活虚拟环境
|
|
||||||
conda create -n orisys python=3.10
|
|
||||||
conda activate orisys
|
|
||||||
```
|
|
||||||
## 安装 CUDA Toolkit 和 cuDNN
|
|
||||||
在官方链接下载和安装:https://developer.nvidia.com/cuda/toolkit
|
|
||||||
或
|
|
||||||
在Anaconda环境中安装(例子使用cuda12.9):
|
|
||||||
https://anaconda.org/channels/nvidia/packages/cuda/overview
|
|
||||||
```
|
|
||||||
conda install nvidia/label/cuda-12.9.0::cuda-toolkit nvidia::cudnn
|
|
||||||
```
|
|
||||||
## SDK安装方法
|
|
||||||
1. 根据系统(win / linux),平台(amd64, aarch64), python(如p38、p39、p310) 版本下载对应的`.whl`文件
|
|
||||||
2. 运行pip命令安装:
|
|
||||||
```bash
|
|
||||||
pip install orisys-{version}-{python-version}-{python-version}-{os}_{machine}.whl
|
|
||||||
```
|
|
||||||
如
|
|
||||||
```bash
|
|
||||||
pip install orisys-0.4.3-cp310-cp310-win_amd64.whl
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **安装 CuPy(根据您的 CUDA 版本选择):**
|
## Sensor API
|
||||||
|
|
||||||
**方法 1:使用 extras(推荐)**
|
### `Sensor` 类别
|
||||||
```bash
|
#### 描述
|
||||||
# 对于 CUDA 12.x
|
|
||||||
pip install orisys[cuda12]
|
|
||||||
|
|
||||||
# 对于 CUDA 13.x
|
|
||||||
pip install orisys[cuda13]
|
|
||||||
```
|
|
||||||
|
|
||||||
**方法 2:手动安装**
|
|
||||||
```bash
|
|
||||||
# 对于 CUDA 12.x
|
|
||||||
pip install cupy-cuda12x>=12.3.0
|
|
||||||
|
|
||||||
# 对于 CUDA 13.x
|
|
||||||
pip install cupy-cuda13x>=13.0.0
|
|
||||||
```
|
|
||||||
|
|
||||||
4. 测试导入:
|
|
||||||
```python
|
|
||||||
import orisys
|
|
||||||
print(orisys.__version__)
|
|
||||||
```
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
## `Sensor` 类别
|
|
||||||
### 描述
|
|
||||||
`orisys`套件的核心类,用于从视频文件或实时摄像头中读取传感器图像,创建一个具体的传感器对象(实例)。并完成形变、光流及力信息的计算与管理。
|
`orisys`套件的核心类,用于从视频文件或实时摄像头中读取传感器图像,创建一个具体的传感器对象(实例)。并完成形变、光流及力信息的计算与管理。
|
||||||
|
|
||||||
### 输入参数
|
#### 输入参数
|
||||||
|
|
||||||
* **vid_src** (`int | str`, 必填): 传感器 ID、相机编号或视频路径。默认为 `0`。
|
* **vid_src** (`int | str`, 必填): 传感器 ID、相机编号或视频路径。默认为 `0`。
|
||||||
* **config_name** (`str`,可选): 传感器配置文件名称或 `json` 文件路径,用于加载对应的参数配置;例如 `box` 或 `./config/box.json`。
|
* **config_name** (`str`,可选): 传感器配置文件名称或 `json` 文件路径,用于加载对应的参数配置;例如 `box` 或 `./config/box.json`。
|
||||||
@@ -69,9 +22,9 @@ print(orisys.__version__)
|
|||||||
* **verbose** (`bool`,可选): 是否输出初始化及运行过程中的详细日志信息,默认为 `False`。
|
* **verbose** (`bool`,可选): 是否输出初始化及运行过程中的详细日志信息,默认为 `False`。
|
||||||
|
|
||||||
|
|
||||||
### 返回
|
#### 返回
|
||||||
* `Sensor` 实例,用于后续图像采集、形变计算和数据读取操作。
|
* `Sensor` 实例,用于后续图像采集、形变计算和数据读取操作。
|
||||||
### 示例
|
#### 示例
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Example 1: 用相机编号创建
|
# Example 1: 用相机编号创建
|
||||||
@@ -82,63 +35,62 @@ sensor = Sensor(0)
|
|||||||
sensor = Sensor("/data/data.mp4")
|
sensor = Sensor("/data/data.mp4")
|
||||||
```
|
```
|
||||||
|
|
||||||
***
|
### `Sensor.get_img` 方法
|
||||||
|
|
||||||
## 2. `Sensor.get_img` 方法
|
#### 描述
|
||||||
|
|
||||||
### 描述
|
|
||||||
|
|
||||||
采集并预处理一帧图像并存到`Sensor` 类别中。
|
采集并预处理一帧图像并存到`Sensor` 类别中。
|
||||||
|
|
||||||
|
|
||||||
### 返回
|
#### 返回
|
||||||
* `bool`:是否成功读取图像。
|
* `bool`:是否成功读取图像。
|
||||||
|
|
||||||
### 状态更新
|
#### 状态更新
|
||||||
* 成功时更新`sensor`类里面的`sensor.img`。处理后的BGR格式图像(`uint8`,尺寸为json文件中`resolution_stitch`对应分辨率)。
|
* 成功时更新`sensor`类里面的`sensor.img`。处理后的BGR格式图像(`uint8`,尺寸为json文件中`resolution_stitch`对应分辨率)。
|
||||||
### 示例
|
#### 示例
|
||||||
|
|
||||||
```python
|
```python
|
||||||
sensor.get_img()
|
sensor.get_img()
|
||||||
```
|
```
|
||||||
***
|
### `Sensor.compute_deformation` 方法
|
||||||
|
|
||||||
## 3. `Sensor.compute_deformation` 方法
|
#### 描述
|
||||||
|
|
||||||
### 描述
|
|
||||||
|
|
||||||
基于当前帧图像计算光流/形变向量场,并通过 HHD 分解将形变场分解为法向/切向分量,同时估计法向力、切向力与扭矩等标量输出。该方法会更新
|
基于当前帧图像计算光流/形变向量场,并通过 HHD 分解将形变场分解为法向/切向分量,同时估计法向力、切向力与扭矩等标量输出。该方法会更新
|
||||||
`sensor`的形变场与力相关状态,并返回当前帧的光流场。
|
`sensor`的形变场与力相关状态,并返回当前帧的光流场。
|
||||||
调用前置条件:需要先通过`get_img()`成功更新图像。
|
调用前置条件:需要先通过`get_img()`成功更新图像。
|
||||||
|
|
||||||
### 输入参数
|
#### 输入参数
|
||||||
* **check_motion**(`bool`,可选): 为 `True` 时先根据光流判断是否有运动,有运动才做 HHD 分解;为 `False` 时每帧都做 HHD。默认 `True`。
|
* **decompose** (`bool`,可选): 是否在计算光流后继续执行 HHD 分解,默认 `True`。
|
||||||
* **threshold**(`float`,可选): **check_motion** 功能的阈值,默认为`6`。
|
* **check_motion** (`bool`,可选): 是否启用运动检测门控。当前不推荐依赖该功能;新代码建议显式传入 `False`,让每帧都执行 HHD 分解。为兼容旧代码,函数默认值仍为 `True`。
|
||||||
* **tail_frames** (`int`,可选): 光流变化低于阈值后继续解算多少帧,确保传感器回复,,默认为 `10`.
|
|
||||||
|
|
||||||
### 返回
|
#### 兼容参数(暂不推荐)
|
||||||
|
|
||||||
* `bool`:是否接触到物体,如果**check_motion**为`False`时返回`None`。
|
以下参数只在 `check_motion=True` 时生效。由于运动检测门控目前不稳定,建议在新代码中暂时不要使用:
|
||||||
|
|
||||||
### 示例
|
* **threshold** (`float`,可选): 运动检测阈值,默认 `6`。
|
||||||
|
* **tail_frames** (`int`,可选): 运动低于阈值后继续解算多少帧,默认 `10`。
|
||||||
|
|
||||||
|
#### 返回
|
||||||
|
|
||||||
|
* `None`: 当 `check_motion=False` 时返回。推荐用法下不要依赖该返回值判断接触状态。
|
||||||
|
* `bool`: 仅当 `check_motion=True` 时返回运动检测结果;该模式目前暂不推荐。
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
|
||||||
```python
|
```python
|
||||||
sensor.get_img()
|
sensor.get_img()
|
||||||
|
sensor.compute_deformation(check_motion=False)
|
||||||
is_touched = sensor.compute_deformation(check_motion=True, threshold=6)
|
|
||||||
# 步骤 2.3: 计算触碰点
|
|
||||||
if is_touched:
|
|
||||||
sensor.compute_contact()
|
sensor.compute_contact()
|
||||||
```
|
```
|
||||||
***
|
### `Sensor.compute_contact` 方法
|
||||||
## 4. `Sensor.compute_contact` 方法
|
|
||||||
|
|
||||||
### 描述
|
#### 描述
|
||||||
基于已计算的法向形变场进行接触区域分析,输出接触质心与轮廓等信息,并生成接触深度图,默认配置在`json`文件中,可按需调节。
|
基于已计算的法向形变场进行接触区域分析,输出接触质心与轮廓等信息,并生成接触深度图,默认配置在`json`文件中,可按需调节。
|
||||||
|
|
||||||
调用前置条件:需先执行`compute_deformation()`。
|
调用前置条件:需先执行`compute_deformation()`。
|
||||||
|
|
||||||
### 返回
|
#### 返回
|
||||||
`dict`为接触分析结果字典,至少包含以下键:
|
`dict`为接触分析结果字典,至少包含以下键:
|
||||||
|
|
||||||
* **expansion_mask**: 扩张区域掩码(`np.ndarray`)。
|
* **expansion_mask**: 扩张区域掩码(`np.ndarray`)。
|
||||||
@@ -148,12 +100,12 @@ if is_touched:
|
|||||||
* **centroid_found**: 是否检测到接触区域(`bool`)。
|
* **centroid_found**: 是否检测到接触区域(`bool`)。
|
||||||
* **contour**: 最大轮廓或轮廓列表(可选字段,存在时返回)。
|
* **contour**: 最大轮廓或轮廓列表(可选字段,存在时返回)。
|
||||||
|
|
||||||
### 状态更新
|
#### 状态更新
|
||||||
|
|
||||||
* **sensor.centroid**: 接触质心(单点为 `(x, y)`,多点为列表)。
|
* **sensor.centroid**: 接触质心(单点为 `(x, y)`,多点为列表)。
|
||||||
* **sensor.centroid_found**: 是否找到质心(`bool`)。
|
* **sensor.centroid_found**: 是否找到质心(`bool`)。
|
||||||
|
|
||||||
### 示例
|
#### 示例
|
||||||
```python
|
```python
|
||||||
sensor.compute_contact()
|
sensor.compute_contact()
|
||||||
centroid, contour, depth = sensor.read_info(
|
centroid, contour, depth = sensor.read_info(
|
||||||
@@ -162,19 +114,18 @@ centroid, contour, depth = sensor.read_info(
|
|||||||
sensor.info.DEPTH,
|
sensor.info.DEPTH,
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
***
|
### `Sensor.read_info` 方法
|
||||||
## 4. `Sensor.read_info` 方法
|
|
||||||
|
|
||||||
### 描述
|
#### 描述
|
||||||
|
|
||||||
按指定的信息类型(`sensor.info`枚举值)批量读取传感器状态数据,并按传入顺序返回对应值的元组。
|
按指定的信息类型(`sensor.info`枚举值)批量读取传感器状态数据,并按传入顺序返回对应值的元组。
|
||||||
该方法通常用于在完成形变计算后,读取光流场、分量场、力、帧率等信息。
|
该方法通常用于在完成形变计算后,读取光流场、分量场、力、帧率等信息。
|
||||||
|
|
||||||
### 输入参数
|
#### 输入参数
|
||||||
|
|
||||||
* **args**: 任意数量的 `Sensor.info` 枚举,用于指定需要获取的数据类型。支持的枚举值及对应数据如下:
|
* **args**: 任意数量的 `Sensor.info` 枚举,用于指定需要获取的数据类型。支持的枚举值及对应数据如下:
|
||||||
|
|
||||||
### 支持的枚 info keys:
|
#### 支持的 info keys
|
||||||
|
|
||||||
向量/图像类
|
向量/图像类
|
||||||
|
|
||||||
@@ -188,6 +139,9 @@ centroid, contour, depth = sensor.read_info(
|
|||||||
* FNORMAL: 法向力,`float`。
|
* FNORMAL: 法向力,`float`。
|
||||||
* FSHEARX: X切向力,`float`。
|
* FSHEARX: X切向力,`float`。
|
||||||
* FSHEARY: Y切向力,`float`。
|
* FSHEARY: Y切向力,`float`。
|
||||||
|
* FNORMAL_RAW: 未经过低通滤波的原始法向力,`float`。
|
||||||
|
* FSHEARX_RAW: 未经过低通滤波的原始 X 切向力,`float`。
|
||||||
|
* FSHEARY_RAW: 未经过低通滤波的原始 Y 切向力,`float`。
|
||||||
* FPS: 输出帧率,`float`。
|
* FPS: 输出帧率,`float`。
|
||||||
* CENTROID: 触碰点的坐标,(x, y)。
|
* CENTROID: 触碰点的坐标,(x, y)。
|
||||||
* CONTOUR: 触碰区域轮廓,`np.ndarray`, shape=(H, W), 未生成时返回`np.empty`
|
* CONTOUR: 触碰区域轮廓,`np.ndarray`, shape=(H, W), 未生成时返回`np.empty`
|
||||||
@@ -198,7 +152,7 @@ centroid, contour, depth = sensor.read_info(
|
|||||||
|
|
||||||
注:H/W 由当前`json`文件中的`resolution_stitch`配置分辨率决定。
|
注:H/W 由当前`json`文件中的`resolution_stitch`配置分辨率决定。
|
||||||
|
|
||||||
### 返回
|
#### 返回
|
||||||
|
|
||||||
`Tuple`
|
`Tuple`
|
||||||
|
|
||||||
@@ -210,7 +164,7 @@ centroid, contour, depth = sensor.read_info(
|
|||||||
- 标量类数据:`float`
|
- 标量类数据:`float`
|
||||||
- 时间戳:`str`(`TIMESTAMP`,格式为 `HH:MM:SS`)
|
- 时间戳:`str`(`TIMESTAMP`,格式为 `HH:MM:SS`)
|
||||||
|
|
||||||
### 示例
|
#### 示例
|
||||||
```python
|
```python
|
||||||
# Example 1:读取标量数据
|
# Example 1:读取标量数据
|
||||||
fps, fn, fx, fy = sensor.read_info(
|
fps, fn, fx, fy = sensor.read_info(
|
||||||
@@ -223,20 +177,112 @@ fps, fn, fx, fy = sensor.read_info(
|
|||||||
# Example 2: 读取单个向量场(注意解包方式)
|
# Example 2: 读取单个向量场(注意解包方式)
|
||||||
flow = sensor.read_info(sensor.info.VRAW)
|
flow = sensor.read_info(sensor.info.VRAW)
|
||||||
```
|
```
|
||||||
***
|
### `Sensor.disconnect` 方法
|
||||||
|
|
||||||
## 5. `Sensor.disconnect` 方法
|
#### 描述
|
||||||
|
|
||||||
### 描述
|
|
||||||
|
|
||||||
释放传感器相关资源并停止后台异步线程。 调用后将关闭视频/摄像头输入源,并销毁所有 OpenCV 窗口。
|
释放传感器相关资源并停止后台异步线程。 调用后将关闭视频/摄像头输入源,并销毁所有 OpenCV 窗口。
|
||||||
|
|
||||||
建议在程序退出前调用以确保资源被正确释放。
|
建议在程序退出前调用以确保资源被正确释放。
|
||||||
|
|
||||||
***
|
### `Sensor.configure` 低通滤波设置
|
||||||
***
|
|
||||||
|
|
||||||
## `orisys.finger` 手指 UV pipeline
|
#### 描述
|
||||||
|
|
||||||
|
运行时更新传感器设置,无需重新连接相机。常用于开启力信号低通滤波,降低 `FNORMAL`、`FSHEARX`、`FSHEARY` 的高频噪声。
|
||||||
|
|
||||||
|
默认采样率按 30 fps 计算,推荐低通截止频率为 5 Hz:
|
||||||
|
|
||||||
|
```python
|
||||||
|
sensor.configure(lowpass_cutoff_hz=5.0, lowpass_sample_rate_hz=30.0)
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:`lowpass_cutoff_hz` 必须小于 Nyquist 频率,即 `lowpass_sample_rate_hz / 2`。例如 30 fps 时,截止频率必须小于 15 Hz。
|
||||||
|
|
||||||
|
#### 输入参数
|
||||||
|
|
||||||
|
* **lowpass_cutoff_hz** (`float`,可选): 力信号低通截止频率,单位 Hz。`0` 表示关闭滤波;正数默认开启滤波。
|
||||||
|
* **lowpass_sample_rate_hz** (`float`,可选): 力信号采样率,默认 `30.0`。
|
||||||
|
* **lowpass_order** (`int`,可选): Butterworth 滤波器阶数,默认 `2`。
|
||||||
|
* **lowpass_enabled** (`bool`,可选): 显式开启或关闭低通滤波。
|
||||||
|
* **hhd_downsample** (`int`,可选): HHD 解算下采样倍数,需为 `>= 1` 的整数,配置默认 `3`。
|
||||||
|
|
||||||
|
#### 读取结果
|
||||||
|
|
||||||
|
开启低通滤波后,`read_info()` 读取的力输出会被滤波:
|
||||||
|
|
||||||
|
```python
|
||||||
|
fn, fx, fy = sensor.read_info(
|
||||||
|
sensor.info.FNORMAL,
|
||||||
|
sensor.info.FSHEARX,
|
||||||
|
sensor.info.FSHEARY,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
原始力输出仍可通过 `FNORMAL_RAW`、`FSHEARX_RAW`、`FSHEARY_RAW` 读取,便于调试或对比。
|
||||||
|
|
||||||
|
## Signal Processing API
|
||||||
|
|
||||||
|
### `orisys.signal_processing.lowpass_filter` 函数
|
||||||
|
|
||||||
|
#### 描述
|
||||||
|
|
||||||
|
对一维、等间隔采样数据执行零相位 Butterworth 低通滤波,适合示例程序或图表中对历史数据做平滑处理。
|
||||||
|
|
||||||
|
默认参数为 `cutoff_hz=5.0`、`sample_rate_hz=30.0`、`order=2`。这表示在 30 fps 数据上使用 5 Hz 低通滤波。
|
||||||
|
|
||||||
|
#### 输入参数
|
||||||
|
|
||||||
|
* **data**: 一维数据序列。
|
||||||
|
* **cutoff_hz** (`float`,可选): 截止频率,默认 `5.0`。传入 `0` 时不做滤波并返回数据副本。
|
||||||
|
* **sample_rate_hz** (`float`,可选): 采样率,默认 `30.0`。
|
||||||
|
* **order** (`int`,可选): Butterworth 滤波器阶数,默认 `2`。
|
||||||
|
|
||||||
|
#### 返回
|
||||||
|
|
||||||
|
* `np.ndarray`: 滤波后的 `float64` 数组。
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
from orisys.signal_processing import lowpass_filter
|
||||||
|
|
||||||
|
filtered_force = lowpass_filter(force_values, cutoff_hz=5.0, sample_rate_hz=30.0)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `orisys.signal_processing.LowpassFilter` 类别
|
||||||
|
|
||||||
|
#### 描述
|
||||||
|
|
||||||
|
有状态的因果 Butterworth 低通滤波器,适合逐帧处理实时数据。它会保留上一帧的滤波状态,并通过 `process()` 一次处理一个多通道样本。
|
||||||
|
|
||||||
|
`Sensor.configure()` 内部使用该类来滤波力信号。构造时默认 `cutoff_hz=0.0`,表示关闭滤波;若要按 30 fps 使用推荐滤波,设置 `cutoff_hz=5.0`、`sample_rate_hz=30.0`。
|
||||||
|
|
||||||
|
#### 输入参数
|
||||||
|
|
||||||
|
* **cutoff_hz** (`float`,可选): 截止频率,默认 `0.0`,表示关闭滤波。
|
||||||
|
* **sample_rate_hz** (`float`,可选): 采样率,默认 `30.0`。
|
||||||
|
* **order** (`int`,可选): Butterworth 滤波器阶数,默认 `2`。
|
||||||
|
* **n_channels** (`int`,可选): 每个样本的通道数,默认 `3`。
|
||||||
|
|
||||||
|
#### 常用方法
|
||||||
|
|
||||||
|
* **configure(...)**: 运行时更新 `cutoff_hz`、`sample_rate_hz` 或 `order`。
|
||||||
|
* **reset(x0=None)**: 清除滤波器状态;传入 `x0` 时会用该样本初始化稳态。
|
||||||
|
* **process(values)**: 滤波一个多通道样本,返回 shape 为 `(n_channels,)` 的 `np.ndarray`。
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
from orisys.signal_processing import LowpassFilter
|
||||||
|
|
||||||
|
filt = LowpassFilter(cutoff_hz=5.0, sample_rate_hz=30.0, n_channels=3)
|
||||||
|
filtered = filt.process([fnormal, fshearx, fsheary])
|
||||||
|
```
|
||||||
|
|
||||||
|
## Finger Pipeline API
|
||||||
|
|
||||||
|
### `orisys.finger` 手指 UV pipeline
|
||||||
|
|
||||||
`examples/finger_pipeline.py` 展示了一个更适合手指/单相机场景的高层 API:
|
`examples/finger_pipeline.py` 展示了一个更适合手指/单相机场景的高层 API:
|
||||||
|
|
||||||
@@ -247,7 +293,7 @@ flow = sensor.read_info(sensor.info.VRAW)
|
|||||||
|
|
||||||
相比直接使用 `Sensor`,`FingerRuntime` 已封装了视频读取、相机尺寸设置、UV 展开和有效区域掩码传递,更适合作为手指 2D/3D 示例与上层应用的入口。
|
相比直接使用 `Sensor`,`FingerRuntime` 已封装了视频读取、相机尺寸设置、UV 展开和有效区域掩码传递,更适合作为手指 2D/3D 示例与上层应用的入口。
|
||||||
|
|
||||||
### 1. `FingerRuntime.open` 方法
|
### `FingerRuntime.open` 方法
|
||||||
|
|
||||||
#### 描述
|
#### 描述
|
||||||
|
|
||||||
@@ -288,7 +334,7 @@ runtime = FingerRuntime.open(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. `FingerRuntime` 常用属性
|
### `FingerRuntime` 常用属性
|
||||||
|
|
||||||
* **runtime.geometry**: `FingerGeometry`,包含输入/输出分辨率、导出资产路径、相机内参、缓存路径等一次性初始化结果。
|
* **runtime.geometry**: `FingerGeometry`,包含输入/输出分辨率、导出资产路径、相机内参、缓存路径等一次性初始化结果。
|
||||||
* **runtime.domain**: UV 展开域对象,内部包含 remap、valid mask 以及可选 surface 数据。
|
* **runtime.domain**: UV 展开域对象,内部包含 remap、valid mask 以及可选 surface 数据。
|
||||||
@@ -296,7 +342,7 @@ runtime = FingerRuntime.open(
|
|||||||
* **runtime.capture**: OpenCV `VideoCapture` 对象。
|
* **runtime.capture**: OpenCV `VideoCapture` 对象。
|
||||||
* **runtime.valid_mask**: 当前 UV 域有效像素掩码。
|
* **runtime.valid_mask**: 当前 UV 域有效像素掩码。
|
||||||
|
|
||||||
### 3. `FingerRuntime.read_source` 方法
|
### `FingerRuntime.read_source` 方法
|
||||||
|
|
||||||
#### 描述
|
#### 描述
|
||||||
|
|
||||||
@@ -308,7 +354,7 @@ runtime = FingerRuntime.open(
|
|||||||
* **ok** (`bool`): 是否成功读取。
|
* **ok** (`bool`): 是否成功读取。
|
||||||
* **frame** (`np.ndarray | None`): 原始 BGR 帧。
|
* **frame** (`np.ndarray | None`): 原始 BGR 帧。
|
||||||
|
|
||||||
### 4. `FingerRuntime.process_frame` 方法
|
### `FingerRuntime.process_frame` 方法
|
||||||
|
|
||||||
#### 描述
|
#### 描述
|
||||||
|
|
||||||
@@ -323,7 +369,7 @@ runtime = FingerRuntime.open(
|
|||||||
|
|
||||||
* `np.ndarray`: 展开后的触觉图像。`color=False` 时通常为单通道灰度图;`color=True` 时为 BGR 图。
|
* `np.ndarray`: 展开后的触觉图像。`color=False` 时通常为单通道灰度图;`color=True` 时为 BGR 图。
|
||||||
|
|
||||||
### 5. `FingerRuntime.grab_and_process` 方法
|
### `FingerRuntime.grab_and_process` 方法
|
||||||
|
|
||||||
#### 描述
|
#### 描述
|
||||||
|
|
||||||
@@ -339,7 +385,7 @@ runtime = FingerRuntime.open(
|
|||||||
* **ok** (`bool`): 是否成功读取并处理。
|
* **ok** (`bool`): 是否成功读取并处理。
|
||||||
* **unrolled** (`np.ndarray | None`): 展开后的触觉图像。
|
* **unrolled** (`np.ndarray | None`): 展开后的触觉图像。
|
||||||
|
|
||||||
### 6. `FingerRuntime.get_img` 方法
|
### `FingerRuntime.get_img` 方法
|
||||||
|
|
||||||
#### 描述
|
#### 描述
|
||||||
|
|
||||||
@@ -353,7 +399,7 @@ runtime = FingerRuntime.open(
|
|||||||
|
|
||||||
* `np.ndarray | None`
|
* `np.ndarray | None`
|
||||||
|
|
||||||
### 7. `FingerRuntime.warmup` 方法
|
### `FingerRuntime.warmup` 方法
|
||||||
|
|
||||||
#### 描述
|
#### 描述
|
||||||
|
|
||||||
@@ -369,13 +415,13 @@ runtime = FingerRuntime.open(
|
|||||||
* **ok** (`bool`): 是否成功完成 warmup 前的采集。
|
* **ok** (`bool`): 是否成功完成 warmup 前的采集。
|
||||||
* **unrolled** (`np.ndarray | None`): warmup 时得到的展开图。
|
* **unrolled** (`np.ndarray | None`): warmup 时得到的展开图。
|
||||||
|
|
||||||
### 8. `FingerRuntime.close` 方法
|
### `FingerRuntime.close` 方法
|
||||||
|
|
||||||
#### 描述
|
#### 描述
|
||||||
|
|
||||||
释放内部 `VideoCapture` 并调用 `runtime.tactile.disconnect()` 清理 `Sensor` 资源。程序退出前应调用。
|
释放内部 `VideoCapture` 并调用 `runtime.tactile.disconnect()` 清理 `Sensor` 资源。程序退出前应调用。
|
||||||
|
|
||||||
### 9. `TactileProcessor` 类别
|
### `TactileProcessor` 类别
|
||||||
|
|
||||||
#### 描述
|
#### 描述
|
||||||
|
|
||||||
@@ -393,7 +439,7 @@ runtime = FingerRuntime.open(
|
|||||||
|
|
||||||
除 `push_frame()` / `warmup()` 之外,`TactileProcessor` 的 `compute_deformation()`、`compute_contact()`、`read_info()`、`reset()` 调用方式与 `Sensor` 基本一致,因此可直接复用前文 `Sensor` 章节中的参数说明。
|
除 `push_frame()` / `warmup()` 之外,`TactileProcessor` 的 `compute_deformation()`、`compute_contact()`、`read_info()`、`reset()` 调用方式与 `Sensor` 基本一致,因此可直接复用前文 `Sensor` 章节中的参数说明。
|
||||||
|
|
||||||
### 10. 最小手指 pipeline 示例
|
### 最小手指 pipeline 示例
|
||||||
|
|
||||||
下面的流程与 `examples/finger_pipeline.py` 一致:
|
下面的流程与 `examples/finger_pipeline.py` 一致:
|
||||||
|
|
||||||
@@ -460,7 +506,7 @@ finally:
|
|||||||
cv2.destroyAllWindows()
|
cv2.destroyAllWindows()
|
||||||
```
|
```
|
||||||
|
|
||||||
### 11. 关于 profile 模式
|
### 关于 profile 模式
|
||||||
|
|
||||||
`examples/finger_pipeline.py` 中的 `--profile` / `--profile-every` 是示例脚本参数,不属于 `orisys` SDK 核心 API。本质上它只是把主循环拆成:
|
`examples/finger_pipeline.py` 中的 `--profile` / `--profile-every` 是示例脚本参数,不属于 `orisys` SDK 核心 API。本质上它只是把主循环拆成:
|
||||||
|
|
||||||
@@ -472,11 +518,11 @@ finally:
|
|||||||
|
|
||||||
并对各阶段做耗时统计。因此在业务代码中,也可以按同样方式自行插入计时逻辑。
|
并对各阶段做耗时统计。因此在业务代码中,也可以按同样方式自行插入计时逻辑。
|
||||||
|
|
||||||
# 可视化相关
|
## 可视化相关
|
||||||
|
|
||||||
## 1. `util.draw_arrows` 方法
|
### `util.draw_arrows` 方法
|
||||||
|
|
||||||
### 输入参数
|
#### 输入参数
|
||||||
|
|
||||||
* **frame**:背景图像
|
* **frame**:背景图像
|
||||||
* **optical_flow**:光流场
|
* **optical_flow**:光流场
|
||||||
@@ -488,12 +534,12 @@ finally:
|
|||||||
* **below_threshold_color**:RGB格式的`uint8`颜色,如(0, 0, 255)
|
* **below_threshold_color**:RGB格式的`uint8`颜色,如(0, 0, 255)
|
||||||
* **edge_margin**: 不显示箭头边距(pixel)
|
* **edge_margin**: 不显示箭头边距(pixel)
|
||||||
|
|
||||||
### 返回
|
#### 返回
|
||||||
`np.ndarray` :箭头光流可视化图像
|
`np.ndarray` :箭头光流可视化图像
|
||||||
|
|
||||||
## 2. `util.draw_contact` 方法
|
### `util.draw_contact` 方法
|
||||||
|
|
||||||
### 输入参数
|
#### 输入参数
|
||||||
|
|
||||||
* **image**:背景图像
|
* **image**:背景图像
|
||||||
* **contour**:轮廓
|
* **contour**:轮廓
|
||||||
@@ -501,42 +547,36 @@ finally:
|
|||||||
* **point_color**:触碰点的颜色 (BGR)
|
* **point_color**:触碰点的颜色 (BGR)
|
||||||
* **region_color**:轮廓的颜色 (BGR)
|
* **region_color**:轮廓的颜色 (BGR)
|
||||||
|
|
||||||
### 返回
|
#### 返回
|
||||||
`np.ndarray` :箭头光流可视化图像
|
`np.ndarray` :箭头光流可视化图像
|
||||||
|
|
||||||
***
|
## 配置标定相关
|
||||||
|
|
||||||
# 配置标定相关
|
### `list_config` 方法
|
||||||
|
|
||||||
## 1. `list_config` 方法
|
#### 描述
|
||||||
|
|
||||||
### 描述
|
|
||||||
列出内置传感器配置参数字典。
|
列出内置传感器配置参数字典。
|
||||||
|
|
||||||
### 返回
|
#### 返回
|
||||||
|
|
||||||
* `list`: 内置传感器配置参数字典的名字列表。
|
* `list`: 内置传感器配置参数字典的名字列表。
|
||||||
***
|
### `export_config` 方法
|
||||||
## 2. `export_config` 方法
|
|
||||||
|
|
||||||
### 描述
|
#### 描述
|
||||||
导出当前传感器实例使用的配置参数字典。
|
导出当前传感器实例使用的配置参数字典。
|
||||||
|
|
||||||
### 输入参数
|
#### 输入参数
|
||||||
* **config_name**: 需要导出的配置参数字典名字,`str`
|
* **config_name**: 需要导出的配置参数字典名字,`str`
|
||||||
* **path**: 导出的路径,默认`"./config/exported_config.json"`
|
* **path**: 导出的路径,默认`"./config/exported_config.json"`
|
||||||
|
|
||||||
### 返回
|
#### 返回
|
||||||
|
|
||||||
* `dict`: 导出的配置参数字典。
|
* `dict`: 导出的配置参数字典。
|
||||||
|
|
||||||
### 保存
|
#### 保存
|
||||||
|
|
||||||
* 导出内置的`config_name`配置参数字典至`path`
|
* 导出内置的`config_name`配置参数字典至`path`
|
||||||
|
|
||||||
***
|
|
||||||
***
|
|
||||||
|
|
||||||
## 常见问题解答 (FAQ)
|
## 常见问题解答 (FAQ)
|
||||||
### Q1:为什么必须先调用`get_img()`,再调用`compute_deformation()`?
|
### Q1:为什么必须先调用`get_img()`,再调用`compute_deformation()`?
|
||||||
A:`compute_deformation()`依赖最近一次获取并拼接的图像数据进行形变与力计算。
|
A:`compute_deformation()`依赖最近一次获取并拼接的图像数据进行形变与力计算。
|
||||||
@@ -556,9 +596,5 @@ A:当需要接触相关信息(如接触质心、轮廓或深度/散度图)
|
|||||||
A:`read_info()`按传入的`sensor.info`参数顺序返回一个元组。
|
A:`read_info()`按传入的`sensor.info`参数顺序返回一个元组。
|
||||||
返回值顺序与参数顺序一一对应,与内部存储顺序无关。
|
返回值顺序与参数顺序一一对应,与内部存储顺序无关。
|
||||||
|
|
||||||
### Q5:如果没有标定文件,或拼接结果不正确,会发生什么?
|
### Q5: 运行报错:“AttributeError: 'Stitcher' object has no attribute 'pos_default'”
|
||||||
A:若未指定或未找到标定文件,系统会在需要时创建新的拼接标定数据并保存到默认路径。
|
|
||||||
拼接结果异常通常与标定数据、拼接配置或输入图像质量有关,建议重新生成拼接标定并检查配置参数。
|
|
||||||
|
|
||||||
### Q6: 运行报错:“AttributeError: 'Stitcher' object has no attribute 'pos_default'”
|
|
||||||
A: 请检查视频源的编号是否正确,以及是否能正常打开视频源。在linux 使用`v4l2-ctl --list-devices` 或 Windows使用`Get-PnpDevice -PresentOnly | Where-Object { $_.Class -match '^Camera' }` 列出摄像头设备。
|
A: 请检查视频源的编号是否正确,以及是否能正常打开视频源。在linux 使用`v4l2-ctl --list-devices` 或 Windows使用`Get-PnpDevice -PresentOnly | Where-Object { $_.Class -match '^Camera' }` 列出摄像头设备。
|
||||||
+83
-52
@@ -1,21 +1,31 @@
|
|||||||
叠动科技软件使用手册
|
---
|
||||||
版本: 0.4.3
|
title: 快速开始
|
||||||
更新日期: 2026年7月13日
|
description: 安装并运行 Orisys SDK 的基础步骤
|
||||||
|
---
|
||||||
|
|
||||||
# 简介
|
# 快速开始
|
||||||
|
|
||||||
|
版本:0.4.3
|
||||||
|
|
||||||
|
## 简介
|
||||||
本文件介绍如何上手和运行叠动视触觉传感器配套的开发套件。本套件是配合叠动视触觉传感器进行图像处理,计算形变、力以及接触信息。它可以从摄像头或视频文件中读取图像,处理后打印和展示处理后信息。
|
本文件介绍如何上手和运行叠动视触觉传感器配套的开发套件。本套件是配合叠动视触觉传感器进行图像处理,计算形变、力以及接触信息。它可以从摄像头或视频文件中读取图像,处理后打印和展示处理后信息。
|
||||||
支持操作系统:Windows、Ubuntu
|
支持操作系统:Windows、Ubuntu
|
||||||
建议Python 版本: 3.10, 3.11, 3.12
|
建议Python 版本: 3.10, 3.11, 3.12
|
||||||
|
|
||||||
# 基础环境设置
|
项目链接:
|
||||||
## 安装 Python 与 Anaconda
|
|
||||||
|
- PyPI 安装页:[https://pypi.org/project/orisys/](https://pypi.org/project/orisys/)
|
||||||
|
- 项目文档/代码:[https://github.com/Orisys-Tech/orisys-sdk](https://github.com/Orisys-Tech/orisys-sdk)
|
||||||
|
|
||||||
|
## 基础环境设置
|
||||||
|
### 安装 Python 与 Anaconda
|
||||||
1. 访问 [Anaconda 官网](https://www.anaconda.com/download) 下载适合您操作系统的安装包(Windows、Linux)。
|
1. 访问 [Anaconda 官网](https://www.anaconda.com/download) 下载适合您操作系统的安装包(Windows、Linux)。
|
||||||
2. 按照提示完成安装。
|
2. 按照提示完成安装。
|
||||||
3. 安装完成后,打开命令行终端(Windows 用户可在”Anaconda Navigator”打开“Anaconda Prompt”),输入 `conda info` ,若能显示 conda 的信息,则说明安装成功。
|
3. 安装完成后,打开命令行终端(Windows 用户可在 “Anaconda Navigator” 打开 “Anaconda Prompt”),输入 `conda info`,若能显示 conda 的信息,则说明安装成功。
|
||||||
|
|
||||||
## 创建conda虚拟环境
|
### 创建 conda 虚拟环境
|
||||||
虚拟环境相当于一个独立的 Python 工作空间,可以避免不同项目所需的库版本冲突。
|
虚拟环境相当于一个独立的 Python 工作空间,可以避免不同项目所需的库版本冲突。
|
||||||
打开命令行终端(或Anaconda Prompt),依次输入以下命令(以 python 3.10 为例):
|
打开命令行终端(或 Anaconda Prompt),依次输入以下命令(以 Python 3.10 为例):
|
||||||
```bash
|
```bash
|
||||||
conda create -n orisys python=3.10
|
conda create -n orisys python=3.10
|
||||||
conda activate orisys
|
conda activate orisys
|
||||||
@@ -23,8 +33,8 @@ conda activate orisys
|
|||||||
|
|
||||||
激活后,命令行提示符前会出现 `(orisys)` 字样,表示当前已进入虚拟环境。
|
激活后,命令行提示符前会出现 `(orisys)` 字样,表示当前已进入虚拟环境。
|
||||||
|
|
||||||
## 创建工作区文件夹
|
### 创建工作区文件夹
|
||||||
“Anaconda Prompt”启动后的默认工作路径在用户根文件夹中,建议创建工作区文件夹方便管理,可以手动创建或者运行`mkdir <path>`命令:, 例如
|
“Anaconda Prompt” 启动后的默认工作路径在用户根文件夹中,建议创建工作区文件夹方便管理。可以手动创建文件夹,也可以运行 `mkdir <path>` 命令。例如:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir orisys_ws
|
mkdir orisys_ws
|
||||||
@@ -35,26 +45,43 @@ mkdir orisys_ws
|
|||||||
```bash
|
```bash
|
||||||
cd "C:\Users\orisys\orisys_ws"
|
cd "C:\Users\orisys\orisys_ws"
|
||||||
```
|
```
|
||||||
<div style="page-break-after: always;"></div>
|
### 安装 CUDA 和 cuDNN(可选)
|
||||||
|
|
||||||
## 安装 CUDA 和 cuDNN(可选)
|
|
||||||
如果您拥有 NVIDIA 显卡,并希望利用 GPU 加速计算,则需要安装 CUDA 工具包和 cuDNN。
|
如果您拥有 NVIDIA 显卡,并希望利用 GPU 加速计算,则需要安装 CUDA 工具包和 cuDNN。
|
||||||
|
|
||||||
1. 方法一(官网下载):
|
1. 方法一(官网下载):
|
||||||
访问 [NVIDIA CUDA Toolkit官网](https://developer.nvidia.com/cuda/toolkit)下载对应系统的 CUDA Toolkit 安装包,并按照提示安装, 注意12.X 和 13.X 的版本对应不同的 CuPy 库版本。
|
访问 [NVIDIA CUDA Toolkit 官网](https://developer.nvidia.com/cuda/toolkit) 下载对应系统的 CUDA Toolkit 安装包,并按照提示安装。注意 12.x 和 13.x 的版本对应不同的 CuPy 库版本。
|
||||||
|
|
||||||
2. 方法二(使用 Anaconda 安装,推荐):
|
2. 方法二(使用 Anaconda 安装,推荐):
|
||||||
在已激活的 orisys 环境中直接运行以下命令(以 CUDA 12.9 为例):
|
在已激活的 orisys 环境中直接运行以下命令(以 CUDA 12.9 为例):
|
||||||
`conda install nvidia/label/cuda-12.9.0::cuda-toolkit nvidia::cudnn`
|
|
||||||
|
```bash
|
||||||
|
conda install nvidia/label/cuda-12.9.0::cuda-toolkit nvidia::cudnn
|
||||||
|
```
|
||||||
|
|
||||||
该方法会自动配置好 CUDA 和 cuDNN,无需手动下载。
|
该方法会自动配置好 CUDA 和 cuDNN,无需手动下载。
|
||||||
|
|
||||||
注意:如果您的计算机没有 NVIDIA 显卡,或者不想使用 GPU 加速,可以跳过此步骤。套件也支持纯 CPU 运行(但速度可能较慢)。
|
注意:如果您的计算机没有 NVIDIA 显卡,或者不想使用 GPU 加速,可以跳过此步骤。套件也支持纯 CPU 运行(但速度可能较慢)。
|
||||||
|
|
||||||
# 安装 orisys SDK
|
## 安装 orisys SDK
|
||||||
|
|
||||||
orisys SDK 以 .whl 文件形式提供,您需要根据操作系统、Python 版本和硬件平台选择正确的文件。
|
推荐优先从 PyPI 安装。PyPI 会自动选择适合当前 Python 版本和操作系统的安装包,适合大多数用户。
|
||||||
|
|
||||||
## 下载正确的 .whl 文件
|
### 使用 PyPI 安装(推荐)
|
||||||
|
确认已经激活 `orisys` 虚拟环境后,运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install orisys
|
||||||
|
```
|
||||||
|
|
||||||
|
如果需要升级到最新版本,运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install --upgrade orisys
|
||||||
|
```
|
||||||
|
|
||||||
|
如果当前网络无法访问 PyPI,或需要安装客服提供的指定版本,可以使用下方 `.whl` 文件安装方式。
|
||||||
|
|
||||||
|
### 使用 .whl 文件安装(离线或指定版本)
|
||||||
从官方渠道获取 .whl 文件,文件名格式通常为:
|
从官方渠道获取 .whl 文件,文件名格式通常为:
|
||||||
orisys-{版本}-{Python版本}-{Python版本}-{操作系统}_{硬件架构}.whl
|
orisys-{版本}-{Python版本}-{Python版本}-{操作系统}_{硬件架构}.whl
|
||||||
例如:orisys-0.4.3-cp310-cp310-win_amd64.whl 表示:
|
例如:orisys-0.4.3-cp310-cp310-win_amd64.whl 表示:
|
||||||
@@ -64,17 +91,16 @@ Python 版本:3.10(cp310)
|
|||||||
操作系统:Windows(win)
|
操作系统:Windows(win)
|
||||||
硬件架构:64位(amd64)
|
硬件架构:64位(amd64)
|
||||||
|
|
||||||
请根据您的系统选择对应的文件,或者咨询客服索取:
|
请根据您的系统选择对应的文件,或者咨询客服索取。
|
||||||
|
|
||||||
## 安装 .whl 文件
|
将下载好的 `.whl` 文件放在一个文件夹中(如上述 `orisys_ws` 文件夹),打开命令行(确保已激活 `orisys` 环境或使用 “Anaconda Prompt”),使用 `cd` 命令切换到该文件夹,然后运行:
|
||||||
将下载好的 .whl 文件放在一个文件夹中(如上述`orisys_ws`文件夹),打开命令行(确保已激活 orisys 环境或使用"Anaconda Prompt"),使用 `cd` 命令切换到该文件夹,然后运行:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 请将文件名替换为您实际下载的文件名
|
# 请将文件名替换为您实际下载的文件名
|
||||||
pip install orisys-0.4.3-cp310-cp310-win_amd64.whl
|
pip install orisys-0.4.3-cp310-cp310-win_amd64.whl
|
||||||
```
|
```
|
||||||
|
|
||||||
## 安装 CuPy(可选)
|
### 安装 CuPy(可选)
|
||||||
注意:如果您没有安装 CUDA,或者使用 CPU 运行,可以跳过这一步。
|
注意:如果您没有安装 CUDA,或者使用 CPU 运行,可以跳过这一步。
|
||||||
|
|
||||||
CuPy 是一个与 NumPy 类似但支持 GPU 加速的库,您需要根据您安装的 CUDA 版本选择对应的 CuPy 版本。
|
CuPy 是一个与 NumPy 类似但支持 GPU 加速的库,您需要根据您安装的 CUDA 版本选择对应的 CuPy 版本。
|
||||||
@@ -84,10 +110,10 @@ CuPy 是一个与 NumPy 类似但支持 GPU 加速的库,您需要根据您安
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 如果您的 CUDA 版本是 12.x
|
# 如果您的 CUDA 版本是 12.x
|
||||||
pip install orisys[cuda12]
|
pip install "orisys[cuda12]"
|
||||||
|
|
||||||
# 如果您的 CUDA 版本是 13.x
|
# 如果您的 CUDA 版本是 13.x
|
||||||
pip install orisys[cuda13]
|
pip install "orisys[cuda13]"
|
||||||
```
|
```
|
||||||
如果之前已经安装了 orisys,可以单独安装 CuPy:
|
如果之前已经安装了 orisys,可以单独安装 CuPy:
|
||||||
|
|
||||||
@@ -95,55 +121,60 @@ pip install orisys[cuda13]
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# CUDA 12.x
|
# CUDA 12.x
|
||||||
pip install cupy-cuda12x>=12.3.0
|
pip install "cupy-cuda12x>=12.3.0"
|
||||||
|
|
||||||
# CUDA 13.x
|
# CUDA 13.x
|
||||||
pip install cupy-cuda13x>=13.0.0
|
pip install "cupy-cuda13x>=13.0.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
## 测试安装
|
### 测试安装
|
||||||
安装完成后,在命令行输入” python“命令进入 Python 环境, 并导入 orisys 库并查看版本号,验证是否成功:
|
安装完成后,在命令行输入 `python` 命令进入 Python 环境,并导入 `orisys` 库查看版本号,验证是否成功:
|
||||||
```bash
|
```bash
|
||||||
python
|
python
|
||||||
|
```
|
||||||
|
|
||||||
|
进入 Python 后继续输入:
|
||||||
|
|
||||||
|
```python
|
||||||
import orisys
|
import orisys
|
||||||
print(orisys.__version__)
|
print(orisys.__version__)
|
||||||
```
|
```
|
||||||
如果输出版本号(例如 0.4.3),则说明安装成功。
|
如果输出版本号(例如 0.4.3),则说明安装成功。
|
||||||
|
|
||||||
# 使用方法
|
## 使用方法
|
||||||
## 运行示例程序
|
### 运行示例程序
|
||||||
套件里面包含了多个例子,建议都在仓库根目录运行(即 `config/`、`examples/`、`assets/` 同级目录)。按产品入口如下:
|
套件里面包含了多个例子。运行示例前,请先进入 SDK 示例所在的文件夹,建议在仓库根目录运行(即 `config/`、`examples/`、`assets/` 同级目录)。如果您是通过 PyPI 安装并且本地没有示例文件,可以从上方项目链接下载示例。按产品入口如下:
|
||||||
|
|
||||||
1. `examples/basic_pipeline.py`:ORY-BOX 复眼/拼接传感器基础 OpenCV 示例(默认配置 `box`)
|
1. `examples/basic_pipeline.py`:ORY-BOX 复眼/拼接传感器基础 OpenCV 示例(默认配置 `box`)
|
||||||
2. `examples/mini_pipeline.py`:ORY-MINI 基础 OpenCV 示例(默认配置 `mini`)
|
2. `examples/mini_pipeline.py`:ORY-MINI 基础 OpenCV 示例(默认配置 `mini`)
|
||||||
3. `examples/finger_pipeline.py`:ORY-FINGER 2D UV 最小流程(默认配置 `finger`)
|
3. `examples/finger_pipeline.py`:ORY-FINGER 2D 显示最小流程(默认配置 `finger`)
|
||||||
4. `examples/finger_pipeline_3dview.py`:ORY-FINGER Qt 3D 可视化界面(默认配置 `finger`)
|
4. `examples/finger_pipeline_3dview.py`:ORY-FINGER Qt 3D 可视化界面(默认配置 `finger`)
|
||||||
|
|
||||||
`finger` 是当前 ORY-FINGER 的产品配置名。`dd02` 仍可作为兼容别名,实际会解析到 `dd02-ov`。
|
`finger` 是当前 ORY-FINGER 的产品配置名。
|
||||||
|
|
||||||
下面先介绍最基础的 ORY-BOX 运行方式。
|
下面先介绍最基础的 ORY-BOX 运行方式。
|
||||||
|
|
||||||
### Windows:
|
#### Windows
|
||||||
1. 检查是否有其他正在连接的USB摄像头设备:打开powershell, 运行命令`Get-PnpDevice -PresentOnly | Where-Object { $_.Class -match '^Camera' }`,如果没有的话不会有输出。
|
1. 检查是否有其他正在连接的 USB 摄像头设备:打开 PowerShell,运行命令 `Get-PnpDevice -PresentOnly | Where-Object { $_.Class -match '^Camera' }`,如果没有的话不会有输出。
|
||||||
2. 把传感器的USB头与电脑连接,再次运行步骤一的命令,会新增一行输出
|
2. 把传感器的 USB 头与电脑连接,再次运行步骤一的命令,会新增一行输出。
|
||||||
3. 如果第一步没有输出,传感器的ID就是“0”,如果第一步有一个设备,传感器的ID就是“1”,如此类推。
|
3. 如果第一步没有输出,传感器的 ID 就是 `0`;如果第一步有一个设备,传感器的 ID 就是 `1`,如此类推。
|
||||||
4. 在确定了传感器的ID(下方例子假设为“0”)后,打开Anaconda Prompt, 切换到工作路径,然后在命令行中输入:
|
4. 在确定了传感器的 ID(下方例子假设为 `0`)后,打开 Anaconda Prompt,切换到工作路径,然后在命令行中输入:
|
||||||
```bash
|
```bash
|
||||||
python examples/basic_pipeline.py -v 0 -c box
|
python examples/basic_pipeline.py -v 0 -c box
|
||||||
```
|
```
|
||||||
5. 程序会运行,并自动打开可视化窗口和打印资料,在窗口上按键盘”q”键可以停止运行,按”r”键可以重设数据
|
5. 程序会运行,并自动打开可视化窗口和打印资料。在窗口上按键盘 `q` 键可以停止运行,按 `r` 键可以重设数据。
|
||||||
|
|
||||||
### Ubuntu:
|
#### Ubuntu
|
||||||
1. 检查是否有其他正在连接的USB摄像头设备:打开终端, 运行命令`v4l2 –list-devices`,如果没有的话不会有输出。
|
1. 检查是否有其他正在连接的 USB 摄像头设备:打开终端,运行命令 `v4l2 --list-devices`,如果没有的话不会有输出。
|
||||||
2. 把传感器的USB头与电脑连接,再次运行步骤一的命令,会新增输出
|
2. 把传感器的 USB 头与电脑连接,再次运行步骤一的命令,会新增输出。
|
||||||
3. 根据新增的输出,例如”/dev/video0”,确定传感器的ID为“0”, 如果是”/dev/video3”的话ID为“3” ,如此类推。
|
3. 根据新增的输出,例如 `/dev/video0`,确定传感器的 ID 为 `0`;如果是 `/dev/video3`,ID 为 `3`,如此类推。
|
||||||
4. 在确定了传感器的ID(下方例子假设为“0”)后,打开终端, 激活相应的python环境,切换到工作路径,然后在命令行中输入:
|
4. 在确定了传感器的 ID(下方例子假设为 `0`)后,打开终端,激活相应的 Python 环境,切换到工作路径,然后在命令行中输入:
|
||||||
```bash
|
```bash
|
||||||
python examples/basic_pipeline.py -v 0 -c box
|
python examples/basic_pipeline.py -v 0 -c box
|
||||||
```
|
```
|
||||||
5. 程序会运行,并自动打开可视化窗口和打印资料,在窗口上按键盘”q”键可以停止运行,按”r”键可以重设数据
|
5. 程序会运行,并自动打开可视化窗口和打印资料。在窗口上按键盘 `q` 键可以停止运行,按 `r` 键可以重设数据。
|
||||||
|
|
||||||
### ORY-FINGER UV 示例
|
#### ORY-FINGER 示例
|
||||||
|
|
||||||
如果您使用的是 ORY-FINGER(配置名 `finger`),可运行:
|
如果您使用的是 ORY-FINGER(配置名 `finger`),可运行:
|
||||||
|
|
||||||
@@ -160,7 +191,7 @@ python examples/finger_pipeline.py --video ./data/sample.mp4 --config finger
|
|||||||
该示例会:
|
该示例会:
|
||||||
|
|
||||||
- 读取相机或视频源
|
- 读取相机或视频源
|
||||||
- 将图像展开到 UV 平面
|
- 将图像展开到平面
|
||||||
- 计算光流、法向/切向力和接触区域
|
- 计算光流、法向/切向力和接触区域
|
||||||
- 在 OpenCV 窗口中显示展开后的光流箭头图
|
- 在 OpenCV 窗口中显示展开后的光流箭头图
|
||||||
|
|
||||||
@@ -174,11 +205,11 @@ python examples/finger_pipeline.py --video ./data/sample.mp4 --config finger
|
|||||||
python examples/finger_pipeline.py --camera_id 0 --config finger
|
python examples/finger_pipeline.py --camera_id 0 --config finger
|
||||||
```
|
```
|
||||||
|
|
||||||
### ORY-FINGER 3D 可视化界面
|
#### ORY-FINGER 3D 可视化界面
|
||||||
|
|
||||||
如果要使用 GUI ,安装GUI依赖:
|
如果要使用 GUI,先安装 GUI 依赖:
|
||||||
```bash
|
```bash
|
||||||
pip install -e ".[gui]"
|
pip install "orisys[gui]"
|
||||||
```
|
```
|
||||||
然后运行:
|
然后运行:
|
||||||
```bash
|
```bash
|
||||||
@@ -191,5 +222,5 @@ python examples/finger_pipeline_3dview.py --video 0 --config finger
|
|||||||
- 力曲线
|
- 力曲线
|
||||||
- 3D 视图
|
- 3D 视图
|
||||||
|
|
||||||
## 开发套件使用方法
|
### 开发套件使用方法
|
||||||
参考开发套件附带的开发文档。
|
参考开发套件附带的开发文档。
|
||||||
@@ -43,7 +43,7 @@ def main():
|
|||||||
# 步骤 2.1:获取并拼接图像(必需)
|
# 步骤 2.1:获取并拼接图像(必需)
|
||||||
sensor.get_img()
|
sensor.get_img()
|
||||||
# 步骤 2.2:计算形变场(必需);返回值表示当前帧是否检测到接触/运动
|
# 步骤 2.2:计算形变场(必需);返回值表示当前帧是否检测到接触/运动
|
||||||
is_contact_now = sensor.compute_deformation(check_motion=True, threshold=0)
|
is_contact_now = sensor.compute_deformation()
|
||||||
# 步骤 2.3:仅在当前帧检测到接触/运动时计算接触区域
|
# 步骤 2.3:仅在当前帧检测到接触/运动时计算接触区域
|
||||||
# if is_contact_now:
|
# if is_contact_now:
|
||||||
sensor.compute_contact()
|
sensor.compute_contact()
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ def main():
|
|||||||
if not ok:
|
if not ok:
|
||||||
break
|
break
|
||||||
|
|
||||||
tactile.compute_deformation(check_motion=True, threshold=0)
|
tactile.compute_deformation()
|
||||||
tactile.compute_contact()
|
tactile.compute_contact()
|
||||||
|
|
||||||
fps, flow, vnormal, img, contour, centroid, depth_map, fnormal, fshearx, fsheary = tactile.read_info(
|
fps, flow, vnormal, img, contour, centroid, depth_map, fnormal, fshearx, fsheary = tactile.read_info(
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ def main():
|
|||||||
# 步骤 2.1:获取并拼接图像(必需)
|
# 步骤 2.1:获取并拼接图像(必需)
|
||||||
sensor.get_img()
|
sensor.get_img()
|
||||||
# 步骤 2.2:计算形变场(必需);返回值表示当前帧是否检测到接触/运动
|
# 步骤 2.2:计算形变场(必需);返回值表示当前帧是否检测到接触/运动
|
||||||
is_contact_now = sensor.compute_deformation(check_motion=True, threshold=0)
|
is_contact_now = sensor.compute_deformation()
|
||||||
# 步骤 2.3:仅在当前帧检测到接触/运动时计算接触区域
|
# 步骤 2.3:仅在当前帧检测到接触/运动时计算接触区域
|
||||||
# if is_contact_now:
|
# if is_contact_now:
|
||||||
sensor.compute_contact()
|
sensor.compute_contact()
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ def main():
|
|||||||
sensor.get_img()
|
sensor.get_img()
|
||||||
recorder.write_if_recording(sensor.frame)
|
recorder.write_if_recording(sensor.frame)
|
||||||
|
|
||||||
sensor.compute_deformation(check_motion=True, threshold=0)
|
sensor.compute_deformation()
|
||||||
sensor.compute_contact()
|
sensor.compute_contact()
|
||||||
fps, fn, fx, fy, flow, depth_map = sensor.read_info(
|
fps, fn, fx, fy, flow, depth_map = sensor.read_info(
|
||||||
sensor.info.FPS,
|
sensor.info.FPS,
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ def main():
|
|||||||
|
|
||||||
writer.write(save_img)
|
writer.write(save_img)
|
||||||
|
|
||||||
sensor1.compute_deformation(check_motion=False) # 计算形变和分解
|
sensor1.compute_deformation() # 计算形变和分解
|
||||||
# 提取信息:
|
# 提取信息:
|
||||||
# 向量场: VRAW, VNORMAL, VSHEAR: 原始,法向,切向
|
# 向量场: VRAW, VNORMAL, VSHEAR: 原始,法向,切向
|
||||||
# 帧率,时间戳: FPS, TIMESTAMP
|
# 帧率,时间戳: FPS, TIMESTAMP
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ class Sensor:
|
|||||||
motion_value = mag.mean()
|
motion_value = mag.mean()
|
||||||
return motion_value > threshold
|
return motion_value > threshold
|
||||||
|
|
||||||
def compute_deformation(self, decompose=True, check_motion=True, threshold=6, tail_frames=10):
|
def compute_deformation(self, decompose=True, check_motion=False, threshold=6, tail_frames=10):
|
||||||
"""
|
"""
|
||||||
Compute optical flow and optionally decompose it into force components.
|
Compute optical flow and optionally decompose it into force components.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"downstreams": [
|
||||||
|
{
|
||||||
|
"name": "orisys-sdk-release",
|
||||||
|
"path": "../orisys-sdk-release",
|
||||||
|
"sync": [
|
||||||
|
{"from": "docs", "to": "docs"},
|
||||||
|
{"from": "examples", "to": "examples", "include": [
|
||||||
|
"README.md",
|
||||||
|
"basic_pipeline.py",
|
||||||
|
"mini_pipeline.py",
|
||||||
|
"finger_pipeline.py",
|
||||||
|
"finger_pipeline_3dview.py",
|
||||||
|
"plot_data.py",
|
||||||
|
"two_sensor_pipeline.py"
|
||||||
|
]},
|
||||||
|
{"from": "config", "to": "config", "include": ["box.json", "finger.json", "mini.json"]}
|
||||||
|
],
|
||||||
|
"checks": [
|
||||||
|
"python -m py_compile examples/basic_pipeline.py examples/finger_pipeline.py examples/finger_pipeline_3dview.py examples/plot_data.py",
|
||||||
|
"python -c \"import pathlib; p=pathlib.Path('.'); hits=[f for f in p.rglob('*') if f.is_file() and 'examples/products' in f.read_text(encoding='utf-8', errors='ignore').replace('\\\\','/')]; print('stale products paths:' if hits else 'no stale products paths'); [print(x) for x in hits]\""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sensor-bench",
|
||||||
|
"path": "../sensor-bench",
|
||||||
|
"sync": [
|
||||||
|
{"from": "config", "to": "config", "include": ["box.json", "mini.json", "dd06-ov-0810.json"]}
|
||||||
|
],
|
||||||
|
"checks": [
|
||||||
|
"python tests/test_smoke.py"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "finger-bench",
|
||||||
|
"path": "../finger-bench",
|
||||||
|
"sync": [
|
||||||
|
{"from": "config", "to": "config", "include": ["finger.json", "dd02-ov.json"]},
|
||||||
|
{"from": "assets/finger", "to": "assets/finger"}
|
||||||
|
],
|
||||||
|
"checks": [
|
||||||
|
"python tests/test_smoke.py",
|
||||||
|
"python -c \"import pathlib; t=pathlib.Path('README.md').read_text(encoding='utf-8'); print('README still mentions examples/products' if 'examples/products' in t else 'README products path ok')\""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Plan and apply downstream file syncs from tools/downstream_sync_manifest.json.
|
||||||
|
|
||||||
|
The ``apply`` command copies files/directories listed in the manifest. It does
|
||||||
|
not delete extra downstream files and does not run downstream tests.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
MANIFEST_NAME = "downstream_sync_manifest.json"
|
||||||
|
|
||||||
|
|
||||||
|
def sdk_root() -> Path:
|
||||||
|
return Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
def load_manifest(root: Path) -> dict:
|
||||||
|
path = Path(__file__).resolve().parent / MANIFEST_NAME
|
||||||
|
with path.open(encoding="utf-8") as handle:
|
||||||
|
data = json.load(handle)
|
||||||
|
if data.get("version") != 1:
|
||||||
|
raise SystemExit(f"unsupported manifest version: {data.get('version')}")
|
||||||
|
if not isinstance(data.get("downstreams"), list):
|
||||||
|
raise SystemExit("manifest is missing a downstreams list")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def selected_downstreams(manifest: dict, target: str | None) -> list[dict]:
|
||||||
|
items = manifest["downstreams"]
|
||||||
|
if target is None:
|
||||||
|
return items
|
||||||
|
matches = [item for item in items if item.get("name") == target]
|
||||||
|
if not matches:
|
||||||
|
names = ", ".join(item.get("name", "<unnamed>") for item in items)
|
||||||
|
raise SystemExit(f"unknown target {target!r}; known: {names}")
|
||||||
|
return matches
|
||||||
|
|
||||||
|
|
||||||
|
def expand_jobs(root: Path, item: dict) -> list[tuple[Path, Path]]:
|
||||||
|
dest_root = (root / item["path"]).resolve()
|
||||||
|
jobs: list[tuple[Path, Path]] = []
|
||||||
|
for entry in item.get("sync", []):
|
||||||
|
src_base = root / entry["from"]
|
||||||
|
dest_base = dest_root / entry["to"]
|
||||||
|
includes = entry.get("include")
|
||||||
|
if includes:
|
||||||
|
for name in includes:
|
||||||
|
jobs.append((src_base / name, dest_base / name))
|
||||||
|
else:
|
||||||
|
jobs.append((src_base, dest_base))
|
||||||
|
return jobs
|
||||||
|
|
||||||
|
|
||||||
|
def print_plan(root: Path, items: list[dict]) -> None:
|
||||||
|
print(f"SDK root: {root}")
|
||||||
|
print()
|
||||||
|
for item in items:
|
||||||
|
dest_root = (root / item["path"]).resolve()
|
||||||
|
print(f"== {item['name']} ==")
|
||||||
|
print(f"path: {dest_root}")
|
||||||
|
jobs = expand_jobs(root, item)
|
||||||
|
if jobs:
|
||||||
|
print("sync:")
|
||||||
|
for src, dest in jobs:
|
||||||
|
print(f" {src} -> {dest}")
|
||||||
|
else:
|
||||||
|
print("sync: (none)")
|
||||||
|
checks = item.get("checks") or []
|
||||||
|
if checks:
|
||||||
|
print("checks:")
|
||||||
|
for command in checks:
|
||||||
|
print(f" {command}")
|
||||||
|
else:
|
||||||
|
print("checks: (none)")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def check_paths(root: Path, items: list[dict]) -> int:
|
||||||
|
missing: list[str] = []
|
||||||
|
print(f"SDK root: {root}")
|
||||||
|
print()
|
||||||
|
for item in items:
|
||||||
|
dest_root = (root / item["path"]).resolve()
|
||||||
|
print(f"== {item['name']} ==")
|
||||||
|
if dest_root.is_dir():
|
||||||
|
print(f"ok downstream root: {dest_root}")
|
||||||
|
else:
|
||||||
|
print(f"MISS downstream root: {dest_root}")
|
||||||
|
missing.append(str(dest_root))
|
||||||
|
for src, dest in expand_jobs(root, item):
|
||||||
|
src_ok = src.exists()
|
||||||
|
dest_note = "exists" if dest.exists() else "missing (ok until apply)"
|
||||||
|
status = "ok " if src_ok else "MISS"
|
||||||
|
print(f"{status} source: {src}")
|
||||||
|
print(f" dest: {dest} [{dest_note}]")
|
||||||
|
if not src_ok:
|
||||||
|
missing.append(str(src))
|
||||||
|
print()
|
||||||
|
if missing:
|
||||||
|
print(f"{len(missing)} missing path(s).")
|
||||||
|
return 1
|
||||||
|
print("all required source and downstream roots exist.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def copy_job(src: Path, dest: Path) -> None:
|
||||||
|
if not src.exists():
|
||||||
|
raise FileNotFoundError(src)
|
||||||
|
if src.is_dir():
|
||||||
|
shutil.copytree(src, dest, dirs_exist_ok=True)
|
||||||
|
return
|
||||||
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(src, dest)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_sync(root: Path, items: list[dict]) -> int:
|
||||||
|
if check_paths(root, items) != 0:
|
||||||
|
print("apply aborted because required paths are missing.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
copied = 0
|
||||||
|
for item in items:
|
||||||
|
print(f"== {item['name']} ==")
|
||||||
|
for src, dest in expand_jobs(root, item):
|
||||||
|
copy_job(src, dest)
|
||||||
|
copied += 1
|
||||||
|
print(f"copied {src} -> {dest}")
|
||||||
|
print()
|
||||||
|
print(f"copied {copied} sync job(s).")
|
||||||
|
print("checks were not run; execute the listed check commands manually.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(argv: list[str]) -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
sub = parser.add_subparsers(dest="command", required=True)
|
||||||
|
|
||||||
|
plan = sub.add_parser("plan", help="print intended sync jobs and checks")
|
||||||
|
plan.add_argument("--target", help="limit to one downstream name")
|
||||||
|
|
||||||
|
check = sub.add_parser("check-paths", help="verify source and dest roots exist")
|
||||||
|
check.add_argument("--target", help="limit to one downstream name")
|
||||||
|
|
||||||
|
apply_cmd = sub.add_parser("apply", help="copy manifest sources to downstreams")
|
||||||
|
apply_cmd.add_argument("--target", help="limit to one downstream name")
|
||||||
|
|
||||||
|
return parser.parse_args(argv)
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None) -> int:
|
||||||
|
args = parse_args(argv if argv is not None else sys.argv[1:])
|
||||||
|
root = sdk_root()
|
||||||
|
items = selected_downstreams(load_manifest(root), getattr(args, "target", None))
|
||||||
|
if args.command == "plan":
|
||||||
|
print_plan(root, items)
|
||||||
|
return 0
|
||||||
|
if args.command == "check-paths":
|
||||||
|
return check_paths(root, items)
|
||||||
|
if args.command == "apply":
|
||||||
|
return apply_sync(root, items)
|
||||||
|
raise SystemExit(f"unknown command: {args.command}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user