Pothole data smoothing and detection of accelerometer data

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
User avatar
arctic_eddie
 
Posts: 233
Joined: Tue Feb 28, 2012 6:01 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by arctic_eddie »

PotHole1Second
PotHole1Second
PotHole1Second.jpg (220.86 KiB) Viewed 1649 times
I had to resort to Gnumeric spreadsheet to do the integration. The graphs are not as nice as SciDAVis but the shape is more believable. The time data is in column 1 and the acceleration is in column 2. This is the one second data set. If it had been a little longer then you should see the displacement coming out of pothole. Here's the equation for the top cells. You can copy C2 and paste it into the remaining rows in that column. Likewise, you can copy D2 and paste it into the remaining rows in that column.

C1 = "=B1*0.01*32"
C2 = "=C1+B2*0.01*32"

D1 = "=C1*0.01*12"
D2 = "=D1+C2*0.01*12"

Here is the plot for acceleration(G), velocity(ft/sec) and displacement(in). This says the pothole was about 4" deep.

zacharoni16
 
Posts: 25
Joined: Thu Aug 16, 2012 6:21 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by zacharoni16 »

WOW Thanks for your hard work, I don't get the formulas you used, care to explain :), and I'm not sure where the 4inches is comming from on the graph confused haha.

Would this be possible to do in real time on the arduino in 1 second windows to tell how deep the potholes are in say... 1 second windows of data? or any length of time of windows. I guess the length of the window would be important because if the pothole event is divided between two windows then it would miss the depth and displacement. so the window size would have to capture a pothole completely right?

User avatar
arctic_eddie
 
Posts: 233
Joined: Tue Feb 28, 2012 6:01 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by arctic_eddie »

The third graph is displacement. The end point after one second says that the sensor had dropped about 4" below it's starting point. You would need to extract some data covering some time before and after an event to see the sensor come out of the pothole and back to the road average surface height.

The cell equations above are the content of the first two rows in the velocity(C) and displacement(D) columns of a spreadsheet. The left two columns are your original data for time and acceleration. The spreadsheet was the only way I could find to do the running sum numerical integration and the graphs. Column B, acceleration, integrates to C, velocity. Column C then integrates to D, displacement. C1 and D1 are the initial values for the integration of the column data. C2 and D2 are the beginning of the running sum. They needed a value in row 1 to start. The content of C2 and D2 can be copied into the row under themselves and all the way to the bottom. The spreadsheet will adjust the row numbers in the expression in each cell. The last row will have an expression that will include itself and the value above, the sum of all previous in that column.

In the mega, it will be very straight forward. In the loop() statement, you would collect the data in a circular buffer at your normal rate, probably 100Hz. The buffer would be long enough to hold perhaps five seconds of data. A circular buffer is one where the index pointer starts over at the beginning after reaching the end. The index minus one then always points to the most recent piece of data. As the data is collected, the running sum integration is updated for the velocity and displacement arrays. They would use the same pointer and have the same size. The output of those arrays would trigger threshold events. The sampling rate would be adjusted to be as fast as possible while allowing all worse case actions to be performed in the loop.

zacharoni16
 
Posts: 25
Joined: Thu Aug 16, 2012 6:21 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by zacharoni16 »

To do the integration itself, I would use the same forumulas you used in the spreadsheet? is the 32 from gravity 32/ft/sec?

I found this library, since all the data is floats : this seems to handle floats:

http://siggiorn.com/arduino-circular-byte-buffer/


A float on the Arduino is 4 bytes, @ 100HZ sampling rate 1sec = 100 floats = 400 bytes, for three arrays that is 1600 bytes

Then for 4 seconds of data: thats 4800 bytes for all three arrays

WOW lol I'll see if I can get away with maybe 60HZ sampling

User avatar
arctic_eddie
 
Posts: 233
Joined: Tue Feb 28, 2012 6:01 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by arctic_eddie »

Yes, the 32 is the conversion from G to ft/sec. In the D column, the 12 is feet to inches. The 0.01 in both columns is the integration time span or "dt" in the integral.

PS
We should look into scripting in SciDAVis using either muParser or Python. I didn't learn anything from the help file as it's for an earlier version and many of the screens and tables have changed. The matrix method should have worked for two columns but it didn't run as expected.

zacharoni16
 
Posts: 25
Joined: Thu Aug 16, 2012 6:21 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by zacharoni16 »

Didn't know SciDavis took Python scripting sweeet.

I don't know if I'll need external SRAM to hold all these floats or not in memory, I'll try 50HZ sampling rate and if it needs increased I'll increase it. I have to work on coding the circuilar buffer, not sure if that library would work out or not

Would I have to be concerned with drifting error of the accelerometer in the integration? I don't have to be 100% accurate, I would think since we are only looking a couple seconds at a time the error could be reset so it doesn't build up?

zacharoni16
 
Posts: 25
Joined: Thu Aug 16, 2012 6:21 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by zacharoni16 »

Can you check if I'm doing this right, I basically set up on my desktop, using rand() to generate random acceleration from -1.6 to 1.6 G's, capturing 3 seconds of data @ a simulated 50HZ sampling rate

It looks like you run *nix, I'm using Sleep() from Windows.h to make the 20mS delay

I just wanted to see if the code looks right to you, I didn't do the cicular buffer yet, I'm kinda confused on how to implement it: the commented out code, is from the class I'm working on for it, but I don't understand it 100% yet

Code: Select all

#include <cstdlib>
#include <iostream>
#include <BANNED>
#include <ctime> // USED BY RAND
#include <windows.h> // Used for delay


using namespace std;



#define SAMPLE_RATE   0.020 // Sample rate in Milliseconds
#define GRAVITYFT_SEC 32 // Gravity velocity 32 feet/sec
#define INCH_FOOT     12 // 12 inches in foot, from velocity to inch displacement calculation










int main(int argc, char *argv[])
{
    srand((unsigned)time(0)); // SEED RAND() for simulation of Geforce Readings
    
    // SIMULATING ACCELERATION READINGS INTO A CIRCULAR BUFFER
    
   // circular_buffer Acceleration; // Create a new Circular buffer for Acceleration
    
   // cb_init(&Acceleration, 150, 4); // Sampling @ 50HZ, 3 seconds of data = 150, size is float data of 4 bytes
    
    //Simulate a sample run of Acceleration data using Rand()
    
    // WE WILL BE SIMULATING "RANDOM" GEFORCE RATINGS using the rand() function constraining to -1.6 to 1.6 GFORCE 
    // These ratings are consistent with our road tests of apparently random vibration and Geforce readings not exceeding about 1.6 G's
    
    float Gforce[150]; // Random Geforce for 3 second window of data
    float velocity[150]; // Hold velocity information
    float displacement[150]; // Hold Displacement information
    
    
    float LO = -1.6; // Low GForce limit recorded from 6 road tests at different speeds
    float HI = 1.6; // High GForce limit recorded from 6 road tests at different speeds
    
    for(int i = 0; i < 150; i++) // 3 Second iwndow of random acceleration data
    {  
            Gforce[i] = LO + (float)rand()/((float)RAND_MAX/(HI-LO)); // Borrowed from Stackexchange : http://stackoverflow.com/questions/686353/c-random-float
            if( i == 0) // Initial values @ first Acceleration
            {
                velocity[i] = Gforce[i] * SAMPLE_RATE * GRAVITYFT_SEC; // Initial velocity
                displacement[i] = velocity[i] * SAMPLE_RATE * INCH_FOOT; // Initial Displacement
            }
            else
            {
                velocity[i] = velocity[0] + (Gforce[i] * SAMPLE_RATE * GRAVITYFT_SEC); // Calculate running velocity into buffer
                displacement[i] = displacement[0] +(velocity[i] * SAMPLE_RATE * INCH_FOOT); // Calculate running displacement into buffer
            }
            //cout << endl << Gforce[i]; // Debugging
            //cb_push_back(&Acceleration, &Gforce[i]);                   // Push the GeForce into the circular buffer
            
            
            Sleep(SAMPLE_RATE*1000); // 20mS delay simulates 50HZ sampling rate Sleep() expects number in mS already so * 1000
            
    }
    // PRINT RESULTS
    for (int j = 0; j < 150; j++)
            {
                cout << setprecision (3) << Gforce[j] << "\t\t" << velocity[j] << "\t\t" << displacement[j] << endl;
            }
    
    // READ THE BUFFER
    
    
    
    
    
    //cb_free(&Acceleration); // Pervent Memory leaks

    system("PAUSE");
    return EXIT_SUCCESS;
}
SAMPLE RUN:

Code: Select all


GFORCE          FT/SEC          Inch Displacement Z axis

-0.93           -0.595          -0.143
-0.377          -0.836          -0.344
0.343           -0.375          -0.233
1.16            0.148           -0.107
-0.559          -0.953          -0.371
0.601           -0.211          -0.193
0.124           -0.516          -0.267
-1.27           -1.41           -0.48
0.809           -0.0775         -0.161
-1.06           -1.27           -0.448
-1.16           -1.34           -0.463
-1.1            -1.3            -0.455
-1.33           -1.44           -0.489
0.256           -0.431          -0.246
-1.44           -1.52           -0.507
1.26            0.211           -0.0923
0.932           0.0015          -0.142
0.789           -0.0903         -0.164
0.318           -0.392          -0.237
-0.355          -0.822          -0.34
1.49            0.357           -0.057
1.38            0.289           -0.0733
1.13            0.131           -0.111
-0.342          -0.814          -0.338
-1.52           -1.57           -0.519
-0.406          -0.855          -0.348
-1.22           -1.37           -0.473
-0.182          -0.712          -0.314
0.0257          -0.579          -0.282
0.112           -0.523          -0.268
-0.624          -0.994          -0.381
-0.35           -0.819          -0.339
0.323           -0.388          -0.236
-1.26           -1.4            -0.479
-1.4            -1.49           -0.501
0.409           -0.333          -0.223
0.328           -0.385          -0.235
-0.713          -1.05           -0.395
-1.33           -1.45           -0.49
0.926           -0.00219                -0.143
1.39            0.293           -0.0725
-0.613          -0.987          -0.38
0.0183          -0.583          -0.283
-0.909          -1.18           -0.425
1.19            0.17            -0.102
1.47            0.345           -0.0601
0.844           -0.0549         -0.156
1.46            0.342           -0.0607
0.555           -0.24           -0.2
0.851           -0.0502         -0.155
1.19            0.164           -0.103
0.324           -0.387          -0.236
-0.951          -1.2            -0.432
0.75            -0.115          -0.17
-0.0729         -0.642          -0.297
0.641           -0.185          -0.187
0.909           -0.0133         -0.146
-0.149          -0.69           -0.308
0.461           -0.3            -0.215
-1.32           -1.44           -0.488
-0.402          -0.852          -0.347
-1.03           -1.25           -0.443
0.711           -0.14           -0.176
1.14            0.137           -0.11
0.797           -0.0849         -0.163
1.31            0.241           -0.085
-0.795          -1.1            -0.408
-0.658          -1.02           -0.387
1.1             0.107           -0.117
1.54            0.388           -0.0497
-0.688          -1.04           -0.391
0.0859          -0.54           -0.272
1.3             0.234           -0.0865
-0.853          -1.14           -0.417
-1.47           -1.53           -0.511
-0.973          -1.22           -0.435
0.409           -0.333          -0.223
-0.793          -1.1            -0.407
-0.664          -1.02           -0.388
1.31            0.247           -0.0836
1.27            0.22            -0.09
0.733           -0.126          -0.173
-0.324          -0.803          -0.335
0.894           -0.0229         -0.148
-0.528          -0.933          -0.367
-0.0603         -0.634          -0.295
-0.363          -0.828          -0.341
0.928           -0.00119                -0.143
-0.749          -1.07           -0.401
-1.14           -1.32           -0.46
1.55            0.399           -0.0469
-0.544          -0.943          -0.369
-0.644          -1.01           -0.384
-1.32           -1.44           -0.488
-1.52           -1.57           -0.519
0.383           -0.35           -0.227
-1.13           -1.32           -0.46
0.191           -0.473          -0.256
-1.27           -1.41           -0.481
-0.505          -0.918          -0.363
1.13            0.129           -0.112
0.39            -0.345          -0.226
1.32            0.248           -0.0832
-0.787          -1.1            -0.406
-1.27           -1.41           -0.48
1.23            0.191           -0.097
-1.04           -1.26           -0.445
-1.43           -1.51           -0.505
0.726           -0.13           -0.174
0.241           -0.441          -0.249
0.123           -0.516          -0.267
-0.663          -1.02           -0.387
-1.29           -1.42           -0.484
1.57            0.408           -0.0448
-0.408          -0.856          -0.348
0.341           -0.376          -0.233
0.191           -0.473          -0.256
1.05            0.0739          -0.125
1.18            0.16            -0.104
-1.43           -1.51           -0.505
0.157           -0.494          -0.261
-0.109          -0.665          -0.302
-0.511          -0.922          -0.364
1.06            0.0844          -0.123
0.161           -0.492          -0.261
0.659           -0.174          -0.184
-1.17           -1.34           -0.465
-1.06           -1.27           -0.448
0.201           -0.466          -0.255
1.58            0.417           -0.0426
-1.39           -1.48           -0.499
0.509           -0.269          -0.207
-0.881          -1.16           -0.421
0.595           -0.214          -0.194
0.852           -0.0494         -0.155
1.01            0.0509          -0.131
-0.804          -1.11           -0.409
-0.462          -0.891          -0.357
1.18            0.163           -0.104
-1.11           -1.31           -0.456
-0.542          -0.942          -0.369
0.64            -0.185          -0.187
-0.204          -0.725          -0.317
0.155           -0.496          -0.262
-1.01           -1.24           -0.441
-1.41           -1.5            -0.503
0.0378          -0.571          -0.28
-1.04           -1.26           -0.446
1.14            0.136           -0.11
-1.34           -1.45           -0.491
Press any key to continue . . .

































































