Monday, May 2, 2016

And now for something completely different...

Spent the weekend out at the coast with a group of aviator friends at the only beach state airport in the lower 48, Copalis State Airport at Copalis Beach, WA.  The sand is rock hard at low tide and I think you could have landed a 747 out there.  The airport normally has about 220 aircraft operations per year.  We landed over 54 aircraft on Saturday from all over the Pacific Northwest, hung out for several hours and departed without leaving any trace of our having been there.  The weather was perfect with about 15 knots of wind straight out of the north making for a rather epic day at the beach.



Sunday, April 24, 2016

Satellite tracking

The beginnings of a new project.  Software is coming together, but now it's time to pull together the hardware.  This will be an antenna azimuth/elevation  satellite tracker.  In previous posts I have explored the calculations necessary and the details of using hobby servos to create a simple proof of concept.


For now I am experimenting with the ATMega2560 board (on the left) as it has plenty of flash and RAM space for the task at hand.  I plan to control the device via a web page that it serves up, so for now I have an ethernet shield attached, though in the end the plan is to use WiFi.

Calculating satellite passes requires an accurate position for the observer in terms of latitude and longitude, but also elevation and time-of-day.  I am using a GPS module to provide this information (lower right).

While the mighty ATMega2560 is certainly capable of generating the necessary PWM signals to position servos, I have decided to offload that to separate controller based on the PCA9685 which provides I2C interfacing of 16 channels of 12 bit PWM data.  This particular board was originally intended to be used as an RGB LED controller, but my plan is to re-purpose it for this task.  It has its own internal 25 MHz oscillator that offloads the ATMega2560 from having to generate these signals.  That, plus the fact that I happened to have one on hand drove the decision to use this component to drive my servos.

After spending some time in the Avionics industry and working on electronic aviation attitude indicators, the math involved in Euler angle solutions to the orientation of a rigid body, plus the mapping of raw gyro, accelerometer and magnetometer sensor data into 3D space is a bit tedious.  Fortunately for me, Bosch has done an amazing job of taking MEMS data from on-die sensors and adding an ARM Cortex M0 processor to produce a single chip solution that abstracts away all the raw sensor fusion and spits out data directly in quaternions, Euler angles or vectors.

The plan is to use this sensor mounted on the antenna fixture to measure azimuth and elevation relative to the calculated position on earth with no careful alignment of antenna orientation required.  The Arduino will accept keplerian elements for the desired satellite and then automagically track the spacecraft as it passes overhead.

More updates as this comes together into a demonstrable solution.

Saturday, April 16, 2016

Interesting problem

Today, I was writing some code that had some bitmap files embedded in them.  Not wanting  to waste space, I decided to use gzip to compress the bitmap.  After converting the compressed file to something that looks like a byte array in C, I get something like this:

unsigned char bitmap[] =
{
    0x1f, 0x8b, 0x08, 0x08, 0x2d, 0x87, 0xf9, 0x56,
    0x02, 0x0b, 0x69, 0x63, 0x6e, 0x5f, 0x73, 0x79,
    0x6e, 0x74, 0x68, 0x2e, 0x62, 0x6d, 0x70, 0x00,
    0xed, 0x99, 0x31, 0x0e, 0x82, 0x30, 0x14, 0x86,
    0x45, 0xa4, 0x5f, 0xf6, 0x0f, 0x21, 0x93, 0x3c,
    0x23, 0x70, 0xbf, 0xab, 0x06, 0xff, 0x9e, 0x00,

    .
    .
    .

    0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xe1, 0x05,
    0x5e, 0xd7, 0xb8, 0x1c, 0x36, 0x40, 0x00, 0x00
};


So, nothing terribly interesting in that, but after compiling this into an executable, I get trapped by my virus scanner saying that my shiny new executable has a virus.  If I comment out the bitmap part of the code, no virus.

Apparently whatever byte pattern is created by compressing this particular bitmap gets flagged by my virus scanner, so in order to debug this code, I need to create an exception for the executable I am producing in the virus scanner.  Never had this happen before, but is certainly an artifact of the world we now live in.

I ran into another interesting trap while unit testing this code.  The basic problem at hand was to embed a series of bitmap files in executable code that could be reconstituted at will.  So, to unit test this code, I created the list of bitmap files and used gzip to compress them into a series of filename.bmp.gz compressed files.  Each of the bitmaps is 16k bytes and they compressed down to about 1k on average, so the savings is significant.

