2017-09-12 160 views
1

我試圖控制使用Arduino Uno和連接到電機的編碼器的兩個直流電機的速度。使用編碼器控制直流電機

我已經寫了一個代碼來檢查編碼器的位置是否有變化,並據此計算電機的速度。

伊夫使用this website的代碼:計算所述編碼器的新的位置和編碼器的舊位置之間的差時碰到的問題

。由於某些原因,即使速度保持不變,差異也會持續上升。

這是我到目前爲止的代碼:

#define pwmLeft 10 
#define pwmRight 5 
#define in1 9 
#define in2 8 
#define in3 7 
#define in4 6 

//MOTOR A 
int motorSpeedA = 100; 
static int pinA = 2; // Our first hardware interrupt pin is digital pin 2 
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3 
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent 
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set) 
volatile long encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255 
volatile long oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor) 
volatile long reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent 

//MOTOR B 
static int pinC = 12; // Our first hardware interrupt pin is digital pin 2 
static int pinD = 33; // Our second hardware interrupt pin is digital pin 3 
volatile byte cFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent 
volatile byte dFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set) 
volatile long encoderPosB = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255 
volatile long oldEncPosB = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor) 
volatile long readingB = 0; 

int tempPos; 
long vel; 
unsigned long newtime; 
unsigned long oldtime = 0; 

void setup() { 
    //MOTOR A 
    pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases) 
    pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases) 
    attachInterrupt(0, PinA, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below) 
    attachInterrupt(1, PinB, RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below) 
    //MOTOR B 
    pinMode(pinC, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases) 
    pinMode(pinD, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases) 
    attachInterrupt(0, PinC, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below) 
    attachInterrupt(1, PinD, RISING); 
    Serial.begin(9600); // start the serial monitor link 
    pinMode (in1, OUTPUT); 
    pinMode (in2, OUTPUT); 
    pinMode (in3, OUTPUT); 
    pinMode (in4, OUTPUT); 
    digitalWrite (8, HIGH); 
    digitalWrite (9, LOW); //LOW 
    digitalWrite (7, LOW); //LOW 
    digitalWrite (6, HIGH); 
    pinMode (pwmLeft, OUTPUT); 
    pinMode (pwmRight, OUTPUT); 
} 

void PinA(){ 
    cli(); //stop interrupts happening before we read pin values 
    reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values 
    if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge 
    encoderPos --; //decrement the encoder's position count 
    bFlag = 0; //reset flags for the next turn 
    aFlag = 0; //reset flags for the next turn 
    } else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation 
    sei(); //restart interrupts 
} 

void PinB(){ 
    cli(); //stop interrupts happening before we read pin values 
    reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values 
    if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge 
    encoderPos ++; //increment the encoder's position count 
    bFlag = 0; //reset flags for the next turn 
    aFlag = 0; //reset flags for the next turn 
    } else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation 
    sei(); //restart interrupts 
} 

void PinC(){ 
    cli(); //stop interrupts happening before we read pin values 
    readingB = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values 
    if(readingB == B00001100 && cFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge 
    encoderPosB --; //decrement the encoder's position count 
    dFlag = 0; //reset flags for the next turn 
    cFlag = 0; //reset flags for the next turn 
    } else if (readingB == B00000100) dFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation 
    sei(); //restart interrupts 
} 

void PinD(){ 
    cli(); //stop interrupts happening before we read pin values 
    readingB = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values 
    if (readingB == B00001100 && dFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge 
    encoderPosB ++; //increment the encoder's position count 
    dFlag = 0; //reset flags for the next turn 
    cFlag = 0; //reset flags for the next turn 
    } else if (readingB == B00001000) cFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation 
    sei(); //restart interrupts 
} 

void loop(){ 
    analogWrite(pwmLeft, motorSpeedA); 
    analogWrite(pwmRight, motorSpeedA); 
    if(oldEncPos != encoderPos) { 
    newtime = millis(); 
    tempPos = encoderPos - oldEncPos; 
    vel = tempPos/(newtime - oldtime); 
    Serial.println(tempPos); 
    oldEncPos = encoderPos; 
    oldtime = newtime; 
    delay(250); 
    } 
    if(oldEncPosB != encoderPosB) { 
    Serial.println(encoderPosB); 
    oldEncPosB = encoderPosB; 
    }  
} 

兩個if語句只是做檢查編碼器是否工作正常。在第一條if語句中,我試圖對速度進行計算。

我會很感激任何反饋意見。

謝謝。

編輯:

我發現那裏有一個編碼器庫,這使得一切都輕鬆了許多。

所以現在我的代碼看起來是這樣的:

#include <Encoder.h> 
#define pwmLeft 10 
#define pwmRight 5 
Encoder myEncA(3, 2); 
Encoder myEncB(13, 12); 
unsigned long oldtimeA = 0; 
unsigned long oldtimeB = 0; 
int speedA = 100; 
int speedB = 130; 

void setup() { 
    Serial.begin(9600); 

    digitalWrite (8, HIGH); 
    digitalWrite (9, LOW); //LOW 
    digitalWrite (7, LOW); //LOW 
    digitalWrite (6, HIGH); 

    pinMode (pwmLeft, OUTPUT); 
    pinMode (pwmRight, OUTPUT); 
} 

long oldPositionA = -999; 
long oldPositionB = -999; 

void loop() { 

    analogWrite(pwmLeft, speedA); 
    analogWrite(pwmRight, speedB); 

    long newPositionA = myEncA.read(); 
    long newPositionB = myEncB.read(); 
    if ((newPositionA != oldPositionA) || (newPositionB != oldPositionB)) { 
    unsigned long newtimeA = millis(); 
    long positionA = newPositionA - oldPositionA; 
    long positionB = newPositionB - oldPositionB; 
    long velB = (positionB)/(newtimeA - oldtimeA); 
    long velA = (positionA)/(newtimeA - oldtimeA); 
    oldtimeA = newtimeA; 
    oldPositionA = newPositionA; 
    oldPositionB = newPositionB; 
    Serial.println(velB); 
    } 
} 

我仍然有我的「B」電機的問題,計算仍是遙遠的某些原因。在循環

汽車 「A」 工作正常

回答

1

一對夫婦的問題,包括被零除錯誤()。此掃描會導致您的控制器重置。分區時總是檢查除數的值!

僅使用正跳不必要地減少了2

在你的讀數分辨率Arduino是一個8位控制器...讀取一個int需要多個指令,這意味着你讀取經過修改的int前,應禁止中斷通過中斷程序。如果不這樣做會導致vakue讀取中出現奇怪的跳躍。這通常是這樣的:

//... 
NoInterrupts(); 
int copyOfValue = value; // use the copy to work with. 
interrupts(); 
//... 

在你的情況下,單個字節值可能足以存儲的運動,每30毫秒復位,這應該給你的255脈衝/ 30毫秒= 8500的最高速度脈衝/秒或1275000轉/分,用於24個滴答/轉編碼器。 :)在這種情況下,不需要禁止讀取中斷。

每30ms一次讀數,1 tick/30ms = 33 tick /秒或85 RPM。運動有點高。您可能需要平均讀數,具體取決於您的應用程序。

此外,您使用的算法將無法正常工作。主要原因是讀取和調整之間的延遲太小。大多數讀數將爲零。刪除println()調用時會遇到問題。我建議在讀數之間至少有30 ms的起搏。取決於您的應用,100毫秒可能會更好一些。使用速度平均的float變量肯定會有所幫助。

void loop() 
{ 
    //... 
    if(oldEncPos != encoderPos) { 
    newtime = millis(); 
    tempPos = encoderPos - oldEncPos; 
    vel = tempPos/(newtime - oldtime); // <-- if newtime == oltime => divide by zero. 
    //... 
    } 
    //... 
} 

編碼器讀取的代碼看起來太過複雜...

#define PIN_A 2 // encoder bit 0 
#define PIN_B 3 // encoder bit 1 

volatile char encVal1; 
volatile unsigned char encPos1; // using char 

void OnEncoder1Change() 
{ 
    char c = (digitalRead(pinA) ? 0b01 : 0) 
      + (digitalRead(pinB) ? 0b10 : 0); // read 
    char delta = (c - encVal1) & 0b11;  // get difference, mask 

    if (delta == 1)       // delta is either 1 or 3 
     ++encPos1; 
    else 
     --encPos1; 
    encVal1 = c;        // keep reading for next time. 
    encPos1 += delta;       // get position. 
    // no need to call sei() 
} 

setup() 
{ 
    pinMode(pinA, INPUT_PULLUP); 
    pinMode(pinB, INPUT_PULLUP); 

    // get an initial value 
    encValA = digitalRead(pinA) ? 0b01 : 0; 
    encValA += digitalRead(pinB) ? 0b10 : 0; 

    // use digitalPinToInterrupt() to map interrupts to a pin # 
    // ask for interrupt on change, this doubles . 
    attachInterrupt(digitalPinToInterrupt(PIN_A), OnEncoder1Change, CHANGE); 
    attachInterrupt(digitalPinToInterrupt(PIN_B), OnEncoder1Change, CHANGE); 


    //... 
} 

unsigned char oldTime; 
unsigned char oldPos; 
int speed; 
void loop() 
{ 
    unsigned char time = millis(); 

    if (time - oldTime > 30)  // pace readings so you have a reasonable value. 
    { 
     unsigned char pos = encPos1; 
     signed char delta = pos - oldPos; 

     speed = 1000 * delta)/(time - oldTime); // signed ticks/s 

     encPos1 -= pos; // reset using subtraction, do you don't miss out 
         // on any encoder pulses. 
     oldTime = time; 
    } 
} 
+0

喜邁克爾感謝您的答覆,我想將你說的變化,但我仍然得到不正確的結果。如果我使用if statemtn的時間差在30以上,我只能得到一些結果(不準確的結果),並且結果由於某種原因而停止 – user7792712

+0

Ive用新代碼添加了一個編輯 – user7792712