Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

## [Unreleased]

### 变更

- 统一灵巧手型号命名:L25 SDK 重命名为 L20,O30i SDK 重命名为 O30;新 API 仅使用 L20 和 O30。

### 新增

- 新增灵巧手原始角度与逻辑角度映射,覆盖 L6、O6、L20Lite 和 L25
Expand Down
26 changes: 13 additions & 13 deletions docs/zh/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# 参考

- [CAN 总线](reference/can.md)
- [CAN FD 总线(L30 / O20 / O30i)](reference/canfd.md)
- [CAN FD 总线(L30 / O20 / O30)](reference/canfd.md)
- [L6](reference/l6/README.md)
- [角度控制](reference/l6/angle.md)
- [速度控制](reference/l6/speed.md)
Expand Down Expand Up @@ -41,10 +41,10 @@
- [故障管理](reference/o20/fault.md)
- [设备信息](reference/o20/version.md)
- [轮询与事件流](reference/o20/stream-polling.md)
- [O30i](reference/o30i/README.md)
- [实现状态与测试范围](reference/o30i/implementation-status.md)
- [HandProtocol_v1.0 协议](reference/o30i/hand-protocol-v1.md)
- [运行时控制与状态](reference/o30i/runtime.md)
- [O30](reference/o30/README.md)
- [实现状态与测试范围](reference/o30/implementation-status.md)
- [HandProtocol_v1.0 协议](reference/o30/hand-protocol-v1.md)
- [运行时控制与状态](reference/o30/runtime.md)
- [L20Lite](reference/l20lite/README.md)
- [角度控制](reference/l20lite/angle.md)
- [速度控制](reference/l20lite/speed.md)
Expand All @@ -53,14 +53,14 @@
- [力传感器](reference/l20lite/force-sensor.md)
- [故障管理](reference/l20lite/fault.md)
- [版本信息](reference/l20lite/version.md)
- [L25](reference/l25/README.md)
- [角度控制](reference/l25/angle.md)
- [速度控制](reference/l25/speed.md)
- [扭矩控制](reference/l25/torque.md)
- [温度读取](reference/l25/temperature.md)
- [力传感器](reference/l25/force-sensor.md)
- [故障管理](reference/l25/fault.md)
- [版本信息](reference/l25/version.md)
- [L20](reference/l20/README.md)
- [角度控制](reference/l20/angle.md)
- [速度控制](reference/l20/speed.md)
- [扭矩控制](reference/l20/torque.md)
- [温度读取](reference/l20/temperature.md)
- [力传感器](reference/l20/force-sensor.md)
- [故障管理](reference/l20/fault.md)
- [版本信息](reference/l20/version.md)
- [O6](reference/o6/README.md)
- [角度控制](reference/o6/angle.md)
- [速度控制](reference/o6/speed.md)
Expand Down
2 changes: 1 addition & 1 deletion docs/zh/src/guide/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ with L6(side="left", interface_name="can0") as hand:
| L6 | 6 | `6 x 256` |
| O6 | 6 | `6 x 256` |
| L20Lite | 10 | `10 x 256` |
| L25 | 16 | `16 x 256` |
| L20 | 16 | `16 x 256` |

映射表默认保存到 `~/.config/linkerbot/hand_angle_mappings.toml`,并按手型号、左右手和 CAN 接口分别保存,例如 `l6:left:can0`。如果需要按项目或设备指定独立配置文件,可以在构造手对象时传入 `angle_mapping_path`。

