完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1 条评论
1个回答
|
|
写在前面的话:如果你只想要我代码,建议你复制粘贴第一个和最后一个,试试。如果你想学习以后如何解决类似问题而不是仅仅的抄代码,建议你读完我啰嗦的话。希望对你有所帮助。你可以抄袭我代码,但请添加出处/引文。
(PS:我的每篇帖子都是用来记录我成长的,相当于公开的日记,很多原创内容不一定是我创造的算法,很多技术本来就存在的,我只不过是把它们学懂了然后用大白话整理在这里了,但是我可以百分百保证我的帖子没有抄袭谁的文章,若有雷同,纯属巧合。我写博客的初衷是记录成长,后来渐渐地有小伙伴私聊称赞我,我就觉得我曾经也是举目四望无援助的萌新,很多技术都是大佬懒得分享,萌新又没几个会的,所以我就想做点承上启下造福萌新的事情。然后写了很多帖子,甚至逼迫自己至少一个月产一篇。那段时间忙碌又快乐着。渐渐地开始做Youtube的搬运工。直到我的萌新福利贴刷到了博客前一万排名,当时很多酸溜溜的小伙伴表示不服,凭什么你这么垃圾都能排前一万?这个,我觉得这个应该和帖子数量和点击率有关系。越来越多的人不再讨论技术而是无脑喷我,然后那段时间我就深刻的反思,觉得是浮躁了。然后就关闭了帖子,全部设置成私密了。今天有技术萌新小伙伴找到我,希望我分享更多关于树莓派的东西,对之前的事情也表示理解。嗯,感谢还在支持我写帖子的你们!今后我们大家一起努力,如果我哪写的不对,请批评指正,谢谢大家!) 一.转向控制:我想用360度伺服电机当小车的驱动电机,Arduino的例子里有个sweep可用来参考,然后根据本william hill官网 博客上的建议,写了如下代码。功能是驱动小车前进(轮子转一圈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)函数写的控制两个伺服舵机当小车驱动电机的例子: 下文所示例子是我用write(degree)函数写的控制两个伺服舵机当小车驱动电机的例子: //arduino的 //这两个函数都可以控制360/180这两种舵机。 /********************这是write()函数的例子***********/ #include 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); } 二.转速控制 然后搜一搜博客william hill官网 ,居然没什么人教这个。然后就搜了搜谷歌。在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 下面我们把上文的所有内容通过下文的代码进行总结与使用。 1. 我用write(degree,speed)函数输入角度控制位移,它的degree值取值范围为【0,180】; 2. 我用attach(pin,min,max)函数配置舵机控制引脚,最小与最大脉宽根据芯片手册设定符合值范围【min=1300,max=1700】; 直接看我的代码吧: #include //#include //arduino的 //这两个函数都可以控制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 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的时候就是慢速后退。再加/减值就是再快点。反之右舵机也是这么设置。 我不太喜欢把所有的细节都告诉你们,不是我藏不藏私的事,而是啥都告诉你们,不利于你们自学,这些东西怎么算怎么查芯片手册都是自己主动学到的,什么都等着别人告诉,就失去了独立处理问题的能力和进步的前提。 愿大家学有所成,玩的愉快! 这不是结束,大家可以拿自己的舵机试一下这个库的别的函数的功能:
|
|
|
|
只有小组成员才能发言,加入小组>>
2444 浏览 0 评论
9160 浏览 4 评论
36840 浏览 19 评论
5040 浏览 0 评论
24825 浏览 34 评论
1554浏览 2评论
1781浏览 1评论
2227浏览 1评论
1581浏览 0评论
555浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-29 18:41 , Processed in 1.252870 second(s), Total 82, Slave 66 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号