Go Down

Topic: Need help with PID controlled DC motor Follow Focus, tuning . (Read 2512 times) previous topic - next topic

mahouny

Hello everybody,

I'm working on PID control of DC motor for follow focus project and I getting quite good results, but still have few issues with it.

It's a closed loop system with:

1. Faulhaber 1741 CXR Permanent Magnet DC motor   ( Data sheet)

2. MC33926 Motor Driver running at 20kHz (Data sheet)

3. Two AS5601  I2C absolute angle sensors connected via I2C multiplexer (Data sheet)

One sensor(Position Feedback) is inside of the gearbox and shows the actual position of output gear's shaft.
Second AS5601 sensor (Position Input) is used in a custom made wired controller.

The problem is that with low P and D values (up to P=6 and D =2) system responds slowly.
I'm getting better reaction to controller movements from: P >=14
And I get perfect reaction without any latency but at very high P and D settings.
For example with D >=56  and D => 34.

I don't touch the I value , because motor starts oscillating when reach the set point, even at the very low I value like 0.03.

Unfortunately, (as it logically should be) the higher are P and D values the higher the "noise" from the motor.

Also I can't get the motor to stop exactly at set point.
Sometime it overshoots and sometime it undershoots it.
Because motor not always (40-50%of the time) stops at set position it doesn't stop sending the output PWM ,which creates the audible (hardly) "sparking"like sound.

This is my first PID controlled close loop system, so probably something is wrong with my sketch.

I have tested the hardware with a simple PWM from 0 to 255 ,in both directions, and motor runs with a stable and very clear PWM output signal.

But with enabled PID it's looks like the PWM signal is oscillating.
At sower PWM speed (50%) motor turns by small "steps", especially when motor is rotates in CW direction.

Here is a small video of rotating motor and PWM output signal on oscilloscope.
https://youtu.be/_vlSMbdC9UY

Could you guys, who have the experience with PID closed loop systems, check it out ?
Should the PWM output duty cycle oscillate like that ? Is it normal for PID ? or should it be stable until the motor stops ?

I understand that with incorrect and/or high P and D values output signal can oscillate. But I've spend more that a week trying to tune the system, but unfortunately still get the above issues.

Here enclosed is the PID sketch.


Any help will be highly appreciated.



mahouny

I forgot one of the most important things ;)
System runs on custom PCB with 5V Atmega328P-AU running at 16MHz.
I use Arduino Pro Mini 5V, 16MHz boot loader and Arduino IDE or Visual Micro for Atmel Studio 6 to upload my sketch.

mahouny

Apparently I'm on my own with that problem.

I will learn more about the PID and tune it...

avr_fred

You said in the first post "Here enclosed is the PID sketch" so where is the code?

No code = no help

Please use code tags to surround your code when posting. This will preserve the formatting.

In general, you shouldn't need D for a simple position application. Adding D has the effect of amplifying any noise in the system which is why the D term is many times referred to the "Disaster" term. An I term of 0.03 is awfully small (fast). Making that a bigger number should slow down the response. If not, something's goofy in you code but we can tell - since you didn't post your code...

MarkT

D is essential for stability once you start to up the gain, but indeed too much will lead to jitter/noise.

Its a balancing act.  P and I both increase the tendency to oscillate, D decreases it.  Try tiny amounts
of I (0.001) - even a little will eliminate long-term inaccuracy.

