Sunday, September 19, 2021

A New Post-Processor to Speed Sand Table Pattern Drawing


A little history

Back when I worked on The Spice Must Flow sand table, I found an undesirable characteristic in Sandify, the software that generates the pattern files. For some patterns, it created a lot of excess motion along the edges of the table that drastically increased the drawing time and was boring to watch. I wrote a crude Perl program that would post-process Sandify pattern files to eliminate most of the excess edge motion, often reducing the pattern drawing time by 50% and reducing boredom by about 90%. Shortly after that, a better implementation of that function was written into Sandify by one of the Sandify programmers, where it now works very well on every pattern generated.

So what's the problem now?

The Arrakis sand table is now working and I've turned my eye toward improvements. Arrakis can run up to 2000 mm/sec at high acceleration (up to 2 g!) thanks to servomotors. It's impressive to watch it moving like that, and throwing sand all over the place, but the sand throwing wipes out some of the detail in the patterns. Sometimes it's nice to run it slowly so the pattern finishes with all its most intricate detail intact. 

I normally set the speed for each pattern in Sandify by using the "program start code" box when I export the pattern. That speed value is applied to all motion in the pattern except homing which is set in the table's controller board firmware. If I want to see a lot of detail in the pattern and don't mind it taking a long time to finish, I set the speed to 100 mm/sec using a G01 F6000 statement. If I want it to run fast, I set the speed to 1000 mm/sec using a G01 F60000 statement.

This is how I set speed in Sandify. G01 F60000 sets the speed to 1000 mm/sec.

And that's the problem. Run the pattern slow, and you get lots of detail, but the motion along the edges, that doesn't contribute to the pattern, also runs slowly. 

Some patterns have a LOT of edge motion and running them slowly can get pretty boring to watch. It would be really nice if there were a way to speed up the edge motion while leaving the drawing motion at a low speed to preserve detail. 


Sandify Pattern File Structure

Sandify pattern files are plain text files that start with a series of comments about the parameters used to generate the pattern contained in the file. Then the "program start gcode" statements, followed by a bunch of G01 statements that define the pattern itself, and finally a few more lines from the "Program end gcode" box in Sandify.

The gcode interpreter in the controller firmware uses each G01 as an instruction to go from the last coordinate specified in the last G01 statement to the new coordinate in the next G01 statement. In gcode, the speed is "modal" which means setting it once applies until you set it to a different value. So when I set the pattern speed to 1000 mm/sec using G01 F60000, as in the example above, that speed is applied to all motion. Sandify doesn't allow for speed changes within the pattern file but gcode does. In fact, gcode allows setting speed on each and every segment described by a sequence of G01 statements. For example,

G01 X112.000 Y554.260 F6000

tells the controller to move the ball from wherever it is to (112,554.26) at a speed of 6000 mm/min (100 mm/sec).

I tested the idea of bumping up the edge speed by manually editing a pattern file so that the edge motion runs at 1000 mm/sec while the drawing speed is 100 mm/sec. First I generated a pattern in Sandify. Then I used notepad++ to search for and mark all gcode statements that include a point on the edge of the table. Then I went through the file and looked for all the edge to edge movement and appended F60000 to those statements. Since speed is modal, I had to append F6000 to the statements that return to drawing on the table so they'd draw at 100 mm/sec to preserve pattern detail. 

Here's a portion of the manually edited file (right) compared to the original file (left). I added speed changes highlighted in orange. It took me about 15 minutes to manually make the edits on this relatively small pattern file.


The video below shows the results of the test- the first is run at 100 mm/sec for all motion, and about half way through the video it switches to the same pattern run at 100 mm/sec for drawing and edge motion increased to 1000 mm/sec. Acceleration is set to 5000 mm/sec^2 for both. The 100 mm/sec pattern took 25:42 to complete and the dual speed version took only 15:13, shaving 10:29 (and a lot of boredom) off the completion time.

This test convinced me that it was worth the effort to try to automate the dual speed process.




Time to program again...


Each G01 line in the Sandify pattern file specifies a point somewhere on the table. Every time the controller board reads a new line of the pattern file, it figures out how to move the motors to go from the previous point to the new point, and applies whatever speed has been specified (subject to acceleration set in the controller's firmware).

I want to specify two speeds, one for the drawing, and one for the edge motion. That will allow me to use a low speed for the drawing that will preserve the fine detail in the pattern and a high speed for the edge motion so that the pattern will finish drawing faster.

Each segment (any two sequential points specified in the pattern file) either draws a line on the table or moves the ball around the edges of the table. The program has to figure out which is which and then apply the low or high speed appropriately.

The first step in figuring out whether a segment specifies drawing on the table or moving along the edge is to figure out where the two end points are among nine possible locations- the four corners, the four edges, or on the table.


The nine possible locations for any single point in the pattern file.

But how do you know the location of a given point? You read the minimum and maximum values of X and Y out of the original file. A is at (minx, miny), B is at (minx, maxy), etc. A point on the left edge is at the minimum X value, but not minimum or maximum Y value (those specify the corners A and B).

Once you know the two positions, it's simple logic to determine if the the segment draws on the table or moves the ball along an edge. Actually, the locations are intermediate values that are not entirely necessary to use. One could just compare X and Y values of the two points and use them to decide the speed, but the locations make it a little easier to understand the logic of the program, to write it, debug it, and to maintain it.

If you know the locations of the two points, you know if that segment draws a line on the table. For example, if the first point is at corner A and the second point is at T, TE, C, or RE, the segment will draw a line on the table, so it should happen at the lower speed. If the second point is anywhere else, it will be edge motion at high speed. If the first point is at TE and the second point is either B or C, the segment is along the edge and movement should happen at the high speed. If the second point is anywhere else, the motion will draw a line on the table so it should happen at the lower speed.

The program I wrote does just what is described above. It compares locations of points specified in sequential G01 lines, determines whether the segment draws a line or moves along the edges and appends the appropriate speed designator to each line.

This program was a lot easier to write than the one I had previously written to remove excess edge motion, but this was a much simpler problem to solve.


The Result

I wrote the post-processor program, dual_speedify.pl, in Perl. Input to the program is the drawing speed, edge speed, and Sandify pattern file name and coordinates of the home point. Output is an edited pattern file using the input file name with the low and high speeds appended. It scans a Sandify pattern file line by line, locates edge motion and sets it to run fast while setting all other motion (that actually draws the pattern) to run slowly to maintain detail. The speed values entered can be the same value if you don't want the speed to change during a pattern.

The Perl program, dual_speedify.pl, can be downloaded here. There are lots of comments so it should be pretty easy to understand it and make changes if you want.

You'll need to have Perl on your computer to run this program. It was written using Perl 5.32 in Windows, but it doesn't use any exotic stuff that may have changed, so it will probably work on older versions of Perl, too.

To run the program, open a command line console, type "perl -w dual_speedify.pl" and then follow instructions:



As it states, there's no error trapping, so type carefully and open the output file to check it before you try to run it on your table. Remember- speeds are specified in mm/min, NOT mm/sec!

Here's a short video of a pattern that was processed with dual_speedify.pl running on Arrakis.