This page shows how to move a Staubli robot from a PC with the SDK: power the arm, describe the motion, send joint, linear and circular moves, then stop, restart or cancel them. The moves are executed by the motion planner of the CS8 or CS9 controller.

A robot is dangerous machinery. Test your code on the [emulator of Staubli Robotics Suite](/staubli/documentation/simulator) first, then on the real robot with low speeds and a person at the emergency stop.

## Prerequisites

- The controller is in remote mode. Otherwise the power and the moves are refused.
- The user of the connection has the right to move the arm.
- The arm is powered: see below.

## Power

`SetPower(true)` powers the arm, `SetPower(false)` switches it off. The answer tells if the controller accepted the request.

**C# : MotionPower**
```csharp
using UnderAutomation.Staubli;
using UnderAutomation.Staubli.Soap.Data;

public class MotionPower
{
    static void Main()
    {
        var controller = new StaubliController();
        controller.Connect("192.168.0.254");

        /**/
        // The controller must be in remote mode
        PowerReturnCode code = controller.Soap.SetPower(true);

        if (code != PowerReturnCode.Success)
            Console.WriteLine($"Arm not powered: {code}"); // OnlyInRemoteMode, EnableTimeout...

        // Power off at the end
        controller.Soap.SetPower(false);
        /**/

        controller.Disconnect();
    }
}
```

**Python : MotionPower**
```python
from underautomation.staubli.staubli_controller import StaubliController
from underautomation.staubli.soap.data.power_return_code import PowerReturnCode

controller = StaubliController()
controller.connect("192.168.0.254")

##
# The controller must be in remote mode
code = controller.soap.set_power(True)

if code != PowerReturnCode.Success:
    print(f"Arm not powered: {code.name}")  # OnlyInRemoteMode, EnableTimeout...

# Power off at the end
controller.soap.set_power(False)
##

controller.disconnect()
```

| `PowerReturnCode`  | Meaning                                     |
| ------------------ | ------------------------------------------- |
| `Success`          | Done                                        |
| `OnlyInRemoteMode` | The controller is not in remote mode        |
| `RobotNotStopped`  | The power cannot change while the arm moves |
| `EnableTimeout`    | The arm was not powered in time             |
| `DisableTimeout`   | The arm was not switched off in time        |

## Motion descriptor

Every move takes a `MotionDesc`: the speed limits, the blending, the tool, the reference frame and the configuration of the arm.

**C# : MotionDescriptor**
```csharp
using UnderAutomation.Staubli;
using UnderAutomation.Staubli.Soap.Data;

public class MotionDescriptor
{
    static void Main()
    {
        var controller = new StaubliController();
        controller.Connect("192.168.0.254");

        double[] joints = controller.Soap.GetCurrentJointPosition(robot: 0);
        IForwardKinematics fk = controller.Soap.ForwardKinematics(robot: 0, joints);

        /**/
        var mdesc = new MotionDesc
        {
            // Limits of the joint speed, acceleration and deceleration
            Velocity = 0.5,
            Acceleration = 0.5,
            Deceleration = 0.5,

            // Limits of the speed of the tool center point
            TranslationVelocity = 0.1,
            RotationVelocity = 0.1,

            // Stop at each point, no blending
            BlendType = BlendType.BlendOff,

            // Absolute target, flange as tool, world as reference frame
            AbsRel = MoveType.Absolute,
            Tool = new Frame(),
            Frame = new Frame(),

            // Keep the configuration of the arm (shoulder, elbow, wrist)
            Config = fk.Config,
        };
        /**/

        controller.Disconnect();
    }
}
```

