Motor Encoder CPR Calculation/Code Incorrect

Hello Everyone,

For a project I'm doing, I have 2 of these motors. They come with encoders. I have been told the calculation to find how many encoder ticks per full spin of the shaft is the gearbox ratio * the CPR of the encoder which is 70:1 and 64 in my case. Resulting in a 4480 CPR. On my motors this doesn't seem to be the case. It might be a problem with my code but I can't seem to place where.

Code:

void setup()
{
  foreman.init();
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(2), M1Encoderadd, RISING);
  attachInterrupt(digitalPinToInterrupt(3), M2Encoderadd, RISING);
  foreman.forward();
}

in forman.init():

  pinMode(M1DIR, OUTPUT);
  pinMode(M2DIR, OUTPUT);
  pinMode(M1PWM, OUTPUT);
  pinMode(M2PWM, OUTPUT);

(don't worry the pins are all the right ones)
interrupt handlers:

void M1Encoderadd(){
    encoder.countM1A++;
}
void M2Encoderadd(){
    encoder.countM2A++;
}

and foreman.forward():

void Foreman::forward(){
    int speed = 100;
    int target = endr.countM1A + 4800;
    while(endr.countM1A < target){
        Serial.println(endr.countM1A);
        digitalWrite(M1DIR, HIGH);
        analogWrite(M1PWM, speed);

    }
    digitalWrite(M1DIR,HIGH);
    analogWrite(M1PWM, 0);
    digitalWrite(M2DIR,HIGH);
    analogWrite(M2PWM, 0);
}

The motor driver I'm using is the one recommended by JRemington in this post.

Please feel free to ask for any other info you need.

Thanks,
Ian

Please post all the code, and state which library you are using.

I'm not using any libraries.
Here is main:

#include "foreman.h"
#include "pinmap.h"
#include "encoder.h"
Foreman foreman;
Encoder encoder;
volatile long m1 = 0;
volatile long m2 = 0;
void M1Encoderadd(){
    encoder.countM1A++;
}
void M2Encoderadd(){
    encoder.countM2A++;
}
void setup()
{
  foreman.init();
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(2), M1Encoderadd, RISING);
  attachInterrupt(digitalPinToInterrupt(3), M2Encoderadd, RISING);
  foreman.forward();
}
 
void loop()
{

}

Encoder:

#include <Arduino.h>
#include "encoder.h"

void Encoder::Init( int _inputPinR, int _inputPinL){
    pinMode(_inputPinR, INPUT_PULLUP);
    m_inputPinR = _inputPinR;
    pinMode(_inputPinL, INPUT_PULLUP);
    m_inputPinL = _inputPinL;
}
volatile bool Encoder::stopCounting = false;
int Encoder::countM1A = 0;
int Encoder::countM2A = 0;
int Encoder::countM1B = 0;
int Encoder::countM2B = 0;

foreman:

#include <Arduino.h>
#include "foreman.h"
#include "pinmap.h"

long previousTime = 0;
float ePrevious = 0;
float eIntegral = 0;

void Foreman::init(){
    wheel.Init();
}
float Foreman::show(){
    return mpu.update();
    
}


float Foreman::pid(int target, float kp, float ki, float kd){
  long currentTime = micros();
  float deltaT = ((float)(currentTime - previousTime)) / 1.0e6;

  int e = target - endr.countM2A;
  float eDerivative = (e - ePrevious) / deltaT;
  eIntegral = eIntegral + e * deltaT;
  float u = (kp * e) + (kd * eDerivative) + (ki * eIntegral);
  
  //Update variables for the next iteration
  previousTime = currentTime;
  ePrevious = e;

  return u;
}

void Foreman::moveMotor(float u){
    float speed = fabs(u);
    if(speed > 255){
        speed = 255;
    }
    if(endr.countM2A > endr.countM1A){
        speed = 0;
    }
    digitalWrite(M2DIR, LOW);
    analogWrite(M2PWM, speed);
}

void Foreman::forward(){
    int target = endr.countM1A + 4880;
    while(endr.countM1A < target){
        Serial.print(endr.countM1A);
        Serial.print("   ");
        Serial.println(endr.countM2A);
        digitalWrite(M1DIR, HIGH);
        analogWrite(M1PWM, 50);

        float kp = 3.00;
        float kd = 0.10;
        float ki = 0.01;
        float u = pid(endr.countM1A, kp, kd, ki);
        moveMotor(u);
    }
    digitalWrite(M1DIR,HIGH);
    analogWrite(M1PWM, 0);
    digitalWrite(M2DIR,HIGH);
    analogWrite(M2PWM, 0);
}

pinmap:

#ifndef _PIN_H_
#define _PIN_H_

#include <Arduino.h>

// RGB
#define PIN_RGB             4

// Motor
#define M1DIR 7
#define M2DIR 8 
#define M1PWM 9
#define M2PWM 10
#define M1Encoder 2
#define M2EN 4
#define M2Encoder 3
#define M1EN 13

#endif

Its a little different from the top. It's cleaner in my opinion. The pid is following this tutorial:

all I need to know is how I find the CPR of the encoders.

Pololu states the CPR, as you have already pointed out. They don't make mistakes in cases of such basic device parameters, for the products they sell.

If you don't get that value with your code and wiring, there is a problem with the code or wiring.

Interrupts are a particularly bad choice for encoder readout if the encoder transitions are not very clean, in which case a transition can result in more than one count.

so what would i use instead?

You can use interrupts if the encoder transitions don't result in more than one count per transition. If that is not the case, the code and/or electronics require significant changes.

Other approaches include input polling, with appropriately timed delays to skip the multiple transitions. You might try the Arduino encoder library, which is well designed and reliable. It offers both interrupt and input polling options.

A good place to start is to determine where the actual problem lies. It is a good idea to use an oscilloscope to examine the transitions on the encoder output pins.

Heres is the catch then:
Right now it seems that the motors are spinning around 4.3 revolutions for 4880 ticks. If the interrupts were counting more than once, wouldn't that mean it would spin less than one full revolution?

I merely pointed out one of the most common and severe problems with using interrupts to monitor switches or encoders.

It seems possible that you were shipped a motor with a different gear ratio. Post on the Pololu forum, where you can expect prompt, professional support from Pololu engineers.

will do. thanks for the help here! I'll come back if I find a solution or other similar problems arise!

I also tried gear ratios of all the other motors, 18.75 was the closest but still not right at 64 CPR.

Perhaps you are missing some encoder pulses due to interface or power problems. Which Arduino are you using, and how are the encoder power and ground connected?

EDIT: I finally took the time to look at the encoder counting procedure and there are three serious problems.

The code below counts only half the rising edges, since the B channel seems to be ignored. For the full encoder resolution, count all the rising and falling edges.

Furthermore, any variable modified by an interrupt service routine must be declared volatile, or changes may and often will be missed in the main loop.

void M2Encoderadd(){
    encoder.countM2A++;  //must be declared volatile
}

Finally, a multi-byte variable shared between an ISR and the main program can be corrupted by the ISR while it is being accessed by the main program. To prevent that, make a copy with the interrupts off, and use the copy.

Example:

noInterrupts();
int countM2A_copy = encoder.countM2A;
interrupts();
Serial.println(countM2A_copy);

I have found the solution:
I didn't account for the fact that I was only using the rising edge of one of the encoders. 64 CPR is both encoders' rising and falling. My equation therefore has been correctly adjusted to 16*70

Actually, the solution was pointed out to you in several places, above, and on the Pololu forum.

yeah. I thank you and everybody else for helping me out.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.