Stream Motion (option **J519**) lets an external application give the position of the robot at every communication cycle (2, 4 or 8 ms depending on the controller). The robot follows these positions in real time. Typical uses:

- trajectories computed outside the robot (path planning, CAD/CAM, simulation)
- sensor guided motion: vision, force sensor, seam tracking
- teleoperation with a joystick or a haptic device

The SDK does the real-time part for you: it synchronizes the positions with the status of the robot, keeps a few positions in advance, stops the robot smoothly if your application stops giving positions, and provides a motion planner that respects the velocity, acceleration and jerk limits of the robot.

## Stream Motion or RMI?

| Need | Use |
| --- | --- |
| Send a few moves (J, L, C) and let the robot plan them | [RMI](/fanuc/documentation/rmi) |
| Follow a path or a target that changes in real time | Stream Motion |
| Control the position at every cycle (2 to 8 ms) | Stream Motion |

## Requirements on the robot

- Option **J519 Stream Motion**. Check it with `Features.HasStreamMotion` ([FTP diagnostics](/fanuc/documentation/ftp-diagnostics)).
- A TP program with the instructions `IBGN start[n]` and `IBGN end[n]` (see below), run in **AUTO** mode at **100% override**.
- `$PARAM_GROUP[1].$SV_OFF_ENB[*]` set to `FALSE`, otherwise the robot raises MOTN-615.
- Resume offset disabled, otherwise the robot raises MOTN-623.
- Only motion group 1 is controlled (robot and up to 3 extended axes).
- The robot listens on UDP port 60015 on the Ethernet port set in `$STMO.$PHYS_PORT`.

## TP program

The robot only accepts positions while a program waits on `IBGN start[n]`. When your application finishes the session, the program continues after `IBGN end[n]`. This program loops, so each loop is a new session:

```plaintext
/PROG  START_STREAM_MOTION_J519
/ATTR
OWNER		= MNEDITOR;
PROTECT		= READ_WRITE;
TCD:  STACK_SIZE	= 0,
      TASK_PRIORITY	= 50,
      TIME_SLICE	= 0,
      BUSY_LAMP_OFF	= 0,
      ABORT_REQUEST	= 0,
      PAUSE_REQUEST	= 0;
DEFAULT_GROUP	= 1,*,*,*,*;
CONTROL_CODE	= 00000000 00000000;
/MN
   1:  LBL[1] ;
   2:  IBGN start[1] ;
   3:  IBGN end[1] ;
   4:  JMP LBL[1] ;
/POS
/END
```

You can add your own instructions before `IBGN start` or after `IBGN end`, for example to set an output or to wait for a signal. The program can be started from the teach pendant, or remotely (see [Run a program remotely](/fanuc/documentation/run-program-remotely)).

`IBGN start` and `IBGN end` instructions are located in the `ASCII INTERFACE` section of the TP program editor. If you don't see this section, please ensure J519 Stream Motion is enabled.

![Add IBGN Start and End Instructions to TP Program](/fanuc/documentation/add-tp-program-ibgn-start-end.gif)

## Quick start

This example moves J1 by 10 degrees and back, with a smooth motion planned within the limits of the robot:

