APIs, concepts, guides, and more
Motion Hold

Execute motion on an Axis or MultiAxis triggered by real-time conditions such as I/O values, axis positions, or software triggers.

🔹 What is Motion Hold?

Motion Hold is a feature available to any motion on an Axis or MultiAxis. The feature allows the user to command (and therefore load) a motion that should not execute immediately. The motion will be triggered by some real-time condition monitored by the RMP firmware.

There are three main types of Motion Hold:

  • I/O triggered motion - A motion holds its execution until an I/O value reaches the desired state.
  • Position triggered motion - A motion holds its execution until an Axis position is exceeded.
  • Software triggered motion - A motion holds its execution until a bit is set in the RMP’s firmware memory.

🔹 Why use Motion Hold?

Motion Hold is especially useful when a motion needs to have a very tight synchronization with some other process – when the synchronization must be accurate to less than one millisecond. A Windows app cannot offer this level of synchronization so it must be configured into the RMP for processing in the real-time firmware for sub-millisecond response to start motion.

Some examples of when Motion Hold might be used for various types:

Note
EXAMPLE 1 - IO Triggered Motion \ An Axis may need to perform its motion precisely when a digital input bit changes state.
EXAMPLE 2 - Position Triggered Motion \ An Axis may need to wait for some other Axis to move into a safe position before it can start its motion.
EXAMPLE 3 - Software Triggered Motion \ An Axis object (Or two axes) that has independent motion but must be started synchronously (can also be accomplished using MultiAxis). The Windows application will set a bit in RMP firmware memory which will trigger both Axis objects to start within the same RMP sample period.

📜 Sample Code

IO Triggered Motion

An Axis may need to perform its motion precisely when a digital input bit changes state. This sample code is done in AKD Drive with Digital IO Inputs switches. A digital input switch triggers to release the HOLD set on the specified Motion. This functionality is available for all Drives but some changes to the sample app may be required.

  • C#

    // Constants
    const int DIGITAL_INPUTS_PDO_INDEX = 3; // Specify the pdo inputs index that represent digital inputs.
    // SET MOTION HOLD
    ulong inputAddress = controller.NetworkInputAddressGet(DIGITAL_INPUTS_PDO_INDEX); // Get host address using the PDO Input Index of Digital Inputs.
    axis.MotionHoldTypeSet(RSIMotionHoldType.RSIMotionHoldTypeCUSTOM); // Use TypeCUSTOM to hold execution based on a particular bit turning ON or OFF.
    axis.MotionHoldUserAddressSet(inputAddress); // Specify the digital inputs host address. This address' value will be used to evaluate the motion hold condition.
    axis.MotionHoldUserMaskSet(0x20000); // Specify the bit you want to mask/watch from the MotionHoldUserAddressSet' address value (this evaluates using a logic AND)
    axis.MotionHoldUserPatternSet(0x20000); // Specify the bit value that will release the motion hold. (When this value is met, motion hold will be released)
    axis.MotionAttributeMaskOnSet(RSIMotionAttrMask.RSIMotionAttrMaskHOLD); // Set the HOLD motion attribute mask ON. (This initializes the HOLD on a particular motion)
    axis.MoveRelative(10); // Command simple relative motion. (This motion will be HOLD by the condition above)
    axis.MotionDoneWait(); // Wait for Motion to be completed.
    axis.MoveRelative(10); // If motion attribute mask off has not been set, this motion will have same HOLD condition as previous move.
    axis.MotionDoneWait(); // Wait for Motion to be completed.
    axis.MotionAttributeMaskOffSet(RSIMotionAttrMask.RSIMotionAttrMaskHOLD);// Set the HOLD motion attribute mask OFF. (This will clear any motion HOLDS that were set on this Axis)
    axis.MoveRelative(10); // This motion will have no HOLD since the previous line has set the motion attribute mask OFF.
    axis.MotionDoneWait(); // Wait for Motion to be completed.
    // Abort and Clear Faults
    axis.Abort();
    axis.ClearFaults();

Position Triggered Motion

An Axis may need to wait for some other Axis to move into a safe position before it can start its motion. This sample code is done in AKD Drive with one Actual Axis and one Phantom Axis. It can be applied to two Phantom Axis or two Actual Axis with the slight changes of code which is guided in comment.

  • C#

    // Constants
    const double TRIGGER_POS = 1; // Specify the position that will be evaluted on triggering/releasing motion
    const int MOVING_AXIS_TARGET = 10;
    const int HOLDINGAXIS_TARGET = 2;
    // Initialize RapidCode Objects
    // Initiazlize Axes: holdingAxis and movingAxis
    Axis holdingAxis = controller.AxisGet(Constants.HOLDING_AXIS_INDEX); // Initialize Axis Class. (Use RapidSetup Tool to see what is your axis number)
    HelperFunctions.CheckErrors(holdingAxis); // [Helper Function] Check that the axis has been initialize correctly.
    Axis movingAxis = controller.AxisGet(Constants.MOVING_AXIS_INDEX); // Initialize HoldAxis Class. (Use RapidSetup Tool to see what is your axis number)
    HelperFunctions.CheckErrors(movingAxis); // [Helper Function] Check that the axis has been initialize correctly.
    // SET UP MOTION HOLD // Condition/Configuration to the Axis(movingAxis) that will hold Motion and its Position that will trigger/release motion
    holdingAxis.MotionHoldTypeSet(RSIMotionHoldType.RSIMotionHoldTypeAXIS_COMMAND_POSITION); // Use RSIMotionHoldTypeAXIS_ACTUAL_POSITION if it is not Phantom Axis.
    holdingAxis.MotionHoldAxisNumberSet(movingAxis.NumberGet()); // Specify motion hold to the Axis(movingAxis) whose position will hold the motion of holdingAxis.
    holdingAxis.MotionHoldAxisPositionSet(TRIGGER_POS); // Specify motion hold position which is the movingAxis's position(need to multiply with USER_UNITS to get correct position value) to trigger/release the motion of holdingAxis.
    holdingAxis.MotionHoldAxisLogicSet(RSIUserLimitLogic.RSIUserLimitLogicGE); // Specify the logic condition that will be evaluated to trigger/release motion based on the SET POSITION(USER_UNITS * TRIGGER_POS).In this case, GT(Greater than or Equal to) motion hold position limit to release
    // SET MOTION HOLD ON
    holdingAxis.MotionAttributeMaskOnSet(RSIMotionAttrMask.RSIMotionAttrMaskHOLD); // Set the HOLD motion attribute mask ON. (This initializes the HOLD on a particular motion)
    holdingAxis.MoveRelative(HOLDINGAXIS_TARGET); // Command simple relative motion(This motion will be hold by condition above until movingAxis's Position passes motion hold position limit)
    // Release MOTION HOLD
    movingAxis.MoveRelative(MOVING_AXIS_TARGET); // Move movingAxis.MovingAxis's position reaches its MotionHold Position limit(in this case, limit is 5). It will trigger/release motion on holdingAxis (holidingAxis will move relatively 10).
    movingAxis.MotionDoneWait();
    holdingAxis.MotionDoneWait();

Software Triggered Motion

