2017-09-12 162 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