![]() |
QPIDfirmware
v0.1
Object oriented firmware for autonomous motor drivers with encoder based PID controller and flexible interface
|
A QPID_Unit objects bundles access to three hardware components, a motor driver, an position encoder, and end stop switches. Softwarewise it represents an advanced motor contoller that can autonomously perform various control tasks. More...
#include <QPID_Unit.h>
Public Types | |
enum | QPIDStates { runningToTarget, targetReached, constantSpeed, constantSpeedUntil, constantSpeedTo, timeoutReached, stoppedByEndStop, stoppedByPositionLimit, toEndStop, endStopReached, calibrating, off, stopped, haltedOnError } |
Symbolic names for QPID_Unit states that determine a QPID_Unit's behavior. The state machine depicted belwo should give a rough idea, but still needs refinement. More... | |
enum | stopMode { brake, coast, PIDcontrolled } |
Symbolic names for way to stop system in states with timeout. More... | |
Public Member Functions | |
QPID_Unit () | |
Constructor. | |
bool | processMessage (uint8_t *m) override |
Interprets messages specific for this class. More... | |
void | init (QPID_MotorDriver *d, QPID_Encoder *e, QPID_Endstop *es1, QPID_Endstop *es2, unsigned long cycleInterval) |
Attaches the neccessary and optional hardware. Motor driver and encoder are a must, one or two endstops are optional. In setups with only one endstop, you must setup it as es1. Should this not fit the directional setup (es1 is expected to be at a low encoder position, i.e. to be reached by negative motor movement) reverse your setup accordingly. More... | |
void | configureSpeedPID (double Kp, double Ki, double Kd, int POn=P_ON_E) |
Set tuning parameters for speed PID. More... | |
void | configurePowerPID (double Kp, double Ki, double Kd, int POn=P_ON_E) |
Set tuning parameters for power PID. More... | |
void | configurePositionLimits (double min, double max) |
Set position limits and enable position checking if both values are different, disable if both are the same. More... | |
void | configureSpeedLimits (double min, double max) |
Set speed limits. Will be used to limit the speedPID's output, which is fed into the powerPID. More... | |
void | configurePowerLimits (double min, double max) |
Set power limits. Will be used to limit the powerPID's output, which is fed into the motor driver. More... | |
void | configureTolerances (double pos, double speed) |
Set tolerances used in runTo() to determine if a positional target has been reached. More... | |
void | runTo (double target) |
Make motor run to target position. More... | |
void | runDistance (double distance) |
Make motor run a given distance. More... | |
void | runAtSpeed (double speed) |
Make motor run at constant speed until told otherwise. Uses only power PID for control, as the speed is fixed. PID control should take care of acceleration at the beginning. QPID_Unit::update() needs to be called repeatedly as often as possible to actually move the system. More... | |
void | runAtSpeedUntil (double speed, unsigned long timeout, stopMode mode) |
Make motor run at constant speed until given timeout is reached. Uses only power PID for control, as the speed is fixed. PID control should take care of acceleration at the beginning. QPID_Unit::update() needs to be called repeatedly as often as possible to actually move the system. More... | |
void | runAtSpeedFor (double speed, unsigned long duration, stopMode mode) |
Make motor run at constant speed for a given number of millis. Uses only power PID for control, as the speed is fixed. PID control should take care of acceleration at the beginning. QPID_Unit::update() needs to be called repeatedly as often as possible to actually move the system. More... | |
void | runAtSpeedTo (double speed, double target, stopMode mode) |
void | runAtSpeedDistance (double speed, double distance, stopMode mode) |
void | calibrate (bool findEndstops, bool findMaxSpeed, bool findDeadbands, bool findAccelerationCurve) |
Do a test run to find system specific parameters. More... | |
void | setKick (double threshold, double thresholdSpeed) |
Due to inertia and friction, DC motors don't start to run at very low speeds, they need a "kick" to get going. After that they'll continue to run even at lower speeds. By setting a kick treshold bigger than 0, power to the motor will be increased to this threshold, if all of the following conditions are met: More... | |
void | disableKick () |
Disables kicking. More... | |
QPIDStates | getState () |
Returns current state. More... | |
void | update () override |
Main activity loop. Updates speed measurement, PIDs etc. Call this as often as possible, particularly with encoders which are not hardware or interrupt driven, and for angular encoders, as the overflow wraparound implementation could fail otherwise. | |
double | kicked (double power) |
bool | outOfBounds () |
Checks hardware endstops. Also checks virtual endstops, if enabled by setPositionLimits(). Brakes and sets state accordingly if one is reached. | |
![]() | |
void | throwError (const char *invokedBy, const char *errorMessage) |
Writes error message and error invoking class to Serial. More... | |
void | log (const char *logEntry) |
Puts timestamped string into the system log (atm: writes it out to Serial). More... | |
Public Attributes | |
QPIDStates | state |
QPID_MotorDriver * | driver |
QPID_Encoder * | encoder |
QPID_Endstop * | endstop1 |
QPID_Endstop * | endstop2 |
double | kickThreshold |
Minimum power to get the motor going from stopped position. More... | |
double | kickThresholdSpeed |
Speeds lower than or equal to this value will be considered as stopped motor that needs a kick at lower speeds. Start with 0 and increase if needed. More... | |
unsigned long | nextCycle |
unsigned long | nextStatistics |
unsigned long | nextDebugInfo |
unsigned long | constantSpeedTimeout |
unsigned long | constantSpeedTarget |
stopMode | runAtSpeedStopMode |
unsigned long | cycleInterval |
double | lastPos |
double | currentPos |
double | targetSpeed |
double | targetPos |
double | currentSpeed |
double | outputPower |
PID * | speedPID |
double | speedKp |
double | speedKi |
double | speedKd |
double | minPos |
double | maxPos |
bool | usePositionLimits |
PID * | powerPID |
double | powerKp |
double | powerKi |
double | powerKd |
double | minSpeed |
double | maxSpeed |
double | minPower |
double | maxPower |
double | posTolerance |
double | speedTolerance |
uint16_t | idleUpdatesCounter |
double | maxPosStat |
double | minPosStat |
double | maxSpeedStat |
double | minSpeedStat |
double | maxPowerStat |
double | minPowerStat |
Additional Inherited Members | |
![]() | |
QPID_Object () | |
Constructor. Begins Serial with 115200 baud for logging and error reporting, if not done previously. | |
A QPID_Unit objects bundles access to three hardware components, a motor driver, an position encoder, and end stop switches. Softwarewise it represents an advanced motor contoller that can autonomously perform various control tasks.
Symbolic names for QPID_Unit states that determine a QPID_Unit's behavior. The state machine depicted belwo should give a rough idea, but still needs refinement.
Enumerator | |
---|---|
runningToTarget | running under PID control, will stop and switch state to targetReached when target reached |
targetReached | motor stopped after target has been reached after runningToTarget |
constantSpeed | running endlessly under PID control |
constantSpeedUntil | running under PID control until constantSpeedTimeout |
constantSpeedTo | running under PID control until constantSpeedTarget reached |
timeoutReached | stopped after constantSpeedUntil |
stoppedByEndStop | stopped by QPID_MotorDriver::brake() after having met an endstop during any moving state different from ::toEndStop |
stoppedByPositionLimit | stopped by QPID_MotorDriver::brake() after running out of bounds set by setPositionLimits() |
toEndStop | running at constant (slow) speed until hitting an endstop |
endStopReached | stopped by QPID_MotorDriver::brake() as expected after hitting an EndStop during ::toEndStop state |
calibrating | determine position of endstops (maxPos/minPos), maxSpeed, deadbands, and acceleration curves |
off | motor disabled, stopped and released by user or by timeout |
stopped | motor enabled, but stopped by user |
haltedOnError | stopped by controller due to emergency event |
enum QPID_Unit::stopMode |
Symbolic names for way to stop system in states with timeout.
Enumerator | |
---|---|
brake | will use QPID_MotorDriver::brake() after timout has been reached |
coast | will use QPID_MotorDriver::coast() after timout has been reached |
PIDcontrolled | will set targetSpeed to 0 and use PID to slow down system |
|
overridevirtual |
Interprets messages specific for this class.
m | Pointer to message |
Reimplemented from QPID_Object.
void QPID_Unit::init | ( | QPID_MotorDriver * | d, |
QPID_Encoder * | e, | ||
QPID_Endstop * | es1, | ||
QPID_Endstop * | es2, | ||
unsigned long | cycleInterval | ||
) |
Attaches the neccessary and optional hardware. Motor driver and encoder are a must, one or two endstops are optional. In setups with only one endstop, you must setup it as es1. Should this not fit the directional setup (es1 is expected to be at a low encoder position, i.e. to be reached by negative motor movement) reverse your setup accordingly.
d | motor driver object (instantiated and fully initialized) |
e | encoder object (instantiated and fully initialized) |
es1 | First 'lower' endstop. 'Lower' means this endstop is at a lower encoder position and will be hit if running in a negative direction. Must be used in setups with only one endstop. Use NULL if not used, a dummy (always off) endstop will be used then. |
es2 | Second 'higher' endstop (optional). 'Higher' means this endstop is at a higher encoder position and will be hit if running in a positive direction. Use this only in setups with two endstops. Use NULL if not used, a dummy (always off) endstop will be used then. |
ci | Cycle interval in milliseconds. Used as interval for speed measurement and PID updates. Make sure that the interval is short enough for the reactivity you need, but long enough for speed measurement to work with enough precision, as it is based on the number of encoder ticks per interval. |
void QPID_Unit::configureSpeedPID | ( | double | Kp, |
double | Ki, | ||
double | Kd, | ||
int | POn = P_ON_E |
||
) |
Set tuning parameters for speed PID.
Kp | |
Ki | |
Kd | |
POn | P_ON_E for Proportional on error (default) or P_ON_M for Proportional on Measurement. |
void QPID_Unit::configurePowerPID | ( | double | Kp, |
double | Ki, | ||
double | Kd, | ||
int | POn = P_ON_E |
||
) |
Set tuning parameters for power PID.
Kp | |
Ki | |
Kd | |
POn | P_ON_E for Proportional on error (default) or P_ON_M for Proportional on Measurement. |
void QPID_Unit::configurePositionLimits | ( | double | min, |
double | max | ||
) |
Set position limits and enable position checking if both values are different, disable if both are the same.
If enabled, the limits will be used (a) as input limits for runTo() to prevent targets that are out of bounds of the system, and (b) as position limits or 'virtal endstops' in constant speed states to prevent the system from running out of bounds.
Position checking is disabled by default. Can be disabled by the user by setting both to the same value (e.g. 0).
min | The smallest acceptable position |
max | The biggest acceptable position |
void QPID_Unit::configureSpeedLimits | ( | double | min, |
double | max | ||
) |
Set speed limits. Will be used to limit the speedPID's output, which is fed into the powerPID.
Both values should be determined empirically as they will depend on many parameters of your setup (type of motor, motor gears, voltage, friction and inertia, encoder resolution etc. pp.)
min | The smallest acceptable speed, defined as encoder clicks per cycleInterval. |
max | The biggest acceptable speed, defined as encoder clicks per cycleInterval. |
void QPID_Unit::configurePowerLimits | ( | double | min, |
double | max | ||
) |
Set power limits. Will be used to limit the powerPID's output, which is fed into the motor driver.
min | The smallest acceptable power. Usually -255 for PWM motors. |
max | The biggest acceptable power. Usually 255 for PWM motors. |
void QPID_Unit::configureTolerances | ( | double | pos, |
double | speed | ||
) |
Set tolerances used in runTo() to determine if a positional target has been reached.
pos | max allowable distance between target and current position |
speed | max speed at target |
void QPID_Unit::runTo | ( | double | target | ) |
Make motor run to target position.
Uses two PIDs for control, one for speed and one for power. PID control will take care of acceleration at the beginning and deceleration at the target. QPID_Unit::update() needs to be called repeatedly as often as possible to actually move the system.
target | Encoder position at which to stop. If enabled, will be checked against and constrained by limits set with configurePositionLimits(). |
void QPID_Unit::runDistance | ( | double | distance | ) |
Make motor run a given distance.
Uses two PIDs for control, one for speed and one for power. PID control will take care of acceleration at the beginning and deceleration at the end. QPID_Unit::update() needs to be called repeatedly as often as possible to actually move the system.
target | encoder ticks after which to stop, sign indicates direction.If enabled, will be checked against and constrained by limits set with configurePositionLimits(). |
void QPID_Unit::runAtSpeed | ( | double | speed | ) |
Make motor run at constant speed until told otherwise. Uses only power PID for control, as the speed is fixed. PID control should take care of acceleration at the beginning. QPID_Unit::update() needs to be called repeatedly as often as possible to actually move the system.
speed | Well, surprise, the speed to be run at |
At the moment, no checks are implemented for valid positions, configured PIDs etc.
Implement endstops!!!
void QPID_Unit::runAtSpeedUntil | ( | double | speed, |
unsigned long | timeout, | ||
stopMode | mode | ||
) |
Make motor run at constant speed until given timeout is reached. Uses only power PID for control, as the speed is fixed. PID control should take care of acceleration at the beginning. QPID_Unit::update() needs to be called repeatedly as often as possible to actually move the system.
speed | The speed to be run at |
timeout | Time in millis() at which to stop |
mode | Way of stopping the system at timeout. One of QPID::brake, QPID_Unit::coast and QPID_Unit::PIDcontrolled |
At the moment, no checks are implemented for valid positions, configured PIDs etc.
Implement endstops!!!
void QPID_Unit::runAtSpeedFor | ( | double | speed, |
unsigned long | duration, | ||
stopMode | mode | ||
) |
Make motor run at constant speed for a given number of millis. Uses only power PID for control, as the speed is fixed. PID control should take care of acceleration at the beginning. QPID_Unit::update() needs to be called repeatedly as often as possible to actually move the system.
speed | The speed to be run at |
duration | Time in millis() for which to run |
mode | Way of stopping the system at timeout. One of QPID_Unit::brake, QPID_Unit::coast and QPID_Unit::PIDcontrolled |
(partly done) At the moment, no checks are implemented for valid positions, configured PIDs etc.
(done) Implement endstops!!!
void QPID_Unit::calibrate | ( | bool | findEndstops, |
bool | findMaxSpeed, | ||
bool | findDeadbands, | ||
bool | findAccelerationCurve | ||
) |
Do a test run to find system specific parameters.
findEndstops | If true, find position of all endstops (one or two). You can use these in minPos and maxPos accordingly. |
findMaxSpeed | If true, will determine maximum attainable speed. Use configurePositionLimits() to prevent crashes. |
findDeadbands | If true, will determine the mininum power needed to making the motor move from a stopped state, and to keep it moving (see setKick()) |
findAccelerationCurve | The relation of power fed to the motor and actual speed is usually not linear. This calibration will determine the relation empirically, so that it can be used to help the powerPID in adjusting speed as demanded. |
Not implemented yet.
Where are these values saved?
Make options an enum insteat of booleans?
void QPID_Unit::setKick | ( | double | threshold, |
double | thresholdSpeed = 0 |
||
) |
Due to inertia and friction, DC motors don't start to run at very low speeds, they need a "kick" to get going. After that they'll continue to run even at lower speeds. By setting a kick treshold bigger than 0, power to the motor will be increased to this threshold, if all of the following conditions are met:
threshold | Minimum power to get the motor going from a stopped state (as defined by thresholdSpeed) |
thresholdSpeed | Speeds lower than or equal to this value will be considered as stopped state that needs a kick at lower speeds. Start with 0 and increase if needed. |
void QPID_Unit::disableKick | ( | ) |
Disables kicking.
QPID_Unit::QPIDStates QPID_Unit::getState | ( | ) |
Returns current state.
double QPID_Unit::kickThreshold |
Minimum power to get the motor going from stopped position.
double QPID_Unit::kickThresholdSpeed |
Speeds lower than or equal to this value will be considered as stopped motor that needs a kick at lower speeds. Start with 0 and increase if needed.