DIY 智能 Arduino 机械臂:主从控制与动作记忆教程
组件与物料
| 组件 | 说明 | 数量 |
|---|---|---|
| Arduino UNO | 主控板 | 1 |
| 微型舵机 | 5 个,构成 5 自由度 | 5 |
| 电位器 | 5 个,对应主控臂关节 | 5 |
| 面包板 | 原型搭建 | 1 |
项目简介
本教程制作一款简单机械臂,通过“主控臂”(电位器模拟关节)控制“从动臂”(舵机驱动),支持动作记录与回放。灵感源自 Stoerpeak 的“迷你机械臂”,多年前想做却因编程基础薄弱搁置,如今重拾并简化分享。
步骤 1:材料清单
- 舵机 ×5
- 电位器 ×5
- Arduino UNO
- 电源(示例用 5V 适配器)
- 面包板
- 结构材料:纸板/木板/阳光板/亚克力,随手可得即可
- 已安装 Arduino IDE
步骤 2:机械臂制作


示例用冰棒棒搭建,材料不限。双面胶粘贴舵机再用螺丝加固。主控臂将电位器粘在冰棒棒上模拟关节。所有部件安装在 A4 画板底座上。参考图片可更直观理解。
步骤 3:电路连接



- 所有舵机并联供电:红线接 +,黑/棕线接 GND
- 信号线(黄/橙)分别接 Arduino PWM 引脚
- 电位器并联接 Arduino 5V 和 GND,中间滑臂接模拟输入引脚
引脚映射:
- 数字引脚 3,5,6,9,10 控制 5 个舵机
- 模拟引脚 A0-A4 读取 5 个电位器
- 引脚 3 对应 A0,引脚 5 对应 A1,以此类推
关键提示: 舵机不由 Arduino 供电时,必须将舵机 GND 与 Arduino GND 共地,否则无法工作。
步骤 4:编程逻辑

核心逻辑:读取电位器值存入数组,回放时遍历数组驱动舵机复现动作。参考教程“Arduino Potentiometer Servo Control & Memory”。
全局变量声明
#include <Servo.h>
// 舵机对象
Servo Servo_0, Servo_1, Servo_2, Servo_3, Servo_4;
// 电位器读值
int Pot_0, Pot_1, Pot_2, Pot_3, Pot_4;
// 舵机当前位置
int Servo_0_Pos, Servo_1_Pos, Servo_2_Pos, Servo_3_Pos, Servo_4_Pos;
// 上一次位置
int Prev_0_Pos, Prev_1_Pos, Prev_2_Pos, Prev_3_Pos, Prev_4_Pos;
// 当前位置(用于比较)
int Current_0_Pos, Current_1_Pos, Current_2_Pos, Current_3_Pos, Current_4_Pos;
int Servo_Position; // 存储角度
int Servo_Number; // 存储舵机编号
int Storage[600]; // 动作记录数组(增大消耗更多内存)
int Index = 0; // 数组索引
char data = 0; // 串口指令
setup() 初始化
void setup() {
Serial.begin(9600);
Servo_0.attach(3); Servo_1.attach(5); Servo_2.attach(6);
Servo_3.attach(9); Servo_4.attach(10);
// 初始化位置 100°(避免 0/180 极限抖动)
Servo_0.write(100); Servo_1.write(100); Servo_2.write(100);
Servo_3.write(100); Servo_4.write(100);
Serial.println("Press 'R' to Record and 'P' to play");
}
电位器映射函数 Map_Pot()
void Map_Pot() {
// 舵机限位 1-179°,避免极限抖动
Pot_0 = analogRead(A0);
Servo_0_Pos = map(Pot_0, 0, 1023, 1, 179);
Servo_0.write(Servo_0_Pos);
Pot_1 = analogRead(A1);
Servo_1_Pos = map(Pot_1, 0, 1023, 1, 179);
Servo_1.write(Servo_1_Pos);
Pot_2 = analogRead(A2);
Servo_2_Pos = map(Pot_2, 0, 1023, 1, 179);
Servo_2.write(Servo_2_Pos);
Pot_3 = analogRead(A3);
Servo_3_Pos = map(Pot_3, 0, 1023, 1, 179);
Servo_3.write(Servo_3_Pos);
Pot_4 = analogRead(A4);
Servo_4_Pos = map(Pot_4, 0, 1023, 1, 179);
Servo_4.write(Servo_4_Pos);
}
主循环 loop()
void loop() {
Map_Pot(); // 实时跟随主控臂
// 串口指令处理
while (Serial.available() > 0) {
data = Serial.read();
if (data == 'R') Serial.println("Recording Moves...");
if (data == 'P') Serial.println("Playing Recorded Moves...");
}
// 录制模式
if (data == 'R') {
// 保存当前位置为上一帧
Prev_0_Pos = Servo_0_Pos; Prev_1_Pos = Servo_1_Pos;
Prev_2_Pos = Servo_2_Pos; Prev_3_Pos = Servo_3_Pos;
Prev_4_Pos = Servo_4_Pos;
Map_Pot(); // 重新读取对比
// 对比各舵机位置变化,记录差异帧
if (abs(Prev_0_Pos - Servo_0_Pos) > 0) { // 修正:原代码逻辑有误,应比较差值
if (Current_0_Pos != Servo_0_Pos) {
Storage[Index] = Servo_0_Pos + 0; // 舵机 0 标识偏移 0
Index++;
}
Current_0_Pos = Servo_0_Pos;
}
// 舵机 1-4 同理,偏移量分别 +100, +200, +300, +400
if (abs(Prev_1_Pos - Servo_1_Pos) > 0) {
if (Current_1_Pos != Servo_1_Pos) {
Storage[Index] = Servo_1_Pos + 100; Index++;
}
Current_1_Pos = Servo_1_Pos;
}
if (abs(Prev_2_Pos - Servo_2_Pos) > 0) {
if (Current_2_Pos != Servo_2_Pos) {
Storage[Index] = Servo_2_Pos + 200; Index++;
}
Current_2_Pos = Servo_2_Pos;
}
if (abs(Prev_3_Pos - Servo_3_Pos) > 0) {
if (Current_3_Pos != Servo_3_Pos) {
Storage[Index] = Servo_3_Pos + 300; Index++;
}
Current_3_Pos = Servo_3_Pos;
}
if (abs(Prev_4_Pos - Servo_4_Pos) > 0) {
if (Current_4_Pos != Servo_4_Pos) {
Storage[Index] = Servo_4_Pos + 400; Index++;
}
Current_4_Pos = Servo_4_Pos;
}
// 串口打印调试信息
Serial.print(Servo_0_Pos); Serial.print(" \t ");
Serial.print(Servo_1_Pos); Serial.print(" \t ");
Serial.print(Servo_2_Pos); Serial.print(" \t ");
Serial.print(Servo_3_Pos); Serial.print(" \t ");
Serial.println(Servo_4_Pos);
Serial.print("Index = "); Serial.println(Index);
delay(50);
}
// 回放模式
if (data == 'P') {
for (int i = 0; i < Index; i++) {
Servo_Number = Storage[i] / 100; // 解码舵机编号
Servo_Position = Storage[i] % 100; // 解码角度
switch (Servo_Number) {
case 0: Servo_0.write(Servo_Position); break;
case 1: Servo_1.write(Servo_Position); break;
case 2: Servo_2.write(Servo_Position); break;
case 3: Servo_3.write(Servo_Position); break;
case 4: Servo_4.write(Servo_Position); break;
}
delay(50);
}
}
}
代码文件下载: Record-Play.ino
步骤 5:测试验证
上传代码后打开“串口监视器”(工具菜单),Arduino 复位。此时主控臂可实时驱动从动臂,但未录制。
- 输入 R 开始录制,操作主控臂完成动作
- 输入 P 回放录制动作,循环执行直到复位
动作流畅度尚不及 Stoerpeak 原版,欢迎大神优化代码或提出建议。
代码文件
- Record-Play.ino (完整代码)
- 代码片段 #1-4 (分模块展示)
原理图
制造工艺
- 基于 Arduino 与 Python 的可靠 RFID 考勤系统
- 基于 Arduino、1Sheeld 与红外控制的万能安卓遥控器
- Arduino 与智能手机打造智能电压表——简单易做的 DIY 项目
- 基于 Arduino MKR1000 与 ThingSpeak 的远程 IoT 机械臂控制系统
- 智能物联网罐:ESP8266 + Arduino + 超声波传感器实时监测罐内物位
- 基于Arduino UNO的精准频率与占空比测量
- 打造功能完整的 Arduino 驱动 3D 打印机械臂:分步指南
- 智能自系鞋带鞋:内置压电发电与计步功能
- Arduino Mega 驱动 Wii Nunchuk 控制 6 自由度机械臂
- Arduino 声纳实时测距与 Processing 可视化
- Bolt IoT + Arduino UNO 实现 6 路 LED 无级调光
- 基于 Arduino 与 ESP8266 的 WiFi 智能门锁:手机扫码解锁