旋转编码器如何运作以及如何与Arduino使用它

在本教程中,我们将学习如何旋转编码器的工作,以及如何使用它与Arduino。你可以观看下面的视频或阅读下面的书面教程。

概述


旋转编码器是一种位置传感器,用于确定旋转轴的角位置。它根据旋转运动产生模拟或数字电信号。

旋转编码器模块

有许多不同类型的旋转编码器,由输出信号或传感技术进行分类。我们将在本教程中使用的特定旋转编码器是增量旋转编码器,它是最简单的定位传感器来测量旋转。

旋转编码器分类

该旋转编码器也称为正交编码器或相对旋转编码器,其输出是一系列方波脉冲。

旋转编码器如何工作


让我们仔细看看编码器,看看其工作原理。以下是如何产生方波脉冲的:编码器具有圆盘,该盘具有均匀间隔的接触区域,该磁盘连接到公共引脚C和另外两个单独的触头A和B,如下所示。

旋转编码器如何运作工作原理betway

当磁盘将开始逐步旋转时,引脚A和B将开始与公共引脚进行接触,并且将相应地产生两个方波输出信号。

如果我们只计算信号的脉冲,则可以使用两个输出中的任何一个来确定旋转位置。但是,如果我们也希望确定旋转方向,我们需要同时考虑两个信号。

我们可以注意到,两个输出信号彼此相位以90度以90度移位。如果编码器顺时针旋转,则输出A将在输出B之前。

旋转编码器输出信号工作原理照片

因此,如果每次信号发生变化,从高到低电平或低到高电平,我们都可以注意到,此时两个输出信号具有相反的值。反之亦然,如果编码器逆时针旋转,则输出信号具有相等的值。因此,考虑到这一点,我们可以轻松地编程我们的控制器来读取编码器位置和旋转方向。

旋转编码器Arduino示例


让我们用Arduino来做一个实际的例子。我将在这个例子中使用的特定模块来自一个突破板,它有5个引脚。第一个引脚是输出A,第二个引脚是输出B,第三个引脚是按钮引脚,当然其他两个引脚是VCC和GND引脚。

旋转编码器Arduino教程示例

我们可以将输出引脚连接到Arduino板的任何数字引脚。

您可以从下面的链接获取此Arduino教程所需的组件:

必威外围提钱披露:这些是联盟链接。作为亚马逊助理,我从合格购买中获得。

源代码


这是Arduino代码:

/ * Arduino旋转编码器教程* *由Dejan Nedelkovski,www.www.mfxpo.combet188官方网站 * * / #define Outputa 6 #define输出组7 int计数器= 0;陷入诗歌;int alaStstate;void setup(){pinmode(outputa,输入);Pinmode(输出,输入);Serial.Begin(9600);//读取outputa AlastState = DigitalRead(Outputa)的初始状态;void循环(){Astate = DigitalRead(Outputa);//读取Outputa //如果outputa的当前状态不同的“当前”状态不同,这意味着发生脉冲(Astate!= AlastState){//如果输出态不同于outputa状态,这意味着编码器是顺时针旋转(DigitalRead(OutputB)!= Astate){Counter ++; } else { counter --; } Serial.print("Position: "); Serial.println(counter); } aLastState = aState; // Updates the previous state of the outputA with the current state }

代码描述:因此,首先我们需要定义编码器连接到的引脚,并定义程序所需的一些变量。在设置部分,我们需要将两个引脚定义为输入,启动串口通信在串行监视器上打印结果,以及读取输出A的初始值,并将值放入变量aLastState。

然后在循环部分中,我们再次读取输出,但现在我们将值放入Astate变量。因此,如果我们旋转编码器并生成脉冲,这两个值会有所不同,并且第一个“如果”语句将变为真实。在之后,使用第二个“if”语句,我们确定旋转方向。如果输出B状态与输出不同的状态,则计数器将增加一个状态,否则它将减小。最后,在串行监视器上打印结果后,我们需要使用Astate变量更新AlaStState变量。

这就是这个例子所需要的。如果上传代码,启动串行监视器并开始旋转编码器,我们将开始在串行监视器中获得值。我有一个特定的模块,在每个完整的循环中进行30次计数。

例2 -使用旋转编码器控制步进电机


除了这个基本的例子之外,我还制作了一个控制a的例子步进电机使用旋转编码器的位置。

使用旋转编码器控制步进电机

以下是此示例的源代码:

