The LEGO Control+ motors - Technic XLarge Linear Motor and Technic Large Linear Motor - have internally the modes POS
(reporting a position) and APOS
(reporting an absolute position). Further does the LEGO Wireless Protocol specify methods StartSpeedForDegrees
and GotoAbsolutePosition
.
Strange thing: When using GotoAbsolutePosition
does not work as expected and goes to strange positions and not the expected absolute physical position!
TL;DR: GotoAbsolutePosition
does not orient itself on the physical APOS
. It is aligned with virtual POS
.
And to put it in context, a memorable quote:
There are only two hard things in Computer Science: cache invalidation and naming things.
-- Phil Karlton
The Modes
Let us start with the simple mode: APOS
is the absolute position in degrees measured from a zero point. It goes from 0, 1 .. 179, 180 then to -180, -179, .., -1, 0. Simple, straight forward. For the current Control+ motors the zero point is physically not marked.
POS
on the other hand is more complicated. The value 0 is aligned with the position of the motor was when the device was activated (most likely: put under power). From that moment on, it counts the degrees moved away from that position. And that number can get quite high (it is a Int32
) and exceeds the traditional 0-360 degree range.
An example
- Motor is put under power:
POS: 0
- Motor is moved by 30 degrees:
POS: 30
- Motor is moved by -70 degrees:
POS: -40
- Motor is further moved by two full turns (aka. -720 degrees) :
POS: -760
Output Commands
StartSpeedForDegrees
moves the motor by the amount of degrees relative to the current position. If the motor is at POS: 20
and the command is invoked with 5 degrees the motor is afterwards in POS: 25
.
GotoAbsolutePosition
moves the motor to a given absolute position within the range of POS
. An example: If the position is POS: -760
and command is invoked with -40 the motor will make two full turns and is not oriented 40 degrees of the physical zero but -40 degrees of the initial position of the motor. Consequently it takes an Int32
as a parameter.
These two unexpected behaviors are traps when programming against the LEGO Wireless Protocol / Powered UP. They are a horrible user experience in regards to their naming (developer perspective). However, they are the correct interface and the right thing to do.
Note: This reflection is based on the Technic Control+ L/XL Linear Motors. There might be a different handling with different motors. I will update the post if I become aware of a difference.
Adjust the mental model
- For most use cases, an (physical) absolute position (
APOS
) is useless. A Technic axle can be connected in 3 wrong offsets compared to the pyhsical zero point of the motor (remember: unmarked). Even more interesting for gears. GotoAbsolutePosition
should be (mentally and in SDKs) namedGotoPosition
to better reflect the fact that it is aligned with thePOS
range and not theAPOS
range.- The two full turns can then easily be explained as the amount of degrees as a delta between the current pos and the targeted position the motor should go-to.
- The 40 degrees of the original motor position are also now okay, because that aligns with the understanding of
POS
.
Note: As an alternative: Maybe APOS
is wrongly named 😀. Or maybe we just need a proper documentation.
Practical Usage: Reset Zero
Assume a Technic Model with a steering. Magically it was calibrated to the center of your steer. The motor is at POS: 254
.
The question here is: What use does GotoAbsolutePosition
bring? Well, there is the method PresetEncoder
. It resets POS
to zero. Applied here, GotoAbsolutePosition
can now comfortable control the steering around the middle position (0) without doing complicated math otherwise needed when using StartSpeedForDegrees
.
Conclusions
Naming is hard. Documentation is sometimes a good idea.
Appendix: Users of SharpBrick.PoweredUp
The mentioned modes and commands are exposed using the following methods:
POS
=>TachoMotor.PositionObservable
APOS
=>AbsoluteMotor.AbsolutePositionObservable
StartSpeedForDegrees
=>TachoMotor.StartSpeedForDegreesAsync
GotoAbsolutePosition
=>AbsoluteMotor.GotoAbsolutePositionAsync
.PresetEncoder
=>TachoMotor.SetZeroAsync
.
There might be a change coming which renames and moves GotoAbsolutePosition
.