Ein
Member
I'm trying to make a prop/cosplay thing that's basically a jetpack with a left and right thruster on it. I'm working on making it play sounds, and I want it to do so in response to the wearer's motion; particularly, I want it to detect if there's a sudden acceleration, play sounds, and increase the intensity of the lights in the thrusters in response.
I've already worked out the basics of the lights and the sounds I'm trying to build in. I have a Teensy 3.2 and a Prop Shield on it. The Prop Shield has motion sensing components and the NXPMotionSense library has given me a pretty decent starting point for tapping into that, I think - I was able to get everything calibrated and outputting information for me.
The sketch I'm running right now is here:
The output I'm getting seems about in line with what I'd expect.
And so on.
I can see the numbers jump in a relative sense if I move the device around; for instance, in the instant I pick it straight up off my desk:
All of this is great, I think, but I'm at a bit of a loss as to how to effectively use this data to bridge the gap to my goal. I'd like to come up with some way of calculating an overall 'activity' value, so I can set the intensity of things like the LEDs to ramp up and decay based on the wearer's movements.
I'm guessing that I need to keep a rolling average of values to compare each update against, and if the variation is big enough, assume that the wearer has moved in a sufficient way to trigger the 'jetpack' effect? But I'm at a loss as to how to establish bounds for these values and then compare them. The orientation values are straightforward enough - the SensorFusion filter handles those calculations - but there doesn't seem to be an equivalent simplification of the acceleration/gyroscope values, and frankly, I'm nowhere near math-smart enough to work through this without spending many many days on it.
Is there a... more general tool or function or library that I can feed the gyro/accel data into to simplify this task? Failing that, can I get any advice as to where to start picking through this problem? I started looking at code relevant to lightsabers, but a lot of those are more based on orientation/swinging than acceleration, and I felt like I started to go cross-eyed.
I've already worked out the basics of the lights and the sounds I'm trying to build in. I have a Teensy 3.2 and a Prop Shield on it. The Prop Shield has motion sensing components and the NXPMotionSense library has given me a pretty decent starting point for tapping into that, I think - I was able to get everything calibrated and outputting information for me.
The sketch I'm running right now is here:
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <play_sd_mp3.h> // https://github.com/FrankBoesing/Arduino-Teensy-Codec-lib
//#include <play_sd_aac.h>
#include <FastLED.h>
#include "easing.h"
#include <NXPMotionSense.h>
#include <EEPROM.h>
//AudioPlaySdWav playSdWav1; //xy=154,422
AudioPlaySdMp3 playMp31;
AudioMixer4 mixer1;
AudioOutputAnalog dac1;
AudioConnection patchCord1(playMp31, 0, mixer1, 0);
AudioConnection patchCord2(playMp31, 1, mixer1, 1);
AudioConnection patchCord3(mixer1, dac1);
NXPMotionSense imu;
NXPSensorFusion filter;
#define LEFT_RING_NEO_PIN 2
#define RIGHT_RING_NEO_PIN 3
#define N_LEDS 16
CRGB ringL[N_LEDS];
CRGB ringR[N_LEDS];
#define PROP_AMP_ENABLE 5
#define FLASH_CHIP_SELECT 6
//#define FLASH_CHIP_SELECT 21 // Arduino 101 built-in SPI Flash
uint8_t max_bright = 128; // Overall brightness definition.
void setup() {
Serial.begin(115200);
AudioMemory(8); //4
delay(2000);
imu.begin();
filter.begin(100);
FastLED.addLeds<NEOPIXEL, LEFT_RING_NEO_PIN>(ringL, N_LEDS);
FastLED.addLeds<NEOPIXEL, RIGHT_RING_NEO_PIN>(ringR, N_LEDS);
FastLED.setBrightness(max_bright);
// Start SerialFlash
if (!SerialFlash.begin(FLASH_CHIP_SELECT)) {
while (1) {
Serial.println ("Cannot access SPI Flash chip");
delay (1000);
}
}
listFiles();
//Set Volume
mixer1.gain(0, 2);
mixer1.gain(1, 2);
//Start Amplifier
pinMode(PROP_AMP_ENABLE, OUTPUT);
digitalWrite(PROP_AMP_ENABLE, HIGH); // Enable Amplifier
fill_solid(ringL, N_LEDS, CRGB::Green);
fill_solid(ringR, N_LEDS, CRGB::Green);
FastLED.show();
//Startup sound
playFile("WELC.MP3");
}
void listFiles(){
Serial.println("All Files on SPI Flash chip:");
if (!SerialFlash.begin(FLASH_CHIP_SELECT)) {
error("Unable to access SPI Flash chip");
}
SerialFlash.opendir();
while (1) {
char filename[64];
uint32_t filesize;
if (SerialFlash.readdir(filename, sizeof(filename), filesize)) {
Serial.print(" ");
Serial.print(filename);
spaces(20 - strlen(filename));
Serial.print(" ");
Serial.print(filesize);
Serial.print(" bytes");
Serial.println();
} else {
break; // no more files
}
}
}
void spaces(int num) {
for (int i=0; i < num; i++) {
Serial.print(" ");
}
}
void error(const char *message) {
while (1) {
Serial.println(message);
delay(2500);
}
}
void playFile(const char *filename)
{
SerialFlashFile ff = SerialFlash.open(filename);
Serial.print("Playing file: ");
Serial.println(filename);
uint32_t sz = ff.size();
uint32_t pos = ff.getFlashAddress();
// Start playing the file. This sketch continues to
// run while the file plays.
playMp31.play(pos,sz);
// Simply wait for the file to finish playing.
//while (playMp31.isPlaying()) {
//yield();
//}
}
void loop() {
if(!playMp31.isPlaying()){
playFile("R301.MP3");
}
readMotionSensor();
}
void readMotionSensor(){
float ax, ay, az;
float gx, gy, gz;
float mx, my, mz;
float roll, pitch, heading;
if (imu.available()) {
// Read the motion sensors
imu.readMotionSensor(ax, ay, az, gx, gy, gz, mx, my, mz);
// Update the SensorFusion filter
filter.update(gx, gy, gz, ax, ay, az, mx, my, mz);
//print the data
roll = filter.getRoll();
pitch = filter.getPitch();
heading = filter.getYaw();
Serial.print("Orientation: ");
Serial.print(heading);
Serial.print(" ");
Serial.print(pitch);
Serial.print(" ");
Serial.println(roll);
Serial.print("Accel X: ");
Serial.println(ax);
Serial.print("Gyro X: ");
Serial.println(gx);
Serial.print("Accel Y: ");
Serial.println(ay);
Serial.print("Gyro Y: ");
Serial.println(gy);
Serial.print("Accel Z: ");
Serial.println(az);
Serial.print("Gyro Z: ");
Serial.println(gz);
Serial.println("=============");
}
}
The output I'm getting seems about in line with what I'd expect.
Code:
Orientation: 58.26 -1.85 -2.94
Accel X: 0.03
Gyro X: -0.88
Accel Y: -0.06
Gyro Y: -0.56
Accel Z: 1.03
Gyro Z: 0.13
=============
Orientation: 58.26 -1.85 -2.94
Accel X: 0.03
Gyro X: -0.63
Accel Y: -0.06
Gyro Y: -0.50
Accel Z: 1.03
Gyro Z: 0.25
=============
Orientation: 58.27 -1.85 -2.93
Accel X: 0.03
Gyro X: -0.81
Accel Y: -0.06
Gyro Y: -0.56
Accel Z: 1.04
Gyro Z: 0.13
=============
Orientation: 58.27 -1.85 -2.93
Accel X: 0.03
Gyro X: -0.88
Accel Y: -0.06
Gyro Y: -0.38
Accel Z: 1.03
Gyro Z: 0.44
=============
Orientation: 58.27 -1.85 -2.93
Accel X: 0.03
Gyro X: -1.06
Accel Y: -0.06
Gyro Y: -0.31
Accel Z: 1.03
Gyro Z: 0.00
=============
And so on.
I can see the numbers jump in a relative sense if I move the device around; for instance, in the instant I pick it straight up off my desk:
Code:
Orientation: 40.70 -8.13 -0.81
Accel X: 0.06
Gyro X: 37.75
Accel Y: -0.02
Gyro Y: -2.44
Accel Z: 0.72
Gyro Z: -8.94
=============
Orientation: 40.74 -8.09 -0.73
Accel X: -0.12
Gyro X: 13.94
Accel Y: -0.18
Gyro Y: -3.44
Accel Z: 0.52
Gyro Z: 4.63
=============
Orientation: 40.93 -8.02 -0.81
Accel X: -0.12
Gyro X: -2.00
Accel Y: -0.18
Gyro Y: 2.31
Accel Z: 0.52
Gyro Z: 18.88
=============
Orientation: 40.98 -7.83 -0.50
Accel X: 0.01
Gyro X: 12.94
Accel Y: 0.44
Gyro Y: 18.88
Accel Z: 0.52
Gyro Z: 12.56
=============
Orientation: 41.02 -7.53 -0.09
Accel X: 0.01
Gyro X: 28.50
Accel Y: 0.44
Gyro Y: 28.63
Accel Z: 0.52
Gyro Z: 9.31
All of this is great, I think, but I'm at a bit of a loss as to how to effectively use this data to bridge the gap to my goal. I'd like to come up with some way of calculating an overall 'activity' value, so I can set the intensity of things like the LEDs to ramp up and decay based on the wearer's movements.
I'm guessing that I need to keep a rolling average of values to compare each update against, and if the variation is big enough, assume that the wearer has moved in a sufficient way to trigger the 'jetpack' effect? But I'm at a loss as to how to establish bounds for these values and then compare them. The orientation values are straightforward enough - the SensorFusion filter handles those calculations - but there doesn't seem to be an equivalent simplification of the acceleration/gyroscope values, and frankly, I'm nowhere near math-smart enough to work through this without spending many many days on it.
Is there a... more general tool or function or library that I can feed the gyro/accel data into to simplify this task? Failing that, can I get any advice as to where to start picking through this problem? I started looking at code relevant to lightsabers, but a lot of those are more based on orientation/swinging than acceleration, and I felt like I started to go cross-eyed.