进阶遥控夹爪机器人:精密抓取 DIY 指南
组件与物料
| Runt Rover Half-pint 底盘 | × | 1 |
| Arduino UNO | × | 1 |
| V5 传感器扩展板 | × | 1 |
| 母对母杜邦线 | × | 1 |
| L9110S 电机驱动板 | × | 1 |
| 90° 单角道支架 (585424) | × | 1 |
| 轻量级舵机轮毂 (Futaba 适配) | × | 1 |
| 标准夹爪套件 A (637094) | × | 1 |
| MG995 舵机 | × | 1 |
| 标准舵机安装板 | × | 1 |
| 90° 单角道支架 | × | 1 |
| 5 节 AA 电池盒 | × | 1 |
| AA 充电电池 (NiMH) | × | 5 |
| .750" 8-32 尼龙隔离柱 | × | 2 |
| .500" 6-32 螺丝含螺母 | × | 8 |
| .3125" 6-32 螺丝 | × | 8 |
应用与在线服务
| Arduino IDE |
项目背景
几个月前儿子想要一台遥控机器人,列了清单后我们精简为可落地方案。最终成品如下:
- 将 UNO 与 V5 传感器扩展板扣合
- 将组合板卡扣入底盘中央夹持座
- 双面胶固定电机驱动板于左侧内壁,蓝牙模块固定于右侧内壁
底盘可自制或选用其他 4WD 封闭式底盘。
步骤 1:所需软件
- Arduino IDE
- Python 2.7 (完整安装含 tkinter)
- pySerial 库
- L9110S 电机驱动库 (
L9110Driver.zip) - 机器人 Arduino 代码 (
blue_t_slave.zip) - Python 遥控上位机程序 (
rover.py)
步骤 2:硬件 BOM 详细清单
- Runt Rover Half-pint 底盘或同类 4WD 封闭底盘
- Arduino Uno + USB 线
- V5 传感器扩展板
- L9110S 电机驱动板 (买两块备用,价格便宜)
- MG995 舵机 ×2
- HC-05/HC-06 蓝牙模块 ×1
- 标准舵机安装板 B ×1
- 90° 支架 ×1、单支架 ×2
- 轻量舵机轮毂 (Futaba 525125) ×1
- 标准夹爪套件 A ×1
- 母对母杜邦线
- 5 节 AA 充电电池 (NiMH) + 充电器
- 双面胶与小扎带
- 5 节 AA 电池盒 (若选用 AA 方案)
- 或 6V NiMH / 7.4V Li-ion 电池组 + 充电器
- 1.250" 6-32 螺丝 ×2
- .750" 8-32 尼龙隔离柱 ×2
- .500" 6-32 螺丝含螺母 ×8
- .3125" 6-32 螺丝 ×8
零件可从 eBay、Sparkfun、Adafruit、ServoCity 采购。
步骤 3:底盘组装
按说明书或视频组装底盘。控制器安装板置于中间孔位,预留前后空间。重要:后续步骤前请先拆下 4 个车轮,避免装配时损坏电机。
步骤 4:夹爪组装
按视频教程组装夹爪。
步骤 5:安装夹爪 - 第一阶段
用料:90° 支架、轻量舵机轮毂、4 颗 .3125" 螺丝。
- 轮毂固定于支架一侧
- 两根单支架、1.250" 螺丝、.500" 螺丝含螺母、2 根尼龙隔离柱、舵机安装板 B
- 单支架穿过底盘内侧,长螺丝在左、短螺丝在右,螺丝头朝内
- 外侧装第二块单支架,长螺丝套入尼龙隔离柱,锁紧舵机安装板
步骤 6:安装夹爪 - 第二阶段
- 第二个舵机装入舵机支架,轴朝上,4 颗 .3125" 螺丝固定
- 带轮毂的 90° 支架安装夹爪,用 4 颗 .500" 螺丝,中间垫 2 个 8-32 螺母作间隔
- 夹爪组件装上舵机输出轴,用舵机自带螺丝锁紧
- 两路舵机线穿过平板孔引至车内
步骤 7:安装电子模块
- UNO 与 V5 扩展板扣合,装入底盘中央卡座
- 双面胶固定电机驱动板于左侧内壁,蓝牙模块于右侧内壁
步骤 8:电机接线
- A-1A → 扩展板引脚 5
- A-1B → 引脚 6
- B-1A → 引脚 3
- B-1B → 引脚 11
车头朝外,左侧电机:两根黑线接 Motor A 第 1 端子,两根红线接第 2 端子;右侧电机同理接 Motor B。
舵机接线:
- Y 轴舵机 (俯仰) → 扩展板引脚 2 (白/橙为信号线)
- X 轴舵机 (夹爪开合) → 扩展板引脚 7
步骤 9:蓝牙模块接线
- 蓝牙 Tx → 扩展板引脚 0 (Arduino Rx)
- 蓝牙 Rx → 扩展板引脚 1 (Arduino Tx)
步骤 10:供电连接
- 5 节 AA 电池盒红线 → 扩展板 Vcc,黑线 → GND
- 确认 V5 扩展板跳线帽已接在跳线针上
- L9110S GND → 扩展板 GND
- 电机电池正极 → L9110S Vcc,负极 → L9110S GND
步骤 11:配置 HC-05/HC-06 蓝牙模块
在电脑 OS 蓝牙设置中配对,记下分配的串口设备名/编号。Windows/Linux/Mac 均可,Linux 推荐 blueman。
步骤 12:烧录 Arduino 代码
- 安装 Arduino IDE
- 安装 L9110 驱动库:解压
L9110Driver.zip到 libraries 文件夹,或用 IDE “添加 .ZIP 库” - 打开
blue_t_slave.ino,选板 Uno,选端口,上传
步骤 13:运行 Python 上位机控制程序
- 解压
rover.py到任意目录 - 终端进入目录,运行
python rover.py - 弹出 GUI 窗口,输入蓝牙串口号点击“Connect”
- 连接成功后控件启用,即可操控
控制说明
键盘控制:
- 方向键:前进、后退、左转 90°、右转 90°
- a:左转 45°、s:右转 45°
- h:停止
- y:设置夹爪 Y 轴 (俯仰)
- u:设置夹爪开合
- c:夹爪复位
前进/后退为持续指令,直到发新指令或停止。转向为定时动作,延时后自动停车。滑块范围 0-180:Y 轴 0=最高 180=最低;X 轴 0=全合 180=全开。需点击“Set”或按对应键生效。“Disconnect”按钮会停车并复位夹爪。
代码
Python 上位机 GUI (rover.py)
#!/usr/bin/env python
# Linux/BSD/Mac 可 chmod +x 直接执行
###########
# Rover Control App
# Written by Scott Beasley - 2015
# Free to use or modify. Enjoy.
###########
import sys, serial, time
from Tkinter import *
import tkFont, tkMessageBox
class App(Frame):
def createWidgets(self):
self.connected = False
self.message = StringVar()
helv6 = tkFont.Font(family='Helvetica', size=6, weight='normal')
self.frame = Frame(self.master); self.frame.pack()
# 顶部连接栏
self.f1 = Frame(self.frame)
Label(self.f1, text="Comm Port: ").pack(side=LEFT)
self.comm_entry = Entry(self.f1, bd=5, name="comm_entry"); self.comm_entry.pack(side=LEFT)
self.connectButton = Button(self.f1, text="Connect", command=self.SerialConnect, name="b_connect"); self.connectButton.pack(side=LEFT)
self.disconnectButton = Button(self.f1, text="Disconnect", command=self.SerialDisconnect, name="b_disconnect"); self.disconnectButton.pack(side=RIGHT)
self.f1.grid(row=0, column=0)
# 控制面板
self.f2 = LabelFrame(self.frame, bd=3, relief="groove", text="Ground Control")
# Y轴滑块
self.g_vert_fm = Frame(self.f2)
self.grip_vert = Scale(self.g_vert_fm, from_=0, to=180); self.grip_vert.grid(row=0, column=0, rowspan=4, sticky=W)
self.grip_vert_set = Button(self.g_vert_fm, text="Set", command=self.GripperY, name="b_grip_vert_set", width=1, height=2, font=helv6); self.grip_vert_set.grid(row=5, column=0, sticky=W)
self.master.bind("", self.GripperY)
self.g_vert_fm.grid(row=0, column=0, rowspan=4, sticky=W)
# 方向按钮
self.leftforwardButton = Button(self.f2, text="\\", command=self.TurnLeft45, name="b_left_forward"); self.leftforwardButton.grid(row=0, column=1); self.master.bind("", self.TurnLeft45)
self.leftButton = Button(self.f2, text="<", command=self.TurnLeft, name="b_left"); self.leftButton.grid(row=1, column=1); self.master.bind("", self.TurnLeft)
self.rightforwardButton = Button(self.f2, text="/", command=self.TurnRight45, name="b_right_forward"); self.rightforwardButton.grid(row=0, column=3); self.master.bind("", self.TurnRight45)
self.haltButton = Button(self.f2, text="Halt!", command=self.Halt, name="b_halt"); self.haltButton.grid(row=1, column=2); self.master.bind("", self.Halt)
self.rightButton = Button(self.f2, text=">", command=self.TurnRight, name="b_right"); self.rightButton.grid(row=1, column=3); self.master.bind("", self.TurnRight)
self.upButton = Button(self.f2, text="^", command=self.Forward, name="b_forward"); self.upButton.grid(row=0, column=2); self.master.bind("", self.Forward)
self.leftdownButton = Button(self.f2, text="/", command=self.TurnRight45, name="b_left_down"); self.leftdownButton.grid(row=2, column=1)
self.downButton = Button(self.f2, text="V", command=self.Reverse, name="b_reverse"); self.downButton.grid(row=2, column=2); self.master.bind("", self.Reverse)
self.rightdownButton = Button(self.f2, text="\\", command=self.TurnLeft45, name="b_right_down"); self.rightdownButton.grid(row=2, column=3)
# X轴滑块
self.g_horz_fm = Frame(self.f2)
self.grip_horz = Scale(self.g_horz_fm, from_=0, to=180, orient=HORIZONTAL); self.grip_horz.grid(row=0, column=0, columnspan=7, sticky=E)
self.grip_horz_set = Button(self.g_horz_fm, text="Set", command=self.GripperX, name="b_grip_horz_set", width=1, height=2, font=helv6); self.grip_horz_set.grid(row=0, column=8)
self.master.bind("", self.GripperX)
self.g_horz_fm.grid(row=4, column=0, columnspan=7, sticky=E)
self.master.bind("", self.GripperHome)
# 状态栏
self.f3 = Frame(self.frame)
Label(self.f3, text="Last action: ").pack(side=LEFT)
Label(self.f3, text=" ", textvariable=self.message).pack(side=RIGHT)
self.f3.grid(row=3, column=0, pady=8)
def CtrlButtonsState(self, bstate):
for btn in [self.leftforwardButton, self.leftButton, self.rightforwardButton, self.rightButton,
self.upButton, self.leftdownButton, self.downButton, self.rightdownButton,
self.haltButton, self.disconnectButton]:
btn.config(state=bstate)
for s in [self.grip_horz, self.grip_vert, self.grip_horz_set, self.grip_vert_set]:
s.config(state=bstate)
def ConnCtrlsState(self, bstate):
self.connectButton.config(state=bstate)
self.comm_entry.config(state=bstate)
def SerialConnect(self):
try:
self.ser = serial.Serial(self.comm_entry.get(), 9600)
except IOError:
tkMessageBox.showerror("Invalid comm port", "Comm port not found."); return
self.ConnCtrlsState(DISABLED); self.CtrlButtonsState(NORMAL)
self.message.set("SerialConnect"); self.connected = True; time.sleep(3)
def SerialDisconnect(self):
try:
self.send_cmd('h', "Halt!"); time.sleep(1); self.ser.close()
except IOError: print("Could not close port...")
self.message.set("SerialDisconnect")
self.ConnCtrlsState(NORMAL); self.CtrlButtonsState(DISABLED)
self.connected = False; time.sleep(2)
def send_cmd(self, action, msg):
if self.connected:
for val in action: self.ser.write(val); self.ser.flush()
self.message.set(msg)
def TurnLeft(self, event=None): self.send_cmd('a', "Left")
def TurnLeft45(self, event=None): self.send_cmd('q', "Left45")
def TurnRight(self, event=None): self.send_cmd('s', "Right")
def TurnRight45(self, event=None): self.send_cmd('e', "Right45")
def Forward(self, event=None): self.send_cmd('w', "Up")
def Reverse(self, event=None): self.send_cmd('z', "Down")
def Halt(self, event=None): self.send_cmd('h', "Halt!")
def GripperX(self, event=None):
grp_change = ('>', chr(self.grip_horz.get()), chr(255))
self.send_cmd(grp_change, "Gripper X")
def GripperY(self, event=None):
grp_change = ('^', chr(self.grip_vert.get()), chr(255))
self.send_cmd(grp_change, "Gripper Y")
def GripperHome(self, event=None):
self.send_cmd(('c', chr(255)), "Gripper Home")
def __init__(self, master=None):
Frame.__init__(self, master); self.pack(); self.createWidgets(); self.CtrlButtonsState(DISABLED)
def main():
root = Tk(); root.geometry("450x350"); root.wm_title("Rover Control Center (RCC)")
App(master=root).mainloop()
if __name__ == '__main__': main()
Arduino 端代码 (blue_t_slave.ino)
/* Bluetooth Rover - Scott Beasley 2015 */
#include <L9110Driver.h>
#include <Servo.h>
#define SERVO_Y 2
#define SERVO_CLAW 7
#define pinAIN1 5; #define pinAIN2 6
#define pinBIN1 3; #define pinBIN2 11
#define MAXFORWARDSPEED 225
#define MAXBACKWARDSPEED 225
#define TOPSPEED 255
#define TURNDELAY 475
#define TURNDELAY45 235
#define BACKUPDELAY 400
#define SERVOMOVEDELAY 200
#define SERVOSEARCHDELAY 85
L9110_Motor motor_left(pinAIN1, pinAIN2);
L9110_Motor motor_right(pinBIN1, pinBIN2);
Servo grip_y_servo, grip_servo;
void setup() {
Serial.begin(9600);
grip_y_servo.attach(SERVO_Y); grip_y_servo.write(90);
grip_servo.attach(SERVO_CLAW); grip_servo.write(90);
delay(500);
}
void loop() {
byte command = 0, val = 0;
if (Serial.available() > 0) command = Serial.read();
switch (command) {
case 'w': go_forward(); break;
case 'z': go_backward(); break;
case 'a': go_left(); delay(TURNDELAY); halt(); break;
case 's': go_right(); delay(TURNDELAY); halt(); break;
case 'q': go_left(); delay(TURNDELAY45); halt(); break;
case 'e': go_right(); delay(TURNDELAY45); halt(); break;
case 'h': halt(); break;
case '>': val = Serial.read(); grip_servo.write(constrain(val, 64, 179)); break;
case '^': val = Serial.read(); grip_y_servo.write(constrain(val, 53, 179)); break;
case 'c': grip_y_servo.write(90); grip_servo.write(90); break;
case 255: Serial.flush(); break;
}
Serial.flush(); delay(125);
}
void go_forward() {
ramp_it(MAXFORWARDSPEED, FORWARD, FORWARD);
motor_left.setSpeed(MAXFORWARDSPEED); motor_right.setSpeed(MAXFORWARDSPEED);
motor_left.run(FORWARD|RELEASE); motor_right.run(FORWARD|RELEASE);
}
void go_backward() {
ramp_it(MAXBACKWARDSPEED, BACKWARD, BACKWARD);
motor_left.setSpeed(MAXBACKWARDSPEED); motor_right.setSpeed(MAXBACKWARDSPEED);
motor_left.run(BACKWARD|RELEASE); motor_right.run(BACKWARD|RELEASE);
}
void go_left() {
ramp_it(TOPSPEED, BACKWARD, FORWARD);
motor_left.setSpeed(TOPSPEED); motor_right.setSpeed(TOPSPEED);
motor_left.run(BACKWARD|RELEASE); motor_right.run(FORWARD|RELEASE);
}
void go_right() {
ramp_it(TOPSPEED, FORWARD, BACKWARD);
motor_left.setSpeed(TOPSPEED); motor_right.setSpeed(TOPSPEED);
motor_left.run(FORWARD|RELEASE); motor_right.run(BACKWARD|RELEASE);
}
void halt() {
motor_left.setSpeed(0); motor_right.setSpeed(0);
motor_left.run(BRAKE); motor_right.run(BRAKE);
}
void ramp_it(uint8_t speed, uint8_t lf_dir, uint8_t rt_dir) {
uint8_t ramp_val = 0, step_val = abs(speed / 4);
if (!speed) step_val = -step_val;
for (uint8_t i = 0; i < 4; i++) {
ramp_val += step_val;
motor_left.setSpeed(ramp_val); motor_right.setSpeed(ramp_val);
motor_left.run(lf_dir|RELEASE); motor_right.run(rt_dir|RELEASE);
delay(25);
}
}
L9110 驱动库
GitHub: https://github.com/jscottb/L9110Driver
自定义零件与外壳
4wdgripperbt2.fzz (Fritzing 设计文件)
原理图
4wdgripperbt2.fzz
制造工艺
- 多位置温度传感器系统,助力智能家居集成
- 862D+ 电烙铁 DIY 智能温控器:精准可靠,低成本替代方案
- MotionSense:基于 Arduino 与 ESP8266 的智能入侵检测系统
- 3D 打印机防火卫士:智能传感器安全系统
- Arduino 简易排斥式磁悬浮:电路与 DIY 指南
- 高性能 Arduino 气象气球数据记录器
- ArduFarmBot 第二部分:构建远程 IoT 控制站
- 进阶微控制器实验平台:ESP32、Arduino、PIC 与多传感器模块集成
- DIY 视觉暂留 (POV) 球体:Arduino Mega 驱动的 LED 光绘艺术
- CoroFence 热成像检测器:Panasonic GridEye + PIR + IoT 集成
- 云监控:实时资产管理的远程控制系统
- 现代工业远程生产控制的5大核心优势