This small device will detect bad posture, if the person wearing this device has it attached to the back.
- 3dprint: STL-Files for printing
- images: All images used in the report or in this readme
- nerd-neck: Source code: Entry point for the program
- utility: Source code: Utility functions and the madgwick adapter
- nerd-neck-report: The mdbook to render the report to pdf
We read IMU data in a cycle. Every 50 milliseconds we receive IMU data. As the IMU values drift, we need a filter(s): Those correct the drift integrated over time with sensor fusion algorithms.
If the orientation of the device passes a certain threshold, the device will start beeping for 2 seconds, notifying the person to rectify its position. It will remain beeping, until the person corrected its position.
The whole device is a wearable, so it needs a battery (LiPo) and a 3d printed casing.
- Calculate an angle to Z-Axis (with Filters), that can be used as "beep" input
-
Do a calibration for the Gyro before starting (if not moving, gyro values should be 0).(Filter already corrects offsets) - Write a little rust driver for the speaker, that controls it
- On a configurable threshold - enable the trigger for the beep speaker
- Apply a LiPo (3.7 Volts) battery adapter to the battery pins
- Design 3d model for the casing to print
- Print the casing
- Assemble the parts
- Write Report with Markdown
- Slides with UniBas flavour
Gyroscope does drift, when integrated over time, so we need to correct those values with sensor fusion algorithms, with other sensor values. Both IMUs we can use, also come with an accelerometer. So we combine smooth short-them gyroscope data with long-term stability of accelerometer data.
Possible Filters:
- Complementary filter (simpler but effective for many use cases).
- Madgwick filter (a fast, quaternion-based algorithm for IMUs).
- Mahony filter (similar to Madgwick with some differences in accuracy and computation).
- Kalman filter (complex but precise, especially for combining multiple sensors).
Note: As we are only interested in the tilt of the IMU (diff angle to the vertical z-axis), we can neglect the yaw angle (which would be corrected be the magnetometer, which we do not have).
So we can use either Madgwick or Mahony with only given accelerometer and gyro values.
There is already a third party crate (ahrs-rs), that already implements both Madgwick and Mahony filters for both 6DoF and 9DoF IMUs.
A choice to be done is the filter gains, KI and KP for Mahony and Beta for Madgwick.
Some recommended beta values for the Madgwick Filter, where you have to choose between responsiveness and stability:
Drones/RC vehicles | 0.1 - 0.3 | High dynamics, prioritize responsiveness. |
---|---|---|
Robotics (general) | 0.05 - 0.2 | Moderate dynamics, balance response and drift. |
Slow-motion systems | 0.01 - 0.05 | Low dynamics, prioritize stability/noise rejection. |
Human motion tracking | 0.01 - 0.1 | Prioritize stability but allow small dynamic motion. |
Static applications | 0.001 - 0.01 | Low dynamics, focus on maximum stability. |
TL;DR: We use the Madgwick filter with beta between 0.01 and 0.1. Currently, we use 0.1. The paper for madgwick is here.
The Madgwick Feedback loop (from paper above):
We have several IMU Sensors
Both can be addressed via a I2C-Bus.
There is already a MPU6050 crate. It's usable with i2c.
There is already a BMI160 create. Also usable with i2c.
This bus comes with a dataline (sda) and a line for the clock signal (scl).
You can connect several devices to the bus, like shown below.
Note from espressif:
The frequency of SCL is influenced by both the pull-up resistor and the wire capacitance. Therefore, users are strongly recommended to choose appropriate pull-up resistors to make the frequency accurate. The recommended value for pull-up resistors usually ranges from 1K Ohms to 10K Ohms. Keep in mind that the higher the frequency, the smaller the pull-up resistor should be (but not less than 1 KOhms). Indeed, large resistors will decline the current, which will increase the clock switching time and reduce the frequency. We usually recommend a range of 2 KOhms to 5 KOhms, but users may also need to make some adjustments depending on their current draw requirements.
TL;DR: Use 2-10kOhm resistors as pull up resistors. The higher the frequency, the lower the ohmage should be.
We use 4.7k Ohms as pull-up resistors.
We have an i2c setup, where two 4.7k Ohm resistors are used as pull up resistors. If scl/sda are off, both signals are pulled up to the logical 3.3 Volts (high). The IMU (on the right) will pull the signal down if needed.
Everything runs on the 3.3-volt level of the MCU.
The piezo speaker is connected to GPIO pin 7. We use a 100 Ohms resistor to protect the GPIO from draining too much current. So the piezo speaker drains at max 3.3v/100 Ohms = 33 mA of current. It's powered with a 1 khz pwm signal, when triggered.
Below the fritzing image is demonstrating the wiring. The parts are aliases though, we use different ones, but the wiring is exactly the same.
Below all used Materials
- MCU: Seeed xiao esp32s3, link
- IMU: DF Robot BMI160 (6 DoF-IMU), link
- 2x 4.7k Ohm Resistors as pull up resistors for the I2C connection
- 1x 100 Ohm Resistor for the piezo speaker
- Breadboard
- Active Piezo Buzzer, link
- Wires
- MCU: Seeed xiao esp32s3, link
- IMU: DF Robot BMI160 (6 DoF-IMU), link
- 2x 4.7k Ohm Resistors as pull up resistors for the I2C connection
- 1x 100 Ohm Resistor for the piezo speaker
- Active Piezo Buzzer, link
- Wires
- Battery: LiPo, 3.7 volts with 1500mAh: link
- JST-PH crimp plugs and sockets (To not directly solder the battery to the MCU), link
Install all the depenendencies:
- Install Rust
- Install esp tooling
cargo install espup espflash
espup install
Then go into the nerd-neck
directory and
source ~/export-esp.h
(generated from espup install
).
To build and flash the firmware to the device,
just run cargo run --release
.
The casing must include
- a LiPo battery (3.7 Volts), e.g. this one (52x42x5mm),
- the BMI160 IMU from DFRobots with dimensions 23x27mm as well as the
- the esp32s3 from seeed with dimensions ~20x25mm. Also having access to its usb-c port.
- the piezo buzzer with its dimensions, e.g. this one (12mm)
As the casing will be quite flat, we have on one layer the flat LiPo battery on the bottom and on the other layer the IMU, the MCU and the piezo buzzer. The wiring will be in between those two.
Also: It should be possible to exchange all parts.
The casing will have the dimension of 56x46x10mm, so we have a will thickness of 1mm on each side.
All below versions were designed with FreeCad, an open-source CAD Software running on all major platforms (Mac, Linux and Windows).
The first design looks like this:
Where the parts are the following:
- Red: the IMU
- Grey: Esp32s3
- Yellow: Piezo speaker
- Blue: the LiPo battery
The casing itself consists of a bottom and a top casing. The bottom casing houses the battery. The top casing holds the rest. Both parts are designed that way, that the bottom half slips into the top half and snaps in.
The whole casing has the following dimensions: 56x46x15mm (circle on top for the buzzer has hight of 6mm)
Changes to V0.1:
- To make the design more 3D-print friendly, there is no exterior for the buzzer any more. The downside is, the buzzer is exposed a bit.
- The hole for the usb got bigger, as it did not go through without drilling.
- The two holders, that hold the bottom part, got 1mm longer, as the bottom part did not fit in.
Changes to V0.2:
- We thickened the holders by increasing their thickness from 1 mm to 2 mm.
- We resized the hole for the usb-c port.
- Decrease the height of the piezo buzzer holder, as the battery would not fit
- Decrease diameter of piezo buzzer hole from 13 mm to 12.3 mm
Changes to V0.3:
Instead of having a top and a bottom, the bottom part can now slide into the top part. As such, we do not need any clips, to hold the bottom port. Only with gravity the casing holds. The bottom part must be attached to a t-shirt in the end, as this will hold the complete construction.
Instead of sliding and clipping, we also tried a pressfit version. The basic idea is that both parts, you want to connect do not perfectly fit to one another. Instead they have an overlay, so you have to press them into each other.
The shear friction between those parts is enough, so they stick together.
The obvious pros. The parts are much easier to design, as you can see below (and compare them with the previous version)
Printed, it looks like this:
Below images of the assembled prototype, case opened and case closed
Opened:
Closed: