Arduino操作伺服电机/舵机(一)

舵机简单的说就是集成了直流电机、电机控制器和减速器等,并封装在一个便于安装的外壳里的伺服单元。能够利用简单的输入信号比较精确的转动给定角度的电机系统。
舵机安装了一个电位器(或其它角度传感器)检测输出轴转动角度,控制板根据电位器的信息能比较精确的控制和保持输出轴的角度。这样的直流电机控制方式叫闭环控制,所以舵机更准确的说是伺服马达,英文servo。
舵机的主体结构如下图所示,主要有几个部分:外壳、减速齿轮组、电机、电位器、控制电路。工作原理是控制电路接收信号源的控制信号,并驱动电机转动;齿轮组将电机的速度成大倍数缩小,并将电机的输出扭矩放大响应倍数,然后输出;电位器和齿轮组的末级一起转动,测量舵机轴转动角度;电路板检测并根据电位器判断舵机转动角度,然后控制舵机转动到目标角度或保持在目标角度。

图 舵机外观及内部主要结构示意图

舵机是一个微型的伺服控制系统,具体的控制原理可以用下图表示:

图 舵机控制原理示意图

控制电路接收信号源的控制脉冲,并驱动电机转动;齿轮组将电机的速度成大倍数缩小,并将电机的输出扭矩放大响应倍数,然后输出;电位器和齿轮组的末级一起转动,测量舵机轴转动角度;电路板检测并根据电位器判断舵机转动角度,然后控制舵机转动到目标角度或保持在目标角度。
模拟舵机需要一个外部控制器(遥控器的接收机)产生脉宽调制信号来告诉舵机转动角度,脉冲宽度是舵机控制器所需的编码信息。舵机的控制脉冲周期20ms,脉宽从0.5ms-2.5ms,分别对应-90度到+90度的位置。

模拟舵机由于使用模拟器件搭建的控制电路,电路的反馈和位置伺服是基于电位器的比例调节方式。电位器由于线性度的影响,精度的影响,个体差异性的问题,会导致控制匹配不了比例电压,比如我期望得到2.5V的电压位置,但第一次得到的是2.3V,经过1个调节周期后,电位器转过的位置已经是2.6V了,这样控制电路就会给电机一个方向脉冲调节,电机往回转,又转过头,然后有向前调节,以至于出现不停的震荡,这就是我们所看到的抖舵现象。在购买一批舵机中会发现有的很好用,有的在空载的时候也会在抖动,有的是在加一定的负载后就开始抖动。

servoAnimation


舵机除电源外,只要一根信号线即可;使用PPM(脉冲比例调制)信号控制;所谓“PPM”,是一个周期约20ms,其间有个宽度在2ms 左右的脉冲控制信号。一般是以1.5ms 为基准,此时舵机居中,小于1.5ms 舵机左转,大于1.5ms,舵机右转;至于角度和脉冲宽度关系各个产品不同,例如:0.5ms 对应左转90 度,2.5ms 对应右转90 度。
舵机内部实际上是由小电机、减速齿轮、驱动电路、位置反馈、比较电路等组成的闭环控制单元,由于其内置减速齿轮,所以输出力矩较大。最早将其改造为轮式机器人动力源的爱好者估计就看上了这些,他们将舵机的限位去除,位置反馈去除,舵机控制电路因得不到反馈,以为还未转到指定的角度,只好一直驱动电机转动,由于去除了限位,输出轴实现了连续转动,就成了一个减速直流电机。而且,利用其原来的左、右转控制逻辑,实现了正、反转控制。从本质上说,舵机和直流减速电机相同,只是利用了舵机内部的驱动和控制电路,从而简化了控制接口和电路。实际上:改造为连续转动的舵机 = 电调 + 直流减速电机电子调速器的控制信号和舵机一样,只是在小型电机中一般用不着电调,因为其功率不太大,一般在10A 以上,100A 也不足为奇,大材小用了。舵机还有一个优点是安装方便,除自身安装外,安装车轮也很方便,因为舵机一般提供丰富的轴输用舵机作为小车动力,可用MCU 直接驱动,不需要再设计、制作额外的驱动电路,这点对于DIY 者而言还是颇具吸引力的。而且控制所需的MCU 资源也有限,只要一个数字I/O口、一个定时器即可,虽说准确生成2ms 左右的脉冲有些技巧,但总的来说编程相对容易。如果用Arduino 系列控制器,其Servo 函数可让你一条语句“搞定”。
结论:使用舵机作为小车驱动一是为了便于装,二是为了便于控,三是可简化电路。其代价就是“力气”略小,可供选择的规格有限。还有就是略有些不“经济”,因为舵机的“贵贱”主要体现在其控制精度上,改为连续运转的驱动电机后,这部分功能给“废”了,是不是有些可惜?不过也不完全“浪费”,舵机的这部分控制通常采用PID 调节,其比例功能可用于调速,当输入的脉冲偏离中点(1.5ms)越远,其驱动电机的速度越快,而且其调节电路使用了电机反电势作为反馈,原设计大概是为了保证舵机在轻、重负荷下都可以按照相近的速度转到指定的角度,以实现较好的控制特性(比如说60 度/0.22 秒)。改为连续转动后正好作为调速功能,这也是使用舵机的一大优点。

图 连续舵机外观及中位点调节示意图

上图所示是较为常用的一款常用的连续旋转舵机,可以使用螺丝刀调节其中位点,以使其脉冲中点能在1.5ms附近变化。

// Controlling a servo position using a potentiometer (variable resistor) 
// by Michal Rinott <http://people.interaction-ivrea.it/m.rinott> 

#include  
 
Servo myservo;  // create servo object to control a servo 
 
int potpin = 0;  // analog pin used to connect the potentiometer
int val;    // variable to read the value from the analog pin 
 
void setup() 
{ 
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
} 
 
void loop() 
{ 
  val = analogRead(potpin);        // reads the value of the potentiometer (value between 0 and 1023) 
  val = map(val, 0, 1023, 0, 179);   // scale it to use it with the servo (value between 0 and 180) 
  myservo.write(val);             // sets the servo position according to the scaled value 
  delay(15);                    // waits for the servo to get there 
}

// Sweep
// by BARRAGAN <http://barraganstudio.com> 
// This example code is in the public domain.


#include  
 
Servo myservo;  // create servo object to control a servo 
                // a maximum of eight servo objects can be created 
 
int pos = 0;    // variable to store the servo position 
 
void setup() 
{ 
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
} 
 
 
void loop() 
{ 
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees    {                                  // in steps of 1 degree      myservo.write(pos);              // tell servo to go to position in variable 'pos'      delay(15);                       // waits 15ms for the servo to reach the position    }    for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
}
    • 光凭这样的描述是无法确切原因的,有很多可能,有可能是这是个连续旋转舵机,有可能电路板上的芯片坏了,也有可能连接电位器的线断了,或者引线端短接了…