Taming Analog Noise
Here's a few ways to use smart software to reduce the amount of
noise in your system.
 |
For hints, tricks and ideas about better ways to build embedded systems, subscribe to The Embedded Muse, a free biweekly e-newsletter. No hype, just down to earth embedded talk. 23,000 other engineers subscribe. It takes just a few seconds (all we need is your email address, which is shared with absolutely no one) to subscribe to the Embedded Muse. |
Last month I wrote about smoothing digital data, particularly
inputs from things like bouncing switches. It's just as important
to smooth some analog inputs, particularly when working with tiny
voltage levels in high noise environments.
One of the most important characteristics of analog noise is that
it is generally distributed over a wide frequency spectrum. Switch
on your PC near a shortwave radio and tune around - you'll hear
pops and squeals virtually everywhere you listen.
A powerful noise reduction technique is to simply filter out all
of the frequencies you don't need. In the case of a 60 Hz sine
wave, feed the input through a 60 Hz filter, and all of the noise
not exactly on frequency will disappear. If the noise is evenly
distributed over, say, 10 Khz, and the filter passes only a 10
Hz bandwidth then - voila! The noise is reduced by three orders
of magnitude.
This is how radios extract microvolts of signal from millivolts
of noise spread across the spectrum. The radio's mixer combines
the RF signal, which is a horrible jumble of thousands of real
signals and gobs of atmospheric noise, with a sine wave at a frequency
proportional to the one of interest. The result is fed through
a narrow filter that passes only the frequency you've tuned to,
removing nearly all of the noise.
DC Signals
What if you need to read a thermistor and compute very high accuracy
temperature readings? Perhaps your system measures dissolved oxygen
in a chemical bath, or light absorption at a specific wavelength
in pineapples.
In each of these cases you are trying to measure what amounts
to a static DC input - one that changes slowly with time. Noise
might swamp the signal, but the signal itself stays stable for
sizeable fractions of a second. How can we create the same sort
of narrow bandpass filter for these DC signals?
I've been collecting algorithms for years (uh, decades), yet have
found very little about how to smooth this sort of data. It seems
the most common approach is to simply average a number of readings
and then produce a result, but this simplistic method has a few
problems.
The first is response time. If the software needs a data point
NOW, it will have to wait for some large number of samples to
be read and averaged before proceeding.
The second problem is the "smearing" of data implicit
to any averaging scheme. The input presumably does change, albeit
slowly. Averaging gives a perhaps incorrect value based on present
and past readings.
Finally, averaging yields a declining return. Increase the number
of averages by an order of magnitude and the noise goes down only
by a factor of 2. For example, if 100 reads is still too noisy,
going to 1000 reads will increase the collection time by 10 but
only double the noise reduction.
To effectively remove noise from DC signals your code must balance
averaging time (you can't average forever because an answer is
needed eventually, and averaging over a changing signal distorts
the result) versus noise reduction. Increasing the number of samples
reduces noise but slows things down. Reduce sampling to get more
accurate signal shapes and faster response, but expect more noise.
Remember averaging's three problems:
- response time
- signal smearing
- declining return
Response Time
Thankfully, in an embedded system the firmware has full control
over the hardware's interrupts. We can immediately improve the
apparent response time of an averaging algorithm by programming
an ISR to constantly read the A/D in the background. When the
mainline code needs a value, the data is already accumulated in
a register in memory.
The ISR can't read and average the input data, because it runs
(presumably) asyncronously with respect to the code that needs
the results. It's best to just have the ISR read raw data into
a memory buffer, and let the main line code or some other task
take care of applying the averaging algorithm to the data.
To avoid accumulating old data, the ISR should gather N samples
in a FIFO buffer. These represent the most recent N readings from
the A/D. Whenever the ISR takes a reading it drops the oldest
sample from the buffer and adds the newest.
Whoever queries the buffer to get a reading then simply averages
the N sample points. Once the buffer is initially filled, then
the response time to a request for data is just the time taken
to do the averaging.
It's important to clear the buffer when significant events occur.
If the sensor assembly is active only when a lamp is on, for example,
then be sure to reset the FIFO's pointers at that time. Use a
simple semaphore to make a routine requesting data wait until
the N samples are taken. Or, return the average of the number
of samples accumulated until that number hits N. The first few
readings will be noisy, but they will be more or less immediate.
As the FIFO fills the signal will settle down.
The literature sometimes refers to the buffer as a "Boxcar".
Boxcars are a special case of the more general averaging technique.
Signal Smearing
If the input does indeed move slowly with time, then any sort
of averaging will smear the result. The output at time T is composed
equally of the signal's behavior now and when the average started.
In some cases the averaging distorts results excessively.
Having just almost sailed transatlantic (jeez... I hate
abandoning ship in mid-ocean), one example that immediately comes
to mind is an autopilot. The input to the computer is a quite
noisy flux-gate compass. The firmware must smooth the noisy compass
input, but must respond to changes in the boat's heading. Too
much averaging will slow the autopilot's response down, making
it ignore fast yawing in heavy seas.
One solution is to adjust the averaging algorithm so the current
reading counts more heavily than a reading taken a few tenths
of seconds ago. Using the data collected by the Boxcar FIFO, multiply
each point by a declining coefficient inversely proportional to
the point's age. For example:
point N 1.00
point N-1 .90
point N-2 .80
etc.
Sum the results. If you really need a true mathematical average
of the input data, then compute a divisor to properly reflect
the scaling of input data. Frequently in embedded systems we're
measuring some oddball thing like voltage and scaling it to match
a physical parameter, so the averaging divisor can often be left
out and combined into the scaling algorithm.
Unlike a Boxcar average, where all of the data is treated the
same regardless of its age, this does imply that the averaging
routine must track the FIFO's insertion pointer to know what the
most recent data is.
If all of the coefficients are exactly 1.0, then this technique
is identical to Boxcaring.
Those of you familiar with image processing may recognize this
technique as the Convolution. Convolutions are a powerful addition
to the smoothing repertoire, and are often used even in smoothing
AC signals.
Unfortunately, convolutions are no noise reduction panacea. The
averaging balancing act once more comes into play. By using an
ISR that accumulates data we've reduced response time, and by
using the convolution we've reduced smearing. However, noise on
the more recent signals contributes more to the result and "averages
out" less. Well, as the saying goes, life is hard and then
you die.
How should one pick the coefficients for a convolution? Looks
at the response time of the analog electronics, the speed of the
A/D, and the possible sampling rate of the ISR. Then, compute
the worst case smearing the system can tolerate. For example:
how much can the autopilot's firmware lag the boat's real position?
Pick coefficients that will give this much smearing or less. If
the system is still too noisy, you may have to speed up the A/D
and ISR to get more samples to average. Brute force is ugly, but
sometimes one has no choice.
I usually model the coefficients in a spreadsheet. Feed sample
data in and plot the results to see just how bad the smearing
effect will be. It's a quick way to check a formal analysis.
With all of the discussion in magazines now about neural networks
I wonder if we couldn't use a learning algorithm to adjust these
coefficients. If the system were "rewarded" for correct
results, smart software could dither the coefficients until optimal
results occurred.
Declining Return
We've looked at smearing and response time. The last problem with
averaging is that of declining return; really noisy signals need
lots of averaging, so acquisition time goes up much, much faster
than smoothing results.
Frankly... I don't have a clue how to handle this directly. Is
there a more effective smoothing technique? Please contact me
if you know of one, because I know many engineers who are saddled
with simple averaging and who are screaming for better results.
You can pre-filter the data stream. If the data is more or less
DC, then noise may represent itself as point-to-point dithering.
Sometimes you can reject those points that are not at least near
the current baseline. The problem lies in establishing what the
baseline should be.
If you have some a priori knowledge that no input should vary
more than some percentage from the baseline (a not unreasonable
assumption when working with a slowly changing signal), then you
can compute an average and reject the outriders. I recommend the
use of a sum-square computation, since most electronic noise is
more or less Gaussian.
It takes a lot of compute time to perform such a rejection. Worse,
sometimes the wrong data gets rejected! If you are forced to average
over only a few points due to time or smearing problems, then
any one really bad sample will throw off the whole average.
Another alternative is to try and fit a curve to the "noise".
Instead of trying to model the input data (which, after all is
more or less just a DC signal and really hard to do anything with),
model the noise and remove it.
Given their random behavior, normal electronic sources of noise
cannot be modelled effectively. However, often a lot of what we
call noise is really some physical aspect of the system. In the
case of the autopilot the biggest source of "noise"
is the yawing of the boat induced by wave action. Waves are periodic.
Some of the better autopilots remove this error by finding the
Fourier series that represents the periodic yawing, modelling
the yaw, and then subtracting this effect from the compass's output.
Changing sea conditions will alter the autopilot's yaw model,
so the firmware must constantly recompute Fourier coefficients
in a background task.
Conclusion
Noise reduction eats up a lot of compute power on many embedded
systems. We're pushing the state of the art on some of the analog
sensors, so can expect noise issues to become even more important.
Please feel free to contact me via Compuserve if you have algorithms
or just war stories.
|