arduino - Controlling DC motor using encoder -
i'm trying control speed of 2 dc motors using arduino uno , encoders connected motors.
i've written code check whether there's change in position of encoder , according calculate velocity of motors.
ive used this website code:
i'm having problems when calculating difference between new position of encoder , old position of encoder. reason difference keeps going though speed stays same.
this code far:
#define pwmleft 10 #define pwmright 5 #define in1 9 #define in2 8 #define in3 7 #define in4 6 //motor int motorspeeda = 100; static int pina = 2; // our first hardware interrupt pin digital pin 2 static int pinb = 3; // our second hardware interrupt pin digital pin 3 volatile byte aflag = 0; // let's know when we're expecting rising edge on pina signal encoder has arrived @ detent volatile byte bflag = 0; // let's know when we're expecting rising edge on pinb signal encoder has arrived @ detent (opposite direction when aflag set) volatile long encoderpos = 0; //this variable stores our current value of encoder position. change int or uin16_t instead of byte if want record larger range 0-255 volatile long oldencpos = 0; //stores last encoder position value can compare current reading , see if has changed (so know when print serial monitor) volatile long reading = 0; //somewhere store direct values read our interrupt pins before checking see if have moved whole detent //motor b static int pinc = 12; // our first hardware interrupt pin digital pin 2 static int pind = 33; // our second hardware interrupt pin digital pin 3 volatile byte cflag = 0; // let's know when we're expecting rising edge on pina signal encoder has arrived @ detent volatile byte dflag = 0; // let's know when we're expecting rising edge on pinb signal encoder has arrived @ detent (opposite direction when aflag set) volatile long encoderposb = 0; //this variable stores our current value of encoder position. change int or uin16_t instead of byte if want record larger range 0-255 volatile long oldencposb = 0; //stores last encoder position value can compare current reading , see if has changed (so know when print serial monitor) volatile long readingb = 0; int temppos; long vel; unsigned long newtime; unsigned long oldtime = 0; void setup() { //motor pinmode(pina, input_pullup); // set pina input, pulled high logic voltage (5v or 3.3v cases) pinmode(pinb, input_pullup); // set pinb input, pulled high logic voltage (5v or 3.3v cases) attachinterrupt(0, pina, rising); // set interrupt on pina, looking rising edge signal , executing "pina" interrupt service routine (below) attachinterrupt(1, pinb, rising); // set interrupt on pinb, looking rising edge signal , executing "pinb" interrupt service routine (below) //motor b pinmode(pinc, input_pullup); // set pina input, pulled high logic voltage (5v or 3.3v cases) pinmode(pind, input_pullup); // set pinb input, pulled high logic voltage (5v or 3.3v cases) attachinterrupt(0, pinc, rising); // set interrupt on pina, looking rising edge signal , executing "pina" interrupt service routine (below) attachinterrupt(1, pind, rising); serial.begin(9600); // start 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 read pin values reading = pind & 0xc; // read 8 pin values strip away pina , pinb's values if(reading == b00001100 && aflag) { //check have both pins @ detent (high) , expecting detent on pin's rising edge encoderpos --; //decrement encoder's position count bflag = 0; //reset flags next turn aflag = 0; //reset flags next turn } else if (reading == b00000100) bflag = 1; //signal we're expecting pinb signal transition detent free rotation sei(); //restart interrupts } void pinb(){ cli(); //stop interrupts happening before read pin values reading = pind & 0xc; //read 8 pin values strip away pina , pinb's values if (reading == b00001100 && bflag) { //check have both pins @ detent (high) , expecting detent on pin's rising edge encoderpos ++; //increment encoder's position count bflag = 0; //reset flags next turn aflag = 0; //reset flags next turn } else if (reading == b00001000) aflag = 1; //signal we're expecting pina signal transition detent free rotation sei(); //restart interrupts } void pinc(){ cli(); //stop interrupts happening before read pin values readingb = pind & 0xc; // read 8 pin values strip away pina , pinb's values if(readingb == b00001100 && cflag) { //check have both pins @ detent (high) , expecting detent on pin's rising edge encoderposb --; //decrement encoder's position count dflag = 0; //reset flags next turn cflag = 0; //reset flags next turn } else if (readingb == b00000100) dflag = 1; //signal we're expecting pinb signal transition detent free rotation sei(); //restart interrupts } void pind(){ cli(); //stop interrupts happening before read pin values readingb = pind & 0xc; //read 8 pin values strip away pina , pinb's values if (readingb == b00001100 && dflag) { //check have both pins @ detent (high) , expecting detent on pin's rising edge encoderposb ++; //increment encoder's position count dflag = 0; //reset flags next turn cflag = 0; //reset flags next turn } else if (readingb == b00001000) cflag = 1; //signal we're expecting pina signal transition detent 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; } }
the 2 if statements made check encoders working properly. in first if statement i'm trying calculations of velocity.
i appreciate feedback.
thank you.
edit:
i found out theres encoder library makes lot easier.
so code looks this:
#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); } }
i still having problems "b" motor, calculation still way off reason.
motor "a" works fine
a couple of issues, including divide 0 error in loop(). scan cause reset of controller. check value of divisor when doing division!
using positive transitions unnecessarily reduces resolution of readings 2.
the arduino 8bit controller... reading int
requires multiple instruction, means should disable interrupts before reading int
that's modified interrupt routine. failure cause odd jumps in vakue read. done this:
//... nointerrupts(); int copyofvalue = value; // use copy work with. interrupts(); //...
in case, single byte value enough store movement, reset every 30 ms, should give top speed of 255 pulses/30ms = 8500 pulses/second or 1275000 rpm 24 ticks/turn encoder. :) in case, no need disable interrupts reading.
with 1 reading per 30ms, 1 tick /30ms = 33 tick/seconds, or 85 rpm. it's bit high motion. may need average readings, depending on application.
also, algorithm using not work. main reason delay between reads , adjustments small. readings of zero. run problem when removing println() calls. suggest pacing of @ least 30 ms between readings. 100 ms may work bit better, depending on application. using float
variable speed average help.
void loop() { //... if(oldencpos != encoderpos) { newtime = millis(); temppos = encoderpos - oldencpos; vel = temppos / (newtime - oldtime); // <-- if newtime == oltime => divide zero. //... } //... }
the encoder reading code seems awfully complex...
#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; // difference, mask if (delta == 1) // delta either 1 or 3 ++encpos1; else --encpos1; encval1 = c; // keep reading next time. encpos1 += delta; // position. // no need call sei() } setup() { pinmode(pina, input_pullup); pinmode(pinb, input_pullup); // initial value encvala = digitalread(pina) ? 0b01 : 0; encvala += digitalread(pinb) ? 0b10 : 0; // use digitalpintointerrupt() map interrupts pin # // ask interrupt on change, 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 have reasonable value. { unsigned char pos = encpos1; signed char delta = pos - oldpos; speed = 1000 * delta) / (time - oldtime); // signed ticks/s encpos1 -= pos; // reset using subtraction, don't miss out // on encoder pulses. oldtime = time; } }
Comments
Post a Comment