encoder and pwm motor no control problem

I am using a 36 slot plastic wheel on shaft of motor with opto sensor.

Pro mini 16mHz.

The PWM is not changing to increase the average current when I slow down the shaft.
The motor stops, pauses, then runs full speed repeating about once every few seconds.

I have checked there is a signal to the interrupt pin.
With PID values set to zero, this is about 300 Hz, motor happily turning steadily at 500rpm.

Is there a problem with the timing of the interval between pulses?
I am not sure why the Setpoint is for 1 second worth of pulses and not the time between each slot.

I copied this section from a previous (apparently successful) posted sketch on the forum.

#include <PID_v1.h>

#define motor  3                     //motor connected on pin 3 
#define encoder  2                   //encoder connected on pin 2 
   
int pcount =0;                       //previous count

unsigned long count=0;                               
unsigned long timep, time, etime;
//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);

void setup() 
{ 
  pinMode(encoder,INPUT);
  digitalWrite(motor,HIGH);//motor control pin HIGH(mosfet off) before setting as OUTPUT
  pinMode(motor,OUTPUT);

  attachInterrupt(0,counter,RISING); //external interrupt INT0 on pin 2  
  timep=micros();
  Setpoint = 360;                    // this will be used to control the speed.360 means it should maintain 360 pulse per second 
  myPID.SetMode(AUTOMATIC);
}
  
void counter()
  {
  count++;
  }
  
  
void loop()
{
  Input = pcount; 

  myPID.Compute();
  int val=map(Output,0,255,235,0); //inverting npn mosfet driver = 255 the motor mosfet is off and 0 is full on.235 is 500rpm
  analogWrite(motor,val);
    
  time= micros();
  etime=time-timep;
  if (etime>1000000)
  {
   pcount=count;               //set previous-count to count
   count=0;                    //reset the counter every second to zero
   timep=time;                 //reset the timer
  }
}

Analog or digital signals out of your encoder? You shouldn't put analog signals into
an interrupt pin, you'll get multiple transistions, use a schmitt-trigger to clean them
up (ie 74HC14).

Hi, thanks Mark for having a think on this.
Yes, slotted opto has built-in schmitt-trigger to give clean TTL transitions.

  time= micros();
  etime=time-timep;
  if (etime>1000000)
  {
   pcount=count;               //set previous-count to count
   count=0;                    //reset the counter every second to zero
   timep=time;                 //reset the timer
  }

Can be improved in various ways:

  if (micros () - timep >= 1000000ul)
  {
    timep += 1000000ul ;
    noInterrupts () ;
    pcount = count ;
    count = 0ul ;
    interrupts () ;
  }

Use long constants for long values, increment timep for drift-free timing,
read and write count atomically so it doesn't get garbled.

Whether any of these are the issue I don't know, but you appear to have
automatic tuning, which I've no idea can cope with varying load, its normally
used for a fixed system like thermostatic control.

Try tuning the PID loop by hand.

Ah, I definitely do not want Automatic tuning.
The library page and example refer to this being possible, but not default.
I should have challenged myself to investigate what the example basic sketch had the AUTOMATIC in there for!

I changed the PID values but it just varied the weird abrupt speed changes.

I will follow up your suggestion.

Thank you.

I have setmode(MANUAL).

The output of the PID now seems to be zero and the motor does not turn at all.

I tried the default PID settings and then much higher ones.

Therefore, seems possible there is no interrupt happening.
Will have to write a blink LED sketch on INT0 and check this out.

The wild swings in motor speed would have been due to the PID AUTOMATIC setmode() trying to find velocity responses?

This short sketch works with only one clean interrupt per slot turning the shaft by hand.

void setup()
{
  pinMode(13, OUTPUT);     // Pin 13 is output to which an LED is connected
  digitalWrite(13, LOW);   // Make pin 13 low, switch LED off
  pinMode(2, INPUT);	   // Pin 2 is input to which a switch is connected = INT0

  attachInterrupt(0, blink1, RISING);

}

