Pages: [1]   Go Down
Author Topic: Large Error Instability  (Read 1700 times)
0 Members and 1 Guest are viewing this topic.
London, ON
Offline Offline
Jr. Member
**
Karma: 0
Posts: 86
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

I am using quadrature (rotary) encoders for motion control via external interupts for a master/slave(DC motor) setup.  When I implement a large error (ie quickly moving my master encoder - or - step response).  The PWM signal go crazy and unstable.  The error between the master and slave encoder increases rather than decreases - which makes my PID controller output infinitely growing values. Also my output rate serial.print (in my main loop) decreases.  When I have a small error (moving slowly with master) everything is great.

Motor has a gear box would makes the scale between the master and slave encoders very imbalanced.  I compensated this by scalling the increments on the encoder count of the master and slave.

ex:

masterEncoder (+/-)= 2.4
slaveEncoder (+/-)= 0.2  

I feel that when there is a large error present, something happens, perhaps a change in bit on my data type or ISR issue.  I am using all floats.

If anyone has suggestions to why I am having instability with a large error, I would appreciate the help.
Let me know if this was confusing to understand.
Daniel

  
« Last Edit: November 13, 2012, 11:41:19 am by dgelman » Logged

Dialup is the future.

Left Coast, CA (USA)
Offline Offline
Brattain Member
*****
Karma: 361
Posts: 17294
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

One possibility is that your ISR function for handling encoder interrupts is too long, complex, or too slow to keep up with the interrupt rate. Without posting your code it's very difficult for anyone to help with your symptoms.

Lefty
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13734
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Please post your code then we can deduce what is happening.
What is the number of interrupts you get approx.
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

London, ON
Offline Offline
Jr. Member
**
Karma: 0
Posts: 86
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry about that, below is my code.

/*
Project:
Name: Daniel G
Date: 10/15/2012
Company:
*/

/*
EnA  |  In1  |  In2  |  Description
-----------------------------------------------
 0   |   X   |   X   |  Free Running Motor Stop
 1   |   1   |   0   |  Forward
 1   |   0   |   1   |  ReverslaveEncoder
 1   |   1   |   1   |  Fast Motor Stop
-----------------------------------------------

CHA: ____EEEE____EEEE____EEEE____EEEE
CHB: EE____EEEE____EEEE____EEEE____EE
When CHA rislaveEncoders check the state of CHB.  From here you can determine the
direction of the encoder corresponding to the Master or Slave
*/

#include <stdio.h>
int In1 = 8;          // Red
int In2 = 9;          // Orange
int EnA = 10;         // Yellow
int encoderPower = 7; // Red
int Master_CHA = 0;   // Green - Interrupt INT0 on Pin 2
int Master_CHB = 4;   // White
int Slave_CHA = 1;    // Purple - Interrupt INT1 on Pin 3
int Slave_CHB = 6;    // Teal
int LOOPCOUNTER = 0;

int Master_CHB_val = 0;
int Slave_CHB_val = 0;
volatile float masterEncoder = 0;
volatile float slaveEncoder = 0;

// Control Signal and Error
float u = 0;
float err = 0;

// PID Control
float Kp = 8;
float Ki = .002;
float Kd = .2;

// Error Variables
float err_old = 0;
float err_intgrl = 0;
float err_dot = 0;
int max_int = 200;
int max_der = 200;

void setup()
{
//  Serial.begin(9600); // Begin serial communication
 
  // slaveEncodertting Motor Pins
  pinMode(In1, OUTPUT); // Setting In1 (direction indicator)
  pinMode(In2, OUTPUT); // Setting In2 (direction indicator)
  pinMode(EnA, OUTPUT); // Setting PWM output
 
  // Temperary HIGH to encoderPower Pin to give Vcc
  pinMode(encoderPower, OUTPUT);
  digitalWrite(encoderPower, HIGH);
 
  // Set Encoder Pins & Interrupt
  pinMode(Master_CHB, INPUT);
  pinMode(Slave_CHB, INPUT);
  attachInterrupt(Master_CHA, Master_checkDirection, RISING); // CHA causes interrupt service routine
  attachInterrupt(Slave_CHA, Slave_checkDirection, RISING);   // CHA causes interrupt service routine
 
  TCCR1B = TCCR1B & 0b11111000 | 0x02; // PWM frequency changed from 500Hz to 3.922KHz - L298 typically can support 25KHz.
}

