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#
Console.WriteLine("📜 Motion Hold: via Digital Input");
const int DIGITAL_INPUTS_PDO_INDEX = 3;
try
{
if (controller.NetworkStateGet() !=
RSINetworkState.RSINetworkStateOPERATIONAL)
{
Console.WriteLine("Network not started. Please start it before running this app.");
return;
}
ulong inputAddress = controller.NetworkInputAddressGet(DIGITAL_INPUTS_PDO_INDEX);
Console.WriteLine("\tFirst motion commanded (held until digital input triggers)");
Console.WriteLine("\tSecond motion commanded (held until digital input triggers)");
Console.WriteLine("\tThird motion commanded (no hold, executes immediately)");
Console.WriteLine($"\tFinal position: {axis.CommandPositionGet()}");
}
finally
{
controller.Delete();
}
Constants used in the C# sample apps.
const int AXIS_0_INDEX
Default: 0.
void MoveRelative(double relativePosition, double vel, double accel, double decel, double jerkPct)
Command a relative point-to-point S-Curve motion.
Represents a single axis of motion control. This class provides an interface for commanding motion,...
static MotionController * Get()
Get an already running RMP EtherCAT controller.
Represents the RMP soft motion controller. This class provides an interface to general controller con...
int32_t MotionDoneWait()
Waits for a move to complete.
void MotionHoldUserMaskSet(int32_t holdMask)
Sets the Motion Hold User bit mask.
void MotionHoldUserPatternSet(int32_t pattern)
Sets the Motion Hold User pattern bit mask.
void MotionAttributeMaskOffSet(RSIMotionAttrMask maskOff)
Turn off a particular motion attribute mask.
void MotionHoldTypeSet(RSIMotionHoldType type)
Set the motion hold type.
void MotionHoldUserAddressSet(uint64_t address)
Sets the Motion Hold User Address.
int32_t AmpEnableSet(bool enable, int32_t ampActiveTimeoutMilliseconds=AmpEnableTimeoutMillisecondsDefault, bool overrideRestrictedState=false)
Enable all amplifiers.
void MotionAttributeMaskOnSet(RSIMotionAttrMask maskOn)
Turn on a particular motion attribute mask.
RSINetworkState
State of network.
RSIMotionAttrMask
Attribute masks for motion. You cannot mix RSIMotionAttrMaskDELAY and RSIMotionAttrMaskAPPEND.
RSIMotionHoldType
Types for MotionHold attribute.
Helpers namespace provides utility functions for common tasks in RMP applications.
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#
Console.WriteLine("📜 Motion Hold: via Position");
const double TRIGGER_POSITION = 1;
const int MOVING_AXIS_TARGET = 10;
const int HOLDING_AXIS_TARGET = 2;
try
{
Helpers.PhantomAxisReset(holdingAxis);
Helpers.PhantomAxisReset(movingAxis);
Console.WriteLine($"\tHolding axis commanded to move to {HOLDING_AXIS_TARGET}, but motion is held");
Console.WriteLine($"\tMoving axis to {MOVING_AXIS_TARGET} to release hold...");
Console.WriteLine($"\tHolding axis final position: {holdingAxis.CommandPositionGet()} (expected: {HOLDING_AXIS_TARGET})");
Console.WriteLine($"\tMoving axis final position: {movingAxis.CommandPositionGet()} (expected: {MOVING_AXIS_TARGET})");
Helpers.AbortMotionObject(holdingAxis);
Helpers.AbortMotionObject(movingAxis);
}
finally
{
controller.Delete();
}
const int AXIS_1_INDEX
Default: 1.
void MotionHoldAxisPositionSet(double position)
Sets the Axis position.
void MotionHoldAxisNumberSet(int32_t number)
Sets the Axis number for Motion Hold.
int32_t NumberGet()
Get the axis number.
void MotionHoldAxisLogicSet(RSIUserLimitLogic logic)
Set the logic when holding for Axis ActualPosition.
RSIUserLimitLogic
Logic options for User Limits.
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#
using System.Threading;
Console.WriteLine("📜 Motion Hold: via Address");
const int MOVE_DISTANCE = 2;
try
{
if (controller.MemoryGet(softwareAddress) != 0x0)
controller.MemorySet(softwareAddress, 0x0);
Console.WriteLine($"\tFirst motion commanded to move {MOVE_DISTANCE} (held)");
Thread.Sleep(100);
Console.WriteLine($"\tPosition while held: {positionWhileHeld} (should be 0)");
controller.MemorySet(softwareAddress, 0x1);
Console.WriteLine("\tReleasing hold via address...");
controller.MemorySet(softwareAddress, 0x0);
Console.WriteLine($"\tPosition after release: {axis.CommandPositionGet()} (expected: {MOVE_DISTANCE})");
Console.WriteLine($"\tSecond motion commanded to move {MOVE_DISTANCE} (held)");
Thread.Sleep(100);
controller.MemorySet(softwareAddress, 0x1);
Console.WriteLine("\tReleasing hold via address...");
controller.MemorySet(softwareAddress, 0x0);
Console.WriteLine($"\tThird motion commanded to move {MOVE_DISTANCE} (no hold)");
Console.WriteLine($"\tFinal position: {axis.CommandPositionGet()} (expected: {MOVE_DISTANCE * 3})");
}
finally
{
controller.Delete();
}
double CommandPositionGet()
Get the current command position.
RSIControllerAddressType
Used to get firmware address used in User Limits, Recorders, etc.
- C++
const int AXIS_COUNT = 1;
const int AXIS_NUMBER = 0;
const double USER_UNITS = 1048576;
const int USER_BUFFER_INDEX = 0;
const double MOTION_DISTANCE = 0.25;
const std::chrono::duration HOLD_TIME = std::chrono::milliseconds(2000);
const char rmpPath[] = "C:\\RSI\\X.X.X\\";
if (USE_HARDWARE)
{
}
else
{
}
try
{
Axis* axis = controller->AxisGet(AXIS_NUMBER);
if (controller->MemoryGet(userBufferAddress) != 0x0)
{
controller->MemorySet(userBufferAddress, 0x0);
}
printf("\nCommanding Motion...\n");
printf("\nSetting Motion Hold...\n");
printf("Commanding Motion...\n");
std::this_thread::sleep_for(HOLD_TIME);
printf("Releasing Motion Hold...\n");
controller->MemorySet(userBufferAddress, 0x1);
controller->MemorySet(userBufferAddress, 0x0);
printf("\nClearing Motion Hold...\n");
printf("Commanding Motion...\n");
}
{
printf("%s\n", err.text);
return -1;
}
controller->Delete();
return 0;