XIAO_ESP32S3-Motion_Classification (3)
XIAO_ESP32S3-Motion_Classification (3)
Motion Classification
Creating a TinyML Anomaly Detection & Motion Classification project with the
Seeed XIAO ESP32S3 and IMU MPU6050
https://www.hackster.io/mjrobot/exploring-machine-learning-with-the-new-xiao-esp32s3-6463e5
Introduction
In my tutorial, TinyML Made Easy: Image Classification, we explored Image
Classification on the new tiny device of the Seeed XIAO family, the ESP32S3
Sense. The Sense has a camera and a mic incorporated, but what happens if
you want another type of sensor as an IMU? No problem! One great
advantage of the XIAO ESP32S3 is its several pins available as an I2C bus
(SDA/SCL pins).
Seeed Studio
On Arduino IDE, navigate to File > Preferences, and fill in the URL:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pag
es/package_esp32_dev_index.json
Next, open boards manager. Go to Tools > Board > Boards Manager… and
enter with esp32. Select and install the most updated package:
On Tools, select the Board (XIAO ESP32S3):
Last, but not least, select the Port where the ESP32S3 is connected.
That is it! The device should be OK. To be sure, run the Blink sketch.
For this project, we will use an IMU, the MPU6050 (or 6500), and a low-cost
(less than 2.00 USD) 6-Axis Accelerometer/Gyroscope unit.
With its dedicated I2C sensor bus, the MPU-6500 directly accepts inputs from
external I2C devices. MPU-6500, with its 6-axis integration, on-chip DMP, and
run-time calibration firmware, enables manufacturers to eliminate the costly
and complex selection, qualification, and system-level integration of discrete
devices, guaranteeing optimal motion performance for consumers. MPU-6500
is also designed to interface with multiple non-inertial digital sensors, such as
pressure sensors, on its auxiliary I2C port.
Usually, the libraries available are for MPU6050, but they work for both devices.
Connecting the HW
Go to Arduino Library Manager and type MPU6050. Install the latest version.
Download the sketch MPU6050_Acc_Data_Acquisition.in:
/*
* Based on I2C device class (I2Cdev) Arduino sketch for MPU6050 class by Jeff
Rowberg <[email protected]>
* and Edge Impulse Data Forwarder Exampe (Arduino) -
https://docs.edgeimpulse.com/docs/cli-data-forwarder
*
* Developed by M.Rovai @11May23
*/
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"
#define FREQUENCY_HZ 50
#define INTERVAL_MS (1000 / (FREQUENCY_HZ + 1))
#define ACC_RANGE 1 // 0: -/+2G; 1: +/-4G
void setup() {
Serial.begin(115200);
// initialize device
Serial.println("Initializing I2C devices...");
Wire.begin();
imu.initialize();
delay(10);
// // verify connection
// if (imu.testConnection()) {
// Serial.println("IMU connected");
// }
// else {
// Serial.println("IMU Error");
// }
delay(300);
void loop() {
Serial.print(ax_m_s2);
Serial.print("\t");
Serial.print(ay_m_s2);
Serial.print("\t");
Serial.println(az_m_s2);
}
}
Note that the values generated by the accelerometer and gyroscope have a
range: [-32768, +32767], so for example, if the default accelerometer range is
used, the range in Gs should be: [-2g, +2g]. So, "1G" means 16384.
For conversion to m/s2, for example, you can define the following:
When you ran the code with the IMU resting over your table, the
accelerometer data shown on the Serial Monitor should be around: 0.00,
0.00, and 9.81. If the values are a lot different, you should calibrate the
IMU.
The MCU6050 can be calibrated using the sketch: mcu6050-calibration.ino.
Run the code. The following will be displayed on the Serial Monitor:
Send any character (in the above example, "x"), and the calibration should
start.
Note that A message MPU6050 connection failed. Ignore this message. For
some reason, imu.testConnection() is not returning a correct result.
In the end, you will receive the offset values to be used on all your sketches:
Take the values and use them on the setup:
Once you run the above sketch, open the Serial Monitor:
Or check the Plotter:
Move your device in the three axes, and you should see the variation on
Plotter:
So, to start, we should collect data. Then, accelerometers will provide the data
on the palette (or container).
From the above images, we can see that primarily horizontal movements
should be associated with the "Terrestrial class, " Vertical movements with the
"Lift Class, " no activity with the "Idle class, " and movent on all three axes to
Maritime class.
Follow the instructions here to install the Node.js and Edge Impulse CLI on your
computer.
Once the XIAO ESP32S3 is not a fully supported development board by Edge
Impulse, we should, for example, use the CLI Data Forwarder to capture data
from our sensor and send it to the Studio, as shown in this diagram:
Image from author
You can also capture your data "offline", storing them on an SD card or send data
via Bluetooth or Wifi for your computer. In this video, you can learn alternative
ways to send data to the Edge Impulse Studio.
Connect your device to the serial port and run the previous code to capture
IMU (Accelerometer) data, "printing them" on the serial. This will allow the
Edge Impulse Studio to "capture" them.
Next, start the CLI Data Forwarder on your terminal, entering (if it is the first
time) the following command:
$ edge-impulse-data-forwarder --clean
Next, enter your EI credentials, and choose your project, variables, and device
names:
Go to your EI Project and verify if the device is connected (the dot should be
green):
Data Collection
As discussed before, we should capture data from all four Transportation
Classes. Imagine that you have a container with a built-in accelerometer:
Now imagine your container is on a boat, facing an angry ocean, on a truck,
etc.:
You can capture, for example, 2 minutes (twelve samples of 10 seconds each)
for the four classes. Using the "3 dots" after each one of the samples, select
2, moving them for the Test set (or use the automatic Train/Test Split tool on
the Danger Zone of Dashboard tab). Below are the result datasets:
Data Pre-Processing
The raw data type captured by the accelerometer is a "time series" and should
be converted to "tabular data". We can do this conversion using a sliding
window over the sample data. For example, in the below figure,
we can see 10 seconds of accelerometer data captured with a sample rate
(SR) of 50Hz. A 2 seconds window will capture 300 data points (3 axis x 2
seconds x 50 samples). We will slide this window each 200ms, creating a
larger dataset where each instance has 300 raw features.
You should use the best SR for your case, taking into consideration, Nyquist's
theorem, which states that a periodic signal must be sampled at more than twice
the highest frequency component of the signal.
On the Studio, this dataset will be the input of a Spectral Analysis block, which
is excellent for analyzing repetitive motion, such as data from accelerometers.
This block will perform a DSP (Digital Signal Processing), extracting features
such as "FFT" or "Wavelets". In the most common case, FFT.
The Time Domain Statistical features per axis/channel are:
● RMS
● Skewness
● Kurtosis,
● Spectral Power
● Skewness
● Kurtosis
So, for example, for an FFT length of 32 points, the resulting output of the
Spectral Analysis Block will be 21 features per axis (a total of 63 features).
Those 63 features will be the Input Tensor of a Neural Network Classifier and
the Anomaly Detection model (K-Means).
You can learn more by digging into the tutorial TinyML under the hood: Spectral
Analysis.
Model Design
Our classifier will be a Dense Neural Network (DNN) that will have 63 neurons
on its input layer, two hidden layers with 20 and 10 neurons, and an output
layer with four neurons (one per each class), as shown here:
Impulse Design
An impulse takes raw data uses signal processing to extract features and then
uses a learning block to classify new data.
We also take advantage of a second model, the K-means, that can be used
for Anomaly Detection. If we imagine that we could have our known classes
as clusters, any sample that could not fit on that could be an outlier, an
anomaly (for example, a container rolling out of a ship on the ocean).
For that, we can use the same input tensor that goes to the NN Classifier as
the input of a K-means model:
Below is our final Impulse design:
Generating features
At this point in our project, we have defined the pre-processing method and
the model designed. Now it is time to have the job done. First, let's take the
raw data (time-series type) and convert it to tabular data. Go to the Spectral
Features tab, and select Save Parameters:
At the top menu, select Generate Features option and Generate Features
button. Each of our 2 seconds window data will be converted into one data
point of 63 features each.
The Feature Explorer will show those data in 2D using UMAP. Uniform Manifold
Approximation and Projection (UMAP) is a dimension reduction technique that
can be used for visualization similarly to t-SNE but also for general non-linear
dimension reduction.
Training
Our model has four layers, as shown below:
As hyperparameters, we will use a Learning Rate of 0.005 and 20% of data
for validation for 30 epochs. After training, we can see that the accuracy is
97%.
And for Anomaly Detection, we should choose the suggested features that are
precisely the most important ones found in the Feature Extraction. The
number of clusters will be 32 as suggested by the Studio:
Testing
Using 20% of the data left behind during the data capture phase, we can
verify how our model will behave with unknown data; if not 100% (what is
expected), the result was not that good (8%), mainly due to the terrestrial
class. Once we have four classes (which output should add 1.0), we can set
up a lower threshold for a class to be considered valid (for example, 0.4):
Now, the Test accuracy will go up to 97%.
You should also use your device (which is still connected to the Studio) and
perform some Live Classification.
Be aware that here you will capture real data with your device and upload it to
the Studio, where an inference will be taken using the trained model (But the
model is NOT in your device).
Deploy
Now it is time for magic˜! The Studio will package all the needed libraries,
preprocessing functions, and trained models, downloading them to your
computer. You should select the option Arduino Library and at the bottom,
select Quantized (Int8) and Build. A Zip file will be created and downloaded to
your computer.
On your Arduino IDE, go to the Sketch tab and select the option Add.ZIP
Library and Choose the.zip file downloaded by the Studio:
Inference
Now it is time for a real test. We will make inferences wholly disconnected
from the Studio. Let's change one of the code examples created when you
deploy the Arduino Library.
In your Arduino IDE, go to File/Examples tab and look for your project, and on
examples, select nano_ble_sense_accelerometer:
Of course, this is not your board, but we can have the code working with only
a few changes.
For example, at the beginning of the code, you have the library related to
Arduino Sense IMU:
/* Includes --------------------------------------------------------------- */
#include <XIAO-ESP32S3-Motion-Classification_inferencing.h>
#include <Arduino_LSM9DS1.h>
Change the "includes" portion with the code related to the IMU:
#include <XIAO-ESP32S3-Motion-Classification_inferencing.h>
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"
On the setup function, initiate the IMU, set the offset values and range:
// initialize device
Serial.println("Initializing I2C devices...");
Wire.begin();
imu.initialize();
delay(10);
//Set MCU 6050 OffSet Calibration
imu.setXAccelOffset(-4732);
imu.setYAccelOffset(4703);
imu.setZAccelOffset(8867);
imu.setXGyroOffset(61);
imu.setYGyroOffset(-73);
imu.setZGyroOffset(35);
imu.setFullScaleAccelRange(ACC_RANGE);
At the loop function, the buffers: buffer[ix], buffer[ix + 1], and buffer[ix + 2] will
receive the 3-axis data captured by the accelerometer. On the original code,
you have the line:
You should change the order of the following two blocks of code. First, you
make the conversion to raw data to "Meters per squared second (ms2)",
followed by the test regarding the maximum acceptance range (that here is in
ms2, but on Arduino, was in Gs):
buffer[ix + 0] *= CONVERT_G_TO_MS2;
buffer[ix + 1] *= CONVERT_G_TO_MS2;
buffer[ix + 2] *= CONVERT_G_TO_MS2;
And that is it! You can now upload the code to your device and proceed with
the inferences. The complete code is available on the project's GitHub.
If you get an error trying to upload the code to the XIAO ESP32S3 as below,
you should switch off ESP NN acceleration.
To do that, locate ei_classifier_config.h in exported Arduino library folder:
/scr/edge-impulse-sdk/classifier/:
Regarding the IMU, this project used the low-cost MPU6050 but could also
use other IMUs, for example, the LCM20600 (6-axis), which is part of the
Seeed Grove - IMU 9DOF (lcm20600+AK09918).
One advantage of the last device is that it has integrated a Grove connector,
which can be helpful in teaching in the case you are using the XIAO with an
extension board, as shown below:
You can follow the instruction here to connect the IMU with the MCU. Only
note that for using the Grove ICM20600 Accelerometer, it is essential to
update the files I2Cdev.cpp and I2Cdev.h that you will download from the
library provided by Seeed Studio. For that, replace both files from this link.
You can find on the GitHub project a sketch for testing the IMU:
accelerometer_test.ino.
On the project's GitHub repository, you will find the last version of all codes and
other docs: XIAO-ESP32S3 - IMU.
Knowing more
If you want to learn more about Embedded Machine Learning (TinyML),
please see these references:
On the TinyML4D website, You can find lots of educational materials on TinyML.
They are all free and open-source for educational uses – we ask that if you use
the material, please cite them! TinyML4D is an initiative to make TinyML
education available to everyone globally.
That's all, folks!
As always, I hope this project can help others find their way into the exciting
world of AI!
link: MJRoBot.org
Thank you
Marcelo