From some recent threads on various on line forums it appears that the usual launch detect algorithm used with acceleration sensors is not optimal in that it can easily fail to detect launch in a number of circumstances.
The usual launch detect algorithm is to select some acceleration level, say 2G and when the acceleration exceeds that level, start a timer. Any time the acceleration drops below this level the timer is reset. Once the timer advances without being reset for some interval, launch detect occurs. The problems with this method are high motor vibration levels and low average acceleration that reset the timer and fast burning motors that burn out prior to launch detect.
The old classic problem is when the motor used has a lot of vibration energy such that it resets launch detect timers. This happens with some Hypertek hybrids. The newer problem which crops up mostly with Aerotech Warp-9 propellant is that the thrust duration is shorter than the launch detect window. There is a simple solution.
The first problem can be cured in one of two ways but the basic approach is to satisfy the Nyquist criteria. The first method is to insert a suitable low pass filter between the sensor and ADC. The best solution here involves one or more op-amps so it requires a bit of board space and of course will not help an existing design. The other option is to just bump the sample rate up high enough so that it isn't as big a problem.
Several years ago I saw some data captured by an RDAS. While the 200 Hz data from a Hypertek flight showed obvious problems it didn't have aliasing so the average thrust was more or less correct. But on a flight sampled at 50Hz the aliasing was obvious and disastrous. So while it appears that 200Hz might be adequate, 1,000Hz would provide more margin and is well within the capability of almost all ADC's. A simple RC low pass filter set to 50Hz still wouldn't hurt and is cheap.
That solves the aliasing problem but still leaves the problem of high vibration levels. That is solved along with the second problem with a new launch detect algorithm.
The solution is to give up the launch window concept and instead use a simple integration and use a velocity criteria for launch detect. I used this with my Kalman filter based altimeter and it worked well. It requires a slight modification to work without a pressure sensor.
The reason for this is that if a naive integration of the acceleration is performed, small errors can accumulate resulting in a false launch detect. This isn't a problem with the Kalman filter because the pressure reading compensates for this problem. In this case the approach is to only integrate the acceleration readings within a short window so that errors cannot accumulate.
So now the question is what velocity to use as the threshold and how long of a window to use? I believe that a velocity threshold that is similar to the rule of thumb for minimum velocity off the launch rod/rail of 40ft/sec should work well. For the window length I think that 1 second should be adequate. These two parameters do impose a lower limit on acceleration of about 1G. Since launch accelerations of less than 4G's is generally considered to be unsafe, this seems reasonable.
So how would the algorithm work? I think an example would illustrate it best so I cobbled up some code and used it on some RDAS data.
While the concept is simple, there are of course a lot of details that have to be taken care of.
The first step is to run the data through a low pass filter. I use a simple recursive filter because of its simplicity. Its performance is equivalent to a first order filter. Not great but adequate. A better filter could of course be used but that would require more resources.
The next step is to do something about a potential problem. Once the motor begins thrusting, we have to stop measuring the offset or the offset will be wrong. Since we are trying to figure out when this happens and will certainly be late, something must be done. I choose to use a double buffered approach.
Every two seconds the current value of the filtered acceleration is copied to a second variable. Since there is an excellent chance that we will get unlucky and this copy happens after the motor starts burning, this second copy is also copied to a third variable. Once launch is detected, this buffering stops and the offset is locked in for the apogee algorithm. Prior to launch detect the offset value used is always between two and four seconds old.
Another detail is number representation. It turns out that when using a 10 bit ADC like the RDAS does, there is enough uncertainty in the offset that significant errors in apogee time can happen. More ADC resolution is really needed here. Since the hardware is locked in place nothing can be done about that. But if the sensor outputs a small amount of random noise along with the signal, the average value can be determined with greater than 10 bits of precision. So I chose to interpret a 32 bit number as having 16 integer bits and 16 fractional bits.
The first step is to subtract 1/256 of the current offset value and the second is to add the current measurement divided by 256. The offset is shifted right 8 bits to the divide on it but the current reading shifted left 8 bits. This simpler than shifting it left 16 bits to match the number representation and then sifting it back right 8 bits to do the divide.
So here is the code to run the filter and buffer:
unsigned int filtered_accel[3];
int count = 0;
void filter(int accel)
{
filtered_accel[0] -= filtered_accel[0] >> 8;
filtered_accel[0] += accel << 8;
/* Every two seconds copy the current value.
Once launch is deteced, this stops to lock
in the offset.
*/
if(++count == 400 && not_launched)
{
count = 0;
filtered_accel[2] = filtered_accel[1];
filtered_accel[1] = filtered_accel[0];
}
}
This code requires a little bit of storage. Three sixteen bit words for the buffer and another for the counter. If the filter code looks a little bit odd I suggest you read my 2002 NAR R&D report as I discuss this filter type at more length there.
Another of the little details is initialization. The filtered offset is used by the main integration loop which could be a problem. The simplest way to initialize this is to just read one sample and use that. But this could be in error, from random noise or something else, by enough to cause a false trigger. The solution is to just wait until the double buffering scheme provides a value. This is done by initializing the buffered values to zero and waiting for a non-zero value to appear.
Once that happens we can subtract the offset from the current sample. But first the current sample must be adjusted to match the number representation used. Because I need more than 16 bits of dynamic range here so a 24.8 fixed point number representation is used. This means that both the offset and current measurement have to be adjusted. Also it turns out that because of the orientation of the sensor on the RDAS, forward acceleration results in smaller numbers. So the sign is inverted. This is handled by the following code:
if(filtered_accel[2] == 0)
continue;
acceleration = (filtered_accel[2] >> 8) - (accel << 8);
Now we get to the code to handle the one second window. This requires that we remember one second of data. At any but the lowest sample rates this requires a lot of storage. More storage than most inexpensive micro-controllers are likely to have. I reduce the storage required by breaking the one second of data into ten pieces. I integrate the acceleration for each tenth of a second and keep a copy. Now I only have to remember the ten values.
Rather than add up all ten values I take a slightly different approach. The velocity integration runs continuously but every tenth of a second I subtract the oldest buffered tenth of a second value. This makes sure that the integration covers only one second at a time.
if( not_launched)
{
if(++i == 20)
{
i = 0;
if(++j == 10)
j = 0;
velocity -= vbuf[j];
vbuf[j] = 0;
}
vbuf[j] += acceleration;
}
velocity += acceleration;
This code also stops the sliding window once launch is detected. For a launch detect algorithm that isn't required but it is required for apogee detection.
Finally we get to the decision for launch detect. Now we need to know how sensitive the accelerometer is. The sensor in the RDAS has a nominal range of +/- 50G so the 10 bit ADC gives a sensitivity of about 0.1G/count. The actual value is different of course but this is just to illustrate the algorithm. 40 ft/sec is equivalent to an acceleration of 1.25 G for one second so the test is:
if(velocity > (13 * 200 * 256))
not_launched = 0;
I rounded up the result of 10 counts/G * 1.25 G to 13 counts. 200 is the result of 1 second * 200 samples/second. 256 comes from the number representation since the lowest four bits are fractional.
One other note is appropriate here. This algorithm depends on the rocket being vertical. If this is running while someone is walking around with it, the variation in acceleration and offset is almost certain to result in a false launch detect. If there is a chance that some fool could do this the only way to protect him is to add another test here. Simple require that the current acceleration be greater than about 2.5 G. This allows for the rocket to have been held inverted and then turned upright.
Here is how the algorithm works on some real data. Several years ago I was given a set of RDAS data that is very handy for this. It was a very "buzzy" hybrid and it has a longer than usual set of pre-launch data. Here is a plot scaled to G's.
That would obviously give the usual launch detect algorithm fits. So how does this new one do?
This shows the integrated velocity in red and the green line shows when launch detect occurred. The units are simply ADC counts as nothing fancy is required for launch detect and apogee detection. A little more detail is perhaps in order so here is a plot showing just the pre-launch period so we can see if it drifts any.
This is basically just a random walk around zero and it never gets close to the launch detect threshold which happens to be 2600.
And now zooming in to the time around launch detection.
You can see that in spite of the noise launch detect occurs about 0.2 seconds after thrust begins. It should really take a bit longer but there is a very large single sample spike in the data at the start of thrust that pushes things up a bit. Another argument for low pass filters.
Here is the complete source file for my test code.
Play with it, use it, criticize it.
After I posted a notice of this to Rocketry Planet, Adrian posted that he used a velocity criteria but controlled baseline drift in a different way. So I thought I should add a discussion of that method here.
The problem is that the integrated velocity can drift and this drift might become large enough that the launch detect criteria is met. I chose to combat this drift by only looking at a fixed period of time. The idea being that the drift would not be large enough to cause a problem in so short a time interval. Adrian instead biases the acceleration measurement negative.
The idea here being that any drift in the velocity will then be towards negative values rather than positive and thus be away from the launch detect threshold. Since this would create a large drift if left alone, the velocity is constrained to always be greater than zero, or floored. Any time a negative value occurs, the velocity is reset to zero. The advantages of this method is that it is simple and it does not require as much memory.
I cobbled up some code to demonstrate this and it works pretty well. The one catch is that this biased velocity has some deliberately introduced error which would effect any apogee algorithm that used it. The simple solution is to run an unbiased integration simultaneously.
I suspect that the reason that this idea never occurred to me is that what started me down the road of investigating altimeters was that the RDAS firmware deliberately biased its acceleration readings which resulted in late apogee deployment. Which perhaps made me think that a bias was always bad.
The code to measure the offset is the same as before but the new launch detect code (complete) is:
/* remove the 1G offset. If the filtered value isn't available
yet, skip all the rest.
*/
if(filtered_accel[2] != 0)
{
acceleration = (filtered_accel[2]>> 8) - ((int)accel << 8);
biased_velocity += acceleration;
velocity += acceleration;
/*
The biased_velocity has its acceleration biased by about
-0.1G (1 count) during pre-launch.
*/
if(not_launched)
{
biased_velocity -= (1<<8); /* Apply the bias. */
if( biased_velocity < 0 )
{
biased_velocity = 0; /* floor the integrals */
velocity = 0;
}
}
if(velocity > (13 * 200 * 256))
not_launched = 0;
}
And as before, some data. First an overall view.
Then a look at the biased velocity prior to launch detect.
Launch detect.
And finally a look at apogee to see how much error the bias causes.
Obviously not a lot of error in this case. This error will increase with smaller initial motor thrust. In any case it is easy to run the unbiased integral.
And finally a look at the offset filter performance.
Aside from the glitch at the beginning (look at the plot of the acceleration above to see why this happens) it wanders about in the range of 522 and 522.3. This is worse than you would usually see because the RDAS acceleration data is not the best. It has no low pass filter on the accelerometer output and noise generated by the altimeter also appears. Most notably noise from the piezo buzzer. Dithering, which improves resolution, depends on the noise being "white". This means that it is uncorrelated and stationary. Stationary means that it doesn't change over time. Correlation is measured by computing an auto-correlation function which is just a comparison of the data to a time shifted version of itself. The periodic nature of the piezo noise in particular increases the correlation.