TinyML 植物健康评估:Arduino Nano 33 BLE Sense + 步进电机扫描建模
元器件清单
| 元器件 | 数量 | 备注 |
|---|---|---|
| Arduino Nano 33 BLE Sense | 1 | 内置 APDS-9960 颜色/手势/接近传感器 |
| Digilent 步进电机 | 1 | 双轴扫描用 |
| DRV8825 步进电机驱动板 | 1 | 控制两路步进电机 |
所需工具
- 3D 打印机 (打印扫描台)
- 电烙铁
所需软件
- Microsoft VS Code
- Arduino IDE
- Edge Impulse Studio (TinyML 模型训练平台)
- Edge Impulse CLI (数据转发)
项目背景
植物叶片因病害、营养缺乏会出现发黄、斑点等颜色变化。利用 Nano 33 BLE Sense 内置的 APDS-9960 传感器扫描叶片颜色,结合 TinyML 训练分类模型,实现植物健康状态的自动评估。项目包含机械扫描台设计、数据采集、模型训练、部署推理全流程。
硬件架构
- 主控:Arduino Nano 33 BLE Sense (nRF52840, 64MHz, 1MB Flash)
- 颜色传感:板载 APDS-9960 (红外反射式 RGB + 临近)
- 运动控制:2× 步进电机 + DRV8825 驱动,实现 XY 双轴平面扫描
- 扫描台:Fusion 360 设计,丝杠传动,PLA 打印,45% 填充
TinyML 环境搭建
- 创建 Edge Impulse 项目
- 安装 Node.js + NPM
- 安装 CLI:
npm install -g edge-impulse-cli - 运行数据转发器:
edge-impulse-data-forwarder - 配置串口、项目 ID、标签 (healthy/unhealthy)
颜色数据采集流程
- 归零:上电后两轴电机逐步移动触碰限位开关,建立原点
- 定义扫描区域:代码中
boundingBox设定扫描范围 (如 40mm × 40mm) - 随机采样:在区域内随机选点,电机移动过程中连续读取颜色
- 颜色处理:
APDS.readColor(r,g,b,c)读取原始值,计算 RGB 归一化比例r/(r+g+b)等 - 数据上传:串口按
R,G,B\n格式发送,数据转发器上传到 Edge Impulse 云端并打标签
模型训练
Impulse 设计:
- 输入:三轴时间序列 (R, G, B 比例)
- 特征提取:频谱分析块 (Spectral Analysis)
- 分类器:Keras 神经网络块
测试验证
采集病叶数据测试,模型准确率约 63%。通过增加训练样本、降低学习率、调整网络结构可进一步提升。
核心代码 (leafReader.ino)
#include
#include
#include
#include "pinDefs.h"
int r, g, b, c, p;
float sum;
AccelStepper xStepper(AccelStepper::DRIVER, STEPPER_1_STEP, STEPPER_1_DIR);
AccelStepper yStepper(AccelStepper::DRIVER, STEPPER_2_STEP, STEPPER_2_DIR);
MultiStepper steppers;
// 扫描边界框 (mm) - 对应步进电机步数
const long boundingBox[2][2] = { {0, 0}, {40, 40} };
void setup() {
Serial.begin(115200);
while (!Serial);
if (!APDS.begin()) {
Serial.println("Could not init APDS9960");
while (1);
}
pinMode(X_AXIS_HOMING_SW, INPUT_PULLUP);
pinMode(Y_AXIS_HOMING_SW, INPUT_PULLUP);
xStepper.setPinsInverted(X_AXIS_DIR);
yStepper.setPinsInverted(Y_AXIS_DIR);
xStepper.setMaxSpeed(150);
yStepper.setMaxSpeed(150);
steppers.addStepper(xStepper);
steppers.addStepper(yStepper);
homeMotors();
}
void loop() {
// 随机目标位置
long randomPos[2];
randomPos[0] = random(boundingBox[0][0], boundingBox[1][0]) * STEPS_PER_MM;
randomPos[1] = random(boundingBox[0][1], boundingBox[1][1]) * STEPS_PER_MM;
steppers.moveTo(randomPos);
// 移动过程中采集颜色
while (steppers.run()) {
if (!APDS.colorAvailable() || !APDS.proximityAvailable()) continue;
APDS.readColor(r, g, b, c);
sum = r + g + b;
p = APDS.readProximity();
// 有效读数条件:有临近信号、清晰度>10、和>0
if (p && c > 10 && sum >= 0) {
float rr = r / sum;
float gr = g / sum;
float br = b / sum;
Serial.printf("%1.3f,%1.3f,%1.3f\n", rr, gr, br);
}
}
}
void homeMotors() {
// X 轴归零
while (digitalRead(X_AXIS_HOMING_SW)) xStepper.move(-1);
// Y 轴归零
while (digitalRead(Y_AXIS_HOMING_SW)) yStepper.move(-1);
xStepper.setCurrentPosition(0);
yStepper.setCurrentPosition(0);
}
引脚定义 (pinDefs.h)
#define STEPPER_1_STEP 2
#define STEPPER_1_DIR 3
#define STEPPER_2_STEP 4
#define STEPPER_2_DIR 5
#define X_AXIS_HOMING_SW 6
#define Y_AXIS_HOMING_SW 7
// 电机方向是否反向
#define X_AXIS_DIR false
#define Y_AXIS_DIR false
#define MICROSTEPS 1
#define STEPS_PER_MM 160 * MICROSTEPS // 根据丝杠导程和细分设置
原理图
3D 打印文件
Thingiverse: 扫描台 CAD 文件
改进方向
- 增加训练样本量 (健康/病叶各类多采集)
- 引入纹理特征 (如 LBP、GLCM) 配合颜色
- 使用更强分类器 (如 MobileNetV2 量化版)
- 添加环境光补偿 (APDS-9960 支持环境光测量)
- 设计自动换叶机构,实现批量筛查
制造工艺
- K30 CO₂ 传感器:室内空气质量实时监测
- 基于 1Sheeld 与 Arduino 的盲聋人士沟通辅助系统
- 智能存钱罐:Arduino Nano 控制硬币识别器实现余额显示
- Arduino + HC-05 蓝牙控制 LED:MIT App Inventor 手机 App 开发全攻略
- DFRobot 电容指纹传感器 (ID809) 接入 Arduino/ESP8266 完整教程
- 基于 Arduino Mega 与 Nextion 显示屏的交互式气象站
- Arduino Mega 驱动 Wii Nunchuk 控制 6 自由度机械臂
- Arduino Nano + 摇杆控制双步进电机:Visuino 图形化教程
- Arduino UNO + 以太网盾板实现低成本太阳辐射测量仪(热电堆原理)
- 紧凑型Arduino雷达系统:HC-SR04超声波传感器配1.8英寸显示屏
- Arduino 合成乐音:从零实现 88 键钢琴全音域
- Arduino + WiFi + IFTTT 自动办公室植物浇水系统:从土壤湿度到推送通知的完整物联网闭环