APIs, concepts, guides, and more
Compensator

Use compensators to add delta offsets to an axis's position based on stored position data for compensating surface irregularities.

🔹 Introduction

A compensator can be used to add a delta offset on the position of one axis based on the position of one or two axis. An example use case would compensating the Z (vertical) axis for X-Y surface irregularities.

In order for your compensator to work you will need to externally collect data on the Z axis deltas for each of the data points in your table. These measurements could be taken manually or if your machine has a touch probe you could write a program to build your compensation table for you.

The compensator works by adding an offset to the position of an axis based on positions stored in a table. This offset is applied right before data is sent do the drive and does not change command position. How data will be retrieved from the table is defined by a min, max, and delta for each input axis.

🔹 Determining required configuration parameters

To configure a compensator, first define the X-Y area to be compensated (Xmin to Xmax, Ymin to Ymax). Then define the spacing of the data points points (delta) for the X and Y axes to determine the compensation table size.

Note
The delta must be a multiple of the difference between the min and the max.

The range in which the compensator will be applied is defined by the min and max. The delta defines the amount of travel between the next compensation value in the table. Linear interpolation is used between points. To determine the number of compensation points required you can use the following equations.

1D Compensation: Points = (positionMax-positionMin) / positionDelta + 1

2D Compensation: Points = PointsX * PointsY

For example:

Image

Axis Min Max Delta
X 0 200000 50000
Y 25000 225000 10000

X_Points = (200000-0)/50000 + 1 = 5\ Y_Points = (225000-25000)/10000 +1 = 21

CompensatorPoints = X_Points * Y_Points = 105

🔹 Pre-allocating memory

Once you know the number of compensators you plan to use and the number of points in the respective tables the functions CompensatorCountSet and CompensatorPointCountSet to reserve memory in the motion controller for the compensator. This must be done before initializing objects that use dynamic memory (Like axes). This will reserve the space in memory on the motion controller. To check the amount of memory available for the compensator and other RSI objects you can check ExternalMemorySizeGet.

🔹 Configure Your Compensator

Once you have started the network you can call CompensatorConfigSet(...) to configure and enable your compensator. To disable your compensator you can use CompensatorDisable(...). For a full list of available functions see our RapidCode API.

Warning
Compensators use counts NOT User Units
Note
Compensator does not affect your command position. The command and compensator output are combined right before getting sent to the drive. You can use CompensatorPositionGet() to see the result of your compensator(s). The compensator will change your actual position Make sure your position error limit is larger than the difference between your largest and smallest compensation table values.
Multiple compensators can be applied to an axis. Overlapping compensators will add to their result.

🔹 Example table

For example, if you were at X=100000 and Y=115000 The value of -1800 counts would be added to your Z target.

Image

Image

🔹 Single Axis Compensator

It is also possible to set up a compensator with the same input and output axis. Such compensators must be configured to use the input axis's command position as the actual position would cause a feedback loop.

An example use case for this feature would be correcting position errors caused by mechanical imperfections in an actuator.

Here is an example of how a commanded position vs a corrected position graph could look for a single axis compensator.

Image

📜 Sample Code

1D Compensator

2D Compensator

  • C#

    /* This sample demonstrates how to configure and use a 2-dimensional compensator.
    A 2D compensation table is applied to a Z axis based on the positions of X and Y axes,
    useful for flatness compensation or surface mapping applications.
    */
    using RSI.RapidCode; // RSI.RapidCode.dotNET;
    Console.WriteLine("📜 Compensator: 2D");
    // set sample config params
    const int INDEX_ZERO = 0;
    const int X_MIN = 0;
    const int X_MAX = 500;
    const int X_DELTA = 100;
    const int X_POINTS = ((X_MAX - X_MIN) / X_DELTA) + 1; // 6 points
    const int Y_MIN = 0;
    const int Y_MAX = 500;
    const int Y_DELTA = 100;
    const int Y_POINTS = ((Y_MAX - Y_MIN) / Y_DELTA) + 1; // 6 points
    const int TOTAL_POINTS = X_POINTS * Y_POINTS; // 36 points total
    // compensator table values use axis COUNTS (not user units)
    // table is organized as rows (X) & columns (Y)
    double[] table = new double[TOTAL_POINTS] {
    0, 0, 0, 0, 0, 0,
    100, 200, -200, 10, 300, 0,
    100, 200, -500, 400, 500, 0,
    0, 0, 0, 0, 0, 0,
    -300, 300, -300, -300, -300, 0,
    0, 0, 0, 0, 0, 0,
    };
    // print compensator info
    Console.WriteLine($"X Range: {X_MIN} to {X_MAX}, Delta: {X_DELTA}");
    Console.WriteLine($"Y Range: {Y_MIN} to {Y_MAX}, Delta: {Y_DELTA}");
    Console.WriteLine($"Points: {TOTAL_POINTS}");
    Console.WriteLine($"2D Compensator Table ({X_POINTS}x{Y_POINTS}):");
    for (int i = 0; i < Y_POINTS; i++)
    {
    for (int j = 0; j < X_POINTS; j++)
    {
    Console.Write($"{table[i * X_POINTS + j],6}");
    }
    Console.WriteLine();
    }
    // get rmp controller
    try
    {
    Helpers.CheckErrors(controller);
    // set compensator count before any RapidCodeObject get/create other than the controller
    controller.CompensatorCountSet(1);
    controller.CompensatorPointCountSet(INDEX_ZERO, table.Length); // set number points allocated on the controller
    // get & configure axes
    Axis x = controller.AxisGet(axisNumber: 0);
    Axis y = controller.AxisGet(axisNumber: 1);
    Axis z = controller.AxisGet(axisNumber: 2);
    // set position error limit trigger
    // init the 2D compensator
    compensatorNumber: INDEX_ZERO,
    firstInputAxis: x,
    firstInputAxisMinimum: X_MIN,
    firstInputAxisMaximum: X_MAX,
    firstInputAxisDelta: X_DELTA,
    secondInputAxis: y,
    secondInputAxisMinimum: Y_MIN,
    secondInputAxisMaximum: Y_MAX,
    secondInputAxisDelta: Y_DELTA,
    outputAxis: z,
    table: table);
    // get results
    double compPos1 = z.CompensationPositionGet();
    x.PositionSet(X_DELTA);
    y.PositionSet(Y_DELTA);
    double compPos2 = z.CompensationPositionGet();
    // print results
    Console.WriteLine($"Compensation at (0,0): {compPos1} (expected: {table[0]})");
    Console.WriteLine($"Compensation at ({X_DELTA},{Y_DELTA}): {compPos2} (expected: {table[7]})");
    // verify accuracy
    if (compPos1 != table[0] || compPos2 != table[7])
    throw new Exception("❌ Compensator compensation position does not match expected value.");
    }
    // handle errors as needed
    finally
    {
    controller.CompensatorCountSet(0); // restore
    controller.Delete(); // dispose
    }
    void ErrorLimitTriggerValueSet(double triggerValue)
    Set the Position Error Limit trigger value.

