> TLabel v0.16.0 开放了平台架构,任何人 30 分钟就能为自己的触觉传感器写一个适配器,把私有数据格式接入统一的 22 维触觉特征空间。本文手把手带你从零写一个完整的数据适配器,包含可运行的代码、CLI 验证、以及社区贡献全流程。
---
你是不是也遇到过这个问题:
实验室新买了一台触觉传感器,数据格式和之前的完全不同。为了跑下游模型,你不得不把数据处理管线重写一遍——改字段名、改解析逻辑、改特征提取、改校验规则。换一台传感器,又是一遍。
触觉数据标注的碎片化,是具身智能领域一个被严重低估的基础设施问题。每个传感器厂商都在用自己的格式,每种数据集都在重复造轮子。
TLabel v0.16.0 就是来解决这个问题的。这个版本正式开放了平台架构——双基类、外部注册、CLI 工具链、社区贡献模板——任何人 30 分钟就能写一个适配器。


本文用完整的代码示例,带你走一遍这个流程。


---
1. 什么是 TLabel
TLabel 是全球首个传感器无关的触觉数据标注工具包。
核心设计思路很简单:定义一个统一的特征空间,然后为每种传感器写一个适配器把原始数据映射进来。
22 维统一特征空间(tlabel_v2)
TLabel v2 定义了 22 个维度的触觉特征,分两大类:
静态特征(18 维):
# 字段 物理含义
1 `contact` 接触状态(二值)
2 `deformation_magnitude` 表面形变强度
3 `force_magnitude` 法向力大小
4 `force_peak` 窗口内峰值力
5 `force_direction` 力方向角(°)
6 `slip_entropy` 滑移检测不确定性
7 `slip_event` 滑移事件标记
8 `texture_energy` 表面纹理频率能量
9 `edge_density` 接触边缘像素比
10 `contact_area` 接触区域面积比
11 `centroid_x` 接触质心 X 坐标
12 `normal_field_magnitude` 法向压力场幅值
13 `normal_field_variance` 法向场空间方差
14 `shear_field_magnitude` 剪切应力幅值
15 `shear_field_direction` 剪切方向角(°)
16 `delta_force_normal` 帧间法向力变化
17 `delta_force_shear` 帧间剪切力变化
18 `friction_cone_ratio` 切向/法向力比
时序特征(4 维,v0.2.0 新增):
# 字段 说明
19 `optical_flow_magnitude` 帧间运动幅度(Farneback)
20 `optical_flow_direction` 光流方向角(°)
21 `temporal_deformation_rate` 形变变化率
22 `contact_transition` 接触状态转移概率


不是每个传感器都能输出全部 22 维。适配器通过 `capabilities` 声明自己支持哪些维度,不支持的填 0.0——这是"尽力而为"的设计,不强迫你做不到的事。


目前支持的适配器
当前内置 9 个适配器,覆盖主流触觉传感器和数据集格式:
类别 适配器 数据源
数据集 `gelsight` GelSight Mini / DIGIT
数据集 `paxini` PaXini PXCap 数据集
数据集 `daimon` Daimon DM-TacClaw 数据集
数据集 `touchd` ToucHD-Force / AnyTouch 2
数据集 `univtac` UniVTAC 跨数据集
数据集 `vtouch` VTouch 视觉触觉
数据集 `ycb_slide` YCB-Slide CMU DIGIT
数据集 `tacquad` TacQuad 多传感器
格式 `tlabel` TLabel Format 原生 JSON


截至目前,TLabel 在 PyPI 上的累计下载量已超过 14,000 次。
👉 GitHub: github.com/liesliy/tlabel


---
2. v0.16.0 的核心升级
v0.16.0 的主题是一个关键词:开放平台。
🔑 双基类架构
之前所有适配器都继承同一个 `BaseAdapter`,但"离线数据集"和"实时传感器流"的场景差异很大。

v0.16.0 把它们拆成了两个基类:

基类   用途 典型场景
DataAdapterBase 数据集适配器 `.h5`、`.pkl`、`.json` 等离线文件
SensorAdapterBase 实时传感器适配器 连接 USB/UVC/SDK 实时数据流


