PersonalSoundtrack: Development Blog

The open-source music player that detects your walking or running speed and plays songs from your music library that match your pace. Song speed is adjusted in real-time to match subtle variations in your gait, while larger, deliberate pace changes cause the device to change songs. You simply put it on and begin moving; that's it.

Monday, November 27, 2006

Version 3.0 Ready for FINAL test build

Well, it's been a semi-long road, but i'm finally ready to build the complete unit. i'll recap what i've done since my last post.

i solved the accelerometer non-linear output issue. my delay loop was the culprit. before explaining the solution, it's important to understand the problem.

how am i determining steps per minute? one method is to keep a counter that allows me to track how much time has passed directly. that is, every 10 microseconds i increment a counter, and simply count how much time has passed and deduce steps per minute. for example, if 1 second passes between each step, that means the user is taking a step 60 times per minute, or 60 steps per minute. likewise, if 0.5 seconds passes between each step, the user is taking 2 steps every second, or 120 steps per minute. easy right?

i'm not so crazy about using time dependency like that because it relies too heavily on the accuracy of my timer. this is bad because time is highly dependent on the speed of the processor.

an alternative method to using time directly, is to measure time indirectly. we can do this by looking at how many samples the accelerometer has output. why? well, what is a sample rate? it is the number of samples that occur PER SECOND. that means the sample rate is a time-based measurement, but doesn't depend on the pic's clock speed. so how can we use the sample rate? well, first we need to know the sample rate of the accelerometer. for example's sake, let's say the accelerometer outputs 100 samples per second, a sample rate of 100hz. we can count how many samples are output by the accelerometer, by simply reading one complete pulse from the pulse wave. since we can do that, we can count how many samples are output between each step that is detected. continuing with our example, if the sample rate is 100 hz, and lets say we are busy reading samples, incrementing our counter every time one sample is read. if we read 100 samples, then detect a step, then read another 100 samples, then a step, how fast is the user walking? well, the user is taking 1 step every 100 samples, and we know that 100 samples are output every 1 second, thus the user is taking 1 step every second. likewise, if we read 50 samples then detect a step, then read another 50 samples, and detect another step, how fast is the user walking? the user is taking 1 step in every 50 samples, and we know that 50 samples is half 100, so half a second. thus, the user is walking 120 steps per minute.

now, the samples quite short. in fact, the accelerometer i have has a sample rate of 138hz; that's a full sample every 7 milliseconds. needless to say, when we are detecting how many samples go by, miniscule errors in the human step pattern result in large variations. no one can step to a beat with an accuracy of 7ms. so, if the system is off by the tiniest bit, the number of samples that we see occurring between each step detected will be greatly affected. while we might expect stepping at 60 steps per minute would be 138 samples, and 120 steps per minute would be half that (~70 samples), my incorrect code was only reading 40 samples at 120 steps per minute. that's less than half the number of samples that occur at 60 steps per minute, which doesn't make sense. it should be roughly half.

fine. so that was the issue. here's how i fixed it:

because the accelerometer is quite sensitive, and measuring vibration leads to an incredibly rough signal (the accelerometer's output looks like the output from a seismograph), we need to do some filtering so that we only count real steps. one step could be seen as 5-6 depending on how closely we look at the accelerometer's output. while i have tried several algorithms to filter the output, the most trivial method worked the best: if the accelerometer's output is large enough, we detect it as a step. this simple one beat out peak and valley measuring, dynamic max/min comparisons, windowed filtering, etc. while i'd like to say i created some nice algorithm to filter it, i can't; the stupid algorithm was the best.

so, the moment i detect a step, i punt out of reading the accelerometer, compute the steps per minute, AND (here's where the error occurred) i then did nothing for 250 milliseconds.

but why?

well, my reasoning was, the moment we see a step, we can simply ignore the next 250 milliseconds because a step every 250 milliseconds is approximately 300 steps per minute. that's very fast. i don't know many people that run that fast in normal situations. so, i figured hey just wait 250 ms then start reading again. that way, i don't count all the garbage vibrations that occur with every step. this was the error in my code.

first, the accelerometer does not output at a constant sample rate. instead, the sample rate is allowed to fluctuate, but the ratio between the on time of the pulse and the off time is always consistent. that means, the sample rate could drop to 80, but the pulses would be sized accordingly. armed with this new information, i set out to make my delay smarter. instead of just waiting a blind 250 ms, i decided to count a certain number of samples, in order to compensate for the changing sample rate. that way, i'd know that after each step, i ignored exactly 30 samples before resuming the program.

this also failed. why?

i wrote some code to output the length of each pulse as it was read. then i tested it. sure enough, the sample rate wasn't varying that much, and only varied when i took a step. we're talking 5-7 samples more or less when a step was taken. was it really THAT big a deal?

then i remembered the time scale i was measuring: one sample was only 7 ms. the slight variation in sample rate could throw the samples off by 5-7, and since that error was created each time i took a step, then the more steps i took per second, the more error accrued. ERRORS ARE CUMULATIVE! so, stepping at 60 steps per minute, i'm only incurring an error once per second, so the overall steps per minute calculation would be off by around 5 samples. not a big deal, but at 180 steps per minute, i was incurring somewhere around 15-21 sample errors. this was further exacerbated by the fact that higher steps per minute usually meant more vigorous vibrations which lead to larger sample rate variations.

so what i was assuming is that ignoring 30 samples would be the same amount of time delayed for each steps per minute. what's in fact happening is the length of time 30 samples takes is different for each steps per minute interaction. at 180 steps per minute, 30 samples a different amount of time than at 60 steps per minute.

my solution was to change the filtering from time-based to sample-based (in a different way). first, i removed the delay, tried it out, and saw that overall the samples were coming out correctly (138 for 60 steps per minute and 69 for 120 steps per minute). however, now i was detecting 2-3 steps for each real step that occurred. my new solution was this:


step detected?
if yes:
     was the step humanly possible?
     if yes:
         blink led
         calculate steps per minute
         send steps per minute out serial line
     if no:
         ignore


so what does humanly possible mean? well, i sat down and tapped this circuit at 300 beats per minute and how it tell me how many samples it was reading between each tap at that pace. it was able to read 30 samples between each step at 300 beats per minute. so humanly possible means, did you read at least 30 samples? now, the device will say "hey i saw a step" multiple times for a single step that actually occurrs, but it won't DO anything about it unless enough samples have been read since the last step. simple solution, perfect results.

now everything is working. next step is to build the prototype board (no breadboard) in a very small size using a pic socket ( so i can reprogram it as needed after real-world testing), and solder everything up to the gumstix board. simon penny has given me a bunch of non-rechargeable li-ion batteries that i can use for testing. i'll be ordering some 1000mhA li-ion batteries from sparkfun that are rechargeable.

tom has ordered some pic sockets, and hopefully they'll be here soon.

so in the end, it seems i've been able to take a large, belt-mounted power hungry device and shrink it down into a device that's the same size as my ipod nano, uses less power, and is SIGNIFICANTLY less computationally expensive.

:)

Tuesday, November 21, 2006

non-linear output of accelerometer solved... sort of

spoke with tom a bit today about the issue of non-linear output from the step detector.

he mentioned two problematic areas: the delay that i take after a step is detected, and the fact that i'm not taking the entire length of the pulse into account.

basically, i'm only looking at the t1 part of the pulse, the part where it's high. as soon as i've read all of the high part of the pulse, i punt, determine if the pulse was wide enough to count as a step, then dump the number of samples i'd read since the last step i detected.

so, t2 (the length of each pulse in total) isn't static. it's period changes based on how much acceleration is being applied, which in turn means the sample rate isn't constant. i went home and made some code changes to have it output t2 constnatly and started playing moving the accelerometer wildly. turns out t2 varies only a small amount, and not in a way that would create the non-linearity of the output. basically, the variation of t2 isn't really a big deal, and 99% of the time t2 is constant.

i wrote some code to have the step detector do nothing but loop as fast as possible reading the pulses, and blink the led after it had read X number of pulses. i then started trying to find how many samples were actually being read in 1 second. i put on a metronome, and found that about 138 samples between blinks = 1 second between blinks, more or less. it was close enough that the desynchornization period between the led and metronome took about 1 min. close enough for me.

SO. that means that, theoretically, if the accelerometer IS putting out a linear change in pulse width, i should be able to set the pic to blink an led every 69 samples and it will blink the led twice as fast, in fact, exactly 120 bpm. so i had it do that, set the metronome to 120, and guess what? almost perfect. 68 samples is close to 120 bpm, but hey, an error on that level is probably my eyes and is completely acceptable.

so the acceleromete IS outputting a linear curve. 138 samples = 60 bpm, and 68 samples = 120 bpm. yay.

i then tried to find the section of my code that would break this linearity. once i got to the delay between steps (after a step is detected, do nothing for about 30 samples so that we don't get double step detection for a single step). i could tap with 60bpm and get about 137 samples on average, and at 120 bpm i could tap the step detector and get about 70 samples between steps.

now i have to figure out why delaying for 30 samples after a step is detected screws things up. my guess is, as soon as a step is taken, t2 is changing the most. 30 samples at 120bpm may not be as long as 30 sample at 60bpm because t2 is sort of bouncing around while the signal settles down from the step.

perhaps filtering somewhere else less time-critical is a solution. i could simply have my python program on the gumstix ignore steps where only a few samples have passed. after all, it seems that 300 bpm is allows 30 samples to be read between steps. not many people can run 300 steps per minute (thats five steps every second) unless you're really sprinting. i could just make an upper bound on things, so that any step that has less than 30 samples between steps is just ignored.

Monday, November 20, 2006

Step detection circuit and the non-linearity of accelerometer output

so here's a more standard circuit diagram for the step detection circuit. of course it won't work without some code for the pic chip..



i'm still stuck on the weird output from the accelerometer. to calculate steps per minute, i'm looking at the number of samples that are read between each step that is detected. the idea is that at 60 steps per minute, the pic chip would read a number of samples from the accelerometer at the sample rate (say, 100hz). then, at 120 steps per minute, the pic chip would be reading about half as many samples between each step (if the sample rate is 100hz, for 120 steps per minute we'd be seeing 50 samples between each step). problem is, i mapped out BPM versus the number of samples that were read from the accelerometer, and it's not a linear curve. that is, at 60 BPM i get a consistent 110 samples read, but at 120 BPM i get around 45. huh?! it's also surprisingly a consistent curve.

need to talk to tom on this one..

Sunday, November 12, 2006

step detector!

so it's done. i have a full-fledged step detector.

final components:

3 capicators
1 resistor
1 led
1 accelerometer
1 pic chip
1 xtal (external oscillator)

it sends an alert out over serial when it detects a step, so that my gumstix board can interface with it.
code will be online soon.


here's a short video of the bread-board version in action (the LED lights up when a step is detected):

Sunday, November 05, 2006

xtal (cystal)!

ok, i'm a newbie. i wasn't using an external crystal for the pic timing. apparently the internal oscillator is useless. (thanks tom!)

so here's my new schematic. i'll be using an 18.432MHz xtal. it has three connections: two connect to clk1 and clk2 on the pic, and one connects to VDD.

tom also sent me some quick code that uses the USART component of the pic, so i don't have to do bit-banging anymore.


code snippet (courtesy tom jennings):

(cut and paste it directly, there are tabs in there that html isn't displaying)


; Initialize the UART for asyncrhonous in and out
; and set the bit rate. This sets PB2 (TD) as output,
; PB1 (RXD) as input and sets the bit rate to the factor
; W. See PIC16F628a data sheet PDF page 73-75.

serinit
banksel TRISB ; bank
bsf TRISB, RXD ; 1 RB1 is input, RD
bcf TRISB, TXD ; 1 RB2 is output, TD

bcf TXSTA, BRGH ; 1 set LOW B.R. range,
movwf SPBRG ; 1 set bit rate divisor,

bsf TXSTA, TXEN ; 1 Tx enable

banksel RCSTA
bsf RCSTA, SPEN ; 0 Rx enable,
bsf RCSTA, CREN ; 0 continuous Rx,

return

; Output W to the UART.

serout banksel PIR1
__so1 btfss PIR1, TXIF ; Tx full
goto __so1 ; wait...
movwf TXREG ; output to transmitter.
return


schematic:

pic usart serial references

i hate when people don't post the references they used to learn something, so i'm going to post what i've been reading on pic serial stuff.

pic 16f648a datasheet (page +-75 is all the serial stuff)

http://ww1.microchip.com/downloads/en/DeviceDoc/40044E.pdf

bit-banger serial tutorial
very good walk through of how serial works conceptually. it's really important to understand how serial works, and not the half-assed way of knowing. sit and read through the first paragraphs of this tutorial.

http://www.winpicprog.co.uk/pic_tutorial7.htm



explanation of common pic terms (CLK1, MCLR, RB7, TOCK1, VSS)
good for just learning what youre supposed to hook up to what.
http://www.mstracey.btinternet.co.uk/pictutorial/picmain.htm


BPS (bits per second) VS. Baud Rate
this is useful to understand in general. in fact, it's short, so if you're doing pic serial stuff, just read it. good to understand baud vs. bps.

http://www.totse.com/en/technology/telecommunications/bitsbaud.html



connecting pic to a PC
in case you want to.

http://www.tigoe.net/pcomp/code/archives/picbasic_pro/000293.shtml

Saturday, November 04, 2006

pic woes

haven't successfully gotten the pic to send serial data to the gumstix. i'm very bad at pic programming apparently. i've got some code in assembly, and i know the gumstix STUART inputs work fine (i used them for v2).

here's the circuit i'm using.. going to meet with tom on monday and see if we can work through this.

once serial is working, i'll put in the code to do PWM reading from the accelerometer.