User avatar
arctic_eddie
 
Posts: 233
Joined: Tue Feb 28, 2012 6:01 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by arctic_eddie »

The code will not generate an integral or running sum as written. The fix is simple.

You have -> velocity = velocity[0] + (Gforce * SAMPLE_RATE * GRAVITYFT_SEC);

That adds the new value to the first and not the previous sum. This code should fix the problem.

Fixed code -> velocity = velocity[i-1] + (Gforce * SAMPLE_RATE * GRAVITYFT_SEC);

The same error is found in the next statement for displacement. Just change the "0" to "i-1". The spreadsheet looks like your code should be that way but when you copy the content of C2 into C3, the referenced cell is always the previous one and not C1. That's accomplished by the "i-1" index.

zacharoni16
 
Posts: 25
Joined: Thu Aug 16, 2012 6:21 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by zacharoni16 »

AH man

I fixed it and the displacement is running up to like 180... so that would mean like a total displacement of 180 inches over 3 seconds ? Obviously if this was real data that would mean a pothole owuld be your least concern lol

Also do I put all three arrays in the circular buffer? I don't understand how to keep the data moving and locking in the 3 second windows with continous flow

Code: Select all

GFORCE          FT/SEC          Inch Displacement Z axis

-0.882          -0.565          -0.136
0.199           -0.437          -0.24
-1.32           -1.29           -0.549
0.928           -0.691          -0.715
0.6             -0.307          -0.788
1.47            0.635           -0.636
0.849           1.18            -0.353
-0.247          1.02            -0.108
1.29            1.85            0.335
0.298           2.04            0.824
-1.04           1.37            1.15
1.1             2.08            1.65
1.52            3.05            2.38
0.078           3.1             3.12
-0.0125         3.09            3.87
1.24            3.88            4.8
0.845           4.42            5.86
0.25            4.58            6.96
0.0463          4.61            8.06
1.37            5.49            9.38
-0.15           5.39            10.7
0.947           6               12.1
1.18            6.75            13.7
-0.791          6.25            15.2
-1.43           5.33            16.5
-1.58           4.32            17.5
1.52            5.29            18.8
-0.208          5.16            20.1
1.36            6.03            21.5
-0.294          5.84            22.9
1.22            6.62            24.5
1.14            7.35            26.3
1.01            8               28.2
0.284           8.18            30.1
1.18            8.93            32.3
-1.43           8.02            34.2
-0.167          7.91            36.1
1.14            8.64            38.2
-1.4            7.74            40
-1.49           6.79            41.7
-0.926          6.2             43.2
-0.575          5.83            44.6
0.978           6.46            46.1
-0.909          5.87            47.5
1.46            6.81            49.2
0.353           7.04            50.8
-1.12           6.32            52.4
-1.12           5.6             53.7
-0.141          5.51            55
0.463           5.8             56.4
-1.1            5.1             57.6
0.591           5.48            59
0.0912          5.54            60.3
-0.47           5.23            61.5
-0.437          4.96            62.7
0.734           5.42            64
-0.343          5.21            65.3
0.836           5.74            66.7
-1.11           5.03            67.9
-0.771          4.54            69
-0.783          4.04            69.9
-0.501          3.72            70.8
-0.569          3.35            71.6
0.765           3.84            72.5
0.568           4.21            73.5
-1.45           3.28            74.3
0.391           3.53            75.2
0.339           3.75            76.1
0.797           4.26            77.1
1.3             5.09            78.3
0.237           5.24            79.6
1.52            6.21            81.1
0.314           6.41            82.6
0.369           6.65            84.2
-0.598          6.26            85.7
-0.905          5.68            87.1
-0.732          5.22            88.3
-1.47           4.27            89.4
0.828           4.8             90.5
0.261           4.97            91.7
0.0473          5               92.9
1.53            5.98            94.3
1.24            6.77            96
-0.0228         6.76            97.6
-0.0453         6.73            99.2
-1.07           6.04            101
-0.345          5.82            102
0.652           6.24            104
1.37            7.12            105
1.15            7.85            107
0.0238          7.87            109
1.43            8.79            111
1.08            9.48            113
1.53            10.5            116
-0.709          10              118
-0.811          9.48            121
-1.06           8.8             123
-1.22           8.02            125
-1.4            7.13            126
0.129           7.21            128
0.199           7.34            130
-0.182          7.22            132
0.135           7.31            133
0.885           7.87            135
0.678           8.31            137
0.922           8.9             139
-1.54           7.91            141
-1.16           7.16            143
-0.632          6.76            145
1.3             7.59            146
-0.67           7.16            148
0.124           7.24            150
-1.19           6.48            151
-0.728          6.01            153
1.22            6.79            154
-1.33           5.94            156
-0.402          5.69            157
-0.532          5.35            159
1.27            6.16            160
0.323           6.37            162
0.428           6.64            163
0.414           6.91            165
-0.614          6.51            166
1.37            7.39            168
0.449           7.68            170
0.55            8.03            172
1.33            8.88            174
-1.2            8.11            176
-0.641          7.7             178
-1.59           6.69            179
1.02            7.34            181
-0.86           6.79            183
-1.55           5.79            184
-0.515          5.46            189
0.352           5.69            187
0.824           6.22            188
1.14            6.94            190
-1.03           6.29            192
-1.13           5.56            193
0.139           5.65            194
0.293           5.84            196
1.08            6.53            197
-1.23           5.75            199
-1.1            5.04            200
-1.17           4.29            201
-0.8            3.78            202
-0.905          3.2             203
-0.0769         3.15            203
-0.323          2.95            204
-0.0189         2.93            205
Press any key to continue . . .

