LEGO Powered UP - Motor Modes POS and APOS

Published on Saturday, August 1, 2020

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

  1. Motor is put under power: POS: 0
  2. Motor is moved by 30 degrees: POS: 30
  3. Motor is moved by -70 degrees: POS: -40
  4. 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

  1. 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.
  2. GotoAbsolutePosition should be (mentally and in SDKs) named GotoPosition to better reflect the fact that it is aligned with the POS range and not the APOS 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.


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.