步进电机是将脉冲信号转换成机械运动的一种特殊电机。步进电机在使用时不需要额外的反馈,这是因为除非失步,否则步进电机每次转动时的角度已知的,由于它的角度位置已知就能精确控制电机运动的位置。一般我们会用Arduino驱动的小型步进电机有以下两种。
步进电机内部实际上产生了一个可以旋转的磁场,如图所示,当旋转磁场依次切换时,转子(rotor)就会随之转动相应的角度。当磁场旋转过快或者转子上所带负载的转动惯量太大时,转子无法跟上步伐,就会造成失步。
从步进电机的矩频特性图上可知,步进电机以越快的速度运行,所能输出的转矩越小,否则将会造成失步。每种不同规格的步进电机都有类似的矩频特性曲线,详细图表需要查阅其规格书。
步进电机的磁极数量规格和接线规格很多,为简化问题,我们这里就先只以四相步进电机为例进行讨论。所谓四相,就是说电机内部有4对磁极,此外还有一个公共端(COM)接电源, ABCD是四线的接头。而四相电机的可以向外引出六条接线(两条COM共同接入Vcc),即GND和ABCD,也可以引出五条线,如图所示,所以有成为六线四相制和五线四相制。
下表中1表示高电平、0表示低电平,我们以下述最简单的一相励磁方式来驱动步进电机
这种方式,电机在每个瞬间只有一个线圈导通,消耗电力小但在切换瞬间没有任何的电磁作用转子上,容易造成振动,也容易因为惯性而失步。
二相励磁方式
这种方式输出的转矩较大且振动较少,切换过程中至少有一个线圈通电作用于转子,使得输出的转矩较大,振动较小,也比一相励磁较为平稳,不易失步。
步进角是步进电机每前进一个步序所转过的角度。在不超载也不失步的情况下,给电机加上一个脉冲信号,它就转过一个步距角。这一简单的线性关系,使得步进电机速度和位置的控制变得十分简单。
综合上述两种驱动信号,下面提出一相励磁和二相励磁交替进行的方式,没传送一个励磁信号,步进电机前进半个步距角。其特点是分辨率高,运转更加平滑。
一–二相励磁方式
下面是这三种驱动方式的时序波形图
驱动问题
不要天真的以为可以直接将Arduino的端口和ABCD分别相连,因为Arduino的数字I/O口最大只能通过约40mA的电流。因此,我们想到了使用晶体管进行放大。常用的方法有三种:
1、直接利用晶体管来驱动,这需要你对电机和晶体管的详细参数有一定了解,才能选择恰当的参数去匹配他们。此外,还必须使用二极管来处理当电机内部线圈产生感应电动势逆向流入晶体管而对晶体管造成损害。
2、使用诸如ULN2003和ULN2803这样的激励器,它实际是内部集成好了放大功能的集成电路芯片,此外也无需额外添加二极管,因为它已经内置了。
3、使用光耦,在驱动芯片或者晶体管的前端再加入光耦合器,以加强隔离步进电机的反电动势,以免损害Arduino。
4、使用L293D这样的H桥的方式来驱动步进电机,详细请参考上两节介绍的L293 Motor Sheild官网的说明。
我们以ULN2003为例,现有的驱动板可以用来驱动步进电机,我们只需要选择Arduino的四个输出端口用杜邦线分别连接驱动板的IN1、IN2、IN3、IN4,再用外置电源连接驱动板的5-12V+接口,并把电源和Arduino的地(GND)与驱动板的(–)共线即可。
ULN2003采用的是达林顿管(Darlington transistor)方式来增强对大电流负载(如步进电机)的驱动,所谓达林顿管其实就是二级放大的三极管而已(如右图所示),经过恰当的三极管型号选择匹配后,两次放大的三极管驱动能力比一个三极管更强。详情请参考ULN2003的DataSheet。但无论哪种方式,记住,使用额外的外接电源来驱动晶体管和集成芯片,它才是电机的真正的能量提供者。
关于实际的步距角
前面所讲述的其实是一个简化模型,真正的步进电机步距角比较小。因为采用了图所示的多齿结构,这种结构类似于游标卡尺的工作原理,所以实际4相步进电机的步距角并非360°/8 = 45°。根据其规格书,本节范例所用的步进电机的步距角是5.625°,如果采用一–二相励磁方式,则可以达到其一半的分辨率。
Arduino控制源码:
- /*
- 步进电机速度控制示例
- 本示例程序用于驱动非极性步进电机。
- 电机的接口连接至Arduino的8至11号端口
- 变阻器连接至模拟端口A0
- 电机将沿着顺时针方向旋转,电位器的模拟量越高,步进电机的转速就越快。
- 因为setSpeed()函数将设定每一步序的时间间隔。你可能会发现当电位器模拟量太低时,电机将会停止旋转。
- */
- #include <Stepper.h>
- const int stepsPerRevolution = 200;
- Stepper myStepper(stepsPerRevolution, 8,9,10,11);
- int stepCount = 0;
- void setup()
- {
- }
- void loop()
- {
- int sensorReading = analogRead(A0);
- int motorSpeed = map(sensorReading, 0, 1023, 0, 100);
- if (motorSpeed > 0)
- {
- myStepper.setSpeed(motorSpeed);
- myStepper.step(stepsPerRevolution/100);
- }
- }