Noob here. As the title suggests, I am getting very high rotational speeds and I think my calculations are correct, so I am really confused. I am using a GA25-371 geared motor with encoders. The motor is labelled 360 CPR and the gear ratio is 1:34. Therefore the encoder cnts per shaft rev should be 360x34=12240. I have verified the cnts per rev by printing the cnts as I manually turn the shaft one revolution. I am pretty much following the basic tutorials found on youtube for using motor encoders and an l298n motor driver. Link to my motor kit.
I have tried commanding the motor full voltage instead of PWMing it to make sure it just not noise from other cable that might be triggering the interrupts, but that has not helped. I am really stumped here. Any input would be really helpful. Thanks.
Heres my code:
int int1 = 8;
int int2 = 9;
int enA = 2;
int enB = 3;
volatile long int pos = 0;
long int prevPos = 0;
int pwmPin = 10;
int countsPerRev = 12240;
float Pi = 3.14159;
float radiansPerRev = 2*Pi;
int kp = 20;
int ki = 0;
int kd = 0;
unsigned long loopTime;
float AngularVelocity_rps = 0; // Init the angular velocity.
float radPerCnt = radiansPerRev/countsPerRev; // Radians per encoder cnt
float setPoint_rps = 2; // Setpoint for angular vel
int printSerial = 0;
/**
Setup pins
**/
void setup()
{
Serial.begin(9600);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(enA, INPUT);
pinMode(enB,INPUT);
pinMode(pwmPin,OUTPUT);
attachInterrupt(digitalPinToInterrupt(enA),readEncoders,RISING);
delay(3000);
loopTime = micros();
}
/**
Function to set motor command
**/
void setMotorCommand(int pwmVal)
{
if(pwmVal > 0)
{
analogWrite(pwmPin, pwmVal);
digitalWrite(int1, HIGH);
digitalWrite(int2, LOW);
}
else if(pwmVal < 0)
{
analogWrite(pwmPin, pwmVal);
digitalWrite(int2, HIGH);
digitalWrite(int1, LOW);
}
else
{
digitalWrite(int1, LOW);
digitalWrite(int2, LOW);
}
}
/**
ISR for encoder pin
**/
void readEncoders(void)
{
int tmp = digitalRead(enB);
if(tmp > 0)
{
pos++;
}
else
{
pos--;
}
}
/**
Main loop. Calculates rotational speed every millisecond and prints data every 100ms
**/
void loop()
{
unsigned long currTime = micros();
unsigned long dt = (currTime - loopTime);
if(dt >= 1000)
{
printSerial++;
int tmpPos = pos;
long int deltaPos = 0;
if(prevPos != tmpPos)
{
deltaPos = tmpPos - prevPos;
AngularVelocity_rps = float(deltaPos)*(radPerCnt*1000); // This is executed every millisecond so velocity in radiansPesSecond is deltaEncoderCnts*radPerEncoderCnts*1000
}
// float error = setPoint_rps - AngularVelocity_rps;
// int cmd = kp*error; // Calculate motor command
// cmd = constrain(cmd,-255,255);
// setMotorCommand(cmd);
setMotorCommand(100); // Giving a test command that is ~40% max command
prevPos = tmpPos;
loopTime = currTime;
if(printSerial >= 500)
{
Serial.print(dt);
Serial.print(" ");
Serial.println(tmpPos);
printSerial = 1;
}
}
else
{
// Do nothing
}
}
Your interrupt is on one transition(RISING) on one pin(enA).
You should expect to see 1/4 of the total quadrature transitions available. I think that the spec of 360 cpr is for all four transitions.
int tmpPos = pos;
Because you have a multi byte variable and you don't want to access it while changing, this reading of the value is best done in a protected state with interrupts disabled.
When I manually spin the wheel one rotation, I see the pos value goes to around 12,240 which is what I expect(350cpr x 34). So I think the way my pos is incremented on the RISING edge is still valid. Isn't it ? Also, if I were incrementing my counter only 25% of the times by such using the rising edge, I would be seeing a lower than expected values. My case is opposite of that.
If I disable the interrupts while storing that value, isn't that risky? Will I get garbage if the interrupt ends up changing the variable when my code is on that line ? I seem to be seeing pretty consistent values, just that they are very high from what I expect.
The RPM that I cam calculating for the output shaft with 0.25 duty cycle is around 1250RMP. And I don't think the motor is even rated to spin that fast. Also, of course looking at it spin also confirms that it is not spinning at 1250 RPM
Your calculations may be varied by the information you provide, the wheel has one rotation near the motor of the tool, As you said you stated that you are using a "GA25-372 geared motor with encoders". The calculations are Amazing they are contributed with great information you have.
Also the noise of the cable when it triggerless interrupts but it does not work it could work as well but I think you should try the motor again and see what helps.
I think the product you came up with is smart thinking!
Yes. And I have factored the gear ratio in my constant to find the encoder cnts per revolution of the output shaft and I have verified that value by manually rotating the output shaft one revolution. Apart from the code and the motor information, is there anything else I can share to give the full picture. Currently I am trying to analyze a video of the wheel rotation to see what the actual rotation rate is. Will update. Thanks
An oscilloscope is very handy to check that there is only one interrupt per encoder transition.
"Switch bounce" can result in many interrupts per transition, which is one of the more serious problems with using interrupts. Since your motor/encoder is cheap and essentially undocumented, that is one of the the first things I would check.
Yes, but unfortunately, optical encoders are not immune to multiple states per transition. That cheap gearmotor/encoder assembly can be found in most of the usual places on the cut-rate hobby market (for example), with differing levels of inadequate documentation, so Arduino kit is not a positive recommendation.
In particular, the Seeed Studio product page documentationsuggests that the encoder outputs are OPEN COLLECTOR, and that external pullup resistors are required on each output.
Hence the quote marks around "switch bounce". I strongly recommend an oscilloscope investigation.
I figured out the issue. Thought I'll give an update. I looked at my project today after many days and it struck me. I was calculating the velocity in radians per second in the code, and just multiplying that value to get the data in RPM. Completely missed dividing by 2PI. Stupidest mistake. Now at max duty cycle, my velocity comes to about 300RPM. Thanks all for all your help. Learnt some important things from your suggestions. Now onto designing the motor controller.