/ *步进电机使用旋转编码器* * by dejan nedelkovski,www.www.mfxpo.com * * /bet188官方网站 #include  //包括液体库液晶LCD(1,2,4,5,6,7);//创建一个LC对象。参数:( RS,ENABLE,D4,D5,D6,D7)//定义引脚编号#define STEPPIN 8 #DEFINE DIRPIN 9 #DEFINE OUTPORA 10 #DEFINE OUTPORB 11 INT计数器= 0;int角度= 0;陷入诗歌;int alaStstate;void setup(){//将两个引脚设置为输出pinmode(steppin,输出);Pinmode(Dirpin,输出);Pinmode(outputa,输入);Pinmode(输出,输入); aLastState = digitalRead(outputA); lcd.begin(16,2); // Initializes the interface to the LCD screen, and specifies the dimensions (width and height) of the display } } void loop() { aState = digitalRead(outputA); if (aState != aLastState){ if (digitalRead(outputB) != aState) { counter ++; angle ++; rotateCW(); } else { counter--; angle --; rotateCCW(); } if (counter >=30 ) { counter =0; } lcd.clear(); lcd.print("Position: "); lcd.print(int(angle*(-1.8))); lcd.print("deg"); lcd.setCursor(0,0); } aLastState = aState; } void rotateCW() { digitalWrite(dirPin,LOW); digitalWrite(stepPin,HIGH); delayMicroseconds(2000); digitalWrite(stepPin,LOW); delayMicroseconds(2000); } void rotateCCW() { digitalWrite(dirPin,HIGH); digitalWrite(stepPin,HIGH); delayMicroseconds(2000); digitalWrite(stepPin,LOW); delayMicroseconds(2000); }

欢迎在评论区提出任何问题。