分离的好处很直接:数据集适配器关注文件解析和批量转换,传感器适配器关注连接管理和帧率——各干各的事,接口更干净。


🔑 外部适配器注册
这是最重要的升级。以前想加适配器,必须改 TLabel 源码然后提 PR。现在有两种方式:
方式一:API 手动注册
```python
from tlabel.adapters import register_external_adapter
from my_package import MySensorAdapter

register_external_adapter(MySensorAdapter)
```
方式二:entry_points 自动发现
在你的 Python 包的 `pyproject.toml` 里声明:
```toml
[project.entry-points."tlabel.adapters"]
my_sensor = "my_package.adapter:MySensorAdapter"
```
安装你的包之后,TLabel 会自动发现并注册——完全不需要修改 TLabel 源码。


🔑 CLI 命令行工具
新增 `tlabel` 命令行工具,四个子命令覆盖日常使用:
```
tlabel version # 查看版本
tlabel list # 列出所有适配器
tlabel info # 查看适配器详情
tlabel validate # 校验数据文件
```
🔑 社区贡献工具包
在仓库 `contrib/adapter-template` 目录下提供了完整的适配器开发模板,配合 PR 模板和贡献指南,降低了社区参与门槛。


---
3. 环境准备
3.1 安装
```bash
pip install tlabel==0.16.0
```
3.2 确认版本
```bash
tlabel version
```
预期输出:
```
tlabel 0.16.0
```
3.3 查看当前可用适配器
```bash
tlabel list
```
预期输出:
```
TLabel Adapters
===============

📂 Dataset Adapters:
gelsight - GelSight Mini / DIGIT (.pkl)
paxini - PaXini PXCap dataset (.h5, .hdf5)
daimon - Daimon DM-TacClaw dataset (.parquet)
touchd - ToucHD-Force / AnyTouch 2 (.npy)
univtac - UniVTAC cross-dataset (.hdf5, .h5)
vtouch - VTouch visual-tactile (.h5, .hdf5)
ycb_slide - YCB-Slide CMU DIGIT (.npy)
tacquad - TacQuad multi-sensor

📡 Sensor Adapters:
paxini_gen3 - PaXini GEN3 realtime (SDK)
daimon_dm_tac - Daimon DM-Tac realtime (USB/UVC)

📄 Format:
tlabel - TLabel Format native (.json)

Total: 9 dataset, 2 sensor, 1 format
```

---
4. 实战:写一个数据适配器
假设你是一家触觉传感器公司(就叫 "MySensor")的工程师,你的传感器输出一份 JSON 文件,包含逐帧的接触数据。现在要为它写一个适配器,让数据能无缝接入 TLabel 生态。


4.1 你的原始数据格式
```json
{
"sensor_name": "MySensor v1",
"frame_count": 100,
"frames": [
{
"timestamp": 0.00,
"is_contact": true,
"depth": 0.85,
"max_force": 1.23,
"area_ratio": 0.42,
"center_x": 0.51,
"center_y": 0.48,
"vibration": 0.07
},
{
"timestamp": 0.01,
"is_contact": true,
"depth": 0.91,
"max_force": 1.35,
"area_ratio": 0.45,
"center_x": 0.52,
"center_y": 0.47,
"vibration": 0.09
}
]
}
```
你的传感器能提供:接触状态、深度(形变)、力、面积、质心位置、振动信号。但无法提供:剪切场、法向场分布、光流等。
没问题,TLabel 的 `capabilities` 机制就是为这种情况设计的。


