So I installed Brett’s PID library mentioned in this thread. The documentation for the constructor looks like this:
PID()
Description
Creates a PID controller linked to the specified Input, Output, and Setpoint. The PID algorithm is in parallel form.
Syntax
PID(&Input, &Output, &Setpoint, Kp, Ki, Kd, Direction)
PID(&Input, &Output, &Setpoint, Kp, Ki, Kd, POn, Direction)
Parameters
Input: The variable we're trying to control (double)
Output: The variable that will be adjusted by the pid (double)
Setpoint: The value we want to Input to maintain (double)
Kp, Ki, Kd: Tuning Parameters. these affect how the pid will change the output. (double>=0)
Direction: Either DIRECT or REVERSE. determines which direction the output will move when faced with a given error. DIRECT is most common.
POn: Either P_ON_E (Default) or P_ON_M. Allows Proportional on Measurement to be specified.
Returns
None
So if I understand correctly, Setpoint is my pedal position, input comes from the encoder, and output goes to the PWM pin for the motor.
Here’s some test code. The first time I upload it, the motor moves briefly in response to the first movement of the pot, but subsequently does nothing. The teensy is correctly showing values from the pot. However, if I move the motor a bit (to make sure the encoder is working) it suddenly moves for a quarter second or so… So basically nothing is happening without a change in encoder values… which makes me wonder if I have the variables assigned wrong.
Edit: on startup, the encoder is moving to a value around 1000…
#include <PID_v1.h>
#include <Encoder.h>
int wiperPin = 14;
int pwmPin = 16;
int motor1pin1 = 17;
int motor1pin2 = 18;
int encoderPin1 = 8;
int encoderPin2 = 9;
int value = 0;
//Define Variables we'll be connecting to
double Setpoint, Input, Output;
//Specify the links and initial tuning parameters
PID pid1(&Input, &Output, &Setpoint,2,5,1, P_ON_M, DIRECT);
Encoder enc(encoderPin1,encoderPin2);
void debug(String var, int val) {
Serial.print(var);
Serial.print(" = ");
Serial.println(val);
}
void drive(int vel) {
//Serial.print("vel = ");
//Serial.println(vel);
if (vel > 10) {
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
analogWrite(pwmPin, vel); //ENA pin
}
else if (vel < -10) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
analogWrite(pwmPin, vel); //ENA pin
}
else {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, LOW);
}
}
void setup() {
// put your setup code here, to run once:
pinMode(motor1pin1, OUTPUT);
pinMode(motor1pin2, OUTPUT);
Serial.begin(9600); // Other baud rates can be used...
Serial.println("encoder test");
Setpoint = 0;
pid1.SetOutputLimits(-255,255);
pid1.SetMode(AUTOMATIC);
}
long encPosition = 0;
void loop() {
long newEnc;
newEnc = enc.read();
if (newEnc != encPosition) {
encPosition = newEnc;
debug("encoder",encPosition);
}
Input = encPosition;
int wiper;
wiper = analogRead(wiperPin);
if (wiper != Setpoint) {
Setpoint = wiper;
debug("wiper",Setpoint); //this value is changing 0-1024
}
pid1.Compute();
drive(Output);
Beyond that, I’m trying to get my head around what I need the PID to do actually, never mind the values for tuning it… Here are some thoughts that I don’t yet have any idea how to approach in code…
The idea is to track as closely as possible the movement of the pedal. This movement might be fast, faster than the maximum speed of the motor, so there will be some lag. But sometimes the movement will be a good bit slower than the maximum rate that the motor can change the pitch, and the algorithm should be able to follow the movement more accurately. I’m concerned that if the movement is TOO slow, the motor may struggle to move slowly enough. With narrow PWM values, the motor sure does whine loudly! Doesn’t sound good for the motors… but go too slowly, and the motor doesn’t move at all.
Both end points of the travel will be known positions, (one of which will be the startup position, zero) and I’m wondering if this information could be useful to the PID algorithm, and if so, how that is expressed…
I think I do want P_ON_M, as that is used to address overshoot, and that seems desirable in this situation. Overshoot is not going to be musical at all.
The encoder has at least 10x the accuracy (possibly 100x?) I’ll need, so I want to make sure that it doesn’t oscillate around needlessly if the error is small.