APIs, concepts, guides, and more
Streaming Motion Sync Outputs

Utilize Streaming Motion Sync Outputs to precisely synchronize digital outputs with specific motion points during streaming movements, such as PT, PVT, or PVAJT, enhancing coordination and control in automated processes.

๐Ÿ”น What are Streaming Motion Sync Outputs?

Streaming motion Sync Outputs allow you to change the state of a digital output(s) at a precise moment in Axis or MultiAxis streaming motion (PT, PVT, or PVAJT).

๐Ÿ”น Why use Streaming Motion Sync Outputs?

Streaming motion Sync Outputs make it easy to change any Digital Output state based on a specified point index (or ElementID), commonly used where precise coordination of motion and Digital Output states are required. For example, triggering a strobe at precise positions.

Example 1

If I make a MovePT() call that contains 5 different motion points. By using StreamingOutputAdd() I can set a specified digital output true/high during the 4th motion point.

Therefore, if our motion points look like:

1st 2nd 3rd 4th 5th โ† motion points
Position[5] = {1, 2, 4, 10, 14} โ† in user counts
Times[5] = {1, 1, 1, 2, 2} โ† in seconds

When it gets to position 10 (or the 4th motion point) the specified output will change its state.

Note
A motion point in Move*PT*() refers to a point that contains Position and Time. If there are 5 motion points, then there are 5 positions with 5 times.

Motion points are zero ordinate. Therefore, if you wish to add a sync output on the 1st motion point you should refer to it as โ€œElementID 0โ€. Moreover, if you wish to add a sync output on the 2nd motion point you should refer to it as โ€œElementID 1โ€.

๐Ÿ”น Sync Outputs Functions

Axis.StreamingOutputsClear()

StreamingOutputsClear()

The Streaming Motion Sync Output List is maintained as an array and index and this function will reset the current index to 0. However, the array backing is not freed. You are allowed to Clear the list as soon as the move method (MovePT(), etc.) returns.

StreamingOutputsEnableSet()

Must be initialized.

This function will set whether Streaming Motion Sync Output(s) is/are enabled. If you are streaming continuous motion with many calls to MovePT (etc.) you must be sure to have the correct value for Enable for EACH call to MovePT().

Setting to:

FALSE - resets the current index to 0, and clears the vector backing.

TRUE - allows you to add the streaming motion sync output(s) into the frames created by the next MovePT() in firmware. Requires that the StreamingOutputs list size is greater than zero, or you will get a motion attribute invalid error.

StreamingOutputAdd()

Must be initialized.

StreamingOutputAdd applies the bitmasks "onMask" and "offMask" to the controller's memory address (IOPoint) when the controller reaches the specified streaming point index (or Element ID).

If you have enabled StreamingOutputs, then you must Add() at least one before calling MovePT (etc.) or you will get an invalid motion attribute error.

๐Ÿ”น Selecting the Right Element ID

For most Streaming Motion, you can use ElementID to determine where you are within a point set. ElementID 5 = Point 5. Element ID will match the point you provided. Streaming Motion using RSIMotionTypeBESSEL, RSIMotionTypeBSPLINE, and RSIMotionTypeBSPLINE2 involved carrying forward 1 point (Bessel, Bspline2) or 2 points (Bspline). It needs to do this for motion calculations.

Consider a Bspline streaming 100 points per chunk. Two would be carried forward each call. 98 would be associated with the first motion ID. 100 for each of the middle ones. 102 would be associated will the final group. For any middle or final chunk, if you wanted output to happen on the Nth element that you provided, you would add 1 (Bessel and Bspline2) or 2 (Bspline only) respectively to the Output Index. If you wanted an Output to take place on the 99th (Bspline only) or 100th logical user points, you would save it for the next chunk and put it at point 0 or 1.

๐Ÿ”น Sync Outputs FAQ

  1. What happens when I call StreamingOutputsEnableSet(true)?

When you call StreamingOutputEnableSet(true), all motion sync outputs that you add will get added to the frames which are created by MovePT (only MovePTs that have been called after calling StreamingOutputEnableSet(true)). It does not interfere with motions that have already been queued, loaded, or running.

After you call your move method (MovePT(), etc.), the StreamingOutputs will be copied inside the library and then automatically streamed/buffered to the RMP as frames with motion. You are allowed to clear or disable StreamingOutputs as soon as the move method returns.

๐Ÿ“œ Sample Code