I created a utility that would take a list of file names and generate a C source code file similar to what is shown above which was then included in a unit test program.  The test program recreated the decompressed file using my decompression algorithm and wrote the resultant file back to the disk in a different folder.  What I expected was to see a directory full of identical files to the original set.  What I got instead was about 1/3 of the files had 2 or 4 bytes extra.  The rest were identical to the original.

This as you might guess had me more than a little concerned that my decompression algorithm, while not detecting any errors, including a CRC check of the decompressed memory image of the file, was still different on disk than the original by length and therefore content.

Loading the bitmaps up into an image viewer showed very minor corruption of the image.  Rats...

To check this out, I did a binary difference of all the affected files and I saw an interesting pattern emerge.  Every file that contained a 0x0A byte value in the original file, had a two byte sequence (0x0D, 0x0A) replacing it in the corrupt file.

For those that spend some time programming computers, this may be recognized as an automatic Unix/Linux end-of-line convention being replaced by a Windows end-of-line convention.  Unix/Linux uses (typically) line-feed characters only at the end of a line of text in a file whilst Windows (typically) uses a carriage-return/line-feed pair of characters.  (Thanks Microsoft...)

The cause of this result was failing to remember to open the new file in binary mode rather than non-binary mode which caused the file write operation to replace every line-feed character in the file with a carriage-return/line-feed pair.  A simple change to the file open to use binary mode and the resulting files were now identical to the originals.  Don't forget to unit test your code.

Saturday, March 19, 2016

Audio Glitches

How do you find something that takes four hours to occur, and then when it does it is over in 10 ms?  I find that it is really hard to be there when it happens.  When it does happen and you can capture the event, how do you figure out the root cause?

This reminds me a bit of the Pitch Drop Experiment, probably one of the longest running scientific experiments I have ever encountered.  Fortunately my problem is reproduce-able in hours, not decades.

The audio signal path is, to say the least, complicated.  Audio originated from a USB source got transmitted to a dedicated micro-controller.  From there it traversed an I2S bus to a DSP where some sample rate conversion is done.  The I2S bus then continues on to an Audinate network audio device where the audio is deposited on a Dante channel and sent via an Ethernet network switch onto a private LAN network.  On the other side of the network, the audio goes through another network switch into another Audinate device and then across an I16S bus to an FPGA where signal switching can be accomplished.  From there across an I32S bus to another DSP where any effects and other audio mixing can be done.  The composite signal then returns through the same path in reverse to the USB device that originated the signal where it is recorded.  What could possibly go wrong?

Well, surprisingly little actually does go wrong, but when it does it can be a bit subtle, so how to track it down?  Well, there are eight signal path segments in each direction.  My approach is to try and isolate each segment and verify them individually.  I needed to keep the audio signal in the digital realm as I certainly didn't want to introduce the complexity of digital to analogue conversions, plus the issue reproduces in the digital realm.

Since one piece of equipment in the scheme was from a 3rd party, the first task was to verify it could source and record digital audio without error and without using any of the suspect equipment.  So, I purchased a digital audio USB interface that would allow me to loop back said audio while keeping it in the digital realm.  I chose a commercially available USB to S/PDIF adapter and just looped the S/PDIF output back to the input.  Now the USB device could playback and record its own signal and see if an exact copy was produced.  No problems found.

So, in the interest of divide and conquer, I started by sending audio all the way to the FPGA but then intercepting the outbound signal and routing it to a 32 channel hard disk audio recorder.  This allowed recording of the digital signal at the mid-point of the signal path while simultaneously recording it at the return end of the signal path.  Multiple four hour runs were free of errors at the signal mid-point, but still had glitches at the return end of the signal path.  This eliminated all but one signal path in the outbound direction.  To eliminate the remaining signal path, I looped back the audio signal at the DSP at the far end of the path back to the FPGA before sending it to the digital audio recorder.  Still clean.

So, now with half the signal path eliminated, I generated a 997 Hz sinusoidal tone in the same DSP (997 is a prime number) that was used to loop back the signal above in order to isolate the return path.  I have audio glitch testing software than is used to analyze the 48,000 audio samples per second over four hours for abnormalities  This ends up being several gigabytes of data.  Once a reproduce-able error is found, I find that generating a triangle waveform where each sample starts at zero and rises by a value of one to the maximum value and the decreases by a value of one to the minimum.  My analysis software can find glitches (basically looking for vertical edges over some threshold) with either sinusoidal or this kind of ramp-up/ramp-down waveform.  When it comes to looking for firmware issues of dropped samples or buffering issues, I usually use a sawtooth waveform.  At the sample rate of 48 kHz, a ramp from zero to maximum and a sudden drop to zero of a 16 bit sample is about 1.365 seconds.  It just sounds like a ~1 hertz click.  At 24 bits, it is a much slower changing waveform at nearly 350 seconds.  Using this kind of data provides predictability to the expected data, making it easier to recognize when things are not working and is of sufficient duration to ensure that no waveform can repeat itself inside any digital data buffering scheme.  Additionally, specialized code can be written to examine the binary data flowing across the system at any point and predict what should be happening at any given time.

Removing complexities in the Ethernet segment such as removing external network switches and using direct connections between pieces of equipment in order to simplify the setup failed to resolve the issue.

The return audio path ended up being the source of the audio glitch.  By having the ability to record the audio at each end of the complete audio path, the next task was to move one signal segment at a time down the return path and loop it back to the recorder.  This allowed isolating each signal segment and being able to rely on previously tested paths to perform the recording.  Again, using divide and conquer techniques, I did a binary search of the remaining signal segments.  If I had 8 segments to check, I went halfway down and looped back to the recorder.  If a problem was found, divide that signal path in half and retest until the offending segment was found.

In the end some restructuring of DMA priorities on one of the micro-controllers and some DSP work was involved in resolving the issue.





Friday, December 25, 2015

More PID ramblings

First of all, Merry Christmas!

Since I cannot seem to turn my brain off on this topic I have been semi-immersed in for a number of weeks, I wanted to jot down a few thoughts on the topic of PID controllers.

My goals in using a PID controller with a mechanical fader that is driven by DC motor are simple, to wit:

  1. Instantaneous snapping to a new position once loaded into the controller.
  2. Positional accuracy and repeatability when the same position is loaded.
  3. Smooth movement of the fader control under PID control when set point changes are small and frequent.
  4. Tolerant of "stair-stepped" set point changes arriving at non-regular or varying intervals while still maintaining smooth movement.
So far, I have achieved the first two goals.  To date, I have been using a positional PID in an attempt to meet all these requirements.  A positional PID is one that takes a positional set point and a current position as input and produces the necessary output to drive the fader to its new position.  It takes care of acceleration and deceleration as necessary to arrive quickly at the new position.

However, when trying to achieve smooth movement that is quite slow, I find that a positional PID is insufficient for the task for the following reasons:
  1. A PID tuned to aggressively snap to a new position is a poor experience when trying to achieve slow but smooth changes to the position.  The fader tends to be very jerky and/or noisy.  The effect looks like a long-term crack user trying to be cool when the authorities arrive.  I have attempted to use multiple tuning settings where I reduce the gains of the P, I and D terms, but while it improves things, it is still unacceptable.
  2. I find that it takes approximately 30% of the motor speed range just to get the fader to move if it is already stopped.  Once it overcomes inertia and friction it will suddenly lunge forward and likely overshoot the desired set point if it is close at hand.
  3. Once the fader is in motion, it takes a lot less energy from the motor to keep it in motion, but there is still a fairly significant dead band in the motor speed range where the fader will stop again if the PID output drops too low.  Anytime the PID calculation must reverse direction in order to slow motion, it must necessarily pass through this dead band on both sides of zero.
What is needed for smooth motion out of a PID is to have the PID control the velocity of the motor, not just to move it to a new position.  I have attempted to simulate this using a positional PID by limiting the range of the output of the PID to something lower than the maximum speed of the motor when it is desired to be non-aggressive about positioning.  However, what I find to be true is that while it helps the overall effect, the results are not sufficiently predictable or repeatable from one fader to the next.  They all have their own friction profiles and while they are carefully manufactured, the motor movement transfer function from one fader to the next varies considerably.

So, to provide smooth movement for non-aggressive position changes, what is needed is a velocity PID that will control the speed of the motor according to a motion profile.  I will be implementing a trapezoidal motion profile where the motor will accelerate to a maximum speed, hold that speed and then decelerate to a stop at the desired position.

So, I think I need the following:
  1. ​Desired velocity - I will calculate desired velocity based off the current positional error.  When the desired position changes, we start moving in that direction according to a motion profile.
  2. Current velocity - I currently calculate this as the derivative term of positional PID.  The first derivative of position is velocity.
  3. A second PID to control motor velocity when being non-aggressive about positioning that will control motor speed according to a motion profile in a tight control loop.