**C# : StreamMotionQuickStart**
```csharp
﻿using UnderAutomation.Fanuc;
using UnderAutomation.Fanuc.Common;
using UnderAutomation.Fanuc.Motion;
using UnderAutomation.Fanuc.StreamMotion;
using UnderAutomation.Fanuc.StreamMotion.Data;
using UnderAutomation.Robotics.Motion;

public class StreamMotionQuickStart
{
    static void Main()
    {
        var robot = new FanucRobot();
        var parameters = new ConnectionParameters("192.168.0.1");
        parameters.StreamMotion.Enable = true;
        robot.Connect(parameters);
        var sm = robot.StreamMotion;

        /**/
        // Read the limits of the robot and start to receive its status
        sm.StartMonitoring();

        // Plan a smooth joint motion: J1 +10 degrees then back, at 20% of the velocity limits
        JointsPosition start = sm.QueueEndJointPosition;
        var target = new JointsPosition(start.Values) { J1 = start.J1 + 10 };
        var planner = new MotionPlanner(sm.JointLimits, null);
        Trajectory trajectory = planner.CreateJointPath(FanucMotion.ToJointValues(start))
            .MoveJoint(FanucMotion.ToJointValues(target), 20, FanucMotion.Cnt(100))
            .MoveJoint(FanucMotion.ToJointValues(start), 20, FanucMotion.Fine())
            .Build();

        // The motion starts when the TP program reaches IBGN start
        int motionId = sm.Enqueue(trajectory);
        sm.WaitForMotion(motionId, 60000);

        // Release the TP program: it continues after IBGN end
        sm.Finish(10000);
        /**/

        robot.Disconnect();
    }
}
```

**Python : StreamMotionQuickStart**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters
from underautomation.fanuc.common.joints_position import JointsPosition
from underautomation.fanuc.motion.fanuc_motion import FanucMotion
from underautomation.robotics.motion.motion_planner import MotionPlanner

robot = FanucRobot()
parameters = ConnectionParameters("192.168.0.1")
parameters.stream_motion.enable = True
robot.connect(parameters)
sm = robot.stream_motion

##
# Read the limits of the robot and start to receive its status
sm.start_monitoring()

# Plan a smooth joint motion: J1 +10 degrees then back, at 20% of the velocity limits
start = sm.queue_end_joint_position
values = list(start.values)
values[0] += 10
target = JointsPosition(*values)
planner = MotionPlanner(sm.joint_limits, None)
trajectory = planner.create_joint_path(FanucMotion.to_joint_values(start)) \
    .move_joint(FanucMotion.to_joint_values(target), 20, FanucMotion.cnt(100)) \
    .move_joint(FanucMotion.to_joint_values(start), 20, FanucMotion.fine()) \
    .build()

# The motion starts when the TP program reaches IBGN start
motion_id = sm.enqueue(trajectory)
sm.wait_for_motion(motion_id, 60000)

# Release the TP program: it continues after IBGN end
sm.finish(10000)
##

