我是靠谱客的博主 舒服纸飞机,最近开发中收集的这篇文章主要介绍180°和360°伺服电机速度控制,转向控制Arduino代码与库(亲测可用),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

写在前面的话:如果你只想要我代码,建议你复制粘贴第一个和最后一个,试试。如果你想学习以后如何解决类似问题而不是仅仅的抄代码,建议你读完我啰嗦的话。希望对你有所帮助。你可以抄袭我代码,但请添加出处/引文。

(PS:我的每篇帖子都是用来记录我成长的,相当于公开的日记,很多原创内容不一定是我创造的算法,很多技术本来就存在的,我只不过是把它们学懂了然后用大白话整理在这里了,但是我可以百分百保证我的帖子没有抄袭谁的文章,若有雷同,纯属巧合。我写博客的初衷是记录成长,后来渐渐地有小伙伴私聊称赞我,我就觉得我曾经也是举目四望无援助的萌新,很多技术都是大佬懒得分享,萌新又没几个会的,所以我就想做点承上启下造福萌新的事情。然后写了很多帖子,甚至逼迫自己至少一个月产一篇。那段时间忙碌又快乐着。渐渐地开始做Youtube的搬运工。直到我的萌新福利贴刷到了博客前一万排名,当时很多酸溜溜的小伙伴表示不服,凭什么你这么垃圾都能排前一万?这个,我觉得这个应该和帖子数量和点击率有关系。越来越多的人不再讨论技术而是无脑喷我,然后那段时间我就深刻的反思,觉得是浮躁了。然后就关闭了帖子,全部设置成私密了。今天有技术萌新小伙伴找到我,希望我分享更多关于树莓派的东西,对之前的事情也表示理解。嗯,感谢还在支持我写帖子的你们!今后我们大家一起努力,如果我哪写的不对,请批评指正,谢谢大家!)

一.转向控制:我想用360度伺服电机当小车的驱动电机,Arduino的例子里有个sweep可用来参考,然后根据本论坛博客上的建议,写了如下代码。功能是驱动小车前进(轮子转一圈360度)精确到让停就停的0.5毫米行程误差。

除了大神们会考虑使用传统的PWM控制(自己写代码发射PWM信号)之外,我们常用的Arduino自带的函数,有两种。一种是write()函数,另一种是writeMicroseconds();函数。这两种,前者是通过设定舵机旋转角度控制移动距离,后者是通过设置延时脉宽(PWM)微妙数来控制位移。设定也很简单:

PS:我说的都是默认值,我写的代码肯定都对,运行正常,但是真实值需要你查芯片手册设定,毕竟你的电机与我的不一样,如果你直接用我的代码运行你的电机,在停止电机的命令的时候电机可能不会停止,会小幅度运转。

servo.write(x): 若选择X值的范围是【0-180】,x是角度值,90是中间位置,0是全速前进,180是反向全速前进。

                        若选择X值的范围是【0-360】,x是角度值,180是中间位置,360是全速前进,-360是反向全速前进

servo.writeMicroseconds(X): 若选择X值的范围是【1000-2000】,x是脉冲微妙数,1500是中间位置,1000是全速前进,2000是反向全速前进。

至于真实值,需要你去查当前使用的伺服电机的芯片手册,比如我的伺服电机(型号:Parallax continuous servo motor)描述如下:

那么X值的范围就应该是【1300-1700】。它的PWM脉冲控制原理如下图所示

即便是如此查芯片手册进行精确设定,也不能保证伺服电机就100%按我们所想的来运动。这个芯片手册在结尾也说了:

都1525微秒了还小幅度转动呢,66666。还a little faster呢,哎。。。官方也是一脸无奈的摆摆手“怪我喽?”。光转向控制(PWM)还不够啊,我们太需要速度控制了。在本文的最后一个代码示例中我自己调除了真实误差10us(比如停车是1490(1500))。

下文所示例子是我用write(degree)函数写的控制两个伺服舵机当小车驱动电机的例子:

//arduino的<servo.h>库提供两个函数控制舵机。write()和writeMicroseconds();
  //这两个函数都可以控制360/180这两种舵机。
  
  /********************这是write()函数的例子***********/
#include <Servo.h> 
  
  Servo servoLeft;
  Servo servoRight;
  int i=0;
  int j=0; 
  void setup() 
  { 
   
    servoLeft.attach(12);//P12是左舵机,P13是右舵机。双轮在前为正
    servoRight.attach(13);
    //servoLeft.write(x);  // 左舵机:x=90舵机不动,x=0舵机全速后退,x=180舵机全速前进
    //servoRight.write(x);  // 右舵机:x=180舵机全速后退,x=90舵机不动,x=0舵机全速前进
  
  } 
  
  void loop() {
    /***************成功了,下文for语句可实现左右舵机同时前进一圈(360°)**************/
      for( i=90 , j=90;i<=180 , j>=0;i += 1 , j -= 1){
      servoLeft.write(i);  // 左舵机前进(90-180)
      servoRight.write(j);  // 右舵机前进(90-0)
      delay(15);
      }
    //如果想前进2圈,就把上文的for语句复制一遍就好了
   
     /*******************90°看起来是一种很优秀的左转啊***************************/
    //servoLeft.write(90);  // 左舵机停
    //servoRight.write(90);  // 右舵机停
    /***************成功了,下文for语句可实现左右舵机同时后退一圈(360°)**************/
      for( i=90 , j=90;i<=180 , j>=0;i += 1 , j -= 1){
      servoLeft.write(j);  // 左舵机后退(90-0)
      servoRight.write(i);  // 右舵机后退(90-180)
      delay(15);
      }
   
      
 
  }

         运行一下不仔细看的话还煞有其事,实际上有两个问题:1、明明write 90 却不能停车,舵机会小幅度颤动(优秀的左转666666.)。2.速度控制实现不了,不能慢一点。

看到这里有的人可能想到了另一种解决方案:延时启动舵机,运动起来就像突突突突的拖拉机,一顿一顿的感觉