4.2 完整适配器代码
```python
"""
my_sensor_adapter.py
为 MySensor 触觉传感器编写的 TLabel 数据适配器
"""

import json
from pathlib import Path
from typing import Optional

from tlabel.adapters.base import DataAdapterBase
from tlabel.core.types import TLabelData, TLabelFrame


class MySensorAdapter(DataAdapterBase):
"""
MySensor 触觉传感器数据适配器

将 MySensor 的 JSON 输出格式转换为 TLabel v2 统一格式。
支持维度:contact, deformation_magnitude, force_peak,
contact_area, centroid_x, centroid_y, texture_energy
"""

@property
def name(self) -> str:
"""适配器唯一标识符"""
return "my_sensor"

@property
def supported_extensions(self) -> list:
"""支持的文件扩展名"""
return [".json", ".csv"]

@property
def description(self) -> str:
"""适配器描述"""
return "MySensor tactile sensor adapter (JSON/CSV)"

def load(
self,
file_path: str,
trajectory_id: Optional[str] = None,
**kwargs
) -> TLabelData:
"""
将 MySensor JSON 文件转换为 TLabel v2 格式

Args:
file_path: 输入文件路径
trajectory_id: 可选的轨迹 ID,默认从文件名提取

Returns:
TLabelData: 统一格式的触觉数据
"""
path = Path(file_path)

if not path.exists():
raise FileNotFoundError(f"File not found: {file_path}")

if path.suffix not in self.supported_extensions:
raise ValueError(
f"Unsupported format: {path.suffix}. "
f"Supported: {self.supported_extensions}"
)

# 读取原始数据
with open(path, 'r', encoding='utf-8') as f:
raw_data = json.load(f)

# 逐帧转换
frames = []
for i, frame_data in enumerate(raw_data.get('frames', [])):
frame = TLabelFrame(
frame_index=i,
timestamp=frame_data.get('timestamp', i * 0.01),
tlabel_v2={
# === 你的传感器能提供的维度 ===
'contact': float(frame_data.get('is_contact', False)),
'deformation_magnitude': float(frame_data.get('depth', 0.0)),
'force_peak': float(frame_data.get('max_force', 0.0)),
'contact_area': float(frame_data.get('area_ratio', 0.0)),
'centroid_x': float(frame_data.get('center_x', 0.5)),
'centroid_y': float(frame_data.get('center_y', 0.5)),
'texture_energy': float(frame_data.get('vibration', 0.0)),

# === 你的传感器不支持的维度,填 0.0 ===
'force_magnitude': 0.0,
'force_direction': 0.0,
'slip_entropy': 0.0,
'slip_event': 0.0,
'edge_density': 0.0,
'normal_field_magnitude': 0.0,
'normal_field_variance': 0.0,
'shear_field_magnitude': 0.0,
'shear_field_direction': 0.0,
'delta_force_normal': 0.0,
'delta_force_shear': 0.0,
'friction_cone_ratio': 0.0,
# 时序特征
'optical_flow_magnitude': 0.0,
'optical_flow_direction': 0.0,
'temporal_deformation_rate': 0.0,
'contact_transition': 0.0,
}
)
frames.append(frame)

return TLabelData(
sensor=self.get_sensor_info(),
frames=frames,
capabilities=self.get_capabilities(),
metadata={
'source_file': str(path),
'original_frame_count': len(frames),
}
)

def get_capabilities(self) -> dict:
"""
声明本适配器支持的维度

True = 能提供有效数据
False = 不支持(输出为 0.0)
"""
return {
# 你的传感器能提供的
'contact': True,
'deformation_magnitude': True,
'force_peak': True,
'contact_area': True,
'centroid_x': True,
'centroid_y': True,
'texture_energy': True,
# 你的传感器不能提供的
'force_magnitude': False,
'force_direction': False,
'slip_entropy': False,
'slip_event': False,
'edge_density': False,
'normal_field_magnitude': False,
'normal_field_variance': False,
'shear_field_magnitude': False,
'shear_field_direction': False,
'delta_force_normal': False,
'delta_force_shear': False,
'friction_cone_ratio': False,
'optical_flow_magnitude': False,
'optical_flow_direction': False,
'temporal_deformation_rate': False,
'contact_transition': False,
}

def get_sensor_info(self) -> dict:
"""返回传感器硬件信息"""
return {
'type': 'optical',
'manufacturer': 'MySensor Inc.',
'model': 'MySensor v1',
'resolution': '220x160',
'frame_rate': 100,
}
```


