> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wuji.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# 触觉接触网格视图

## 概述

本篇实践介绍如何将 `tactile_binary` 接触判定渲染为终端网格。应用场景包括观察接触位置、检查标定效果和调整接触灵敏度。

对应的 Python 示例为 [8.tactile\_contact\_view.py](https://github.com/wuji-technology/wuji-sdk/blob/main/examples/python/wuji_glove/8.tactile_contact_view.py)，C 示例为 [6\_tactile\_contact\_view.c](https://github.com/wuji-technology/wuji-sdk/blob/main/examples/c/wuji_glove/6_tactile_contact_view.c)。 触觉数据的字段定义见[触觉](/docs/wuji-glove/sdk-data-reference/tactile)，本页说明集成流程和运行时行为。

<Warning>
  本文的网格渲染、布局映射、参数行为、未标定时的表现均为实测结果。**有接触时的画面未实测**——手套需要有人戴着并真实触碰才会产生接触。
</Warning>

## 前置条件

### 硬件

| 项目 | 要求 |
| - | - |
| Wuji Glove | 1 只，**已完成触觉标定** |
| 连接方式 | 以太网 |
| 终端 | 至少 31 列宽、26 行高 |

### 软件

| 项目 | 要求 |
| - | - |
| 操作系统 | Ubuntu 22.04 LTS 及以上版本 |
| Wuji SDK | v2026.8.31 或更高版本 |
| Python | 3.10 及以上版本 |
| C 工具链 | CMake 3.16 及以上版本，以及解压后的 SDK 包 |

<Tabs>
  <Tab title="Python">
    ```bash theme={null}
    pip install wuji-sdk
    ```
  </Tab>

  <Tab title="C">
    ```bash theme={null}
    cmake -S examples/c/wuji_glove -B build/glove \
      -DWUJI_SDK_INCLUDE_DIR="${SDK}/include" \
      -DWUJI_SDK_LIB="${SDK}/lib/libwuji_sdk_c.so"
    cmake --build build/glove --target 6_tactile_contact_view
    ```
  </Tab>
</Tabs>

### 前序步骤

**先做触觉标定。** 没标定过的手套 `tactile_binary` 精确恒为 0，网格永远是空的。见[触觉标定](/docs/wuji-glove/sdk-practice/tactile-calibration)。

判断标不标定过，直接看这条流是不是全零：

```python theme={null}
frame = glove.tactile_binary().subscribe().recv()
print("已标定" if any(v > 0 for v in frame.data) else "全零，可能未标定")
```

## 网格是原始阵列的几何排布

这是理解整个视图的关键：**24 × 31 = 744**，正好等于原始 `tactile` 流的通道数。网格不是重新采样出来的，就是把 744 个通道按传感器的物理排布铺开。

映射规则实测为行优先：

```text theme={null}
index = row * 31 + col
```

`tactile`、`tactile_binary`、`tactile_residual` 三条流长度都是 744，无效通道位置完全一致（实测均为 218 个）。所以三个视图的网格轮廓是同一个形状。

### 无效通道勾出手的轮廓

实测按行统计无效通道数：

```text theme={null}
行  0: 27    行  6: 9     行 12: 0     行 18: 9
行  1: 18    行  7: 9     行 13: 2     行 19: 9
行  2: 14    行  8: 9     行 14: 6     行 20: 9
行  3: 14    行  9: 6     行 15: 5     行 21: 11
行  4: 14    行 10: 2     行 16: 4     行 22: 12
行  5: 11    行 11: 0     行 17: 6     行 23: 12
```

边缘行无效多、中间行几乎为零。渲染出来就是一个手的形状——五根手指在上方分叉，掌心在中间连成一片。

没有任何一整列全是无效通道，744 这个数字本身已经是剔除过硬件屏蔽列之后的形态。

## 图例

| 字符 | 判据 | 含义 |
| - | - | - |
| `#` | `v > 0.5` | 接触 |
| `.` | `-0.5 <= v <= 0.5` | 手表面，无接触 |
| 空格 | `v < -0.5` | 无效或被屏蔽的 taxel |

`-1.0` 是无效通道的哨兵值，所以空格判据写成 `v < -0.5` 而不是 `v == -1.0`，避开浮点相等比较。

## 状态行

网格上方一行：

```text theme={null}
contacts=0   sensitivity=1.0 peak=0.000 row=0 col=14 len=744  (+/- sensitivity, q quit)
```

| 字段 | 含义 |
| - | - |
| `contacts` | 本帧 `v > 0.5` 的 taxel 数 |
| `sensitivity` | 当前灵敏度 |
| `peak` | 本帧最大值 |
| `row` / `col` | 最大值所在的网格坐标，由 `peak_idx // 31` 与 `peak_idx % 31` 算出 |
| `len` | 帧长度，正常是 744 |

## 灵敏度有两层钳制，只有一层是真的

此处为本篇的关键限制。

| 层 | 范围 | 行为 |
| - | - | - |
| 示例层 `clamp_sensitivity` | `[0.5, 3.0]` | 超出就钳到边界 |
| SDK 参数层 | **不钳制** | 任意值都写得进去 |

实测直接调 SDK 接口：

```text theme={null}
set(1.5) -> 读回 1.5
set(3.0) -> 读回 3.0
set(5.0) -> 读回 5.0
set(0.1) -> 读回 0.1
```

绕过示例，`glove.set("algorithms.tactile_binary.sensitivity", 5.0)` 能成功写入并读回。示例的 `[0.5, 3.0]` 是**推荐范围**，不是 SDK 强制的边界。

所以命令行传 `--sensitivity 20.0` 时，状态行显示的是 `3.0`——示例先钳了再写：

```text theme={null}
═══ sensitivity=20.0 ═══
contacts=0   sensitivity=3.0 peak=0.000 ...
```

### 这个参数写的是设备

`SENSITIVITY_PARAM = "algorithms.tactile_binary.sensitivity"`，设置通过 `glove.set()` 下发，影响的是 `tactile_binary` 这条流本身，不是本地显示。同一时刻别的订阅者也会受影响。

未设置过时读取会抛异常，不是返回默认值：

```text theme={null}
WujiException: Parameter not set: algorithms.tactile_binary.sensitivity
```

示例的 `set_sensitivity` 特意包了 try，写失败也不让视图挂掉——观察工具中途崩掉比读数不准更糟。

## 渲染限流

流是 130 Hz，渲染限到 10 Hz：

```python theme={null}
RENDER_INTERVAL_S = 0.1

if now - self._last_render < RENDER_INTERVAL_S:
    return
```

回调在 SDK 工作线程上按帧触发，不限流的话终端刷屏会把这条线程拖垮。**这是回调里做渲染的标准写法**：快速判断该不该画，不该画就立刻返回。

## 集成步骤

完整示例：[Python tactile\_binary 视图](https://github.com/wuji-technology/wuji-sdk/blob/main/examples/python/wuji_glove/8.tactile_contact_view.py) 和 [C tactile\_binary 视图](https://github.com/wuji-technology/wuji-sdk/blob/main/examples/c/wuji_glove/6_tactile_contact_view.c)。

以下为接入片段。Python 导入与 manager 初始化、C 初始化与连接错误分支、回调定义均沿用 [Python 示例](https://github.com/wuji-technology/wuji-sdk/blob/main/examples/python/wuji_glove/8.tactile_contact_view.py) 和 [C 示例](https://github.com/wuji-technology/wuji-sdk/blob/main/examples/c/wuji_glove/6_tactile_contact_view.c)。C 每次调用应检查返回状态，失败时按 [Python 示例](https://github.com/wuji-technology/wuji-sdk/blob/main/examples/python/wuji_glove/8.tactile_contact_view.py) 和 [C 示例](https://github.com/wuji-technology/wuji-sdk/blob/main/examples/c/wuji_glove/6_tactile_contact_view.c) 释放已有资源并退出。

### 1. 连接目标手套

按序列号连接目标手套。多设备环境不要依赖扫描结果顺序。

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    glove = manager.connect(sn=serial_number, device_name="my_glove")
    ```
  </Tab>

  <Tab title="C">
    ```c theme={null}
    WujiConnectTarget target = {
        .kind = WUJI_CONNECT_TARGET_KIND_SN, .value = serial_number,
    };
    struct WujiDevice *dev = NULL;
    wuji_connect(&target, "glove_0", NULL, &dev);
    ```
  </Tab>
</Tabs>

### 2. 设置灵敏度并订阅

二值流的灵敏度写入设备，影响所有订阅者。写入失败时保留视图并报告错误，再注册回调。

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    try:
        glove.set("algorithms.tactile_binary.sensitivity", sensitivity)
    except WujiException as exc:
        print(exc, file=sys.stderr)
    sub = glove.tactile_binary().subscribe_with_callback(on_binary)
    ```
  </Tab>

  <Tab title="C">
    ```c theme={null}
    if (wuji_glove_set_tactile_binary_sensitivity(dev, sensitivity) != WUJI_STATUS_OK)
        fprintf(stderr, "%s\n", wuji_last_error());
    struct WujiSub *sub = NULL;
    wuji_glove_subscribe_tactile_binary(dev, on_binary, NULL, &sub);
    ```
  </Tab>
</Tabs>

### 3. 限时运行并收尾

视图退出时先关闭订阅，再断开设备。回调线程不直接执行阻塞渲染。

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    try:
        time.sleep(seconds)
    finally:
        sub.close()
        manager.disconnect_all()
    ```
  </Tab>

  <Tab title="C">
    ```c theme={null}
    sleep(seconds);
    wuji_sub_close(sub);
    wuji_dev_disconnect(dev);
    wuji_dev_release(dev);
    wuji_shutdown();
    ```
  </Tab>
</Tabs>

## 运行与输出

<Tabs>
  <Tab title="Python">
    ```bash theme={null}
    python examples/python/wuji_glove/8.tactile_contact_view.py --sn <serial number>
    python examples/python/wuji_glove/8.tactile_contact_view.py --sn <serial number> \
      --seconds 60 --sensitivity 1.5
    ```
  </Tab>

  <Tab title="C">
    ```bash theme={null}
    ./build/glove/6_tactile_contact_view
    ```
  </Tab>
</Tabs>

实测输出（未标定、无人佩戴）：

```text theme={null}
contacts=0   sensitivity=1.0 peak=0.000 row=0 col=14 len=744  (+/- sensitivity, q quit)
              ....
      ....   ......   ...
     ......  ......  .....
     ......  ......  .....
     ......  ......  .....
 ... ......  ......  .....
...........  ......  .....
...........  ......  .....
...........  ......  .....
...........  ......  ..... ...
...................  ..........
...............................
...............................
.... .................... .....
     .................... .....
.    .................... .....
.    ..........................
.    ..................... ...
.    .....................
.    .....................
.    .....................
.    ...................
.    ..................
.    ..................
```

以上为实机运行结果，设备是右手 Wuji Glove，手套静置无人佩戴且未做触觉标定。

网格轮廓清晰可读：上方五根手指分叉，中间掌心连成一片，左侧竖着一列是拇指。`contacts=0` 与 `peak=0.000` 是未标定的典型表现——**这个轮廓来自无效通道的分布，与是否完成标定无关**，所以看到手形不等于流有数据。

标定并戴上手套之后，接触区域会变成 `#`，`contacts` 随按压面积增长，`peak` 上升，`row` / `col` 指向按得最重的位置。

## 注意事项

| 分支点 | 表现 | 处理 |
| - | - | - |
| 未做触觉标定 | 网格全是 `.`，`contacts=0`，`peak=0.000` 恒定 | 先跑触觉标定 |
| 手套未佩戴 | 同上 | 戴上手套触碰 |
| 灵敏度超出 \[0.5, 3.0] | 示例钳到边界后再下发 | 需要越界值就绕过示例直接 `set()` |
| 灵敏度参数写失败 | 打一行提示到 stderr，视图继续 | 检查设备连接 |
| 非交互终端 | 键盘控制不可用，跑满 `--seconds` 或等 `Ctrl-C` | 用 `--sensitivity` 预设值 |
| `--seconds 0` | 一直跑到按 `q` 或 `Ctrl-C` | 默认就是 0 |
| 帧长度不是 744 | 超出部分按无效处理 | 示例用 `limit = min(len(data), 24*31)` 兜底 |

交互式终端下按 `+` / `-` 调灵敏度、`q` 退出。

## 扩展用法

### 用它验证标定质量

标定完挨个区域按一遍，看网格对应位置是不是亮起。哪片区域怎么按都不亮，说明标定时那里没被覆盖到，回去重标那一组动作。这比看 `dead_pct` 一个数字更能定位问题。

### 调灵敏度找工作点

`+` / `-` 实时调。压着不放时慢慢调低，直到刚好不再误报，就是这只手套的工作点。记录到应用配置中。

### 想自己定阈值就看残差

`tactile_binary` 的判定阈值是 SDK 按灵敏度算的。需要完全控制阈值时，使用 `tactile_residual`。该流提供连续值，阈值由应用侧设定。见[触觉残差网格视图](/docs/wuji-glove/sdk-practice/tactile-residual-view)。

### 网格坐标对应到点云

状态行的 `row` / `col` 可以换回通道索引 `row * 31 + col`，再对上 `tactile_point_cloud` 的空间坐标，就知道接触点在三维空间的位置。点云只含 526 个有效点，换算时要跳过无效通道。

## 相关内容

* [触觉标定](/docs/wuji-glove/sdk-practice/tactile-calibration)：本篇的前置条件
* [触觉残差网格视图](/docs/wuji-glove/sdk-practice/tactile-residual-view)：同一网格，看连续信号
* [数据订阅：回调](/docs/wuji-glove/sdk-practice/subscribe-callback)：744 / 526 / 218 三个通道数的来历
* [通用数据类型](/docs/wuji-sdk/data-reference/)：`TactileBinary` 结构


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.