project.controller - A controller for Fatigue test bench
The repository holds the code of the firmware for a test bench. The bench tests pressure accumulator with respect to fatigue. The bench performs a sequence of square wave reference between an upper an lower pressure (PI controller) and maintains the temperature using a chiller and a resistance.
The firmware is composed by several systems that cooperates in order to control the hardware device:
- a pressure controller (
PresControl.h
) - a temperature controller (
TempControl.h
) - a storage system (
Storage.h
) - a serial interface parser (
SerialParser.h
)
The main setup
and loop
operates on a state machine, and are in the file arduino.ino
, the core file to be compiled and uploaded
to the controller board. The static configuration and defaults are defined in Config.h
.
Emergency Button
It is possible to connect an emergency button to the controller. When the button is pressed an interrupt that puts the controller in the alarm state is called.
The settings for the emergency button are:
EMERGENCYBTN_PIN
: Pin for emergency button, must have Interrupt capabilitiesEMERGENCYBTN_MODE
: Default mode for emergency button signal: valid are RISING, LOW, CHANGE, FALLING. Check on Arduino attachInterrupt documentation
Pressure Controller
The pressure control is a PI controller that requires a reference generator as input. The pressure control follows:
+---- + +---------+ +-------+
| REF |------>o---+->| PI Sat |---->| PLANT |------+-----> P actuator
+-----+ ^- | +---------+ +-------+ |
| | +------------+ |
| +-->| ALARM Ctrl |-->STOP |
| +------------+ |
+-------------------------------------+
|
V +-------------+
P accumul -->o------>| ALARM Accum |-->STOP
+-------------+
The reference generator must create two set point with a square wave that depends upon the period and the duty cycle. The PI control is saturated. The Alarms are hybrid counters that fire an error if the system is for too long away from the references.
The Alarm Control is raised when the difference between the reference pressure and the measured accumulator pressure is too distant for too long from the reference pressure OR if accumulator pressure is over a certain threshold.
The Alarm Accumulator is raised when the distance between the measured accumulator pressure and the measured actuator pressure is above a certain threshold for a defined time.
PI Controller
The controller implements the following closed loop equation
e_i' = e_p = (p_ref - p_meas)
u = SAT( kp * e_p + ki * e_i )
The control gain are variable. The saturation limits are not.
The saturation limits the output value between two bounds, sat_min and sat_max. In pseudocode
if u <= sat_min then return sat_min
if u >= sat_max then return sat_max
return u
Square Wave Generator
This class is actually a counter that is able to generate a square wave output inside two limits. The square wave period are defined upon PERIOD and DUTY CYCLE. The dynamical generator is:
t' = 1
s' = 0
and has the following jump rules:
Condition: t ≥ duty_cycle * T and s = 0
t+ = t
s+ = 1 - s
Condition: t ≥ T and s = 1
t+ = 0
s+ = 1 - s
And the ouput of the dynamical system is:
y = s y1 + (1 - s) y0
But to make things faster, some evaluations are simplified in the code itself.
Controller Alarm
The Alarm Control is raised when the difference between the reference pressure and the measured accumulator pressure is too distant for too long from the reference pressure OR if accumulator pressure is over a certain threshold.
The class responds to the following dynamical system:
t' = s
s' = 0
and to the following jump conditions:
Condition: abs(p_act - p_ref) ≥ TH and s != 1
t+ = t
s+ = 1
Condition: abs(p_act - p_ref) ≤ TH and s != 0
t+ = 0
s+ = 0
the actual alarm is raised when the following condition occurs:
Condition: t ≥ TH_time
Alarm is raised
that is enabled when when system pressure deviates from the control input,
Condition: p_act ≥ TH_pres
Alarm is raised
which is really unlikely to occur.
Accumulator Alarm
The Alarm Accumulator is raised when the distance between the measured accumulator pressure and the measured actuator pressure is above a certain threshold for a defined time. The class responds to the following dynamical system:
t' = s
s' = 0
and to the following jump equations:
Condition: abs(p_act - p_acc) ≥ TH and s != 1
t+ = t
s+ = 1
Condition: abs(p_act - p_acc) ≤ TH and s != 0
t+ = 0
s+ = 0
the actual alarm is raised when the following condition occurs:
Condition: t ≥ TH_time
Alarm is raised
Pressure controller defines
PRESCTRL_CTRLALARM_PRES_TH
: Pressure threshold for the Controller AlarmPRESCTRL_CTRLALARM_TIME_TH
: Time threshold (out-of-bound) for the Controller AlarmPRESCTRL_CTRLALARM_OVERPRESS_TH
: Over pressure threshold for the Controller AlarmPRESCTRL_ACCALARM_PRES_TH
: Time threshold (out-of-bound) for the Accumulator AlarmPRESCTRL_ACCALARM_TIME_TH
: Over pressure threshold for the Accumulator AlarmPRESCTRL_PI_SATMIN
: Minimum value for PI saturationPRESCTRL_PI_SATMAX
: Maximum value for PI saturationPRESCTRL_PACTUATOR_SENSOR_PIN
: Pin selection for actuator pressure sensorPRESCTRL_PACCUMULATOR_SENSOR_PIN
: Pin selection for accumulator pressure sensorsPRESCTRL_PACT_OUT_PIN
: Pin selection for actuator command outputPRESCTRL_ENABLE_PRES_CTRL
: Pin for enabling overall ressure controlPRESCTRL_SENSOR_ACTUATOR_M
: Sensor linear coefficientPRESCTRL_SENSOR_ACTUATOR_Q
: Sensor offset coefficientPRESCTRL_SENSOR_ACCUMULATOR_M
: Sensor linear coefficientPRESCTRL_SENSOR_ACCUMULATOR_Q
: Sensor offset coefficientPRESCTRL_MOVAVG_ACTUATOR
: Exponential Moving average trade offPRESCTRL_MOVAVG_ACCUMULATOR
: Exponential Moving average trade off
Temperature Controller
The class handles the control for the temperature. It is actually a system in the form:
x' = f(x, c, r) x : temperature plant state. We have a sensor for it
c' = 0 c : chiller state
r' = 0 r : resistance state
t' = s * LOOP_TIMING t : counter for alarm state
s' = 0 s : derivative of the timer
This dynamic equation may evolve with jumps if those conditions are reached:
Condition: s != -1 and t ≤ Tset - Tdelta
x+ = x
c+ = 0
r+ = 1 * r_enabled
t+ = 0
s+ = -1
r_enabled
is an additional bit that allows the resistance to be turned on, and can be overridden by the user.
Condition: s != 1 and t ≥ Tset + Tdelta
x+ = x
c+ = 1 * c_enabled
r+ = 0
t+ = 0
s+ = 1
v_enabled
is an additional bit that allows the resistance to be turned on, and can be overridden by the user. When
the temperature is inside a certain hysteresis band all the output are turned off:
Condition: (s == 1 and t ≤ Tset) or (s == -1 and t ≥ Tset)
x+ = x
c+ = 0
r+ = 0
t+ = 0
s+ = 0
This switch condition is not optimal (the temperature osccilates quite a bit) but it allows to save some interrupts on the relays and to preserve their operative time. If the system deviates from the set point, after a certain amount of time an alarm is raised.
Condition abs(t) ≥ alarm_threshold and TEMP_CTRL = 1
An error is raised in the state machine and all the system is shut down
Temperature controller defines
TEMPCTRL_OFFSET
: Offset degrees for temperature controllerTEMPCTRL_LIMIT_TIME_ALARM
: Time accumulator for alarm limitTEMPCTRL_CHILLER_PIN
: Pin for chiller digital outTEMPCTRL_RESISTANCE_PIN
: Pin for resistance digital outTEMPCTRL_TEMPSENSOR_PIN
: Pin for analog temperature sensorTEMPCTRL_MOVAVG_ALPHA
: Exponential Moving average trade offTEMPCTRL_SENSOR_M
: Sensor linear coefficientTEMPCTRL_SENSOR_Q
: Sensor offset coefficient
Storage system
The class saves and restores some configuration in the state of the machine. The idea is to store and restore only upon user request. The configuration saved are:
unsigned long max_cycle
: Max cycle configurationfloat kp
: Proportional gain configurationfloat ki
: Integral gain configurationfloat period
: Square wave period configurationfloat duty_cycle
: Square wave duty cycle configurationfloat t_set
: Temeperature set point configurationfloat p_low
: Low pressure configuration for referencefloat p_high
: High pressure configuration for reference
and the cycle
count is also saved.
Storage defines
STORAGE_REF_ADDRESS
: The default storage reference address offsetSTORAGE_FREQUENCY
: Specify when to write current cycle number to EEPROM (1000 means every 1000 cycles)
Serial Parser
A simple implementation of a serial parser state machine
that is able to receive and send two structs of data that
are defined in Messages.h
.
The input is read in a non blocking manner and it is updated as soon as data is available.
The output is written all at the same time, blocking (through flush
) the board
in the operation, so we have to make sure that the operation:
- is not performed in an interrupt routine
- do not send too much data for a single operation
Receiving data algorithm
The command receives ALL the character from the serial monitor if they are available and once all the char have been written to a temporary buffer, they are copied to the output structure of the parser. This means that if the transmitting endpoint spams too much char on the serial it will block our receiving endpoint.
When data is fully received it is immediately copied in the output structure, and the flag data_ready is set to true. That means: if we are sending too much info from the sending endpoint, we are forcing an overwrite. Last command sent is always more important with respect to the previous ones.
If checksum is not respected, data is not copied and error is set to true. Error is reset to false at the first good package received. Received data is expected to be little-endian.
The command to be executed are defined in Commands.h
, in the form of an array
of function pointer.
Sending Data Algorithm
Sends on the serial the output structure. The ouptut structure should be less than 64byte to be sure not to full the output buffer of the Arduino. Longer structure can be used but for sure the operation timing will depend on the length of the structure itself.
The operation is made as blocking in order to avoid chenge in the output structure while writing data to serial.
FIXME: Here I'm doing a blocking operation. It is performed only if requested via serial thus it is stil safe, but if the serial disconnects while sending with flush blocking everything, then I may have some problem. With enough timer one should define a timer for maximum transmission time and raise an alert if the threshold time is reached.
Checksum algorithm
The checksum algorithm is a XOR accumulation:
check = 0x00
for each byte in buffer:
check = check XOR byte
return check
If a checksum is failed the system enters the Alarm state.
Input command protocol
The input command protocol is a 6 bytes array of char that is transformed to a little-endian structure composed by:
- 1 character: the command to operate
- 1 float: an input value for the command
- 1 character: a checksum
0 1 2 3 4 5
+---+ +---+---+---+---+ +---+
0x0_ | | | | | | | | |
+---+ +---+---+---+---+ +---+
CMD VALUE CHECK
The commands are described in the Commands.h
and currently are:
Byte | Command Enum | Description |
---|---|---|
0x00 |
Hearthbeat |
Request an update about the system state |
0x01 |
ManualTemperatureControl |
Overrides (disabling) the temperature control |
0x02 |
AutomaticTemperatureControl |
Enables the pressure control |
0x03 |
ManualPressureControl |
Overrides (disabling) the pressure control |
0x04 |
AutomaticPressureControl |
Enables the pressure control |
0x05 |
TogglePauseCycle |
Pauses / restarts the control system |
0x06 |
EmergencyStopCycle |
Calls immediately the alarm status |
0x07 |
ToggleChillerActuation |
Enables chiller actuation |
0x08 |
ToggleResistanceActuation |
Enables resistance actuator |
0x09 |
SetTemperature |
Sets temperature setpoint |
0x0A |
SetPressureHigh |
Sets pressure set point for reference generator |
0x0B |
SetPressureLow |
Sets pressure set point for reference generator |
0x0C |
SetPressure |
Overrides current pressure set point |
0x0D |
OverridePIControl |
Override PI Control with voltage input |
0x0E |
SetPIProportionalGain |
Sets PI proportional gain |
0x0F |
SetPIIntegrativeGain |
Sets PI integrative gain |
0x10 |
SetMaximumCycleNumber |
Sets target number for cycles |
0x11 |
SetCurrentCycleNumber |
Override current Cycle number |
0x12 |
SetReferencePeriod |
Setup reference square wave period |
0x13 |
SetReferenceDutyCycle |
Setup reference square wave cycle |
0x14 |
SystemReboot |
Force an immediate system reboot |
0x15 |
StartCycle |
Starts the system (Running state) |
0x16 |
SaveStorageConfig |
Save current configuration in the EEPROM |
0x17 |
LoadStorageConfig |
Load storage config from EEPROM. Extremely risky, it may be corrupted data |
0x18 |
LoadStorageCycle |
Load current cycle number from EEPROM. Extremely Risky it may be corrupted data |
0x19 |
CommandCodeSiz |
This last one is a size for the array of function pointers |
Output state protocol
The output sends information about the current operating state of the system:
float t_meas
: Temeprature measurementsfloat p_meas
: Actuator Pressure measurementsfloat q_meas
: Accumulator pressure measurementsfloat kp
: Pressure proportional gainfloat ki
: Pressure Integral gainfloat t_set
: Temeprature set pointfloat p_set
: Pressure set point for actuatorfloat u_pres
: Current output for pressure controlfloat period
: Square wave generator periodfloat duty_cycle
: Duty cycle coefficientfloat cycle
: Cycles numberfloat max_cycle
: Max cycles Numberchar config
: Actuators configurationchar state
: Current state flagchar error
: Last error flagchar check
: Communication checksum (accumulated xor)
0 1 2 3 4 5 6 7 8 9 A B C D E F
+---+---+---+---+ +---+---+---+---+ +---+---+---+---+ +---+---+---+---+
0x0_ | | | | | | | | | | | | | | | | | | | |
+---+---+---+---+ +---+---+---+---+ +---+---+---+---+ +---+---+---+---+
Meas. Temp. Meas. P Actuat. Meas. P Accumul. PI Kp
0 1 2 3 4 5 6 7 8 9 A B C D E F
+---+---+---+---+ +---+---+---+---+ +---+---+---+---+ +---+---+---+---+
0x1_ | | | | | | | | | | | | | | | | | | | |
+---+---+---+---+ +---+---+---+---+ +---+---+---+---+ +---+---+---+---+
PI Ki Set point T Set point P PI output
0 1 2 3 4 5 6 7 8 9 A B C D E F
+---+---+---+---+ +---+---+---+---+ +---+---+---+---+ +---+---+---+---+
0x2_ | | | | | | | | | | | | | | | | | | | |
+---+---+---+---+ +---+---+---+---+ +---+---+---+---+ +---+---+---+---+
Period Duty Cycle Cycle Cycle Target
0 1 2 3
+---+ +---+ +---+ +---+
0x3_ | | | | | | | | SIZE = 0x34 = 52 bytes
+---+ +---+ +---+ +---+
Config State Error Check
The update MUST BE REQUESTED through the command
Hearthbeat
!
Config table
The char 0x30
reports the system configuration:
Byte | Config | Description |
---|---|---|
0x01 |
Chiller Enabled | Additional bit to enable the chiller actuation |
0x02 |
Resistance Enabled | Additional bit to enable the resistance actuation |
0x04 |
Actuator Enabled | Additional bit to enable the pressure actuation |
State Table
The char 0x31
reports the system state:
Byte | State | Description |
---|---|---|
0x01 |
Alarm | The system is in alarm mode. Only save configuration, hearthbeat and system reboot commands are accepted |
0x02 |
Pause | The system is in pause between cycles |
0x04 |
Running | The system is running the cycles |
0x08 |
Waiting | Waiting for user input, it is actually never used |
0x0A |
Serial Setup | Very initial state, it is actually never reported |
Interfacing
It is possible to interface the device with a raspberry PI using the library project.controller.interface
Serial Parser defines
SERIAL_PORT
: Serial port used for communicationSERIAL_PORT_BAUD
: Serial port baud speed (bps)SERIAL_SYNC_CHAR
: Signature for accepting incoming connectionSERIAL_PARSER_TEMP_MIN
: Minimum accepted temperature set point in celsiusSERIAL_PARSER_TEMP_MAX
: Maximum accepted temperature set point in celsiusSERIAL_PARSER_PRESSURE_MIN
: Maximum accepted temperature set point in barSERIAL_PARSER_PRESSURE_MAX
: Maximum accepted temperature set point in barSERIAL_PARSER_KPI_MIN
: Minimum value for the PI gainSERIAL_PARSER_KPI_MAX
: Maximum value for the PI gainSERIAL_PARSER_PERIOD_MIN
: Minimum value for the square wave generation in secsSERIAL_PARSER_PERIOD_MAX
: Maximum value for the square wave generation in secs