写在前面

之前我写了arduino驱动步进电机带动丝杆运动的博客,链接在下面:
arduino控制步进电机移动丝杆实现拨片架水平移动
既然已经可以驱动丝杆带动拨片架运动了,接下就是需要驱动ST90S微型舵机来带动拨片拨取货物了,本博客就介绍arduino如何驱动ST90S微型舵机以及分享在驱动过程中所遇到的问题以及解决方法

图片概览

舵机/拨片安装方法

舵机驱动板

思路以及实现

可以看到上图中我们总共有5个拨片,每个拨片都是由一个舵机去控制的,因此我们总共需要5个小舵机,为了区分他们,我将其进行了标号

并且他们的变量名称我是按照他们的位置来描述的

#include <Servo.h>
Servo UpLeftServo; // 上左,从后往前看,下同
Servo UpRightServo ; // 上右
Servo DownLeftServo; // 下左
Servo DownRightServo ; // 下右
Servo DownMidServo ; // 下中

arduino自带舵机驱动的库,叫Servo.h,因此在使用之前我们先要将其包含进来。定义好名字之后就要进行初始化了,前面我们定义了舵机控制对象,在这里我们要将其进行初始化,函数就是XXX.attach(pin)XXX是定义的舵机名字,pin是该舵机的信号引脚,在我们用的板子中‘D’开头的信号引脚初始化的时候不用带‘D’,否则会提示找不到该引脚,还请注意

void AngleInit()
{//开始都是先都打到90度
    UpLeftServo.write(90);
    delay(20);
    DownLeftServo.write(90);
    delay(20);
    DownMidServo.write(90);
    delay(20);
    DownRightServo.write(90);
    delay(20);
    UpRightServo.write(90);

}
void setup() {
  for(uint8_t i = 1 ; i < 6;i++){
      last_turn_angle[i] = 90;
  }
  UpLeftServo.attach(7); //定义舵机的引脚为
  DownLeftServo.attach(10);
  DownMidServo.attach(A0);
  DownRightServo.attach(A3);
  UpRightServo.attach(A2);
  AngleInit();
  Serial.begin(9600); //设置波特率
  while (Serial.read() >= 0) {};
}

因为舵机打角是连续的才比较稳定,因此我是让舵机按照1°的变化趋势进行打角。如果一次性舵机让舵机打一个很大的角度,舵机很有可能会用力过猛,导致打到相应的角度之后进入一个抖动的状态,如下图所示

同时还有一个问题,那就是如果我不知道某一时刻舵机的打角,那么我按照1°打角变化的话,那就不知道是按1°递增还是按1°递减了,因此我就在初始化的时候都把舵机打到某一个特定的位置,我设定的是90°
下层3个舵机角度与状态关系

上层2个舵机角度与状态关系

那么之后相对于给90°进行偏移打脚,即我们串口送给arduino板子的舵机打脚范围是-90~90°时即可完成世界坐标下的0 ~ 180°打脚。但是这样还是不够,因为在后面打脚变化了之后我还需要知道上一次的打脚位置,因此我就设置了一个last_turn_angle数组,专门用来存放每个舵机上一次的打脚,并且根据上一次的打脚来打这次需要的角度,程序实现如下

int last_turn_angle[6];
void setup() {
    for(uint8_t i = 1 ; i < 6;i++){//初始化里的赋值操作,初始化打脚都是90度
        last_turn_angle[i] = 90;
    }
}
void TestServo()
{
    send_sta = Serial.write("&");
    char symbol;
    char angle;
    char steer_pos;
    uint8_t now_id;
    steer_pos = mov_cmd[0];
    symbol = mov_cmd[1];
    angle = mov_cmd[2];


    if(symbol == '+'){
        turn_angle = angle - '0';
    }else if(symbol == '-'){
        turn_angle = -(angle - '0');
    }

    turn_angle *= 10;
    turn_angle = 90 + turn_angle;
    if(turn_angle > 177) turn_angle = 177;//限幅
    else if(turn_angle < 2) turn_angle = 2;//限幅
    now_id = (steer_pos - '0');

    if(steer_pos == '1'){ServoCtl(UpLeftServo,last_turn_angle[now_id]);}
    else if(steer_pos == '2'){ServoCtl(DownLeftServo,last_turn_angle[now_id]);}
    else if(steer_pos == '3'){ServoCtl(DownMidServo,last_turn_angle[now_id]);}
    else if(steer_pos == '4'){ServoCtl(DownRightServo,last_turn_angle[now_id]);}
    else if(steer_pos == '5'){ServoCtl(UpRightServo,last_turn_angle[now_id]);}
    last_turn_angle[now_id] = turn_angle;
    Serial.println(turn_angle);
    send_sta = Serial.write("#");
}

void ServoCtl(Servo steer,uint8_t last_turn_angle)
{
  int symbol = 0;
  if(turn_angle > last_turn_angle){
    symbol = 1;
  }else{
    symbol = -1;
  }

  for(uint8_t i = last_turn_angle;i != turn_angle; i += symbol){
    steer.write(i);
  }
}

但是即使我做了这样的1°变化缓冲操作之后,舵机打脚时还是会出现抖动的状态,百思不得其解,遂上网搜索原因,有说servo库所用定时器和串口定时器冲突的,有说没有共地的,我都试了,但是没有用,只好冷静下来自己思考了。
灵感总是来得出乎意料,我想是不是因为我每次打脚都打到底的原因了呢?因为我每次都让舵机打到0°或者180°,相当于是都打到了舵机的极点,这样可能不够稳定,因此我就想着给舵机打角限幅,限制到了2~177°,程序如下

if(turn_angle > 175) turn_angle = 175;//限幅
else if(turn_angle < 5) turn_angle = 5;//限幅

最终解决了问题,实现了舵机的稳定控制打角,效果如下

串口命令

因为程序是串口接收命令的,因此loop()里面是这样写的

void loop() {
    delay(50);
    while (Serial.available() > 0) {
      delay(100);
      send_sta = Serial.readBytes(mov_cmd,6);
      TestServo();
      break;
    }
    while (Serial.read() >= 0){}
}

为了保证控制的稳定性,我在程序里面设置了应答措施,当arduino板子接收到命令之后会通过send_sta = Serial.write("&");语句,也就是通过串口发送一个&符号告诉上层自己接收到了命令,当arduino完成了动作之后会通过send_sta = Serial.write("#");语句,也就是通过串口发送一个#符号告诉上层自己完成了命令
具体的命令传递数组含义如下图所示

烧写问题

在烧写程序的时候,偶尔会报这样一个错误

我也在网上查了很多资料,有说板子型号没选对的,有说没有共地的,我都尝试了一下,发现并没有什么用,最终还是通过插拔烧写线和重启arduino IDE来解决这个问题的,目前并不知道该问题的具体解决方法,但是通过上述操作还是顺利的将程序烧写进了arduino板子。

实际上,就算烧写的时候arduino IDE报了如上错误,程序还是会被烧写进arduino板子里的。


(づ ̄3 ̄)づ╭❤~一键三连,这次一定(๑•̀ㅂ•́)و✧