Pharmaceutical companies all over the world are diligently working overtime to produce an accepted COVID-19 vaccine. While this work continues, other companies are already planning how to distribute a potential vaccine. Perhaps the most significant challenge exists in temperature control, as some COVID-19 vaccine candidates will require ultra-low temperatures (ULT), typically ranging from -86°C to -45°C. For COVID-19 vaccines requiring ULTs, maintaining these freezing temperatures throughout the process, from manufacturing to delivery to patients, is necessary. Failure at any point to do so can result in wasted vaccines, which are so desperately needed. Because the temperature level is critical to keeping the COVID-19 vaccine at maximum efficacy, the equipment to monitor and document temperature levels is essential. In particular, the ability to document storage conditions is critical.
Cold Chain monitoring has always been a very complex process, but the addition of monitoring of vaccines developed in response to the world-wide pandemic presents one of the greatest challenges ever faced by global supply chain contributors.
Getting the FDA’s authorization is only the first of several hurdles the companies will face in the race to the finish line. The Pfizer vaccine, whose working name is BNT162b2, has stringent storage requirements that may make it more difficult to distribute large numbers of doses nationwide.
BNT162b2 has to be kept unusually cold—as in about minus 70 degrees Celsius (minus 94 F), or within a range of minus 80 to minus 60 degrees C (minus 112 to minus 76 F). This is chillier than a winter’s day in Antarctica. By contrast, the seasonal flu vaccine has to be kept at a comparatively warm 4 degrees C, or 39.2 degrees F, according to Sheila Keating, PhD, associate professor of laboratory medicine at the University of California, San Francisco School of Medicine.
Why do COVID vaccines need to be kept so cold?It has to do with a very important ingredient: messenger RNA (or mRNA). The mRNA in the vaccines is genetic material that teaches our immune cells how to make the spike protein found on the virus that causes COVID-19, according to the Centers for Disease Control and Prevention (CDC). The immune system responds by creating antibodies against that specific spike protein, ultimately learning how to protect the body against the virus in the future.
By their very nature, mRNA molecules are unstable—if they hung out for a long time, our cells could accumulate harmful levels of the proteins they help create.
Introduction to the PoCAlthough Vaccines are ought to be stored at specific temperatures, and the temperature variable is widely considered for Monitoring Vaccine supply chain, however, there are a few more variables that have to be considered here. Vaccine storage units which are highly reliable, cost $5000 - $15000. As a result, low income regions prefer going with Dry Ice Storage units with lower accuracy in keeping up a constant temperature required, in shorts terms, not reliable. In this situation, along with temperature monitoring, it is important to introduce Humidity and Pressure variables. Pressure helps in assessing whether these storage units are sealed and do not let out air, which may provide constant temperature, but the payload on the storage units increases, decreasing efficacy and increasing costs. Similarly,Humidity measures dampness inside these units. Increased dampness isn't a good condition for these vaccines.
K-means Anomaly detection is a great method of assesment of these variables in Environmental monitoring to track the condition of Covid Vaccine over time. This algorithm helps in understanding when one of the three variables of Temperature, Humidity and Pressure varies and by what factor. The higher the factor of variance, the higher is the change in Environmental data.
For this project, I have been using Arduino Nano 33 BLE Sense, because of the ease of deployement and customisation on this board and highly accurate Environment sensor, but using EdgeImpulse, you can deploy this Model on many more EdgeImpulse supported boards / or using generated libraries on custom boards like -
- ST IoT Discovery Kit
- Silabs Thunderboard sense 2
- Nordic supported boards adding custom sensors
- Any board with supported libraries, attaching temperature, humidity, pressure modules like ESP32. You can also alter the modules by tweaking the sensor data on the ML Model a bit.
Ease of Model Deployment using EdgeImpulse:
EdgeImpulse simplifies the model training and deployement process to custom boards, so more attention can be diverted to applying these models in practical arrangements. Rather than sending Raw sensor data to cloud, processing on cloud and increasing latency between data transfer, Embedded Machine Learning makes it easier optimising models to be deployed to MCUs out of the box. This makes it energy efficient, faster, privacy enabled and of course, affordable. Rather than sending all the Raw data to cloud, applying ML algorithms on Cloud and later transferring the results on the board, the Anomaly detection and data processing can be done on the MCU and the finalised results will be sent to the Cloud for monitoring.
The project is public and hosted on EI Cloud and can be viewed there - https://studio.edgeimpulse.com/public/16818/latest. You can clone the project and make necessary changes as per need and deploy it in practical scenarios.
Capturing Raw Data from Sensors on Arduino Nano 33 BLE Sense:
Sensor data off board can easily be captured using the Data Forwarder tool on the EdgeImpulse CLI ( Command Line Interface ). https://docs.edgeimpulse.com/docs/cli-data-forwarder. The Data forwarder establishes communication with the sensors on the MCU and the EdgeImpulse Studio, collects Raw Data and visualises this data. This is a simple sensor data capture script which will colect the Temperature, Humidity and pressure variables off board: https://github.com/dhruvsheth-ai/K-means-Anomaly-detection/blob/main/Data-Forwarder/main.ino. Flash this script to your Arduino Board and run this command to capture data and send to the EdgeImpulse studio -
$ edge-impulse-data-forwarder
After you have done this, head to the EdgeImpulse studio to begin training.
Make sure you have cloned this project from the link given above before starting. In the Data capture example, I have scaled down the pressure variable by a thirds. So, the pressure would be divided by 3 while it is being captured. This is done because the pressure value at Room temperature i.e 30C was 100Pa. Scaling down by 3 helped the ML model learn better. Also Note: The Environmental data collected is data from Room temperature due to constraints in access of incredibly cold temperature which a Vaccine container requires. Hovewer, these values can be changed easily and a model can be trained to best fit you practical approach.
Below are some example samples of Data accumulated at room temperature in the training set, termed as "Normal":
The "Normal" Data has been collected as a single label in the training set. Over here, it is evident that Pressure variable has been scaled down, and usually remains constant, whereas "Humidity" shows defiance and varying values. This defiance is on a small factor and hence, the model interprets all these labels as "Normal". The below are samples of training set. These samples have highly varying variables and hence, during testing these will show the Value of anomaly with respect to the variance in Sensor readings.
Once the training samples are enough for the model to make accurate Anomaly estimates, proceed with the Impulse and parameters section.
Here, I have used two processing blocks, Flatten and Spectral value. Flatten helps in flattening an entire axis to a single value, which helps in particularly reducing Weight size clearing off unwanted readings. It's useful for sensor data which does not vary a lot over time. Spectral analysis helps in extractiing spectral features and power charecteristics for Repetitive data type.
The main part over here is the Learning block. Since we are working on finding Magnitude of Anomalies in data, it becomes evident to use the "K-means Anomaly" block. I have added the output of both the blocks as input for the K-means Anomaly detection Input features. This method helps in finding outliers in unknown data, exactly what we are looking into.
This is an algorithm which implements Anomaly detection in K-means clustering. A threshold value can be added to detect anomalies: if the distance between a data point and its nearest centroid is greater than the threshold value, then it is an anomaly.
Flattening Data:
The selected processing block here is Flattening, and is mainly useful for slow moving data. Raw features captured in a single axis are penalised to recieve a single value, later reducing weight size by Regularization. After Processing features here, we get derived constants from the values provided -
Since, we have trained it under just one class - "Normal" to understand whether the temperature is normal, and rest of the times print Anomaly, we do not need to bother a lot about where the Generated features are, but just note that all the features are clustered i.e. Not dispersed.
The features in the above image may seem dispersed but note that, the co-ordinates on the axis are of close values.
For Eg- For pressure, the points are from 30.202 to 30.204. For humidity, it is from 26 to 30. So, all these values comparitively are nearby.
Similarly, I used the "Spectral Features" processing block to give the data an edge, provide spectral power, frequency domain which is helpful if the data seems to be repetitive. Even static data is, so it might help in making the model more accurate.
Seems that most of the Feature extraction work is handled by EdgeImpulse, leaveing you room to explore where the Data locates.
I've added output from the processing blocks as input features for K-means anomaly detection Algorithm. Output features from both the blocks like Spectral power, Peak, Frequency, RMS, Avg etc are taken as input features in the learning block.
K-means algorithms is one of the most used unsupervised ML algorithms across various industries, and it is a powerful technique to cluster various input parameters into different clusters and find the centroid for each cluster. The final deliverables from this analysis will be providing number of clusters with each cluster centroid. As a new data point becomes available, the process can be automated in a fashion so that each new data point can be assigned to the predefined cluster centroids. This automated activity can be used to draw type curve boundaries accurately
You can make it out above that the output features generated from the processing blocks are used as input features here, and a standard score of 32 is set as cluster count ( Recommended, not compulsory to be in multiples of 32 ). Also, I have set the minimum score as 0.6 before tagging as Anomaly to ensure that arbitary data is not tagged as Anomaly. The score is not bound to be between 0 and 1, like a normal regression algorithm. Anomaly scores are negative as well as positive, depends on the Vector. Sometimes, I found the Anomaly scores to be around 600.
This is an example data collected tagged as an Anomaly in the plot. In the bottom, you can view the Anomaly score with a minimum of 7, maximum of 43, Avg of 26 which is way above 1. These are examples of testing set, you can view and make changes to get better results - https://studio.edgeimpulse.com/public/16818/latest
Embedded Machine Learning models can be improvised with every change you make, either in processing blocks or Learning blocks or data! Vaccine environmental sensing and monitoring is an important application for Embedded ML based Anomaly detection, and a lot of companies will soon adapt this concept.
You can view that the Algorithm predicts Anomalies with precision in the training set. Note - All data in training set is Anomaly data. Check it out on the public project, clone and edit it.
Deployment to MCU:
After the training, for a practical scenario, in order to deploy this project to monitor Anomalies in real time on site, it is necessary that the model is deployed to the MCU. Classification on cloud is an option where the inference is done on cloud, but that was a traditional methodology, which increases latency and inference time. You could deploy the Model to the MCU itself, decreasing latency, costs, and increasing number of integrations!
I'm using it as an Arduino library for deploying models to the Arduino Nano 33 BLE Sense. This board has the sensors needed to get the project in place as a PoC with highly reliable sensor readings. You can change the board to any other supported by EdgeImpulse or if the libraries work out, possibility is endless. Integrate custom sensor modules and you're done!
Flash this code to your Arduino Nano 33 BLE Sense-
From the public project provided, head to deployement and download the Arduino library. In case you wish to download it directly, clone this repo and head to the directory:
$ git clone https://github.com/dhruvsheth-ai/K-means-Anomaly-detection.git
$ cd K-means-Anomaly-detection
After this, you'll have the zip library with you and add this library to your arduino IDE. I'm using the web IDE, you could use any.
You cannot directly compile and upload the static_buffer
script to the board. You'll need to alter it a bit. Here's what you should deploy. Before deploying, I'll go through the code,
Since there is no more than one class for classification, all we need to focus on is anomaly classification. The calculations for the anomaly classification are done on the board to decrease latency between cloud communication and to be able to deploy in remote areas. The anomaly predicted needs to be monitored as well. For this purpose, the Particle Argon board communicates to a Particle Dasboard to know exactly when the Anomaly of the Vaccine containers increases and by how much. This uses a low power cellular connectivity to establish communication. I'd recommend you to use a LoraWan device and network for communication for the Best Remote low-power communication between peripherals. Since the Arduino Nano 33 BLE Sense doesn't have a WiFi unit, it'll serially communicate with the Particle Argon sending the Anomaly score to the board.
Here's the code for the Arduino Nano 33 BLE Sense, Have a look -
https://create.arduino.cc/editor/Schemobotics/025dc8bf-5ece-452e-ba46-168ff0b84e75/preview
#include <test_inference.h>
#include <Arduino_LPS22HB.h>
#include <Arduino_HTS221.h>
#define FREQUENCY_HZ 50
#define INTERVAL_MS (1000 / (FREQUENCY_HZ + 1))
// to classify 1 frame of data you need EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE values
float features[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE];
// keep track of where we are in the feature array
size_t feature_ix = 0;
static bool debug_nn = false;
float anomalydata;
typedef struct {
int anomaly;
int var1;
int var2;
int var3;
int var4;
int var5;
int var6;
} WeatherData;
// Forward declarations
void getWeatherData(WeatherData &data);
void sendWeatherData(const WeatherData &data);
// Global variables
char sendBuf[256];
void setup()
{
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Edge Impulse Inferencing Demo");
if (!HTS.begin()) {
Serial.println("Failed to initialize pressure sensor!");
while (1);
}
if (!BARO.begin()) {
Serial.println("Failed to initialize pressure sensor!");
while (1);
}
}
void loop() {
static unsigned long last_interval_ms = 0;
WeatherData data;
getWeatherData(data);
sendWeatherData(data);
delay(3000);
if (millis() > last_interval_ms + INTERVAL_MS) {
last_interval_ms = millis();
float t = HTS.readTemperature();
float h = HTS.readHumidity();
float p = BARO.readPressure();
// keep filling the features array until it's full
features[feature_ix++] = t;
features[feature_ix++] = h;
features[feature_ix++] = p * 0.3;
// full frame of data?
if (feature_ix == EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
ei_impulse_result_t result;
// create signal from features frame
signal_t signal;
numpy::signal_from_buffer(features, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
// run classifier
EI_IMPULSE_ERROR res = run_classifier(&signal, &result, true);
ei_printf("run_classifier returned: %d\n", res);
if (res != 0) return;
// print predictions
ei_printf("Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.): \n",
result.timing.dsp, result.timing.classification, result.timing.anomaly);
// print the predictions
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
ei_printf("%s:\t%.5f\n", result.classification[ix].label, result.classification[ix].value);
}
#if EI_CLASSIFIER_HAS_ANOMALY == 1
ei_printf("anomaly:\t%.3f\n", result.anomaly);
anomalydata = (float)result.anomaly;
#endif
// reset features frame
feature_ix = 0;
}
}
}
void getWeatherData(WeatherData &data) {
ei_impulse_result_t result;
// This just generates random data for testing
data.anomaly = anomalydata;
data.var1 = rand();
data.var2 = rand();
data.var3 = rand();
data.var4 = rand();
data.var5 = rand();
data.var6 = rand();
}
void sendWeatherData(const WeatherData &data) {
snprintf(sendBuf, sizeof(sendBuf), "%d,%d,%d,%d,%d,%d,%d\n",
data.anomaly, data.var1, data.var2, data.var3, data.var4, data.var5, data.var6);
Serial.print(sendBuf);
}
void ei_printf(const char *format, ...) {
static char print_buf[1024] = { 0 };
va_list args;
va_start(args, format);
int r = vsnprintf(print_buf, sizeof(print_buf), format, args);
va_end(args);
if (r > 0) {
Serial.write(print_buf);
}
}
Don't worry about var1, var2, var3 etc. They are random variables which you could use to communicate different information to your Argon. For example, send the raw features or Temp, Humidity, Pressure data in raw format. The Particle Argon will just print the Anomaly score to the dashboard. You can alter that as well!
Go to build.particle.io and flash this code to your Particle Argon( In a modification, you could use Nano 33 IoT or a LoraWan device would be the best choice since it is operational in remote locations of vaccine warehouses where WiFi bands can't reach. In case that's not available, proceed with the Argon)
Flash this to your Argon board -
https://go.particle.io/shared_apps/6038e0e0dec42800172c3491
#include "Particle.h"
// Constants
const size_t READ_BUF_SIZE = 256;
// Structures
typedef struct {
int anomaly;
int var1;
int var2;
int var3;
int var4;
int var5;
int var6;
} WeatherData;
// Forward declarations
void processBuffer();
void handleWeatherData(const WeatherData &data);
// Global variables
int counter = 0;
unsigned long lastSend = 0;
char readBuf[READ_BUF_SIZE];
size_t readBufOffset = 0;
void setup() {
Serial.begin(9600);
// Serial1 RX is connected to Arduino TX (1)
// Serial2 TX is connected to Arduino RX (0)
// Photon GND is connected to Arduino GND
Serial1.begin(115200);
}
void loop() {
// Read data from serial
while(Serial1.available()) {
if (readBufOffset < READ_BUF_SIZE) {
char c = Serial1.read();
if (c != '\n') {
// Add character to buffer
readBuf[readBufOffset++] = c;
}
else {
// End of line character found, process line
readBuf[readBufOffset] = 0;
processBuffer();
readBufOffset = 0;
}
}
else {
Serial.println("readBuf overflow, emptying buffer");
readBufOffset = 0;
}
}
}
void processBuffer() {
// Serial.printlnf("Received from Arduino: %s", readBuf);
WeatherData data;
if (sscanf(readBuf, "%d,%d,%d,%d,%d,%d,%d", &data.anomaly, &data.var1, &data.var2,
&data.var3, &data.var4, &data.var5, &data.var6) == 7) {
handleWeatherData(data);
}
else {
Serial.printlnf("invalid data %s", readBuf);
}
}
void handleWeatherData(const WeatherData &data) {
Serial.printlnf("got Anomaly=%d v1=%d v2=%d v3=%d v4=%d v5=%d v6=%d",
data.anomaly, data.var1, data.var2, data.var3, data.var4, data.var5, data.var6);
Particle.publish("Anomaly", String(int(data.anomaly)), PRIVATE);
}
Particle.publish()
publishes the Anomaly score to the Dashboard. Connect your boards with TX of Argon to RX of Arduino and RX of Argon to TX of Arduino. This way -
Once that's done, head over to console.particle.io and check the Anomaly results. I've converted the result to int()
for an easy view:
And, it publishes the data over to cloud with a standard delay of 3 seconds and a buffer of 300ms give or take, compared to 10seconds minumun latency and 20second calculation time over cloud.
Output from the Serial Monitor on Arduino IDE:
The first value detected is the anomaly score, while others are random values.
This can not only be accomplished by the Arduino Nano 33 BLE sense, but a few other boards supported directly by EdgeImpulse Studio, like Silabs Thunderboard Sense 2, ST IoT Discovery kit; both programmable over MBED IDE.
Comments