void servoTutututu(){
  /***************下文for语句可实现左右舵机同时前进四分之一圈为一周期90/4=23**************/
      
      //servoLeft1.write(180);  // 左舵机前进
      //servoRight1.write(0);  // 右舵机前进
      for( i=90 , j=90;i<=113 , j>=67;i += 1 , j -= 1){
      servoLeft1.write(i);  // 左舵机前进(90-113)
      servoRight1.write(j);  // 右舵机前进(90-67)
      delay(15);
      

      
}

这种治标不治本的方法是可以辅助减速,但不是最优解,在本文结尾可以看真正的减速操作(减速函数+延时)。

二.转速控制

然后搜一搜博客论坛,居然没什么人教这个。然后就搜了搜谷歌。在GitHub上早有大神在2010年搞定了所以问题,并更新了Arduino库。于是乎我们直接打开ArduinoIDE软件的属性,查找文件位置,找到lib文件夹,打开如下图所示servo.h

看人家说的:

The methods are:

    Servo - Class for manipulating servo motors connected to Arduino pins.

    attach(pin )  - Attaches a servo motor to an i/o pin.
    attach(pin, min, max  ) - Attaches to a pin setting min and max values in microseconds
    default min is 544, max is 2400  
 
    write()     - Sets the servo angle in degrees.  (invalid angle that is valid as pulse in microseconds is treated as microseconds)
    writeMicroseconds() - Sets the servo pulse width in microseconds
    read()      - Gets the last written servo pulse width as an angle between 0 and 180.
    readMicroseconds()   - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
    attached()  - Returns true if there is a servo attached.
    detach()    - Stops an attached servos from pulsing its i/o pin.

首先,我们试一下这个“attach(pin, min, max  ) ”设定,若想知道这两个值,要自己去查当前使用的伺服电机的芯片手册。

min(可选):脉冲宽度,以微秒为单位,对应伺服上的最小(0度)角度(默认为544)
max(可选):脉冲宽度,以微秒为单位,对应伺服上的最大(180度)角度(默认为2400)

因为我们之前只是用了attach(pin),系统使用默认值,毕竟天下没有完全相同的伺服电机,所以在write(90)的时候,舵机没有像我们想象的那样执行停机操作。误差是存在的。

下图是我找到一个180°伺服电机的芯片手册:(注意我打勾√的几个值,要搞懂它们是啥意思。)

死区宽度:(阶跃脉冲产生阶跃时波动的最小脉冲宽度就是死区宽度,这是机械误差,例如开关抖动)

其次,我们用一下write(value, speed)控制伺服电机的速度。太方便了!

write(value)     - Sets the servo angle in degrees.  (invalid angle that is valid as pulse in microseconds is treated as microseconds)
write(value, speed) - speed varies the speed of the move to new position 0=full speed, 1-255 slower to faster
write(value, speed, wait) - wait is a boolean that, if true, causes the function call to block until move is complete

细心的人会发现我的库没有更新呢,所以上段绿色的没有在servo.h中被定义。这就需要我们自己手动更新库或者直接去GitHub下载(https://github.com/netlabtoolkit/VarSpeedServo),本着“手把手教你入坑”的理念,在这说明一下怎么把库文件导入Arduino库中。

第一步,下载.ZIP文件

第二步,把ZIP文件放Library文件夹中,并操作IDE进行导入

选中Arduino图标,右键打开属性,点击打开文件位置,然后打开Library文件夹,把下载好的ZIP文件解压并粘贴在Library文件夹中。然后打开ArduinoIDE软件。打开你写的程序(若没写,可以复制粘贴本文第一个例子)在所有程序的第一行,点击一下,然后打开上方的“Sketch”再点击“Include library”选中刚才复制粘贴的文件夹“VarSpeedServo-master”,然后你就会发现在你的程序中第一行出现了“#include <VarSpeedServo.h>”库文件名,你还需要删除老的库文件名“#include <Servo.h>”。

下面我们把上文的所有内容通过下文的代码进行总结与使用。

1. 我用write(degree,speed)函数输入角度控制位移,它的degree值取值范围为【0,180】;

2. 我用attach(pin,min,max)函数配置舵机控制引脚,最小与最大脉宽根据芯片手册设定符合值范围【min=1300,max=1700】;

直接看我的代码吧:

#include <VarSpeedServo.h>
//#include <Servo.h>//我们已经不需要这个老库了

//arduino的<servo.h>库提供两个函数控制舵机。write()和writeMicroseconds();
  //这两个函数都可以控制360/180这两种舵机。
  
  /********************这是write()函数的例子***********/


  VarSpeedServo servoLeft;//注意这里因为调用的是新库,所以要把左边红字Servo改为VarSpeedServo
  VarSpeedServo servoRight;
  int i=0;
  int j=0; 
  void setup() 
  { 
   
    servoLeft.attach(12,1300,1700);//P12是左舵机,P13是右舵机。双轮在前为正.使用芯片手册值
    servoRight.attach(13,1300,1700);
    //servoLeft.write(x);  // 左舵机:x=90舵机不动,x=0舵机全速后退,x=180舵机全速前进
    //servoRight.write(x);  // 右舵机:x=180舵机全速后退,x=90舵机不动,x=0舵机全速前进
  
  } 
  
  void loop() {
    /***************成功了,下文for语句可实现左右舵机同时前进一圈(360°)**************/
      for( i=90 , j=90;i<=180 , j>=0;i += 1 , j -= 1){
      servoLeft.write(i,100);  // 左舵机前进(90-180)
      servoRight.write(j,100);  // 右舵机前进(90-0)
      //servoRight.write(j,100,true);  // 右舵机前进(90-0),选择中等速度100,如果为true,则导致函数调用阻塞,直到移动完成
      delay(15);
      }
    //如果想前进2圈,就把上文的for语句复制一遍就好了
   
     /*******************90°看起来是一种很优秀的左转啊***************************/
    //servoLeft.write(90);  // 左舵机停
    //servoRight.write(90);  // 右舵机停
    /***************成功了,下文for语句可实现左右舵机同时后退一圈(360°)**************/
      for( i=90 , j=90;i<=180 , j>=0;i += 1 , j -= 1){
      servoLeft.write(j,100);  // 左舵机后退(90-0)
      servoRight.write(i,100);  // 右舵机后退(90-180)
      delay(15);
      }
   
      
 
  }

上文是使用write(degree,speed)函数进行速度控制,实际上就是控制PWM的占空比,简单的说就是我第二个示例的优化版,第二个示例开拖拉机,突突突,它的原理就是通过开关发动机使发动机在关闭的时期进行自然减速。但是实际上当行驶距离太短,加速时间太短的时候,以上方法便不适用于精确控制了。

那么,即想短距离高精度控制泊车,又想精确测量与控制行驶速度,我们怎么办呢?很简单,那就是用writeMicroseconds()函数。

下面的程序是我写的,我把PWM信号计算公式写在程序备注中了。

#include <VarSpeedServo.h>

VarSpeedServo servoLeft1;//define left servo motor
VarSpeedServo servoRight1;//define right servo motor

double i=0;
double j=0; 

int inputL = A0;//CNY70 Output as an input to Arduino我用了三个红外传感器循迹黑白线
int inputM = A1;//CNY70 Output as an input to Arduino
int inputR = A2;//CNY70 Output as an input to Arduino

int outputL = 8;//Pin8 be output connect Left LED在这定义了三个输出,外接小灯泡当车辆转向灯
int outputM = 9;//Pin9 be output connect Middle LED
int outputR = 10;//Pin10 be output connect Right LED

int L = 5;//Left infrared sensor if 5(0) means meet black line检测左白线
int M = 5;//Middle infrared sensor if 5(0) means meet black line检测中黑线
int R = 5;//Right infrared sensor if 5(0) means meet black line检测右白线

void servoStop();//这些是子函数,这是停车
void servoBack();//后退
void servoGo();//前进
void servoLeft();//左转
void servoRight();//右转
void servoGo1cycle();//前进一圈(精确控制)
void servoBack1cycle();//后退一圈(精确控制)

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);//9600波特率
  
  //Take the two wheels in front, as the positive direction
  
  servoLeft1.attach(12,1290,1690);//P12 is Left Motor,datasheet PWM Microseconds[1290-1690]
  servoRight1.attach(13,1290,1690);//P13 is Right Motor,datasheet PWM Microseconds[1290-1690]
  //servoLeft1.writeMicroseconds(x);  // 左舵机:1290(1300)后退,1690(1700)前进,有10微妙//的误差1490中位(1500中位)
  //servoRight1.writeMicroseconds(x);  // 右舵机:1290(1300)前进,1690(1700)后退,有10微//妙的误差1490中位(1500中位)
  pinMode(inputL,INPUT);
  pinMode(inputM,INPUT);
  pinMode(inputR,INPUT);
  
  pinMode(outputL,OUTPUT);
  pinMode(outputM,OUTPUT);
  pinMode(outputR,OUTPUT);

  digitalWrite(8,HIGH);
  digitalWrite(9,HIGH);
  digitalWrite(10,HIGH);
}


