机械专业大二学生纯手工打造一台机械臂——充满妥协

我所建的为三自由度连杆机械臂,选择搭建这款机械臂主要是因为此机械臂操作简单,易于掌握,便于学习。

以下为我在学习制造这款机械臂的一些流程:

主要零件

主控板:arduino uno r3

扩展板:cnc shield v3雕刻机扩展板

42步进电机,SG90电机,PVC板,A4988驱动模块,HC-06模块

由于我做一件事喜欢把它列出来,所以做了思维导图:

并且在坐标自寻上我也进行了数学结算:

 

 以下为代码部分:

在代码部分中,由于本人编程能力较弱,一部分代码和编程思维均借鉴了多位B站UP主,并且本文章仅为记录我学习机械臂的过程。

手机模式和键盘模式代码:

/*电机步进模式须在cnc电机扩展版上进行调试。
 * 启动程序,程序开始默认为键盘控制模式
 * 在键盘模式中,'m'可一键切换至手机模式。
 * 手机模式
 * 在手机模式中,'m'可一键切换至键盘模式。
 */

#include <Servo.h>
#include <AccelStepper.h>
#include <SoftwareSerial.h>
SoftwareSerial BTserial(12, 13); // 建立SoftwareSerial对象,RX引脚12(SpnEn)接SpnDir, TX引脚13(SpnDir)接SpnEn
 
// 定义电机控制用常量
const int enablePin = 8;  // 使能控制引脚
 
const int xdirPin = 5;     // x方向控制引脚
const int xstepPin = 2;    // x步进控制引脚
const int ydirPin = 6;     // y方向控制引脚
const int ystepPin = 3;    // y步进控制引脚
const int zdirPin = 7;     // z方向控制引脚
const int zstepPin = 4;    // z步进控制引脚

const int clawMin = 144;    //存储电机极限值
const int clawMax = 180;



 
const int moveSteps = 5;    //步进电机运行使用的运行步数(仅适用于手机模式)

char cmd;        //电机指令字符
int data;        //电机指令参数
char stepperName;  //控制电机编号
int stepNumber;   //步进电机运行步数
int clawstep;              //夹爪电机的目标角度
bool mode = 1;       //mode = 1: 键盘模式,  mode = 0: 手机模式

int DSD = 15; //Default Servo Delay (默认电机运动延迟时间)
              //此变量用于控制电机运行速度.增大此变量数值将
              //降低电机运行速度从而控制机械臂动作速度。

int clawMoveStep = 2;  // 每一次按下手柄按键,舵机移动量(仅适用于手机模式)

Servo claw ;                                         //建立伺服电机对象———夹爪电机
AccelStepper base_stepper(1,xstepPin,xdirPin);       //建立步进电机对象———底座电机
AccelStepper forearm_stepper(1,ystepPin,ydirPin);    //建立步进电机对象———前臂电机
AccelStepper upperarm_stepper(1,zstepPin,zdirPin);   //建立步进电机对象———后臂电机

void setup() {
  pinMode(xstepPin,OUTPUT);     // Arduino控制A4988x步进引脚为输出模式
  pinMode(xdirPin,OUTPUT);      // Arduino控制A4988x方向引脚为输出模式
  pinMode(ystepPin,OUTPUT);     // Arduino控制A4988y步进引脚为输出模式
  pinMode(ydirPin,OUTPUT);      // Arduino控制A4988y方向引脚为输出模式
  pinMode(zstepPin,OUTPUT);     // Arduino控制A4988z步进引脚为输出模式
  pinMode(zdirPin,OUTPUT);      // Arduino控制A4988z方向引脚为输出模式

  pinMode(enablePin,OUTPUT);   // Arduino控制A4988使能引脚为输出模式
  digitalWrite(enablePin,LOW); // 将使能控制引脚设置为低电平从而让
                               // 电机驱动板进入工作状态
  claw.attach(11);      // claw 伺服舵机连接引脚Z+  舵机代号'c'
  delay(200);          // 稳定性等待

  claw.write(90);  
  delay(10); 
  
  Serial.begin(9600);
  BTserial.begin(9600); // HC-06 默认波特率 9600
  
  Serial.println(F("/\\\\\\\\\\\\\\\\\\"));     
  Serial.println(F("****The robotic arm serves you*****"));   
  Serial.println(F("/\\\\\\\\\\\\\\\\\\"));  
  Serial.println(F(""));  
  Serial.println(F("Please input motor command:")); 
 
  Serial.print("HC-06 DEMO/TEST  ");
  
  BTserial.print("AT");   //可在此处输入设置HC-06蓝牙模块的AT指令。
                          //此AT指令须在HC-06未连接蓝牙状态输入。



  base_stepper.setMaxSpeed(100);   // 设置电机最大速度为500
  base_stepper.setAcceleration(50.0);      // 初始化电机速度为0  setSpeed(0);
  
  forearm_stepper.setMaxSpeed(100);   
  forearm_stepper.setAcceleration(50.0);      

  upperarm_stepper.setMaxSpeed(100);
  upperarm_stepper.setAcceleration(50.0); 
claw.write(144);
base_stepper.setCurrentPosition(0);
forearm_stepper.setCurrentPosition(0);
upperarm_stepper.setCurrentPosition(0);
}

