QPIDfirmware  v0.1
Object oriented firmware for autonomous motor drivers with encoder based PID controller and flexible interface
QPID_Unit Class Reference

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>

Inheritance diagram for QPID_Unit:

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.
 
- Public Member Functions inherited from QPID_Object
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_MotorDriverdriver
 
QPID_Encoderencoder
 
QPID_Endstopendstop1
 
QPID_Endstopendstop2
 
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

- Protected Member Functions inherited from QPID_Object
 QPID_Object ()
 Constructor. Begins Serial with 115200 baud for logging and error reporting, if not done previously.
 

Detailed Description

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.

Todo:
implement acceleration/deceleration ramps as an alternative to PID control

Member Enumeration Documentation

◆ QPIDStates

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.

dot_inline_dotgraph_1.png
See also
getState()
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

◆ 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

Member Function Documentation

◆ processMessage()

bool QPID_Unit::processMessage ( uint8_t *  m)
overridevirtual

Interprets messages specific for this class.

Parameters
mPointer to message
Returns
true if correct comamnd was found and executed
  • 0x00: overall length of message including this byte and the final checksum byte
  • 0x01: destination, i.e. no. of QPID_Unit and its subsystem which is being adressed.
    • bits 0-3: QPID_Unit no. This allows for QPID_Unit systems with max. 16 QPID_Units
    • bits 4-6: subsystem (0 = main system; 1 = motor driver; 2 = encoder; 3 = endstop 1; 4 = endstop 2)
    • bit 7: reserved
  • 0x02: command; specific for each subsystem, no general meaning
  • 0x03: command specific parameters start here
  • 0xnn: checksum byte, nn should be length from 0x00

Reimplemented from QPID_Object.

◆ init()

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.

Todo:
how to reverse? Change polarization of encoder or motor; by software: implement PID.SetControllerDirection()
Parameters
dmotor driver object (instantiated and fully initialized)
eencoder object (instantiated and fully initialized)
es1First '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.
es2Second '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.
ciCycle 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.

◆ configureSpeedPID()

void QPID_Unit::configureSpeedPID ( double  Kp,
double  Ki,
double  Kd,
int  POn = P_ON_E 
)

Set tuning parameters for speed PID.

Parameters
Kp
Ki
Kd
POnP_ON_E for Proportional on error (default) or P_ON_M for Proportional on Measurement.
See also
http://brettbeauregard.com/blog/2017/06/proportional-on-measurement-the-code/

◆ configurePowerPID()

void QPID_Unit::configurePowerPID ( double  Kp,
double  Ki,
double  Kd,
int  POn = P_ON_E 
)

Set tuning parameters for power PID.

Parameters
Kp
Ki
Kd
POnP_ON_E for Proportional on error (default) or P_ON_M for Proportional on Measurement.
See also
http://brettbeauregard.com/blog/2017/06/proportional-on-measurement-the-code/

◆ configurePositionLimits()

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).

Parameters
minThe smallest acceptable position
maxThe biggest acceptable position

◆ configureSpeedLimits()

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.)

Parameters
minThe smallest acceptable speed, defined as encoder clicks per cycleInterval.
maxThe biggest acceptable speed, defined as encoder clicks per cycleInterval.

◆ configurePowerLimits()

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.

Parameters
minThe smallest acceptable power. Usually -255 for PWM motors.
maxThe biggest acceptable power. Usually 255 for PWM motors.

◆ configureTolerances()

void QPID_Unit::configureTolerances ( double  pos,
double  speed 
)

Set tolerances used in runTo() to determine if a positional target has been reached.

Parameters
posmax allowable distance between target and current position
speedmax speed at target

◆ runTo()

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.

Parameters
targetEncoder position at which to stop. If enabled, will be checked against and constrained by limits set with configurePositionLimits().
Todo:
(partly done) At the moment, no checks are implemented for valid positions, configured PIDs etc.

◆ runDistance()

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.

Parameters
targetencoder ticks after which to stop, sign indicates direction.If enabled, will be checked against and constrained by limits set with configurePositionLimits().

◆ runAtSpeed()

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.

Parameters
speedWell, surprise, the speed to be run at
Todo:

At the moment, no checks are implemented for valid positions, configured PIDs etc.

Implement endstops!!!

◆ runAtSpeedUntil()

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.

Parameters
speedThe speed to be run at
timeoutTime in millis() at which to stop
modeWay of stopping the system at timeout. One of QPID::brake, QPID_Unit::coast and QPID_Unit::PIDcontrolled
Todo:

At the moment, no checks are implemented for valid positions, configured PIDs etc.

Implement endstops!!!

◆ runAtSpeedFor()

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.

Parameters
speedThe speed to be run at
durationTime in millis() for which to run
modeWay of stopping the system at timeout. One of QPID_Unit::brake, QPID_Unit::coast and QPID_Unit::PIDcontrolled
Todo:

(partly done) At the moment, no checks are implemented for valid positions, configured PIDs etc.

(done) Implement endstops!!!

◆ calibrate()

void QPID_Unit::calibrate ( bool  findEndstops,
bool  findMaxSpeed,
bool  findDeadbands,
bool  findAccelerationCurve 
)

Do a test run to find system specific parameters.

Parameters
findEndstopsIf true, find position of all endstops (one or two). You can use these in minPos and maxPos accordingly.
findMaxSpeedIf true, will determine maximum attainable speed. Use configurePositionLimits() to prevent crashes.
findDeadbandsIf true, will determine the mininum power needed to making the motor move from a stopped state, and to keep it moving (see setKick())
findAccelerationCurveThe 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.
Todo:

Not implemented yet.

Where are these values saved?

Make options an enum insteat of booleans?

◆ setKick()

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:

  • (1) power demanded from the motor is different from zero
  • (2) power demanded from the motor is below the threshold;
  • (3) current speed is lower than or equal to the threshold speed, and
    See also
    disableKick()
    Parameters
    thresholdMinimum power to get the motor going from a stopped state (as defined by thresholdSpeed)
    thresholdSpeedSpeeds 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.

◆ disableKick()

void QPID_Unit::disableKick ( )

Disables kicking.

See also
setKick()

◆ getState()

QPID_Unit::QPIDStates QPID_Unit::getState ( )

Returns current state.

Returns
see QPID_Unit::QPIDStates

Member Data Documentation

◆ kickThreshold

double QPID_Unit::kickThreshold

Minimum power to get the motor going from stopped position.

See also
kicked()

◆ kickThresholdSpeed

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.

See also
kicked()

The documentation for this class was generated from the following files: