Motor not moving on PWM from PID

I wrote a program for position control of DC motor

/*******************************************************************************
 *program to control dc motor using rotary encoder with 2 interuppts
 *Utilises PID for controlling motor
 *From PIDtune in MATLAB Kp = 0.0790, Ki = 0.0067604, Kd = 0.0016901
 *Program written for Arduino Uno
 *******************************************************************************/

#define encoder0PinA 2
#define encoder0PinB 3
#define motorACW 5
#define motorCW 6

#include <PID_v1.h>

volatile unsigned int encoder0Pos = 0, Error;

double Setpoint, Input, Output;  //Define variables we will be connecting to
PID motorControl(&Input, &Output, &Setpoint, 0.079042, 0.0067604, 0.0016901, DIRECT);  //Specify the links and initial tuning parameters

void setup() {
  pinMode(encoder0PinA, INPUT); 
  pinMode(encoder0PinB, INPUT);
  pinMode(motorCW, OUTPUT);
  pinMode(motorACW, OUTPUT);
  
  attachInterrupt(0, doEncoderA, CHANGE);  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(1, doEncoderB, CHANGE);  // encoder pin on interrupt 1 (pin 3)
  
  Input = encoder0Pos;
  Setpoint = 1000;
  motorControl.SetMode(AUTOMATIC);
  motorControl.SetOutputLimits(100,255);

  Serial.begin (9600);
}

void loop(){
  Input = encoder0Pos;
  motorControl.Compute();
  Error = Setpoint - encoder0Pos;
   
  if (Error > 0){
    analogWrite(motorCW,Output);
    digitalWrite(motorACW,LOW);
  }
 
  else if (Error < 0){
    digitalWrite(motorCW,LOW);
    analogWrite(motorACW,Output);
  }
}

void doEncoderA(){

  if (digitalRead(encoder0PinA) == HIGH) // look for a low-to-high on channel A
  { 
    if (digitalRead(encoder0PinB) == LOW) // check channel B to see which way encoder is turning
    {  
      encoder0Pos = encoder0Pos + 1;         // CW
    } 
    else 
    {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }

  else   // must be a high-to-low edge on channel A                                       
  { 
      if (digitalRead(encoder0PinB) == HIGH)   // check channel B to see which way encoder is turning
    {   
      encoder0Pos = encoder0Pos + 1;          // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
  Serial.println (encoder0Pos, DEC);
}

void doEncoderB(){
  
  if (digitalRead(encoder0PinB) == HIGH) // look for a low-to-high on channel B
  {   
    if (digitalRead(encoder0PinA) == HIGH) // check channel A to see which way encoder is turning
    {  
      encoder0Pos = encoder0Pos + 1;         // CW
    } 
    else 
    {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }
  
  else // Look for a high-to-low on channel B
  { 
    if (digitalRead(encoder0PinA) == LOW) // check channel B to see which way encoder is turning
    {   
      encoder0Pos = encoder0Pos + 1;          // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
}

To check whether motor is giving proper response to PWM signals or not I wrote a simple code mentioned below

//written for uno
#define motorCW 5
#define motorACW 6

void setup() {
  pinMode(motorCW, OUTPUT);
  pinMode(motorACW, OUTPUT);
  }

void loop() {
  digitalWrite(motorCW,LOW);
  analogWrite(motorACW,150);
  }

If I give pwm value below 150, motor doesn't moves properly and refuses to move below pwm value 100. Motor simply hums and the TIP 122/127 (in the h bridge circuit) heats up.

Using Pitmann motor GM9236 (Datasheet attached). Circuit of H - Bridge is attached

Is the problem due to H - Bridge not able to supply enough current?
What if I use motorControl.SetOutputLimits(150,255)? Will I get accurate motion?

Design21modified.jpg

9230_dc_motor.pdf (829 KB)

Yes, that H-bridge design will work well as a heater, but so long as it
has heatsink(s) and doesn't get above 100C it doesn't mean anything is
wrong.

It will be wasting 3 to 5V in the poorly designed output drivers BTW, so its
not going to work at all for a 6V supply, and perform v. poorly at 12V.

I cant see that you are feeding your PID Output to the motor. Cant see that you are using PID Output at all.

/T

Using Darlingtons in an emitter-follower is, um, quite wasteful - the output can never approach the supply rails because of the doubled Vbe drop. They work much better when acting as common-emitter, ie with the NPN at the bottom and with inverted drive for the PNP at the top.

Better yet, use FETs.

Worse still, you are running it I think in fast-decay mode (the whole power supply is expressed across the motor inductance via the diodes while the transistors are off). If you want it to run at low power, you probably want slow-decay mode. Requires some rewiring.

Anyway, I think what you're finding is that the motor has friction and will not turn at half power. You could set the PID limits to (150,255) and put a special case in there so that you write zero to the motor when the PID output is at its minimum.

I decreased the frequency of PWM by including following code in void setup() TCCR0B = TCCR0B & 0b11111000 | 0x05;
This decreases the frequency of PWM to 61 Hz and made a minor change in circuit (circuit attached)
Motor is able to run at pwm value 50 and above. But it vibrates alot. The good part is, TIP is not heating. :slight_smile:

What other changes can I make?

Design21modified.gif

Does the PID loop not work then? With an integral term it should cope with friction.

You want several kHz PWM for a motor of that size, possibly ultrasonic to avoid
audible noise. However those Darlingtons aren't fast to switch off, MOSFETs are
a far better bet.

You do NOT want to put a big capacitor across the motor. Doing so will clamp the motor voltage and cause the H-bridge to dissipate much more power because there will significant voltage (Vsupply - Vcap) across the transistors.

When driving a motor with PWM, you are taking advantage of the inductance of the motor which permits instantaneous changes in voltage but forces there to be only gradual changes in current (dI/dt = V/L). If you put a C across the motor, you cannot instantaneously change the voltage without providing infinite current to the capacitor. Charging the capacitor forces the transistors to dissipate power. And that's ignoring L-C resonance effects you might get.

If you want it to work better:

  • get rid of the capacitor across the motor
  • don't use slow darlington devices as PWM switches
  • definitely don't use darlingtons as emitter-follower
  • use FETs instead (or an integrated motor-driver chip that includes FETs)

Does the PID loop not work then?

Yes the motor is running but the encoder is malfunctioning. I've posted the problem here. Speed of reading 500 cpr Rotary Encoder less - gives inaccurate results - Sensors - Arduino Forum

A big capacitor across the motor terminals will eventually destroy the H-bridge and
the capacitor!

Usually a small value of capacitance across the motor is added to reduce RFI from the
brushes arcing, perhaps 10nF to 100nF. The large capacitance should be decoupling
on the DC supply to the bridge, this can and should be lots of capacitance to reduce
radiating EMI from the supply leads, and bolster the supply when under brief overload
(such as a sudden start). Such decoupling should be in the form of multiple high
ripple-current capacitors to minimize heating of the capacitors themselves.