robot.disconnect()
```

![J1 during this trajectory, relative to its start position.](/fanuc/documentation/diagrams/stream-motion-quick-start.svg)

## Try it in the demo application

The [demo application](/fanuc/documentation/demo-app) (Windows) has a Stream Motion page to test this feature without writing code.

Connect to your robot or to a ROBOGUIDE virtual robot, start the monitoring, then run the TP program described above. Once the robot reaches `IBGN start`, a session starts and the page shows:

- the client state, the cycle time measured on the robot, the protocol version and the number of active sessions
- the robot status at each cycle: joint and Cartesian position, and whether the robot is moving
- two demo motions built with the motion planner: a joint move of J1 back and forth, and a horizontal circle in Cartesian space
- pause, resume, abort and override applied to the current session
- a log of the sessions and motions, with counters for lost status messages and underruns

![Stream Motion page of the Showcase demo application](/fanuc/documentation/demo-stream-motion-showcase-forms.gif)

The C# source of this page is in the `StreamMotionControl.cs` file of the [Fanuc.NET repository](https://github.com/underautomation/Fanuc.NET/blob/main/UnderAutomation.Fanuc.Showcase.Forms/Components/StreamMotionControl.cs).

## How it works

1. `StartMonitoring()` reads the limits of the robot, starts the status output of the robot and measures the communication cycle.
2. When the TP program reaches `IBGN start`, the client state becomes `Ready`.
3. As soon as positions are available, a session starts. Positions come from one source at a time:
   - a **queue of trajectories** (`Enqueue`), see [Send trajectories](/fanuc/documentation/stream-motion-trajectories)
   - a **target** that the robot follows (`StartTracking`), see [Real-time control](/fanuc/documentation/stream-motion-real-time)
   - your own **callback**, called at each cycle (`StartCallbackStreaming`)
4. `Finish()` waits until the robot is at rest, then the TP program continues after `IBGN end`.

Trajectories are created with the motion planner of the SDK, described in the [Motion](/fanuc/documentation/motion) section.

During a session, the robot sends its status at every cycle and the SDK answers with the next position. The positions are sent a little in advance (`BufferLeadTime`), so that a late answer of the PC does not stop the robot:

![Exchange between the robot and the SDK at each communication cycle, with 3 cycles of BufferLeadTime.](/fanuc/documentation/diagrams/stream-motion-cycle.svg)

## Protocol versions

Set the version in `ConnectionParameters.StreamMotion.ProtocolVersion`. It must not be higher than the system variable `$STMO.$USABLE_VER` (this variable does not exist on older software, which only support version 1).

| Version | Difference |
| --- | --- |
| 1 (default) | Supported by all controllers. Positions are sent in single precision. |
| 2 | Joint positions are sent in double precision. Recommended for joint motions when available. |
| 3 | The robot can slow down its status output during an automatic stop. Handled by the SDK. |

A version 4 exists on some recent controllers for ROS 2 only. It is not supported.

## Useful system variables

| Variable | Description |
| --- | --- |
| `$STMO.$USABLE_VER` | Highest protocol version of the controller |
| `$STMO.$COM_INT` | Theoretical communication cycle (the SDK measures the real one) |
| `$STMO.$PKT_STACK` | Size of the position buffer of the robot. Use the same value for `PacketStackSize` |
| `$STMO.$START_MOVE` | Number of positions received before the robot starts to move. Keep it low (default 1) |
| `$STMO.$PHYS_PORT` | Ethernet port used by Stream Motion |
| `$STMO.$THRS_ABNPOS` | Threshold of the abnormal position alarm (MOTN-625) |
| `$STMO_GRP[1].$FLTR_LN` | Filter applied by the robot to the positions: smoother motion, with a small delay |
| `$STMO_GRP[1].$JNT_VEL_LIM`, `$JNT_ACC_LIM`, `$JNT_JRK_LIM` | Reference limits of each axis, also given by `ReadLimits()` |
| `$MCR.$GENOVERRIDE` | General override, must be 100 |

These variables can be read with [CGTP, SNPX or Telnet](/fanuc/documentation/read-write-system-variables).

## Next steps

- [Connection, status & session](/fanuc/documentation/stream-motion-session): connect, read the status and the limits, run several sessions.
- [Send trajectories](/fanuc/documentation/stream-motion-trajectories): queue trajectories, override, pause and abort.
- [Real-time control](/fanuc/documentation/stream-motion-real-time): follow a target, or compute each position.
- [I/O during motion](/fanuc/documentation/stream-motion-io): read and write I/O synchronized with the motion.
- [Troubleshooting](/fanuc/documentation/stream-motion-troubleshooting): alarms and best practices.

## API reference

**Members of StreamMotion.Internal.StreamMotionClientBase**
```csharp
public abstract class StreamMotionClientBase : IDisposable {
    // Creates a Stream Motion client
    protected StreamMotionClientBase()

    // Stops smoothly on the path of the current trajectory, then cancels the current and the queued trajectories.
    // The session stays open and the robot keeps its position. The callback streaming and the target tracking are stopped,
    // and the robot stops as fast as the limits allow.
    public void Abort()

    // Format of the positions of the current session or of the queued trajectories. Only valid when <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.HasActiveFormat" data-throw-if-not-resolved="false"></xref> is true.
    public PositionFormat ActiveFormat { get; }

    // Adds a range of 16 consecutive I/O to read. Each position sent to the robot reads one range, so several ranges are read one after the other.
    // Values are only read during a session.
    public void AddIOMonitor(IOType type, int index)

    // Number of positions sent in advance and kept in the robot buffer during the current session
    public int BufferLead { get; }

    // Cartesian limits used to stop the robot smoothly when the positions stop in Cartesian format.
    // When it is null, conservative values are used.
    public CartesianLimits CartesianLimits { get; set; }

    // Removes all ranges of I/O to read
    public void ClearIOMonitors()

    // Opens the UDP socket to the robot. The robot does not send anything before <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.StartMonitoring" data-throw-if-not-resolved="false"></xref> is called.
    protected void ConnectInternal(string ip, StreamMotionConnectParametersBase parameters)

    // Indicates whether the client is connected
    public bool Connected { get; }

    // Communication cycle of the robot measured from the status, in seconds (for example 0.008 or 0.002). 0 while it is not known.
    // Trajectories are sampled at this period.
    public double CycleTime { get; }

    // Disconnects from the robot. If a session is active, the robot is stopped smoothly and the session is finished first.
    public void Disconnect()

    // Disconnects and releases the resources
    public void Dispose()

    // Disconnects and releases the resources
    protected virtual void Dispose(bool disposing)

    // Adds a trajectory at the end of the queue. The session starts automatically when the robot accepts positions,
    // and the trajectories are sent one after the other, without any change between them.
    public int Enqueue(Trajectory trajectory)

    // Raised when an error occurs in the communication thread
    public event EventHandler<StreamMotionErrorEventArgs> ErrorOccurred

    // Finishes the session when the queue is empty and the robot is at rest: the last position is sent with the end flag,
    // and the program continues after the IBGN end instruction. If a program waits on IBGN start without session, it is released at the current position.
    // The callback streaming and the target tracking are stopped first.
    public bool Finish(int timeoutMs)

    // Returns the last read state of one I/O. The I/O must be in a range added with <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.AddIOMonitor(UnderAutomation.Fanuc.StreamMotion.Data.IOType%2cSystem.Int32)" data-throw-if-not-resolved="false"></xref>.
    // It returns false while the range was never read.
    public bool GetIO(IOType type, int index)

    // Indicates if the format of the positions is fixed. A session uses only one format, chosen by the first queued trajectory,
    // <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.StartTracking(UnderAutomation.Robotics.Motion.PositionFormat%2cSystem.Double%2cSystem.Double)" data-throw-if-not-resolved="false"></xref> or <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.StartCallbackStreaming(UnderAutomation.Robotics.Motion.PositionFormat)" data-throw-if-not-resolved="false"></xref>. While this is true, positions in the other format
    // throw a <xref href="UnderAutomation.Fanuc.StreamMotion.StreamMotionException" data-throw-if-not-resolved="false"></xref> with <xref href="UnderAutomation.Fanuc.StreamMotion.StreamMotionError.FormatMismatch" data-throw-if-not-resolved="false"></xref>.
    // It becomes false when the queue is empty and no session is active: call <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.Finish(System.Int32)" data-throw-if-not-resolved="false"></xref> to use the other format in the next session.
    public bool HasActiveFormat { get; }

    // Time in seconds by which the I/O events of trajectories are sent before their position (default 0).
    // It compensates the delay between the reception of a position by the robot and the real motion.
    public double IOAnticipation { get; set; }

    // Last values of the ranges of I/O added with <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.AddIOMonitor(UnderAutomation.Fanuc.StreamMotion.Data.IOType%2cSystem.Int32)" data-throw-if-not-resolved="false"></xref>
    public IOValue[] IOValues { get; }

    // IP address of the robot
    public string Ip { get; }

    // Indicates if positions are given by the <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.SetpointRequested" data-throw-if-not-resolved="false"></xref> event
    public bool IsCallbackStreaming { get; }

    // Indicates if the queued trajectories are paused
    public bool IsPaused { get; }

    // Indicates if the robot follows a target given by <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.SetJointTrackingTarget(UnderAutomation.Fanuc.Common.JointsPosition)" data-throw-if-not-resolved="false"></xref> or <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.SetCartesianTrackingTarget(UnderAutomation.Fanuc.Common.XYZWPRPosition)" data-throw-if-not-resolved="false"></xref>
    public bool IsTracking { get; }

    // Joint limits used to stop the robot smoothly when the positions stop in joint format.
    // It is set to the reference limits of the robot when the limits are read.
    public JointLimits JointLimits { get; set; }

    // Last status received from the robot, or null if no status was received
    public StreamMotionStatus LastStatus { get; }

    // Limits read from the robot by <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.ReadLimits" data-throw-if-not-resolved="false"></xref> or when the status output starts. Null if they were not read.
    public StreamMotionLimits Limits { get; }

    // Raised when the robot received the last position of a queued trajectory
    public event EventHandler<MotionEventArgs> MotionCompleted

    // Speed of the queued trajectories in percent (greater than 0, up to 100, default 100). The robot itself must run at 100% override,
    // so this value slows down the trajectories on their path: the positions are the same, only the time is stretched.
    // A change is applied progressively.
    public double Override { get; set; }

    // Stops smoothly on the path of the current trajectory. The queue is kept and <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.Resume" data-throw-if-not-resolved="false"></xref> continues the motion.
    // Use it instead of a HOLD, which is not available during Stream Motion.
    public void Pause()

    // UDP port of the robot
    public int Port { get; }

    // Protocol version used by this client
    public int ProtocolVersion { get; }

    // Position where the next queued Cartesian trajectory must start: end of the queue, or last position sent when the queue is empty.
    // Before any Cartesian position was sent, it is the Cartesian position of the status (flange center in the world frame by default).
    // Some controllers expect Cartesian positions of the active tool frame: the start of the first trajectory is then not checked.
    // Null if no status was received.
    public ExtendedCartesianPosition QueueEndCartesianPosition { get; }

    // Position where the next queued joint trajectory must start: end of the queue, or current position when the queue is empty.
    // Null if no status was received.
    public JointsPosition QueueEndJointPosition { get; }

    // Number of trajectories waiting or running
    public int QueuedMotionCount { get; }

    // Reads the velocity, acceleration and jerk limits of all axes from the robot.
    // The status output is stopped during the reading and started again.
    // Some controllers do not answer while a program waits on an IBGN start instruction: read the limits before.
    // Not allowed during a session.
    public StreamMotionLimits ReadLimits()

    // Continues the queued trajectories after <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.Pause" data-throw-if-not-resolved="false"></xref>
    public void Resume()

    // Number of sessions started since the connection. A session starts when positions are sent after an IBGN start instruction.
    public int SessionCount { get; }

    // Raised when a session ends
    public event EventHandler<SessionEndedEventArgs> SessionEnded

    // Raised when a session starts (first position sent after an IBGN start instruction)
    public event EventHandler<SessionEventArgs> SessionStarted

    // Gives a new Cartesian target to follow (see <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.StartTracking(UnderAutomation.Robotics.Motion.PositionFormat%2cSystem.Double%2cSystem.Double)" data-throw-if-not-resolved="false"></xref>).
    // Extended axes are used when the target is an <xref href="UnderAutomation.Fanuc.Common.ExtendedCartesianPosition" data-throw-if-not-resolved="false"></xref>, otherwise they keep their target.
    public void SetCartesianTrackingTarget(XYZWPRPosition target)

    // Gives a new joint target to follow (see <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.StartTracking(UnderAutomation.Robotics.Motion.PositionFormat%2cSystem.Double%2cSystem.Double)" data-throw-if-not-resolved="false"></xref>)
    public void SetJointTrackingTarget(JointsPosition target)

