What is your code? You could back out from the 164.xx that is being printed as Input to whatever is causing it.
code is like PID controller speed control for a DC motor with encoder - #15 by zhomeslice
// This version of Tacometer auto adjusts for difference in pulses and has Stall Recovery
#include <PID_v3.h> // uses my new version of PID_v1
// Adjust these variables:
int PulsesPerRevolution = 100;// From Tachometer How Many Pulses Pur Revolution
int SampleDuration = 30; // in Milliseconds -- How often do you want to trigger the PID Lower the number the more detailed control but the more the arduino processor time is used up
double consKp = 0, consKi = 0.1, consKd = 0;
double Setpoint = 150;// RPM
#define PWMpin 12
#define interruptPin 2
#define HBridgeEnable pinMode(10, OUTPUT);digitalWrite(10, HIGH); // enables my H-Bridge Remove line 35 also
//Define Variables we'll be connecting to
double Input, Output;
//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT);
// Varables used for Calculations
volatile unsigned long timeX = 1;
unsigned long StallTimer = SampleDuration *3;
volatile int Counts = 1;
double PulsesPerMinute;
volatile unsigned long LastTime;
volatile int PulseCtrX;
int PulseCtr;
unsigned long Counter;
unsigned long Time;
void setup() {
Serial.begin(115200);
Serial.println("Test Tachometer");
PulsesPerMinute = (60 * 1000000) / (PulsesPerRevolution / Counts);// initialize
pinMode(interruptPin, INPUT_PULLUP);
pinMode(PWMpin, OUTPUT);
HBridgeEnable // Remove this if you don't need it
attachInterrupt(digitalPinToInterrupt(interruptPin), sensorInterrupt, FALLING);
myPID.SetSampleTime(1);
myPID.SetOutputLimits(0, 255); // Standard PWM Range
myPID.SetMode(AUTOMATIC);
}
void loop() {
readRpm();
/*
static unsigned long SpamTimer;
if ((unsigned long)(millis() - SpamTimer) >= 15000) {
SpamTimer = millis();
Setpoint = (Setpoint < 5000) ? 7000 : 3000;
}
*/
}
// New version of sensorInterrupt
void sensorInterrupt()
{
static int Ctr;
unsigned long Time;
Ctr++;
if (Ctr >= Counts) { // so we are taking an average of "Counts" readings to use in our calculations
Time = micros();
timeX += (Time - LastTime); // this time is accumulative ovrer those "Counts" readings
LastTime = Time;
PulseCtrX ++; // will usually be 1 unless something else delays the sample
Ctr = 0;
}
}
void readRpm()
{
static unsigned long STime;
if (!PulseCtrX) {
if ((unsigned long)(millis() - STime) >= StallTimer) {
Time = micros();
timeX += (Time - LastTime); // this time is accumulative ovrer those "Counts" readings
LastTime = Time;
PulseCtrX ++; // will usually be 1 unless something else delays the sample
Serial.print(" Stall \t");
//Setpoint +=100;
} else return; // << Added lets not stop interrupts unless we know we are ready (keep other code happy).
}
cli (); // clear interrupts flag
Time = timeX; // Make a copy so if an interrupt occurs timeX can be altered and not affect the results.
timeX = 0;
PulseCtr = PulseCtrX ;
PulseCtrX = 0;
sei (); // set interrupts flag
if (PulseCtr > 0) {
Input = (double) (PulsesPerMinute / (double)(( (unsigned long)Time ) * (unsigned long)PulseCtr)); // double has more percision
// PulseCtr = 0; // set pulse Ctr to zero
AverageCapture(Input);
debug();
myPID.Compute();
analogWrite(PWMpin, Output);
long DSDur = ((long) Time * (long)PulseCtr) - ((long) SampleDuration * 1000L);
Counts -= DSDur * .015;
Counts = max(Counts, max(1, PulsesPerRevolution * .25));
Time = 0; // set time to zero to wait for the next rpm trigger.
Counter += PulseCtr;
PulseCtr = 0; // set pulse Ctr to zero
PulsesPerMinute = (60.0 * 1000000.0) / (double)((double)PulsesPerRevolution / (double)Counts);
STime = millis();
}
}
float AvgArray[100];
int Readings = 0;
void AverageCapture(float in) {
static int Position = 0;
AvgArray[Position] = in;
Position++;
Readings++;
Readings = min (100, Readings); // 100 readings 0-99;
if (Position >= 100)Position = 0;//99 spots
}
float AverageValue() {
float Total = 0;
float Average;
if (!Readings)return (0.0);
for (int Position = 0; Position < Readings; Position++) {
Total += AvgArray[Position];
}
Average = Total / Readings;
return (Average);
}
void debug(){
char S[20];
for (static long QTimer = millis(); (long)( millis() - QTimer ) >= 100; QTimer = millis() ) { // one line Spam Delay at 100 miliseconds
Serial.print("Counts: "); Serial.print(Counts );
// Serial.print(" Target RPM: ");Serial.print(RPM );
// Serial.print(" Counts: "); Serial.print(Counts );
// Serial.print(" time: "); Serial.print(Time );
// Serial.print(" DeltaT: "); Serial.print(Time * PulseCtr);
Serial.print(" PulseCtr: "); Serial.print(PulseCtr );
// Serial.print(" PulsesPerMinute: "); Serial.print(dtostrf(PulsesPerMinute, 6, 1, S));
// Serial.print("\t Average: "); Serial.print(dtostrf(AverageValue(), 6, 1, S));
// Serial.print(" Setpoint: "); Serial.print(dtostrf(Setpoint, 6, 1, S) );
Serial.print(" Calculated RPM: "); Serial.print(dtostrf(Input, 6, 1, S));
Serial.print(" Output: "); Serial.print(dtostrf(Output, 6, 2, S));
Serial.print("\t ");
Serial.println();
}
}
My guess is that readRPM() doesn't calculate RPM during a stall:
During a stall it should be possible to upper-bound the speed using the time since the start of the sketch or time since the last encoder pulse. It's not immediately clear what the routine does when stalled.
My code guessed that a good indication of a stall was 3 times the sample duration.
if no pulses were detected within 3 sample duration then we need to trigger the stall indication
change your SampleDuration time to something larger say 100ms
int SampleDuration = 100; // in Milliseconds --
This may improve how the PID and Stall respond to your low RPM setpoint of 150
ZHomeSlice