aima_phone_catcher/README.md

11 KiB
Raw Blame History

瓴控 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。在本目录执行:

uv sync --locked
uv run --locked pytest

uv sync 会创建 .venv 并按照 uv.lock 安装依赖。如果项目位于不支持虚拟环境 软链接的 Samba/NAS 挂载盘,可以把环境放到本地磁盘;后续 uv run 也要保留 同一个变量:

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]:

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 设备:

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. 服务配置与启动

复制配置:

cp .env.example .env

.env.example 已对应当前 MS3008 双电机现场,关键项为:

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_HTTP_HOST=0.0.0.0
MOTOR_HTTP_PORT=8000
MOTOR_API_TOKEN=

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,启动前导入:

set -a
. ./.env
set +a
uv run --locked lingkong-motor-service

也可直接一键启动(自动加载 .env、同步锁定依赖并检查端口冲突):

./script/launch.sh

如果 .env 不存在,脚本会根据 .env.example 创建一份,完成硬件检查并设置 MOTOR_HARDWARE_ENABLED=true 后再次运行。

服务默认监听 0.0.0.0:8000,即全部 IPv4 网络接口。本机可打开 http://127.0.0.1:8000/docs,其他电脑应访问 http://<服务器真实局域网IP>:8000/docs;0.0.0.0 只是监听地址,不能作为客户端目标。 当前 MOTOR_API_TOKEN 留空,不启用 API Key,任何能访问 TCP 8000 端口的电脑都可调用 电机接口。请只在可信局域网中使用,并用主机或网络防火墙限制可访问范围。 真实硬件上只能运行一个服务进程,不要使用 --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 是一个不依赖项目其他脚本的简单手动测试程序。 它直接使用 python-can 的 canalystii 后端,默认面板 CAN1、1 Mbps、电机 ID 1和2。 运行前要停止 HTTP 服务。

读取状态2(0x9C):

uv run --locked python tests/test_can.py status

发送 open 映射(ID 1 = +400、ID 2 = -400,持续 10 Hz, Ctrl-C 后自动零输出并 Stop):

uv run --locked python tests/test_can.py torque 400 -400

发送 close 映射(ID 1 = -400、ID 2 = +400,持续 10 Hz, Ctrl-C 后自动零输出并 Stop):

uv run --locked python tests/test_can.py torque -400 400

停止 ID 1、2:

uv run --locked python tests/test_can.py stop

仅测试一台时,另一台的值填 0,例如 torque 20 0。MS 的该控制值是开环 powerControl raw,不是 N·m 力矩。

6. HTTP 接口

完成上述低值方向验证后,正常操作顺序如下:

SERVER_IP="<服务器真实局域网IP>"
curl -X POST "http://${SERVER_IP}:8000/v1/motor/open"
# 方向切换会自动执行零输出、Stop 和 20 ms 等待
curl -X POST "http://${SERVER_IP}:8000/v1/motor/close"
curl -X POST "http://${SERVER_IP}:8000/v1/motor/open"
curl -X POST "http://${SERVER_IP}:8000/v1/motor/stop"
curl "http://${SERVER_IP}:8000/v1/motor/status"

open、close、stop 成功时均返回 HTTP 200 OK,响应 JSON 包含 "code": 200、执行动作和当前电机状态。例如:

{
  "code": 200,
  "accepted": true,
  "action": "stop",
  "state": "stopped"
}

实际响应还包含电机 ID、控制量、广播状态等字段。硬件未启用或状态冲突仍返回 HTTP 409,CAN/USB 通讯失败仍返回 HTTP 503。

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 设备。

当前配置不需要 API Key。如果以后显式设置了 MOTOR_API_TOKEN,请求才需携带:

curl -X POST -H "X-API-Key: your-token" \
  "http://${SERVER_IP}:8000/v1/motor/open"

7. 验证边界

自动化测试只验证配置、协议编码、HTTP 状态机和持续广播逻辑,不证明电机已经运动、 方向正确、状态回包可信或急停有效。真实 MS3008 验证结果必须单独记录,并包含 电机 ID、回包、raw、脉冲时长、停止结果和 USB/CAN 错误计数。