void loop() {
  L = analogRead(inputL)+5;//当红外传感器检测到黑线输出0,但是用IF的<>判断的时候,
//数值不能为负,所以要把原始值加5进行消负数处理,所以当检测到黑线时,输出5而不是0
  M = analogRead(inputM)+5;
  R = analogRead(inputR)+5;

  Serial.print(L);
  Serial.print(M);
  Serial.println(R);
  
  //Serial.println(val);//监视器显示数据
  //delay(500);
  
  if(M<6 && L>6 && R>6){servoGo();}
  if(R<6 && M>6 && L>6){servoStop();servoRight();servoGo();}
  if(L<6 && M>6 && R>6){servoStop();servoLeft();servoGo();}
  //servoLeft();
  //servoStop();

    
}

void servoGo(){
  /***************就是前进**************/
      

      servoLeft1.writeMicroseconds(1550);  // 左舵机前进(1490-1690);   加60 速度控制
      servoRight1.writeMicroseconds(1430);  // 右舵机前进(1490-1290);   减60 速度控制
      digitalWrite(outputL,HIGH);
      digitalWrite(outputM,LOW);
      digitalWrite(outputR,HIGH);

      
}
void servoBack(){
  /***************就是后退**************/
      servoLeft1.writeMicroseconds(1430);  // 左舵机后退(1490-1290);    
      servoRight1.writeMicroseconds(1550);  // 右舵机后退(1490-1690);   
      digitalWrite(outputL,HIGH);
      digitalWrite(outputM,LOW);
      digitalWrite(outputR,HIGH);
  
  }
 void servoStop(){
  /***************就是停车**************/
      
      servoLeft1.writeMicroseconds(1490);  
      servoRight1.writeMicroseconds(1490);  
      digitalWrite(outputL,HIGH);
      digitalWrite(outputM,HIGH);
      digitalWrite(outputR,HIGH);
     
      }
  
  
  void servoLeft(){
  /***************单舵机,前向左转(右舵机前进,左舵机不动)90°**************/
      for( j=1490;j>=1440;j -= 1.99){ //1.99为考虑误差后的实验真实值
      servoLeft1.writeMicroseconds(1490);  // 左舵机前进(1490-1690);    
//1490-1290=200;200us/90°=2.22us/°;理论计算值是2.22,这里代表,每前进1°,需要发射2.22微妙脉冲
      servoRight1.writeMicroseconds(j);  // 右舵机前进(1490-1440);   //(1490-1290)/(360°/90°)=50; 1490-50=1440
      delay(15);
      }
      digitalWrite(outputL,LOW);
      digitalWrite(outputM,HIGH);
      digitalWrite(outputR,HIGH);
      delay(15);

      }
      
    
   void servoRight(){
    /***************单舵机,前向右转(左舵机前进,右舵机不动)90°**************/
      for( i=1490;i<=1540;i += 1.90){ //1.90为考虑误差后的真实值
      servoLeft1.writeMicroseconds(i);  // 左舵机前进(1490-1690);    //(1490-1690)/(360°/90°)=50; 1490+50=1540
      servoRight1.writeMicroseconds(1490);  // 右舵机前进(1490-1440);   
      delay(15);
      }
      digitalWrite(outputL,HIGH);
      digitalWrite(outputM,HIGH);
      digitalWrite(outputR,LOW);
      delay(15);

      }
    
   void servoGo1cycle(){
    /***************完美级,下文for语句可实现左右舵机同时前进一圈(360°)**************/
    for( i=1490 , j=1490;i<=1690 , j>=1290;i += 1.99 , j -= 1.99){ //1.99为考虑误差后的真实值
      servoLeft1.writeMicroseconds(i);  // 左舵机前进(1490-1690);    1490-1290=200;200/90=2.22;
      servoRight1.writeMicroseconds(j);  // 右舵机前进(1490-1290);   故考虑误差的情况下增/减计数在2.22左右
      delay(15);
      }
    }
   void servoBack1cycle(){
    /***************完美级,下文for语句可实现左右舵机同时后退一圈(360°)**************/
      for( i=1490 , j=1490;i<=1690 , j>=1290;i += 1.90 , j -= 1.90){
      servoLeft1.writeMicroseconds(j);  // 左舵机后退
      servoRight1.writeMicroseconds(i);  // 右舵机后退
      delay(15);
      }
    }

对上文补充说明:

1.   10微妙的真实误差是经过真实测试出来的。它的测试方法是:连接好舵机(用几个连接几个),你输入servo.writeMicroseconds(1500)进行中位测试,理想情况下舵机应该静止不动,如果它动了,你应该条件这个值,它的范围在芯片手册中给出,比如我的芯片手册给的是5%的误差(1500*5%=15us),那么你的调节范围就是【1485,1515】然后你每5微妙为一次改变值进行调节。最后我调出当中值等于1490时,舵机们(相同型号舵机误差也一样)静止不动。

2.   关于速度增量值的计算:我们知道,Arduino示例中的Sweep例子中是+-1,它的意思是,每延时15微妙,电机前进一度。同理我们用writeMicroseconds函数时计算的增量值的单位是(us/度)它的意思是每前进一度,舵机PWM发送N微秒脉冲。那么怎么算呢?比如我们要实现舵机旋转四分之一圈(360°/4=90度)的精确控制。电机从0°到360°的脉冲范围是1290(真实值)到1490(真实值),经历的真实脉冲时间是1490-1290=200微秒(一圈)。四分之一圈就是200/4=50微秒。如果我们的电机从1290进行增计数的话,最终应该增加到1290+50=1340结束。增量等于200us/90°=2.22us/°。(注意:这个增量是理论计算值,不是真实值,因为真实值还要考虑误差)加上真实误差(根据经验是0.3的误差,根据芯片手册的计算是0.111(2.22*5%)但是理论就是理论,与真实永远差很多,所以我们按0.2那么调),增量的矫正范围是【1.90-2.50】,经过实际测试,发现1.99是小车前进增量的理想值(实际操作就是让它转一圈看它下次停的位置与原位置差多少,要是靠后了就加计数调整,要是靠前了就减计数调整)1.90是小车后退增量的理想值。