User avatar
arctic_eddie
 
Posts: 233
Joined: Tue Feb 28, 2012 6:01 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by arctic_eddie »

Each of the three arrays would be a circular buffer which would be continuously filled and evaluated during actual road use. For your testing purposes, you can fill the accel array with real data for some length of time and calculate the other two arrays at the same index position as you collect each data point, just as you're doing now. The line that gets the random number would be replaced by an analog read of the sensor data. You could just clamp the sensor to a table and smack it with a book somewhere during the capture. Accel and velocity should ring and displacement should show some disturbance with the same height before and after the smack.

zacharoni16
 
Posts: 25
Joined: Thu Aug 16, 2012 6:21 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by zacharoni16 »

hmmm I'm perplexed can't get my circular buffer to work it keeps crashing :///




Code: Select all

#include <cstdlib>
#include <iostream>
#include <BANNED>
#include <ctime> // USED BY RAND
#include <windows.h> // Used for delay

using namespace std;


#define SAMPLE_RATE   0.020 // Sample rate in Milliseconds
#define GRAVITYFT_SEC 32 // Gravity velocity 32 feet/sec
#define INCH_FOOT     12 // 12 inches in foot, from velocity to inch displacement calculation
void READ_BUFFER();


class RingBuffer
{
private:
  int size;
  float* buffer;
  int start;
  int end;
  int cnt;

public:
  RingBuffer(int size);
  ~RingBuffer();
  void push(float value);
  float pop();
  float peek();
  int count();
};


RingBuffer::RingBuffer(int size)
: size(size), start(0), end(0), cnt(0)
{
  buffer = (float*)calloc(size, sizeof(float));
}

RingBuffer::~RingBuffer()
{
  free(buffer);
}

void RingBuffer::push(float value)
{
  buffer[end] = value;
  if (++end > size) end = 0;
  if (cnt == size) {
    if (++start > size) start = 0;
  } else {
    ++cnt;
  }
}

float RingBuffer::pop()
{
  float value = buffer[start];
  if (cnt > 0) {
    --cnt;
    if (++start > size) start = 0;
  }
  return value;
}

float RingBuffer::peek()
{
  return buffer[start];
}

int RingBuffer::count()
{
  return cnt;
}

    RingBuffer Gforce(150);
    RingBuffer velocity(150);
    RingBuffer displacement(150);
    
