Electronic – arduino – Motor fader PID control

arduinocontrol systemmotor controllerpid controller

I'm trying to control a motorized fader (linear slide potentiometer) using an Arduino.
PID control gives good results for "jumping" to a specific target position, but tracking ramps is a problem, it's not smooth at all. The movement is very jerky, no matter what I try.

Here's a plot of the reference position, the measured position and the motor output when tracking a ramp:
Tracking a ramp

And here's a video of that same test.

On commercial systems, it seems much smoother, see this.

Details:
The motor fader is an Alps RSA0N11M9A0K. To drive it, I'm using an ST L293D H-bridge, powered by a regulated 10 V DC power supply (XL6009).
On the Arduino UNO (ATmega328P), I'm using pins 9 and 10, with a PWM frequency of 31.372 kHz to make it inaudible (Timer1 with a prescaler of 1, TCCR1B = (TCCR1B & 0b11111000) | 0b001).
The potentiometer is wired between ground and 5V, with the wiper going to ADC0, as usual.

The controller:
I'm using a simple PID controller with anti-windup, that updates at a rate of 1 kHz (Ts = 1e-3 s):

float update(int16_t input) {
  int16_t error = setpoint - input;
  int16_t newIntegral = integral + error;
  float output = k_p * error 
               + k_i * newIntegral * Ts 
               + k_d * (input - previousInput) / Ts;

  if (output > maxOutput)
    output = maxOutput;
  else if (output < -maxOutput)
    output = -maxOutput;
  else
    integral = newIntegral;

  previousInput = input;
  return output;
}

The output of the controller is a value from -127 to 127. The PWM output is generated as follows:

const int8_t knee = 48;

uint8_t activation(int8_t val) {
  if (val == 0)
    return 0;
  else {
    return map(val, 0, 127, 2 * knee, 255);
  }
}

void writeMotor(int8_t val) {
  if (val >= 0) {
    analogWrite(forward, activation(val));
    digitalWrite(backward, 0);
  } else {
    analogWrite(backward, activation(-val));
    digitalWrite(forward, 0);
  }
}

I added 48 to the 7-bit PWM signal, because that's where the motor starts moving at 31 kHz, and then I scale it up to an 8-bit number (because that's what the analogWrite function expects):
PWM-speed

What I've tried:
I've tried adding an EMA filter to the input, to the control signal, to the derivative component of the PID controller, but no avail. I also tried lowering the resolution of the analog input, using hysteresis to stop it from flipping between two values when stationary. This doesn't seem to affect anything.
Increasing the time step to 10 ms doesn't seem to help either.

I've also tried doing a system identification in MATLAB, and tried tuning it in Simulink (following this video series). I got a model with a fit of 91%, but I didn't know how to deal with the input and output non-linearities of the MATLAB model, how they affect the PID tuning, and how to implement it on the Arduino.

The final thing I've tried is making two different controllers: one for large jumps in the reference position, and one for small errors when tracking a ramp. This seems to help a bit, because then I can increase the integral coefficient when tracking, without increasing the overshoot when jumping.
However, by increasing the integral (and proportional) gain, the motor is now always doing something, even when it should be stationary and the reference doesn't change. (It doesn't really move, but you can feel it vibrating.)
I have virtually no derivative gain, because increasing it higher than 1e-4 seems to make it even jerkier, and I don't really notice any difference between 0 and 1e-4.

My guess is that it needs more power to overcome static friction, then the dynamic friction is less, so it overshoots, so it drives the motor backwards, causing it to stop again, then it has to overcome static friction again, it shoots forward again, etc.

How do commercial controllers overcome this problem?

My background:
I'm in my third bachelor year of Electrical Engineering, I've followed courses on control theory, digital signal processing, LQR control etc. so I have some theoretical background, but I'm having trouble applying all those theories to this real-world system.


Edit:
I've tested the open-loop sensor measurements, as laptop2d recommended, and I'm quite surprised with the results: At high PWM frequencies, there are nasty peaks in the readings. At 490 Hz, there are none.
And this is at a constant duty cycle, so I can't imagine what kind of noise I get when the motor is reversing direction very quickly.

enter image description here

So I'll have to find a way to filter that noise out before I start working on the controller again.

Edit 2:
Using an exponential moving average filter was not enough to filter out the noise.

EMA

I've tried with poles in 0.25, 0.50 and 0.75. Small poles didn't have much effect, and larger poles added too much latency, so I had to lower the gains to keep it stable, resulting in worse overall performance.

I've added a 0.1 µF capacitor across the potentiometer (between wiper and ground), and that seems to clean it up.

For now, it works well enough. In the meantime, I'm reading through the paper posted by Tim Wescott.
Thank you all for your help.

Best Answer

A control system is only as good as it's sensor, run the sensor open loop and remove the control input. Create your own input to the sensor and slide it slowly (or find a way to slide it slowly reliably) while taking position data to make sure it's not the sensor. If the sensor is noisy, then improve the performance of the sensor by getting a new sensor or paralleling it, or by filtering the output of the sensor. You may need a sensor with higher resolution.

If the sensor is not noisy then you'll need to get a different control loop. PID's are first order systems and not really great at rate control.