aima_phone_catcher/README.md

252 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 瓴控 MS3008 双电机 HTTP 控制服务
该项目使用 `uv` 管理 Python 环境,通过 `python-can` 的
`canalystii`/PyUSB 后端直连 CANalyst-II,并提供 HTTP 接口控制
广播模式下的两台瓴控 MS3008 电机。
当前现场配置为:
- CAN 波特率:`1 Mbps`
- 电机 ID:`1,2`
- 广播控制帧:标准帧 `0x280`,DLC 8
- 持续广播频率:`10 Hz`
- HTTP `open`:ID 1 = `+400` raw,ID 2 = `-400` raw
- HTTP `close`:ID 1 = `-400` raw,ID 2 = `+400` raw
- 服务启动:自动 Enable;正常退出:自动 Stop 后 Disable
- MS3008 开环 raw 范围:`-850..+850`
同时控制 ID 1、2 时,固定控制帧分别为:
- `open`:`0x280 90 01 70 FE 00 00 00 00`
- `close`:`0x280 70 FE 90 01 00 00 00 00`
> `400` 是 MS3008 的协议原始开环控制量,不是力矩,也不是 N·m。
> 正负号只表示协议方向;机械正反方向必须逐台、低值、短脉冲实测确认。
> 上述映射是方向确认后的目标值,禁止将其用于首次上电试转。
## 1. 硬件和协议边界
- 两台电机必须已用厂商上位机设为广播模式、`1 Mbps`,ID 分别为 1 和 2。
- `0x280` 的四个 16 位小端槽位依次对应 ID 1..4。本现场使用前两个槽位,
ID 3、4 槽位写 0。总线上不要同时运行另一个广播控制程序。
- MS3008 的 `0x280` 是开环控制命令,不是 MF/MG 的力矩电流命令;不得用
“力矩 400”“400 N·m”等方式描述或标定。
- 每次 HTTP Stop 会显式执行两轮“零控制量 + 协议 Stop”,间隔 `20 ms`;
每轮的 Stop 帧最多等待 `100 ms` 让 CANalyst-II TX 队列处理完。
这仍不等于电机 ACK、物理急停、STO 或断电。
- `MOTOR_HARDWARE_ENABLED=true` 时,服务启动自动发送零输出和 `0x88` Enable;
HTTP 不再提供 `enable`/`disable` 接口。
- 服务正常退出会先执行两轮 Stop,再发送 `0x80` Disable。`0x80`
会清除电机多圈计数;`SIGKILL`、进程崩溃或断电时无法保证执行退出清理。
普通暂停应调用 `stop`;`open`/`close` 换向不会 Disable。
- USB 设备打开或 `send()` 返回不代表电机已实际转动;可在 HTTP
服务停止时使用独立手动脚本读取状态回包作为可选诊断。
## 2. 用 uv 创建环境
需要 Python 3.11 或更高版本,并先安装 [uv](https://docs.astral.sh/uv/)。在本目录执行:
```bash
uv sync --locked
uv run --locked pytest
```
`uv sync` 会创建 `.venv` 并按照 `uv.lock` 安装依赖。如果项目位于不支持虚拟环境
软链接的 Samba/NAS 挂载盘,可以把环境放到本地磁盘;后续 `uv run` 也要保留
同一个变量:
```bash
export UV_PROJECT_ENVIRONMENT=/tmp/lingkong-motor-service-venv
uv sync --locked
uv run --locked pytest
```
## 3. CANalyst-II 直连后端
当前设备为 `VID:PID 04d8:0053`。它使用厂商自定义的 USB 协议,
不会自动生成 SocketCAN `canX` 接口。服务固定使用
`python-can[canalystii]`:
```dotenv
MOTOR_CAN_CHANNEL=0
MOTOR_CAN_DEVICE=0
MOTOR_CAN_BITRATE=1000000
```
通道号与面板标识的对应关系为:
- 面板 `CAN1` → `MOTOR_CAN_CHANNEL=0`
- 面板 `CAN2` → `MOTOR_CAN_CHANNEL=1`
- `MOTOR_CAN_DEVICE=0` 表示 PyUSB 枚举到的第一块 CANalyst-II,与通道号无关。
可只读检查 USB 设备:
```bash
lsusb -d 04d8:0053
lsusb -t
```
该 USB interface 不应绑定 `usbserial_generic`,也不应把强制生成的
`ttyUSB0..5` 当成可靠 CAN 通道。如果 PyUSB 报权限错误,应为
`04d8:0053` 设置范围明确的 udev 权限规则,不要以 root 长期运行 HTTP 服务。
### CAN 接线检查
断电后检查:
- 总线两端各有约 `120 Ω` 终端,CAN_H 与 CAN_L 之间实测应约为 `60 Ω`。
- USB-CAN 与电机控制侧信号地可靠共地。
- CAN_H 对 CAN_H、CAN_L 对 CAN_L,不能接反。
- 采用总线型连接并尽量缩短支线;`1 Mbps` 对布线和终端更敏感。
该后端不能可靠报告 CAN ACK、bus-off 或真实发送失败;停止时的
TX 队列等待只证明设备队列已处理,不证明电机已收到或机械已停。因此服务的
`can_connected` 只表示 USB 对象已打开。独立手动状态脚本可读取 ID 1、2 的
`0x9C` 回包作为可选诊断。
## 4. 服务配置与启动
复制配置:
```bash
cp .env.example .env
```
`.env.example` 已对应当前 MS3008 双电机现场,关键项为:
```dotenv
MOTOR_HARDWARE_ENABLED=false
MOTOR_CAN_CHANNEL=0
MOTOR_CAN_DEVICE=0
MOTOR_CAN_BITRATE=1000000
MOTOR_IDS=1,2
MOTOR_OPEN_CONTROL_RAW=400
MOTOR_CLOSE_CONTROL_RAW=400
MOTOR_BROADCAST_FREQUENCY_HZ=10
```
`MOTOR_OPEN_CONTROL_RAW` 和 `MOTOR_CLOSE_CONTROL_RAW` 是幅值;程序按电机 ID
固定方向:`open` 为 `{1: +400, 2: -400}`,`close` 为
`{1: -400, 2: +400}`。
完成接线和物理安全准备后,将 `MOTOR_HARDWARE_ENABLED` 改为 `true`。
此开关是启动安全总闸:服务启动后会立即自动 Enable,无需 HTTP 调用。
程序不会自动读取 `.env`,启动前导入:
```bash
set -a
. ./.env
set +a
uv run --locked lingkong-motor-service
```
也可直接一键启动(自动加载 `.env`、同步锁定依赖并检查端口冲突):
```bash
./script/launch.sh
```
如果 `.env` 不存在,脚本会根据 `.env.example` 创建一份,完成硬件检查并设置
`MOTOR_HARDWARE_ENABLED=true` 后再次运行。
Swagger 界面默认为 <http://127.0.0.1:8000/docs>。真实硬件上只能运行一个服务
进程,不要使用 `--reload` 或多个 worker。
## 5. 首次实机测试顺序
必须先逐台验证,不能直接以 ID `1,2` 和目标 raw 首次试转:
1. 断开负载并架空输出轴,设置电源限流,确认物理急停和断电回路有效。
2. 在 HTTP 服务未运行时,可选执行
`uv run --locked python tests/test_can.py status`,观察 ID 1、2 的 `0x9C`
状态回包作为独立诊断。运行该脚本时不得同时启动 HTTP 服务。
3. 临时设置 `MOTOR_IDS=1`,将两个方向的控制量都改为 `20`。服务启动并自动使能后只调用一次
`open`(服务内部会以 10 Hz 持续广播 ID 1 = `+20`),约 `100 ms` 时必须显式调用 `stop`;
确认完全停稳后,才以同样方式测试 `close` 的 ID 1 = `-20/100 ms`,再显式 `stop`、停稳并
最后用 `Ctrl-C` 正常退出,由服务自动 Stop 并 Disable。
4. 保持 raw `20`,把 `MOTOR_IDS` 改为 `2`,按 ID 2 `open=-20`、
`close=+20` 的预期验证第二台电机。
5. 两台的状态、方向和停机都确认后,才逐级提高控制量。每一级、每个方向之间
都必须 Stop 并确认机械完全停稳,最终映射为 `open={1:+400,2:-400}`、
`close={1:-400,2:+400}`。
6. 首次方向验证仍必须执行“当前方向 → Stop → 确认停稳 → 另一方向”。正常运行时
`open → close` 和 `close → open` 都可直接调用;服务会自动停止当前广播,发送零输出
与 Stop,等待 `20 ms` 后开始另一方向广播。该等待不代表机械轴已经停稳。
`100 ms` 是上限目标,不是普通 shell/HTTP 调度的硬实时保证。旁站人员必须一直
可以操作物理急停;必须由操作者显式调用 HTTP `stop`,且 HTTP `stop` 不能作为唯一保护。
### 手动 CAN 测试脚本
[`tests/test_can.py`](tests/test_can.py) 是一个不依赖项目其他脚本的简单手动测试程序。
它直接使用 `python-can` 的 `canalystii` 后端,默认面板 CAN1、1 Mbps、电机 ID 1和2。
运行前要停止 HTTP 服务。
读取状态2(0x9C):
```bash
uv run --locked python tests/test_can.py status
```
发送 `open` 映射(ID 1 = `+400`、ID 2 = `-400`,持续 10 Hz,
`Ctrl-C` 后自动零输出并 Stop):
```bash
uv run --locked python tests/test_can.py torque 400 -400
```
发送 `close` 映射(ID 1 = `-400`、ID 2 = `+400`,持续 10 Hz,
`Ctrl-C` 后自动零输出并 Stop):
```bash
uv run --locked python tests/test_can.py torque -400 400
```
停止 ID 1、2:
```bash
uv run --locked python tests/test_can.py stop
```
仅测试一台时,另一台的值填 `0`,例如 `torque 20 0`。MS 的该控制值是开环
`powerControl raw`,不是 N·m 力矩。
## 6. HTTP 接口
完成上述低值方向验证后,正常操作顺序如下:
```bash
curl -X POST http://127.0.0.1:8000/v1/motor/open
# 方向切换会自动执行零输出、Stop 和 20 ms 等待
curl -X POST http://127.0.0.1:8000/v1/motor/close
curl -X POST http://127.0.0.1:8000/v1/motor/open
curl -X POST http://127.0.0.1:8000/v1/motor/stop
curl http://127.0.0.1:8000/v1/motor/status
```
`MOTOR_HARDWARE_ENABLED=true` 时,启动过程会自动 Enable。HTTP 只保留
`open`、`close`、`stop` 和 `status`,不提供 `enable`/`disable`。
`open` 固定使用 `{1:+400,2:-400}`,`close` 固定使用
`{1:-400,2:+400}`;HTTP 请求不能临时提高 raw。状态响应中
`control_raw_by_motor` 显示每台电机的实际有符号控制量;保留的 `control_raw`
字段表示 ID 1(单独配置 ID 2 时表示 ID 2)。调用任一运动接口后,服务内部线程都会以
`MOTOR_BROADCAST_FREQUENCY_HZ`
(默认 10 Hz)持续发送完整 `0x280` 四槽帧,不会自动超时,HTTP 客户端断开也不会
停止。`open` 与 `close` 相互切换时会自动执行零输出与 Stop;最终仍必须显式调用
`stop`。单次 `stop` 会自动重复两轮停止序列,不需要客户端再调用两次。
服务正常退出会自动执行两轮 Stop,再 Disable 并关闭 CAN 设备。
如果设置了 `MOTOR_API_TOKEN`,请求需携带:
```bash
curl -X POST -H "X-API-Key: your-token" \
http://127.0.0.1:8000/v1/motor/open
```
## 7. 验证边界
自动化测试只验证配置、协议编码、HTTP 状态机和持续广播逻辑,不证明电机已经运动、
方向正确、状态回包可信或急停有效。真实 MS3008 验证结果必须单独记录,并包含
电机 ID、回包、raw、脉冲时长、停止结果和 USB/CAN 错误计数。