software:firmware
MakAir Firmware
pc_cmv_controller.cpp
Go to the documentation of this file.
1 
8 // INCLUDES ==================================================================
9 
10 // Associated header
11 #include "../includes/pc_cmv_controller.h"
12 
13 // External
14 #include "Arduino.h"
15 #include <algorithm>
16 
17 // Internal
18 
19 #include "../includes/main_controller.h"
20 #include "../includes/pressure_valve.h"
21 
22 // INITIALISATION =============================================================
23 
25 
26 // FUNCTIONS ==================================================================
27 
28 // cppcheck-suppress misra-c2012-5.2 ; false positive
34 
37  for (uint8_t i = 0u; i < PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN; i++) {
40  }
41 
50 }
51 
53 
57 
62  // Reset PID values
71  for (uint8_t i = 0; i < PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN; i++) {
75  }
76 
77  // Apply blower ramp-up
78  if (m_blowerIncrement >= 0) {
79  blower.runSpeedWithRampUp(m_blowerSpeed + static_cast<uint16_t>(abs(m_blowerIncrement)));
80  } else {
81  // When blower increment is negative, we need to check that it is less than current speed
82  // If not, it would result in an overflow
83  if (static_cast<uint16_t>(abs(m_blowerIncrement)) < m_blowerSpeed) {
85  - static_cast<uint16_t>(abs(m_blowerIncrement)));
86  } else {
88  }
89  }
92 }
93 
95  // Keep the inspiratory valve open using a PID
96  int32_t inspiratoryPidValue = PCinspiratoryPID(mainController.pressureCommand(),
98 
99  inspiratoryValve.open(inspiratoryPidValue);
101 
102  // m_plateauStartTime is used for blower regulations, -5 is added to help blower convergence
107  }
108 }
109 
111  // Close the inspiratory valve
113 
114  // Open the expiratos valve so the patient can exhale outside
117 }
118 
120 
122  int16_t peakDelta =
124 
125  // Number of tick for the half ramp (120 ms)
126  int32_t halfRampNumberfTick =
127  1000 * 120 / static_cast<int32_t>(MAIN_CONTROLLER_COMPUTE_PERIOD_MICROSECONDS);
128 
129  // Update blower only if patient is plugged on the machine
130  if (mainController.peakPressureMeasure() > 20) {
131  if (m_plateauStartTime < ((mainController.ticksPerInhalation() * 30u) / 100u)) {
132  // Only case for decreasing the blower: ramping is too fast or overshooting is too high
133  if ((m_plateauStartTime < static_cast<uint32_t>(abs(halfRampNumberfTick)))
134  || ((peakDelta > 15)
135  && (m_plateauStartTime < ((mainController.ticksPerInhalation() * 20u) / 100u)))
136  || (peakDelta > 25)) {
137  m_blowerIncrement = -100;
138  DBG_DO(Serial.println("BLOWER -100");)
139  } else {
140  m_blowerIncrement = 0;
141  DBG_DO(Serial.println("BLOWER 0");)
142  }
143  } else if (m_plateauStartTime < ((mainController.ticksPerInhalation() * 40u) / 100u)) {
144  DBG_DO(Serial.println("BLOWER +0");)
145  m_blowerIncrement = 0;
146  } else if (m_plateauStartTime < ((mainController.ticksPerInhalation() * 50u) / 100u)) {
147  m_blowerIncrement = +25;
148  DBG_DO(Serial.println("BLOWER +25"));
149  } else if (m_plateauStartTime < ((mainController.ticksPerInhalation() * 60u) / 100u)) {
150  m_blowerIncrement = +50;
151  DBG_DO(Serial.println("BLOWER +50"));
152  } else {
153  m_blowerIncrement = +100;
154  DBG_DO(Serial.println("BLOWER +100"));
155  }
156  }
157 
158  DBG_DO(Serial.print("Plateau Start time:");)
159  DBG_DO(Serial.println(m_plateauStartTime);)
160 }
161 
162 int32_t
163 PC_CMV_Controller::PCinspiratoryPID(int32_t targetPressure, int32_t currentPressure, int32_t dt) {
164  int32_t minAperture = inspiratoryValve.minAperture();
165  int32_t maxAperture = inspiratoryValve.maxAperture();
166  int32_t inspiratoryValveAperture;
167  int32_t derivative = 0;
168  int32_t smoothError = 0;
169  int32_t totalValues = 0;
170  int32_t proportionnalWeight;
171  int32_t derivativeWeight;
172 
173  int32_t coefficientP;
174  int32_t coefficientI;
175  int32_t coefficientD;
176 
177  int32_t temporarym_inspiratoryPidIntegral = 0;
178 
179  // Compute error
180  int32_t error = targetPressure - currentPressure;
181 
182  // Windowing (it overrides the parameter.h coefficients)
183  if (error < 0) {
184  coefficientI = 200;
185  coefficientP = 2500;
186  coefficientD = 0;
187  } else {
188  coefficientI = 50;
189  coefficientP = 2500;
190  coefficientD = 0;
191  }
192 
193  // Calculate the derivative part
194  // Include a moving average on error for smoothing purpose
198  >= static_cast<int32_t>(PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN)) {
200  }
201  for (uint8_t i = 0u; i < PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN; i++) {
202  totalValues += m_inspiratoryPidLastErrors[i];
203  }
204  smoothError = totalValues / static_cast<int32_t>(PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN);
205 
206  // Fast mode ends at 20 mmH20 from target
207  // When changing from fast mode to PID, set the integral to the previous value
208  if (error < 20) {
210  proportionnalWeight = (coefficientP * error) / 1000;
211  derivativeWeight = (coefficientD * derivative / 1000);
213  * ((int32_t)m_inspiratoryValveLastAperture - maxAperture)
214  / (minAperture - maxAperture)
215  - (proportionnalWeight + derivativeWeight);
216  }
217  m_inspiratoryPidFastMode = false;
218  }
219 
220  // In fast mode: everything is openned (open loop)
222  // Ramp from 125 to 0 angle during 250 ms
223  int32_t increment =
224  (5 * static_cast<int32_t>(MAIN_CONTROLLER_COMPUTE_PERIOD_MICROSECONDS)) / 10000;
225  if (m_inspiratoryValveLastAperture >= abs(increment)) {
226  inspiratoryValveAperture =
227  max(minAperture, min(maxAperture, m_inspiratoryValveLastAperture - increment));
228  } else {
229  inspiratoryValveAperture = 0;
230  }
231  } else { // If not in fast mode, the PID is used
232  derivative = ((dt == 0)) ? 0 : ((1000000 * (m_inspiratoryPidLastError - smoothError)) / dt);
233 
234  temporarym_inspiratoryPidIntegral =
235  m_inspiratoryPidIntegral + ((coefficientI * error * dt) / 1000000);
236  temporarym_inspiratoryPidIntegral =
238  min(PID_BLOWER_INTEGRAL_MAX, temporarym_inspiratoryPidIntegral));
239 
240  proportionnalWeight = ((coefficientP * error) / 1000);
241  int32_t integralWeight = temporarym_inspiratoryPidIntegral;
242  derivativeWeight = coefficientD * derivative / 1000;
243 
244  int32_t blowerCommand = proportionnalWeight + integralWeight + derivativeWeight;
245  inspiratoryValveAperture =
246  max(minAperture,
247  min(maxAperture, maxAperture + (minAperture - maxAperture) * blowerCommand / 1000));
248  }
249 
250  // If the valve is completely open or completely closed, don't update the integral part
251  if ((inspiratoryValveAperture != minAperture) && (inspiratoryValveAperture != maxAperture)) {
252  m_inspiratoryPidIntegral = temporarym_inspiratoryPidIntegral;
253  }
254 
255  m_inspiratoryValveLastAperture = inspiratoryValveAperture;
256  m_inspiratoryPidLastError = smoothError;
257 
258  return inspiratoryValveAperture;
259 }
260 
261 int32_t
262 PC_CMV_Controller::PCexpiratoryPID(int32_t targetPressure, int32_t currentPressure, int32_t dt) {
263  int32_t minAperture = expiratoryValve.minAperture();
264  int32_t maxAperture = expiratoryValve.maxAperture();
265  int32_t expiratoryValveAperture;
266  int32_t derivative = 0;
267  int32_t smoothError = 0;
268  int32_t totalValues = 0;
269  int32_t temporarym_expiratoryPidIntegral = 0;
270  int32_t proportionnalWeight;
271  int32_t derivativeWeight;
272 
273  int32_t coefficientP;
274  int32_t coefficientI;
275  int32_t coefficientD;
276 
277  // Compute error
278  int32_t error = targetPressure + PID_PATIENT_SAFETY_PEEP_OFFSET - currentPressure;
279 
280  // Calculate derivative part
281  // Include a moving average on error for smoothing purpose
285  >= static_cast<int32_t>(PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN)) {
287  }
288  for (uint8_t i = 0u; i < PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN; i++) {
289  totalValues += m_expiratoryPidLastErrors[i];
290  }
291  smoothError = totalValues / static_cast<int32_t>(PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN);
292  derivative = (dt == 0) ? 0 : (1000000 * (smoothError - m_expiratoryPidLastError)) / dt;
293 
294  // Windowing (it overrides the parameter.h coefficients)
295  if (error < 0) {
296  coefficientI = 50;
297  coefficientP = 2500;
298  coefficientD = 0;
299  } else {
300  // For a high PEEP, a lower KI is required
301  // For PEEP = 100 mmH2O, KI = 120
302  // For PEEP = 50 mmH2O, KI = 250
303  if (mainController.peepCommand() > 100) {
304  coefficientI = 120;
305  } else {
306  coefficientI = ((-130 * ((int32_t)mainController.peepCommand())) / 50) + 380;
307  }
308 
309  coefficientP = 2500;
310  coefficientD = 0;
311  }
312 
313  // Fast mode ends at 30 mmH20 from target
314  // When changing from fast mode to PID, set the integral to the previous value
315  if (error > -30) {
317  proportionnalWeight = (coefficientP * error) / 1000;
318  derivativeWeight = (coefficientD * derivative / 1000);
319  m_expiratoryPidIntegral = 1000 * ((int32_t)m_expiratoryValveLastAperture - maxAperture)
320  / (maxAperture - minAperture)
321  - (proportionnalWeight + derivativeWeight);
322  }
323  m_expiratoryPidFastMode = false;
324  }
325 
326  // Fast mode: open loop with ramp
328  // Ramp from 125 to 0 angle during 250 ms
329  int32_t increment =
330  (5 * static_cast<int32_t>(MAIN_CONTROLLER_COMPUTE_PERIOD_MICROSECONDS)) / 10000;
331  if (m_expiratoryValveLastAperture >= abs(increment)) {
332  expiratoryValveAperture =
333  max(minAperture, min(maxAperture, m_expiratoryValveLastAperture - increment));
334  } else {
335  expiratoryValveAperture = 0;
336  }
337  } else { // If not in fast mode, the PID is used
338  temporarym_expiratoryPidIntegral =
339  m_expiratoryPidIntegral + ((coefficientI * error * dt) / 1000000);
340  temporarym_expiratoryPidIntegral =
342  min(PID_PATIENT_INTEGRAL_MAX, temporarym_expiratoryPidIntegral));
343 
344  proportionnalWeight = ((coefficientP * error) / 1000);
345  int32_t integralWeight = temporarym_expiratoryPidIntegral;
346  derivativeWeight = coefficientD * derivative / 1000;
347 
348  int32_t patientCommand = proportionnalWeight + integralWeight + derivativeWeight;
349 
350  expiratoryValveAperture = max(
351  minAperture,
352  min(maxAperture, maxAperture + (maxAperture - minAperture) * patientCommand / 1000));
353  }
354 
355  // If the valve is completely open or completely closed, don't update integral part
356  if ((expiratoryValveAperture != minAperture) && (expiratoryValveAperture != maxAperture)) {
357  m_expiratoryPidIntegral = temporarym_expiratoryPidIntegral;
358  }
359 
360  m_expiratoryPidLastError = smoothError;
361  m_expiratoryValveLastAperture = expiratoryValveAperture;
362 
363  return expiratoryValveAperture;
364 }
Blower blower
Definition: blower.cpp:20
uint16_t getTargetSpeed() const
Get target speed value.
Definition: blower.cpp:103
void runSpeedWithRampUp(uint16_t p_targetSpeed)
Run the blower to a given speed applying a ramp-up to prevent high current drain.
Definition: blower.cpp:44
int32_t dt() const
Get the delta of time since the last cycle (in ms)
uint32_t tick() const
Get the tick number of the current cycle.
int16_t pressure() const
Get the current measured pressure.
int16_t peakPressureMeasure() const
Get the measured peak pressure.
int16_t peepCommand() const
Get the desired PEEP.
int16_t plateauPressureCommand() const
Get the desired plateau pressure.
uint32_t ticksPerInhalation() const
Get the duration of an inhalation in ticks.
int32_t pressureCommand() const
Get the pressure command.
Controller for the CMV mode.
int32_t m_expiratoryPidLastError
Error of the last computation of the expiratory PID.
int32_t m_inspiratoryPidLastError
Error of the last computation of the expiratory PID.
uint16_t m_blowerSpeed
Current blower speed.
bool m_expiratoryPidFastMode
Fast mode at start of expiration.
int32_t m_inspiratoryPidIntegral
Error of the last computation of the blower PID.
PC_CMV_Controller()
Default constructor.
int32_t m_expiratoryValveLastAperture
Last aperture of the blower valve.
void initCycle() override
Begin a new breathing cycle.
int32_t m_expiratoryPidIntegral
Integral gain of the patient PID.
int32_t m_inspiratoryPidLastErrorsIndex
Last error index in inspiratory PID.
void inhale() override
Control the inhalation.
int32_t m_inspiratoryPidLastErrors[PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN]
Last errors in inspiratory PID.
bool m_inspiratoryPidFastMode
Fast mode at start of inspiration.
void setup() override
Initialize controller.
int32_t m_expiratoryPidLastErrors[PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN]
Last errors in expiratory PID.
uint16_t m_plateauStartTime
Number of ticks when plateau is reached for the first time.
void exhale() override
Control the exhalation.
int32_t m_blowerIncrement
Current blower speed increment (to apply at the beginning of the next cycle)
int32_t PCexpiratoryPID(int32_t targetPressure, int32_t currentPressure, int32_t dt)
PID to control the patient valve during some specific steps of the cycle.
int32_t m_expiratoryPidLastErrorsIndex
Last error index in inspiratory PID.
bool m_plateauPressureReached
True if plateau pressure has been reached (but not necessarily converged)
int32_t PCinspiratoryPID(int32_t targetPressure, int32_t currentPressure, int32_t dt)
PID to control the blower valve during some specific steps of the cycle.
void endCycle() override
End the current breathing cycle.
void calculateBlowerIncrement()
Determine the blower speed to adopt for next cycle.
int32_t m_inspiratoryValveLastAperture
Last aperture of the blower valve.
uint16_t minAperture() const
Minimum valve aperture angle in degrees.
void open()
Request opening of the Pressure Valve.
uint16_t maxAperture() const
Maximum valve aperture angle in degrees.
void close()
Request closing of the Pressure Valve.
#define DBG_DO(statement)
Expand arbitrary code only when in debug mode.
Definition: debug.h:24
MainController mainController
uint16_t i
static const int32_t PID_PATIENT_INTEGRAL_MIN
Definition: parameters.h:146
#define PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN
Definition: parameters.h:152
static const int32_t PID_BLOWER_INTEGRAL_MAX
Definition: parameters.h:139
#define DEFAULT_BLOWER_SPEED
Definition: parameters.h:194
static const int32_t PID_PATIENT_INTEGRAL_MAX
Definition: parameters.h:145
#define MAIN_CONTROLLER_COMPUTE_PERIOD_MICROSECONDS
Definition: parameters.h:30
static const int32_t PID_BLOWER_INTEGRAL_MIN
Definition: parameters.h:140
#define MIN_BLOWER_SPEED
Definition: parameters.h:192
static const int32_t PID_PATIENT_SAFETY_PEEP_OFFSET
Increase target pressure by an offset (in mmH2O) for safety, to avoid going below the target pressure...
Definition: parameters.h:150
PC_CMV_Controller pcCmvController
PressureValve inspiratoryValve
PressureValve expiratoryValve