Week 6: Electronic Input Devices
Table of Contents
We covered various types of capacitive sensors this week, I use charged copper sheets to create my own water-level sensor and also tacked 3D positoning using an the MPU6050.
Items #
- MPU6050 (or another 6-9 axis IMU)
- Breadboard
- ESP32
- Wires
3D positioning with the MPU6050 #
At the start of the week, I thought a great idea would be to get a 3D positioning. The idea is that I could use the acceleration and gyroscope readings from the MPU6050, apply some fancy filters and data-processing to somehow get a more accurate reading, and double-integrate the processed acceleration values to obtain the 3D displacement.
$$ \dot{x}(t) = \int_0^t \ddot{x}(t) dt \implies v(t) = v(0) + at $$ $$ x = \int \dot{x}(t) dt = \int \int \ddot{x} dt \implies x(t) = x(0) + v(0)t + 0.5at^2$$
Calibration #
To calibrate the sensor, we first need to know how the gyro and accelerometer work. The accelerometer reads the force on a small mass relative to its own rest frame, so a stationary accelerometer should read 1G (9.81m/s) in the z-direction and 0G in the x and y directions. In free-fall, it would read 0G’s in all directions. The gyro reads the angular velocity of the sensor, so a stationary gyro should read 0 in all directions. The accelerometer provides an absolute measurement (not relative to an initial value) while the gyroscope is a relative measurement (measurement is relative to an initial value).
Adafruit MPU6050 Library #
The first thing we need to do is calibrate the sensor. This is because the sensor is not perfect, and will have some offset in the readings when stationary. There wasn’t really a best-practice for calibrating the sensor. The Adafruit library examples output numbers in $m/s^2$ for the accelerometer and $rad/s$ for the gyroscope. We can do this by taking the average of the readings over a period of time, and subtracting that from the readings.
Offsets
AccelX:0.54
AccelY:0.24
AccelZ:10.24
GyroX:-0.04
GyroY:0.17
GyroZ:0.46
Laying the sensor flat on a table, we can see that the accelerometer reads $10.24m/s^2$ in the z-direction, and $0.54m/s^2$ in the x-direction and $0.24m/s^2$ in the y-direction. The gyroscope reads 0.46rad/s in the z-direction, and 0.17rad/s in the y-direction and -0.04rad/s in the x-direction. This is a good start, but we can do better.
We can also calibrate the sensor by using gravity as a reference for all 6 sides of the sensor. If the sensor is perfectly linear, then we should expect that the accelerometer also reads $-10.24m/s^2$ in the z-direction if it is flipped over by 180 degrees along the $x$ or $y$ axis. We can also repeat this for the X and Y axes. After doing this, it does not seem like the sensor is linear, furthermore the axes don’t appear to be independent (however I have to give the sensor the benefit of the doubt because when doing the tests my level reference may not truly be perpendicular to gravity).
The gyroscope appears to give consistent offsets when the sensor isn’t moving which is a good sign.
Offsets
AccelX:0.54
AccelY:0.24
AccelZ:10.24
GyroX:-0.04
GyroY:0.17
GyroZ:0.46
AccelX:1.48
AccelY:0.10
AccelZ:-9.69
GyroX:-0.04
GyroY:0.17
GyroZ:0.46
Offsets
AccelX:0.70
AccelY:-9.78
AccelZ:-0.06
GyroX:-0.04
GyroY:0.17
GyroZ:0.46
Offsets
AccelX:0.80
AccelY:9.87
AccelZ:0.08
GyroX:-0.04
GyroY:0.17
GyroZ:0.46
Offsets
AccelX:10.62
AccelY:-0.01
AccelZ:0.39
GyroX:-0.04
GyroY:0.16
GyroZ:0.46
Offsets
AccelX:-8.93
AccelY:-0.14
AccelZ:-0.10
GyroX:-0.04
GyroY:0.17
GyroZ:0.46
Code for calibration
// Basic demo for accelerometer readings from Adafruit MPU6050
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
Adafruit_MPU6050 mpu;
void setup(void) {
Wire.begin(16, 17);
Serial.begin(115200);
while (!Serial) {
delay(10); // will pause Zero, Leonardo, etc until serial console opens
}
// Try to initialize!
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1) {
delay(10);
}
}
mpu.setAccelerometerRange(MPU6050_RANGE_16_G);
mpu.setGyroRange(MPU6050_RANGE_250_DEG);
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
Serial.println("");
delay(100);
}
void Update(){
float ax = 0;
float ay = 0;
float az = 0;
float gx = 0;
float gy = 0;
float gz = 0;
float currentMicros = micros();
float n = 0;
while(n<1000){
if (micros() - currentMicros > 100){ // Get less correlated readings
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
ax += a.acceleration.x;
ay += a.acceleration.y;
az += a.acceleration.z;
gx += g.gyro.x;
gy += g.gyro.y;
gz += g.gyro.z;
currentMicros = micros();
n += 1;
}
}
ax = ax/1000;
ay = ay/1000;
az = az/1000;
gx = gx/1000;
gy = gy/1000;
gz = gz/1000;
Serial.println("Offsets");
Serial.print("AccelX:");
Serial.println(ax);
Serial.print("AccelY:");
Serial.println(ay);
Serial.print("AccelZ:");
Serial.println(az);
Serial.print("GyroX:");
Serial.println(gx);
Serial.print("GyroY:");
Serial.println(gy);
Serial.print("GyroZ:");
Serial.println(gz);
}
void loop() {
/* Get new sensor events with the readings */
Update();
}
Electronic Cats library #
The Adafruit library is quite limiting in that it’s not really easy to decipher what goes on in the background, so after calibrating using the Adafruit library, I decided to try out the Electronic Cats library. The Electronic Cats library is much more flexible, it allows you to read raw values and also has functions that allow you to specify your sensor offsets. However, they only allow single offset values for each axis and thus don’t allow for non-linear calibration. One of the major benefits of the Electronic Cats library is accessing the Digital Motion Processor (DMP) (which will be explained below).
Sensor fusion #
Sensor fusion is the process of combining multiple sensors to get a more accurate reading. In our case, we are able to use the accelerometer readings to get a more accurate rotational reading compared to only relying on the gyroscope. The idea is that we integrate the gyroscope (since it is a relative measure) to get the change in orientation of the sensor. We can also use the acceleration readings to get a noisy absolute measure of orientation by assuming that total acceleration magnitude is 1G (when stationary) to obtain partial orientation information (pitch and roll, however yaw is unidentifiable through accelerometer readings). These calculations are relatively complex and I won’t explain them here but it is well documented in this blog post. There are many filters that you could use here, Kalman (plus extended variants) as well as Madgwick (plus extensions) are popular.
Coming back to the DMP… the electronic cats library allows us to access the DMP which does all this sensor fusion for us. After doing some research, there are some pros and cons to relying on the DMP.
Con: It appears that the DMP on the MPU6050 uses a proprietary algorithm, and source code isn’t available to understand how the sensors are combined. It is not the most accurate sensor-fusion algorithm out there. Pro: Sensor fusion processing can be quite costly, especially if many sensors are used with a single micro-controller. The DMP takes all sensor-fusion calculations off the microprocessor, freeing up processing power for other stuff.
Orientation #
3D positioning (dead-reckoning) #
My own sensor #
Using the copper sheets, I made my own capacitative water-level sensor. The schematic is shown