Sync Outputs

  • C#

    const int TOTAL_POINTS = 4; // total number of points
    const int EMPTY_CT = -1; // Number of points that remains in the buffer before an e-stop
    const int OUTPUT_INDEX = 0; // This is the index of the digital output that will go active when the user limit triggers.
    const int NODE_INDEX = 0; // The EtherCAT Node we will be communicating with
    double[] positions = { 1.0, 2.0, 3.0, 4.0 }; // These will be the streaming motion 5 positions.
    double[] times = { 0.5, 0.1, 0.2, 0.4 }; // These will be the streaming motion 5 positions' time.
    int outputEnableID = 2; // The motion element ID at which to set the output
    int outputDisableID = 3; // The motion element ID at which to set the output
    // Set up the inputs
    // IOPoint output0 = IOPoint.CreateDigitalOutput(axis, RSIMotorGeneralIo.RSIMotorGeneralIo16); // Retrieve DOUT 1, Method 1: requires you know the io adress in memory, slightly faster
    IOPoint output0 = IOPoint.CreateDigitalOutput(controller.NetworkNodeGet(NODE_INDEX), OUTPUT_INDEX); // Retrieve DOUT 1 Method 2: only need to know node index
    output0.Set(false); // Set the output low
    // Set up Sync Outputs
    axis.StreamingOutputsEnableSet(true); // Enable streaming output.
    // ENABLE the Sync Output(s)
    axis.StreamingOutputAdd(output0, true, outputEnableID); // This will turn DOUT1 High when the streaming motion reaches its 3rd motion point.
    axis.StreamingOutputAdd(output0, false, outputDisableID); // This will turn DOUT1 Low when the streaming motion reaches its 4th motion point.
    // DISABLE the Sync Output(s)
    // axis.StreamingOutputAdd(output0, false, outPutEnableID);
    axis.MovePT(RSIMotionType.RSIMotionTypePT, positions, times, TOTAL_POINTS, EMPTY_CT, false, true); // Start Streaming Motion
    while (!axis.MotionDoneGet())
    {
    if (axis.MotionIdExecutingGet() > outputEnableID && axis.CommandPositionGet() < outputEnableID)
    {
    Assert.That(output0.Get(), Is.EqualTo(true), "The output should be triggered");
    }
    else
    {
    Assert.That(output0.Get(), Is.EqualTo(false), "The output should NOT be triggered");
    }
    }
    axis.StreamingOutputsEnableSet(false); // Disable Sync Outputs.
    axis.AmpEnableSet(false); // Disable the motor.

  • C++

    /* CONSTANTS */
    // *NOTICE* The following constants must be configured before attempting to run with hardware.
    // Axis configuration parameters
    const int AXIS_COUNT = 1; // Specify how many axes you have.
    const int AXIS_NUMBER = 0; // Specify which axis/motor to control.
    const double USER_UNITS = 1048576; // Specify your counts per unit / user units. (the motor used in this sample app has 1048576 encoder pulses per revolution)
    // IO configuration parameters
    const int NODE_INDEX = 0; // Specify which EtherCat Node will be used
    const int OUTPUT_INDEX = 0;
    const int OUTPUT_ENABLE_ID = 2; // The motion element ID at which to set the output
    const int OUTPUT_DISABLE_ID = 3; // The motion element ID at which to set the output
    // Motion parameters
    const int TOTAL_POINTS = 4; // total number of points
    const int EMPTY_CT = -1; // Number of points that remains in the buffer before an e-stop
    const double POSITIONS[] = { 0.25, 0.50, 0.75, 1.0 }; // These will be the streaming motion 5 positions.
    const double TIMES[] = { 0.5, 1.0, 1.5, 2.0 }; // These will be the streaming motion 5 positions' time.
    // 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.
    char rmpPath[] = "C:\\RSI\\X.X.X\\"; // Insert the path location of the RMP.rta (usually the RapidSetup folder)
    // 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.
    axis->PositionSet(0); // Make sure motor starts at position 0 everytime.
    axis->UserUnitsSet(USER_UNITS); // Change your user units.
    axis->ErrorLimitActionSet(RSIAction::RSIActionNONE); // Set Error Limit Action.
    axis->Abort(); // If there is any motion happening, abort it.
    axis->ClearFaults(); // Clear faults.>
    axis->AmpEnableSet(true); // Enable the motor.
    // Set up the inputs
    // Ensure the digital out is set low.
    //IOPoint *output0 = IOPoint::CreateDigitalOutput(axis, RSIMotorGeneralIo.RSIMotorGeneralIo16); // Retrieve DOUT 1, Method 1: requires you know the io adress in memory, slightly faster
    IOPoint* output0 = IOPoint::CreateDigitalOutput(controller->IOGet(NODE_INDEX), OUTPUT_INDEX); // Retrieve DOUT 1 Method 2: only need to know node index
    output0->Set(false);
    // Set up Sync Outputs
    axis->StreamingOutputsEnableSet(true); // Enable streaming output.
    // ENABLE the Sync Output(s)
    axis->StreamingOutputAdd(output0, true, OUTPUT_ENABLE_ID); // This will turn DOUT1 High when the streaming motion reaches its 3rd motion point.
    axis->StreamingOutputAdd(output0, false, OUTPUT_DISABLE_ID); // This will turn DOUT1 Low when the streaming motion reaches its 4th motion point.
    // DISABLE the Sync Output(s)
    //axis->StreamingOutputAdd(output0, false, outPutEnableID);
    axis->MovePT(RSIMotionType::RSIMotionTypePT, POSITIONS, TIMES, TOTAL_POINTS, EMPTY_CT, false, true); // Start Streaming Motion
    printf("Motion started. Waiting to complete.\n");
    axis->MotionDoneWait(); // What for Streaming Motion to be done.
    printf("Motion Complete. The outputs should have been set\n");
    axis->StreamingOutputsEnableSet(false); // Disable Sync Outputs.
    axis->AmpEnableSet(false); // Disable the motor.
    }
    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;