Single Axis Compensator

  • C#

    /* This sample demonstrates how to configure a compensator that modifies the motion of itself.
    The axis command position is used as the input to apply compensation to the same axis,
    useful for correcting mechanical non-linearities or backlash.
    */
    using RSI.RapidCode; // RSI.RapidCode.dotNET;
    Console.WriteLine("📜 Compensator: Single Axis");
    // set sample config params
    const int INDEX_ZERO = 0;
    const int MIN = 10;
    const int MAX = 110;
    const int DELTA = 10;
    const int POINTS = ((MAX - MIN) / DELTA) + 1; // 11 points
    // compensator table (values are in raw counts not user units)
    // at position: 10 20 30 40 50 ... ... 110
    // adjust by: ↓ ↓ ↓ ↓ ↓ ... ... ↓
    double[] table = new double[POINTS] { 0, 2, -3, -5, -3, 2, -3, 0, 2, -3, -5 };
    // print compensator info
    Console.WriteLine($"Range: {MIN} to {MAX}, Delta: {DELTA}");
    Console.WriteLine($"Points: {POINTS}");
    Console.WriteLine("Compensator Table:");
    for (int i = 0; i < POINTS; i++)
    {
    int inputPos = MIN + (i * DELTA);
    Console.WriteLine($"@ Pos: {inputPos}, Compensate by: {table[i]}");
    }
    // get rmp controller
    try
    {
    Helpers.CheckErrors(controller);
    // set compensator count before any RapidCodeObject get/create other than the controller
    controller.CompensatorCountSet(1);
    controller.CompensatorPointCountSet(compensatorNumber: INDEX_ZERO, pointCount: table.Length);
    // get & configure axis
    Axis axis = controller.AxisGet(axisNumber: 0);
    axis.Abort();
    axis.ClearFaults();
    controller.SampleWait(10);
    axis.PositionSet(0);
    // verify the three values are equal before configuring compensator
    if (controller.CompensatorPointCountGet(INDEX_ZERO) == table.Length && POINTS == table.Length)
    {
    // configure compensator using AXIS_COMMAND_POSITION as input to modify same axis
    compensatorNumber: INDEX_ZERO,
    inputAxis: axis,
    inputAxisMinimum: MIN,
    inputAxisMaximum: MAX,
    inputAxisDelta: DELTA,
    outputAxis: axis,
    table: table);
    // command motion
    axis.AmpEnableSet(true);
    axis.MoveSCurve(position: MIN + DELTA); // move to 2nd point
    // get results
    double compPos = axis.CompensationPositionGet();
    // print results
    Console.WriteLine($"Compensation position: {compPos} (expected: {table[1]})");
    // verify compensation
    if (compPos != table[1])
    throw new Exception("❌ Compensator compensation position does not match expected value.");
    }
    }
    // handle errors as needed
    finally
    {
    controller.CompensatorCountSet(0); // restore
    controller.Delete(); // dispose
    }
    void MoveSCurve(double position, double vel, double accel, double decel, double jerkPct)
    int32_t MotionDoneWait()
    Waits for a move to complete.
    int32_t AmpEnableSet(bool enable, int32_t ampActiveTimeoutMilliseconds=AmpEnableTimeoutMillisecondsDefault, bool overrideRestrictedState=false)
    Enable all amplifiers.
    @ RSIAxisMasterTypeAXIS_COMMAND_POSITION
    Use command position from master.