3.   关于控制前进速度的改进方法:既然用了writeMicroseconds函数,看我上文的例子,在那个前进的子函数中,我也没用循环(循环是用来精确控制位移的),就是直接前进,然后比如左电机吧【1490-1690】前进;【1490-1290】后退,1290与1690分别是全速后退与全速前进。1490是中位停止。所以当你写1490+50=1540的时候就是慢速前进,写1490-50=1440的时候就是慢速后退。再加/减值就是再快点。反之右舵机也是这么设置。

 

我不太喜欢把所有的细节都告诉你们,不是我藏不藏私的事,而是啥都告诉你们,不利于你们自学,这些东西怎么算怎么查芯片手册都是自己主动学到的,什么都等着别人告诉,就失去了独立处理问题的能力和进步的前提。

愿大家学有所成,玩的愉快

这不是结束,大家可以拿自己的舵机试一下这个库的别的函数的功能:

The methods are:
   VarSpeedServo - Class for manipulating servo motors connected to Arduino pins.
   attach(pin )  - Attaches a servo motor to an i/o pin.
   attach(pin, min, max  ) - Attaches to a pin setting min and max values in microseconds
   default min is 544, max is 2400  
 
   write(value)     - Sets the servo angle in degrees.  (invalid angle that is valid as pulse in microseconds is treated as microseconds)
   write(value, speed) - speed varies the speed of the move to new position 0=full speed, 1-255 slower to faster
   write(value, speed, wait) - wait is a boolean that, if true, causes the function call to block until move is complete
   writeMicroseconds() - Sets the servo pulse width in microseconds
   read()      - Gets the last written servo pulse width as an angle between 0 and 180.
   readMicroseconds()  - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
   attached()  - Returns true if there is a servo attached.
   detach()    - Stops an attached servos from pulsing its i/o pin.
   slowmove(value, speed) - The same as write(value, speed), retained for compatibility with Korman's version
   stop() - stops the servo at the current position
   sequencePlay(sequence, sequencePositions); // play a looping sequence starting at position 0
   sequencePlay(sequence, sequencePositions, loop, startPosition); // play sequence with number of positions, loop if true, start        //at position
   sequenceStop(); // stop sequence at current position

以上子函数膜拜大神的地址为:https://github.com/netlabtoolkit/VarSpeedServo/blob/master/VarSpeedServo.h

最后

以上就是舒服纸飞机为你收集整理的180°和360°伺服电机速度控制,转向控制Arduino代码与库(亲测可用)的全部内容,希望文章能够帮你解决180°和360°伺服电机速度控制,转向控制Arduino代码与库(亲测可用)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(75)

评论列表共有 0 条评论

立即
投稿
返回
顶部