// ISR for Master (Controller)
void Master_checkDirection()
{
  Master_CHB_val = digitalRead(Master_CHB);        // When CHA RAISES check CHB
  if (Master_CHB_val == 0)  masterEncoder += 1.4;    // Increment Master count for a direction
  else masterEncoder -= 1.4;                         // Decrement Master count for the other direction   
}

// ISR for Slave (Motor)
void Slave_checkDirection()
{
  Slave_CHB_val = digitalRead(Slave_CHB);           // When CHA RAISES check CHB
  if (Slave_CHB_val == 0)  slaveEncoder += 0.6;    // Increment Slave count for a direction
  else slaveEncoder -= 0.6;                        // Decrement Slave count for the other direction
}

// Function to Set Direction of PWM
void setDirection(boolean In1_State, boolean In2_State)
{
  digitalWrite(In1, In1_State);
  digitalWrite(In2, In2_State);
}

void loop()
{
  err = slaveEncoder - masterEncoder;
//  LOOPCOUNTER++;
 
  if (err < 0)  setDirection(LOW, HIGH);
  else if (err > 0)  setDirection(HIGH, LOW);

  err_dot = err - err_old;
  if (err_dot > max_der) err_dot = max_der;
  else if (err_dot < -max_der)  err_dot = -max_der;

  err_intgrl = err + err_intgrl;
  if (err_intgrl > max_int)  err_intgrl = max_int;
  else if (err_intgrl < -max_int)  err_intgrl = -max_int;

  u = (float)(abs(Kp*err + Kd*err_dot + Ki*err_intgrl));
  constrain(u, 0, 255);
 
  analogWrite(EnA, u); // Write PWM value as the encoder count of the Master (controller)
//  if (LOOPCOUNTER == 1200) 
//  {
//    Serial.print(masterEncoder);
//    Serial.print(" || ");
//    Serial.print(slaveEncoder);     
//    Serial.print(" || ");
//    Serial.println(err); 
//    LOOPCOUNTER = 0;
//  }
  err_old = err;
}
Logged

Dialup is the future.

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 208
Posts: 8855
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
 constrain(u, 0, 255);

Since constrain() returns the constrained value you probably want:

Code:
 u = constrain(u, 0, 255);

It might work better is you calculate the PID before you set the direction:
Code:
  err_dot = err - err_old;

  err_intgrl = err + err_intgrl;

  int PID = Kp*err + Kd*err_dot + Ki*err_intgrl;
  constrain(PID, -255, 255);

 if (PID < 0) {
    setDirection(LOW, HIGH);
    PID = -PID;
  }
  else
    setDirection(HIGH, LOW);

  analogWrite(EnA, PID);
« Last Edit: November 13, 2012, 04:46:50 pm by johnwasser » Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

London, ON
Offline Offline
Jr. Member
**
Karma: 0
Posts: 86
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello John,

Thank you for your input.  Your approach seems much cleaner but unfortunately, I am still getting the same results. 

