我是靠谱客的博主 舒服纸飞机,这篇文章主要介绍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)函数写的控制两个伺服舵机当小车驱动电机的例子:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//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.速度控制实现不了,不能慢一点。

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
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】;

直接看我的代码吧:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#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信号计算公式写在程序备注中了。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#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°伺服电机速度控制内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部