Expand Down
2 changes: 1 addition & 1 deletion docs/zh/src/guide/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ uv add linkerbot --extra kinetix
> conda install pinocchio -c conda-forge
> ```

如果只使用灵巧手(L6 / L20 Lite / L25 等),无需安装此依赖。
如果只使用灵巧手(L6 / L20 Lite / L20 等),无需安装此依赖。
32 changes: 16 additions & 16 deletions docs/zh/src/reference/canfd.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# CAN FD 总线(L30 / O20 / O30i
# CAN FD 总线(L30 / O20 / O30

本页说明如何在 Linux 上通过 **python-can + SocketCAN** 连接 L30、O20 和 O30i。SDK 仍保留厂商动态库后端;如需使用 `libcanbus.so` / `HCanbus.dll`,请分别参考 [L30](./l30/README.md)、[O20](./o20/README.md) 和 [O30i](./o30i/README.md)。
本页说明如何在 Linux 上通过 **python-can + SocketCAN** 连接 L30、O20 和 O30。SDK 仍保留厂商动态库后端;如需使用 `libcanbus.so` / `HCanbus.dll`,请分别参考 [L30](./l30/README.md)、[O20](./o20/README.md) 和 [O30](./o30/README.md)。

## 两种 CAN FD 后端

| 后端 | `interface_type` | 适用环境 | 连接参数 |
| ---------- | ------------------ | --------------- | ------------------------------------------------------------------- |
| 厂商动态库 | `"ctypes"`(默认) | Linux / Windows | `device_index`、`channel_index` 或 O20 的 `device`、`channel` |
| SocketCAN | `"socketcan"` | Linux | L30 使用 `channel="can0"`;O20/O30i 使用 `socketcan_channel="can0"` |
| 后端 | `interface_type` | 适用环境 | 连接参数 |
| ---------- | ------------------ | --------------- | ------------------------------------------------------------------ |
| 厂商动态库 | `"ctypes"`(默认) | Linux / Windows | `device_index`、`channel_index` 或 O20 的 `device`、`channel` |
| SocketCAN | `"socketcan"` | Linux | L30 使用 `channel="can0"`;O20/O30 使用 `socketcan_channel="can0"` |

选择 SocketCAN 后不需要 `libcanbus.so`,但 Linux 必须已经识别 CAN 适配器,并将对应网络接口配置为 CAN FD 模式。

Expand Down Expand Up @@ -47,7 +47,7 @@ sudo ip link set can0 up
ip -details -statistics link show can0
```

L30、O20 和 O30i 默认都使用 `frame_type=0x04`,即发送 CAN FD 帧但不启用 bit-rate switching。同一条按上述方式配置的 CAN FD 链路仍需保留 5 Mbit/s 数据段,以接收设备可能发送的 BRS 帧。
L30、O20 和 O30 默认都使用 `frame_type=0x04`,即发送 CAN FD 帧但不启用 bit-rate switching。同一条按上述方式配置的 CAN FD 链路仍需保留 5 Mbit/s 数据段,以接收设备可能发送的 BRS 帧。

## 连接 L30

Expand Down Expand Up @@ -76,7 +76,7 @@ L30 的 SocketCAN 参数:
| `channel` | `None` | SocketCAN 接口名,例如 `"can0"` |
| `bitrate` | `1_000_000` | 仲裁段速率 |
| `data_bitrate` | `5_000_000` | 数据段速率 |
| `frame_type` | `0x04` | CAN FD、不启用 BRS;`0x0C` 显式启用 BRS |
| `frame_type` | `0x04` | CAN FD、不启用 BRS;`0x0C` 显式启用 BRS |
| `auto_reconfigure` | `False` | 是否允许 SDK 调用 `ip link` 重新配置接口 |
| `node_id` | `1` | L30 设备 NodeID,范围 1~31 |
| `host_id` | `0` | 主机节点 ID,范围 0~31 |
Expand Down Expand Up @@ -114,24 +114,24 @@ O20 的 SocketCAN 参数:
| `side` | `"right"` | 决定默认 `device_id` |
| `device_id` | `None` | 显式覆盖 O20 设备 ID |

L30 与 O20/O30i 的 SocketCAN 接口参数名称不同,是为了保留各手型原有构造函数中整数 `channel` 参数的兼容性。
L30 与 O20/O30 的 SocketCAN 接口参数名称不同,是为了保留各手型原有构造函数中整数 `channel` 参数的兼容性。

## 连接 O30i
## 连接 O30

O30i 使用 HandProtocol_v1.0(HOP)、11 位标准帧 ID,实测默认请求 ID 为 `0x001`、响应 ID 为 `0x401`。它与 O20 一样使用 CAN FD、无 BRS(`frame_type=0x04`)。
O30 使用 HandProtocol_v1.0(HOP)、11 位标准帧 ID,实测默认请求 ID 为 `0x001`、响应 ID 为 `0x401`。它与 O20 一样使用 CAN FD、无 BRS(`frame_type=0x04`)。

```python
from linkerbot import O30i
from linkerbot import O30

with O30i(
with O30(
interface_type="socketcan",
socketcan_channel="can0",
) as hand:
info = hand.version.get_device_info(timeout_ms=1000)
print(info.product_model, info.protocol_name, info.protocol_version)
```

O30i 的 SocketCAN 参数:
O30 的 SocketCAN 参数:

