写在前面

这篇博客不同于前面的树莓派和底盘子系统的控制了,而是要进行讲解分析如何通过arduino控制步进电机移动丝杆实现拨片架的水平移动了,那么为什么要实现这么一个功能呢?
原因很简单,因为我们的购物机器人依靠拨片拨取货物,但是在货架上或者仓库里的时候,购物机器人的拨片可能拨不到货物,因此就需要依靠丝杆带动拨片架去拨取货物

实现效果


步进电机驱动介绍

TB6600型驱动器接线方法

TB6600升级版步进电机驱动器的A+-和B+-是连接步进电机的黑绿红蓝,EN(使能端) DIR(方向信号线) PUL(脉冲线),驱动可以采用共阴极也可以采用共阳极,共阳共阴接好后只需用arduino给PUL送进脉冲信号以及给DIR送进方向信号即可,至于EN的信号端可以悬空,这样比较方便,供电端口24V

arduino驱动方法

按照上述方法连接好信号线后,就可以编写arduino程序控制步进电机了,在这里我使用了2种方法控制步进电机

按键控制

之前做电赛的时候还有一块调参的板子,上面有按键还有拨码,因此我就将其用作按键控制丝杆的调试工具了。

首先最主要的就是步进电机的控制方法了,线连接好之后,只需用arduino给PUL送进脉冲信号以及给DIR送进方向信号即可

#define pul1 9//pul接9号口 速度
#define dir1 8//dir接8号口 方向
#define speed_pul 4

脉冲信号的产生方法如下

  digitalWrite(pul1, HIGH);
  delayMicroseconds(speed_pul);
  digitalWrite(pul1, LOW);
  delayMicroseconds(speed_pul);

可以看到程序的实现方法很简单,就是通过给引脚输送一个高电平,延时一段时间,再输送一个低电平,再延时一段时间,从而产生方波信号。speed_pul这个参数就是方波中,高低电平的持续时间,那么这个方波频率的高低对于步进电机的运动会有怎样的影响呢?
我设计了一个实验,如图所示,我测量了不同方波频率下,丝杆从点1移动到点2所花费的时间

测试结果如下

横坐标是运动完全程所花费的时间,纵坐标是speed_pul数值大小,因为当speed_pul小于3时,步进电机不能移动,因此就将这种情况下的时间设定为0,不做考虑。从图中可以发现,随着speed_pul数值的减小,步进电机移动完全程的时间也在减少,因此为了减少购物机器人在场上移动丝杆的时间,我将speed_pul参数设置为了4。
产生了方波信号之后,只需要再给步进电机方向它就可以运动了,于是乎控制步进电机左移的程序如下所示

void turn_left()
{
  digitalWrite(dir1, HIGH);
  digitalWrite(pul1, HIGH);
  delayMicroseconds(speed_pul);
  digitalWrite(pul1, LOW);
  delayMicroseconds(speed_pul);
}

右移程序也可以类推

void turn_right()
{
  digitalWrite(dir1, LOW);
  digitalWrite(pul1, HIGH);
  delayMicroseconds(speed_pul);
  digitalWrite(pul1, LOW);
  delayMicroseconds(speed_pul);
}

至此完成了步进电机的驱动,接下来只需要结合按键即可,首先我们需要设置按键信号接入arduino的引脚
const int up_btn, down_btn, left_btn = 2, right_btn = 3;
接下来就是逻辑分析了,因为按键按下之后引脚呈现的是低电平,因此每检测到一个低电平之后我就输送50个方波信号给步进电机的驱动,从而控制步进电机带动丝杆转动一定距离,那么为什么是50个方波信号呢?其实也很好理解,一次性输送的方波信号个数越多,相当于驱动步进电机转动的时间越长,丝杆移动的距离也就越远,拨片架移动的距离也就越远。为了保证按键控制的精细度,我就设置了按一次按键发送50个方波信号。
具体实现如下

int btn_move_dis = 50;
void key_board()
{
  int i = 0;
  int left_flag;
  int right_flag;
  left_flag = digitalRead(left_btn);
  right_flag = digitalRead(right_btn);
  if (left_flag == LOW)
  {
    for (i = 0; i < btn_move_dis; i++)
    {
      turn_left();
    }
  }
  else if (right_flag == LOW)
  {
    for (i = 0; i < btn_move_dis; i++)
    {
      turn_right();
    }
    exeTime2 = millis() - exeTime1;
  }
}

串口控制

在实际的运行过程中,都是由树莓派发送指令控制其他子系统的,因此步进电机的控制数据肯定也是通过串口接收的,所以我们还要加一个串口控制的部分

  while (Serial.available() > 0) {
    delay(100);
    move_ctl_dis = Serial.parseInt();//将串口接收到的字符转化成数字
    Serial.println(move_ctl_dis);
  }
  while (Serial.read() >= 0) {}//清空缓冲区

这段程序还是比较简单好搞明白的,那么现在就是有一个问题。在按键控制步进电机的时候是arduino检测到一个低电平就会就会发送50个方波信号,那么发送了50个方波信号之后,丝杆实际上能移动多少我们不得而知。其实这其中的问题是我们目前还不能精细的控制步进电机的移动距离,而只能控制它往特定方向进行连续运动。因此我们就需要修改一下驱动程序

int move_ctl_dis;
unsigned long cm_cnt = 32000; //32000是1cm
  if (move_ctl_dis != 0) {
    if (move_ctl_dis > 0) {
      for (int i = 0; i < move_ctl_dis; i++)
      {
        for (unsigned long j = 0; j < cm_cnt; j++ ) {
          turn_left();
        }
      }
    } else if (move_ctl_dis < 0) {
      move_ctl_dis = -move_ctl_dis;
      for (int i = 0; i < move_ctl_dis; i++)
      {
        for (unsigned long j = 0; j < cm_cnt; j++ ) {
          turn_right();
        }
      }
    }
    move_ctl_dis = 0;
  }

这段程序有什么特别的呢?我们抓取主要的一部分来做特别分析

 for (int i = 0; i < move_ctl_dis; i++)
 {
   for (unsigned long j = 0; j < cm_cnt; j++ ) {
     turn_left();
   }
 }

其中外循环的move_ctl_dis是arduino接收到的上层发送过来的移动距离,而内循环则代表了每发送cm_cnt个方波信号,丝杆就会带动拨片架移动1cm,这个数据是通过实际测试得出的,因此move_ctl_dis的绝对值即拨片架实际移动的距离,正负不过是左右移动的区别而已,因此在主循环里这样写就可以了

void loop()
{
  key_board();
  CatchMove();
  while (Serial.available() > 0) {
    delay(100);
    move_ctl_dis = Serial.parseInt();
    Serial.print("Serial.parseInt:");
    Serial.println(move_ctl_dis);
    move_ctl_dis = move_ctl_dis;
  }
  while (Serial.read() >= 0) {}
}

最后还有一个问题,那就是我们还不能知道拨片架的实际位置,这样不利于我们进一步控制,因此我想了个办法。那就是在刚开始的时候总是通过按键将拨片架移动到特定位置,那么之后的位置都是相对于这个初始位置移动的,这样我们就有了参照,才能准确、稳定控制拨片架照我们预想的方式运动。


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