You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The *sampling rate* of the accelerometer, which can often be calibrated, determines the number of measurements per second. For instance, an accelerometer with a sampling rate of 100 returns 100 data points for each $x$, $y$, and $z$ time series every second.
31
31
@@ -57,15 +57,15 @@ The force of gravity causes an acceleration in the direction of gravity, which w
57
57
58
58
Suppose a smartphone is lying on a table screen-side up. In this orientation, our coordinate system is such that the negative $z$ direction is the one that gravity is acting on. Gravity will pull our phone in the negative $z$ direction, so our accelerometer, *even when perfectly still*, will record an acceleration of 9.8 $m/s^2$ in the negative $z$ direction. Accelerometer data from our phone in this orientation is shown in \aosafigref{500l.pedometer.accelerationtotalphonestill}.
59
59
60
-
\aosafigure{pedometer-images/acceleration-total-phone-still.png}{Example accelerometer data at rest}{500l.pedometer.accelerationtotalphonestill}
60
+
\aosafigure[333pt]{pedometer-images/acceleration-total-phone-still.png}{Example accelerometer data at rest}{500l.pedometer.accelerationtotalphonestill}
61
61
62
62
Note that $x(t)$ and $y(t)$ remain constant at 0, while $z(t)$ is constant at -1 *g*. Our accelerometer records all acceleration, including gravitational acceleration.
63
63
64
64
Each time series measures the *total acceleration* in that direction. Total acceleration is the sum of *user acceleration* and *gravitational acceleration*.
65
65
66
66
User acceleration is the acceleration of the device due to the movement of the user, and is constant at 0 when the phone is perfectly still. However, when the user is moving with the device, user acceleration is rarely constant, since it's difficult for a person to move with a constant acceleration.
To count steps, we're interested in the bounces created by the user in the direction of gravity. That means we're interested in isolating the 1-dimensional time series which describes **user acceleration in the direction of gravity** from our 3-dimensional acceleration signal (\aosafigref{500l.pedometer.componentsignals}).
71
71
@@ -81,7 +81,7 @@ What if a person carries the phone in a bag on their shoulder, with the phone in
81
81
82
82
Yikes. Now all three of our components have a non-zero gravitational acceleration, so the user acceleration in the direction of gravity is now split amongst all three time series. To determine user acceleration in the direction of gravity, we first have to determine which direction gravity is acting in. To do this, we have to split total acceleration in each of the three time series into a user acceleration time series and a gravitational acceleration time series (\aosafigref{500l.pedometer.component3}).
Then we can isolate the portion of user acceleration in each component that is in the direction of gravity, resulting in just the user acceleration in the direction of gravity time series.
$x_{g}(t)$ and $z_{g}(t)$ hover around 0, and $y_{g}(t)$ very quickly drops to $-1g$. The initial 0 value in $y_{g}(t)$ is from the initialization of the formula.
141
141
@@ -155,7 +155,7 @@ The result is the time series seen in
### 2. Isolating User Acceleration in the Direction of Gravity
@@ -170,22 +170,22 @@ When working with coordinates, you won't get very far before being introduced to
170
170
171
171
The dot product takes us from 3-dimensional space to 1-dimensional space (\aosafigref{500l.pedometer.dotproduct}). When we take the dot product of the two time series, user acceleration and gravitational acceleration, both of which are in 3-dimensional space, we'll be left with a single time series in 1-dimensional space representing the portion of user acceleration in the direction of gravity. We'll arbitrarily call this new time series $a(t)$, because, well, every important time series deserves a name.
We can implement the dot product for our earlier example using the formula $a(t) = x_{u}(t)x_{g}(t) + y_{u}(t)y_{g}(t) + z_{u}(t)z_{g}(t)$, leaving us with $a(t)$ in 1-dimensional space (\aosafigref{500l.pedometer.accelerationdotproduct}).
179
179
180
-
\aosafigure{pedometer-images/acceleration-dotproduct.png}{Implementing the dot product}{500l.pedometer.accelerationdotproduct}
180
+
\aosafigure[333pt]{pedometer-images/acceleration-dotproduct.png}{Implementing the dot product}{500l.pedometer.accelerationdotproduct}
181
181
182
182
We can now visually pick out where the steps are in $a(t)$. The dot product is very powerful, yet beautifully simple.
183
183
184
184
### Solutions in the Real World
185
185
186
186
We saw how quickly our seemingly simple problem became more complex when we threw in the challenges of the real world and real people. However, we're getting a lot closer to counting steps, and we can see how $a(t)$ is starting to resemble our ideal sine wave. But, only "kinda, sorta" starting to. We still need to make our messy $a(t)$ time series smoother. There are four main issues (\aosafigref{500l.pedometer.problems}) with $a(t)$ in its current state. Let's examine each one.
In accounting for these four scenarios, we've managed to bring our messy $a(t)$ fairly close to our ideal sine wave (\aosafigref{500l.pedometer.accelerationfiltered}), allowing us to count steps.
214
214
@@ -316,7 +316,7 @@ Dealing with multiple input formats is a common programming problem. If we want
316
316
317
317
The cleanest way for us to deal with this is to take our two input formats and fit them into a standard format as soon as possible, allowing the rest of the program to work with this new standard format. Our solution requires that we work with user acceleration and gravitational acceleration separately, so our standard format will need to be split into the two accelerations (\aosafigref{500l.pedometer.standardformat}).
Our standard format allows us to store a time series, as each element represents acceleration at a point in time. We've defined it as an array of arrays of arrays. Let's peel that onion.
322
322
@@ -328,15 +328,15 @@ Our standard format allows us to store a time series, as each element represents
328
328
329
329
The input into our system will be data from an accelerometer, information on the user taking the walk (gender, stride, etc.), and information on the trial walk itself (sampling rate, actual steps taken, etc.). Our system will apply the signal processing solution, and output the number of steps calculated, the delta between the actual steps and calculated steps, the distance travelled, and the elapsed time. The entire process from input to output can be viewed as a pipeline (\aosafigref{500l.pedometer.pipeline}).
In the spirit of separation of concerns, we'll write the code for each distinct component of the pipeline --- parsing, processing, and analyzing --- individually.
334
334
335
335
### Parsing
336
336
337
337
Given that we want our data in the standard format as early as possible, it makes sense to write a parser that allows us to take our two known input formats and convert them to a standard output format as the first component of our pipeline. Our standard format splits out user acceleration and gravitational acceleration, which means that if our data is in the combined format, our parser will need to first pass it through a low-pass filter to convert it to the standard format.
In the future, if we ever have to add another input format, the only code we'll have to touch is this parser. Let's separate concerns once more, and create a `Parser` class to handle the parsing.
342
342
@@ -410,7 +410,7 @@ We'll handle short and bumpy peaks by avoiding them during step counting.
410
410
411
411
Now that we have our data in the standard format, we can process it to get in into a state where we can analyze it to count steps (\aosafigref{500l.pedometer.input2}).
The purpose of processing is to take our data in the standard format and incrementally clean it up to get it to a state as close as possible to our ideal sine wave. Our two processing operations, taking the dot product and filtering, are quite distinct, but both are intended to process our data, so we'll create one class called a `Processor`.
416
416
@@ -665,13 +665,13 @@ We're through the most labour intensive part of our program. Next, we'll build a
665
665
666
666
When a user first enters the app by navigating to `/uploads`, they see a table of existing data and a form to submit new data by uploading an accelerometer output file and trial and user information (\aosafigref{500l.pedometer.app1}).
Submitting the form stores the data to the file system, parses, processes, and analyzes it, and redirects back to `/uploads` with the new entry in the table.
671
671
672
672
Clicking the **Detail** link for an entry presents the user with the following view in \aosafigref{500l.pedometer.app3}.
The information presented includes values input by the user through the upload form, values calculated by our program, and graphs of the time series following the dot product operation, and again following filtering. The user can navigate back to `/uploads` using the *Back to Uploads* link.
0 commit comments