Tuesday, July 16, 2013

Lorenz in Living Color: Part 3 - Differential Equations on the Arduino Uno

We left off last last time with my successful efforts to implement a fourth-order Runge-Kutta algorithm to solve the Lorenz system of differential equations in Microsoft Excel.  To make this project all I hoped it to be, though, I needed to do the same thing using an Arduino Uno.

The Arduino platform is great in many, many ways because it is largely able to hide a lot of the details that have made microcontrollers tricky to deal with for decades. But once you start moving beyond simple flashing LEDs, controlling DC motor speed, and reading analog temperature sensors, that messiness and mandatory attention to detail begins to reappear.  Very quickly, trouble with execution time, accuracy, data types, memory management and program space become pressing.  For this project I had a few specific concerns.

The first was accuracy.  Like all chaotic systems, the Lorenz system is sensitive to small deviations which, over time, cause a divergence between states that started out very near each other.  Approximations at any step of the process can lead to widely differing results in short order.  This is particularly a problem when using a numerical solver because numerical solutions always involve some degree of estimation; you always only get approximate solutions.

For the purposes of this project, I was not highly concerned with the accuracy of the specific solution, only that the system continues to behave in the characteristically chaotic fashion.  As long as the colors kept oscillating back and forth I was OK; if the system ever leveled out and settled into a single color or small spectrum of colors I had problems.  Not knowing enough about the system, I was not sure what degree of accuracy was needed to make this work.  As the programmer reference states, floating point variables (values with a decimal place) only have six or seven digits of accuracy.  It was not obvious to me if this would be enough accuracy for my needs.

My second concern was speed. The Arduino Uno runs at 16MHz by default and the Runge-Kutta algorithm uses floating point values every step of the way.  Floating point operations are the slow on the Uno because it has no dedicated floating point processor so I knew calculating the solutions could be time consuming.  I was also concerned with the time it would take to send out the new color state to all of the LEDs I was using.  As I could tell from the tutorial from the distributor, each LED would need 24 bits sent and I was planning on using 50 LEDs.  For the system to work as I wanted, both the calculations and the updates would have to be fast enough that I could update the system in real time.

There was only one way to address these concerns: try and see how it works. My first pass at implementing the Runge-Kutta code was a failure and unfortunately for me, I didn't realize I had made a critical mistake in translating my work in Excel to Arduino code.  When I ran this faulty code the system quickly settled out into a constant value, leading me to believe that the Uno lacked the precision for this application. These results lead me to desperation and I decided to take an entirely different tack: I was going to cheat.

Rather than calculating the solution to the Lorenz system I decided to take the red, green, and blue values I had already found when implementing the Runge-Kutta algorithm in Excel and store them in the Uno.  There wasn't a ton of room in the flash memory available for me to use but it was enough and I was able to demonstrate that this could work.  The Uno would just play back the pre-calculated solution and then and use it to drive the LEDs.  It worked but I wasn't happy that I would only ever be able to display a fairly limited set of values, those stored in flash memory.  This small setback and the normal busy-ness of life led me to sideline the project for several months.

A month or two ago I  picked the project up again with inspiration from an artist friend of mine and did the hard work of remembering where I left off and developing new strategies to solve the problem before me.  I ordered a dedicated floating point processor with hopes it would provide me the precision I thought I needed.  I ordered a 32-bit Arduino Due which has this precision built in the microprocessor architecture (and it runs much faster).  I even ordered extra off-chip flash memory to store an impossibly large amount of pre-calculated values (worst-case scenario). And while I waited for those to arrive in the mail, I relearned how to implement the Runge-Kutta algorithm and looked over the code I had written.  With a more systematic approach to checking my work I discovered errors in both my Excel and Arduino code and after correcting both was greeted with surprising results.

The Uno had enough calculating precision and speed to do what was needed. The solution to the Lorenz system that it finds appears to be accurate enough though I won't be sure until I get the system entirely assembled and can let it run for hours or days.  This result greatly encouraged me.  I was getting close but there was still more to do.


No comments:

Post a Comment