Absolute position regulation controls one motor so that its position matches the position set by the user. At any time, even when the motor is moving, the set position can be changed, and the control algorithm will move the motor towards the new position.

The algorithm can be limited in speed and acceleration. Without limitation, the motor will rush as quickly as possible to the given position. With speed limitation, the maximum motor speed can be limited. With acceleration limitation, the rate of speed change can be limited to avoid brutal speed change. In this case, the speed of the motor will be limited so that it will be able to brake to the desired position without going past the limited acceleration.

In synchronised mode, the regulation is done on the sum and the difference of position of the two motors. By increasing the position sum, both motors will turn in the positive direction. By increasing the position difference, motors will turn in the opposite direction. This is really useful for two wheeled robots, where the position sum represents the linear movement and the position difference represents the angular movement.

This is inspired by control algorithms used in APBTeam robots (see it in action!).

Compared to original speed regulation, absolute position regulation offers the following advantages:

  • position consign can be changed at any time,
  • target position is held automatically,
  • motor can slow down before the target position is reached,
  • can be used remotely with precise movements,
  • smooth transition between acceleration, full speed and deceleration,
  • regulated synchronised mode.

Work in progress status

The programming interface may still change as this is a quite new feature. The current NXC API is implemented in NXC and is not as flexible as the other NXC API. For example, the motor argument only accept one motor at a time while the definitive API will accept several motors with one single function call.

API is available in Bricxcc version 3.3.8.9 or NBC/NXC version 1.2.1 r4.

For older versions so you will have to use the posreg.h header.

Basic operation

First step is to enable the position regulation using one of the enable functions:

void PosRegEnable (byte output);
void PosRegEnablePID (byte output, byte p, byte i, byte d);

The first one will use default regulation parameters, while the second one allows you to fine tune each regulation coefficients.

Once regulation is enabled, the motor can be moved to any position using:

void PosRegSetAngle (byte output, long angle);

The angle parameter specifies the new set position. The 0 angle corresponds to the motor position at the time regulation was enabled. Any positive value corresponds to an angle which can be reached by turning in the positive direction from the starting position. Any negative value corresponds to an angle which can be reached by turning in the negative direction from the starting position. You can use values greater than 360 degree to make the motor turn several turns to the target position. Here is an example:

PosRegSetAngle (OUT_A, 90);        // 1
PosRegSetAngle (OUT_A, -90);       // 2
PosRegSetAngle (OUT_A, 90);        // 3
PosRegSetAngle (OUT_A, 360 + 180); // 4

Remember that the current set position can be change at any time, that's why PosRegSetAngle will return immediately so that you can do something else in your program while the motor is moving.

About regulation time

Original LEGO firmware uses a regulation interval of 100 ms. You will get better results using a 10 ms regulation interval.

On recent NXC versions, you can do this using:

SetMotorRegulationTime (10);

Speed and acceleration limiting

The default setting will limit acceleration and speed. This provides smooth movements and ensure that the motor will always be able to follow the consign.

You can use the PosRegSetMax function to change the limits. Without acceleration limit (use value 0), the motor will directly use the maximum speed without transition if any movement is needed. Without speed limit (use value 0), on position change, the motor will rush as quickly as possible to the given position. In any case, the regulation is always activated, motor is under control!

Speed unit is degree per 100 ms, acceleration unit is degres per (100 ms)². This means that acceleration represents the maximum increase of speed per 100 ms.

This PosRegSetMax function can be called at anytime to change maximum parameters. If maximum acceleration is set, maximum speed should be set too (use 100 if you do not want to limit maximum speed).

void PosRegSetMax (byte output, byte max_speed, byte max_acceleration);

Here is a graph showing the motor position using the three different modes:

PosRegSetMax (OUT_A, 0, 0); /* Without limit. */
PosRegSetMax (OUT_A, 40, 0); /* With speed limit. */
PosRegSetMax (OUT_A, 40, 10); /* With speed and acceleration limit. */

(Note: PID could be tuned to remove the overshoot in unlimited mode)

Position can change at any time

Here is an example with the corresponding graph:

task main ()
{   
    PosRegEnable (OUT_A);
    PosRegSetMax (OUT_A, 60, 10);
    PosRegSetAngle (OUT_A, 360);
    Wait (1500);
    PosRegSetAngle (OUT_A, 0);
    Wait (500);
    PosRegSetAngle (OUT_A, 270);
    Wait (2000);
}

Here is another example where the motor A will follow movement from motor B:

task main ()
{   
    /* Enable without limit. */
    PosRegEnable (OUT_A);
    PosRegSetMax (OUT_A, 0, 0);
    /* Now follow motor B. */
    while (1)
      { 
        PosRegSetAngle (OUT_A, MotorTachoCount (OUT_B));
        Wait (1);
      }
}

How to wait for movement completion?

When using absolute position regulation, regulation is always working in the background. If you have to wait until the movement is finished, you will have to test the current position until satisfied.

One simple solution:

while (MotorTachoCount (OUT_A) != my_target)
    Wait (1);

If your PID setting gives a little overshot, you could add a filter (wait for the position to be stable for several milliseconds).

If your PID filter is not strong enough, you may accept a range (wait for the position to be near the target).

If you want to handle unexpected blocking, you should add a timeout to detect such situation.

Synchronised mode

TODO: document synchronised mode.

Low level interface

You do not need to read this part if you only want to use the NXC API.

Absolute position regulation provides two new regulation modes:

  • OUT_REGMODE_POS (4)
  • OUT_REGMODE_POS_SYNC (6)

As long as one of these modes is enabled, position is controlled. When enabling, it is advised to reset counters so that the motor does not move at startup (use the UF_UPDATE_RESET_COUNT flag).

Position is set using the TachoLimit field and the UF_UPDATE_TACHO_LIMIT update flag. When absolute position regulation is enabled, on each update, the new value replaces the old value.

Maximum speed and acceleration are set using two new sbyte output fields: MaxSpeed (16) and MaxAcceleration (17). They are updated at the same time as the PID coefficients using the UF_UPDATE_PID_VALUES update flag.

MaxSpeed and MaxAcceleration are also available in output IOMap, at offset (MotorNb * 32 + 30) and (MotorNb * 32 + 31).