| 参数 | 默认值 | 说明 |
| ------------------- | ----------- | ---------------------------------------- |
Expand All @@ -144,7 +144,7 @@ O30i 的 SocketCAN 参数:
| `request_id` | `0x001` | 11 位标准帧请求 ID |
| `response_id` | `None` | 默认自动计算为 `request_id \| 0x400` |

O30i 的连接、协议安全边界和控制接口详见 [O30i reference](./o30i/README.md)。
O30 的连接、协议安全边界和控制接口详见 [O30 reference](./o30/README.md)。

## 让 SDK 配置接口

Expand Down Expand Up @@ -206,7 +206,7 @@ ip -details link show can0
- 灵巧手和 CAN 适配器是否供电;
- CAN_H、CAN_L 和 GND 是否正确连接;
- 总线两端是否各有一个 120 Ω 终端电阻;
- L30 的 `node_id`、O20 的 `side/device_id`,或 O30i 的 `request_id/response_id` 是否与设备一致;
- L30 的 `node_id`、O20 的 `side/device_id`,或 O30 的 `request_id/response_id` 是否与设备一致;
- `ip -details -statistics link show can0` 中是否出现 `bus-off`、错误计数持续增长;
- 是否显式设置了 `frame_type=0x0C`;若 BRS 发送后 bus-off 或发送队列卡住,恢复默认 `0x04`;
- `candump -tz -x can0` 是否能看到请求帧和设备应答帧。
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# L25 灵巧手
# L20 灵巧手

## 快速开始

```python
from linkerbot import L25
from linkerbot import L20

with L25(side="left", interface_name="can0") as hand:
with L20(side="left", interface_name="can0") as hand:
# 设置角度
hand.angle.set_angles([50.0] * 16)

Expand All @@ -25,7 +25,7 @@ with L25(side="left", interface_name="can0") as hand:

## 关节说明

L25 灵巧手拥有 16 个自由度,分布在 5 根手指上。
L20 灵巧手拥有 16 个自由度,分布在 5 根手指上。

| 索引 | 名称 | 标识 |
| ---- | ---------- | -------------- |
Expand Down Expand Up @@ -60,13 +60,13 @@ L25 灵巧手拥有 16 个自由度,分布在 5 根手指上。

## 统一流式读取

L25 提供统一的事件流接口,通过 `hand.stream()` 和 `hand.start_polling()` 获取所有传感器数据。初始化时会自动以默认间隔启动轮询(角度 60 Hz、力传感器 30 Hz),无需手动调用 `start_polling()`。
L20 提供统一的事件流接口,通过 `hand.stream()` 和 `hand.start_polling()` 获取所有传感器数据。初始化时会自动以默认间隔启动轮询(角度 60 Hz、力传感器 30 Hz),无需手动调用 `start_polling()`。

```python
from linkerbot import L25
from linkerbot.hand.l25 import SensorSource, AngleEvent, TemperatureEvent
from linkerbot import L20
from linkerbot.hand.l20 import SensorSource, AngleEvent, TemperatureEvent

with L25(side="left", interface_name="can0") as hand:
with L20(side="left", interface_name="can0") as hand:
# 可同时指定多个传感器及各自的轮询间隔(秒)
# 再次调用 start_polling() 会覆盖之前的设置
hand.start_polling({SensorSource.ANGLE: 0.05, SensorSource.TEMPERATURE: 1.0})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# 角度控制

通过 `hand.angle` 控制和读取 L25 灵巧手的 16 个关节电机角度。
通过 `hand.angle` 控制和读取 L20 灵巧手的 16 个关节电机角度。

- **角度范围**: 0-100
- **单位**: 无量纲(映射到关节电机实际角度)

## 设置角度

```python
from linkerbot import L25
from linkerbot.hand.l25 import L25Angle
from linkerbot import L20
from linkerbot.hand.l20 import L20Angle

# 使用列表(16 个关节)
hand.angle.set_angles([50.0] * 16)

# 使用 L25Angle 对象
angles = L25Angle(
# 使用 L20Angle 对象
angles = L20Angle(
thumb_abd=50.0, # 拇指侧摆
thumb_yaw=30.0, # 拇指旋转
thumb_root1=60.0, # 拇指根部
Expand Down Expand Up @@ -47,7 +47,7 @@ hand.angle.set_raw_angles([128] * 16)

SDK 发送控制帧前会将“标准 raw 值”通过角度映射表转换为“硬件 raw 值”。默认映射是线性直通:`0 -> 0`,`255 -> 255`。

L25 的映射表形状为 `16 x 256`:
L20 的映射表形状为 `16 x 256`:

- 行:关节索引,顺序与 [关节说明](./README.md#关节说明) 一致。
- 列:标准 raw 输入值 `0-255`。
Expand All @@ -61,14 +61,14 @@ hand.angle.set_angle_mapping(mapping)
hand.angle.reset_angle_mapping()
```

