使用正交旋转编码器计算RPM

时间:2017-07-28 01:52:47

标签: arduino

我编写了以下代码来计算使用正交旋转编码器和Arduino Mega的直流电机的转速:

int N3 = 7; //N3 sur la L298N motor shield
int N4 = 8; //N4 sur la L298N motor shield
int ENB = 9; //ENB sur la L298N motor shield
int potPin = A0; //analog pin 0 sur la carte arduino
int valeurLu = 0; //valeur lu du potentiomètre
int valeur_a_ecrire = 0; //valeur à envoyer au moteur
int pin_A_encodeur = 3;
int etat_courant_encodeur = 0;
int etat_precedant_encodeur = 0;

void setup() {
  attachInterrupt(digitalPinToInterrupt(3),updatePosition,CHANGE);
  pinMode(N3, OUTPUT);
  pinMode(N4, OUTPUT);
  pinMode(ENB, OUTPUT);
  pinMode(A0, INPUT);
  pinMode(pin_A_encodeur, INPUT);
  Serial.begin(9600);
}

void loop() {
  valeurLu = analogRead(potPin); 
  valeur_a_ecrire = (255.0/1023.0)*valeurLu;
  digitalWrite(N4, HIGH);
  digitalWrite(N3, LOW);
  analogWrite(ENB, valeur_a_ecrire);
  etat_courant_encodeur = digitalRead(pin_A_encodeur);
  Serial.print(valeur_a_ecrire);
  Serial.print(" ");
  Serial.println(etat_courant_encodeur);
}

我能够读取编码器发送的读取信息,但它只是一系列的1和0(11000111 ......等)。如何使用此信息计算电机的转速?我的编码器的分辨率为每转64个计数。在此先感谢有关如何解决此问题的任何帮助。

2 个答案:

答案 0 :(得分:0)

由于您只是从编码器读取A引脚,每回合只能获得16次脉冲。

这是一种方法......

//...
#define SAMPLE_DELAY (1000)   //  this gets 1 reading per second.
                              //  adjust the delay to fit your needs
#edfine PULSES_PER_TURN (32)  //  32 state changes per turn on 1 line, 
unsigned int pulseCount;
bool lastState;
unsigned int lastTime; 
float rpm;                    // speed in turns/minute, doesn't have to be floating point.

void setup()
{
    // ...
    pinMode(pin_A_encodeur, INPUT);
    lastState = digitalRead(pin_A_encodeur);
}

void loop()
{
    bool curState = digitalRead(pin_A_encodeur);
    if (curState != lastState)
    {
        ++pulseCount;
        lastState = curState;
    }

    if ((unsigned int)millis() - lastTime >= SAMPLE_DELAY)  
    {
         rpm = (pulseCount * (60000.f / ((unsigned int)millis() - lastTime))) / PULSES_PER_TURN;
         pulseCount = 0;
         lastTime = (unsigned int)millis();
    }
    //...
}

铸造milis()的返回值是因为我们不需要millis计数器的所有32位计数到1000,节省了2个字节的内存。

通过玩SAMPLE_DELAY,您会注意到读数的稳定性和采样频率之间存在折衷。 SAMPLE_DELAY的最佳值取决于您的应用以及电机的速度范围。

结果取决于主循环中的其他内容。你的循环不应该有任何延迟(),opr你可能会错过一些脉冲。如果无法避免,则应在ISR中移动脉冲检测代码。

另一种方法:

//...
#define SAMPLE_DELAY (1000)   //  this gets 1 reading per second.
                              //  adjust the delay to fit your needs
#define PULSES_PER_TURN (32)  //  32 state changes per turn on 1 line, 
volatile unsigned int pulseCount;  // note the volatile keyword here.
unsigned int lastTime; 
float rpm;                    // speed in turns/minute, doesn't have to be floating point.

void setup() {
    // ...
    pinMode(pin_A_encodeur, INPUT);
    lastState = digitalRead(pin_A_encodeur);
    attachInterrupt(digitalPinToInterrupt(pin_A_encodeur), onPulse, CHANGE);
}

void loop() {
    if ((unsigned int)millis() - lastTime >= SAMPLE_DELAY)  
    {
         unsigned int pulses;
         noInterrupts();
         pulses = pulseCount;  // the atmega is 8 bits, you must disable interruts
         pulseCount = 0;       // to read/write 16 bit values accessed by ISR
         interrupts();         // re-enable interrupts.

                               // 60000 milliseconds in 1 minute for rpms.
         rpm = (pulses * (60000.f / ((unsigned int)millis() - lastTime))) / PULSES_PER_TURN;
         lastTime = (unsigned int)millis();
    }
    //...
}

void onPulse() {
    ++pulseCount;
}

请注意,数字采样总会导致读数发生一些变化。请记住,如果您决定将RPM存储为整数,则必须使用unsigned long type,包括每分钟的毫秒数60000ul,以避免溢出。

另请注意,并非所有引脚都相同。只有一些可以链接到中断的引脚。请参阅arduino doc:https://www.arduino.cc/en/Reference/AttachInterrupt

答案 1 :(得分:0)

感谢更新和澄清,我已经能够在达到稳定状态后获得稳定的电机转速值。考虑到不可能从arduino IDE中将arduino数据写入文件,我决定在MATLAB中对编码器进行编程,将编码器数据写入文件,稍后我将对其进行解码,以确定车轮的RPM。安装在电机上(电机和车轮之间有一个100:1的变速箱)。

命令Arduino板的MATLAB代码:

  clear;
    clc;
    close all;

    a = arduino('COM5','Uno')
    writeDigitalPin(a, 'D7', 1), writeDigitalPin(a, 'D8', 0)
    writePWMDutyCycle(a,'D9',0.7)

    %Create a file for storing simulation data
    fID = fopen('DemoUnoVmatlab2.txt','w');

    startTime = datetime('now');

    try
        while(1)
            t =  datetime('now') - startTime;

            %Write simulation data to file
            fprintf(fID, '%f\t%f\n', second(datenum(t)), double(readDigitalPin(a,'D2')));
        end
        fclose(fID); 
    catch
        fclose(fID);   
    end

用于处理采集数据和计算角位置的MATLAB函数

function [] = counterVmatlab(signal_A,time)
    count = 0;
    previousState = 0;
    angularPos = 0;
    ppr = 32; % encoder resolution, 32 and NOT 64 because only channel A is used here (see encoder's data sheet)
    gearRatio = 100;
    results = fopen('resultsVmatlab.txt','w');

    for i = 1:length(signal_A)
        presentState = signal_A(i);
        if presentState ~= previousState
            count = count + 1;
            previousState = presentState;
            fprintf(results,'%f\t%f\t%f\t%f\n', count, i, angularPos, time(i)); % i ==> at what time did a change in state occur
            angularPos = angularPos + 360/(ppr*gearRatio);
        end   
    end
    fclose(results);

通过绘制角位置和角速度的曲线图,我发现角速度在约5度/秒的稳定值附近变化(参见下面的附加屏幕截图)。考虑到我在12V直流电源上运行一个小电机,我预计角速度大约是360度/秒(这是通过观察电机转动可以观察到的)。所以我想知道我的上述解码算法是否良好?任何帮助将非常感激。

<强> GRAPH Angular position and angular velocity