The Motion module creates smooth robot trajectories that respect velocity, acceleration and jerk limits. It works offline, without robot connection, and its trajectories can be sent with [Stream Motion](/fanuc/documentation/stream-motion), used in a simulation, or checked before use.

Motions are described as in a TP program: joint (J), linear (L) and circular (C) motions, with a speed and a FINE, CNT or CR termination. The module also creates splines through points, geometric shapes, and trajectories from your own positions.

The planner is in the `UnderAutomation.Robotics.Motion` namespace. This namespace is the same in all UnderAutomation robot SDKs, so the same code can plan trajectories for other robot brands. The `FanucMotion` class of `UnderAutomation.Fanuc.Motion` converts FANUC positions, FINE, CNT and CR terminations and I/O to the types of the planner.

## Quick start

**C# : MotionQuickStart**
```csharp
using UnderAutomation.Fanuc.Common;
using UnderAutomation.Fanuc.Motion;
using UnderAutomation.Robotics.Geometry;
using UnderAutomation.Robotics.Motion;

public class MotionQuickStart
{
    static void Main()
    {
        // Limits of the robot (example values). Read them with robot.StreamMotion.ReadLimits().ReferenceLimits
        var jointLimits = new JointLimits(
            new double[] { 120, 120, 180, 180, 180, 180 },
            new double[] { 300, 300, 450, 675, 675, 675 },
            new double[] { 1125, 1125, 1687, 2530, 1265, 2530 });
        var cartesianLimits = new CartesianLimits(500, 2000, 10000, 90, 360, 1800);

        /**/
        var planner = new MotionPlanner(jointLimits, cartesianLimits);

        // Joint motions, as J instructions of a TP program
        var home = new JointValues(0, 0, 0, 0, -90, 0);
        var pick = new JointValues(30, 20, -10, 0, -70, 30);
        Trajectory trajectory = planner.CreateJointPath(home)
            .MoveJoint(pick, 50, FanucMotion.Cnt(100))   // 50% speed, CNT100
            .MoveJoint(home, 50, FanucMotion.Fine())
            .Build();

        Console.WriteLine($"Duration: {trajectory.Duration:0.000} s");

        // Position at any time, or one position per communication cycle
        JointValues middle = trajectory.GetJoints(trajectory.Duration / 2);
        JointValues[] samples = trajectory.SampleJoints(0.008);

        // FANUC joint position, when needed
        JointsPosition fanucMiddle = FanucMotion.ToJointsPosition(middle);

        // Velocity, acceleration and jerk of each axis, computed as the robot does
        TrajectoryReport report = trajectory.Check(jointLimits, 0.008, false);
        Console.WriteLine(report.IsValid);
        /**/
    }
}
```

**Python : MotionQuickStart**
```python
from underautomation.fanuc.motion.fanuc_motion import FanucMotion
from underautomation.robotics.geometry.joint_values import JointValues
from underautomation.robotics.motion.joint_limits import JointLimits
from underautomation.robotics.motion.cartesian_limits import CartesianLimits
from underautomation.robotics.motion.motion_planner import MotionPlanner

# Limits of the robot (example values). Read them with robot.stream_motion.read_limits().reference_limits
joint_limits = JointLimits(
    [120, 120, 180, 180, 180, 180],
    [300, 300, 450, 675, 675, 675],
    [1125, 1125, 1687, 2530, 1265, 2530])
cartesian_limits = CartesianLimits(500, 2000, 10000, 90, 360, 1800)

##
planner = MotionPlanner(joint_limits, cartesian_limits)

# Joint motions, as J instructions of a TP program
home = JointValues([0, 0, 0, 0, -90, 0])
pick = JointValues([30, 20, -10, 0, -70, 30])
trajectory = planner.create_joint_path(home) \
    .move_joint(pick, 50, FanucMotion.cnt(100)) \
    .move_joint(home, 50, FanucMotion.fine()) \
    .build()

print(f"Duration: {trajectory.duration:.3f} s")

# Position at any time, or one position per communication cycle
middle = trajectory.get_joints(trajectory.duration / 2)
samples = trajectory.sample_joints(0.008)

# FANUC joint position, when needed
fanuc_middle = FanucMotion.to_joints_position(middle)

# Velocity, acceleration and jerk of each axis, computed as the robot does
report = trajectory.check(joint_limits, 0.008, False)
print(report.is_valid)
##
```

![Joint positions of this trajectory. With CNT100, the robot passes near pick without stopping. With FINE, it stops at home.](/fanuc/documentation/diagrams/motion-quick-start.svg)

## Try it in the Showcase app

The **Showcase** demo application (Windows, WinForms) has a Stream Motion page with two buttons that build trajectories with the motion planner and send them to the robot with [Stream Motion](/fanuc/documentation/stream-motion). Download it from the [download page](/fanuc/download).

- **Joint demo** uses a `JointPathBuilder`: J1 moves to +amplitude, then to -amplitude with a `Cnt` termination between the two moves, then back to the start with `Fine`.
- **Cartesian demo** uses a `CartesianPathBuilder`: a horizontal circle of the given radius, starting and ending at the current position.

Change the amplitude, the radius or the speed and send the demo again: the duration of the resulting trajectory is shown in the log, and the joint and Cartesian positions update in real time while the robot moves.

![Stream Motion page of the Showcase demo application, with the joint and Cartesian demo motions](/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).

## Main concepts