void loop() {
    if (Serial.available()>0) { //Serial.available()>0 //BTserial.available()>0
       cmd = Serial.read();
        Serial.print(cmd);
    if( mode == 1 ){
      keyboard_input(cmd);  //键盘控制模式

    } else {
      cellphone_input(cmd); //手机控制模式
    }  
  }


   base_stepper.run();     //底座电机运行
   forearm_stepper.run();  //前臂电机运行
   upperarm_stepper.run(); //后臂电机运行
}


//Arduino根据串行指令执行相应操作
//
void keyboard_input(char cmd){
  //判断是否因搞错模式而输入错误的指令信息(键盘模式下输入手机模式信息)
    if (   cmd == 'w' || cmd == 's' || cmd == 'a' || cmd == 'd' 
        || cmd == '5' || cmd == '4' || cmd == '6' || cmd == '8' ){
    Serial.println("Warning!!! Warning!!! Warning!!! Currently in the keyboard_input"); 
    delay(100);
    while(Serial.available()>0) char wrongCommand = Serial.read();  //清除串口缓存的错误指令
    return;
  } 
  
if( cmd == 'b' || cmd == 'f' || cmd == 'u'){
  data = Serial.parseInt();
  RunCommand(cmd,data);
  }else{
    switch (cmd){
           case 'm' ://切换至手机控制模式
           mode = 0; 
           Serial.println("Enter the cellphone control mode");
           break;
           case 'o' ://使机械臂复位
           reset_arm();
           Serial.println("Receive command");
           break;
           case 'c' ://夹爪电机运行
           int servoData = Serial.parseInt();
           servoCmd(cmd, servoData, DSD);  // 机械臂舵机运行函数(参数:舵机名,目标角度,延迟/速度)
           Serial.println("Receive the claw operation command");
           break;
           default :  //未知指令反馈
           Serial.println("Unknown Command.");
      } 
    }


  
  }
  void cellphone_input (char cmd ){
      //判断人类是否因搞错模式而输入错误的指令信息(手机模式下输入键盘模式信息)
  if (cmd == 'b' || cmd == 'f' || cmd == 'u' || cmd == 'v'){
    Serial.println("Warning!!! Warning!!! Warning!!! Currently in the cellphone_input");
    delay(100);
    while(Serial.available()>0) char wrongCommand = Serial.read();  //清除串口缓存的错误指令
    return;
  } 
  int basestep;
  int forearmstep;
  int upperarmstep;
  //int clawstep
 switch(cmd){
        case 'a' ://base_stepper向左
        basestep = base_stepper.currentPosition() - moveSteps;
        Serial.println("A run command was received");
        delay(500);
        base_stepper.moveTo(basestep);
        //CellphoneRunCommand('a',basestep);
        break;
        case 'd' ://base_stepper向右
        basestep = base_stepper.currentPosition() + moveSteps;
        Serial.println("A run command was received");
        delay(500);
        base_stepper.moveTo(basestep);
        //CellphoneRunCommand('d',basestep);
        break;
        case 'w' ://upperarm_stepper向下
        upperarmstep = upperarm_stepper.currentPosition() - moveSteps;
        Serial.println("A run command was received");
        delay(500);
        upperarm_stepper.moveTo(upperarmstep);
        //CellphoneRunCommand('w',upperarmstep);
        break;
        case 's' ://upperarm_stepper向上
        upperarmstep = upperarm_stepper.currentPosition() + moveSteps;
        Serial.println("A run command was received");
        delay(500);
        upperarm_stepper.moveTo(upperarmstep);
        //CellphoneRunCommand('s',upperarmstep);
        break;
        case '8' ://forearm_stepper向下
        forearmstep = forearm_stepper.currentPosition() - moveSteps;
        Serial.println("A run command was received");
        delay(500);
        forearm_stepper.moveTo(forearmstep);
        //CellphoneRunCommand('8',forearmstep);
        break;
        case '5' ://forearm_stepper向上
        forearmstep = forearm_stepper.currentPosition() + moveSteps;
        Serial.println("A run command was received");
        delay(500);
        forearm_stepper.moveTo(forearmstep);
        //CellphoneRunCommand('5',forearmstep);       
        break;
        case '4' ://夹爪打开
        Serial.println("Received Command: Claw Open Up");     
        clawstep = claw.read() - clawMoveStep;
        servoCmd('c', clawstep, DSD);
        break;
        case '6' ://夹爪闭合
        Serial.println("Received Command: Claw Close Down");        
        clawstep = claw.read() + clawMoveStep;
        servoCmd('c', clawstep, DSD);
        break;
        case 'm' :   //切换至键盘控制模式 
        mode = 1; 
        Serial.println("Enter the keyboard control mode");
        break;
        case 'o' ://使机械臂复位
        reset_arm();
        Serial.println("Receive command");
        break;
           //default :  //未知指令反馈
          //Serial.println("Unknown Command.");       
  }
    }

  void RunCommand(char stepperName,int stepNumber){

    switch (stepperName){
           case 'b' ://底座电机运行,利用move函数使电机运行相应步数(示例:b1024 - 使电机运行1024步 )
           Serial.println("A run command was received");
           //delay(500);
           base_stepper.move(stepNumber);
           break;
           case 'f' :
           Serial.println("A run command was received");
           //delay(500);
           forearm_stepper.move(stepNumber);
           break;
           case 'u' :
           Serial.println("A run command was received");
           //delay(500);
           upperarm_stepper.move(stepNumber);
           break;
           default:  //未知指令反馈
           Serial.println("Unknown Command.");  
      }

    }

  void reset_arm(){//机械臂复位函数。 
    /*runToNewPosition与moveTo函数功能基本相同。唯一区别是runToNewPosition函数将“block”程序运行。
     * 即电机没有到达目标位置前,Arduino将不会继续执行后续程序内容。
     */
    Serial.println("Receive command"); 
    base_stepper.runToNewPosition(0);
    forearm_stepper.runToNewPosition(0);
    upperarm_stepper.runToNewPosition(0);
    claw.write(145);
    delay(500);
    Serial.println("The reset command was completed");
    
    }
    void servoCmd(char servoName, int toPos, int servoDelay){
      int fromPos; //建立变量,存储电机起始运动角度值
      if(toPos >= clawMin && toPos <= clawMax){    
      fromPos = claw.read();  // 获取当前电机角度值用于“电机运动起始角度值”
      } else {
      Serial.println("+Warning: Claw Servo Value Out Of Limit!");
      return;        
      }
      //指挥夹爪电机运行
      if (fromPos <= toPos){  //如果“起始角度值”小于“目标角度值”
      for (int i=fromPos; i<=toPos; i++){
      claw.write(i);
      delay (15);
       }
       }else { //否则“起始角度值”大于“目标角度值”
          for (int i=fromPos; i>=toPos; i--){
          claw.write(i);
          delay (15);
          Serial.println("Receive command"); 
          }
          }
      }

