Getting the motor e...
 
Notifications
Clear all

Getting the motor encoders to work

57 Posts
5 Users
5 Reactions
2,788 Views
robotBuilder
(@robotbuilder)
Member
Joined: 5 years ago
Posts: 2045
Topic starter  

Posted by: @thrandell

@robotbuilder

I was wondering, when you get the encoders under control are you planning to implement a PID controller to steer your robot?

Tom

 

Only if required or just to say I could use PID if I wanted to.  PID involves an error signal and is all about avoiding oscillation around or overshooting of some some goal state such as standing up right or keeping a distance from a wall or smoothly following a black line drawn on the floor and so on...

 

 


   
ReplyQuote
robotBuilder
(@robotbuilder)
Member
Joined: 5 years ago
Posts: 2045
Topic starter  

@byron

How are you thinking of tackling the brains programming? If you've started on it how large has your program grown?

In stages and at different levels. Getting a correct reading of the encoder pulses is a low level project to enable its use for things like some dead reckoning (odometry), speed control, motor monitoring (are the wheels actually turning? and so on. At the high level getting the robot to make use of visual targets for navigations and object recognition.

 


   
ReplyQuote
robotBuilder
(@robotbuilder)
Member
Joined: 5 years ago
Posts: 2045
Topic starter  

@zander
@byron
@thrandell
@davee

Or anyone else that might like to help and can figure it out.

The mystery remains. To recap. I have a dc motor with an encoder. It should require 800 pulses for one complete rotation. However the first complete rotation only seems to require 750 pulses?? However every rotation after that does require a count of 800 pulses. So for example if I want to rotate the wheel 10 complete times that means counting 8000 pulses minus a mysterious 50 for the first rotation?

I would add that with the code below the wheel does end up exactly where it started, be it one or more complete rotations. So in that sense, apart from the mystery 50, it works perfectly.

Is there any clever sleuth that can figure out the reason for the 50 which is probably staring me right in the face!

I will have to give it a break, again, and come back to it tomorrow.

 

// Motor A
int enA = 11;
int in1 = 10;
int in2 = 9;
// Motor B
int in3 = 7;
int in4 = 6;
int enB = 5;

volatile int counter1 = 0;
volatile int counter2 = 0;
volatile int totalCount = 8000;  // 10 complete rotations
volatile int oldCount1 = 0;

// button pins assignments
const int btn0 = 4;        // press button purple wire
const int btn1 = 3;        // state of encoder1 green wire
const int btn2 = 2;        // state of encoder2 yellow wire

void forwardA(int rate){
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
  analogWrite(enA, rate);  
}

void forwardB(int rate){
  digitalWrite(in3, LOW);
  digitalWrite(in4, HIGH);
  analogWrite(enB, rate);  
}

void reverseA(int rate){
  digitalWrite(in1, LOW);
  digitalWrite(in2, HIGH);
  analogWrite(enA, rate);  
}

void reverseB(int rate){
  digitalWrite(in3, HIGH);
  digitalWrite(in4, LOW);
  analogWrite(enB, rate);  
}

void turnOffMotorA(){
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
}

void turnOffMotorB(){
  digitalWrite(in3, LOW);
  digitalWrite(in4, LOW);
}


void setup()
{

  // Set all the motor control pins to outputs
 
  pinMode(enA, OUTPUT);
  pinMode(enB, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);

  pinMode(btn0,INPUT_PULLUP);

  pinMode(3,INPUT); // set as input
  //digitalWrite(3,HIGH); // enable internal pullup resister
  attachInterrupt(digitalPinToInterrupt(3), encoder1, RISING); // interrupt initialization
  
  pinMode(2,INPUT); // set as input
  //digitalWrite(2,HIGH); // enable internal pullup resister
  attachInterrupt(digitalPinToInterrupt(2), encoder2, RISING); // interrupt initialization
  
  //Serial.begin( 9600 );
  Serial.begin(115200);
  // Serial.begin(57600);
  //Serial.println("Starting up");
  
}

// interrrupt server routines for reading encoder

void encoder1()
{
  counter1++;
}

void encoder2()
{
  counter2++;
}



void loop()
{

  // repeats loop() until button is pressed

  if (!digitalRead(btn0)){   // then button is pressed

    counter1  = 0;   // zero counter
    oldCount1 = 0;  // zero last counter value

    Serial.println(counter1);
    Serial.println("****");

    forwardA(200);  // start motorA

   do {  // totalCount rotations
      if (oldCount1 != counter1){
        Serial.println(counter1);
        oldCount1 = counter1;
      }
    } while (counter1 < totalCount-50);

    Serial.println("======");
    Serial.println(counter1);

    turnOffMotorA();

  }

}

 

This is the start and finish of the print out when I run the above code.

To enlarge an image, right click image and choose Open link in new window.

printOut2

 


   
ReplyQuote
byron
(@byron)
No Title
Joined: 5 years ago
Posts: 1131
 

@robotbuilder

Its odd that in your different attempts the difference twixt encoder pulse counts and wheel rotation counts have varied.  Your latest session reports 50 whereas previous sessions report 64 and even, once, an exact match.

Taking it that the white line drawn on the wheel matches up exactly to its start datum point, then it points to the recording of the encoder pulse.  Are your encoders optical, magnetic or mechanical?

Note the following from the blurb I linked to previously. 

7.1 Debouncing

Contact bounce or vibration effects cause an oscillating signal on one line. The state diagram shows that this is logically indistinguishable from a physical movement of the encoder backwards and forwards across one transition point. Consequently any valid decoding algorithm will register a change in position of one LSB forwards and backwards

Maybe a little shaking of your bot as the wheels start to turn and get up to speed?  A bit of jitter as you press the button to start the motors turning.   How about getting your program to start the wheels in motion by itself after a short delay without need a button press.

Perhaps consider using the truth table shown in the state diagram shown in the article to count the pulses. 

Another suggestion, to see if it makes any difference,  is to stop all those serial prints, simply make the wheels turn and stop at the 8000 interrupts count, and then see how your white line matches its stop marker.


   
ReplyQuote
robotBuilder
(@robotbuilder)
Member
Joined: 5 years ago
Posts: 2045
Topic starter  

@byron

Its odd that in your different attempts the difference twixt encoder pulse counts and wheel rotation counts have varied. Your latest session reports 50 whereas previous sessions report 64 and even, once, an exact match.

Maybe the earlier attempts were not exactly on 64 as I simply attached the encoder output to the Arduino using a pull up resistor and in fact I thought the value may have been due to extra pulses being counted.  However it is exactly 50 now and stops the white line exactly where it started. Changing to more than one complete rotation I found that the rest of the rotations did in fact read 800 pulses not the 750 for the first rotation regardless of the PWM value.

It seems like after the motors are started the wheel rotates passed the first 50 slots without recording a pulse.

The encoders are optical. I am using a comparator with a feedback resistor so it operates as a scmitt trigger although as a plain comparator it seems to work just as well. The LM393 encoder module is just wired as a comparator.

The reason I don't think it is noise is because random noise would probably not return an exact 50 number.

I turned off the Serial interface and Serial prints and it made no difference.

Yes that exact match without any subtracting of any number before I modified the code has flummoxed me. Was I mistaken with a subtraction still there? I will never know unless I can get it working as it should.

Next I am going to try and monitor the pulses with software rather than using the hardware interrupt.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 4 years ago
Posts: 7283
 

@robotbuilder @byron Since this only happens during the first rotation. I would look at pre-allocating all the variables so there is no first-time cost in allocating a variable. It's been a while, but I think declaring them static will accomplish that. I would also properly declare the INPUT_PULLUP where needed and NOT use volatile on variables that do not need it. The only variables that need it are those that are in the ISR.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote
byron
(@byron)
No Title
Joined: 5 years ago
Posts: 1131
 

@robotbuilder @zander

Instead of:

pinMode(2, INPUT); //set as input

should that be 

pinMode(2, INPUT_PULLUP);

I don't see this counting for a loss of 50 pulses just on the first rotation unless its something to do with the motor getting up to speed and the counting circuit being a bit dodgy at startup speeds and needing good pullups.  Maybe additional resistor pullups are required, if you've not already done so.  Maybe a good time to buy an oscilloscope?  (I've never used one, and @davee is the best to advise if this could help for this electrical sleuthing.)


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 4 years ago
Posts: 7283
 

@byron I have no idea where the 50 pulses have gone, but my approach is to fix everything that is even slightly wrong because we don't know. It can't hurt anything and takes less than 1 second.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote
byron
(@byron)
No Title
Joined: 5 years ago
Posts: 1131
 

@zander I quite agree.  I would eliminate everything except the motor driving code and the encoder counting.  I would eliminate the button press to start the sequence and just let the program kick everything off after a short delay after it starts up to steady its nerves.  But if the encoder pins need a pullup, then ensure INPUT_PULLUP is used as I see that setting the pin high has been commented out.


   
Ron reacted
ReplyQuote
robotBuilder
(@robotbuilder)
Member
Joined: 5 years ago
Posts: 2045
Topic starter  

@zander
@byron

I did try the INPUT_PULLUP in one test but someone advised that the resistance might be too high 10K+ and I should perhaps use my own external pullup resistor which I did, a 2.2K resistor which is why that line was commented out.

I used to own an oscilloscope decades ago but had to sell it. Not really worth buying one just for this issue and as useful as they are for some situations I will struggle on without one.  I did try and read the wave form at the encoder output using an Arduino analog pin and the serial plotter as a display with mixed results. This latest version of the Arduino IDE doesn't seem to be giving me the easy use of such things that the original IDE did.

Thanks for the interest and suggestions.

I will resolve the problem and probably feel silly for not seeing earlier what the issue was 🙂

 


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 4 years ago
Posts: 7283
 

@robotbuilder I doubt that the value of pullup has anything to do with the issue, but the overuse of volatile and the use of automatic storage will cause a malloc the first time thru the code. Also, not having the count a const will cause a first-pass slowdown and maybe more. I didn't study that part enough.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote
robotBuilder
(@robotbuilder)
Member
Joined: 5 years ago
Posts: 2045
Topic starter  

@zander
@davee
@byron
@thrandell

The thought occurred to me that after the motors are turned off the wheels may in fact still be turning. So I added a delay and a print to see if that was the case and it appears to be the case. In the code below I added a delay after turning off the motors to check counter1 to see if was still counting and it was!! So it doesn't matter how many rotations (lots of 800 pulses) it will keep going another 74 to 76 extra pulses. So it was in fact accurately counting the first rotation as 800 plus or minus 1 pulse.

Serial output from the code below:

Output Serial Monitor x
======
725 out of loop
725 motor off
799 value after 3 seconds

On an old robot using relay h bridge I used to brake by shorting the motors out via a high wattage resistor.

I do have another glitch where occasionally the wheel fails to do a full rotation after pressing the button stopping with the counter showing a counter value of 512 on exiting the loop.

// Motor A
int enA = 11;
int in1 = 10;
int in2 = 9;
// Motor B
int in3 = 7;
int in4 = 6;
int enB = 5;

volatile int counter1 = 0;
volatile int counter2 = 0;
int totalCount = 800;  // 1 complete rotation
int PWMA = 90;
int PWMB = 90;

// button pins assignments
const int btn0 = 4;        // press button purple wire

void forwardA(int rate){
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
  analogWrite(enA, rate);  
}

void forwardB(int rate){
  digitalWrite(in3, LOW);
  digitalWrite(in4, HIGH);
  analogWrite(enB, rate);  
}

void reverseA(int rate){
  digitalWrite(in1, LOW);
  digitalWrite(in2, HIGH);
  analogWrite(enA, rate);  
}

void reverseB(int rate){
  digitalWrite(in3, HIGH);
  digitalWrite(in4, LOW);
  analogWrite(enB, rate);  
}

void turnOffMotorA(){
  analogWrite(enA,0);
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
}

void turnOffMotorB(){
  analogWrite(enB,0);
  digitalWrite(in3, LOW);
  digitalWrite(in4, LOW);
}


void setup()
{

  // Set all the motor control pins to outputs
 
  pinMode(enA, OUTPUT);
  pinMode(enB, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);

  pinMode(btn0,INPUT_PULLUP);

  pinMode(3,INPUT); // set as input
  //digitalWrite(3,HIGH); // enable internal pullup resister
  attachInterrupt(digitalPinToInterrupt(3), encoder1, RISING); // interrupt initialization
  
  pinMode(2,INPUT); // set as input
  //digitalWrite(2,HIGH); // enable internal pullup resister
  attachInterrupt(digitalPinToInterrupt(2), encoder2, RISING); // interrupt initialization
  
  //Serial.begin( 9600 );
  Serial.begin(115200);
  // Serial.begin(57600);
  //Serial.println("Starting up");

}

// interrrupt server routines for reading encoder

void encoder1()
{
  counter1++;
}

void encoder2()
{
  counter2++;
}


void loop()
{

  if (!digitalRead(btn0)){   // then button is pressed

    counter1  = 0;   // zero counters
    counter2  = 0;  //

    forwardA(PWMA);  // start motorA
    forwardB(PWMB);  // start motorB

    while (counter1 < 725) {  // totalCount rotations
      //Serial.println(counter1);
    }

    Serial.println("======");
    Serial.println(counter1);

    turnOffMotorA();
    turnOffMotorB();

    Serial.println(counter1);

    // if motor still turning encoder will still be counting
    delay(3000);
    Serial.println(counter1);

  }

}

 


   
ReplyQuote
Page 4 / 4