I feel that if I exceed my PID control signal to much, it goes unstable. By providing a step response the PID signal is too high (high err) in which causes instability.  Contraining the signal is not helping for some reason.  During instability (after a step response - my serial monitor just shows a repeating 255 or -255, never decreasing.

I constrained the maximum errors but I still am getting the same weird results.

Code:
  err_dot = err - err_old;
  err_dot = constrain(err_dot, -max_der, max_der);

  err_intgrl = err + err_intgrl;
  err_intgrl = constrain(err_intgrl, -max_int, max_int);


The problem I feel is the gear box on the DC motor.  Because the scaling is not 1:1 - as the maximum PWM signal is set, the slaveEncoder goes to infinite because the gearbox is sending encoder positions in the thousands.  I am trying to compensate for this with the scaling the encoder count in Slave_checkDirection() and Master_check Direction() but it is only efficient until a certain point. 

Your thoughts?
Logged

Dialup is the future.

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 208
Posts: 8855
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

What values of 'err' are you getting?
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

London, ON
Offline Offline
Jr. Member
**
Karma: 0
Posts: 86
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Notice below the err values are just fine when I move slowly, but applying a large instantaneous motion to my masterEncoder (essentially a step response).  The err builds up exponentially starting at -12668.67.

Code:
-6.23
-1.12
-1.12
-1.12
6.37
6.96
2.14
-5.98
-0.60
8.68
15.87
5.08
-2.71
-3.31
-1.20
0.60
4.20
7.80
5.70
-0.30
-0.30
-0.30
-1.80
-1.80
-2.10
-5.70
-12.01
-11.12
6.57
-4.51
-11.69
-9.27
-8.96
-0.85
-0.85
-0.85
-35.35
-12668.67
-25233.88
-33518.90
-42608.51
-52169.74
-61758.05
-71490.49
-81504.08
-91523.61
-101471.30
-111362.58
Logged

Dialup is the future.

the land of sun+snow
Offline Offline
Faraday Member
**
Karma: 159
Posts: 2916
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
u = (float)(abs(Kp*err + Kd*err_dot + Ki*err_intgrl));

I would take the PID signals apart, and see which one is causing the trouble.

IE, set Kd=Ki=0 initially.
Logged

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 208
Posts: 8855
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
u = (float)(abs(Kp*err + Kd*err_dot + Ki*err_intgrl));

I would take the PID signals apart, and see which one is causing the trouble.

IE, set Kd=Ki=0 initially.

Since it is the error term itself (err) that is running away I don't think the PID parameters will matter.

Looks like the error term is jumping about 10,000 to 12,000 at a time.  How many pulses per second are you expecting?  Since the Master counts up by 1.4 you are either getting 9,000 pulses in a small fraction of a second (there are no delays in the PID loop) or the floating-point math is going wrong.

About the fact that there are no delays...  perhaps it would work better if you updated the PID loop at fixed intervals.  Say 100 times a second (every 10 milliseconds).

I would also consider working in integers, particularly in the ISR's, because floating-point math is so time consuming.
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

the land of sun+snow
Offline Offline
Faraday Member
**
Karma: 159
Posts: 2916
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Since it is the error term itself (err) that is running away I don't think the PID parameters will matter.

Probably so, but it's all a big feedback loop, so everything may contribute.
Logged

London, ON
Offline Offline
Jr. Member
**
Karma: 0
Posts: 86
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Even with Kp, I am experiencing this problem.  I do not think it has something to do with my PID parameters.  I personally think it has something to do with the scaling between master and slave.  The gearhead on the motor has a reduction radio of 377:1 while my master is 1:1.  This difference in scales (I believe) should cause some issue.

But, I also feel it could be ISR and floating point math - because even with a scaling ratio the PWM shouldn't go on to infinite.

In regards to your question, "How many pulses per second are you expecting?" I believe that is coming from the large amount of pulses from the slave end with the gearbox.  Because of the gearbox, there are tons of pulses coming in - especially when the motor exceeds maximum speed.

This is unnecessary frustration smiley-sad

Daniel

EDIT: johnwasser, maximum PWM signal is given (100%) - as this happens when the err goes through the roof.  I get pulses coming back at ~43Khz.  The Arduino should be able to process the ISR at this frequency, can it not?  Perhaps givening maximum PWM signal produces too quick of a interupt frequency in which the processor cannot keep up?

« Last Edit: November 15, 2012, 02:25:24 pm by dgelman » Logged

Dialup is the future.

the land of sun+snow
Offline Offline
Faraday Member
**
Karma: 159
Posts: 2916
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
if (err_intgrl > max_int)

err_intgrl = constrain(err_intgrl, -max_int, max_int);

I suppose these both work properly with mixed data types, but I probably wouldn't do it that way,
rather make all quantities = floats, or use casts.

Quote
This is unnecessary frustration

LOL, the longer you're an engineer, the more you realize it's really more a matter of 'delayed gratification',
and not something else.

Think of the guys who built the Curiosity mars rover. They worked on that thing open-loop for 10 years,
with only a wing and a prayer that the thing wouldn't crash on landing, due to the hyper complicated
landing scheme they came up with. Or else die in flight for one of 10,000 other reasons.

Logged

Pages: [1]   Go Up
Jump to: