The physics model used here is really a simplification of the one used in the LED Matrix Sand Toy shown above. That project runs on a fairly large 64x64 LED matrix. It uses a Raspberry Pi for the processing, which gives it the computational power required. The code was written in C++, which also helps with the execution speed.
With the continued use of C++ (ala Arduino), a smaller version was even able to run on a Feather 32u4 in the Animated LED Sand project show below.
From that guide there is this commentary:
The first version of the python code could barely move 5 grains around in anything approaching a smooth speed. The current version does a reasonable job with 10 grains.
That was the motivation to try and see if things could be simplified even further to improve performance when using CircuitPython. First, let's review the "complex" model used on the projects mentioned above.
Physics may be too strong a term. This is really just kinematics - how things move without worrying about the specifics of the forces involved. In our case, "force" comes from acceleration. That can be used to update the velocity of a given particle (code):
And then the updated velocity can be used to update position (code):
The LED Matrix Sand projects above are based on these basic equations.
The value for acceleration comes from the accelerometer, and everything else is computed from there, sand particle by sand particle, one time step at a time. The concept of terminal velocity is used as a way set a maximum upper bound on velocity. Additionally, collisions are accounted for by having particles "bounce" off each other. This is even done inelastically to provide more realism.
In summary, the key items of the "complex" physics model are:
- Proper acceleration / velocity kinematics
- Enforcing terminal velocity
- Modeling inelastic collisions
All of that of course requires code and CPU cycle time to process. Which, if you have the processing power, is great. What if you don't?
So are all those kinematic details necessary? To provide realistic motion, yes. However, to actually appreciate the effects of that realism, the simulation needs to play out on a reasonably sized display, like a 64x64 matrix. For a smaller display, like an 8x8, there really isn't enough space for the realism to be seen. So what if we make some fairly sweeping and hand wavy simplifications?
- Just move one pixel in direction of current acceleration
- Attempt diagonal move for any collision
And that's it.
Move one pixel in direction of current acceleration - one of the red arrows.
For diagonal collisions, attempt to move left/right.
Will that work? Well...we weren't sure either. So we coded it up and tried it out. Let's see how it works with some basic breadboard examples.