概述
写在前面的话:如果你只想要我代码,建议你复制粘贴第一个和最后一个,试试。如果你想学习以后如何解决类似问题而不是仅仅的抄代码,建议你读完我啰嗦的话。希望对你有所帮助。你可以抄袭我代码,但请添加出处/引文。
(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代码与库(亲测可用)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复