Delay Compensation in the G-Code Firmware

I’ve been building small RepRapped objects successfully.  (Larger objects not so successfully… I’ll talk about that another time.)  One significant problem I’ve noticed is that a series of short line segments is drawn much too slowly.  You can see this if you draw a circle using just the cartesian bot.  If the circle has only a few segments, then it is drawn at about the speed you specify, but the speed falls as the number of segments increases.

The RepRap host software has several knobs intended to compensate, but I found it very difficult to find the right adjustments.  No adjustment seemed to be right for all line segments.  The knobs in Skeinforge seem even more complicated.

I tried to compensate for the delays by adding buffers to the G-Code firmware.  I figured that if the firmware always has the next command ready in a buffer, there will be no pauses.  I wrote code that buffered several line drawing commands in the Arduino, but as it turned out, even a 16 segment buffer did not reduce the pauses at all.

Then I started thinking about what’s really going on.  The Arduino can’t calculate anywhere near as fast as a modern CPU.  For each line segment, the Arduino does a lot of floating point calculations, including several decimal to floating point conversions and a square root operation to calculate the line distance.  No amount of buffering or interrupt coordination is going to reduce the time required to perform those calculations.  We could move some of the calculations to the host, but I am not interested in giving up the current simplicity of the protocol.

So then I started to think about compensating for the command processing delay automatically.  I realized that every line drawing command is likely to incur about the same amount of calculation delay.  All I need to do is raise the feed rate automatically to compensate for the delay.

Using a little algebra, I found a simple way to calculate an adjusted feed rate based on the line distance and a constant command processing delay.  I added code to the firmware that performs that calculation, then I wrote a script that computed the delay per line drawing command.  I measured the delay to be about 28 milliseconds.

I plugged that number into the firmware and it worked!  Now, drawing a curve takes about the same amount of time whether there are 16 segments or 500.  I hardly have to worry about beading or stretching anymore.

The adjustment works so well in practice that I don’t think the host needs to be able to control it.  It adjusts the speed of every segment, regardless of what host software produced the commands, and not just short segments; it adjusts short segments more than long segments.  The adjustment always seems to produce the expected results.  I think everyone ought to take advantage of this because it makes the machine much easier to tune.

I’ve posted the patch on SourceForge.

I’ll describe how I derived the calculation.

r = specified feedrate, mm/s
R = adjusted feedrate, mm/s
d = line distance, mm
w = latency (seconds) per command caused by command transmission and processing
t = time to draw the line

r*t = d

t = d/r

The equation for R should have the same time and should include w:

t = w + d/R

We can reasonably assume that the command processing latency is about the same for every line.  R will be slightly higher than r, to compensate for w.  By substituting t with d/r, we can compute R.

w + d/R – t = 0

w + d/R – d/r = 0

w/d + 1/R – 1/r = 0

-w/d – 1/R + 1/r = 0

1/r – w/d = 1/R

1 / (1/r – w/d) = R

So computing R is not very hard and we can do it right in the firmware. (After all, the firmware already computes a square root to find the distance of each line, so a couple more divisions will make only a trivial difference.)  Note that if w = 0 (implying no command processing delay), then the w/d term becomes 0 and R = 1/(1/r), thus R = r.  Also note that (1/r – w/d) can be zero, negative, or so small that R is computed as an excessively large feed rate. We need to handle those situations by limiting the adjusted feedrate to the maximum feedrate the steppers can handle.

The firmware actually deals with feed rates in mm/min, so the equation becomes:

60 / (60/r – w/d) = R

This computation is implemented by the patch mentioned above.