void loop() {
  delay(1000);  
  digitalWrite(13, LOW);   // Make pin 13 low, switch LED off
  /* Nothing to do: the program jumps automatically to Interrupt Service Routine "blink"
     in case of a hardware interrupt on Ardiono pin 2 = INT0 */
}  


void blink1(){              // Interrupt service routine
  digitalWrite(13, HIGH);
}

Trying to understand what is not going as planned with this sketch, I have inserted a monitor using the on board LED.
The PID is commented out for now.

I do not get the response I should get.
A PWM value of 25 turns the motor at about 600rpm, the measured encoder pulse frequency is about 80 in 200mS.
Therefore the LED should not be blinking.
If I slowed the motor by pressure on the shaft, yes.

#include <PID_v1.h>

#define motor  3                     //motor connected on pin 3 
#define encoder  2                   //encoder connected on pin 2 
   
int pcount =0;                       //previous count

unsigned long count=0;                               
unsigned long timep, time, etime;
//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
//PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);

void setup() 
{ 
  pinMode(encoder,INPUT);
  digitalWrite(motor,HIGH);//motor control pin HIGH(mosfet off) before setting as OUTPUT
  pinMode(motor,OUTPUT);
  pinMode(13,OUTPUT);    //LED on board
  digitalWrite(13,LOW);  //LED debugging
  
  attachInterrupt(0,counter,RISING); //external interrupt INT0 on pin 2  
  timep=micros();
  Setpoint = 60;                    // pulses in 200mS this will be used to control the speed.
 // myPID.SetMode(MANUAL);
}
  
void counter()
  {
  count++;
  }
  
  
void loop()
{
  Input = pcount; 

//  myPID.Compute();
  if (pcount < 60){
    digitalWrite(13,HIGH);
    delay(100);
    digitalWrite(13,LOW);
    delay(500); 
  }
  
    Output = 25;
  
  int val=255-Output; //inverted npn driver = 255 the motor mosfet is off and 0 is full on.
  analogWrite(motor,val);
   
    if (micros () - timep >= 200000ul)
  {
    timep += 200000ul ;
    noInterrupts () ;
    pcount = count ;
    count = 0ul ;
    interrupts () ;
  }  
 
}

Can you explain please why your suggested code has the timep += 200000ul? = 400000?

  if (micros () - timep >= 200000ul)
  {
    timep += 200000ul ;
    noInterrupts () ;
    pcount = count ;
    count = 0ul ;
    interrupts () ;
  }

Sorry, yes I understand now it's like adding a little building block on top of the existing 'timep' so it is higher next time around.

This has been solved.

Might help someone else to explain how.

Speed regulation under load is not perfect, but can use full power of motor to prevent stall and maintain smooth slow speed.

A large (like a car fan motor) DC motor with encoder on shaft and no gearbox is very tricky to control with PID.
The values have to be totally spot on, or it will hunt wildly up and down the speed range, overshooting and undershooting constantly.

I decided the Library must work, then spent many tedious hours making small progressive changes. Sort of human PID approach, Irony.

I am using AUTOMATIC, but I suppose the values could be polled from myPID and used in MANUAL.
The motor therefore goes through a short cycle before settling down each time arduino is powered up.

Now the values I put in as initial 'tuning' = code snippet here;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,0.1,0.9,0, DIRECT);

This is a 12v 3000rpm Shinko DC Servo Motor, winding resistance 0.25Ohm.
I followed the procedures found on tutorials for PID tuning, but came to the conclusion the order of tuning parameters in myPID is different or something else not making sense.
The 3rd 'damping' value has completely the opposite effect!!

Hope this conclusion is helpful.

http://www.shapeoko.com/forum/viewtopic.php?f=30&t=6311

The method of measuring the interval between encoder pulses in the above link worked much better in my case.
This may be a general approach to encoders directly fixed to the shaft.

I used exactly the same PID tuning values.
It holds the speed rpm steady at any load (within limits of current available), with very little overshoot when load is suddenly removed.