- 补充 5 层架构分层图(脚本→解析器→结构构建→硬件实现→系统库) - 详解 .mt 脚本语法、块类型、占位符规则 - 补充 PCA9685/Servo/MPU6050/Speaker 各硬件寄存器与校准参数 - 说明执行流程:stdin→FSM 解析→链表→build/play/free - 列出 I2C 总线保护、GC 析构、pthread_cancel 等安全限制 - 补充 11 条已知缺陷清单(D1-D11) - 新增 NN/CNN 测试命令与 DRL 预留接口说明
385 lines
16 KiB
Markdown
385 lines
16 KiB
Markdown
# bot4cj — 机器人硬件脚本解析器
|
||
|
||
基于**仓颉(Cangjie)** 编程语言和 Linux I2C 接口的机器人动作脚本解析器。用一种矩阵式脚本来描述机器人的硬件构成与动作序列,解析后驱动真实硬件执行。
|
||
|
||
## 架构分层
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────┐
|
||
│ .mt 矩阵脚本(硬件定义 + 动作序列) │
|
||
├──────────────────────────────────────────────────────────────┤
|
||
│ MatrixParser(P.cj) — FSM 逐字符解析 → MatrixObject 链表 │
|
||
├──────────────────────────────────────────────────────────────┤
|
||
│ RobotStruct(r.cj) — 从矩阵对象构建硬件变量(createHardVars) │
|
||
│ MatrixVars_Hardware 基类 + 闭包回调: │
|
||
│ · build() → rise 回调(打开硬件、获取资源) │
|
||
│ · play() → things 回调(执行动作,参数/返回值均为 JsonValue) │
|
||
│ · free() → free 回调(释放资源) │
|
||
├──────────────────────────────────────────────────────────────┤
|
||
│ 硬件实现:hd/*.cj │
|
||
│ PCA9685(I2C 0x40, 50Hz PWM, 角度→脉宽 0.5~2.5ms) │
|
||
│ Servo(舵机:max/min/offset 校准 + 角度翻转) │
|
||
│ MPU6050(陀螺+加速度,±500°/s ±8g,后台 10ms 采样线程+卡尔曼) │
|
||
│ Speaker(正弦波发声,5ms 淡入淡出) │
|
||
│ fb.cj(外部硬件扩展口) │
|
||
├──────────────────────────────────────────────────────────────┤
|
||
│ 系统库:cpp/ + cuda/ │
|
||
│ libi2c4cj.so — I2C glibc 封装 + 20 槽锁数组 │
|
||
│ libmt4cj.so — FP32 矩阵运算(CPU / CUDA 二选一) │
|
||
│ libfft4cj.so — 基-2 Cooley-Tukey FFT │
|
||
│ libcnn4cj.so — Conv/Pool/GA-Pool/Flatten + Adam/Momentum │
|
||
│ libsnd4cj.so — WaveAPI / ALSA 音频播放 │
|
||
├──────────────────────────────────────────────────────────────┤
|
||
│ 数学/NN 库:math/*.cj │
|
||
│ Matrix1(二维矩阵)+ Tensor + 6 种激活函数 + Xavier 初始化 │
|
||
│ Nn1 全连接层(前向/反向/权重更新) │
|
||
│ Af(Sigmoid/Tanh/ReLU/Leaky_ReLU/Swish/Softmax) │
|
||
└──────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## 技术栈
|
||
|
||
| 层 | 技术 | 说明 |
|
||
|----|------|------|
|
||
| 应用层 | **仓颉 Cangjie** | 主程序、解析器、机器人结构、数学库、HTTP 服务 |
|
||
| 系统层 | C / C++ | I2C 驱动、矩阵运算、FFT、CNN、音频 |
|
||
| 加速层 | CUDA(可选) | `MODE=cpu/cuda` 切换 |
|
||
| 脚本层 | `.mt` 矩阵脚本 | 硬件定义 + 动作序列 |
|
||
| 依赖包 | ctype4cj / pthread4cj / spi4cj / lua4cj | 仓颉官方扩展包 |
|
||
| 构建 | Makefile(C/C++ 共享库)+ cjpm.toml(仓颉包管理) | — |
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
bot4cj/
|
||
├── src/
|
||
│ ├── main.cj # 入口:CLI 参数解析(-i/-o/--test)+ stdin 管道执行
|
||
│ ├── P.cj # 矩阵脚本解析器(FSM 逐字符解析)
|
||
│ ├── r.cj # RobotStruct + MatrixVars_Hardware 基类
|
||
│ ├── testCNN.cj # CNN 测试入口
|
||
│ ├── testCNN1.cj # 几何 CNN 测试
|
||
│ ├── hd/
|
||
│ │ ├── I2C.cj # I2C 封装 + 20 槽锁数组
|
||
│ │ ├── PCA9685.cj # PCA9685 舵机控制板(已标记由 Lua 替代)
|
||
│ │ ├── Servo.cj # 舵机:校准 + 角度翻转
|
||
│ │ ├── MPU6050.cj # MPU6050 陀螺+加速度+卡尔曼滤波
|
||
│ │ ├── snd.cj # 扬声器
|
||
│ │ ├── fb.cj # 外部硬件扩展
|
||
│ │ └── extern/
|
||
│ │ └── macro_ExternHardware.cj
|
||
│ ├── math/
|
||
│ │ ├── Matrix.cj # 二维矩阵(+运算符重载)
|
||
│ │ ├── Tensor.cj # 张量
|
||
│ │ ├── Af.cj # 6 种激活函数
|
||
│ │ ├── Ini.cj # Xavier/Kaiming 初始化
|
||
│ │ ├── nn.cj # 全连接层 Nn1
|
||
│ │ ├── nns.cj # 旧版神经网络(XNOR 门测试)
|
||
│ │ ├── cnn.cj # CNN 仓颉绑定
|
||
│ │ ├── pooling.cj # 池化
|
||
│ │ ├── flatten.cj # Flatten
|
||
│ │ ├── fft.cj # FFT 仓颉绑定
|
||
│ │ ├── kalman.cj # 卡尔曼滤波
|
||
│ │ └── Adam.cj # Adam 优化器
|
||
│ ├── cpp/ # C/C++ 系统库源
|
||
│ │ ├── I2C.c # I2C glibc 封装
|
||
│ │ ├── Matrix.cpp # CPU 矩阵运算
|
||
│ │ ├── fft1.c # FFT 单实序列
|
||
│ │ ├── fft.cpp # FFT 双实序列 + 基-2 Cooley-Tukey
|
||
│ │ ├── kalman.c # 卡尔曼滤波
|
||
│ │ ├── snd.cpp # 音频播放(WaveAPI/ALSA)
|
||
│ │ └── cnn/ # 纯 C++ CNN(Conv/Pool/GA-Pool/Flatten)
|
||
│ └── cuda/
|
||
│ ├── matrix.cu # GPU 矩阵运算 kernel
|
||
│ └── error.cuh # CUDA 错误检查宏
|
||
├── modules/ # Lua 硬件模块(ADS1115/ADS1256/PCA9685/SPI)
|
||
├── scripts/ # .mt 动作脚本(std/jump/lie-down/tilt-* 等)
|
||
├── test/ # C/Lua 测试脚本
|
||
├── docs/Reports/ # 深度分析报告
|
||
├── cjpm.toml # 仓颉包配置
|
||
├── Makefile # C/C++ 共享库构建
|
||
├── _set.sh # 编译(cjpm update + build)
|
||
├── _run.sh # 运行(stdin 管道)
|
||
├── compile.ps1 # Windows 编译脚本
|
||
├── run.ps1 # Windows 运行脚本
|
||
├── README.md # 旧版说明
|
||
└── README.robot.md # 本文件
|
||
```
|
||
|
||
## 快速开始
|
||
|
||
```bash
|
||
# 1. 编译 C/C++ 共享库(一次性)
|
||
make MODE=cpu # CPU 模式(默认)
|
||
# 或
|
||
make MODE=cuda # GPU 模式(需要 nvcc)
|
||
|
||
# 2. 编译仓颉程序
|
||
bash _set.sh
|
||
|
||
# 3. 运行动作脚本(stdin 管道)
|
||
cat scripts/std.mt | bash _run.sh
|
||
```
|
||
|
||
> **硬件要求**:树莓派(aarch64)或其他 Linux I2C 设备。x86_64 开发板需自行验证 I2C 参数。Windows 暂不支持(依赖 glibc)。
|
||
|
||
## CLI 参数
|
||
|
||
```
|
||
./bot # 从 stdin 读取 .mt 脚本执行
|
||
./bot --help # 打印帮助
|
||
./bot --test nn # 运行神经网络与门测试
|
||
./bot --test cnn # 运行 CNN 测试
|
||
./bot --test cnn1 # 运行几何 CNN 测试
|
||
./bot --test matrix_o # 解析 ss/test.mt 并保存为 ss/output.mt
|
||
./bot --test matrix_2 # 解析 ss/output.mt → 执行 → 再次保存
|
||
./bot -i model.json -o out.json # 加载/保存神经网络模型(DRL 预留)
|
||
```
|
||
|
||
## .mt 脚本语法
|
||
|
||
行列式表格,每列以空格或制表符分隔,每行以分号 `;` 结尾。
|
||
|
||
### 块结构
|
||
|
||
```
|
||
@BlockName
|
||
column1 column2 column3;
|
||
value1 value2 value3;
|
||
@
|
||
```
|
||
|
||
每个 `@` 块由 `@Name` 行开始,`@` 行结束。支持的块类型:
|
||
|
||
| 块 | 说明 |
|
||
|----|------|
|
||
| `@Alias` | 列名别名(如 `A` → `angle`、`F` → `frequency`) |
|
||
| `@I2C` | 定义 I2C 设备(name + path) |
|
||
| `@PCA9685` | 定义 PCA9685 舵机控制板(name + I2C 引用 + 地址) |
|
||
| `@MPU6050` | 定义 MPU6050 传感器(name + I2C 引用 + 地址) |
|
||
| `@Speaker` | 定义扬声器(channels + sample_rate + volume + max_f + min_f) |
|
||
| `@Servo` | 定义舵机(name + max + min + offset + controller + channel) |
|
||
| `@Action` | **动作矩阵**:列头为硬件变量的属性,行为每帧的参数 |
|
||
|
||
### 占位符
|
||
|
||
`-` 表示"沿用上一行同列的值",当前行该列不产生调用。
|
||
|
||
### 完整示例
|
||
|
||
```mt
|
||
@Alias
|
||
name alias;
|
||
A angle;
|
||
F frequency;
|
||
@
|
||
|
||
@I2C
|
||
name path;
|
||
i2c-0 /dev/i2c-0;
|
||
i2c-1 /dev/i2c-1;
|
||
@
|
||
|
||
@PCA9685
|
||
name I2C address;
|
||
pca9685-0 i2c-1 0x40;
|
||
@
|
||
|
||
@Speaker
|
||
name channels sample_rate volumn max_f min_f;
|
||
spk 2 48000 0.6 5000 -;
|
||
@
|
||
|
||
@Servo
|
||
name max min offset controller channel;
|
||
s1 90 - - pca9685-0 0;
|
||
s2 180 - - - 1;
|
||
s3 -90 - - - 2;
|
||
s4 -180 - - - 3;
|
||
@
|
||
|
||
@Action
|
||
s1.A s2.A s3.A s4.A spk.F spk.delay;
|
||
45 - 45 - 10 200; # 站立
|
||
90 - - - - 20; # 左转
|
||
@
|
||
```
|
||
|
||
## 执行流程
|
||
|
||
```
|
||
stdin.read()
|
||
↓
|
||
P.MatrixParser (FSM 逐字符解析)
|
||
↓
|
||
LinkedList<MatrixObject> 链表
|
||
↓
|
||
RobotStruct.createHardVars() # 按 @I2C/@PCA9685/@Servo/@Speaker/@MPU6050 构建硬件变量
|
||
↓
|
||
robot.active() # build():逐个硬件调用 rise 回调(打开 I2C、初始化 PWM 等)
|
||
↓
|
||
robot.run() # 逐行遍历 @Action 矩阵,每列调用对应硬件的 play() 回调
|
||
↓
|
||
robot.deactive() # free():倒序释放资源
|
||
```
|
||
|
||
`play()` 的输入输出均为 `JsonValue`,实现了硬件与脚本之间的统一通信协议。
|
||
|
||
## 已支持的硬件
|
||
|
||
### PCA9685(I2C 舵机控制板)
|
||
- 地址:默认 `0x40`
|
||
- PWM 频率:50Hz(舵机标准 20ms 周期)
|
||
- 舵机脉宽映射:0°→0.5ms,180°→2.5ms
|
||
- 角度映射到 12 位 PWM 值(0-4095)
|
||
- **注意**:PCA9685.cj 代码已标记"由 Lua 脚本取代",保留作备用
|
||
|
||
### Servo(舵机)
|
||
- 支持 `max` / `min` 角度限制与 `offset` 漂移校准
|
||
- `max < 0` 时自动翻转角度(反向安装的舵机)
|
||
- 通过闭包回调与实际硬件(PCA9685/Lua 模块)解耦
|
||
|
||
### MPU6050(6 轴传感器)
|
||
- 陀螺仪:±500°/s 量程
|
||
- 加速度计:±8g 量程
|
||
- 后台 10ms 采样线程(pthread_create + pthread_cancel)
|
||
- 卡尔曼滤波融合数据
|
||
- **已知问题**:`ADDR_0` 与 `ADDR_1` 常量均为 `0x68`([MPU6050.cj:14-15](src/hd/MPU6050.cj#L14-L15)),第二个地址应改为 `0x69`
|
||
|
||
### Speaker(扬声器)
|
||
- 支持正弦波发声(指定频率 `F` + 延时 `delay`)
|
||
- 5ms 淡入淡出,避免"咔哒"声
|
||
- 通道数/采样率/音量可在 `.mt` 中配置
|
||
- **已知问题**:`play()` 仅 sleep 指定延时,真实发声依赖底层 C++ 实现
|
||
|
||
## 机器人型号适配
|
||
|
||
### Dashun 蜘蛛机器人(默认)
|
||
10 舵机 + 2 云台 + 扬声器,建模文件见 [gopsc/Dashun](https://github.com/gopsc/Dashun):
|
||
|
||
| 舵机 | 关节 | 舵机 | 关节 |
|
||
|------|------|------|------|
|
||
| s1 | 左下水平 (rb0) | s6 | 右上垂直 (lf1) |
|
||
| s2 | 左下垂直 (rb1) | s7 | 左上水平 (rf0) |
|
||
| s3 | 右下水平 (lb0) | s8 | 左上垂直 (rf1) |
|
||
| s4 | 右下垂直 (lb1) | s9 | 云台1 (cam0) |
|
||
| s5 | 右上水平 (lf0) | s10 | 云台2 (cam1) |
|
||
|
||
### 可用脚本
|
||
|
||
`scripts/` 目录下预写了多种动作脚本:
|
||
- `std.mt` — 站立姿态
|
||
- `jump.mt` — 跳跃
|
||
- `lie-down.mt` — 趴下
|
||
- `left-hand.mt` / `rght-hand.mt` — 单手动作
|
||
- `left-turn.mt` / `tilt-f.mt` / `tilt-b.mt` / `tilt-l.mt` / `tilt-r.mt` — 转向与倾斜
|
||
|
||
## 构建
|
||
|
||
### 依赖
|
||
|
||
```bash
|
||
# 仓颉 1.1.3
|
||
# 仓颉官方扩展包:ctype4cj / pthread4cj / spi4cj / lua4cj
|
||
# libc / libpthread
|
||
# liblua-5.4(可选,Lua 硬件模块支持)
|
||
sudo ln -s /usr/lib/aarch64-linux-gnu/liblua5.4.so /usr/lib/aarch64-linux-gnu/liblua.so
|
||
sudo ln -s /usr/lib/x86_64-linux-gnu/liblua5.4.so /usr/lib/x86_64-linux-gnu/liblua.so
|
||
```
|
||
|
||
### 构建步骤
|
||
|
||
```bash
|
||
# 1. 构建 C/C++ 共享库
|
||
make MODE=cpu
|
||
# 产出:libi2c4cj.so libmt4cj.so libfft4cj.so libsnd4cj.so
|
||
|
||
# 2. (可选)构建 CNN
|
||
make libcnn4cj.so
|
||
|
||
# 3. (可选)CUDA 模式需 NVIDIA CUDA Toolkit
|
||
make MODE=cuda
|
||
|
||
# 4. 仓颉主程序
|
||
bash _set.sh
|
||
```
|
||
|
||
[_set.sh](_set.sh) 执行 `cjpm update` + `cjpm build`,自动解析 cjpm.toml 依赖。
|
||
|
||
### 运行
|
||
|
||
```bash
|
||
bash _run.sh scripts/std.mt
|
||
# 或
|
||
cat scripts/std.mt | bash _run.sh
|
||
```
|
||
|
||
[_run.sh](_run.sh) 以 `cjStackSize=64MB` / `cjHeapSize=64MB` 启动。
|
||
|
||
## 神经网络支持
|
||
|
||
项目内置了全连接层 Nn1([math/nn.cj](src/math/nn.cj))与 CNN([math/cnn.cj](src/math/cnn.cj) + [cpp/cnn/](src/cpp/cnn/)):
|
||
|
||
- **Nn1**:多层感知机,支持前向传播、反向传播、权重更新
|
||
- **CNN**:Conv → ReLU → Pool → GA-Pool → Flatten → FC
|
||
- **优化器**:Adam / Momentum
|
||
- **激活函数**:Sigmoid / Tanh / ReLU / Leaky_ReLU / Swish / Softmax
|
||
- **初始化**:Xavier / Kaiming
|
||
|
||
### 测试
|
||
|
||
```bash
|
||
./bot --test nn # 与门训练(2 输入 → 1 输出)
|
||
./bot --test cnn # CNN 前向/反向测试
|
||
./bot --test cnn1 # 几何分类 CNN 测试
|
||
```
|
||
|
||
### DRL 预留(未完成)
|
||
|
||
main.cj 中预留了 DRL 流程(注释状态):加载预训练权重 → 执行动作 → 保存微调模型。通过 `-i` / `-o` 参数指定输入/输出路径。
|
||
|
||
## 安全与限制
|
||
|
||
### I2C 总线保护
|
||
- [hd/I2C.cj](src/hd/I2C.cj) 内置 **20 槽锁数组**,多硬件共享 I2C 总线时自动互斥
|
||
- 每个 I2C fd 独立加锁,避免并发写入冲突
|
||
|
||
### 硬件关闭时机
|
||
- `MatrixVars_Hardware` 析构依赖仓颉 GC,**不保证**在确定性时机调用 `free()`
|
||
- 机器人使用 `try/finally` 在 [main.cj:80-87](src/main.cj#L80) 中显式调用 `deactive()`,确保硬件资源正确释放
|
||
|
||
### 线程安全
|
||
- MPU6050 采样通过 `pthread_create` 启动后台线程
|
||
- `pthread_cancel` 依赖系统定义的取消点(sleep / 系统调用),可能在非安全点中断
|
||
|
||
### 平台支持
|
||
- ✅ Linux aarch64(树莓派)/ x86_64
|
||
- ❌ Windows(依赖 glibc)
|
||
|
||
## 已知问题
|
||
|
||
| ID | 描述 | 影响 |
|
||
|----|------|------|
|
||
| D1 | 仓颉 GC 不保证析构器调用,I2C fd 可能泄漏 | 长时间运行后 I2C 资源耗尽 |
|
||
| D2 | Speaker `play()` 仅 sleep,真实发声未接入 C++ | 扬声器无实际声音 |
|
||
| D3 | MPU6050 `pthread_cancel` 可能在非安全点中断 | 传感器数据不一致 |
|
||
| D4 | PCA9685 初始化被标记为"由 Lua 取代",但仍保留主路径 | 代码存在双实现 |
|
||
| D5 | `_set.sh` 会删除 `a.out` | 误删用户产物 |
|
||
| D6 | Softmax 雅可比矩阵实现错误(math/Af.cj) | NN 训练收敛异常 |
|
||
| D7 | Tensor 乘法仅支持 2D,3D+ 会 panic | 限制模型复杂度 |
|
||
| D8 | Tanh 导数实现错误(使用 `1 - y^2` 而非 `1 - tanh^2(x)`) | NN 训练收敛异常 |
|
||
| D9 | CNN `testGeometryCNN` 使用随机数据 | 测试无实际意义 |
|
||
| D10 | MPU6050 `ADDR_0` 与 `ADDR_1` 均为 `0x68` | 无法同时使用两个 MPU6050 |
|
||
| D11 | `@Action` 与 `@Action_Auto` 并存未合并 | 脚本语法冗余 |
|
||
|
||
## 相关资源
|
||
|
||
- [Dashun 蜘蛛机器人建模](https://github.com/gopsc/Dashun)
|
||
- [深度分析报告](docs/Reports/analysis_20260525_00.md)(28 章节,1280 行)
|
||
- [仓颉编程语言](https://cangjie-lang.org/)
|
||
- [PCA9685 数据手册](https://www.nxp.com/docs/en/data-sheet/PCA9685.pdf)
|
||
- [MPU6050 数据手册](https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf)
|
||
|
||
## 许可证
|
||
|
||
MIT License
|