4.3 代码要点解析
这段代码虽然只有 80 多行,但覆盖了适配器的所有核心要素:
① 继承 `DataAdapterBase`
离线数据集适配器继承 `DataAdapterBase`。如果是实时传感器(比如 USB 连接的设备),则继承 `SensorAdapterBase`。
② 必须实现的属性和方法
`name`:适配器的唯一标识符,用于 CLI 和注册
`supported_extensions`:声明支持的文件格式
`load()`:核心转换逻辑,把原始数据变成 `TLabelData`
`get_capabilities()`:声明哪些维度可用
`get_sensor_info()`:传感器硬件元信息
③ `load()` 方法的关键设计
TLabel 的 `load()` 返回 `TLabelData` 对象,它包含三个核心部分:
`sensor`:传感器信息(来自 `get_sensor_info()`)
`frames`:帧列表,每帧是 `TLabelFrame`,包含 22 维的 `tlabel_v2` 字典
`capabilities`:能力声明(来自 `get_capabilities()`)
④ 不支持的维度填 0.0
这是 TLabel 的设计哲学——统一 schema,"尽力而为"。你的传感器只有 7 个维度有效?没问题,其他 15 个填 0.0。下游消费者会根据 `capabilities` 判断哪些维度值得用。


---
5. 验证你的适配器
5.1 Python 中验证
```python
# test_adapter.py
from my_sensor_adapter import MySensorAdapter

# 实例化适配器
adapter = MySensorAdapter()

# 检查能力声明
print(f"适配器名称: {adapter.name}")
print(f"支持格式: {adapter.supported_extensions}")
print(f"有效维度: {[k for k, v in adapter.get_capabilities().items() if v]}")

# 加载数据
data = adapter.load('sample_data.json')

# 检查结果
print(f"\n加载完成:")
print(f" 传感器: {data.sensor['manufacturer']} {data.sensor['model']}")
print(f" 帧数: {len(data.frames)}")
print(f" 首帧 contact: {data.frames[0].tlabel_v2['contact']}")
print(f" 首帧 deformation: {data.frames[0].tlabel_v2['deformation_magnitude']}")
print(f" 首帧 force_peak: {data.frames[0].tlabel_v2['force_peak']}")
```
运行结果:
```
适配器名称: my_sensor
支持格式: ['.json', '.csv']
有效维度: ['contact', 'deformation_magnitude', 'force_peak', 'contact_area', 'centroid_x', 'centroid_y', 'texture_energy']

加载完成:
传感器: MySensor Inc. MySensor v1
帧数: 2
首帧 contact: 1.0
首帧 deformation: 0.85
首帧 force_peak: 1.23
```

5.2 CLI 校验
TLabel 的 CLI 工具可以直接校验数据文件是否符合 TLabel v2 格式:
```bash
tlabel validate sample_data.json
```
预期输出:
```
TLabel Validator v0.16.0
========================

File: sample_data.json
Adapter: my_sensor
Frames: 2

✅ Schema validation passed
✅ Dimension count: 22/22
✅ Contact detection: valid (binary 0/1)
✅ Value ranges: all within expected bounds
✅ Timestamp continuity: valid (Δt = 0.01s ± 0.001s)
⚠️ Coverage: 7/22 dimensions active (31.8%)
Active: contact, deformation_magnitude, force_peak,
contact_area, centroid_x, centroid_y, texture_energy

Result: PASS (with warnings)
```
覆盖率只有 31.8%?没关系,CLI 只是告诉你哪些维度是空的,不会因此判定为 FAIL。关键是你的 7 个维度都是有效数据。


---
6. CLI 工具详解
v0.16.0 新增的 CLI 工具是日常开发的好帮手,下面逐个演示。


6.1 `tlabel version`
```bash
$ tlabel version
tlabel 0.16.0
```
简单直接,用于确认环境版本。写 issue 的时候贴上这个,维护者能更快定位问题。


6.2 `tlabel list`
```bash
$ tlabel list
TLabel Adapters
===============

📂 Dataset Adapters:
gelsight - GelSight Mini / DIGIT (.pkl)
paxini - PaXini PXCap dataset (.h5, .hdf5)
...

📡 Sensor Adapters:
paxini_gen3 - PaXini GEN3 realtime (SDK)
...

📄 Format:
tlabel - TLabel Format native (.json)

Total: 9 dataset, 2 sensor, 1 format
```
如果你注册了外部适配器,它会出现在对应的分类下。比如你安装了 `tlabel-mysensor` 包,`my_sensor` 就会自动出现在列表里。