It you have step-inputs then its best to gate the I term during the transient response (if the output
of the PID loop is above some threshold, don't compute the integral).  The I term will "wind-up"
otherwise and lead to overshoot and creep-back.  The I-term is mainly doing its work once you are
close to the endpoint.

The other thing to know is that the longer the period of your loop, the worse everything will be since
delays always reduce stability.  For motor positioning think 1kHz or higher for motion control
feedback loops, 10kHz or higher for current-control loops (if using an inner torque loop and current
sensing).

Another issue you have to consider is quantization of error - this means that with a fast loop the
D-values are very noisy - often you have use a smoothed estimate of speed rather than raw
differences in encoder values.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

mahouny

Hi,
thanx for the such informative reply.
The code was here as attached file which was downloaded many times.
But I think I have accidentally deleted it when edited my message.

Here is my PID code:


Code: [Select]
#include <Wire.h>

//------------ I2C Chips ------------
#define AS5601      0x36  // AS5601 HEX address
#define MUX         0x70  // PCA9544A Multiplexer HEX address
byte mux_channel = 0;      // Multiplexer channel number (0,1,2,3)
int reading = 0;
int Raw_Angle = 0;
int Angle = 0;
byte result;

//------------ I/O PINS ------------
#define Motor1DirPin    8    // Motor1 Direction Pin
#define Motor1PWM_Pin   9   // Motor1 PWM Pin

//------------ INPUTS ------------
int     Inpit1Pos = 0;     // Magnetic Rotary Encoder N°1 actual position (0-4096 value from I2C sensor)

//------------ MOTORS PARAMETERS ------------
int     Motor1Speed = 0;        // Motor1 Speed (Analog Write PWM value 0-255)
int     Motor_1Position = 0;  // Motor1 actual position (0-4096 value from Motor1 output shaft I2C sensor)


//------------ FEEDBACK SENSORS ------------
#define FB1   A6       // Motor1 Feedback Pin


double  motorSpeed = 0.0, potentiometerPosition = 100.0, shaftPosition = 100.0; //initial position in center

int     serialData = 100;
int     lastData = 100;
double  I, lastInput;
double  maxSpeed =  400.0;   // This value will be increased after testing
double  minSpeed = -400.0;   // This value will be increased after testing


//pid parameters, must be tuned

long    lastSample = 0, sampleTime = 2; //sample rate time
double  kp = 89.0; // 32 Position multiplier (gain)  17 at sample rate = 10,
double  ki = 0.0; // Intergral multiplier (gain)
double  kd = 0.0;  //12 Derivative multiplier (gain)
//double  kpSerial = 0.0; // Serial Input for Position multiplier (gain)
//double  kiSerial = 0.0; // Serial Input for Intergral multiplier (gain)
//double  kdSerial = 0.0; // Serial Input for Derivative multiplier (gain) SPEED OF REACTION



//============= SETUP ===============

void setup()
{
  //Serial.begin(115200);
  Wire.begin();
  delay(500);
TWBR = 12; // Set I2C speed to 400 khz

// Set Timer 1 PWM speed at 20kHz
TCCR1A = 0b10100000;
TCCR1B = 0b00010001;
ICR1 = 400;  
/* Timer 1 configuration
prescaler: clockI/O / 1
outputs enabled
phase-correct PWM
top of 400
PWM frequency calculation
16MHz / 1 (prescaler) / 2 (phase-correct) / 400 (top) = 20kHz */

  pinMode(Motor1DirPin, OUTPUT);  // Set as OUTPUT Motor1 Direction pin
  pinMode(Motor1PWM_Pin, OUTPUT); // Set as OUTPUT Motor1 PWM speed pin
  pinMode(FB1, INPUT);      // Set as INPUT Motor1 current feedback pin

}


//=========== MAIN LOOP ===========

void loop()
{

if (millis() - lastSample >= sampleTime)
  {
    MultiplexerChannel(0);  // Select multiplexer channel 0
    AngleSensorRead();      // Read value from position input sensor

    MultiplexerChannel(3);  // Select multiplexer channel 3
    AngleSensorRead();      // Read value from motor position feedback sensor

    motorSpeed = PID(Inpit1Pos, Motor_1Position); //calculate PID
    lastSample = millis();                        //sample time Ts

if (motorSpeed < 0.0)       // Rotate motor in CCW direction
  {
      int positiveSpeed = motorSpeed * -1; //convert to positive value
      digitalWrite(Motor1DirPin, LOW);
      analogWrite(Motor1PWM_Pin, positiveSpeed );
   }
 else if (motorSpeed >0.0) // Rotate motor in CW direction
   {
      int speedPositive = maxSpeed - motorSpeed;
      digitalWrite(Motor1DirPin, HIGH);          
      analogWrite(Motor1PWM_Pin, speedPositive);  //
   }
else
   {
     Serial.println("STOP MOTOR");
   }
  }

}


//==================================================//
//            SELECT MULTIPLEXER CHANNEL            //
//==================================================//

void MultiplexerChannel(byte channel)
{
  byte controlRegister = 0x04;
  controlRegister |= channel;
  mux_channel = channel;
  Wire.beginTransmission(0x70);   // Multiplexer Address
  if (channel == 0xFF) {
    Wire.write(0x00);             // Unselect all channels
  }
  else {
    Wire.write(controlRegister);  // Set to selected channel
  }
  Wire.endTransmission();
}


//==================================================//
//                 READ SENSORS VALUE               //
//==================================================//

void AngleSensorRead()
{
  // Raw angle
  Wire.beginTransmission(0x36);       // Transmit to device as5601
  Wire.write(byte(0x0E));             // Sets register pointer
  result = Wire.endTransmission();    // Stop transmitting

  Wire.requestFrom(0x36, 2);          // Request 2 bytes from as5601
  Raw_Angle = Wire.read();            // Receive high byte
  Raw_Angle = Raw_Angle << 8;         // Shift high byte to be high 8 bits
  Raw_Angle |= Wire.read();           // Receive low byte as lower 8 bits

  switch (mux_channel)
  {
    case 0:    
      Inpit1Pos = Raw_Angle;          // Assign the value from Input Rotary Encoder1 on Ch0      
      break;
    case 3:
      Motor_1Position = Raw_Angle;    // Assign the value from Motor1 angle sensor on Ch3
      break;
    default:
      // Nothing to do here
      break;
  }

}


//==================================================//
//                 PID SPEED CALCULATION            //
//==================================================//

/////////////////////////////////////////////////////////
///Function name: PID
///Return value: calculated motor speed
///Parameters: target position, sensor data
///Description: calculated motor speed using PID algorithm
//////////////////////////////////////////////////////////

double lastError=0;
long errorTime=0;
double PID(double inputPosition, double sensorInput) {

//inputPosition = mapInterval(inputPosition, 40, 3890, 0, 4096);  // NOT OVERSHOTS:  0, 4096, 20, 4096  - If lower first max value it works like gear ratio for ex. 1:2
double error = inputPosition - sensorInput; // Error value



if (error<0) //negative error
  {
if(lastError<error)
  {
  errorTime=millis();
  }
  lastError=error;
  }
else if(lastError<0)
  {
lastError=0; //reset
  }

  //calculating PID values
  double P = kp * error;                //P
  I += (ki * error);
  if (I > 4096) I = 4096;
  else if (I < -4096) I = -4096;        //I
  double D = (sensorInput - lastInput);
  D = kd * D;                           //D

  double motorSpeed = P + I - D;    // calculated motor speed
  if(motorSpeed<0)
  {
    motorSpeed=motorSpeed*-1;
    if(motorSpeed>4096)motorSpeed=4096;
    motorSpeed = mapInterval(motorSpeed, 0, 4096, 0, maxSpeed);  // Input position from input encoder 0-4096
    motorSpeed=motorSpeed*-1;
  }
  else
  {
    if(motorSpeed>4096)motorSpeed=4096;
    motorSpeed = mapInterval(motorSpeed, 0, 4096, 0, maxSpeed);  // Input position from input encoder 0-4096
  }
  
  if (motorSpeed > maxSpeed) motorSpeed = maxSpeed;   //checking limits
  else if (motorSpeed < minSpeed) motorSpeed = minSpeed;

  lastInput = sensorInput;   //remember for next time

  return motorSpeed;
}


/////////////////////////////////////////////////////////
///Function name: mapInterval
///Return value: new value
///Parameters: val in interval A-B mapped to interval C-D
///Description: mapping value from interval A-B to inerval C-D
//////////////////////////////////////////////////////////
long mapInterval(long val, long A, long B, long C, long D) {
  long y = (long)((val - A) * (D - C));
  y /= (B - A);
  y += C;
  return y;
}





I've set the speed limits to: -400 to 400, because I was told that at 20KHz PWM I should use not the default analog.write 255 max value, but 400.

Also with 20KHz PWM if I set the max speed to 255, the "micro steps" in CW direction immediately appears.

Maybe something is wrong in speed calculation...

Could you guys check it out ?


By the way, there was many Serial.print (@ 115200) and serial.read statements in my code.
For visual output of : speed, position, error values and to change PID values "on the fly" via terminal.

I have cleaned the whole code and removed everything not related to PID / sensors and motor.

It seems to be better now.
At least, now I don't see those weird "steps" with same : -400 to 400 speed.
There still some fast jitter at the set point, however.

I think that now I need to tune the system again, from the very beginning.

I will make the line chart in OpenOffice spreed-sheet, and collect: Input-Output-Error data from 1-2 big movements, trough serial.print.
This will help me to visualize my settings and see what have to be adjusted.

Regards

MorganS

This looks very wrong:
Code: [Select]
      int speedPositive = maxSpeed - motorSpeed;

What are you trying to do here? You can't write anything bigger than 255 to analogWrite() so it's basically going to always drive at 255 and nothing slower. You've got no proportional control at all. No wonder PID isn't working.
"The problem is in the code you didn't post."

mahouny

Hi,
that was a kind of a "patch" to avoid big jitter steps when motor rotating in CW direction.

Something is wrong with my PID calculation, because if  I set speed limits to -255 to 255
then when I rotate the "Input position" controller knob into CW direction, the speed is periodically switched from positive to negative (CW anf CCW) so motor makes "two steps forward and few steps back"
That creates a jittery movement.
Of course it also causes a huge loss of torque in CW direction.

here is a video demonstrating the problem:
https://youtu.be/qxJt88usHWE


This was tested with the previously posted code, but with:
double  maxSpeed =  255.0;   // CW rotation 0 to 255
double  minSpeed = -255.0;   // CCW rotation  0 to -255

and following loop, which includes the serial.print statements showing the direction and speed:


Code: [Select]
/=========== MAIN LOOP ===========

void loop()
{

if (millis() - lastSample >= sampleTime)
  {
    MultiplexerChannel(0);  // Select multiplexer channel 0
    AngleSensorRead();      // Read value from Input Rotary Encoder 1

    MultiplexerChannel(3);  // Select multiplexer channel 3
    AngleSensorRead();      // Read value from Motor1 angle sensor
 
    motorSpeed = PID(Inpit1Pos, Motor_1Position); //calculate PID
    lastSample = millis();                        //sample time Ts

if (motorSpeed < 0.0)       // Rotate motor in CCW direction
  {
      int positiveSpeed = motorSpeed * -1; //convert to positive value
      digitalWrite(Motor1DirPin, LOW);
      analogWrite(Motor1PWM_Pin, positiveSpeed );
      Serial.print(" CCW Speed: "); Serial.println(motorSpeed);
   }
 else if (motorSpeed >0.0) // Rotate motor in CW direction
   {
      //int speedPositive = maxSpeed - motorSpeed;
      digitalWrite(Motor1DirPin, HIGH);           
      analogWrite(Motor1PWM_Pin, motorSpeed);  //
      Serial.print("************** CW Speed: "); Serial.print(motorSpeed); Serial.println("**************");
   }
else
   {
    // Serial.println("STOP MOTOR");
   }
  }
}


Following output shows how the speed is switched, creating those "steps" :

Code: [Select]
CCW Speed: -42.00
 CCW Speed: -42.00
 CCW Speed: -42.00
 CCW Speed: -42.00
 CCW Speed: -42.00
 CCW Speed: -42.00
 CCW Speed: -39.00
 CCW Speed: -39.00
 CCW Speed: -34.00
 CCW Speed: -28.00
 CCW Speed: -20.00
 CCW Speed: -11.00
 CCW Speed: -6.00
************** CW Speed: 2.00**************
************** CW Speed: 10.00**************
 CCW Speed: -6.00
 CCW Speed: -47.00
 CCW Speed: -55.00
 CCW Speed: -47.00
 CCW Speed: -27.00
 CCW Speed: -16.00
 CCW Speed: -2.00
************** CW Speed: 5.00**************
************** CW Speed: 14.00**************
 CCW Speed: -13.00
 CCW Speed: -69.00
 CCW Speed: -54.00
 CCW Speed: -32.00
 CCW Speed: -23.00
 CCW Speed: -12.00
 CCW Speed: -4.00
************** CW Speed: 1.00**************
 CCW Speed: -1.00
 CCW Speed: -12.00
 CCW Speed: -31.00
 CCW Speed: -3.00
 CCW Speed: -9.00
************** CW Speed: 7.00**************
************** CW Speed: 15.00**************
 CCW Speed: -34.00
 CCW Speed: -61.00
 CCW Speed: -61.00
 CCW Speed: -38.00
 CCW Speed: -30.00
 CCW Speed: -10.00
 CCW Speed: -2.00
************** CW Speed: 5.00**************
************** CW Speed: 14.00**************
 CCW Speed: -35.00
 CCW Speed: -69.00
 CCW Speed: -63.00
 CCW Speed: -54.00
 CCW Speed: -35.00
 CCW Speed: -23.00
 CCW Speed: -7.00
************** CW Speed: 1.00**************
************** CW Speed: 12.00**************
 CCW Speed: -37.00
 CCW Speed: -71.00
 CCW Speed: -54.00
 CCW Speed: -34.00
************** CW Speed: 4.00**************
************** CW Speed: 10.00**************
 CCW Speed: -73.00
 CCW Speed: -53.00
 CCW Speed: -45.00
 CCW Speed: -22.00
************** CW Speed: 21.00**************
************** CW Speed: 44.00**************
************** CW Speed: 24.00**************
 CCW Speed: -42.00
 CCW Speed: -19.00
 CCW Speed: -33.00
 CCW Speed: -7.00
************** CW Speed: 19.00**************
************** CW Speed: 39.00**************
 CCW Speed: -22.00
 CCW Speed: -5.00
************** CW Speed: 37.00**************
************** CW Speed: 62.00**************
************** CW Speed: 34.00**************
 CCW Speed: -13.00
************** CW Speed: 15.00**************
 CCW Speed: -48.00
 CCW Speed: -48.00
 CCW Speed: -16.00
************** CW Speed: 25.00**************
************** CW Speed: 28.00**************
 CCW Speed: -30.00
 CCW Speed: -10.00
 CCW Speed: -24.00
************** CW Speed: 20.00**************
************** CW Speed: 37.00**************
 CCW Speed: -7.00
 CCW Speed: -32.00
 CCW Speed: -20.00
************** CW Speed: 15.00**************
************** CW Speed: 18.00**************
 CCW Speed: -70.00
 CCW Speed: -44.00
 CCW Speed: -39.00
 CCW Speed: -27.00
************** CW Speed: 14.00**************
************** CW Speed: 25.00**************
************** CW Speed: 5.00**************
 CCW Speed: -89.00
 CCW Speed: -66.00
 CCW Speed: -91.00
 CCW Speed: -62.00
 CCW Speed: -32.00
 CCW Speed: -6.00
************** CW Speed: 15.00**************
************** CW Speed: 31.00**************
 CCW Speed: -43.00
 CCW Speed: -43.00
 CCW Speed: -9.00
************** CW Speed: 9.00**************
************** CW Speed: 32.00**************
 CCW Speed: -23.00
 CCW Speed: -9.00
 CCW Speed: -11.00
************** CW Speed: 24.00**************
************** CW Speed: 2.00**************
 CCW Speed: -84.00
 CCW Speed: -78.00
 CCW Speed: -38.00
************** CW Speed: 8.00**************
************** CW Speed: 25.00**************
 CCW Speed: -50.00
 CCW Speed: -10.00
 CCW Speed: -27.00
************** CW Speed: 17.00**************
************** CW Speed: 6.00**************
 CCW Speed: -96.00
 CCW Speed: -65.00
 CCW Speed: -59.00
 CCW Speed: -3.00
************** CW Speed: 7.00**************
************** CW Speed: 35.00**************
 CCW Speed: -40.00
 CCW Speed: -17.00
 CCW Speed: -19.00
************** CW Speed: 8.00**************
************** CW Speed: 8.00**************
 CCW Speed: -78.00
 CCW Speed: -122.00
 CCW Speed: -66.00
 CCW Speed: -40.00
 CCW Speed: -18.00
************** CW Speed: 6.00**************
************** CW Speed: 17.00**************
 CCW Speed: -52.00
 CCW Speed: -40.00
 CCW Speed: -62.00
 CCW Speed: -20.00
 CCW Speed: -17.00
************** CW Speed: 4.00**************
************** CW Speed: 15.00**************
 CCW Speed: -82.00
 CCW Speed: -67.00
 CCW Speed: -81.00
 CCW Speed: -53.00
 CCW Speed: -45.00
 CCW Speed: -25.00
 CCW Speed: -3.00
************** CW Speed: 10.00**************
************** CW Speed: 24.00**************
 CCW Speed: -53.00
 CCW Speed: -95.00
 CCW Speed: -47.00
 CCW Speed: -24.00
 CCW Speed: -5.00
************** CW Speed: 14.00**************
************** CW Speed: 16.00**************
 CCW Speed: -33.00
 CCW Speed: -35.00
 CCW Speed: -35.00
 CCW Speed: -24.00
 CCW Speed: -7.00
 CCW Speed: -1.00
************** CW Speed: 17.00**************
************** CW Speed: 28.00**************
 CCW Speed: -49.00
 CCW Speed: -90.00
 CCW Speed: -51.00
 CCW Speed: -26.00
 CCW Speed: -3.00
************** CW Speed: 13.00**************
************** CW Speed: 32.00**************
 CCW Speed: -40.00
 CCW Speed: -45.00
 CCW Speed: -14.00
************** CW Speed: 7.00**************
************** CW Speed: 16.00**************
 CCW Speed: -67.00
 CCW Speed: -58.00
 CCW Speed: -58.00
 CCW Speed: -41.00
 CCW Speed: -38.00
 CCW Speed: -13.00
************** CW Speed: 3.00**************
************** CW Speed: 11.00**************
 CCW Speed: -69.00
 CCW Speed: -105.00
 CCW Speed: -54.00
 CCW Speed: -23.00
 CCW Speed: -18.00
************** CW Speed: 1.00**************
************** CW Speed: 9.00**************
 CCW Speed: -65.00
 CCW Speed: -82.00
 CCW Speed: -51.00
 CCW Speed: -23.00
 CCW Speed: -9.00

mahouny

It seems that the PID calculated speed value is almost always NEGATIVE.

Normally once input is turned into CW direction speed value also should be positive, but it is positive for very brief moment and then switched back to (-) , and then again back to (+)

mahouny

I think I'm going crazy ;)
I found the source of problem, but have no idea how fix it :(

In fact motor position sensor is mounted inside of the gearbox (worm gear 40:1) on worm wheel shaft (output shaft)

When motor is rotating CW the output shaft (with position sensor) also rotates in CW direction.
BUT, when the shaft is rotates in CW position PID calculation command motor to rotate into CCW direction.
This is why in CW direction motor makes "one step forward and two steps back"


MarkT

You seem to be running your control loop at 500Hz.  Try much higher rate, certainly 5kHz or more is
a good starting point.  The longer the latency in your loop the greater the tendency to oscillate.  Your
output is 20kHz PWM so you have the ability to twiddle the output at 20kHz rate so use it!

How prompt is the info from an AS5601? - I've used the AS5040 myself, and that is pretty slow (96us
sample period).  No feedback loop can perform better than its position sensor. 

Also if you are using step-type inputs I forgot to mention feed-forward, which can help with transient
handling by doing most of the work for the transient and leaving the PID loop to pick up the crumbs.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

mahouny

I think I found the problem with the jitter movement.

I forgot, that when direction PIN is switched, the max and min values should be inverted.
So in one direction the max speed is 255 (400 in my case) and in other direction the max speed is : 0 (zero)
Now that part of the code looks like that:

Code: [Select]
double motorSpeed = P + I - D;    // calculated motor speed
  
  if(motorSpeed<0)
  {
    motorSpeed=motorSpeed*-1;
    if(motorSpeed>4096)motorSpeed=4096;
    motorSpeed = mapInterval(motorSpeed, 0, 4096, 0, maxSpeed);  // Input position from input encoder 0-4096

    CWdirection = 1;
    //Serial.print(" NEG  Speed: "); Serial.print(motorSpeed);
  }
  else
  {
    if(motorSpeed>4096)motorSpeed=4096;
    motorSpeed = mapInterval(motorSpeed, 4096,0,  0, maxSpeed);  // Input position from input encoder 0-4096
    CWdirection = 0;
  }

This however ONLY if i set the max PWM speed to 400.

I'm not sure why, but it seem that when Timer1 is set to 20kHz,  PWM 255 outputs slightly more than a half of maximum PWM.

@MarkT

Thank you for the advice.
I'm quite newbie in Arduino ;) could you suggest me how can I set higher frequency?
Now the whole loop takes: 780 μs.

About the AS5601:
Each time when the code reads data from AS5601, it takes  ~ 208 μs

I use a custom made magnetic encoder with AS5601 inside. Some kind of "digital potentiometer"
So both, the input and the output are the same type of sensor.


MorganS

So, is there still a problem?
Quote
This however ONLY if i set the max PWM speed to 400.
PID control is hard. The I and D terms are very difficult to get right. For something like a follow-focus you don't need particularly smart feedback loops. Try to write your own proportional controller. If the current position is x units away from the desired location, drive the motor with K*x speed, where K is a constant. Try to make it work slowly at first, then turn up K later to make it faster.
"The problem is in the code you didn't post."

mahouny

Yes indeed for the first time  PID is a bit harder than I thought it would be. :)


Thanks for the help guys. I really appreciate that.

Finally I almost perfectly tuned the loop.

I added another "digital pot" to adjust Kp, Ki and Kd values (one by one) on the fly, and printed PID values, actual position, set point and error via serial.print.
All in a single line.
As serial output was constantly updated I saw "in live mode" how the adjustment affects position error.

So I've set the Kp value first , until I got fast response without noise.
Then Ki value until it started oscillating and then Kd value until the oscillation has gone.

Of-course it would be much easier and precise with some software or some kind of line chart, but for now it's ok.

There is still only one minor bug.
Despite the fact that sensor's reading is very stable, sometime in steady position there is a very small +-1 error, which creates some audible "sparking" noise, because motor is trying to switch the direction at high frequency.
I think I'll just add an averaging filter. It should eliminate that "floating" error.




MarkT

Whoops, yes that sensor is I2C so may not be fast enough for performant motion control loops.

If you can live with low gain and slow response then do so.  An incremental encoder would be
so much better for this kind of thing - no latency beyond a few us for an interrupt handler.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up