1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
/*
* Pid.h
*
* Created on: 21 Jul 2016
* Author: David
*/
#ifndef SRC_PID_H_
#define SRC_PID_H_
/**
* This class implements a PID controller for the heaters
*/
#include "FOPDT.h"
class PID
{
enum class HeaterMode : uint8_t
{
// The order of these is important because we test "mode > HeatingMode::off" to determine whether the heater is active
fault,
off,
heating,
cooling,
stable,
// All states from here onwards must be PID tuning states because function IsTuning assumes that
tuning0,
tuning1,
tuning2,
lastTuningMode = tuning2
};
static const size_t NumPreviousTemperatures = 4; // How many samples we average the temperature derivative over
public:
PID(Platform* p, int8_t h);
void Init(float pGain, float pTc, float pTd, float tempLimit, bool usePid); // (Re)Set everything to start
void Reset();
void Spin(); // Called in a tight loop to keep things running
void SetActiveTemperature(float t);
float GetActiveTemperature() const;
void SetStandbyTemperature(float t);
float GetStandbyTemperature() const;
void SetTemperatureLimit(float t);
float GetTemperatureLimit() const;
void Activate(); // Switch from idle to active
void Standby(); // Switch from active to idle
bool Active() const; // Are we active?
void SwitchOff(); // Not even standby - all heater power off
bool SwitchedOff() const; // Are we switched off?
bool FaultOccurred() const; // Has a heater fault occurred?
void ResetFault(); // Reset a fault condition - only call this if you know what you are doing
float GetTemperature() const; // Get the current temperature
float GetAveragePWM() const; // Return the running average PWM to the heater. Answer is a fraction in [0, 1].
uint32_t GetLastSampleTime() const; // Return when the temp sensor was last sampled
float GetAccumulator() const; // Return the integral accumulator
void StartAutoTune(float maxTemp, float maxPwm, StringRef& reply); // Start an auto tune cycle for this PID
bool IsTuning() const;
void GetAutoTuneStatus(StringRef& reply); // Get the auto tune status or last result
const FopDt& GetModel() const // Get the process model
{ return model; }
bool SetModel(float gain, float tc, float td, float maxPwm, bool usePid); // Set the process model
bool IsHeaterEnabled() const // Is this heater enabled?
{ return model.IsEnabled(); }
void GetHeaterProtection(float& pMaxTempExcursion, float& pMaxFaultTime) const
{ pMaxTempExcursion = maxTempExcursion; pMaxFaultTime = maxHeatingFaultTime; }
void SetHeaterProtection(float pMaxTempExcursion, float pMaxFaultTime)
{ maxTempExcursion = pMaxTempExcursion; maxHeatingFaultTime = pMaxFaultTime; }
void SetM301PidParameters(const M301PidParameters& params)
{ model.SetM301PidParameters(params); }
private:
void SwitchOn(); // Turn the heater on and set the mode
void SetHeater(float power) const; // Power is a fraction in [0,1]
TemperatureError ReadTemperature(); // Read and store the temperature of this heater
void DoTuningStep(); // Called on each temperature sample when auto tuning
bool ReadingsStable(size_t numReadings, float maxDiff) const
pre(numReadings >= 2; numReadings <= MaxTuningTempReadings);
static size_t GetMaxRateIndex(); // Auto tune helper function
void DisplayBuffer(const char *intro); // Debug helper
void FitCurve(); // Calculate G, td and tc from the accumulated readings
float GetExpectedHeatingRate() const; // Get the minimum heating rate we expect
Platform* platform; // The instance of the class that is the RepRap hardware
float activeTemperature; // The required active temperature
float standbyTemperature; // The required standby temperature
float temperatureLimit; // The maximum allowed temperature for this heater
float maxTempExcursion; // The maximum temperature excursion permitted while maintaining the setpoint
float maxHeatingFaultTime; // How long a heater fault is permitted to persist before a heater fault is raised
float temperature; // The current temperature
float previousTemperatures[NumPreviousTemperatures]; // The temperatures of the previous NumDerivativeSamples measurements, used for calculating the derivative
size_t previousTemperatureIndex; // Which slot in previousTemperature we fill in next
FopDt model; // The process model and PID parameters
float iAccumulator; // The integral PID component
float lastPwm; // The last PWM value we output, before scaling by kS
float averagePWM; // The running average of the PWM, after scaling.
uint32_t timeSetHeating; // When we turned on the heater
uint32_t lastSampleTime; // Time when the temperature was last sampled by Spin()
uint16_t heatingFaultCount; // Count of questionable heating behaviours
int8_t heater; // The index of our heater
uint8_t previousTemperaturesGood; // Bitmap indicating which previous temperature were good readings
HeaterMode mode; // Current state of the heater
bool active; // Are we active or standby?
bool tuned; // True if tuning was successful
uint8_t badTemperatureCount; // Count of sequential dud readings
static_assert(sizeof(previousTemperaturesGood) * 8 >= NumPreviousTemperatures, "too few bits in previousTemperaturesGood");
static float *tuningTempReadings; // the readings from the heater being tuned
static float tuningStartTemp; // the temperature when we turned on the heater
static float tuningPwm; // the PWM to use, 0..1
static float tuningMaxTemp; // the maximum temperature we are allowed to reach
static uint32_t tuningBeginTime; // when we started the tuning process
static uint32_t tuningPhaseStartTime; // when we started the current tuning phase
static uint32_t tuningReadingInterval; // how often we are sampling, in milliseconds
static size_t tuningReadingsTaken; // how many temperature samples we have taken
static float tuningTimeOfFastestRate; // how long after turn-on the fastest temperature rise occurred
static float tuningFastestRate; // the fastest temperature rise
static const size_t MaxTuningTempReadings = 128; // The maximum number of readings we keep. Must be an even number.
};
inline bool PID::Active() const
{
return active;
}
inline float PID::GetActiveTemperature() const
{
return activeTemperature;
}
inline float PID::GetStandbyTemperature() const
{
return standbyTemperature;
}
inline void PID::SetTemperatureLimit(float t)
{
temperatureLimit = t;
}
inline float PID::GetTemperatureLimit() const
{
return temperatureLimit;
}
inline float PID::GetTemperature() const
{
return temperature;
}
inline bool PID::FaultOccurred() const
{
return mode == HeaterMode::fault;
}
inline bool PID::SwitchedOff() const
{
return mode == HeaterMode::off;
}
inline uint32_t PID::GetLastSampleTime() const
{
return lastSampleTime;
}
inline float PID::GetAccumulator() const
{
return iAccumulator;
}
inline void PID::SetHeater(float power) const
{
platform->SetHeater(heater, power);
}
inline bool PID::IsTuning() const
{
return mode >= HeaterMode::tuning0;
}
#endif /* SRC_PID_H_ */
|