Get the robot position
Read the current Cartesian position (robtarget) and the joint position (jointtarget) of an ABB robot, and convert between them.
To get where an ABB robot is, call robot.Rws.MotionSystem.GetRobTarget("ROB_1") for the Cartesian position of the tool, and GetJointTarget("ROB_1") for the value of each axis. Both are read operations, they need no mastership. Positions are in millimetres, joints in degrees.
robtarget or jointtarget
The two describe the same robot at the same moment, from two points of view.
RobTarget | JointTarget | |
|---|---|---|
| Describes | where the tool is | where each axis is |
| Unit | millimetres | degrees, millimetres for a linear axis |
| Content | X, Y, Z, Orientation, Configuration, ExternalAxes | RobotAxes (axis 1 to 6), ExternalAxes |
| Depends on | the tool, the work object and the frame | nothing else |
| Ambiguous | no, but several joint sets reach it | no |
Orientation is a Quaternion, the four values RAPID writes as rot. Configuration is the RobotConfiguration, the quarter revolution each deciding axis sits in, which is what tells apart the joint combinations that reach the same pose. An external axis the system does not define comes back as ExternalJoints.NotInUse.
Read the position
ROB_1 is the usual name of the robot arm. Get the exact name of the mechanical units of your system with robot.Rws.MotionSystem.GetMechanicalUnits().
// Where the tool is, in millimetres, with the axis configuration and the external axesRobTarget target = robot.Rws.MotionSystem.GetRobTarget("ROB_1");Console.WriteLine($"X={target.X} Y={target.Y} Z={target.Z}");Console.WriteLine($"orientation {target.Orientation}");Console.WriteLine($"configuration {target.Configuration}");Console.WriteLine($"external axes {target.ExternalAxes}");// The same reading in another frame, with a given tool and work objectRobTarget inWorld = robot.Rws.MotionSystem.GetRobTarget("ROB_1", CoordinateSystem.World,tool: "tGripper", workObject: "wobj0");Console.WriteLine(inWorld);// The joint values, robot axes in degreesJointTarget joints = robot.Rws.MotionSystem.GetJointTarget("ROB_1");Console.WriteLine($"axis 1 = {joints.RobotAxes.Axis1} deg");Console.WriteLine($"axis 2 = {joints.RobotAxes.Axis2} deg");// An external axis the system does not define comes back as ExternalJoints.NotInUseif (joints.ExternalAxes.AxisA != ExternalJoints.NotInUse){Console.WriteLine($"external axis A = {joints.ExternalAxes.AxisA}");}// alwaysRead asks the controller to measure again instead of answering with the value it holdsJointTarget measured = robot.Rws.MotionSystem.GetJointTarget("ROB_1", alwaysRead: true);Console.WriteLine(measured);// Cartesian position without the external axes. ExternalAxes is null here.RobTarget cartesian = robot.Rws.MotionSystem.GetCartesianPosition("ROB_1");Console.WriteLine(cartesian);// The raw values of the measurement system of the unitRobotJoints physical = robot.Rws.MotionSystem.GetPhysicalJoints("ROB_1");Console.WriteLine(physical);// The same two positions seen from a RAPID task instead of a mechanical unitRobTarget taskTarget = robot.Rws.Rapid.GetRobTarget("T_ROB1");JointTarget taskJoints = robot.Rws.Rapid.GetJointTarget("T_ROB1");// Which external joints of the task carry a real valueRapidExternalJointStates states = robot.Rws.Rapid.GetExternalJointStates("T_ROB1");Console.WriteLine($"external joint 1 : {states.Joint1}");// The units the task can moveforeach (RapidMechanicalUnitItem unit in robot.Rws.Rapid.GetMechanicalUnits("T_ROB1")){Console.WriteLine($"{unit.Name} : {unit.Type}, {unit.Mode}");}
Four things in that example are worth separating:
GetRobTargetgives the position, the orientation, the axis configuration and the external axes.GetCartesianPositiongives the same position without the external axes.GetJointTargetgives the joints. WithalwaysRead: truethe controller measures again instead of answering with the value it holds, which matters when the robot is moved by hand.GetPhysicalJointsgives the raw values of the measurement system of the unit, before the calibration offsets.- The same two positions can be read from a RAPID task instead of a mechanical unit, with
robot.Rws.Rapid.GetRobTarget("T_ROB1")andGetJointTarget("T_ROB1").
Motion system or RAPID task
Both readings exist because the two services answer different questions.
robot.Rws.MotionSystemreads a mechanical unit. Use it when you think in terms of hardware: this arm, this positioner, this external axis. You choose the frame, the tool and the work object in the call.robot.Rws.Rapidreads a task. Use it when you think in terms of the program: the task uses the tool and the work object that are active in it right now, so the answer matches what aMoveLof that task would produce.
On a single robot system with one motion task the two give the same numbers. On a MultiMove system, or when the tool active in the task is not the one you want to measure from, they do not.
Frames, tool and work object
By default the position is expressed in the base frame of the unit, with its active tool and work object. CoordinateSystem selects another frame: World, Base, Tool or WorkObject. The tool and workObject arguments name the ones to measure with, without changing what the robot uses.
Reading the same point with two different tools gives two different RobTarget. This is the usual cause of an unexplained offset between a value read by the SDK and the same value shown on the teach pendant.
Convert a jointtarget into a robtarget
The controller does the kinematics for you. Forward kinematics turns joint values into a pose, inverse kinematics turns a pose into joint values. The inverse case has several answers, so you also pass the previous joint values and the axis configuration to pick one.
// These four calculations work in metres and radians, not in millimetres and degrees.// Tool relative to the mounting flange. Here the flange itself, with no rotation.Pose toolFrame = new Pose(0, 0, 0, new Quaternion(1, 0, 0, 0));// Forward kinematics: where the tool would be for these joint valuesJointTarget joints = new JointTarget(new RobotJoints(0, 0, 0, 0, 0.5, 0), new ExternalJoints());RobTarget pose = robot.Rws.MotionSystem.GetPoseFromJoints("ROB_1", toolFrame, joints);Console.WriteLine($"tool at {pose.X} {pose.Y} {pose.Z} metres, configuration {pose.Configuration}");// Inverse kinematics: which joint values put the tool at that pose.// previousJoints decides between the solutions the pose admits.JointTarget solution = robot.Rws.MotionSystem.GetJointsFromPose("ROB_1", pose, new ExternalJoints(),toolFrame, joints, pose.Configuration);Console.WriteLine($"axis 5 = {solution.RobotAxes.Axis5} rad");// The controller has a second calculation for the same question. It does not always// pick the same solution.JointTarget other = robot.Rws.MotionSystem.GetJointsFromCartesian("ROB_1", pose, new ExternalJoints(),toolFrame, joints, pose.Configuration);Console.WriteLine(other);// Every way of reaching the pose. A six axis robot usually has eight.JointSolution[] solutions = robot.Rws.MotionSystem.GetAllJointSolutions("ROB_1", pose, new ExternalJoints(),toolFrame, pose.Configuration);foreach (JointSolution s in solutions){Console.WriteLine($"{s.Configuration} : {s.RobotAxes}");}
These calculations are pure computation, they do not move the robot and they work on positions the robot is not at.
Millimetres, metres, degrees and radians
The readings answer in millimetres and degrees. The four kinematics calculations work in metres and radians, in both directions. Nothing in the answer says which one it is, so convert explicitly when you feed one into the other.
// The readings answer in millimetres and degreesRobTarget reading = robot.Rws.MotionSystem.GetRobTarget("ROB_1");JointTarget joints = robot.Rws.MotionSystem.GetJointTarget("ROB_1");Console.WriteLine($"{reading.X} mm, axis 5 = {joints.RobotAxes.Axis5} deg");// The four kinematics calculations work in metres and radians, in both directions.// Convert the joints before sending them.JointTarget inRadians = new JointTarget(ToRadians(joints.RobotAxes), joints.ExternalAxes);Pose toolFrame = new Pose(0, 0, 0, new Quaternion(1, 0, 0, 0));RobTarget computed = robot.Rws.MotionSystem.GetPoseFromJoints("ROB_1", toolFrame, inRadians);// And convert the answer back to millimetres to compare it with the readingConsole.WriteLine($"{computed.X * 1000} mm computed, {reading.X} mm read");static RobotJoints ToRadians(RobotJoints degrees){double f = Math.PI / 180.0;return new RobotJoints(degrees.Axis1 * f, degrees.Axis2 * f, degrees.Axis3 * f,degrees.Axis4 * f, degrees.Axis5 * f, degrees.Axis6 * f);}static RobotJoints ToDegrees(RobotJoints radians){double f = 180.0 / Math.PI;return new RobotJoints(radians.Axis1 * f, radians.Axis2 * f, radians.Axis3 * f,radians.Axis4 * f, radians.Axis5 * f, radians.Axis6 * f);}
Going further
- Motion system, position & kinematics, the complete reference
- Read & write RAPID variables, to read a taught
robtargetfrom the program - RAPID tasks & program execution
// Gets the joint values a mechanical unit currently stands at (synchronous) The robot axes are expressed in degrees.JointTarget GetJointTarget(string mechanicalUnit, bool alwaysRead = false);// Gets where the tool of a mechanical unit currently is (synchronous) The position is expressed in millimetres.RobTarget GetRobTarget(string mechanicalUnit, CoordinateSystem coordinateSystem = CoordinateSystem.Base, string tool = null, string workObject = null);// Sends the robot to a cartesian target (synchronous) The position is expressed in millimetres, in the coordinate system currently active for the mechanical unit selected for jogging.void SetPositionTarget(RobTarget target);
Every method also exists in an asynchronous version, with the same name followed by Async and an optional CancellationToken.
public class RobTarget : Pose {// Initializes a new target at the origin, with no rotationpublic RobTarget()// Initializes a new targetpublic RobTarget(double x, double y, double z, Quaternion orientation, RobotConfiguration configuration, ExternalJoints externalAxes)// Axis configuration used to reach the pose. Never null.public RobotConfiguration Configuration { get; set; }// Values of the six external axes, null when the reading does not report thempublic ExternalJoints ExternalAxes { get; set; }// Returns a string representation of this targetpublic override string ToString()}
public class JointTarget {// Initializes a new joint target with every axis at zeropublic JointTarget()// Initializes a new joint targetpublic JointTarget(RobotJoints robotAxes, ExternalJoints externalAxes)// Values of the six external axes. Never null.public ExternalJoints ExternalAxes { get; set; }// Values of the six axes of the robot arm. Never null.public RobotJoints RobotAxes { get; set; }// Returns a string representation of this joint targetpublic override string ToString()}
public class RobotConfiguration {// Initializes a new configuration with every quarter revolution set to zeropublic RobotConfiguration()// Initializes a new configurationpublic RobotConfiguration(int quarter1, int quarter4, int quarter6, int quarterX)// Quarter revolution axis 1 sits inpublic int Quarter1 { get; set; }// Quarter revolution axis 4 sits inpublic int Quarter4 { get; set; }// Quarter revolution axis 6 sits inpublic int Quarter6 { get; set; }// Index of the arm configuration, which tells the remaining joint combinations apartpublic int QuarterX { get; set; }// Returns a string representation of this configurationpublic override string ToString()}
public enum CoordinateSystem {// The base frame of the mechanical unitBase = 2// The frame of the active toolTool = 3// The controller reported a frame this library does not knowUnknown = 0// The frame of the active work objectWorkObject = 4// The world frame, shared by every mechanical unit of the systemWorld = 1}