工业级循线搬运机器人 — 物料自动化传输方案
零部件与材料
| SparkFun Arduino Pro Mini 328 - 5V/16MHz | ×1 | |
| 定制 PCB | ×1 | 布局文件见附件,也可不做 PCB 飞线实现 |
| 超声波测距模块 HC-SR04 | ×1 | |
| RFID 模块 (RC522 等) | ×1 | |
| SparkFun RedBot 循线传感器 | ×3 | |
| TI L293D H 桥驱动器 (1A) | ×1 | 兼容 L293D |
| 2S 锂聚合物电池 1000mAh 7.4V | ×1 | 必须 7.4V,型号不限 |
| 140 RPM 减速电机对 | ×1 | 需自行打印连接底盘的连接件 |
| 65mm 车轮 | ×1 | |
| 跳线若干 | ×1 | |
| 拨动开关 | ×1 | |
| RFID 标签 | ×4 | 数量视任务数而定 |
| 蜂鸣器 | ×1 | |
| 40P 单排排针 | ×1 |
所需工具
- 3D 打印机
- 激光切割机(无则可外发,作者在线下单)
- 电烙铁
软件与在线服务
- Arduino IDE
- Autodesk Fusion 360(机械设计)
项目详解
参观波兰奥佩尔工厂见到 AGV 物料搬运机器人,原理是高级循线:磁条替代黑胶带,磁传感器替代光电传感器,外加安全功能。作者决定自制低成本、开源、Arduino 驱动的迷你版,智能搬运物料。
设计理念
先手绘草图(纸笔无限制、无干扰、快速迭代),再进 Fusion 360 赋予尺寸。作者不擅绘图但草图足以确定整体形态。

部分 3D 打印,部分激光切割 4mm 胶合板(也可用亚克力)。前部 3D 打印件集成传感器、电子仓、电池仓。渲染图效果极佳:


激光切割外发(波兰本地厂商),要求厂家补偿激光切口宽度使指榫配合完美。DXF 文件见附件。

前部 3D 打印件(白色 PLA,2 周壁 30% 填充 0.2mm 层高,需支撑,约 8 小时)。作者打磨、喷漆、原子灰刮平、再打磨喷漆,历时 2 天,镜面效果堪比注塑:

电子系统:Arduino、传感器、电机、PCB

核心:Arduino Pro Mini + 定制 PCB(Fritzing 设计,文件见附件)。
传感器:3 路循线传感器 + RFID 读卡器(识别线旁标签执行任务)。
驱动:L293D 驱动两减速电机。
蜂鸣器:识别特定 RFID 标签时鸣叫。
供电:2S LiPo (7.4V),PCB 上 3.3V 稳压给 RFID。
避障:HC-SR04 超声波,但读取耗时长,作者另做了一个快速测距子项目(文中链接)优化此问题。
PCB 制造:碳粉热转印法,腐蚀、清洗、钻孔、焊接。单层板需 5 根跳线(Fritzing 蓝线)。




电池座焊接 XT60/XT30 公头,通电测试 Arduino 亮灯无冒烟:


电机引线预留长焊上 PCB,最后再剪短。

机械组装

指榫涂木工胶固化数小时,确保刚性。



3D 打印前盖用短螺丝(6mm)从底部锁入,配平垫保护胶合板,顶面无螺丝痕迹:


安装电机、后万向轮、传感器(M3 螺丝):



机身组装完成,接线进前盖电子仓。因电池实测尺寸超预期,电池放左侧,电子放右侧,均完美贴合。
超声波模块就位。电池接口加拨动开关,后盖预留开关孔。



固件逻辑

合盖前通过 USB-UART 给 Pro Mini 烧录。代码注释详尽,另附 C# 编写的 任务规划器(ILFPlanner.exe),可视化生成任务数组粘贴进 Arduino IDE。需预知 RFID 卡 UID,可另做简易读卡器获取。

最终组装与测试

合上前盖双翻盖,拧紧短螺丝。整理内部空间塞进所有元件。


成品全貌:电子隐藏前仓,传感器/电机外露。充电需打开后盖取电池,改进建议:在 3D 件开孔引出平衡充电线。


实地测试:模拟产线
搭建简易产线:纸板铺地(黑胶带反光不足),父子协助。任务流程:
1. 机器人取料 → 2. 不同工位取走贴标签件 → 3. 成品送入仓库。
视频记录全过程,一切按设计运行:

