沉思的牛 发表于 2014-5-11 21:46:45

旋转编码器控制舵机实例

本帖最后由 沉思的牛 于 2014-5-11 22:58 编辑

1.1 先来了解舵机:   在机器人机电控制系统中,舵机控制效果是性能的重要影响因素。舵机可以在微机电系统和航模中作为基本的输出执行机构,其简单的控制和输出使得单片机系统非常容易与之接口。   舵机是一种位置(角度)伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。目前在高档遥控玩具,如航模,包括飞机模型,潜艇模型;遥控机器人中已经使用得比较普遍。舵机是一种俗称,其实是一种伺服马达。   还是看看具体的实物比较过瘾一点:

1.2 其工作原理是:
控制信号由接收机的通道进入信号调制芯片,获得直流偏置电压。它内部有一个基准电路,产生周期为20ms,宽度为1.5ms的基准信号,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。最后,电压差的正负输出到电机驱动芯片决定电机的正反转。当电机转速一定时,通过级联减速齿轮带动电位器旋转,使得电压差为0,电机停止转动。当然我们可以不用去了解它的具体工作原理,知道它的控制原理就够了。就象我们使用晶体管一样,知道可以拿它来做开关管或放大管就行了,至于管内的电子具体怎么流动是可以完全不用去考虑的。

1.3 舵机的控制:
舵机的控制一般需要一个20ms左右的时基脉冲(50HZ),该脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分。以180度角度伺服为例,那么对应的控制关系是这样的:   0.5ms--------------0度;   1.0ms------------45度;   1.5ms------------90度;   2.0ms-----------135度;   2.5ms-----------180度;

知道了舵机,现在我们来了解旋转编码器:      编码器有绝对式编码器和增量式编码器;      使用编码器就不像电位器那样做AD转换了;编码器直接输出脉冲量;
      今天我们用的是增量式;
    旋转编码器可通过旋转可以计数正方向和反方向转动过程中输出脉冲的次数,旋转计数不像电位计,这种转动计数是没有限制的。配合旋转编码器上的按键,可以复位到初始状态,即从0开始计数
2.1工作原理:    增量编码器是一种将旋转位移转换为一连串数字脉冲信号的旋转式传感器。这些脉冲用来控制角位移。在Eltra编码器中角位移的转换采用了光电扫描原理。读数系统以由交替的透光窗口和不透光窗口构成的径向分度盘(码盘)的旋转为依据,同时被一个红外光源垂直照射,光把码盘的图像投射到接收器表面上。接收器覆盖着一层衍射光栅,它具有和码盘相同的窗口宽度。接收器的工作是感受光盘转动所产生的变化,然后将光变化转换成相应的电变化。再使低电平信号上升到较高电平,并产生没有任何干扰的方形脉冲,这就必须用电子电路来处理。读数系统通常采用差分方式,即将两个波形一样但相位差为90°的不同信号进行比较,以便提高输出信号的质量和稳定性。读数是再两个信号的差别基础上形成的,从而消除了干扰。

2.2内部结构,输出波形
增量编码器给出两相方波,它们的相位差90°,通常称为A通道和B通道。其中一个通道给出与转速相关的信息,与此同时,通过两个通道信号进行顺序对比,得到旋转方向的信息。还有一个特殊信号称为Z或零通道,该通道给出编码器的绝对零位,此信号是一个方波与A通道方波的中心线重;


其实数控机床的手轮也是个增量式编码器;



最后说一句:其实增量式编码器很简单,只要区分了A,B两相的先后时间(相位),就能判断旋转方向;如果还不明白,买一个,接到LED上看电平变化,然后拆开看内部结构;
楼主使用的是这款编码器:

好了原理都介绍完了,现在开始控制;今天我们使用AVR单片机来控制(ATMEL16);
需要元件:1. AVR(ATMEL16)单片机最小系统;p.s:楼主现在特别懒,尽是买现成的模块~~~~;2. ATMEL16单片机一片;3. 舵机一个;
4. 编码器一个;
5. 杜邦线若干;
6. 编程器一个;
7. 电脑一台(必须的http://bbs.fishc.com/static/image/smiley/lovely/20080925104601801.gif);




看看楼主连接好的:
一般舵机红色是VCC,黄色是信号线,棕色是GND;
编码器自己最好用LED测试一下,看电平变化,你会更加明白;
使用单片机的PB0.PB1脚输入,OC1B脚输出PWM信号;





这里要说的是,楼主用舵机做了一个云台,上面那个模块是超声波测距的;准备装在小车上;


硬件连接好了,来谈谈程序怎么写:
因为AVR单片机的定时器有PWM功能,所以我们用它来产生频率为50HZ的PWM信号;
用起来比51单片机方便多了;
然后是编码器信号识别,其实和按键识别差不多,主要是区别信号的相位,先后顺序;
楼主使用状态转移法;其实用简单的按键识别程序都行;
置于怎么设置寄存器,楼主就不多说了, data sheets上面说得很清楚;(我传一份中文的);

然后看看楼主的部分代码吧(全部代码见附件,欢迎各位鱼友指正)
main.c
/***************************************
   编码器控制舵机测试程序                  
   PB0.PB1输入,OC1B输出PWM信号   
   鱼C论坛--沉思的牛                        
   http://bbs.fishc.com/                     
****************************************/



#include <iom16v.h>
#include "coder.h"
#include <iom16v.h>
#include "def.h"

//初始化PWM
void PWM_Init()
{
      DDRD|=0X30;    //OC1B输出PWM信号
      
      TCCR1A=0X63;   //TC控制寄存器A设置模式
      TCCR1B=0X1B;   //TC控制寄存器B设置64分频
      
      OCR1A=3749;    //计数上限值到达 电平清零    频率
      OCR1B=195;   //匹配数值到达   电平置为    脉宽
                                 //最高390舵机就达到极限了最好用示波器看看脉宽
      
}

void main()
{
      UCHAR value=0xff;
      
      PWM_Init();
      Encoder_Init();

      while(TRUE)
      {
               
                Get_Value(&value);   //高速扫描没有问题

                if(value==RES_A_ENTERCLOSE)   //A通道
                {
                        if(OCR1B<390)
                                        OCR1B+=3;    //调整脉宽
                }
                if(value==RES_B_ENTERCLOSE)//B通道
                {
                        if(OCR1B>92)
                              OCR1B-=3;      //调整脉宽
                }
               
      }
      
}
coder.c
/***************************************
   编码器控制舵机测试程序                  
   PB0.PB1输入,OC1B输出PWM信号   
   鱼C论坛--沉思的牛                        
   http://bbs.fishc.com/                     
****************************************/
//代码编辑怎么回事啊   缩进老是不对~~~

#include "coder.h"
#include <iom16v.h>
#incldue "def.h"


void Encoder_Init()
{
      DDRB|=0X03;
      PORTB|=0X03;
      
      DDRB&=0XFC;   //设置为输入
      PORTB|=0X03;//带上拉
}


//检查编码器有没有旋转
BOOL Check_Rotate()
{
      if( (PINB&=0X03) != 0X03)
                return TRUE;
      else
                return FALSE;
}

//扫描编码器 和扫描键盘一样
UCHAR Coder_Scan()
{
      UCHAR temp;
      
      temp=PINB;
      temp&=0x03;//使用低两位 PB0 PB1
      
      switch(temp)
      {
                case 0x02: return A_VALUE;   //A相
                case 0x01: return B_VALUE;   //B相
                case 0x00: return MEENWHILE_VALUE; //两相信号同时来的时候
                case 0x03: return NULL_VALUE;      //两相信号都没有
      }
}

//获取通道值
void Get_Value(UCHAR *value)
{
      static UCHAR state=STATE_MOTIONLESS;
      UCHAR temp=0xff;
      
      temp=Coder_Scan();//得到值
      
      switch(state)
      {
                case STATE_MOTIONLESS:
                              {
                                        if(temp==A_VALUE)//A相信号接收到
                                        {
                                                state=STATE_A_ENTERCLOSE;//A信号有效,自然B信号就无效
                                                break;
                                        }
                                        else if(temp==B_VALUE) //B相信号接收到
                                        {
                                                state=STATE_B_ENTERCLOSE;//B信号有效
                                                break;
                                        }
                                        else
                                             break;   
                              }      
                case STATE_A_ENTERCLOSE:
                              {
                                                                        
                                             state=STATE_NULL;//收到A信号后B信号无效
                                             *value=RES_A_ENTERCLOSE;
                                                                        
                                 }
                                 break;
                case STATE_B_ENTERCLOSE:
                                  {
                                             state=STATE_NULL; //收到B信号后后A信号无效
                                             *value=RES_B_ENTERCLOSE;
                                  }
                                  break;
                case STATE_NULL:
                                  {
                                             if(temp==NULL_VALUE)//两相信号都没有的时候
                                              {
                                                      state=STATE_MOTIONLESS;// 回到静止状态
                                                      *value=0xff;             // 输出值也无效
                                                      break;
                                             }
                                                               
                                             else
                                                {
                                                          *value=0xff;    //输出值无效
                                                         break;
                                                 }
                                                               
                                 }
      }
}

视频地址
楼主才搬砖回来,手上有点脏,谅解,谅解~~~
http://v.youku.com/v_show/id_XNzEwNzE0MDMy.html
http://v.youku.com/v_show/id_XNzEwNzA2MzM2.html


源代码~~~~
**** Hidden Message *****

骑着蜗牛狂奔 发表于 2014-5-14 16:32:33

这么好的帖子,怎么没人顶,,,版主你的头像太好玩了。。

zlhforever 发表于 2014-5-14 17:05:33

果断支持:lol:

枫界易城 发表于 2014-5-14 13:02:05

感谢楼主分享,,,,,,,

沉思的牛 发表于 2014-5-14 17:09:14

骑着蜗牛狂奔 发表于 2014-5-14 16:32 static/image/common/back.gif
这么好的帖子,怎么没人顶,,,版主你的头像太好玩了。。

还有更好玩的哦{:7_168:}

沉思的牛 发表于 2014-5-14 19:45:24

枫界易城 发表于 2014-5-14 13:02 static/image/common/back.gif
感谢楼主分享,,,,,,,

谢谢支持

dgkepu 发表于 2014-7-11 14:50:28

果断支持果断支持

灭·世 发表于 2016-8-6 16:40:32

舵机怎么选的,20公斤的有多大力,谢谢

兔子和鸡 发表于 2017-4-22 10:14:07

很棒的内容

旧城堡 发表于 2018-5-17 20:33:07

顶一下

adon001 发表于 2020-9-6 03:09:14

找很久,学习学习

c菜鸟i 发表于 2020-12-7 14:55:00

let me look look

mpghhhhh 发表于 2022-1-8 09:36:24

谢谢

fbhs 发表于 2022-2-9 09:57:47

.

zzzxyx 发表于 2022-2-24 10:03:30

666
页: [1]
查看完整版本: 旋转编码器控制舵机实例