PS2手柄控制6轴机械臂程序设计与实现
一、系统概述
使用PS2手柄作为输入设备,通过Arduino主控板(如Arduino Mega 2560或Uno)控制6轴舵机机械臂(如SMALLROBOTAM机械臂),实现无线遥控操作。系统核心是将PS2手柄的摇杆、按键信号转换为舵机角度指令,通过PWM信号驱动6个舵机(对应机械臂的6个自由度:底座旋转、大臂、小臂、手腕俯仰、手腕旋转、夹爪),适用于教育演示、工业抓取、娱乐互动等场景。
二、系统架构与硬件组成
2.1 硬件清单
| 组件 | 型号/参数 | 数量 | 功能说明 |
|---|---|---|---|
| 主控板 | Arduino Mega 2560(或Uno) | 1 | 处理PS2手柄信号,生成PWM控制舵机 |
| PS2手柄 | PlayStation 2原装或兼容手柄 | 1 | 提供摇杆、按键输入(无线/有线) |
| PS2接收器 | PS2无线接收器模块 | 1 | 接收手柄信号,通过SPI与Arduino通信 |
| 舵机 | MG996R(扭矩11kg·cm)或SG90(扭矩1.8kg·cm) | 6 | 驱动机械臂6个关节(建议大关节用MG996R,小关节用SG90) |
| 舵机驱动板 | PCA9685(16路PWM,I2C)或直接接Arduino PWM引脚 | 1(可选) | 扩展PWM输出,减轻Arduino负担 |
| 机械臂结构 | SMALLROBOTAM 6轴机械臂套件(或3D打印) | 1套 | 包含舵机支架、连接件、夹爪等 |
| 电源 | 5V/10A开关电源(舵机单独供电) | 1 | 为舵机提供足够电流(避免Arduino电源过载) |
2.2 接线示意图
PS2接收器 → Arduino
DAT (数据) → 引脚11 (MISO)
CMD (命令) → 引脚12 (MOSI)
SEL (片选) → 引脚10 (SS)
CLK (时钟) → 引脚13 (SCK)
VCC → 5V
GND → GND
舵机 → Arduino/PCA9685
舵机1(底座) → 引脚9(或PCA9685通道0)
舵机2(大臂) → 引脚10(或PCA9685通道1)
舵机3(小臂) → 引脚11(或PCA9685通道2)
舵机4(手腕俯仰) → 引脚12(或PCA9685通道3)
舵机5(手腕旋转) → 引脚13(或PCA9685通道4)
舵机6(夹爪) → 引脚14(或PCA9685通道5)
舵机VCC → 5V/10A电源(独立供电)
舵机GND → 与Arduino共地
注意:若使用PCA9685舵机驱动板,需通过I2C(SDA→A4,SCL→A5)连接Arduino,可同时驱动16路舵机,避免Arduino PWM引脚不足。
三、软件设计:PS2手柄库与舵机控制
3.1 开发环境与库安装
-
IDE:Arduino IDE(1.8.x或更高)
-
必需库:
-
PS2X_lib:PS2手柄通信库(下载地址)
-
Servo:Arduino内置舵机库(或Adafruit_PWMServoDriver,若使用PCA9685)
-
-
库安装:将下载的PS2X_lib文件夹放入Arduino的
libraries目录,重启IDE。
3.2 程序结构
3.3 核心代码实现
3.3.1 头文件与全局变量
#include <PS2X_lib.h> // PS2手柄库
#include <Servo.h> // 舵机库
// 若使用PCA9685,则包含 #include <Adafruit_PWMServoDriver.h>
// PS2手柄引脚定义(SPI接口)
#define PS2_DAT 11 // MISO(黄色线)
#define PS2_CMD 12 // MOSI(橙色线)
#define PS2_SEL 10 // SS(蓝色线)
#define PS2_CLK 13 // SCK(红色线)
// 舵机引脚定义(Arduino直接驱动)
#define SERVO_BASE 9 // 底座舵机
#define SERVO_SHOULDER 10 // 大臂舵机
#define SERVO_ELBOW 11 // 小臂舵机
#define SERVO_WRIST_P 12 // 手腕俯仰
#define SERVO_WRIST_R 13 // 手腕旋转
#define SERVO_GRIPPER 14 // 夹爪舵机
// 舵机角度限制(根据机械臂结构调整)
#define BASE_MIN 0
#define BASE_MAX 180
#define SHOULDER_MIN 30
#define SHOULDER_MAX 150
#define ELBOW_MIN 50
#define ELBOW_MAX 170
#define WRIST_P_MIN 0
#define WRIST_P_MAX 180
#define WRIST_R_MIN 0
#define WRIST_R_MAX 180
#define GRIPPER_MIN 0 // 夹爪闭合
#define GRIPPER_MAX 90 // 夹爪张开
// 全局变量
PS2X ps2x; // PS2手柄对象
Servo servo_base, servo_shoulder, servo_elbow, servo_wrist_p, servo_wrist_r, servo_gripper;
int error; // 手柄连接错误码
int pressures = 0; // 是否启用压力感应(0=禁用)
int rumble = 0; // 是否启用手柄震动(0=禁用)
// 当前舵机角度
int angle_base = 90, angle_shoulder = 90, angle_elbow = 90;
int angle_wrist_p = 90, angle_wrist_r = 90, angle_gripper = 0;
3.3.2 初始化函数(setup)
void setup() {
Serial.begin(115200);
delay(300); // 等待手柄初始化
// 初始化PS2手柄
error = ps2x.config_gamepad(PS2_CLK, PS2_CMD, PS2_SEL, PS2_DAT, pressures, rumble);
if (error == 0) {
Serial.println("PS2手柄初始化成功!");
} else {
Serial.println("PS2手柄初始化失败,错误码: " + String(error));
}
// 初始化舵机(若使用PCA9685,则替换为pwm.begin())
servo_base.attach(SERVO_BASE);
servo_shoulder.attach(SERVO_SHOULDER);
servo_elbow.attach(SERVO_ELBOW);
servo_wrist_p.attach(SERVO_WRIST_P);
servo_wrist_r.attach(SERVO_WRIST_R);
servo_gripper.attach(SERVO_GRIPPER);
// 设置舵机初始位置(机械臂“归零”姿势)
servo_base.write(angle_base);
servo_shoulder.write(angle_shoulder);
servo_elbow.write(angle_elbow);
servo_wrist_p.write(angle_wrist_p);
servo_wrist_r.write(angle_wrist_r);
servo_gripper.write(angle_gripper);
delay(1000); // 等待舵机到位
}
3.3.3 主循环(loop)与手柄映射
void loop() {
ps2x.read_gamepad(false, 0); // 读取手柄数据(不震动)
// 1. 左摇杆控制底座(LX)和大臂(LY)
if (ps2x.Analog(PSS_LX) < 50) { // 左摇杆向左
angle_base = constrain(angle_base - 1, BASE_MIN, BASE_MAX);
servo_base.write(angle_base);
} else if (ps2x.Analog(PSS_LX) > 200) { // 左摇杆向右
angle_base = constrain(angle_base + 1, BASE_MIN, BASE_MAX);
servo_base.write(angle_base);
}
if (ps2x.Analog(PSS_LY) < 50) { // 左摇杆向上(大臂向上)
angle_shoulder = constrain(angle_shoulder - 1, SHOULDER_MIN, SHOULDER_MAX);
servo_shoulder.write(angle_shoulder);
} else if (ps2x.Analog(PSS_LY) > 200) { // 左摇杆向下(大臂向下)
angle_shoulder = constrain(angle_shoulder + 1, SHOULDER_MIN, SHOULDER_MAX);
servo_shoulder.write(angle_shoulder);
}
// 2. 右摇杆控制小臂(RY)和手腕俯仰(RX)
if (ps2x.Analog(PSS_RY) < 50) { // 右摇杆向上(小臂向上)
angle_elbow = constrain(angle_elbow - 1, ELBOW_MIN, ELBOW_MAX);
servo_elbow.write(angle_elbow);
} else if (ps2x.Analog(PSS_RY) > 200) { // 右摇杆向下(小臂向下)
angle_elbow = constrain(angle_elbow + 1, ELBOW_MIN, ELBOW_MAX);
servo_elbow.write(angle_elbow);
}
if (ps2x.Analog(PSS_RX) < 50) { // 右摇杆向左(手腕向下)
angle_wrist_p = constrain(angle_wrist_p - 1, WRIST_P_MIN, WRIST_P_MAX);
servo_wrist_p.write(angle_wrist_p);
} else if (ps2x.Analog(PSS_RX) > 200) { // 右摇杆向右(手腕向上)
angle_wrist_p = constrain(angle_wrist_p + 1, WRIST_P_MIN, WRIST_P_MAX);
servo_wrist_p.write(angle_wrist_p);
}
// 3. 按键控制手腕旋转和夹爪
if (ps2x.Button(PSB_L1)) { // L1:手腕逆时针
angle_wrist_r = constrain(angle_wrist_r - 2, WRIST_R_MIN, WRIST_R_MAX);
servo_wrist_r.write(angle_wrist_r);
}
if (ps2x.Button(PSB_R1)) { // R1:手腕顺时针
angle_wrist_r = constrain(angle_wrist_r + 2, WRIST_R_MIN, WRIST_R_MAX);
servo_wrist_r.write(angle_wrist_r);
}
if (ps2x.Button(PSB_L2)) { // L2:夹爪闭合
angle_gripper = constrain(angle_gripper - 3, GRIPPER_MIN, GRIPPER_MAX);
servo_gripper.write(angle_gripper);
}
if (ps2x.Button(PSB_R2)) { // R2:夹爪张开
angle_gripper = constrain(angle_gripper + 3, GRIPPER_MAX, GRIPPER_MAX);
servo_gripper.write(angle_gripper);
}
// 4. 功能键:复位到初始位置(SELECT + START)
if (ps2x.Button(PSB_SELECT) && ps2x.Button(PSB_START)) {
angle_base = 90; angle_shoulder = 90; angle_elbow = 90;
angle_wrist_p = 90; angle_wrist_r = 90; angle_gripper = 0;
servo_base.write(angle_base);
servo_shoulder.write(angle_shoulder);
servo_elbow.write(angle_elbow);
servo_wrist_p.write(angle_wrist_p);
servo_wrist_r.write(angle_wrist_r);
servo_gripper.write(angle_gripper);
delay(500);
}
// 5. 串口打印当前角度(调试用)
Serial.print("Base:"); Serial.print(angle_base);
Serial.print(" Shoulder:"); Serial.print(angle_shoulder);
Serial.print(" Elbow:"); Serial.print(angle_elbow);
Serial.print(" WristP:"); Serial.print(angle_wrist_p);
Serial.print(" WristR:"); Serial.print(angle_wrist_r);
Serial.print(" Gripper:"); Serial.println(angle_gripper);
delay(10); // 控制循环频率(约100Hz)
}
参考代码 PS2手柄控制SMALLROBOTAM6轴机械臂程序 www.youwenfan.com/contentcns/183209.html
四、PS2手柄按键映射方案(6轴机械臂)
| 手柄部件 | 映射功能 | 动作说明 | 代码对应 |
|---|---|---|---|
| 左摇杆(LX) | 底座旋转 | 左/右转动机械臂底座 | ps2x.Analog(PSS_LX) |
| 左摇杆(LY) | 大臂俯仰 | 上/下控制大臂抬起/放下 | ps2x.Analog(PSS_LY) |
| 右摇杆(RY) | 小臂俯仰 | 上/下控制小臂抬起/放下 | ps2x.Analog(PSS_RY) |
| 右摇杆(RX) | 手腕俯仰 | 左/右控制手腕上下摆动 | ps2x.Analog(PSS_RX) |
| L1按钮 | 手腕旋转(逆时针) | 按住持续逆时针旋转 | ps2x.Button(PSB_L1) |
| R1按钮 | 手腕旋转(顺时针) | 按住持续顺时针旋转 | ps2x.Button(PSB_R1) |
| L2按钮 | 夹爪闭合 | 按住持续闭合夹爪 | ps2x.Button(PSB_L2) |
| R2按钮 | 夹爪张开 | 按住持续张开夹爪 | ps2x.Button(PSB_R2) |
| SELECT+START | 复位姿势 | 同时按下回到初始位置 | ps2x.Button(PSB_SELECT) && ps2x.Button(PSB_START) |
| 方向键(上/下) | 预留功能 | 可扩展为速度调节/模式切换 | ps2x.Button(PSB_PAD_UP/DOWN) |
五、调试与优化技巧
5.1 常见问题排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 手柄无响应 | 接线错误、手柄未配对 | 检查PS2接收器接线(DAT→11,CMD→12,SEL→10,CLK→13);长按手柄“MODE”键配对 |
| 舵机抖动/不动 | 电源不足、信号干扰 | 舵机单独用5V/10A电源供电;信号线远离电源线;增加滤波电容(100μF+0.1μF) |
| 机械臂运动不流畅 | 舵机速度过快、角度突变 | 使用servo.writeMicroseconds()替代servo.write(),实现平滑移动;增加delay(20) |
| 角度范围超限 | 机械结构限制 | 根据实际机械臂调整#define中的角度限制(如SHOULDER_MIN/MAX) |
5.2 性能优化
-
使用PCA9685驱动板:若舵机数量多(>6),建议使用PCA9685(I2C接口),避免Arduino PWM引脚不足,同时提供更稳定的PWM信号。
-
平滑运动算法:避免角度突变,采用“缓动函数”(easing function)实现平滑移动:
void smoothMove(Servo &servo, int targetAngle, int speed) { int current = servo.read(); while (current != targetAngle) { current += (current < targetAngle) ? 1 : -1; servo.write(current); delay(speed); // speed越小,移动越慢 } } -
保存预设姿势:利用手柄按键(如△○×□)保存/调用预设姿势,实现“一键抓取”、“一键放置”等动作。
六、扩展功能
-
轨迹记录与回放:添加SD卡模块,记录舵机角度序列,实现“示教再现”。
-
无线升级:通过蓝牙(HC-05)或Wi-Fi(ESP8266)接收上位机指令,实现手机APP控制。
-
力反馈:在夹爪添加压力传感器(FSR),当夹持力过大时,手柄震动(需支持震动的PS2手柄)。
-
视觉辅助:添加摄像头(如OpenMV),实现目标识别与自动抓取。
七、总结
本程序实现了PS2手柄对6轴机械臂的实时控制,通过摇杆模拟量控制(底座、大臂、小臂、手腕俯仰)与按键数字量控制(手腕旋转、夹爪),提供了直观的人机交互方式。代码采用模块化设计,便于扩展为更多自由度或添加高级功能(如轨迹规划)。硬件上注意电源分离(舵机独立供电)与信号抗干扰,软件上通过角度限制与平滑移动确保机械臂运动安全、流畅。
浙公网安备 33010602011771号