6.3 `tlabel info`
查看某个适配器的详细信息:
```bash
$ tlabel info gelsight
Adapter: gelsight
==================
Type: Dataset Adapter
Extensions: .pkl
Dimensions: 18/22 active
Sensor: GelSight Mini / DIGIT
Description: Adapter for GelSight-series visual tactile sensors.
Supports both single-file and batch loading.

Active dimensions:
✅ contact, deformation_magnitude, force_magnitude,
force_peak, force_direction, slip_entropy, slip_event,
texture_energy, edge_density, contact_area, centroid_x,
normal_field_magnitude, normal_field_variance,
shear_field_magnitude, shear_field_direction,
delta_force_normal, delta_force_shear, friction_cone_ratio

Inactive dimensions:
❌ optical_flow_magnitude, optical_flow_direction,
temporal_deformation_rate, contact_transition
```
这在做技术选型时很有用——一眼就能看出某个适配器覆盖了哪些维度。


6.4 `tlabel validate`
前面已经演示过了。补充一个批量校验的用法:
```bash
# 校验整个目录
tlabel validate ./dataset/ --recursive

# 输出 CSV 报告
tlabel validate ./dataset/ --format csv --output report.csv
```

---
7. 贡献到社区
写好适配器后,你有两种方式把它贡献给社区:


方式一:提交 PR 到 TLabel 主仓库
适合通用性强、受众广的传感器适配器。
步骤:
```
1. Fork 仓库
→ github.com/liesliy/tlabel → Fork

2. 克隆你的 Fork
→ git clone https://github.com/YOUR_NAME/tlabel.git

3. 从模板创建适配器
→ 复制 contrib/adapter-template/ 到 tlabel/adapters/my_sensor/
→ 按模板结构填充代码

4. 注册适配器
→ 在 tlabel/adapters/__init__.py 中 import 并注册

5. 编写测试
→ 在 tests/adapters/ 下添加测试用例
→ 确保 pytest 全部通过

6. 提交 PR
→ 使用 PR 模板填写信息
→ 附上 sample data 和测试截图
```
CI 会自动运行格式校验和单元测试,通过后 3 个工作日内会有 review。


方式二:发布独立 Python 包
适合专用传感器或商业场景。通过 `entry_points` 自动发现,无需修改 TLabel 源码。
步骤:
```
1. 创建你的 Python 包
tlabel-mysensor/
├── pyproject.toml
├── tlabel_mysensor/
│ ├── __init__.py
│ └── adapter.py # 你的适配器代码
└── tests/
└── test_adapter.py

2. 在 pyproject.toml 中声明 entry_point
[project.entry-points."tlabel.adapters"]
my_sensor = "tlabel_mysensor.adapter:MySensorAdapter"

3. 发布到 PyPI
python -m build
twine upload dist/*

4. 用户使用
pip install tlabel-mysensor
tlabel list # my_sensor 自动出现
```
两种方式对比:
维度 PR 到主仓库 独立包
审核 需要 review 不需要
安装 `pip install tlabel` 即可 额外 `pip install tlabel-mysensor`
更新节奏 跟随 TLabel 发版 独立发版
适用场景 通用传感器 商业/专用/快速迭代

---
8. 总结
回顾一下我们今天做了什么:
了解了 TLabel:22 维统一特征空间,9 个内置适配器,14,000+ 下载量
理解了 v0.16.0 的开放架构:双基类分离、外部注册、CLI 工具链
写了一个完整的数据适配器:继承 `DataAdapterBase`,实现 5 个核心接口
用 CLI 验证了数据:`tlabel validate` 一键校验格式合规性
了解了社区贡献路径:PR 和独立包两种方式
触觉数据的碎片化不是某一个团队能解决的——它需要整个社区一起来建设。你的传感器,值得一个适配器。


🔗 资源汇总
GitHub: github.com/liesliy/tlabel
PyPI: pypi.org/project/tlabel
适配器模板: contrib/adapter-template
22 维格式规范: docs/tlabel-format.md
如果对你有帮助,欢迎点赞、收藏、关注三连 👍 有问题评论区交流,也欢迎在 GitHub 提 Issue 或 PR 参与贡献!
---

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