We can visualize position, acceleration and motion profiles as follows:


To determine the desired velocity, I will calculate the positional PID error.  This is simply the difference between the current position and the desired position.  The further I have to go, the faster I will go up to a maximum limit.  As the fader moves, the error changes (approaches zero) as the fader approaches its target.  The fader loop will calculate the desired velocity as the current position changes towards the target.  As friction changes, voltage changes and other external disturbances affect the actual velocity, the PID will calculate the necessary correction in velocity to stay on the motion profile.

More to come...

Sunday, December 20, 2015

Christmas Math

Just for fun...



I wish you all the best in this holiday season.

Saturday, November 28, 2015

PID Controllers

Wow...  Where to begin.  I have had the pleasure of trying to get my head around implementing a fairly simple controller for a linear fader control that is also motor controlled.

This is an Alps fader which is basically a linear 10K potentiometer that has a small DC motor mounted on the back.  It has a 100mm travel length and the fader control is touch sensitive.  Mouser and other suppliers have them, though I cannot recommend hobbyists buying them through these channels as they are quite expensive.  I have seen them on eBay and other auction sites at significant savings.


The motor is mounted along the axis of the linear fader making a very low profile solution.  The motor drives a toothed belt to move the fader to any position desired remotely.  The motor is a simple DC motor that can be driven by up to 10 VDC.


So, one could envision simply putting a voltage across the pot and connecting the wiper to an Arduino analogue input.  Moving the pot would report a digital number from 0-1023 at any given instant in time representing the voltage across the pot at the wiper and therefore, the relative position of the fader control.

 However, if we wanted to simply tell the fader to position itself to any of its 1024 positions it is capable of, we would basically need to turn the motor on and drive it in the correct direction while reading the pot position until it attains the correct value.  This would be an appropriate application for a PID controller.

To control a DC motor, it is most convenient to use an H-Bridge circuit.  As can be seen by the circuit diagram below, turning on Q1 and Q4 will drive the motor in one direction whilst turning on Q3 and Q2 will drive the motor in the opposite direction.  As an addition feature turning on Q1 and Q2 or Q3 and Q4 will short out the power supply and allow for stress testing your circuit protection implementation...


In reality, there are nice single chip H-Bridge solutions that not only allow for controlling the motor direction, but also allow for PWM control of motor speed.  Most suppliers of robotics components will have typical examples of these controllers as well as places like Adafruit and Sparkfun.

In my next posting, I will get into the details of the hookup and programming of the PID controller for this fader.  More to come...

Saturday, October 17, 2015

Fun with PIDs

PID?  What the heck is a PID?

According to Wikipedia, PID is a Proportional-Integral-Derivative controller (PID controller) which is any kind of control loop feedback mechanism.  These controllers find common usage in industrial control systems.

What a PID controller does is continuously calculate an error value as the difference between the measured value of something that is changing and the desired value.

Perhaps a simple example is in order.  Let's say you want to fill a pan of water at a specific temperature from the tap.  You have two sources of water, cold and hot.  You will need to control the mix of hot and cold water to obtain the desired temperature which you are going to measure with your finger.  So you start with a guess of the hot and cold water settings, measure the result and adjust the hot and cold flow until satisfied.  Making a change that is too big may result in overshooting or undershooting the desired temperature.

Lets call the water temperature sensed by your finger the "process variable" or PV.  The temperature that is desired, let's call the "set point" or SP.  The amount of change of the water flow mix of hot and cold, let's call the "control variable" or CV.  And lastly, the difference between the measured temperature and the desired temperature is the "error" or e.

The most obvious way to change the water flow is in proportion to the current error.  The bigger the error, the bigger the change.  A slightly more complex methodology might want consider the rate of change of the error adding more water flow control depending on how fast the error is approaching zero (a derivative action).  Another refinement might be to consider some historical accumulated error information to detect whether the temperature is settling out too low or too high and make the appropriate corrections (an integral action).  Another way to perform this integral action would be change the current water tap position in steps proportional the current error.

When making changes that result in overshoot or undershoot of the desired set point, continuing to do so will result in oscillations around the desired set point that either grow or decay with time.  If the oscillations decay, the system is stable.  If they grow, the system is unstable.  If they remain at a constant amplitude, the system is marginally stable.  The desired goal is a gradual convergence to the desired set point, so the controller may try to dampen future oscillations by tempering its adjustments by a process called reducing the loop gain.

