Pages: [1]   Go Down
Author Topic: Need help reading Encoder values.  (Read 1082 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 10
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Okay.

The long story short is that I am building a hexapod robot. I'm using six pololu gear motors with quadrature hall effect encoders mounted on the back. I am using an Arduino Mega to read all six encoders, and I am having a hell of a lot of trouble getting the data to look the way it should.

I tested the encoder inputs on a oscilloscope. Everything appeared correct. 0-5V signals, 90 degree offset.

The first thing I tried was loading the Teensy Encoder library that the Arduino Encoder page links to. I loaded the library, set up an encoder object, and made a small chunk of test code. This test code told one of my robot legs to spin at 1/5 speed, so approximately 20 RPM. I outputted the position data via serial to see what my microcomputer was seeing. My data was spazzing between the values of -12 to 12, and seemed to get "stuck" on a value sometimes, sitting at 0 to 9 for half a revolution at a time. I couldn't make any sense of it.

I set the teensy code aside and tried to use the nested "For" loop code that the Arduino Encoder page used. When I used that, I got ~256 counts per revolution, which was odd, as even measuring the falling and rising edge of Encoder Pin A and Encoder Pin B, I should only get a maximum of 64 counts per revolution. If I'm reading it correctly, that code is only counting a single edge of Pin A (when it goes from high to low) so I should only be counting sixteen.

Well, at least it was consistent, I thought. But when I tried to extend the nested for loop code to two motors, things got crazy again. It started only counting ~38 counts per revolution of each motor. Then I was royally confused.

So, has anyone used a two-channel quadrature encoder with an Arduino before? And does anyone know where I should start in getting consistent values for each encoder/motor combo? Any and all advice appreciated.
« Last Edit: November 17, 2013, 08:06:49 pm by projectzero » Logged

Germany
Offline Offline
Full Member
***
Karma: 1
Posts: 141
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

in the past I was working on making your own encoder (hardware and software). You can look at my blog:

Simple rotary encoder:
http://heliosoph.mit-links.info/make-your-own-rotary-encoder/

Quadrature encoder:
http://heliosoph.mit-links.info/make-your-own-quadrature-rotary-encoder/

This is a very basic example for reading an encoder:
http://heliosoph.mit-links.info/rotary-encoder-software-for-arduino/

Elektrix
Logged

My blog about arduino and Linux themes:
http://heliosoph.mit-links.info/

0
Offline Offline
Shannon Member
****
Karma: 206
Posts: 12178
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


The first thing I tried was loading the Teensy Encoder library that the Arduino Encoder page links to. I loaded the library, set up an encoder object, and made a small chunk of test code. This test code told one of my robot legs to spin at 1/5 speed, so approximately 20 RPM. I outputted the position data via serial to see what my microcomputer was seeing. My data was spazzing between the values of -12 to 12, and seemed to get "stuck" on a value sometimes, sitting at 0 to 9 for half a revolution at a time. I couldn't make any sense of it.

You need to post your code if you want help with it, we can't magically guess what
the problem is!
Logged

[ I won't respond to messages, use the forum please ]

Offline Offline
Newbie
*
Karma: 0
Posts: 10
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

in the past I was working on making your own encoder (hardware and software). You can look at my blog:

Simple rotary encoder:
http://heliosoph.mit-links.info/make-your-own-rotary-encoder/

Quadrature encoder:
http://heliosoph.mit-links.info/make-your-own-quadrature-rotary-encoder/

This is a very basic example for reading an encoder:
http://heliosoph.mit-links.info/rotary-encoder-software-for-arduino/

Elektrix

Thanks Elektrix. I look into interrupts but because of the volume of encoder data (six encoders, two signal wires apiece) I'd need 12 interrupts which the Arduino doesn't come near to having. Have you written any encoder code that uses digitalRead()? I know it's inferior, but shikata ga nai, eh?


The first thing I tried was loading the Teensy Encoder library that the Arduino Encoder page links to. I loaded the library, set up an encoder object, and made a small chunk of test code. This test code told one of my robot legs to spin at 1/5 speed, so approximately 20 RPM. I outputted the position data via serial to see what my microcomputer was seeing. My data was spazzing between the values of -12 to 12, and seemed to get "stuck" on a value sometimes, sitting at 0 to 9 for half a revolution at a time. I couldn't make any sense of it.

You need to post your code if you want help with it, we can't magically guess what
the problem is!

I'm at work now, but I'll post my code tonight.
Logged

Germany
Offline Offline
Full Member
***
Karma: 1
Posts: 141
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You should be able to take some other inputs as interrupt, too:

http://playground.arduino.cc/Code/Interrupts

Never tried this myself...

There are links to libraries, you could also search for "Pin Change Interrupts".

Elektrix
Logged

My blog about arduino and Linux themes:
http://heliosoph.mit-links.info/

Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

My issue is very similar to the encoder issues I see discussed here. I'm new to Arduino but very experienced with electronics & programming.

I'm trying to use an Adruino Leonardo to replace the cruise control module in my car. I'm not sure the Leonardo will have enough program memory for the task, but it was a good place to start the project. I'll upgrade the hardware if necessary.

The problem I'm having is with interrupts. The Leonardo is mounted directly on the old cruise control module's PCB to take advantage of drivers for two solenoids and various inputs, filtering etc. But I can ground the interrupt pin and I still get interrupts! This is with the Leonardo powered by USB and the board NOT in the car.

I went so far as to remove the Leonardo from the cruise module PCB so it's just the Leonardo alone. Still I get interrupts even when the pin is grounded (the "attach" uses the RISING parameter).

According to the Arduino reference and Leonardo specific hardware pages, 5 external interrupts are possible for the Leonardo (quoted from Arduino website): "3 (interrupt 0), 2 (interrupt 1), 0 (interrupt 2), 1 (interrupt 3) and 7 (interrupt 4)". I presume the numbers outside parenthesis are the actual pin numbers, so interrupt 1 is on pin 3 (4th pin: rx, tx, pin 2, PIN 3). I use this pin for the speedometer pulse input.

With the pin grounded the only conclusion to draw is something is either wrong with the Leonardo or there is something in the "operational / overhead" code provided by the Arduino IDE that is triggering the interrupts.

I have reduced the sketch to bare minimum. The Leonardo is connected to a Sparkfun 16x2 LCD, driven off pin 12. The sketch attaches the function to serve as an interrupt handler, which just saves the delta time from the last interrupt occurrence. The loop() calls one function to display the time interval on the LCD.

The documentation is rather sparse concerning interrupts. But if anyone can provide any info or pointers as to how to get an accurate time between two pulses using an interrupt service routine, or have input about the sketch I'de love to hear from you.

Thanks for taking the time to read this lengthy post. My test sketch will follow in another post.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is my test sketch:

Quote
#include <SoftwareSerial.h>

//// Pin definitions
#define LED       13
#define DIS        7
#define VENT       6
#define VAC        5
#define BRAKE      4 // Move this to pin 2 to use interrupt 0
#define SPEED      3
#define CNTRL     A0
#define S_POS     A1

//// Global vars
byte ledState;
volatile unsigned long deltaT, lastTime;

SoftwareSerial LCD(13,12); // RX, TX pins

//-------------------------------------------------------------------------------------------
void setup()
{
  LCD.begin(9600);         // All SerLCDs are 9600 Baud by default
  if (0) toggleSplash();   // This is only necessary once?
//  delay(1200);             // Required if splash is displayed
  
  pinMode(BRAKE, INPUT);   // A "1" when brake is applied
  pinMode(LED, OUTPUT);    // The on board LED on pin 13

  pinMode(SPEED, INPUT);   // Speed is relative to space between pulses
  attachInterrupt(1, speedInterupt, RISING);

  pinMode(VENT, OUTPUT);   // Vent
  pinMode(VAC, OUTPUT);    // Vac
  pinMode(DIS, OUTPUT);    // Disable = 1
  digitalWrite(DIS, HIGH); // Start with cruise disabled
}

//-------------------------------------------------------------------------------------------
void loop()
{
//  clearScreen();
//  selectLineOne();
//  showBrake();
  showSpeed();
//  delay(100);
//  digitalWrite(LED, ledState ^= 1);    // Toggle the LED to indicate looping
}

// Prints a double value with number of decimal places determined by precision.
// example: double2ASCII(buf,  3.1415, 2); // prints 3.14 (two decimal places).  
// The allowed range of precision is 1 to 6 inclusive. Otherwise the value will
// be displayed as an integer (no decimal point and all to the right of it will
// not be displayed).
char * double2ASCII(char *buffer, double val, char precision)
{
   double frac, frac1, mult = 1;
   char i, padding[7];
   
   if (precision > 0 && precision < 7) {      
                frac = frac1 = (abs(val) - (int)abs(val));  // Get the fractional portion (2 copies)
      while (precision--) mult *= 10;             // Multiplier to move fraction to left
                frac *= mult;                               //  of decimal point
   
                // Fill a buffer with leading zeros for the value to the right of decimal point
      for (i = 0; frac1 && (frac1 *= 10) < 1; i++) padding = '0';
      padding = 0;
      
      sprintf(buffer, "%d.%s%u", (int)val, padding, (unsigned)frac); //
   }
   else sprintf(buffer, "%d", (int)val);  // No precision; treat as an integer
   
   return buffer;
}

void showBrake()
{
  LCD.print(digitalRead(BRAKE) ? "Br=1 " : "Br=0 ");
}

void showSpeed()
{
char buffer[17];

  clearScreen();
  selectLineOne();
  LCD.print(double2ASCII(buffer, (double)deltaT, 0));
}

// Interrupt service routine for speedometer pulses.
// Calculates the time since the last interrupt. The
// result is in global var deltaT.
void speedInterupt()
{
  unsigned long diff, now = micros();

//  noInterrupts();            // Using this causes loop to stop

  if (now < lastTime) now += lastTime; // Accomodate rollover
  diff = now - lastTime;
  if (diff > 100)              // Can also use to gauge min speed for cruise
  {
    deltaT = diff;
    lastTime = now;
  }
//  interrupts();
}

////////////////////////////////////// LCD SUPPORT BELOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//-------------------------------------------------------------------------------------------
void clearScreen()
{
  //clears the screen, you will use this a lot!
  LCD.write(0xFE);
  LCD.write(0x01);
}
//-------------------------------------------------------------------------------------------
void selectLineOne()
{
  //puts the cursor at line 0 char 0.
  LCD.write(0xFE); //command flag
  LCD.write(128); //position
}
//-------------------------------------------------------------------------------------------
void selectLineTwo()
{
  //puts the cursor at line 0 char 0.
  LCD.write(0xFE); //command flag
  LCD.write(192); //position
}
//-------------------------------------------------------------------------------------------
void toggleSplash()
{
  //this toggles the spalsh screenif off send this to turn onif on send this to turn off
  LCD.write(0x7C); //command flag = 124 dec
  LCD.write(9); // 0x09
}
//--------------------------------------------------------------------------------------------
// Sets the cursor position of the SerLCD.  
void setCursorPosition(int row, int col)
{
 int pos;
 if (row == 1) pos = col;
 else                pos = col + 64;
                
 pos = pos + 128;   // Cursor move command
 LCD.write(0xFE);
 LCD.write(pos);
}

« Last Edit: November 20, 2013, 06:03:50 pm by Motechman » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 10
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

MarkT, Electrix - Apologies for the delay. This is the nested "if" loop code I was trying to use for the encoders.  The first ~10 lines of code, digitalRead(hefmotor5), is the hall effect sensor. Once the hall effect sensor goes high, y5 is set to high, and the arduino is supposed to start reading the encoder. Hefmotor5 is the hall effect sensor, enc5 and enc52 are the two hall effect sensor and.

Code:
   //THIS BLOCK OF CODE HOMES MOTOR 5
       digitalRead(hefmotor5);
      // if (digitalRead(hefmotor5) == HIGH){
        // Serial.println("Motor5 is not at home.");}
        
       if (digitalRead(hefmotor5) == LOW){
         Serial.println("Motor 5 HEF Trigger.");
         init5dir = LOW;
          y5 = HIGH;
       }
      
       if (y5 == HIGH){  
         n5 = digitalRead(enc5);
         if ((enc5last == LOW) && (n5 == HIGH)) {
           if (digitalRead(enc5) == LOW) {enc5pos--;}
           else {enc5pos++;}
         }
         enc5last = n5;
         x5 = abs(enc5pos);
         Serial.println(x5);
       }
      
       if (x5 >= 8){
       init5 = 0;
       Serial.println("5 is Home");
       }

This code worked well enough until I tried to paste an identical block with all the variables changed to the next motor, motor six. When I tried to run the two codes together it started counting a different value for the amount of pulses per revolution. I thought it must be missing pulses for some reason.


I had deleted this code, but I made a rough sketch of what I used for the teensy Arduino library. The problem was it never got near breaking out of the "if" loop because the encoder.read() function was twitching randomly between -10 and 10.

Code:
void loop() {
 
  digitalWrite(motor6dir, init6dir);
  analogWrite(motor6speed, init6);
  Encoder encoder6(enc6, enc62);

  while (true){
   
    digitalRead(hefmotor6);
      // if (digitalRead(hefmotor5) == HIGH){
        // Serial.println("Motor5 is not at home.");}
         
       if (digitalRead(hefmotor6) == LOW){
         Serial.println("Motor 5 HEF Trigger.");
         init5dir = LOW;
          y5 = HIGH;
         
       if (y5 == HIGH){
         encoder6.read();
         if (encoder6.read() > 32){
         analogWrite(motor6speed, 0);
         }
         
       }
   
   
   
   
  }
}
« Last Edit: November 20, 2013, 11:49:11 pm by projectzero » Logged

Pages: [1]   Go Up
Jump to: