Coordinate systems, not a G91 exception

Why a tool offset belongs in an absolute endpoint, not in a relative move.

A visual, source-grounded trace through Smoothieware’s tool selection, coordinate conversion, planned machine position, and the two real offset-setting bugs that can make a later G91 move look suspicious.

Scope: current edge, Robot.cpp and ToolManager.cpp. No firmware behaviour is inferred without a source path.

One physical carriage, two coordinate descriptions translation
MCS Z 100
WCS Z 90
tool offset +10
First principle

Separate what the machine is from what the program calls it.

Most confusion disappears once each number has one job. The tool offset does not move the carriage. It changes how a work coordinate is translated into a carriage target.

MCS

Machine coordinate system

The carriage’s planned physical position. Smoothieware stores this in machine_position. It is the reference for a G91 delta.

WCS

Work coordinate system

The coordinate system the G-code author sees. It includes the selected work offset, G92 and tool offset.

TOOL OFFSET

A translation value

The vector between the machine reference and the active tool’s effective point. It shifts WCS interpretation, not the current carriage.

G91 DELTA

A displacement

“Move from here by this much.” A displacement is unchanged by a constant translation. That is basic coordinate geometry, not a Smoothie special case.

The whole argument in two lines

Offsets transform points. They cancel out of differences.

The signs below match the current implementation: WCS offsets and tool offset are added when converting an absolute WCS endpoint into MCS, while G92 is subtracted.

MCS = WCS + WCS offset − G92 + tool offset

This is for an absolute endpoint: “go to WCS Z 50.”

ΔMCS = ΔWCS

This is for a relative move: “move Z by −5.” Every constant offset cancels.

Absolute asks for a destination.

To find that destination in machine space, Smoothie must apply the active coordinate translations.

target = requested_WCS + offsets
Relative asks for a change.

It begins at the already-planned machine position and adds only the requested displacement.

target = machine_position + delta
Concrete trace

Walk through the exact “change tool, then G91” sequence.

Click each step. The blue marker is MCS carriage position. The amber marker is the currently displayed WCS coordinate. The physical move remains exactly five millimetres.

Step 01 / no offset

Both coordinate systems happen to agree.

The planned physical carriage position is MCS Z100. With no work or tool translation in this simplified trace, the WCS readout also says Z100.

machine_position100
tool_offset0
reported WCS100
Source trace

The G91 branch is intentionally simple, and that is evidence of correctness.

Every source writer and reader supports one state model. A tool switch replaces tool_offset; it does not touch machine_position. The relative branch starts from that machine position and adds the requested delta.

Robot.cpp / process_moveabsolute vs relative endpoint
if (absolute_mode) {
    // point conversion: offsets apply
    target[Z_AXIS] = param[Z_AXIS]
        + wcs_offset - g92_offset
        + tool_offset;

} else {
    // displacement: translations do not apply
    target[Z_AXIS] = param[Z_AXIS]
        + machine_position[Z_AXIS];
}

No hidden double-application

process_move has one tool-offset use for absolute XYZ endpoints. The G91 branch never reads it.

Tool changes drain motion first

ToolManager waits for the conveyor to become idle before deselecting the old tool and replacing the offset.

Planner acceptance owns state update

After a move is accepted, Smoothie copies the target into machine_position. Coordinate settings alone do not mutate it.

Counterfactual

What would break if the tool offset were added to G91?

Suppose the carriage is at MCS Z100 and the new tool offset is +10. The desired relative command is G91 G1 Z-5.

Wrong: add the offset to the delta

This treats a change in coordinate origin as if it were a physical displacement. The new target would move to MCS Z105, five millimetres in the wrong direction.

100 + (-5) + 10 = 105

Correct: add only the delta

G91 says “from the current planned machine position, move by minus five.” The target is MCS Z95. The WCS readout follows the new tool translation naturally.

100 + (-5) = 95
Actual findings

There are two concrete state bugs that can impersonate a G91 problem.

The relative transform is sound. The offset supplied to that transform can still be wrong. These two paths create a shifted WCS, so a correct relative displacement begins from an unexpected displayed coordinate.

G43.1 accumulates when it should replace.

The handler starts with the existing tool_offset and adds supplied axes. Under LinuxCNC semantics, G43.1 changes or replaces the named current offsets. Repeating a measured value should not add it again.

G43.1 Z10
G43.1 Z12
= 22
G43.1 Z10
G43.1 Z12
= 12

G10 L1 bypasses the unit conversion.

ToolManager stores raw get_value() axis words. All motion and G43 paths use to_millimeters(). In inch mode, Z1 becomes 1 mm in this path instead of 25.4 mm.

G20
G10 L1 ... Z1
= 1 mm
G20
G10 L1 ... Z1
= 25.4 mm
Special command

G43.2 is the intentional case that changes offset and MCS together.

Smoothie’s non-standard G43.2 adds the given offset and calls delta_move(deltas, ...). That deliberately moves the carriage by the same amount, preserving the current WCS readout. A subsequent G91 is still a normal delta from the new machine position.

G43.2 Z+10

Command carries an offset increment.

Offset +10

tool_offset receives the increment.

MCS +10

delta_move physically plans the matching move.

WCS unchanged

The two translations cancel in the displayed WCS coordinate.

G91 remains literal

The next relative value is still only a displacement.

Interactive sanity check

Move the two constants. The relative result stays relative.

Adjust the planned MCS position, then set a tool offset and issue a fixed G91 Z-5. Watch the blue MCS endpoint move by exactly five regardless of the amber WCS translation.

Choose the starting state

This is the same scalar equation Smoothie uses per primary axis. It is intentionally a coordinate test, not a motion simulator.

100 mm
10 mm
START MCS100
TOOL OFFSET+10
G91 END MCS95
Expected: endpoint equals start MCS − 5. Tool offset changes only the WCS label.
What to prove in firmware

Turn the explanation into compact regression tests.

These checks separate geometry, command semantics, and unit handling. That boundary makes a failing test point to one layer instead of blaming G91 generically.

Tool change then relative line

Set MCS to 100, set tool offset to +10, issue G91 Z-5. Assert planned MCS is 95 and displayed WCS is 85.

RELATIVE
Tool change then absolute line

With the same offset, issue G90 Z50. Assert planned MCS is 60. This proves offset application stays in point conversion.

ABSOLUTE
Repeated G43.1 value

Apply Z10 then Z12. Assert the named active offset becomes 12, not 22, once the replacement semantics are chosen.

G43.1
G10 L1 under G20

Set inch mode and program Z1. Assert the active tool offset becomes 25.4 mm, matching all other distance-bearing commands.

UNITS
G43.2 preserves WCS

Assert that its paired offset and delta move leave the WCS number unchanged while MCS advances by the increment.

G43.2
The conclusion

Keep translations in endpoint conversion. Keep deltas as deltas.

Jim’s G91 branch has the right shape. The next useful work is to make the offset-setting paths match their stated command semantics, then lock this coordinate contract down with focused regression tests.