When the controller starts from a stable state with no error (PV = SP), any changes made by the controller will be in response to changes in other inputs to the process that affect it.  These changes are known as disturbances.  An example of a disturbance in our simple system described above would be a change in the tap water temperature caused by an external event such as the water heater deciding to turn on its heating element.

In theory, a PID controller can be used to control any process which has a measurable output (PV), a known ideal for that output (SP) and an input to the process (CV) that will affect the PV.  So, as you can see PID controllers find uses in any problem domain that needs to regulate temperature, force, speed, pressure, flow rate, weight, position and just about any other variable for which a measurement exists.

So, now just a little bit about the theory of PID controllers.  The name as we have seen above comes from its three terms (Proportional, Integral and Derivative) which are summed to calculate the output of the controller.

The clasasic formula for the algorithm (from Wikipedia) is:


where:
   Kp - Proportional gain.  These first three are algorithm tuning parameters
   Ki  - Integral gain
   Kd - Derivative gain

   e   - Error = SP - PV

   t   - Time or instantaneous time (the present)
   T  - Variable of integration (takes values from time 0 to present time t)

What is desired is a controller that will smoothly make adjustments to the desired set point with minimal over-shoot and under-shoot.  Directly implementing this formula in code would lead to some simple solutions that would most likely suffer from a number of short-comings such as the following:

  • Sample Time - The formula requires recalculation at a regular interval since both integration and derivatives are a function of time.
  • Derivative Kick - Any time the set point (SP) is changed, the error is changed (SP-PV) and the derivative of this change is infinity.  In practice however, since the change in time is never zero, it ends up being a very big number which when fed into the calculation results is an undesirable spike in the output.
  • On the fly Tuning Changes - Changes in the three tuning parameters affect the integration of the error value over time.  Any change in Ki will be multiplied by the entire error sum that has been accumulating when we really only want it to affect the result going forward.
  • Reset Wind-up - Most controllers have some limit on operational ranges.  For example a water valve can only be set in the range of completely closed to completely open.  If the PID controller does not know about these ranges, it may calculate an output value that is out of range.  Over time when it tries to continue to add more water flow beyond maximum, only to have it clamped by the physical size of the pipe, the algorithm will continue to ask for more and more beyond the limit.  The result is that the output gets "wound up" way beyond the maximum limit.  Where the problem reveals itself is when the set point (SP) is dropped, the algorithm needs to wind back down again to the maximum before it will even affect the output.  This results in what looks like a lag in response of the controller to the new set point.
  • Switching the PID Controller on/off - This occurs when you validly decide that regardless of what the PID controller is doing, you want to override its decision for a period of time.  Now, when you stop overriding it's decision, you get a sudden, huge change in the output.  The controller keeps trying to adjust the output to get the desired result, but it doesn't see any change, so it adjusts the output a little more and so on.  It would be like externally overriding a volume control of a stereo system while trying to adjust the volume internally.  No change internally has any effect.  Then you switch off the external override and suddenly you have a huge volume change.
  • Initialization - While it is useful to be able to turn off the PID and set your own override, when you turn it back on, the PID jumps back to the last output value it had set resulting in another spike in the output.  These transitions need to be seamless.
  • Changing Direction - The PID algorithm may be used to drive a system that is either "direct acting" (an increase in the output causes an increase in the input) or "reverse acting" (an increase in the output causes a decrease in the input).  A refrigeration system for example is a reverse acting system as an increase in the cooling results in a decrease in temperature.  Changing the sign of Kp, Ki and Kd allows proper control of a reverse acting systems.
Rather than detail the solutions for all of these potential issues, let me refer you to a most excellent blog by Brett Beauregard detailing the improvements one-by-one starting with the most basic code example.  It is a great read and not one I could not improve upon.

There is a great PID implementation for the Arduino also available at the Arduino Playground which will be the subject of my next posting.

Sunday, October 11, 2015

Postage Stamp Micro-controller

I have updated my ATMega328 controller board with a new layout to fix the previously posted deficits and have sent the gerbers off to OshPark for production.  Hopefully I will have the set back in a week or so.  After validation, I will make the design public at OshPark so anyone interested can purchase the boards.  This is a one inch square PCB with an ATMega328 processor, full set of IO pins and programmable via an ISP connector.


See my previous postings for the schematic and further details.  This was originally a project done for purposes of learning how to use KiCAD.