software:firmware
MakAir Firmware
vc_cmv_controller.cpp
Go to the documentation of this file.
1 
8 // INCLUDES ==================================================================
9 
10 // Associated header
11 #include "../includes/vc_cmv_controller.h"
12 
13 // External
14 #include "Arduino.h"
15 #include <algorithm>
16 
17 // Internal
18 #include "../includes/main_controller.h"
19 #include "../includes/pressure_valve.h"
20 
21 // INITIALISATION =============================================================
22 
24 
25 // FUNCTIONS ==================================================================
26 
27 // cppcheck-suppress misra-c2012-5.2 ; false positive
31 
34  for (uint8_t i = 0u; i < NUMBER_OF_SAMPLE_LAST_VALUES; i++) {
36  }
37  for (uint8_t i = 0u; i < PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN; i++) {
39  }
40 
42  m_blowerTicks = 0;
49 }
50 
52  // No specific setup code
53 }
54 
58  // Reset PID values
64  for (uint8_t i = 0; i < PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN; i++) {
67  }
68 
69  for (uint8_t i = 0u; i < NUMBER_OF_SAMPLE_LAST_VALUES; i++) {
71  }
73 
78  m_duringPlateau = false;
79 }
80 
83 
85 
86  int32_t inspirationRemainingDurationMs =
87  static_cast<int32_t>((mainController.ticksPerInhalation() - mainController.tick())
90 
91  if (inspirationRemainingDurationMs > 20) {
92  // m_targetFlowMultiplyBy1000 =
93  // (60 * 1000
94  // * (mainController.tidalVolumeCommand() - mainController.currentDeliveredVolume()))
95  // / inspirationRemainingDurationMs; // in mL/min
96  // m_targetFlowMultiplyBy1000 = max(int32_t(0), m_targetFlowMultiplyBy1000);
98  }
99 
100  // The safety volume is the volume that will be delivered during the closing of the valve.
101  // Taking is into account allows better accuracy on volume delivery
102  int32_t safetyVolume = mainController.inspiratoryFlow() * VALVE_RESPONSE_TIME_MS / 60000;
103  if (!m_duringPlateau
105  > mainController.tidalVolumeCommand() - safetyVolume) {
108  + static_cast<uint16_t>(mainController.plateauDurationCommand())
110  m_duringPlateau = true;
111  }
112  // If before plateau
114  - static_cast<uint16_t>(mainController.plateauDurationCommand())
116  int32_t flow = mainController.inspiratoryFlow();
117  int32_t blowerPressure = blower.getBlowerPressure(flow);
118  int32_t patientPressure = mainController.pressure();
119 
120  // Use Venturi equation to find the sectionToOpen in order to get the target flow.
121  // https://en.wikipedia.org/wiki/Venturi_effect
122  int32_t A1MultiplyBy100 = 3318;
123  int32_t rhoMultiplyBy100 = 120;
124  int32_t twoA1SquareDotDeltaPressureMultiplyBy100 =
125  100 * 2 * (A1MultiplyBy100 * A1MultiplyBy100 / 10000)
126  * (98 * (blowerPressure - patientPressure) / 10);
127  int32_t divider = (rhoMultiplyBy100 * (m_targetFlowMultiplyBy1000 / 60)
128  * (m_targetFlowMultiplyBy1000 / 60) / 100);
129  int32_t tempRatio =
130  (divider == 0) ? 0 : (twoA1SquareDotDeltaPressureMultiplyBy100 / divider);
131  int32_t sectionToOpen;
132  if (m_targetFlowMultiplyBy1000 == 0) {
133  sectionToOpen = 0;
134  } else {
135  int32_t divider2 = ((tempRatio + 100) < 0) ? 0 : sqrt(tempRatio + 100);
136  sectionToOpen = (divider2 == 0) ? 0 : (A1MultiplyBy100 * 10 / divider2);
137  }
138 
139  // Open the valve to a specific section (in mm^2 multiplied by 100)
140  inspiratoryValve.openSection(sectionToOpen);
141  } else { // else : during plateau, valves are closed
143  }
144 
145  // Compute max inspiratory flow
148  }
149 }
150 
152  // Open the expiration valve using PID so the patient can exhale outside
153  int32_t expiratoryValveOpenningValue = PCexpiratoryPID(
155 
156  (void)expiratoryValve.openLinear(expiratoryValveOpenningValue);
157 
159  // m_inspiratoryValveLastAperture = inspiratoryValveOpenningValue;
160 }
161 
163 
165  // Compute target flow for inspiration
166  int32_t inspirationDurationMs =
168  - static_cast<uint16_t>(mainController.plateauDurationCommand())); // in ms
169  m_targetFlowMultiplyBy1000 = (inspirationDurationMs == 0)
170  ? 0
171  : (60 * 1000 * mainController.tidalVolumeCommand())
172  / inspirationDurationMs; // in mL/min
173 
174  // Blower always run at full speed
175  m_blowerSpeed = 1800;
176 }
177 
178 int32_t
179 VC_CMV_Controller::PCexpiratoryPID(int32_t targetPressure, int32_t currentPressure, int32_t dt) {
180  int32_t minAperture = expiratoryValve.minAperture();
181  int32_t maxAperture = expiratoryValve.maxAperture();
182  int32_t expiratoryValveAperture;
183  int32_t derivative = 0;
184  int32_t smoothError = 0;
185  int32_t totalValues = 0;
186  int32_t temporaryExpiratoryPidIntegral = 0;
187  int32_t proportionnalWeight;
188  int32_t derivativeWeight;
189 
190  int32_t coefficientP;
191  int32_t coefficientI;
192  int32_t coefficientD;
193 
194  // Compute error
195  int32_t error = targetPressure + PID_PATIENT_SAFETY_PEEP_OFFSET - currentPressure;
196 
197  // Calculate derivative part
198  // Include a moving average on error for smoothing purpose
202  >= static_cast<int32_t>(PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN)) {
204  }
205  for (uint8_t i = 0u; i < PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN; i++) {
206  totalValues += m_expiratoryPidLastErrors[i];
207  }
208  smoothError = totalValues / static_cast<int32_t>(PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN);
209  derivative = (dt == 0) ? 0 : (1000000 * (smoothError - m_expiratoryPidLastError)) / dt;
210 
211  // Windowing (it overrides the parameter.h coefficients)
212  if (error < 0) {
213  coefficientI = 50;
214  coefficientP = 2500;
215  coefficientD = 0;
216  } else {
217  // For a high PEEP, a lower KI is required
218  // For PEEP = 100 mmH2O, KI = 120
219  // For PEEP = 50 mmH2O, KI = 250
220  if (mainController.peepCommand() > 100) {
221  coefficientI = 120;
222  } else {
223  coefficientI = ((-130 * ((int32_t)mainController.peepCommand())) / 50) + 380;
224  }
225  coefficientP = 2500;
226  coefficientD = 0;
227  }
228 
229  // Fast mode ends at 30 mmH20 from target
230  // When changing from fast mode to PID, set the integral to the previous value
231  if (error > -30) {
233  proportionnalWeight = (coefficientP * error) / 1000;
234  derivativeWeight = (coefficientD * derivative / 1000);
235  m_expiratoryPidIntegral = 1000 * ((int32_t)m_expiratoryValveLastAperture - maxAperture)
236  / (maxAperture - minAperture)
237  - (proportionnalWeight + derivativeWeight);
238  }
239  m_expiratoryPidFastMode = false;
240  }
241 
242  // Fast mode: open loop with ramp
244  expiratoryValveAperture = 0;
245  } else { // If not in fast mode, the PID is used
246  temporaryExpiratoryPidIntegral =
247  m_expiratoryPidIntegral + ((coefficientI * error * dt) / 1000000);
248  temporaryExpiratoryPidIntegral =
250  min(PID_PATIENT_INTEGRAL_MAX, temporaryExpiratoryPidIntegral));
251 
252  proportionnalWeight = ((coefficientP * error) / 1000);
253  int32_t integralWeight = temporaryExpiratoryPidIntegral;
254  derivativeWeight = coefficientD * derivative / 1000;
255 
256  int32_t patientCommand = proportionnalWeight + integralWeight + derivativeWeight;
257 
258  expiratoryValveAperture = max(
259  minAperture,
260  min(maxAperture, maxAperture + (maxAperture - minAperture) * patientCommand / 1000));
261  }
262 
263  // If the valve is completely open or completely closed, don't update integral part
264  if ((expiratoryValveAperture != minAperture) && (expiratoryValveAperture != maxAperture)) {
265  m_expiratoryPidIntegral = temporaryExpiratoryPidIntegral;
266  }
267 
268  m_expiratoryPidLastError = smoothError;
269  m_expiratoryValveLastAperture = expiratoryValveAperture;
270 
271  return expiratoryValveAperture;
272 }
Blower blower
Definition: blower.cpp:20
uint16_t getTargetSpeed() const
Get target speed value.
Definition: blower.cpp:103
int32_t getBlowerPressure(int32_t p_flow)
Given a flow in mL/min, return an estimated pressure just at the output of the blower.
Definition: blower.cpp:85
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 tidalVolumeCommand() const
int16_t pressure() const
Get the current measured pressure.
const int32_t targetInspiratoryFlowCommand() const
get target inspiratory flow in mL/min (used in VC modes)
int32_t currentDeliveredVolume() const
Get the measured Tidal Volume. Updated in real time.
int16_t peepCommand() const
Get the desired PEEP.
int16_t plateauPressureCommand() const
Get the desired plateau pressure.
int32_t inspiratoryFlow() const
Get the current inspiratoryFlow.
int16_t plateauDurationCommand() const
void ticksPerInhalationSet(uint32_t p_ticksPerInhalation)
Get the duration of an inhalation in ticks.
uint32_t ticksPerInhalation() const
Get the duration of an inhalation in ticks.
int32_t pressureCommand() const
Get the pressure command.
uint16_t minAperture() const
Minimum valve aperture angle in degrees.
void openSection(int32_t p_sectionMultiplyBy100)
Request opening of the Pressure Valve with a given section (in mm^2)
uint16_t maxAperture() const
Maximum valve aperture angle in degrees.
void close()
Request closing of the Pressure Valve.
uint16_t openLinear(uint16_t p_command)
Request opening of the Pressure Valve with a given angle with linearization.
Controller for the Volume Controled mode.
uint16_t m_blowerSpeed
Current blower speed.
int32_t m_blowerTicks
Blower ticks.
void endCycle() override
End the current breathing cycle.
int32_t m_inspiratoryFlowLastValuesIndex
Last flow index.
void calculateBlower()
Determine the blower speed to adopt for 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.
VC_CMV_Controller()
Default constructor.
void exhale() override
Control the exhalation.
int32_t m_inspiratoryPidIntegral
Integral gain of the inspiratory PID.
int32_t m_expiratoryPidLastErrorsIndex
Last error index in expiratory PID.
void initCycle() override
Begin a new breathing cycle.
int32_t m_maxInspiratoryFlow
Max flow during inspiration.
int32_t m_expiratoryPidLastErrors[PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN]
Last errors in expiratory PID.
int32_t m_expiratoryPidIntegral
Integral gain of the expiratory PID.
void setup() override
Initialize controller.
bool m_expiratoryPidFastMode
Fast mode at start of expiration.
int32_t m_inspiratoryFlowLastValues[NUMBER_OF_SAMPLE_LAST_VALUES]
Last flow values.
int32_t m_expiratoryPidLastError
Error of the last computation of the PID.
int32_t m_expiratoryValveLastAperture
Last aperture of the blower valve.
int32_t m_targetFlowMultiplyBy1000
int32_t m_inspiratoryValveLastAperture
Last aperture of the inspiratory valve.
void inhale() override
Control the inhalation.
MainController mainController
uint16_t i
static const int32_t PID_PATIENT_INTEGRAL_MIN
Definition: parameters.h:146
#define NUMBER_OF_SAMPLE_LAST_VALUES
Definition: parameters.h:154
#define VALVE_RESPONSE_TIME_MS
Definition: parameters.h:175
#define PC_NUMBER_OF_SAMPLE_DERIVATIVE_MOVING_MEAN
Definition: parameters.h:152
#define DEFAULT_BLOWER_SPEED
Definition: parameters.h:194
#define MAIN_CONTROLLER_COMPUTE_PERIOD_MS
Definition: parameters.h:28
static const int32_t PID_PATIENT_INTEGRAL_MAX
Definition: parameters.h:145
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
PressureValve inspiratoryValve
PressureValve expiratoryValve
VC_CMV_Controller vcCmvController