Stream Motion (J519)
Stream Motion is Fanuc's real-time streaming motion control interface over UDP. Send continuous motion commands at 125Hz or 250Hz for smooth, coordinated motion.
Stream Motion is Fanuc's real-time streaming motion control interface, available as the J519 paid option on compatible controllers. It enables external applications to send continuous motion commands to the robot at high frequency (125Hz or 250Hz), making it ideal for applications requiring smooth, coordinated motion such as:
- Force control and adaptive machining
- Sensor-guided motion
- Real-time trajectory generation
- Human-robot collaboration
- Custom motion planning algorithms
Requirements
Controller Requirements
- Fanuc controller with J519 Stream Motion option installed
- Compatible robot model (not all models support Stream Motion - verify with Fanuc)
- Network connectivity (UDP port 60015 by default)
Key Limitations
| Feature | Limitation |
|---|---|
| Motion groups | Group 1 (robot) + up to 3 auxiliary axes |
| Coordinate mode | Joint OR Cartesian (cannot switch mid-session) |
| Update rate | 125Hz or 250Hz (controller dependent) |
| Control type | Position only (no velocity/torque control) |
Quick Start
var robot = new FanucRobot();var parameters = new ConnectionParameters("192.168.1.1");parameters.StreamMotion.Enable = true;robot.Connect(parameters);float nextJ1 = 10, nextJ2 = 20, nextJ3 = 30, nextJ4 = 0, nextJ5 = -45, nextJ6 = 90;robot.StreamMotion.StateReceived += (sender, e) =>{var state = e.State;Console.WriteLine($"Position: J1={state.JointPosition.J1:F2}°");if (state.Status.ReadyForCommands &&!state.Status.InMotion &&!state.Status.CommandReceived){var command = new CommandPacket{DataStyle = DataStyle.Joint,MotionData = new MotionData{J1 = nextJ1,J2 = nextJ2,J3 = nextJ3,J4 = nextJ4,J5 = nextJ5,J6 = nextJ6}};robot.StreamMotion.SendCommand(command);}};robot.StreamMotion.Start();// ... your application logic ...robot.StreamMotion.Stop();
Architecture Overview
Stream Motion uses a UDP-based protocol with the following communication pattern:
┌─────────────────┐ ┌─────────────────┐
│ Your App │ │ Fanuc Robot │
│ │ │ │
│ ┌───────────┐ │ Start Packet │ ┌───────────┐ │
│ │ Stream │──┼────────────────────> | │ Stream │ │
│ │ Motion │ │ │ │ Motion │ │
│ │ Client │<─┼──────────────────────┼──│ Server │ │
│ └───────────┘ │ State Packets │ └───────────┘ │
│ │ │ (~500Hz) │ ▲ │
│ │ │ │ │ │
│ └─────────┼──────────────────────┼───────┘ │
│ │ Command Packets │ │
│ │ (125/250Hz) │ │
└─────────────────┘ └─────────────────┘
Communication Flow
- Connect - Establish UDP socket connection
- Start - Send start packet to begin streaming session
- Receive States - Robot sends state packets at ~500Hz
- Send Commands - Your app sends interpolated positions at 125/250Hz
- Stop - Send stop packet to end session
- Disconnect - Close socket
Robot Program Setup
A TP (Teach Pendant) program must be running on the robot to handle Stream Motion commands.
Required TP Program
Create a program named for instance START_STREAM_MOTION_J519.ls with this content:
/PROG START_STREAM_MOTION_J519/ATTROWNER = 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;/MN1: LBL[1] ;2: IBGN start[1] ;3: IBGN end[1] ;4: JMP LBL[1] ;/POS/END
Program Explanation
| Line | Instruction | Description |
|---|---|---|
| 1 | LBL[1] | Loop label |
| 2 | IBGN start[1] | Start Stream Motion session |
| 3 | IBGN end[1] | End Stream Motion session |
| 4 | JMP LBL[1] | Loop back to handle next session |
Running the Program
- Upload the program to the robot via FTP or teach pendant
- Start the program in AUTO mode
- The program loops, waiting for Stream Motion connections
- Your application connects and controls motion via UDP
- When streaming stops, the program loops back to wait for the next connection
Adding Custom Logic
You can add instructions outside the Stream Motion block:
/MN1: LBL[1] ;2: DO[1]=ON ; // Turn on output before streaming3: IBGN start[1] ;4: IBGN end[1] ;5: DO[1]=OFF ; // Turn off output after streaming6: WAIT 1.00(sec) ; // Wait before next session7: JMP LBL[1] ;/POS/END
Connection and Streaming
Connecting to the Robot
var robot = new FanucRobot();// Connect with default settings (port 60015)var parameters = new ConnectionParameters("192.168.1.1");parameters.StreamMotion.Enable = true;robot.Connect(parameters);// Standalone clientvar client = new StreamMotionClient();client.Connect("192.168.1.1");// Start / stop streamingrobot.StreamMotion.Start();Console.WriteLine($"Streaming: {robot.StreamMotion.IsStreaming}");Console.WriteLine($"Frequency: {robot.StreamMotion.MeasuredFrequency:F1} Hz");robot.StreamMotion.Stop();robot.StreamMotion.Disconnect();
Standalone Client
You can also use the StreamMotionClient class directly without FanucRobot.
Starting and Stopping Streaming
State Packets
The robot sends state packets continuously at approximately 500Hz. Each packet contains the robot's current state.
StatePacket Properties
| Property | Type | Description |
|---|---|---|
SequenceNumber | uint | Packet sequence for synchronization |
TimeStamp | uint | Robot timestamp (ms, 2ms resolution) |
Status | RobotStatus | Status flags |
JointPosition | MotionData | Current joint positions (J1-J9) |
CartesianPosition | MotionData | Current Cartesian position (X,Y,Z,W,P,R + E1-E3) |
MotorCurrents | MotionData | Motor currents in Amperes |
IORead | IOReadResult | Result of I/O read operation |
RobotStatus Flags
| Flag | Description |
|---|---|
ReadyForCommands | Robot is ready to receive command packets |
CommandReceived | Robot has received and is processing a command |
SystemReady | System ready (SYSRDY) |
InMotion | Robot is currently executing motion |
Handling State Packets
robot.StreamMotion.StateReceived += (sender, e) =>{var state = e.State;// Read current joint positionsConsole.WriteLine($"J1: {state.JointPosition.J1:F3}°");Console.WriteLine($"J2: {state.JointPosition.J2:F3}°");// Read Cartesian positionConsole.WriteLine($"X: {state.CartesianPosition.X:F2} mm");Console.WriteLine($"Y: {state.CartesianPosition.Y:F2} mm");// Check statusConsole.WriteLine($"Ready: {state.Status.ReadyForCommands}");Console.WriteLine($"In Motion: {state.Status.InMotion}");// Read motor currentsConsole.WriteLine($"Motor 1: {state.MotorCurrents.Axis1:F2} A");// Only send command when readybool canSend = state.Status.ReadyForCommands &&!state.Status.CommandReceived &&!state.Status.InMotion;};
Command Packets
Command packets are sent to instruct the robot to move to a new position.
CommandPacket Properties
| Property | Type | Description |
|---|---|---|
DataStyle | DataStyle | Joint or Cartesian |
MotionData | MotionData | Target position |
IsLastData | bool | Marks end of motion sequence |
ReadIOType | IOType | Type of I/O to read |
ReadIOIndex | ushort | I/O index to read |
ReadIOMask | ushort | I/O read mask |
WriteIOType | IOType | Type of I/O to write |
WriteIOIndex | ushort | I/O index to write |
WriteIOMask | ushort | I/O write mask |
WriteIOValue | ushort | Value to write |
MotionData Structure
MotionData holds 9 axis values that can be accessed by axis number or name.
Sending Commands
// MotionData: 9 axis valuesvar motion = new MotionData();motion.J1 = 10.0f; motion.J2 = 20.0f; motion.J3 = 30.0f;motion.J4 = 0.0f; motion.J5 = -45.0f; motion.J6 = 90.0f;// Cartesian: motion.X, motion.Y, motion.Z, motion.W, motion.P, motion.R// Full command packetvar command = new CommandPacket{DataStyle = DataStyle.Joint,MotionData = new MotionData { J1 = 10, J2 = 20, J3 = 30, J4 = 0, J5 = -45, J6 = 90 },IsLastData = false};robot.StreamMotion.SendCommand(command);// Convenience: joint motionrobot.StreamMotion.SendJointCommand(new MotionData { J1 = 10, J2 = 20, J3 = 30, J4 = 0, J5 = -45, J6 = 90 },isLastData: false);// Convenience: Cartesian motionrobot.StreamMotion.SendCartesianCommand(new MotionData { X = 500, Y = 100, Z = 300, W = 180, P = 0, R = 0 },isLastData: false);
Protocol State Machine
The Stream Motion protocol follows a strict state machine. Understanding this is critical for proper operation.
State Transitions
┌─────────────────────────────────────┐
│ │
▼ │
┌───────────────────────────┐ │
│ READY FOR COMMANDS │ │
│ ReadyForCommands = true │ │
│ CommandReceived = false │ │
│ InMotion = false │ │
└───────────────────────────┘ │
│ │
│ Send Command │
▼ │
┌───────────────────────────┐ │
│ COMMAND RECEIVED │ │
│ CommandReceived = true │ │
└───────────────────────────┘ │
│ │
│ Robot starts motion │
▼ │
┌───────────────────────────┐ │
│ IN MOTION │ │
│ InMotion = true │────────────────────────>┤
│ (may receive multiple │ Motion complete │
│ packets in this state) │ │
└───────────────────────────┘ │
Correct Command Timing
Only send the next command when ALL these conditions are met:
See the state packet handling above : check ReadyForCommands, CommandReceived, and InMotion flags before sending.
⚠️ Common Mistakes
| Mistake | Consequence |
|---|---|
| Sending commands too fast | Commands dropped, erratic motion |
Ignoring CommandReceived | Duplicate commands sent |
Ignoring InMotion | Buffer overflow, jerk errors |
Not checking ReadyForCommands | Commands rejected |
Motion Interpolation
Why Interpolation is Required
Stream Motion operates at 125Hz or 250Hz. Each command should represent a small incremental move. The robot's motion planner expects smooth, continuous position updates that respect:
- Velocity limits - Maximum speed for each axis
- Acceleration limits - Maximum rate of velocity change
- Jerk limits - Maximum rate of acceleration change (smoothness)
Simple Linear Interpolation (Educational Example)
// Simple linear interpolator (educational)public static float[] LinearInterpolate(float[] start, float[] target, double t){float[] pos = new float[6];for (int i = 0; i < 6; i++)pos[i] = start[i] + (float)(t * (target[i] - start[i]));return pos;}// S-curve interpolation (recommended for smooth motion)public static float[] SCurveInterpolate(float[] start, float[] target, double t){// S-curve: s(t) = 6t^5 - 15t^4 + 10t^3double s = t * t * t * (t * (t * 6 - 15) + 10);float[] pos = new float[6];for (int i = 0; i < 6; i++)pos[i] = start[i] + (float)(s * (target[i] - start[i]));return pos;}
S-Curve Interpolation (Recommended)
For smooth, jerk-limited motion, use an S-curve or quintic polynomial profile. The snippet above shows both linear and S-curve approaches.
Integration with Stream Motion
Combine the interpolator with the Stream Motion state machine to send smoothly interpolated positions at each cycle.
Querying Axis Limits
Before commanding motion, you can query the robot's axis limits. This must be done before starting streaming.
// Query velocity limits for axis 1var velocityAck = robot.StreamMotion.RequestThreshold(1, ThresholdType.Velocity);Console.WriteLine($"Axis 1 max velocity: {velocityAck.ThresholdsNoLoad[19]:F2} deg/s");// Query acceleration limitsvar accelAck = robot.StreamMotion.RequestThreshold(1, ThresholdType.Acceleration);// Query jerk limitsvar jerkAck = robot.StreamMotion.RequestThreshold(1, ThresholdType.Jerk);// Query all axesfor (uint axis = 1; axis <= 6; axis++){var ack = robot.StreamMotion.RequestThreshold(axis, ThresholdType.Velocity);Console.WriteLine($"J{axis} max velocity: {ack.ThresholdsNoLoad[19]:F2} deg/s");}
AckPacket Threshold Arrays
The threshold arrays contain 20 values at different velocity percentages:
| Index | Velocity % |
|---|---|
| 0 | 5% |
| 1 | 10% |
| ... | ... |
| 19 | 100% |
Use ThresholdsNoLoad for unloaded robot and ThresholdsMaxLoad for maximum payload.
I/O Operations
Stream Motion supports reading and writing digital I/O during motion.
Reading I/O
var motionData = new MotionData { J1 = 10, J2 = 20, J3 = 30, J4 = 0, J5 = -45, J6 = 90 };// Read I/O during motionvar readCommand = new CommandPacket{DataStyle = DataStyle.Joint,MotionData = motionData,ReadIOType = IOType.DI,ReadIOIndex = 1,ReadIOMask = 0xFFFF};robot.StreamMotion.SendCommand(readCommand);// Write I/O during motionvar writeCommand = new CommandPacket{DataStyle = DataStyle.Joint,MotionData = motionData,WriteIOType = IOType.DO,WriteIOIndex = 1,WriteIOMask = 0x0001,WriteIOValue = 0x0001};robot.StreamMotion.SendCommand(writeCommand);
Writing I/O
Best Practices
var command = new CommandPacket{DataStyle = DataStyle.Joint,MotionData = new MotionData { J1 = 10, J2 = 20, J3 = 30, J4 = 0, J5 = -45, J6 = 90 }};// Always check state before sendingrobot.StreamMotion.StateReceived += (s, e) =>{if (e.State.Status.ReadyForCommands &&!e.State.Status.CommandReceived &&!e.State.Status.InMotion){robot.StreamMotion.SendCommand(command);}};// Handle errorsrobot.StreamMotion.ReceiveError += (s, e) =>{Console.WriteLine($"Error: {e.Exception.Message}");};// Query limits before motionfor (uint i = 1; i <= 6; i++){var ack = robot.StreamMotion.RequestThreshold(i, ThresholdType.Velocity);}// Clean shutdowntry { robot.StreamMotion.Start(); }finally{if (robot.StreamMotion.IsStreaming) robot.StreamMotion.Stop();}
1. Always Implement Proper Interpolation
2. Respect the Protocol State Machine
3. Handle Errors Gracefully
4. Query Limits Before Motion
5. Clean Shutdown
Troubleshooting
Common Errors
| Error | Cause | Solution |
|---|---|---|
| "Joint jerk limit exceeded" | Motion too fast/abrupt | Implement proper S-curve interpolation |
| "Robot not ready for commands" | Wrong state | Wait for ReadyForCommands=true |
| Connection timeout | Network issue | Check IP, port, firewall |
| "Streaming already started" | Double start | Check IsStreaming before Start() |
| Commands not executed | Sent too fast | Follow protocol state machine |
Diagnostic Logging
robot.StreamMotion.StateReceived += (s, e) =>{var state = e.State;Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] " +$"Seq={state.SequenceNumber} " +$"Ready={state.Status.ReadyForCommands} " +$"CmdRcv={state.Status.CommandReceived} " +$"Motion={state.Status.InMotion} " +$"J1={state.JointPosition.J1:F3}");};Console.WriteLine($"Robot frequency: {robot.StreamMotion.RobotFrequency:F1} Hz");Console.WriteLine($"Measured frequency: {robot.StreamMotion.MeasuredFrequency:F1} Hz");Console.WriteLine($"Packets received: {robot.StreamMotion.PacketCount}");
Checking Frequencies
Complete Example
Here's a complete example that moves the robot through a sequence of waypoints:
class StreamMotionExample{private FanucRobot _robot;private float[][] _waypoints;private int _currentWaypoint;private bool _waitingForAck;private readonly object _lock = new object();public void Run(){_robot = new FanucRobot();// Define waypoints (J1-J6 in degrees)_waypoints = new float[][]{new float[] { 0, 0, 0, 0, 0, 0 },new float[] { 30, 10, -20, 0, 45, 0 },new float[] { -30, 20, -40, 90, 0, 45 },new float[] { 0, 0, 0, 0, 0, 0 }};_currentWaypoint = 0;try{// ConnectConsole.WriteLine("Connecting to robot...");var parameters = new ConnectionParameters("192.168.1.1");parameters.StreamMotion.Enable = true;_robot.Connect(parameters);Console.WriteLine("Connected!");// Query axis limitsfor (uint axis = 1; axis <= 6; axis++){var ack = _robot.StreamMotion.RequestThreshold(axis, ThresholdType.Velocity);Console.WriteLine($" J{axis} max velocity: {ack.ThresholdsNoLoad[19]:F1} deg/s");}// Setup event handlers_robot.StreamMotion.StateReceived += OnStateReceived;_robot.StreamMotion.ReceiveError += OnReceiveError;// Start streaming_robot.StreamMotion.Start();// Wait for user to stopConsole.WriteLine("Moving through waypoints... Press Enter to stop.");Console.ReadLine();}finally{// Clean shutdownif (_robot.StreamMotion.IsStreaming)_robot.StreamMotion.Stop();_robot.StreamMotion.Disconnect();}}private void OnStateReceived(object sender, StateReceivedEventArgs e){lock (_lock){var status = e.State.Status;if (_waitingForAck){if (!status.InMotion && !status.CommandReceived)_waitingForAck = false;elsereturn;}if (!status.ReadyForCommands || status.InMotion || status.CommandReceived)return;// Advance to next waypoint_currentWaypoint = (_currentWaypoint + 1) % _waypoints.Length;float[] target = _waypoints[_currentWaypoint];Console.WriteLine($"Moving to waypoint {_currentWaypoint}");var command = new CommandPacket{DataStyle = DataStyle.Joint,MotionData = new MotionData{J1 = target[0],J2 = target[1],J3 = target[2],J4 = target[3],J5 = target[4],J6 = target[5]},IsLastData = true};_robot.StreamMotion.SendCommand(command);_waitingForAck = true;}}private void OnReceiveError(object sender, ReceiveErrorEventArgs e){Console.WriteLine($"Error: {e.Exception.Message}");}static void Main(string[] args){new StreamMotionExample().Run();}}
Summary
Stream Motion (J519) provides powerful real-time robot control, but requires careful implementation:
- ✅ Install J519 option on your controller
- ✅ Run the TP program with
IBGN start[]/IBGN end[] - ✅ Implement smooth interpolation - never jump to positions
- ✅ Follow the protocol state machine - check status before sending
- ✅ Query and respect axis limits - velocity, acceleration, jerk
- ✅ Handle errors gracefully - implement reconnection logic