Files
micro_servers/bot4cj/README.robot.md
T
qing f214c862d3 docs(bot4cj): 新增 README.robot.md 完整技术手册
- 补充 5 层架构分层图(脚本→解析器→结构构建→硬件实现→系统库)
- 详解 .mt 脚本语法、块类型、占位符规则
- 补充 PCA9685/Servo/MPU6050/Speaker 各硬件寄存器与校准参数
- 说明执行流程:stdin→FSM 解析→链表→build/play/free
- 列出 I2C 总线保护、GC 析构、pthread_cancel 等安全限制
- 补充 11 条已知缺陷清单(D1-D11)
- 新增 NN/CNN 测试命令与 DRL 预留接口说明
2026-07-30 11:54:51 +08:00

16 KiB
Raw Blame History

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      # 本文件

快速开始

# 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 动作矩阵:列头为硬件变量的属性,行为每帧的参数

占位符

- 表示"沿用上一行同列的值",当前行该列不产生调用。

完整示例

@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),第二个地址应改为 0x69

Speaker(扬声器)

  • 支持正弦波发声(指定频率 F + 延时 delay)
  • 5ms 淡入淡出,避免"咔哒"声
  • 通道数/采样率/音量可在 .mt 中配置
  • 已知问题:play() 仅 sleep 指定延时,真实发声依赖底层 C++ 实现

机器人型号适配

Dashun 蜘蛛机器人(默认)

10 舵机 + 2 云台 + 扬声器,建模文件见 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 — 转向与倾斜

构建

依赖

# 仓颉 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

构建步骤

# 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 执行 cjpm update + cjpm build,自动解析 cjpm.toml 依赖。

运行

bash _run.sh scripts/std.mt
# 或
cat scripts/std.mt | bash _run.sh

_run.sh 以 cjStackSize=64MB / cjHeapSize=64MB 启动。

神经网络支持

项目内置了全连接层 Nn1(math/nn.cj)与 CNN(math/cnn.cj + cpp/cnn/):

  • Nn1:多层感知机,支持前向传播、反向传播、权重更新
  • CNN:Conv → ReLU → Pool → GA-Pool → Flatten → FC
  • 优化器:Adam / Momentum
  • 激活函数:Sigmoid / Tanh / ReLU / Leaky_ReLU / Swish / Softmax
  • 初始化:Xavier / Kaiming

测试

./bot --test nn      # 与门训练(2 输入 → 1 输出)
./bot --test cnn     # CNN 前向/反向测试
./bot --test cnn1    # 几何分类 CNN 测试

DRL 预留(未完成)

main.cj 中预留了 DRL 流程(注释状态):加载预训练权重 → 执行动作 → 保存微调模型。通过 -i / -o 参数指定输入/输出路径。

安全与限制

I2C 总线保护

  • hd/I2C.cj 内置 20 槽锁数组,多硬件共享 I2C 总线时自动互斥
  • 每个 I2C fd 独立加锁,避免并发写入冲突

硬件关闭时机

  • MatrixVars_Hardware 析构依赖仓颉 GC,不保证在确定性时机调用 free()
  • 机器人使用 try/finally 在 main.cj:80-87 中显式调用 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 并存未合并 脚本语法冗余

相关资源

许可证

MIT License