【花雕学编程】Arduino BLDC 之通过串口控制三关节机械臂

在这里插入图片描述
《Arduino 手册(思路与案例)》栏目介绍:
在电子制作与智能控制的应用领域,本栏目涵盖了丰富的内容,包括但不限于以下主题:Arduino BLDC、Arduino CNC、Arduino E-Ink、Arduino ESP32 SPP、Arduino FreeRTOS、Arduino FOC、Arduino GRBL、Arduino HTTP、Arduino HUB75、Arduino IoT Cloud、Arduino JSON、Arduino LCD、Arduino OLED、Arduino LVGL、Arduino PID、Arduino TFT,以及Arduino智能家居、智慧交通、月球基地、智慧校园和智慧农业等多个方面与领域。不仅探讨了这些技术的基础知识和应用领域,还提供了众多具体的参考案例,帮助读者更好地理解和运用Arduino平台进行创新项目。目前,本栏目已有近4000篇相关博客,旨在为广大电子爱好者和开发者提供全面的学习资源与实践指导。通过这些丰富的案例和思路,读者可以获取灵感,推动自己的创作与开发进程。
https://blog.csdn.net/weixin_41659040/category_12422453.html

在这里插入图片描述
Arduino BLDC 之 通过串口控制三关节机械臂

一、主要特点
精准控制:

通过 Arduino 控制 BLDC 电机,实现三关节机械臂的高精度运动,能够在预设的轨迹和位置之间平滑移动,确保机械臂的稳健性和准确性。
串口通信:

采用串口通信(如 UART)实现与计算机或其他控制设备的实时数据传输,使得指令的发送与响应更加高效,适合远程控制和调试。
灵活的运动模式:

支持多种运动模式(如关节空间控制、笛卡尔空间控制),用户可以根据具体任务选择不同的控制方式,增强系统的适应性。
可编程性和扩展性:

用户可以根据需求编写自定义控制程序,轻松实现不同的应用场景和功能,具有良好的扩展性。
实时反馈机制:

通过传感器(如编码器)获取实时反馈,确保机械臂运动的准确性和稳定性,减少误差和偏差。

二、应用场景
工业自动化:

在制造业中,三关节机械臂可用于物品搬运、装配、焊接等任务,提高生产效率和精度。
实验室研究:

在科研实验中,机械臂可用于自动化实验、样品处理等,减轻实验人员的工作负担。
教育与培训:

在工程和机器人教育领域,三关节机械臂是学习自动化控制、机器人技术和编程的理想平台。
医疗设备:

在医疗领域,机械臂可用于手术辅助、康复训练等,提高医疗操作的精确度和安全性。
家庭服务:

在智能家居中,机械臂可以用于执行简单的日常任务,如物品递送、清洁等,提高生活便利性。

三、注意事项
电机选择与配置:

选择适合的 BLDC 电机,确保其具备足够的扭矩和精度,以满足机械臂的运动需求。
控制算法设计:

设计合适的控制算法(如 PID 控制),确保机械臂在运动过程中的平稳性和准确性,避免过冲或振荡。
串口通信稳定性:

确保串口通信的稳定性,避免因通信延迟或数据丢失导致的控制失效,必要时可以实现数据校验机制。
传感器集成:

根据需要选择合适的传感器(如位置传感器、力反馈传感器),提高控制精度和系统反馈能力。
安全性考虑:

在设计和使用过程中考虑机械臂的安全性,设置合理的运动范围和速度限制,避免对人员和设备造成伤害。

在这里插入图片描述
1、基础串口控制机械臂

#include <Servo.h>

Servo joint1; // 第一关节
Servo joint2; // 第二关节
Servo joint3; // 第三关节

void setup() {
    Serial.begin(9600);
    joint1.attach(9); // 第一关节控制引脚
    joint2.attach(10); // 第二关节控制引脚
    joint3.attach(11); // 第三关节控制引脚
}

void loop() {
    if (Serial.available()) {
        char command = Serial.read(); // 读取串口命令

        switch (command) {
            case '1':
                joint1.write(90); // 第一关节旋转90°
                break;
            case '2':
                joint1.write(0); // 第一关节旋转0°
                break;
            case '3':
                joint2.write(90); // 第二关节旋转90°
                break;
            case '4':
                joint2.write(0); // 第二关节旋转0°
                break;
            case '5':
                joint3.write(90); // 第三关节旋转90°
                break;
            case '6':
                joint3.write(0); // 第三关节旋转0°
                break;
            default:
                break;
        }
    }
}

2、基于串口输入的角度控制

#include <Servo.h>

Servo joint1;
Servo joint2;
Servo joint3;

void setup() {
    Serial.begin(9600);
    joint1.attach(9);
    joint2.attach(10);
    joint3.attach(11);
}

void loop() {
    if (Serial.available()) {
        int joint = Serial.parseInt(); // 读取关节编号
        int angle = Serial.parseInt(); // 读取角度

        switch (joint) {
            case 1:
                joint1.write(angle); // 设置第一关节角度
                break;
            case 2:
                joint2.write(angle); // 设置第二关节角度
                break;
            case 3:
                joint3.write(angle); // 设置第三关节角度
                break;
            default:
                break;
        }
    }
}

3、预设动作序列控制

#include <Servo.h>

Servo joint1;
Servo joint2;
Servo joint3;

void setup() {
    Serial.begin(9600);
    joint1.attach(9);
    joint2.attach(10);
    joint3.attach(11);
}

void loop() {
    if (Serial.available()) {
        char command = Serial.read(); // 读取串口命令

        if (command == 'a') {
            // 动作序列A
            joint1.write(0);
            joint2.write(90);
            joint3.write(180);
            delay(1000);
            joint1.write(90);
            joint2.write(0);
            joint3.write(90);
        } else if (command == 'b') {
            // 动作序列B
            joint1.write(180);
            joint2.write(90);
            joint3.write(0);
            delay(1000);
            joint1.write(90);
            joint2.write(90);
            joint3.write(90);
        }
    }
}

要点解读
串口通信:

所有示例程序通过 Serial 库实现与电脑或其他串口设备的通信。使用 Serial.begin(9600) 来设置串口波特率,确保数据传输稳定。
机械臂关节控制:

使用 Servo 库控制机械臂的三个关节。每个关节通过 attach() 函数连接到 Arduino 的特定引脚,以便进行角度控制。
基础命令控制:

第一个示例通过简单的字符命令(‘1’ 到 ‘6’)来控制每个关节的角度,适合基本的操作场景。
角度控制:

第二个示例允许通过串口输入关节编号和角度,进行更灵活的控制。使用 Serial.parseInt() 读取用户输入的整数值,使得用户可以直接设置关节的具体角度。
预设动作序列:

第三个示例实现了预设动作序列,当接收到特定字符命令(如 ‘a’ 和 ‘b’)时,机械臂会执行一系列动作。这种方式适合需要重复执行特定任务的场景。
延迟控制:

在预设动作序列中,通过 delay() 函数控制动作之间的间隔,确保机械臂在执行每个动作时有足够的时间完成。
可扩展性:

这些示例提供了基础框架,可以根据需求扩展功能。例如,可以通过添加更多的关节、引入传感器反馈(如位置传感器)或使用更复杂的控制算法(如 PID 控制)来提升机械臂的性能和灵活性。

在这里插入图片描述

4、基础位置控制(PWM+方向信号)

// 机械臂关节控制引脚定义
#define JOINT1_PWM 9   // 关节1 PWM引脚
#define JOINT1_DIR 8   // 关节1 方向引脚
#define JOINT2_PWM 10
#define JOINT2_DIR 7
#define JOINT3_PWM 11
#define JOINT3_DIR 6
 
// 关节角度限制(单位:度)
#define J1_MIN 0
#define J1_MAX 180
#define J2_MIN 30
#define J2_MAX 150
#define J3_MIN 10
#define J3_MAX 160
 
void setup() {
  Serial.begin(115200);
  // 设置PWM频率为31.25kHz(避免BLDC噪音)
  TCCR1B = TCCR1B & 0b11111000 | 0x01;  // 引脚9,10
  TCCR2B = TCCR2B & 0b11111000 | 0x01;  // 引脚11
  
  // 初始化引脚
  pinMode(JOINT1_PWM, OUTPUT);
  pinMode(JOINT1_DIR, OUTPUT);
  pinMode(JOINT2_PWM, OUTPUT);
  pinMode(JOINT2_DIR, OUTPUT);
  pinMode(JOINT3_PWM, OUTPUT);
  pinMode(JOINT3_DIR, OUTPUT);
}
 
void loop() {
  if (Serial.available() >= 12) { // 等待完整指令(3关节×4字节)
    if (Serial.read() == 0xAA) {  // 帧头校验
      // 读取3个关节的角度值(0-255映射到实际角度范围)
      int j1 = map(Serial.read(), 0, 255, J1_MIN, J1_MAX);
      int j2 = map(Serial.read(), 0, 255, J2_MIN, J2_MAX);
      int j3 = map(Serial.read(), 0, 255, J3_MIN, J3_MAX);
      
      // 控制关节1
      digitalWrite(JOINT1_DIR, (j1 > 90) ? HIGH : LOW); // 假设90度为中立点
      analogWrite(JOINT1_PWM, map(abs(j1-90), 0, 90, 0, 255));
      
      // 控制关节2(类似处理)
      digitalWrite(JOINT2_DIR, (j2 > 90) ? HIGH : LOW);
      analogWrite(JOINT2_PWM, map(abs(j2-90), 0, 60, 0, 255));
      
      // 控制关节3
      digitalWrite(JOINT3_DIR, (j3 > 85) ? HIGH : LOW);
      analogWrite(JOINT3_PWM, map(abs(j3-85), 0, 75, 0, 255));
      
      // 发送确认回执
      Serial.write(0x55);
    }
  }
}

要点解读:

PWM频率优化:通过修改定时器寄存器将频率提升至31.25kHz,减少BLDC电机噪音
安全限位:通过宏定义限制关节活动范围,防止机械结构损坏
协议设计:使用0xAA帧头+3字节数据+0x55确认的简单协议
方向控制:通过比较目标角度与中立点(如90度)决定旋转方向

5、轨迹插补控制(接收G代码)

#include <math.h>
 
// 全局变量
float currentPos[3] = {90, 90, 90}; // 初始位置(中立点)
float targetPos[3];
unsigned long stepTime = 0;
bool motionComplete = true;
 
void setup() {
  Serial.begin(115200);
  // 引脚初始化同案例一
}
 
void loop() {
  // 1. 解析G代码指令
  if (Serial.available()) {
    String cmd = Serial.readStringUntil('\n');
    if (cmd.startsWith("G1")) {  // 线性插补指令
      parseGCode(cmd);
      motionComplete = false;
      stepTime = millis();
    }
  }
  
  // 2. 执行轨迹插补(每50ms更新一次)
  if (!motionComplete && millis() - stepTime >= 50) {
    bool allArrived = true;
    for (int i=0; i<3; i++) {
      if (abs(currentPos[i] - targetPos[i]) > 0.5) {
        currentPos[i] += (targetPos[i] > currentPos[i]) ? 0.5 : -0.5;
        allArrived = false;
      }
    }
    
    // 更新电机输出
    updateMotors();
    
    if (allArrived) {
      motionComplete = true;
      Serial.println("OK"); // 运动完成反馈
    }
    stepTime = millis();
  }
}
 
void parseGCode(String cmd) {
  // 示例指令:G1 X120 Y80 Z50 F1000
  if (cmd.indexOf('X') != -1) {
    targetPos[0] = cmd.substring(cmd.indexOf('X')+1).toFloat();
    targetPos[0] = constrain(targetPos[0], J1_MIN, J1_MAX);
  }
  // Y/Z参数类似处理...
}
 
void updateMotors() {
  // 关节1控制(带正弦加速曲线)
  float progress = sin(min(1.0, (millis()-stepTime)/1000.0) * PI/2);
  int pwm = map(currentPos[0], J1_MIN, J1_MAX, 0, 255) * progress;
  digitalWrite(JOINT1_DIR, (currentPos[0] > 90) ? HIGH : LOW);
  analogWrite(JOINT1_PWM, pwm);
  
  // 关节2/3类似处理...
}

要点解读:

G代码支持:实现类似3D打印机的指令系统(G1 Xnn Ynn Znn)
轨迹平滑:使用正弦函数实现加速曲线,避免运动冲击
增量式控制:每次更新0.5度,平衡精度与响应速度
状态反馈:运动完成后发送"OK"确认

6、力矩控制模式(需电流检测)

// 电流检测相关
#define CURRENT_SENSOR A0
#define JOINT1_CURRENT_LIMIT 1500 // mA
 
// PID参数
float Kp = 0.8, Ki = 0.1, Kd = 0.05;
float errorSum[3] = {0};
float lastError[3] = {0};
 
void setup() {
  Serial.begin(115200);
  analogReference(EXTERNAL); // 使用外部参考电压提高精度
}
 
void loop() {
  static unsigned long lastPID = 0;
  if (millis() - lastPID >= 10) { // 100Hz PID循环
    for (int i=0; i<3; i++) {
      // 1. 读取目标位置(通过串口实时更新)
      float target = currentPos[i]; // 实际应用应从串口获取
      
      // 2. 读取当前位置(需编码器反馈)
      float current = readEncoder(i); // 伪代码,需实际实现
      
      // 3. PID计算
      float error = target - current;
      errorSum[i] += error;
      float delta = error - lastError[i];
      lastError[i] = error;
      
      // 4. 电流限制
      float output = Kp*error + Ki*errorSum[i] + Kd*delta;
      int pwm = constrain(abs(output), 0, 255);
      
      // 5. 电流检测与保护
      int currentRead = analogRead(CURRENT_SENSOR);
      if (currentRead > JOINT1_CURRENT_LIMIT) {
        pwm = 0; // 过流保护
        Serial.println("WARNING: Overcurrent!");
      }
      
      // 6. 更新电机
      digitalWrite(JOINT1_DIR, (output > 0) ? HIGH : LOW);
      analogWrite(JOINT1_PWM, pwm);
    }
    lastPID = millis();
  }
  
  // 串口调试接口
  if (Serial.available()) {
    String cmd = Serial.readStringUntil('\n');
    if (cmd.startsWith("SET")) {
      // 示例指令:SET 1 120.5 (关节1目标120.5度)
      int joint = cmd.substring(4,5).toInt();
      float angle = cmd.substring(6).toFloat();
      if (joint >=1 && joint <=3) {
        currentPos[joint-1] = constrain(angle, 
          (joint==1)?J1_MIN:(joint==2)?J2_MIN:J3_MIN,
          (joint==1)?J1_MAX:(joint==2)?J2_MAX:J3_MAX);
      }
    }
  }
}

要点解读:

电流闭环:通过电流检测实现力矩限制,保护机械结构
PID控制:实现位置闭环控制,需配合编码器反馈
动态参数调整:可通过串口发送"PID Kp0.8 Ki0.1 Kd0.05"调整参数
保护机制:过流时立即停止输出并报警

注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。

在这里插入图片描述

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

驴友花雕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值