    // Raised in callback streaming mode (see <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.StartCallbackStreaming(UnderAutomation.Robotics.Motion.PositionFormat)" data-throw-if-not-resolved="false"></xref>) each time a position must be sent.
    // The handler must give the next position with SetJoints or SetCartesian.
    // It runs on the communication thread, a few cycles before the robot executes the position, and must return quickly.
    // The first requested position (CycleIndex 0) must be the current position of the robot, and the next ones must connect smoothly to it.
    public event EventHandler<SetpointRequestEventArgs> SetpointRequested

    // Starts to take the positions from the <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.SetpointRequested" data-throw-if-not-resolved="false"></xref> event instead of the queue.
    // The session starts automatically when the robot accepts positions.
    public void StartCallbackStreaming(PositionFormat format)

    // Starts the status output of the robot. The robot then sends its status every communication cycle.
    // The limits of the robot are read first when they are not known yet, and this method returns when the communication cycle is measured.
    public void StartMonitoring()

    // Maximum distance between the first position of a trajectory and the position where it starts, in mm or degrees (default 0.01).
    public double StartTolerance { get; set; }

    // Starts to follow a target position: the robot goes to the last target given by <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.SetJointTrackingTarget(UnderAutomation.Fanuc.Common.JointsPosition)" data-throw-if-not-resolved="false"></xref> or
    // <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.SetCartesianTrackingTarget(UnderAutomation.Fanuc.Common.XYZWPRPosition)" data-throw-if-not-resolved="false"></xref> as fast as the limits allow, and stops on it. The target can change at any time,
    // even during the motion: the robot then goes smoothly to the new target.
    // The limits are <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.JointLimits" data-throw-if-not-resolved="false"></xref> in joint format, and <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.CartesianLimits" data-throw-if-not-resolved="false"></xref> in Cartesian format (the linear and angular limits
    // are shared between X, Y, Z and between the 3 rotation axes). Each axis moves independently, so the path to the target is not a straight line.
    // The first target is the current position. The session starts automatically when the robot accepts positions.
    // The delay between a new target and the start of the motion is about <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.BufferLead" data-throw-if-not-resolved="false"></xref> cycles plus the delay of the robot:
    // reduce the buffer lead time of the connection parameters for a faster reaction.
    public void StartTracking(PositionFormat format, double speedPercent = 100, double accelerationPercent = 100)

    // Current state of the client
    public StreamMotionState State { get; }

    // Communication statistics since the status output was started
    public StreamMotionStatistics Statistics { get; }

    // Raised when a status is received. It is raised on a dedicated thread and only with the latest status:
    // if the handler is slow, some status are skipped.
    public event EventHandler<StatusReceivedEventArgs> StatusReceived

    // Stops the callback streaming. The last position is kept, and the robot stops smoothly if it was moving.
    public void StopCallbackStreaming()

    // Stops the status output of the robot. Not allowed during a session: call <xref href="UnderAutomation.Fanuc.StreamMotion.Internal.StreamMotionClientBase.Finish(System.Int32)" data-throw-if-not-resolved="false"></xref> first.
    public void StopMonitoring()

    // Stops the target tracking. If the robot was moving, it stops as fast as the limits allow, then it keeps its position.
    public void StopTracking()

    // Raised when the queue becomes empty while the robot is moving. The robot is then stopped smoothly.
    public event EventHandler<MotionEventArgs> Underrun

    // Waits until the queue is empty and the robot does not move. During target tracking, waits until the robot is stopped on the target.
    public bool WaitForIdle(int timeoutMs)

    // Waits until the robot received the last position of a queued trajectory
    public bool WaitForMotion(int motionId, int timeoutMs)

    // Waits until a program executes an IBGN start instruction and the robot accepts positions.
    public bool WaitForReady(int timeoutMs)

    // Writes one digital I/O with the next position sent to the robot (only during a session)
    public void WriteIO(IOType type, int index, bool value)

    // Writes up to 16 consecutive digital I/O with the next position sent to the robot (only during a session)
    public void WriteIOGroup(IOType type, int index, int mask, int value)
}
```