| Class | Role |
| --- | --- |
| `JointLimits` | Velocity, acceleration and jerk of each axis (9 axes). Read them from the robot with `StreamMotion.ReadLimits()` |
| `CartesianLimits` | Linear and angular velocity, acceleration and jerk, for Cartesian motions. You choose them |
| `MotionPlanner` | Creates joint and Cartesian paths with the limits, and optional tool and user frames |
| `JointPathBuilder`, `CartesianPathBuilder` | Chain motions, waits and I/O, then `Build()` the trajectory |
| `Trajectory` | Result: position at any time, samples at a fixed period, I/O events, limit checks |
| `JointValues`, `CartesianPose` | Joint and Cartesian positions of the planner. A `CartesianPose` has X, Y, Z, an `Orientation` and optional external axes |
| `FanucMotion` | Conversions with `JointsPosition` and `XYZWPRPosition`, FINE, CNT and CR terminations, I/O signals, FANUC positions of a trajectory |

All motions use jerk limited profiles: the acceleration changes progressively, which gives smooth motions and avoids the jerk alarms of the robot. 100% speed means the velocity limits, and the acceleration percentage (ACC) scales the acceleration and the jerk.

![Velocity of J1 for the same joint motion. The speed percentage limits the velocity, ACC changes the slopes.](/fanuc/documentation/diagrams/motion-speed-acceleration.svg)

## What do you want to do?

| Need | Page |
| --- | --- |
| Move to positions with J, L, C motions, FINE, CNT, CR | [Joint & Cartesian motions](/fanuc/documentation/motion-moves) |
| Pass through a list of points, draw a circle, a rectangle or a helix | [Splines & shapes](/fanuc/documentation/motion-splines-shapes) |
| Use positions computed by your application, check or slow down a trajectory | [Trajectories from points](/fanuc/documentation/motion-trajectory-from-points) |
| Convert orientations and positions between frames | [Frames & orientations](/fanuc/documentation/motion-frames-orientations) |

## Units and conventions

- Joint positions in degrees (mm for linear axes), Cartesian positions in mm, angles in degrees.
- Velocities per second, accelerations per second squared, jerks per second cubed.
- The W, P, R angles of FANUC use the fixed XYZ convention: `CartesianPose.FromEuler(x, y, z, w, p, r, EulerConvention.FixedXYZ)` gives the same pose as `FanucMotion.ToCartesianPose(new XYZWPRPosition(x, y, z, w, p, r))`.
- `FanucMotion.SampleCartesian()` gives FANUC positions whose W, P, R angles stay continuous: an angle can go beyond 180 degrees instead of jumping to -180.

## API reference

**Members of Motion.MotionPlanner**
```csharp
public class MotionPlanner {
    // Creates a planner
    public MotionPlanner(JointLimits jointLimits, CartesianLimits cartesianLimits)

    // Limits for Cartesian motions
    public CartesianLimits CartesianLimits { get; set; }

    // Starts a Cartesian path
    public CartesianPathBuilder CreateCartesianPath(XYZWPRPosition start)

    // Starts a joint path
    public JointPathBuilder CreateJointPath(JointsPosition start)

    // Limits for joint motions. 100% speed uses the velocity limits of this object.
    public JointLimits JointLimits { get; set; }

    // Tool frame, relative to the flange. When it is set, the targets of Cartesian motions are positions of this tool,
    // and the trajectory gives the flange positions. Null when the targets are flange positions.
    public XYZWPRPosition ToolFrame { get; set; }

    // User frame, relative to the world frame. When it is set, the targets of Cartesian motions are expressed in this frame,
    // and the trajectory gives positions in the world frame. Null when the targets are in the world frame.
    public XYZWPRPosition UserFrame { get; set; }
}
```

**Members of Motion.JointLimits**
```csharp
public class JointLimits {
    // Creates limits with all values set to 0
    public JointLimits()

    // Creates limits from arrays of values. Arrays can contain less than 9 values, missing values are set to 0.
    public JointLimits(double[] velocity, double[] acceleration, double[] jerk)

    // Acceleration limit of each axis (9 values)
    public double[] Acceleration { get; }

    // Number of axes handled by this class
    public const int AxisCount = 9

    public override bool Equals(object obj)

    public override int GetHashCode()

    // Jerk limit of each axis (9 values)
    public double[] Jerk { get; }

    // Returns a copy of these limits where each value is multiplied by the given factors
    public JointLimits Scale(double velocityFactor, double accelerationFactor, double jerkFactor)

    public override string ToString()

    // Velocity limit of each axis (9 values)
    public double[] Velocity { get; }
}
```

**Members of Motion.CartesianLimits**
```csharp
public class CartesianLimits {
    // Creates limits with all values set to 0
    public CartesianLimits()

    // Creates limits with the given values
    public CartesianLimits(double linearVelocity, double linearAcceleration, double linearJerk, double angularVelocity, double angularAcceleration, double angularJerk)

    // Angular acceleration limit in deg/s²
    public double AngularAcceleration { get; set; }

    // Angular jerk limit in deg/s³
    public double AngularJerk { get; set; }

    // Angular velocity limit in deg/s
    public double AngularVelocity { get; set; }

    public override bool Equals(object obj)

    public override int GetHashCode()

    // Linear acceleration limit in mm/s²
    public double LinearAcceleration { get; set; }

    // Linear jerk limit in mm/s³
    public double LinearJerk { get; set; }

    // Linear velocity limit in mm/s
    public double LinearVelocity { get; set; }

    public override string ToString()
}
```