An Axis object (Or two axes) that has independent motion but must be started synchronously (can also be accomplished using MultiAxis). The Windows application will set a bit in RMP firmware memory which will trigger both Axis objects to start within the same RMP sample period. This sample code is done in AKD Drive with one Actual axis. There are a lots of available/free firmware address. Some are suggested in comment. Available/free firmware addresses can be found using vm3 as long as there is no label on address, it can be used.

  • C#

    // Constants
    UInt64 SOFTWARE_ADDRESS = controller.AddressGet(RSIControllerAddressType.RSIControllerAddressTypeUSER_BUFFER, 1);
    int WAIT_TIME = 10;
    int MOVE_DIST = 2;
    // SET MOTION HOLD ON AVAILABLE SOFTWARE ADDRESS
    axis.MotionHoldTypeSet(RSIMotionHoldType.RSIMotionHoldTypeCUSTOM); // Use TypeCUSTOM to hold execution based on a particular bit turning ON or OFF.
    axis.MotionHoldUserAddressSet(SOFTWARE_ADDRESS); // Specify the available hostAddress . This address' value will be used to evaluate the motion hold condition.
    axis.MotionHoldUserMaskSet(0x1); // Specify the bit you want to mask/watch from the MotionHoldUserAddressSet' address value (this evaluates using a logic AND)
    axis.MotionHoldUserPatternSet(0x1); // Specify the bit value that will release the motion hold. (When this value is met, motion hold will be released)
    // Check the condition to be false at first
    if (controller.MemoryGet(SOFTWARE_ADDRESS) != 0x0) // Check Available host address value is mask to be false (in this case 0x0)
    {
    controller.MemorySet(SOFTWARE_ADDRESS, 0x0); // if not, mask it to false value/condition (in this case 0x0)
    }
    // SET MOTION HOLD
    axis.MotionAttributeMaskOnSet(RSIMotionAttrMask.RSIMotionAttrMaskHOLD); // Set the HOLD motion attribute mask ON. (This initializes the HOLD on a particular motion)
    axis.MoveRelative(MOVE_DIST); // Command simple relative motion. (This motion will be HOLD by the condition above)
    System.Threading.Thread.Sleep(WAIT_TIME); // Sleep for (x) miliseconds before releasing motion hold.
    var expectedCmdPos1 = axis.CommandPositionGet(); // Sould be 0
    // RELEASE MOTION HOLD
    controller.MemorySet(SOFTWARE_ADDRESS, 0x1); // Release Motion Hold by specifying the host address value to SET Condition (in this case 0x10000)
    axis.MotionDoneWait(); // Wait for motion to be done
    controller.MemorySet(SOFTWARE_ADDRESS, 0x0); // Specify host address value back to false value/condition (in this case 0x0)
    var expectedCmdPos2 = axis.CommandPositionGet(); // Should be MOVE_DIST
    // COMMAND MOTION AGAIN
    axis.MoveRelative(MOVE_DIST); // Command simple relative motion again. (This motion will be HOLD by the condition above)
    System.Threading.Thread.Sleep(WAIT_TIME); // Sleep for (x) miliseconds before releasing motion hold.
    // RELEASE MOTION HOLD
    controller.MemorySet(SOFTWARE_ADDRESS, 0x1); // Release Motion Hold by specifying the host address value to SET Condition (in this case 0x1)
    axis.MotionDoneWait(); // Wait for motion to be done
    controller.MemorySet(SOFTWARE_ADDRESS, 0x0); // Specify host address value back to false value/condition (in this case 0x0)
    // CLEAR MOTION HOLD
    axis.MotionAttributeMaskOffSet(RSIMotionAttrMask.RSIMotionAttrMaskHOLD); // Set the HOLD motion attribute mask OFF. (This will clear any motion HOLDS that were set on this Axis)
    axis.MoveRelative(MOVE_DIST); // This motion will have no HOLD since the previous line has set the motion attribute mask OFF.
    axis.MotionDoneWait(); // Wait for Motion to be completed.

  • C++

    /* CONSTANTS */
    // *NOTICE* The following constants must be configured before attempting to run with hardware.
    // Axis configuration parameters
    const int AXIS_COUNT = 1;
    const int AXIS_NUMBER = 0;
    const double USER_UNITS = 1048576;
    // Index for the address in the user buffer to use for the motion hold
    const int USER_BUFFER_INDEX = 0;
    // Motion parameters
    const double MOTION_DISTANCE = 0.25;
    const std::chrono::duration HOLD_TIME = std::chrono::milliseconds(2000);
    // To run with hardware, set the USE_HARDWARE flag to true AFTER you have configured the parameters above and taken proper safety precautions.
    USE_HARDWARE = false;
    // The rmpPath only needs to be set if the project directory is different than the rapid setup directory.
    // *NOTICE* When setting the rmpPath, be sure to uncomment the rmpPath arguement in MotionController::CreateFromSoftware() in the Run() function.
    const char rmpPath[] = "C:\\RSI\\X.X.X\\"; // Insert the path location of the RMP.rta (usually the RapidSetup folder)"
    /* SAMPLE APP BODY */
    // Initialize MotionController class.
    MotionController* controller = MotionController::CreateFromSoftware(/*rmpPath*/); // NOTICE: Uncomment "rmpPath" if project directory is different than rapid setup directory.
    SampleAppsHelper::CheckErrors(controller); // [Helper Function] Check that the axis has been initialize correctly.
    // Setup the controller for the appropriate hardware configuration.
    if (USE_HARDWARE)
    {
    }
    else
    {
    SampleAppsHelper::SetupControllerForPhantoms(controller, AXIS_COUNT, { AXIS_NUMBER });
    }
    try
    {
    Axis* axis = controller->AxisGet(AXIS_NUMBER); // Initialize Axis Class. (Use RapidSetup Tool to see what is your axis number)
    SampleAppsHelper::CheckErrors(axis); // [Helper Function] Check that the axis has been initialize correctly.
    // GET AXIS READY
    axis->UserUnitsSet(USER_UNITS); // Specify the counts per Unit.
    axis->PositionSet(0); // Make sure motor starts at position 0 every time.
    axis->ErrorLimitTriggerValueSet(1); // Set the position error trigger value
    axis->Abort(); // If there is any motion happening, abort it.
    axis->ClearFaults(); // Clear faults.>
    axis->AmpEnableSet(true); // Enable the motor.
    // GET AN AVAILABLE MEMORY ADDRESS
    uint64_t userBufferAddress = controller->AddressGet(RSIControllerAddressType::RSIControllerAddressTypeUSER_BUFFER, USER_BUFFER_INDEX);
    // SET MOTION HOLD ON AVAILABLE SOFTWARE ADDRESS
    axis->MotionHoldTypeSet(RSIMotionHoldType::RSIMotionHoldTypeCUSTOM); // Use TypeCUSTOM to hold execution based on a particular bit turning ON or OFF.
    axis->MotionHoldUserAddressSet(userBufferAddress); // Specify the available hostAddress . This address' value will be used to evaluate the motion hold condition.
    axis->MotionHoldUserMaskSet(0x1); // Specify the bit you want to mask/watch from the MotionHoldUserAddressSet' address value (this evaluates using a logic AND)
    axis->MotionHoldUserPatternSet(0x1); // Specify the bit value that will release the motion hold. (When this value is met, motion hold will be released)
    // Check the condition to be false at first
    if (controller->MemoryGet(userBufferAddress) != 0x0) // Check Available host address value is mask to be false (in this case 0x0)
    {
    controller->MemorySet(userBufferAddress, 0x0); // if not, mask it to false value/condition (in this case 0x0)
    }
    // COMMAND MOTION (NO HOLD YET)
    printf("\nCommanding Motion...\n");
    axis->MoveRelative(MOTION_DISTANCE); // Command simple relative motion. (This motion will have no HOLD)
    // SET MOTION HOLD
    printf("\nSetting Motion Hold...\n");
    axis->MotionAttributeMaskOnSet(RSIMotionAttrMask::RSIMotionAttrMaskHOLD); // Set the HOLD motion attribute mask ON. (This initializes the HOLD on a particular motion)
    // COMMAND MOTION AGAIN (MOTION WILL BE HELD)
    printf("Commanding Motion...\n");
    axis->MoveRelative(MOTION_DISTANCE); // Command simple relative motion. (This motion will be HOLD by the condition above)
    std::this_thread::sleep_for(HOLD_TIME); // Sleep for 3 second before releasing motion hold.
    // RELEASE MOTION HOLD (MOTION WILL START)
    printf("Releasing Motion Hold...\n");
    controller->MemorySet(userBufferAddress, 0x1); // Release Motion Hold by specifying the host address value to SET Condition (in this case 0x10000)
    axis->MotionDoneWait(); // Wait for motion to be done
    controller->MemorySet(userBufferAddress, 0x0); // Specify host address value back to false value/condition (in this case 0x0)
    // CLEAR MOTION HOLD
    printf("\nClearing Motion Hold...\n");
    axis->MotionAttributeMaskOffSet(RSIMotionAttrMask::RSIMotionAttrMaskHOLD); // Set the HOLD motion attribute mask OFF. (This will clear any motion HOLDS that were set on this Axis)
    // COMMAND MOTION AGAIN (NO HOLD)
    printf("Commanding Motion...\n");
    axis->MoveRelative(-2 * MOTION_DISTANCE); // This motion will have no HOLD since the previous line has set the motion attribute mask OFF.
    axis->MotionDoneWait(); // Wait for Motion to be completed.
    // Abort and Clear Faults
    axis->Abort();
    axis->ClearFaults();
    }
    catch (RsiError const& err)
    {
    printf("%s\n", err.text); // If there are any exceptions/issues this will be printed out.
    return -1;
    }
    controller->Delete(); // Delete the controller as the program exits to ensure memory is deallocated in the correct order.
    return 0;