int main(int argc, char *argv[])
{
    srand((unsigned)time(0)); // SEED RAND() for simulation of Geforce Readings
    
    // SIMULATING ACCELERATION READINGS INTO A CIRCULAR BUFFER
    
    

    
    //Simulate a sample run of Acceleration data using Rand()
    
    // WE WILL BE SIMULATING "RANDOM" GEFORCE RATINGS using the rand() function constraining to -1.6 to 1.6 GFORCE 
    // These ratings are consistent with our road tests of apparently random vibration and Geforce readings not exceeding about 1.6 G's
    
    //float Gforce[150]; // Random Geforce for 3 second window of data
    //float velocity[150]; // Hold velocity information
   // float displacement[150]; // Hold Displacement information
    
    
    float LO = -1.6; // Low GForce limit recorded from 6 road tests at different speeds
    float HI = 1.6; // High GForce limit recorded from 6 road tests at different speeds
    
    for(int i = 0; i < 300; i++) // 6 Second iwndow of random acceleration data pretend this is streaming data from analogREAD()
    {  
             if (Gforce.count() == 150) // if we are at a 3 second window
               READ_BUFFER(); // READ the contents 
               
            Gforce.push(LO + (float)rand()/((float)RAND_MAX/(HI-LO))); // Borrowed from Stackexchange : http://stackoverflow.com/questions/686353/c-random-float
            /*
            if( i == 0) // Initial values @ first Acceleration
            {
                velocity.push((Gforce.peek() * SAMPLE_RATE * GRAVITYFT_SEC)); // Initial velocity
                displacement.push((velocity.peek() * SAMPLE_RATE * INCH_FOOT)); // Initial Displacement
            }
            */
            
           
            
                velocity.push(velocity.peek() + (Gforce.pop() * SAMPLE_RATE * GRAVITYFT_SEC)); // Calculate running velocity into buffer
                displacement.push(displacement.peek() +(velocity.pop() * SAMPLE_RATE * INCH_FOOT)); // Calculate running displacement into buffer
            
            
           
            
            Sleep(SAMPLE_RATE*1000); // 20mS delay simulates 50HZ sampling rate Sleep() expects number in mS already so * 1000
            
    }

    

    system("PAUSE");
    return EXIT_SUCCESS;
}

void READ_BUFFER()
{
         // PRINT RESULTS
               cout << "GFORCE" << "\t\t" << "FT/SEC" << "\t\t" << "Inch Displacement Z axis" << endl << endl;
    for (int j = 0; j < 150; j++)
            {
                cout << setprecision (3) << Gforce.pop() << "\t\t" << velocity.pop() << "\t\t" << displacement.pop() << endl;
            }
}

User avatar
arctic_eddie
 
Posts: 233
Joined: Tue Feb 28, 2012 6:01 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by arctic_eddie »

Here's a short sketch to collect the acceleration data and calculate the velocity and displacement arrays. The data will appear in the serial monitor and can then be copied and pasted into a spreadsheet. A few values in the #define section will have to be changed to match your experiment. Also, the ADC initializing in setup() and scaling and offset needs to be adjusted in loop(). If this works OK for you then I'll add the circular buffer part for continuous capture and threshold triggering. You may need to use a Mega due to memory requirements for the arrays.

Code: Select all

// Pothole Detector V1.0 by Arctic_Eddie, 2/5/2013
// This sketch detects and processes data collected from an accelerometer for a fixed length of time

// Define constants
#define SAMPLE_DELAY       200 // Sample delay in milliseconds, probably 20
#define GRAVITYFT_SEC   32.174 // Gravity to velocity,feet/sec
#define G_OFFSET           2.0 // Make the G reading zero for the resting state
#define G_SCALE            4.0 // If accelerometer is on +/- 2G scale
#define INCH_FOOT         12.0 // 12 inches in foot, from velocity to inch displacement calculation
#define NUM_SAMPLES         20 // Capture 6 seconds, probably 300
#define ACCEL_PIN           A0 // Use analog pin A0

// Define global veriables
uint16_t indeX = 0;                   // Pointer into data arrays
uint32_t dTime;                       // Clock time for delayed reading
float acceleration[NUM_SAMPLES];      // Space for accel samples
float velocity[NUM_SAMPLES];          // Space for velocity samples
float displacement[NUM_SAMPLES];      // Space for displacement samples

// Set up system
void setup()
{
    Serial.begin( 115200 ); // Prepare the serial monitor to receive the data
    
    Serial.println( "\n\rTime, Acceleration, Velocity, Displacement" ); // Print heading
    
    // Initialize the accelerometer ****************************
    
    // Start the delay clock
    dTime = millis();
}