**Python : MotionDescriptor**
```python
from underautomation.staubli.staubli_controller import StaubliController
from underautomation.staubli.soap.data.motion_desc import MotionDesc
from underautomation.staubli.soap.data.frame import Frame
from underautomation.staubli.soap.data.blend_type import BlendType
from underautomation.staubli.soap.data.move_type import MoveType

controller = StaubliController()
controller.connect("192.168.0.254")

joints = controller.soap.get_current_joint_position(0)
fk = controller.soap.forward_kinematics(0, joints)

##
mdesc = MotionDesc()

# Limits of the joint speed, acceleration and deceleration
mdesc.velocity = 0.5
mdesc.acceleration = 0.5
mdesc.deceleration = 0.5

# Limits of the speed of the tool center point
mdesc.translation_velocity = 0.1
mdesc.rotation_velocity = 0.1

# Stop at each point, no blending
mdesc.blend_type = BlendType.BlendOff

# Absolute target, flange as tool, world as reference frame
mdesc.abs_rel = MoveType.Absolute
mdesc.tool = Frame()
mdesc.frame = Frame()

# Keep the configuration of the arm (shoulder, elbow, wrist)
mdesc.config = fk.config
##

controller.disconnect()
```

- `Velocity`, `Acceleration`, `Deceleration`: limits of the joints.
- `TranslationVelocity`, `RotationVelocity`: limits of the tool center point.
- `BlendType`: `BlendOff` stops at each point. `BlendJoint` and `BlendCartesian` join the moves without a stop, from `DistanceBlendPrevious` before the point to `DistanceBlendNext` after it.
- `Tool` and `Frame`: the tool center point and the reference frame of the Cartesian targets. `new Frame()` is the identity: the flange, and the world frame.
- `AbsRel`: `Absolute` targets, or `Relative` to the current position.
- `Config`: the configuration of the arm. See [Kinematics](/staubli/documentation/soap-kinematics).

Start with low values and increase them step by step. Check on the emulator that the speeds and distances are the ones you expect before the first move of a real robot.

## Moves

| Method                                | Path                    | Target                       |
| ------------------------------------- | ----------------------- | ---------------------------- |
| `MoveJJ(robot, joints, mdesc)`        | joint interpolation     | joint values, in radians     |
| `MoveJC(robot, frame, mdesc)`         | joint interpolation     | a Cartesian frame            |
| `MoveL(robot, frame, mdesc)`          | straight line           | a Cartesian frame            |
| `MoveC(robot, frameB, frameC, mdesc)` | circle through `frameB` | the Cartesian frame `frameC` |

**C# : MotionMoves**
```csharp
using UnderAutomation.Staubli;
using UnderAutomation.Staubli.Soap.Data;

public class MotionMoves
{
    static void Main()
    {
        var controller = new StaubliController();
        controller.Connect("192.168.0.254");

        double[] joints = controller.Soap.GetCurrentJointPosition(robot: 0);
        IForwardKinematics fk = controller.Soap.ForwardKinematics(robot: 0, joints);
        var mdesc = new MotionDesc { Velocity = 0.5, Acceleration = 0.5, Deceleration = 0.5, TranslationVelocity = 0.1, RotationVelocity = 0.1, Config = fk.Config };

        Frame target = controller.Soap.ForwardKinematics(robot: 0, joints).Position;
        target.Px += 0.1;
        Frame intermediate = controller.Soap.ForwardKinematics(robot: 0, joints).Position;
        intermediate.Py += 0.05;

        /**/
        controller.Soap.SetPower(true);

        // Joint move to joint values, in radians
        IMoveResult result = controller.Soap.MoveJJ(robot: 0, joints, mdesc);

        // Joint move to a Cartesian frame
        result = controller.Soap.MoveJC(robot: 0, target, mdesc);

        // Straight line to a Cartesian frame
        result = controller.Soap.MoveL(robot: 0, target, mdesc);

        // Circle through an intermediate frame, to a target frame
        result = controller.Soap.MoveC(robot: 0, intermediate, target, mdesc);

        // The controller accepted the move, or tells why not
        if (result.ReturnCode != MotionReturnCode.Success)
            Console.WriteLine($"Move {result.Id} refused: {result.ReturnCode}");
        /**/

        controller.Disconnect();
    }
}
```