Sync Outputs using Element ID

  • C++

    /* CONSTANTS */
    // *NOTICE* The following constants must be configured before attempting to run with hardware.
    // Axis configuration parameters
    const int AXIS_COUNT = 1; // number of axes
    const int AXIS_NUMBER = 0; // axis number
    const double USER_UNITS = 1048576; // encoder counts per rev (set as appropiate)
    // Motion parameters
    const double TIME_SLICE = 0.01; // 0.01s = 10ms
    const int REVS = 2; // number of revolutions
    const int RPS = 1; // revs / sec
    const int TOTAL_POINTS = (int)(REVS / TIME_SLICE / RPS); // total number of points
    const int EMPTY_CT = -1; // Number of points that remains in the buffer before an e-stop
    // 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;
    /* SAMPLE APP BODY */
    // Initizalize the controller from software w/ multiple axes
    MotionController* controller = MotionController::CreateFromSoftware();
    // Setup the controller for the appropriate hardware configuration.
    if (USE_HARDWARE)
    {
    }
    else
    {
    SampleAppsHelper::SetupControllerForPhantoms(controller, AXIS_COUNT, { AXIS_NUMBER });
    }
    MultiAxis* multiAxis;
    try {
    // add an additional axis for the multiaxis supervisor
    controller->MotionCountSet(controller->AxisCountGet() + 1);
    // create the multiaxis using the ID of the first free axis (0 indexed)
    multiAxis = controller->MultiAxisGet(controller->MotionCountGet() - 1);
    // populate the multiaxis
    Axis* axis = controller->AxisGet(AXIS_NUMBER);
    axis->UserUnitsSet(USER_UNITS);
    axis->ErrorLimitActionSet(RSIAction::RSIActionNONE);
    axis->EStopAbort();
    axis->ClearFaults();
    axis->PositionSet(0);
    multiAxis->AxisAdd(axis);
    // populate the positions and times
    std::vector<double> positions, times;
    for (int i = 0; i < TOTAL_POINTS; i += AXIS_COUNT)
    {
    positions.push_back(i * TIME_SLICE * RPS);
    times.push_back(TIME_SLICE);
    }
    // prepare the controller (and drive)
    multiAxis->Abort();
    multiAxis->ClearFaults();
    assert(multiAxis->StateGet() == RSIState::RSIStateIDLE);
    multiAxis->AmpEnableSet(true);
    // set up the inputs
    IOPoint* output0 = IOPoint::CreateDigitalOutput(multiAxis->AxisGet(AXIS_NUMBER), RSIMotorGeneralIo::RSIMotorGeneralIo16);
    // ensure the digital out is set low
    output0->Set(0);
    // The motion element ID at which to set the output
    int outPutEnableID = TOTAL_POINTS / 2;
    // enable streaming output
    multiAxis->StreamingOutputsEnableSet(true);
    // Enable the streaming output (The following are functionally equivalent)
    //multiAxis->StreamingOutputAdd(output0->MaskGet(), 0, output0->AddressGet(), outPutEnableID);
    multiAxis->StreamingOutputAdd(output0, true, outPutEnableID);
    // Disable the output (The following are functionally equivalent)
    //multiAxis->StreamingOutputAdd(0, output0->MaskGet(), output0->AddressGet(), outPutEnableID);
    //multiAxis->StreamingOutputAdd(output0, false, outPutEnableID);
    multiAxis->MovePT(RSIMotionType::RSIMotionTypePT, &positions[0], &times[0], TOTAL_POINTS, EMPTY_CT, false, true);
    printf("Motion started. Waiting to complete.\n");
    multiAxis->MotionDoneWait();
    printf("Motion Complete. The outputs should have been set\n");
    multiAxis->StreamingOutputsClear(); // cleanup for next run
    multiAxis->EStopAbort();
    }
    catch (RsiError const& err) {
    printf("\n%s\n", err.text);
    return -1;
    }
    controller->Delete(); // Delete the controller as the program exits to ensure memory is deallocated in the correct order.
    return 0;