void loop()
{
    // Collect the specified amount of data, calculate other values, and print results
    
    // This section is for a single pass to fill the arrays once ********************
    // Get a random value or use accelerometer reading here
    acceleration[indeX] = ( ( analogRead( ACCEL_PIN ) / 1024.0 ) * G_SCALE ) - G_OFFSET; // Get an acceleration reading
    
    // Calculate the running sum integral for velocity and displacement
    if( indeX == 0 ) // First reading, no previous data to add to running sum
    {
        velocity[indeX] = acceleration[indeX] * SAMPLE_DELAY * GRAVITYFT_SEC / 1000.0;
        displacement[indeX] = velocity[indeX] * SAMPLE_DELAY * INCH_FOOT / 1000.0;
    }
    else // Not the first reading so add the data from the previous index
    {
        velocity[indeX]     = ( acceleration[indeX] * SAMPLE_DELAY * GRAVITYFT_SEC / 1000.0 )
                              + velocity[indeX-1]; // The running sum
        displacement[indeX] = ( velocity[indeX] * SAMPLE_DELAY * INCH_FOOT / 1000.0 )
                              + displacement[indeX-1];     // The running sum     
    }
    
    // Print the results in spreadsheet readable format
    Serial.print( float( indeX * SAMPLE_DELAY ) / 1000.0 );    // The actual time value
    Serial.print( ", " );                                      // Add a comma separator
    Serial.print( acceleration[indeX] );                       // Acceleration
    Serial.print( ", " );                                      // Add a comma separator
    Serial.print( velocity[indeX] );                           // Velocity
    Serial.print( ", " );                                      // Add a comma separator
    Serial.println( displacement[indeX] );                     // Displacement
    
    // Pause until the next interval
        // Check clock for correct delay
    while( ( millis() - dTime ) < ( SAMPLE_DELAY * indeX ) ) {} // Pause here until sample interval has passed
    
    indeX++;
    if( indeX > NUM_SAMPLES ) // Stall here, test is over
    {
        Serial.print( "End of test" );
        while( true ) {} // Stall here
    }
}

// Supporting routines go here *********************************************

zacharoni16
 
Posts: 25
Joined: Thu Aug 16, 2012 6:21 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by zacharoni16 »

Yeah nice :) seems to work similar to my previous code.

The continuous sampling into the circular buffer with triggering is the hardest part I can't get working hahah Its bugging me right now haha :lol:

My accelerometer is -3g to +3g so scale would be 6.0 right? seems to be working

Here are some of my thoughts,

We will be sampling continously as long as the Arduino is running.

The continuously data is being streamed into the circular buffer of a "window" size of N seconds

The window size "array size" of N samples is then operated on by integration for velocity and displacement.

The current running sum, will get the total displacement for the window size of N seconds correct?

so for a pothole event, trigger, there would have to be a routine to find the max displacement given in the window at that time. Or the average then max, not sure

Would negative and positive displacement, be a key signature, if you have a large negative displacement and a large positive displacement within the window, that could be a pothole, or would we take the ABS() and not worry about the negative.

Also, would each "window" be independent, meaning everything gets reset as old data is being replaced by new data. I think if each window's displacement dependended on previous windows displacement, it would have a very large approaching infinity integration error?

User avatar
arctic_eddie
 
Posts: 233
Joined: Tue Feb 28, 2012 6:01 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by arctic_eddie »

The G_SCALE would be 6.0. However, if the ADC range, 0 - 1023, equals -3G to +3G then you can modify the map() function to get an fmap() function. Then fmap() the raw ADC reading directly to G. You can make the function by copying map() to fmap() then change the data type from long to float. You would still need to apply the G_OFFSET or you could skew the G map range so that the offset is included, -2G to +4G.

The window values will start with the end of the previous data, unless you reset the window arrays, preferred. Otherwise, your threshold detector might be confused if you had been going up or down a hill. You may have to maintain a running average of previous data as a baseline.

If the window arrays are reset each time then a pothole is some negative value relative to the starting value. You'll have to do some actual tests with an A-frame mounted sensor to see the real profile.

I'm working on the continuous version now. It doesn't have a window but is a continuous stream using a revolving short buffer. You would add threshold detectors and resets to the output arrays.

User avatar
arctic_eddie
 
Posts: 233
Joined: Tue Feb 28, 2012 6:01 pm

Re: Pothole data smoothing and detection of accelerometer data

Post by arctic_eddie »

Here is the continuous version. You will have to add your sensor initialization and adjust some of the #define statements. For continuous sampling, you don't need a buffer but only the sum of the previous values for the running integral. However, if you want a past history window for a running average then you will need the buffer. The buffer should hold data for twice the length of the event you want to detect. The running average will use the oldest half so that when the event is complete then the averaging window is just entering the event window.

Good luck on your project.

Code: Select all

// Pothole Detector V2.0 by Arctic_Eddie, 2/5/2013
// This sketch detects and processes data collected from an accelerometer on a continuous basis.
// Threshold detectors can be added which operate on the three arrays to count particular events.