坐标自寻代码: 

float basepos;
float forearmpos;
float upperarmpos;
float upperarmpos1;
float upperarmpos2;
float pi=3.14;


float touying;

const int basehigh=13;
int duanbian;
int xiebian;
int x;
float y;
float z;
#include <AccelStepper.h>
// 定义电机控制用常量
const int enablePin = 8;  // 使能控制引脚
 
const int xdirPin = 5;     // x方向控制引脚
const int xstepPin = 2;    // x步进控制引脚
const int ydirPin = 6;     // y方向控制引脚
const int ystepPin = 3;    // y步进控制引脚
const int zdirPin = 7;     // z方向控制引脚
const int zstepPin = 4;    // z步进控制引脚



const int Num=6;
const int upperarm=16;
const int forearm=14;


AccelStepper base_stepper(1,xstepPin,xdirPin);       //建立步进电机对象———底座电机
AccelStepper forearm_stepper(1,ystepPin,ydirPin);    //建立步进电机对象———前臂电机
AccelStepper upperarm_stepper(1,zstepPin,zdirPin);   //建立步进电机对象———后臂电机
void setup() {
  pinMode(xstepPin,OUTPUT);     // Arduino控制A4988x步进引脚为输出模式
  pinMode(xdirPin,OUTPUT);      // Arduino控制A4988x方向引脚为输出模式
  pinMode(ystepPin,OUTPUT);     // Arduino控制A4988y步进引脚为输出模式
  pinMode(ydirPin,OUTPUT);      // Arduino控制A4988y方向引脚为输出模式
  pinMode(zstepPin,OUTPUT);     // Arduino控制A4988z步进引脚为输出模式
  pinMode(zdirPin,OUTPUT);      // Arduino控制A4988z方向引脚为输出模式

  pinMode(enablePin,OUTPUT);   // Arduino控制A4988使能引脚为输出模式
  digitalWrite(enablePin,LOW); // 将使能控制引脚设置为低电平从而让
                               // 电机驱动板进入工作状态
Serial.begin(9600);
  Serial.println(F("/\\\\\\\\\\\\\\\\\\"));     
  Serial.println(F("****The robotic arm serves you*****"));   
  Serial.println(F("/\\\\\\\\\\\\\\\\\\"));  
  Serial.println(F(""));  
  Serial.println(F("Please input motor command:"));
    base_stepper.setMaxSpeed(100);   // 设置电机最大速度为500
  base_stepper.setAcceleration(50.0);      // 初始化电机速度为0  setSpeed(0);
  
  forearm_stepper.setMaxSpeed(100);   
  forearm_stepper.setAcceleration(50.0);      

  upperarm_stepper.setMaxSpeed(100);
  upperarm_stepper.setAcceleration(50.0); 

base_stepper.setCurrentPosition(0);
forearm_stepper.setCurrentPosition(0);
upperarm_stepper.setCurrentPosition(0);
}