49回复

  1. 凯夫拉尔

    我不确定为什么你将角度乘以1.8,如果完全旋转由200步组成(200 * 1.8 = 360度)?然而,您提到完全旋转是在计数器变量中看到的30个步骤引起的。似乎系统显然能够检测小于12度的变化(30 * 12 = 360度)。

    例如,一个完全旋转会使计数器从0到30带,角度为0到30,这将与0到54度(30 * 1.8)相关。我想知道我在这里出了问题。

    任何澄清都会很棒!

    回复
    • Dejan Nedelkovski.

      是的,你是对的。
      旋转编码器全周期是30个步骤。因此,如果一个旋转编码器步骤是一个步进电机步骤,则需要200步进行全电机旋转,或者为1个电机循环的200/30 = 6.666编码器周期。据此,您可以编制Arduino来做任何您想要的事情。
      例如,如果您希望将1个编码器周期为1个电机循环,则需要将Arduino程序编程到步进电机的步进步骤,为编码器等步骤等......

      回复
  2. 萨姆

    所以在你的榜样时,当你移动编码器一个位置这意味着你的步进电机已经移动了1.8degrees(1步)

    如果是的话,屏幕如何以1度增量显示
    例如,如果您将编码器从初始零位置移动1周期移动
    代码是否应该打印一个1.8度的角度到液晶?
    我非常不确定你使用过的数学数学
    和一个真正的初学者在这里

    回复
    • Dejan Nedelkovski.

      这是真的,每个编码器位置步进移动1步或1.8度。在LCD上打印度时,我使用步骤* 1.8表达式来获得度。例如,50个编码器位置,或50步* 1.8 = 90度。

      回复
  3. Jameel.

    做得好的朋友!我理解得很好。我在机器elecronic遇到了问题。你能解决它吗?
    我在有HVDC站的电网工作。在这个HVDC站中,有晶闸管(SCR)阀门银行。在AC到DC转换期间,这种晶闸管得到加热。Hechm,冷却系统包含用于冷却该晶闸管。冷却系统由4个NOS冷却器组成,顶部和轴向风扇电机在底部的水喷嘴,用于强制空气冷却。
    这四个冷却器处于自动模式。也就是说,当温度高时,冷却器开始一个接一个地使用,以将温度保持在预设的限制和反之亦然。
    当温度开始掉落时,通过向PLC发送命令,冷却器开始逐个(首先在第一输出偏差中)。
    每个冷却器都装有阻尼器。这个阻尼器有三个位置:开、中、关。假设这4台冷却器现在在使用中。因此,当温度开始下降(由于环境温度或功率降低),PLC发送命令给冷却器关闭其风门从全开到半开。假设PLC向1号冷却器发送命令,将其阻尼器关闭到一半。所以冷却器1号阻尼器电机转动阻尼器轴,当阻尼器达到半闭位置时,由PLC(微动开关)感知位置,PLC发送停止命令给阻尼器电机。然后PLC将监测温度。如果温度进一步降低,PLC发送命令给冷却器1号风门电机,完全关闭风门,关闭喷淋泵和风机电机。阐述了高压直流冷却系统的主要原理。
    现在我们正面临一个问题,即冷却器无法在自动模式下操作,因为阻尼雕像不发送到PLC。我们也没有得到OEM的支持。
    我有一个想法将编码器安装到阻尼电机轴上。请注意,阻尼电机是慢动移动的电机。它需要大约3分钟移动阻尼器从完全打开到完全关闭位置(和vISE VERSA)。编码器(与奥迪诺这样的一些微处理器合作)将感知阻尼器的位置,并发送三个位置命令(打开,中间和关闭)到PLC。
    我想通过使用光学编码器和奥多诺UNO / MEGA来制作照片类型并测试它。你能帮助我编程和什么类型的编码器我会选择。

    问候
    Jameel,印度

    回复
    • Dejan Nedelkovski.

      谢谢Jameel!你的想法听起来不错,有可能完成。然而,我很抱歉,但我无法帮助您,因为我不在自定义项目上工作。bet188me

      回复
  4. 约翰

    我是一名退休的ee和业余无线电运营商(致电N1abe),具有很多经验;然而,作为该体验的一部分,我学会了不重新发明轮子。也就是说,我使用我通过亚马逊的DDS9850模块设计了基于Arduino的直接数字合成器。到目前为止,我已经能够通过串口使用它。我最初将使用编译的机器人基本应用程序,但我想要一个独立的设置,最好是电池供电。想到的是使用旋转编码器来设置频率。不幸的是,Arduino网站上没有太多关于他们的网站,所以我做了一个搜索并找到了你的网站。瞧!你有我正在寻找的代码,谢谢。其他地方使用中断的其他代码示例,但我想要一个没有使用它们的版本。 I copied yours and it compiled without error. When I get the encoders I will integrate your code into mine. I am probably going to use a separate push button since the encoders I am getting do not have an integral switch. I am not at this time looking to add a display because my goal is to keep the project simple and will set the frequency with an o’scope. The DDS produces a stable signal up to 40 MHz and will make a nice lab bench signal source to compliment my home brew function generator. Once my project is done I will be more than happy to share my design with you and intend to attribute you in my final code.

    回复
  5. 丹尼尔

    嗨,谢谢你的伟大工作......我现在正在使用带有LCD显示的旋转编码器的项目。我有你的例子2的完整原理图吗?谢谢

    回复
    • Dejan Nedelkovski.

      谢谢!嗯,我没有特定的电路原理图,但我对每个组件具有单独的电路原理图,旋转编码器,液晶显示屏和伺服电机控制,因此您不应有任何问题。

      回复
      • 丹尼尔

        我使用的是28BYJ-48步进电机,和你在视频中使用的不一样。28BYJ步进电机通过4个数字引脚连接Arduino,但你使用的那个只通过2个引脚连接(stepin和dirPin)。28BYJ-48电机的代码你有什么想法吗?

  6. 莱姆斯因力

    嗨Dejan,
    我有兴趣和想要建立这个项目,可以让我借给我使用的原理图和模块的细节。

    谢谢。
    问候,
    莱姆。

    回复
    • Dejan Nedelkovski.

      好吧,一切都已经解释道。您可以弄清楚如何从本教程中连接编码器,如果您检查步进电机控制教程,可以找到用于连接驱动器和步进电机的电路原理图。

      回复
  7. ANA.

    这就跟你问声好!
    伟大的项目,真的很喜欢它!
    我只是想知道你用来找出轴角的等式。它是基于ppr吗?我正在使用1024ppr增量编码器,我没有能够找到轴角和计数器值的匹配。
    谢谢!

    回复
  8. ·埃

    嗨Dejan,

    谢谢你的伟大教程。也适用于I2C和SPI。

    但我有一个关于编码器阅读的问题。

    我的增量总是2而不是1.我也添加了几个电路来消除去抖动但没有办法。

    请你帮助我好吗 ?

    回复
      • 迈克C.

        德国使用的编码器可能只有2个纠葛只有2个过渡,而你的有4个?当你转动旋钮时,2个引脚状态GO 00,10,11,01等。一些编码器当引脚在11时具有棘爪,当时00 00,有些则在00和11处具有棘爪,我怀疑德国有其中一个。

        因此,德国的开始,也许在00和一个制动(2个过渡,即10,11)中将他带到11点,所以在他的节目中是一个“步骤”。在下次点击左右,它再次转换为01然后再次00,再次进行另一个“步骤”。您的代码将从00开始从00开始,即10,11,01,00,所以他的代码在编码器上计算2个“步骤”,只有一个在他身上。你的棘手队可能会设置为11,而不是00,但原则是一样的。您将不得不攻击此代码一点以使其忽略额外的步骤。

  9. 阿德里安

    伟大的项目几乎我需要的东西。Arduino会记住电机的最后位置是否有可能(比如68度)?因此,如果Arduino断电然后重新启动它将“记住”位置并在LCD上读取68?

    回复
  10. arduinomaster.

    我认为没有人可以这样做,因为纽扣队不无所事事。当我们都使用不同的步进驱动器时需要电路图。最好的是在赋予指导方面写下这一点。
    起初,我很兴奋,但现在在4个小时后我只有头疼。

    回复
    • Dejan Nedelkovski.

      我没有在示例中使用该按钮,但它只是简单的按钮。至于步进示例,您可以在“A4988驱动程序”的详细教程上找到电路原理图。

      回复
  11. JukkaKilpiö.

    嗨Dejan!

    我认为第一个代码示例中有一个小错误。IF语句“如果(agraite!= AlastState)”捕获编码器时钟脉冲的上升和下降沿。这意味着一个旋转编码器步骤将使两个变化进行计数器。如果您替换“如果(agrate!= alaStstate)”,“如果(agrate> alaStstate)”只计算上升沿。

    回复
    • JAROMIR OBR.

      我同意。如果使用条件(Astate!= AlastState),则对轴的一步进行反击两次。如果你使用(Astate> AlastState)或(Astate 1 - > 0 - > 1。

      回复
  12. 胡安aguire

    大家好,我来自阿根廷。伟大的项目!
    我跟随你的步骤,它有效。虽然我有问题!如果我变得慢,它会很好地柜台,但如果我快速转弯,它是不受控制的并且重复步骤!
    我使用了来自EPSON打印机的编码器。
    这可能是因为编码器有很多分辨率吗?
    是否能够使用此编码器?
    从已经非常感谢你!
    来自阿根廷的问候!!

    回复
    • Dejan Nedelkovski.

      问题可能在代码中,这不是一个很多优化的代码。这只是一个简单的示例代码,它可能需要如此优化,以实现更好的结果。

      回复
  13. Max -Joseph.

    这种方法只使用了旋转编码器分辨率的一半,因为每个周期有4种不同的状态(11,10,00,01),但你只检查每个方向上的两个变化(如果从10到00或从01到11 - > counter++;如果从11到01或从00到10 - >计数器-)。所以你错过了11到10,00到01,01到00和10到11。

    要获得完整的解决方案,您必须检查每个更改。示例代码(不易阅读但高效且紧凑):
    ##############################################
    int计数器;
    bool preva = 1,prevb = 1;

    无效循环(){
    BOOL A = DigitalRead(PIN_rotarya),B = DigitalRead(Pin_rotaryB);

    if(b!= prevb)计数器+ =(b-prevb)*(a?+1:-1);
    否则if(a!= preva)计数器+ =(a-preva)*(b?-1:+1);
    否则退货;//没有任何改变:退出

    preva = a;
    prevb = b;
    serial.println(计数器);
    }
    ##############################################

    如果以正常速度转动旋转编码器,则此操作非常棒。But if you turn it too fast or you do other stuff in your loop() that takes longer time you will miss a step and it will jump from 11 to 00 or from 10 to 01. Then it can’t know, which direction it was turned, so the counter will get slightly incorrect.

    但你可以通过检查它之前转向的方向,并相应地增加或减少计数器2。
    或者您可以使用中断而不是检查循环中的引脚状态()中的状态,以确保未错过步骤。

    回复
      • lluch joan.

        更清晰,更有效的方法来获得编码器的全部分辨率。它更有效,因为它不使用任何整数算术,并且利用了逻辑运算符的“短路”评估规则,在大多数情况下,它将跳过大部分代码。

        ##############################################
        int计数器= 0;
        字节Encoderst = 0;
        void循环()
        {
        字节A = DigitalRead(Pina);
        字节B = DigitalRead(PINB);
        字节st = (b<<1) | a;
        如果(Encoderst!= St)
        {
        if((encoderst == 0 && st == 2)||(encoderst == 1 && st == 0)||(Encoderst == 2 && st == 3)||(Encoderst == 3 && st ==1))计数器;
        else counter ++;
        serial.println(计数器);
        Encoderst = St;
        }
        }
        ##############################################

    • Yvan Beguin

      使用仅二进制运算符更有效的代码

      ##############################################
      int计数器;
      bool preva = 1,prevb = 1;

      无效循环(){
      BOOL A = DigitalRead(PIN_rotarya),B = DigitalRead(Pin_rotaryB);

      计数器+ =(a ^ preva)|(b ^ prevb)?a ^ prevb?1:-1:0;

      preva = a;
      prevb = b;
      serial.println(计数器);
      }
      ##############################################

      回复
  14. 尼古拉

    您的教程对我非常有帮助。
    但我需要一个非常精确的电机,每步获得1度。你有什么想法如何实现它吗?所以我将使用减少来实现这一目标吗?
    大regads.
    Поздрав

    回复
  15. 乔Apache

    嗨Dejan,
    谢谢你这么伟大的教程。感谢您分享您的知识并制作更好的世界。
    Eng Apache.

    回复
  16. andreas kraus.

    谢谢你,先生。你帮了我很多。现在,1990年的Fanuc A860-0201-T001编码器正在与我的项目合作。

    回复
  17. 约翰尼尔森

    你好伟大的教程,,,,我不确定所有的组件显示。有Arduino,屏幕,转盘,面包板,跳线,还有一个小玩意,看起来像散热器,,,,我不知道那是什么,还有接线图在哪里?
    非常感谢卓越的解释。竖起大拇指和订阅。
    干杯
    约翰尼尔森

    回复
    • Dejan Nedelkovski.

      大家好,谢谢!你正在谈论的项目是在第二个例子中,那就是A4988步进电机驱动器。关于它和如何使用Arduino控制步进电机的更多细节,你可以查看我的特殊教程。干杯!

      回复
  18. lluch joan.

    示例中的编码器代码只占编码器节拍的一半。这是因为计数器仅在A引脚更改后才更新,而不是当两个引脚中的任何一个更改时。对于您的应用程序来说,它可能还可以,但如果您想利用编码器的全分辨率,就不能认为它是好的。

    回复
  19. Naresh.

    每次编码器通电时,它开始从零计数,无论轴都是轴的或其先前的位置。

    我们如何在电力的功率后从先前的编码位置开始。

    回复
    • 德国

      嗯,编码器本身没有这样的能力。但是,您可以通过将其存储在Arduino的EEPROM中的位置来实现这一点。因此,下次电源您将从EEPROM读取存储的值并继续。

      回复
      • Naresh.

        嗨Dejan,

        首先,感谢你的回复。
        我是新手,使用Arduino Atmega 2560。
        我已将连接的编码器5540连接到直流电机轴,增量编码器有两个通道连接到I / O引脚。编码器读取电机位置,(编码器反馈)如何存储在EEPROM中。

        请给我任何示例代码或例程。

        问候,
        naresh。
        祝你今天过得愉快 ..!!

    • William P O'Sullivan.

      我可能发生这种情况的唯一方法是如果某种方式将编码器移动回0,那么从最后的存储位置就是。您可以使用伺服或其他电机。这将需要很多工作。我想你想要“旋钮”点击电源上电?
      / s / wp

      回复
  20. 奈杰尔

    一切都很简单,和谐......直到我去了并添加了一个I2C OLED显示屏。我预计刷新屏幕的延迟是终端干扰脉冲检测的定时。

    我正在使用一个Arduino nano和一个盾牌,意味着我只有引脚9,10和11可用,所以我不能附加ISR ......我所希望使用旋转编码器......对我来说是划船按钮

    谢谢你的一个非常好的,简单的教程

    回复
  21. 奥斯卡

    大家好!
    即时建立一个测量工具来测量不同对象的长度。我可以用这种技术来衡量长度吗?

    另外,那是什么屏幕?我喜欢它,我在哪里可以找到类似的?

    回复

发表评论

您的电子邮件地址将不会被公布。

受到推崇的

2019年初学者和爱好者的最佳进入级示波器

为初学者和爱好者最好的示波器

受到推崇的

2019年初学者的8个最佳Arduino Starter Kits

初学者的8个最佳Arduino Starter Kits

受到推崇的

用于初学者和爱好者的最佳3D打印机 -  3D打印

初学者和爱好者的最佳3D打印机