// Define constants
#define SAMPLE_DELAY     200.0 // Sample delay in milliseconds, probably 20
#define GRAVITYFT_SEC   32.174 // Gravity to velocity,feet/sec
#define G_OFFSET           1.0 // Make the G reading zero for the resting state, usually 1.0 for actual use
#define G_SCALE            6.0 // If accelerometer is on +/- 3G scale
#define INCH_FOOT         12.0 // 12 inches in foot, from velocity to inch displacement calculation
#define NUM_SAMPLES         20 // Capture 6 seconds, probably 300
#define ACCEL_PIN           A0 // Use analog pin A0

// Define global veriables
uint8_t firstTime = true;             // First time through loop so initial calc not same as first rollover
uint16_t indeX    = 0;                // Pointer into data arrays
uint32_t tindeX   = 0;                // Total index counter and not reset
uint32_t dTime;                       // Clock time for startup reading
float acceleration[NUM_SAMPLES];      // Space for acceleration samples
float velocity[NUM_SAMPLES];          // Space for velocity samples
float displacement[NUM_SAMPLES];      // Space for displacement samples

// Set up system
void setup()
{
    Serial.begin( 115200 ); // Prepare the serial monitor to receive the data
    
    Serial.println( "\n\rTime, Acceleration, Velocity, Displacement" ); // Print heading

    randomSeed( analogRead( A1 ) ); // Seed the random number generator with noise
    
    // Initialize the accelerometer ****************************
    
    // Start the delay timer
    dTime = millis();
}

void loop()
{
    // Collect continuous data, calculate other values, and print results
    
    // This section is for continuous running to fill the arrays
    // Get a random value or use accelerometer reading here
    ///acceleration[indeX] = ( ( analogRead( ACCEL_PIN ) / 1024.0 ) * G_SCALE ) - G_OFFSET; // Get an acceleration reading
    acceleration[indeX] = float( random( 1025 ) - 512 ) / 1024.0; // Make it +/- 0.5G
    
    // Calculate the running sum integral for velocity and displacement
    if( indeX == 0 ) // First reading, no previous data to add to running sum
    {
        if( firstTime == true ) // Load the first position with initial data
        {
            velocity[indeX]     = acceleration[indeX] * SAMPLE_DELAY * GRAVITYFT_SEC / 1000.0;
            displacement[indeX] = velocity[indeX] * SAMPLE_DELAY * INCH_FOOT / 1000.0;
            firstTime           = false; // Prevent first time from happening again
        }
        else // Use last position, NUM_SAMPLES-1, just before rollover for accumulated values
        {
            velocity[indeX]     = ( acceleration[indeX] * SAMPLE_DELAY * GRAVITYFT_SEC / 1000.0 )
                                  + velocity[NUM_SAMPLES-1];         // The previous running sum
            displacement[indeX] = ( velocity[indeX] * SAMPLE_DELAY * INCH_FOOT / 1000.0 )
                                  + displacement[NUM_SAMPLES-1];     // The previous running sum     
        }
    }
    else // Not the first reading so add the data from the previous index
    {
        velocity[indeX]     = ( acceleration[indeX] * SAMPLE_DELAY * GRAVITYFT_SEC / 1000.0 )
                              + velocity[indeX-1];         // The running sum
        displacement[indeX] = ( velocity[indeX] * SAMPLE_DELAY * INCH_FOOT / 1000.0 )
                              + displacement[indeX-1];     // The running sum     
    }

    // Insert threshold detector, running average, and reset action here *******************
    
    // Print the results in spreadsheet readable format
    Serial.print( float( tindeX * SAMPLE_DELAY ) / 1000.0 );   // The actual time value
    Serial.print( ", " );                                      // Add a comma separator
    Serial.print( acceleration[indeX] );                       // Acceleration
    Serial.print( ", " );                                      // Add a comma separator
    Serial.print( velocity[indeX] );                           // Velocity
    Serial.print( ", " );                                      // Add a comma separator
    Serial.println( displacement[indeX] );                     // Displacement
    
    // Pause until the next interval, check clock for correct delay
    while( ( millis() - dTime ) < ( SAMPLE_DELAY * tindeX ) ) {}  // Pause here until sample interval has passed

    // Index the pointers
    tindeX++;                       // Increment the total index pointer
    indeX = ++indeX % NUM_SAMPLES;  // Increment the array indexer but cause rollover, circular buffer
}

// Supporting routines go here *********************************************
float fmap( float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”