**Python : MotionMoves**
```python
from underautomation.staubli.staubli_controller import StaubliController
from underautomation.staubli.soap.data.motion_desc import MotionDesc
from underautomation.staubli.soap.data.motion_return_code import MotionReturnCode

controller = StaubliController()
controller.connect("192.168.0.254")

joints = controller.soap.get_current_joint_position(0)
fk = controller.soap.forward_kinematics(0, joints)
mdesc = MotionDesc()
mdesc.velocity = 0.5
mdesc.acceleration = 0.5
mdesc.deceleration = 0.5
mdesc.translation_velocity = 0.1
mdesc.rotation_velocity = 0.1
mdesc.config = fk.config

target = controller.soap.forward_kinematics(0, joints).position
target.px += 0.1
intermediate = controller.soap.forward_kinematics(0, joints).position
intermediate.py += 0.05

##
controller.soap.set_power(True)

# Joint move to joint values, in radians
result = controller.soap.move_jj(0, joints, mdesc)

# Joint move to a Cartesian frame
result = controller.soap.move_jc(0, target, mdesc)

# Straight line to a Cartesian frame
result = controller.soap.move_l(0, target, mdesc)

# Circle through an intermediate frame, to a target frame
result = controller.soap.move_c(0, intermediate, target, mdesc)

# The controller accepted the move, or tells why not
if result.return_code != MotionReturnCode.Success:
    print(f"Move {result.id} refused: {result.return_code.name}")
##

controller.disconnect()
```

A move method returns the answer of the controller to the request. It does not tell that the arm has reached the target. `IMoveResult.ReturnCode` tells if the move was accepted, and `Id` identifies it:

| `MotionReturnCode` | Meaning                                               |
| ------------------ | ----------------------------------------------------- |
| `Success`          | The move is accepted                                  |
| `NotReady`         | The arm is not ready to move, for example not powered |
| `ParameterError`   | A value of the target or of the descriptor is wrong   |
| `MisuseError`      | The request is not allowed in this state              |
| `UnexpectedError`  | Another error of the controller                       |

To wait for the end of a move, read the position until it reaches the target: see [Position](/staubli/documentation/soap-position).

## Stop, restart and cancel

**C# : MotionControl**
```csharp
using UnderAutomation.Staubli;
using UnderAutomation.Staubli.Soap.Data;

public class MotionControl
{
    static void Main()
    {
        var controller = new StaubliController();
        controller.Connect("192.168.0.254");

        /**/
        // Stop the arm now
        MotionReturnCode code = controller.Soap.StopMotion();

        // Continue the moves that were stopped
        code = controller.Soap.RestartMotion();

        // Or cancel all the moves that are not finished
        code = controller.Soap.ResetMotion();
        /**/

        controller.Disconnect();
    }
}
```

**Python : MotionControl**
```python
from underautomation.staubli.staubli_controller import StaubliController

controller = StaubliController()
controller.connect("192.168.0.254")

##
# Stop the arm now
code = controller.soap.stop_motion()

# Continue the moves that were stopped
code = controller.soap.restart_motion()

# Or cancel all the moves that are not finished
code = controller.soap.reset_motion()
##

controller.disconnect()
```

- `StopMotion()` stops the arm on its path.
- `RestartMotion()` continues the moves that were stopped.
- `ResetMotion()` cancels every move that is not finished. Call it after an error, before new moves.

## Try it in the demo application



## Reference

**Methods of SoapClientBase**
```csharp
// Move the robot to a target position using a Cartesian path
IMoveResult MoveC(int robot, Frame frameB, Frame frameC, MotionDesc mdesc);

// Move the robot to a target position using a Cartesian path with joint constraints
IMoveResult MoveJC(int robot, Frame frame, MotionDesc mdesc);

// Move the robot to a target position using joint positions
IMoveResult MoveJJ(int robot, double[] joints, MotionDesc mdesc);

// Move the robot to a target position using a linear path in Cartesian space
IMoveResult MoveL(int robot, Frame frame, MotionDesc mdesc);

// Reset the motion of the robot
MotionReturnCode ResetMotion();

// Restart the motion of the robot
MotionReturnCode RestartMotion();

// Set the power state of the robot (controller mut be in remote mode)
PowerReturnCode SetPower(bool power);

// Stop the motion of the robot immediately
MotionReturnCode StopMotion();
```

Every method also exists in an asynchronous version, with the same name followed by `Async` and an optional `CancellationToken`.

**Members of Soap.Data.MotionDesc**
```csharp
public class MotionDesc {
    // Initializes a new instance of the <xref href="UnderAutomation.Staubli.Soap.Data.MotionDesc" data-throw-if-not-resolved="false"></xref> class.
    public MotionDesc()

    // Specifies whether the motion is defined in absolute or relative terms.
    public MoveType AbsRel { get; set; }

    // Maximum allowed joint acceleration, as a percentage of the robot's nominal acceleration.
    public double Acceleration { get; set; }

    // Specifies the type of blending to be applied when transitioning between motion segments.
    public BlendType BlendType { get; set; }

    // Contains additional configuration parameters specific to the robot type, such as anthropomorphic, SCARA, or VRBX configurations.
    public Config Config { get; set; }

    // Maximum allowed joint deceleration, as a percentage of the robot's nominal deceleration.
    public double Deceleration { get; set; }

    // In joint and Cartesian blending modes, the distance between the target point where blending ends and the next point, in millimeters or inches, depending on the length unit of the application.
    public double DistanceBlendNext { get; set; }

    // In the joint and Cartesian blending modes, the distance between the target point where blending begins and the next point, in millimeters or inches, depending on the length unit used in the application.
    public double DistanceBlendPrevious { get; set; }

    public override bool Equals(object obj)

    // Defines the frame in which the tool position is located, including both position and orientation.
    public Frame Frame { get; set; }

    // Frequency of motion in Hz.
    public double Frequency { get; set; }

    public override int GetHashCode()

    // Maximum permitted tool rotation speed, in degrees per second.
    public double RotationVelocity { get; set; }

    public override string ToString()

    // Defines the pose of the robot's tool center point (TCP) in flange, including both position and orientation.
    public Frame Tool { get; set; }

    // Maximum allowed feed rate of the tool center, in mm/s or inches/s depending on the length unit of the application.
    public double TranslationVelocity { get; set; }

    // Maximum allowable joint speed, as a percentage of the robot's nominal speed.
    public double Velocity { get; set; }
}
```

**Members of Soap.Data.IMoveResult**
```csharp
public interface IMoveResult {
    // Identifier of the motion command.
    int Id { get; }

    // Return code indicating the outcome of the motion command.
    MotionReturnCode ReturnCode { get; }
}
```

**Members of Soap.Data.MotionReturnCode**
```csharp
public enum MotionReturnCode {
    // Motion command misuse error.
    MisuseError = 3

    // The robot is not ready to execute motion.
    NotReady = 1

    // Invalid parameter provided to the motion command.
    ParameterError = 2

    // Success, no error occurred.
    Success = 0

    // An unexpected error occurred during motion.
    UnexpectedError = 4
}
```

**Members of Soap.Data.PowerReturnCode**
```csharp
public enum PowerReturnCode {
    // Timeout while disabling power.
    DisableTimeout = 3

    // Timeout while enabling power.
    EnableTimeout = 2

    // Power can only be changed in remote mode.
    OnlyInRemoteMode = 4

    // Cannot change power while the robot is not stopped.
    RobotNotStopped = 1

    // Success, no error occurred.
    Success = 0
}
```

**Members of Soap.Data.BlendType**
```csharp
public enum BlendType {
    // Cartesian-space blending between motion segments.
    BlendCartesian = 2

    // Joint-space blending between motion segments.
    BlendJoint = 1

    // No blending; the robot stops at each target point.
    BlendOff = 0
}
```

**Members of Soap.Data.MoveType**
```csharp
public enum MoveType {
    // Absolute motion in the reference frame.
    Absolute = 0

    // Relative motion from the current position.
    Relative = 1
}
```