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或更高)

  • 必需库

    1. PS2X_lib:PS2手柄通信库(下载地址

    2. Servo:Arduino内置舵机库(或Adafruit_PWMServoDriver,若使用PCA9685)

  • 库安装:将下载的PS2X_lib文件夹放入Arduino的libraries目录,重启IDE。

3.2 程序结构

graph TD A[程序开始] --> B[初始化PS2手柄] B --> C[初始化舵机/PCA9685] C --> D[读取PS2手柄数据] D --> E{手柄连接正常?} E -->|是| F[解析摇杆/按键值] E -->|否| G[显示“手柄未连接”] F --> H[映射为舵机角度] H --> I[限制角度范围] I --> J[写入舵机角度] J --> K[延时10ms] K --> D

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 性能优化

  1. 使用PCA9685驱动板:若舵机数量多(>6),建议使用PCA9685(I2C接口),避免Arduino PWM引脚不足,同时提供更稳定的PWM信号。

  2. 平滑运动算法:避免角度突变,采用“缓动函数”(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越小,移动越慢
      }
    }
    
  3. 保存预设姿势:利用手柄按键(如△○×□)保存/调用预设姿势,实现“一键抓取”、“一键放置”等动作。

六、扩展功能

  1. 轨迹记录与回放:添加SD卡模块,记录舵机角度序列,实现“示教再现”。

  2. 无线升级:通过蓝牙(HC-05)或Wi-Fi(ESP8266)接收上位机指令,实现手机APP控制。

  3. 力反馈:在夹爪添加压力传感器(FSR),当夹持力过大时,手柄震动(需支持震动的PS2手柄)。

  4. 视觉辅助:添加摄像头(如OpenMV),实现目标识别与自动抓取。

七、总结

本程序实现了PS2手柄对6轴机械臂的实时控制,通过摇杆模拟量控制(底座、大臂、小臂、手腕俯仰)与按键数字量控制(手腕旋转、夹爪),提供了直观的人机交互方式。代码采用模块化设计,便于扩展为更多自由度或添加高级功能(如轨迹规划)。硬件上注意电源分离(舵机独立供电)与信号抗干扰,软件上通过角度限制与平滑移动确保机械臂运动安全、流畅。

posted @ 2026-04-02 09:48  w199899899  阅读(12)  评论(0)    收藏  举报