项目风险点多(首次手绘设计、首次激光切割、C# 非专业、PCB 单层跳线多),但全流程意外顺利。作者乐于分享,欢迎评论提问!
源代码
工业循线机器人 Arduino 代码
/* Industrial Line Follower - by Nikodem Bartnik
* https://NikodemBartnik.pl | https://www.youtube.com/user/nikodembartnik
*/
#include <SPI.h>
#include <MFRC522.h>
// 引脚定义(若原理图改动需同步修改)
#define LINESENSOR1 6
#define LINESENSOR2 7
#define LINESENSOR3 8
#define BUZZER A0
#define MOTOR1A 2
#define MOTOR1B 3
#define MOTOR2A 4
#define MOTOR2B 5
#define RFID_SDA 10
#define RFID_SCK 13
#define RFID_MOSI 11
#define RFID_MISO 12
#define RFID_RST 9
#define DISTANCESENSOR A1
// 常量
#define STOP_TIME 3000
#define BEEP_LENGTH 200
#define MOTOR_SPEED 110
#define MINIMUM_TIME_BETWEEN_CARDS 2000
MFRC522 mfrc522(10, 9);
long last_card_read;
// 任务数组(由 C# 任务规划器生成,粘贴此处)
#define COMMANDS_LENGTH 4
char* Type[4] = {"Stop", "Beep&stop", "Beep&ignore", "Stop"};
int Value[4] = {3000, 1000, 0, 5000};
char* CardID[4] = {"12 14 B1 2F", "F6 34 F9 25", "ED B9 E0 2B", "83 87 3B 2E"};
void setup() {
SPI.begin(); mfrc522.PCD_Init();
pinMode(LINESENSOR1, INPUT); pinMode(LINESENSOR2, INPUT); pinMode(LINESENSOR3, INPUT);
pinMode(BUZZER, OUTPUT);
pinMode(MOTOR1A, OUTPUT); pinMode(MOTOR1B, OUTPUT);
pinMode(MOTOR2A, OUTPUT); pinMode(MOTOR2B, OUTPUT);
pinMode(DISTANCESENSOR, INPUT);
}
void loop() {
// 循线逻辑:中间传感器在线 -> 直行;左/右偏离 -> 左/右修正
if (digitalRead(LINESENSOR1) == LOW && digitalRead(LINESENSOR2) == HIGH && digitalRead(LINESENSOR3) == LOW) Forward();
else if (digitalRead(LINESENSOR1) == HIGH && digitalRead(LINESENSOR2) == LOW && digitalRead(LINESENSOR3) == LOW) { Left(); delay(20); }
else if (digitalRead(LINESENSOR1) == LOW && digitalRead(LINESENSOR2) == LOW && digitalRead(LINESENSOR3) == HIGH) { Right(); delay(20); }
// RFID 任务触发(防抖)
if (millis() - last_card_read >= MINIMUM_TIME_BETWEEN_CARDS) {
if (!mfrc522.PICC_IsNewCardPresent()) return;
if (!mfrc522.PICC_ReadCardSerial()) return;
String content = "";
for (byte i = 0; i < mfrc522.uid.size; i++) {
content.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
content.concat(String(mfrc522.uid.uidByte[i], HEX));
}
content.toUpperCase(); content = content.substring(1);
for (int a = 0; a < COMMANDS_LENGTH; a++) {
last_card_read = millis();
if (content == CardID[a]) {
if (Type[a] == "Stop") { Stop(); delay(Value[a]); }
else if (Type[a] == "Beep&stop") { digitalWrite(BUZZER, HIGH); Stop(); delay(Value[a]); digitalWrite(BUZZER, LOW); }
else if (Type[a] == "Beep&ignore") { Stop(); digitalWrite(BUZZER, HIGH); delay(500); digitalWrite(BUZZER, LOW); }
else if (Type[a] == "Ignore") { /* 继续循线 */ }
}
}
}
}
void Forward() {
digitalWrite(MOTOR1A, HIGH); analogWrite(MOTOR1B, 255 - MOTOR_SPEED);
digitalWrite(MOTOR2A, HIGH); analogWrite(MOTOR2B, 255 - MOTOR_SPEED);
}
void Backward() {
digitalWrite(MOTOR1A, LOW); analogWrite(MOTOR1B, MOTOR_SPEED);
digitalWrite(MOTOR2A, LOW); analogWrite(MOTOR2B, MOTOR_SPEED);
}
void Left() {
digitalWrite(MOTOR1A, LOW); analogWrite(MOTOR1B, MOTOR_SPEED);
digitalWrite(MOTOR2A, HIGH); analogWrite(MOTOR2B, 255 - MOTOR_SPEED);
}
void Right() {
digitalWrite(MOTOR1A, HIGH); analogWrite(MOTOR1B, 255 - MOTOR_SPEED);
digitalWrite(MOTOR2A, LOW); analogWrite(MOTOR2B, MOTOR_SPEED);
}
void Stop() {
digitalWrite(MOTOR1A, LOW); digitalWrite(MOTOR1B, LOW);
digitalWrite(MOTOR2A, LOW); digitalWrite(MOTOR2B, LOW);
}
任务规划器 (C#)
无预览(仅供下载 ZIP)。
任务规划器可执行文件
无预览(Windows 直接运行,无需安装)。
自定义部件与外壳
- 前盖 3D 打印件(白 PLA,打磨喷漆镜面,约 8 小时,需支撑)
- 左侧盖板
- 右侧盖板
- 激光切割 DXF:后板、底板、左板、右板(4mm 材料)
- Fusion 360 源文件(可编辑)
原理图
- Fritzing 源文件
- 完整连接原理图
- 单面 PCB 制作用 PDF(无顶层)
制造工艺