映射表默认保存到 `~/.config/linkerbot/hand_angle_mappings.toml`。保存时会按手型号、左右手和 CAN 接口区分,例如 `l25:left:can0`。也可以在构造手对象时通过 `angle_mapping_path` 指定 TOML 路径。
映射表默认保存到 `~/.config/linkerbot/hand_angle_mappings.toml`。保存时会按手型号、左右手和 CAN 接口区分,例如 `l20:left:can0`。也可以在构造手对象时通过 `angle_mapping_path` 指定 TOML 路径。

## 读取角度

### 阻塞读取

```python
from linkerbot import L25
from linkerbot import L20
from linkerbot.exceptions import TimeoutError

try:
Expand All @@ -93,7 +93,7 @@ if data:
通过顶层 `hand.stream()` 统一接收所有传感器事件:

```python
from linkerbot.hand.l25 import SensorSource, AngleEvent
from linkerbot.hand.l20 import SensorSource, AngleEvent

hand.start_polling({SensorSource.ANGLE: 0.1})

Expand All @@ -110,9 +110,9 @@ finally:
## 完整示例

```python
from linkerbot import L25
from linkerbot import L20

with L25(side="left", interface_name="can0") as hand:
with L20(side="left", interface_name="can0") as hand:
# 设置角度
hand.angle.set_angles([0.0] * 16)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 故障管理

L25 灵巧手的故障检测与清除功能。
L20 灵巧手的故障检测与清除功能。

## 概述

Expand All @@ -11,7 +11,7 @@ L25 灵巧手的故障检测与清除功能。

## 故障码表

L25 使用位标志(Flag)表示故障状态,每个关节电机可同时存在多个故障。
L20 使用位标志(Flag)表示故障状态,每个关节电机可同时存在多个故障。

| 故障码 | 值 | 说明 |
| --------------------- | --- | ------------ |
Expand Down Expand Up @@ -44,7 +44,7 @@ except TimeoutError:

**返回值**:`FaultData` 对象,包含:

- `faults`:`L25Fault` 故障数据
- `faults`:`L20Fault` 故障数据
- `timestamp`:时间戳

**异常**:
Expand Down Expand Up @@ -74,7 +74,7 @@ hand.fault.clear_faults()
通过顶层 `hand.stream()` 统一接收所有传感器事件:

```python
from linkerbot.hand.l25 import SensorSource, FaultEvent
from linkerbot.hand.l20 import SensorSource, FaultEvent

hand.start_polling({SensorSource.FAULT: 0.2})

Expand All @@ -93,7 +93,7 @@ finally:

## 数据类说明

### L25Fault 属性
### L20Fault 属性

| 属性 | 说明 |
| -------------- | ---------- |
Expand All @@ -114,20 +114,20 @@ finally:
| `pinky_root1` | 小指根部 |
| `pinky_tip` | 小指指尖 |

### L25Fault 方法
### L20Fault 方法

```python
# 检查是否有任何故障
faults.has_any_fault() # -> bool

# 转为列表
faults.to_list() # -> list[L25FaultCode]
faults.to_list() # -> list[L20FaultCode]

# 索引访问
faults[0] # thumb_abd
```

### L25FaultCode 方法
### L20FaultCode 方法

```python
# 检查单个关节电机是否有故障
Expand All @@ -142,9 +142,9 @@ faults.thumb_abd.get_fault_names() # -> list[str]
### 检查故障状态

```python
from linkerbot import L25
from linkerbot import L20

with L25(side="left", interface_name="can0") as hand:
with L20(side="left", interface_name="can0") as hand:
# 读取故障状态
data = hand.fault.get_blocking(timeout_ms=500)

Expand All @@ -164,10 +164,10 @@ with L25(side="left", interface_name="can0") as hand:
### 持续监控

```python
from linkerbot import L25
from linkerbot.hand.l25 import SensorSource, FaultEvent
from linkerbot import L20
from linkerbot.hand.l20 import SensorSource, FaultEvent

with L25(side="left", interface_name="can0") as hand:
with L20(side="left", interface_name="can0") as hand:
hand.start_polling({SensorSource.FAULT: 0.2})

try:
Expand Down
Loading