带限制和变速的伺服电机控制


#1

作者:Digi-Key工程师 Robert Fay

增强型扫描 (Sweep)

微控制器伺服电机控制的常见入门工具是“扫描(Sweep)”程序。当您下载Arduino IDE时,会包含“扫描(Sweep)”程序,该程序是Arduino的众多编程示例的一个常见程序。程序代码演示了Arduino伺服库命令的功能,并列出了使伺服臂扫过其整个位置范围所需的步骤。

可借助一些改进措施和外部设备,使扫描(Sweep)伺服在某一设定行程范围内基于某一可变速度移动,从而使其功能变得更加强大。此类伺服适用于许多与爱好相关的项目,例如火车模型上横臂设计或飞机模型上的门及起落架的精确移动。

用于限制移动区域和速度的步骤可通过多种方式实现,具体取决于您的编程和传感器使用的相关经验。本示例将在标准扫描电路中添加三个电位器,并对Arduino代码进行一些改进。演示的核心器件是使用Atmega328P微控制器(uC)的Arduino Mini V5(图1)。但不会使用uC上所有的输入和输出,这样做是为了将来加入其他更多的外围设备。

image

设置

增强型扫描项目从安装试验电路板上的uC开始,其具有所有必需的电源和输入/输出连接,包括三个外部电位器输入、一个ON/OFF触摸开关输入、LED中心位置,以及极限指示器,驱动单个伺服的一个输出。

试验电路板线路配置图见图2。

注意:标准跳线套件(例如Digi-Key原型网页上的标准跳线套件)会根据长度进行颜色编码,因此线路颜色可能会沿着线路走向发生变化。文中所列的示意图显示了单色布线,可以用散装线轴的定制切割线来进行布线。

所需零件:

伺服:180度(1528-1083-ND)、触摸开关(450-1650-ND)、电位器(3310P-125-103L-ND)、Arduino Mini V5(1050-1044-ND)。二极管 - 绿色(160-1130-ND)。二极管 - 红色(160-1127-ND)、电容(445-8571-ND)、用于Arduino编程的USB转串口转换器(1050-1021-ND)。10k电阻(CF18JT10K0CT-ND)。

布局考虑事项

典型伺服的工作电压为4.8 - 6.5VDC,在带载或变方向时可用多达3安培的电流。虽然伺服直接从uC接收信号,但Arduino Mini板载稳压器无法提供伺服所需的电流量级。请注意,伺服电源线直接连接到6V电源。由于uC具有板载稳压器,因此也可以在相同的6V电源上运行。如果伺服和uC在不同的电源上运行,则必须使用相同的地线,以便伺服器识别来自uC的信号。本示例中的触摸开关用于通过uC启动/停止伺服。

图2 - 某些线路路径可能会被夸大以显示连接性

代码(因出于教学目的而添加了大量注释)

> /* The potentiometers are used to provide adjustable input on analog pins A0, A1, A2
> 
> The analog inputs are used for left and right limits in servo travel as well as servo travel speed.
> 
> When deactivated, the servo returns to the nearest limit position at maximum speed.
> 
> An LED on digital pin 8 indicates each crossing of the center servo position. An additional LED on pin 9 indicates the servo has reached the maximum limit. The tactile switch is pulled HIGH via a 10k resistor when not pressed. Pressing the button changes its state to LOW (direct connection to ground). */
> 
> /********************************* Configuration ************************************/
> 
> #include <Servo.h> //Include the Arduino servo library
> 
> Servo wiperServo; //Create a name for the servo
> 
> /*************************** Declare and initialize variables ****************************/
> 
> int pos; int lastPos; //Current position variables
> 
> int leftServo; int rightServo; //Desired position variables
> 
> int servoSpeed = 2; //Set the initial servo speed
> 
> /* startAngle variable - The left and right limit potentiometers can be adjusted to add/subtract an adjustable number of degrees between zero and 90 minus the startAngle variable. With the startAngle variable set at 70, the servo will travel 70 degrees from center before the pot adjustment has any effect. If the pot is set to zero, the servo will travel 70 degrees and then reverse direction. If the pot is set to its maximum, the servo will travel the full 90 degrees and then reverse direction. If the startAngle variable is set to 50 as another example, the limit potentiometers will have 40 degrees of adjustment. The higher the variable value, the finer the potentiometer control. */
> 
> int startAngle = 70;
> 
> //Designate pin 2 as an interrupt for ON/OFF control using the tactile switch
> 
> const byte interruptPin = 2;
> 
> //Set the system status flag. Also used for ON/OFF control
> 
> volatile byte flag = LOW;
> 
> /**************************** Setup area - Runs one time ****************************/
> 
> void setup() {
> 
> pinMode(8, OUTPUT); pinMode(9, OUTPUT); //Set digital pins 8 and 9 as outputs
> 
> //Flash red LED 3 times to indicate uC has entered setup mode.
> 
> for (int flash = 0; flash < 3; flash++)
> 
> {
> 
> digitalWrite(8, HIGH); delay(200); digitalWrite(8, LOW); delay(200);
> 
> }
> 
> //Identify the servo signal digital pin
> 
> wiperServo.attach(10);
> 
> //Identify interrupt pin, function to run on interrupt, HIGH to LOW detection
> 
> attachInterrupt(digitalPinToInterrupt(interruptPin), onOff, FALLING);
> 
> //Read the analog level of uC pin A0, convert the 12bit value to a degree range (0-90),
> 
> rightServo = map(analogRead(A0), 0, 1023, 90, 0);
> 
> //Confine the range of servo travel to the right of center to 0-90.
> 
> rightServo = constrain(rightServo, 0, 90);
> 
> //Run the servo. The delay gives the servo time to reach its destination.
> 
> wiperServo.write(rightServo); delay(2000);
> 
> //Read the analog level of uC pin A1, convert the 12bit value to a degree range (90-180),
> 
> leftServo = map(analogRead(A1), 0, 1023, 90, 180);
> 
> //Confine the range of servo travel to the left of center to 90-180.
> 
> leftServo = constrain(leftServo, 90, 180);
> 
> //Run the servo. The delay gives the servo time to reach its destination.
> 
> wiperServo.write(leftServo); delay(2000);
> 
> //Flash an LED 3 times to indicate end of servo test.
> 
> for (int flash = 0; flash < 3; flash++)
> 
> {
> 
> digitalWrite(8, HIGH); delay(200); digitalWrite(8, LOW); delay(200);
> 
> }
> 
> //Remember the servo position as a starting point in the main program
> 
> lastPos = leftServo;
> 
> } //End of setup
> 
> /************************* Main program - Loops continuously ***********************/
> 
> void loop() {
> 
> //Get 12bit value from speed potentiometer and map to desired output range
> 
> //Lower values of this variable cause higher servo speeds.
> 
> servoSpeed = map(analogRead(A2), 0, 1023, 0, 20);
> 
> //Set range limits for speed - less than 2 will cause servo chatter
> 
> servoSpeed = constrain(servoSpeed, 2, 18);
> 
> //Turn on the green LED if leftServo value is at the center position.
> 
> if (analogRead(A1) < 25)
> 
> {
> 
> leftServo = 90;
> 
> digitalWrite(8, HIGH);
> 
>   delay(25);
> 
> }
> 
> else
> 
>   {
> 
> leftServo = map(analogRead(A1), 26, 1023, 90 + startAngle, 180);
> 
> leftServo = constrain(leftServo, 90, 180);
> 
> //Turn on the Red LED if leftServo is approaching the maximum position.
> 
> if (leftServo >= 175) digitalWrite(9, HIGH);
> 
> else digitalWrite(9, LOW);
> 
>   }
> 
>   //Turn on the green LED if rightServo value is at the center position.
> 
>  if (analogRead(A0) < 25)
> 
>   {
> 
> rightServo = 90;
> 
> digitalWrite(8, HIGH);
> 
> delay(25);
> 
> }
> 
> else
> 
>   {
> 
> rightServo = map(analogRead(A0), 26, 1023, 90 - startAngle, 0);
> 
> rightServo = constrain(rightServo, 0, 90);
> 
> //Turn on the Red LED if rightServo is approaching the maximum position.
> 
> if (rightServo <= 5) digitalWrite(9, HIGH);
> 
> else digitalWrite(9, LOW);
> 
> }
> 
> //The following responds to a tactile button press (High to Low transition).
> 
> if (flag)
> 
> {
> 
> if (lastPos > rightServo)
> 
> //Continuous motion loop - from right limit to left limit
> 
> for (pos = lastPos; pos >= rightServo; pos--)
> 
> {
> 
> //If the flag changes state (turned OFF), jump out of the loop
> 
> if (!flag) break;
> 
> //Run the servo motion and LED indication function
> 
>  runServo(servoSpeed);
> 
>   }
> 
> if (lastPos < leftServo)
> 
>  
> 
> //Continuous motion loop - from left limit to right limit
> 
> for (pos = lastPos; pos <= leftServo; pos++)
> 
> {
> 
>  if (!flag) break;
> 
> runServo(servoSpeed);  
> 
>    }
> 
> }
> 
> //If the flag, set by the switch, is false (OFF) enter this loop
> 
> if (!flag)
> 
> {
> 
> if (lastPos >= 90)
> 
> {
> 
> //If the last position is left of center, go to the left limit
> 
> wiperServo.write(leftServo);
> 
> //Remember the current position.
> 
> pos = leftServo;
> 
>   }
> 
>  else
> 
>   {
> 
> //If the last position is right of center, go to the right limit
> 
> wiperServo.write(rightServo);
> 
> //Remember the current position
> 
>   pos = rightServo;
> 
> }
> 
>  }
> 
> } //End of main program

自定义函数

> /*********************************** Custom Functions ******************************/
> 
> //The following function runs when a falling edge (HIGH to LOW) interrupt occurs (switch press is detected)
> 
> void onOff()
> 
> {
> 
> //Toggle the flag status
> 
> flag = !flag;
> 
> }
> 
> //The following function runs when servo motion is required
> 
> void runServo(int rate)
> 
> {
> 
> //Send the position value to the servo
> 
> wiperServo.write(pos);
> 
> //Remember the last position for determining which way to go home when flag is false
> 
> lastPos = pos;
> 
> //Turn ON the green LED when at center
> 
> if (pos == 90) digitalWrite(8, HIGH);
> 
> //Turn OFF the servo center indicator when at any angle other then 90
> 
>   if (pos != 90) digitalWrite(8, LOW);
> 
> //Stop for a moment when the servo arm reaches a limit before reversing direction.
> 
>   if (pos == rightServo) delay(100);
> 
> if (pos == leftServo) delay(100);
> 
> //Set the speed by delaying the time between position values
> 
> delay(rate);
> 
> }
> 
>  //End of custom functions

总结

伺服电机是功能强大且易于使用的设备,可将信号转换为移动。上面的代码演示了如何操作两个简单的变量rightServo和leftServo,以使伺服电机执行许多操作,而不仅仅是简单的扫描(Sweep)功能。可使用这两个相同的变量轻松修改代码,以执行更多可能的操作。触摸开关可以替换为可在由高至低的信号配置下工作的任何触发设备(例如磁性开关或具有逻辑输出的其他传感器)。应用范围可在很多场合实现。

英文原文链接:Servomotor control with limits and variable speed