void loop() {
  // put your main code here, to run repeatedly:
 if (Serial.available()>1){
   int data = Serial.parseInt();
   x=data/100;
   y=abs((data-x*100)/10);
   z=abs(data%10);
   

    Serial.println(x);
    Serial.println(y);
    Serial.println(z);
    calculate();
    
    Serial.print("basepos= ");Serial.println(basepos);
    Serial.print("upperarmpos= ");Serial.println(upperarmpos);
    Serial.print("forearmpos= ");Serial.println(forearmpos);
    RunCommand(basepos,upperarmpos,forearmpos);
   base_stepper.run();     //底座电机运行
   forearm_stepper.run();  //后臂电机运行
   upperarm_stepper.run(); //前臂电机运行
 }  
}
void calculate(){
basepos=atan(x/y)*(180/pi)/1.8;

//底角角度
delay(500);
touying=sqrt(pow(x,2)+pow(y,2))-Num;

delay(500);
duanbian=abs(basehigh-z);
xiebian=sqrt(pow(duanbian,2)+pow(touying,2));
upperarmpos1=(acos((pow(upperarm,2)+pow(xiebian,2)-pow(forearm,2))/(2*upperarm*xiebian)))*(180/pi);
if(basehigh==z){
  upperarmpos2=0;
  }else if(basehigh>z){
    upperarmpos2=atan(touying/duanbian)* (180/pi)-90;
    }
    else if(basehigh<z){
      upperarmpos2=atan(duanbian/touying)-90;
      }
      upperarmpos=(upperarmpos1+upperarmpos2-15)/1.8;
//前臂角度


forearmpos=(acos((pow(upperarm,2)+pow(forearm,2)-pow(xiebian,2))/(2*upperarm*forearm))*(180/pi)+(upperarmpos-90))/1.8;
//后臂角度

}
void RunCommand(int b,int u,int f){
      Serial.println("Receive command");
       Serial.println(b);
       Serial.println(u);
       Serial.println(f);
    
    upperarm_stepper.runToNewPosition(u);
    delay(100);
    forearm_stepper.runToNewPosition(f);
    delay(500);
    base_stepper.runToNewPosition(b);
    delay(500);
  }

作为一名机械专业学生,实在不想在代码上花费过多时间(哭笑),没有将两个代码合并,并且本人编程能力很弱,代码中可能有许多错误。

本文章仅供个人学习所用。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值