Forward & Inverse Kinematics
Perform forward and inverse kinematics calculations offline for FANUC industrial robots and CRX cobots using DH parameters.
Inverse kinematics (IK) and forward kinematics (FK) let you move between joint space and Cartesian space. FK computes the tool p ose from known joint angles, while IK finds joint angles for a desired pose. The kinematics utilities in the Fanuc SDK are off line helpers: you can evaluate poses and joint solutions without connecting to a controller, making them perfect for simulatio n, path validation, and pre-deployment checks.
Why this matters for both industrial arms and cobots
The SDK ships with two analytical solvers and automatically chooses the right one for you:
- OPW industrial arms: Classical 6-axis Fanuc robots with an ortho-parallel base and spherical wrist, based on the paper
An Analytical Solution of the Inverse Kinematics Problem of Industrial Serial Manipulators with an Ortho-parallel Basis and a Spherical Wristby Mathias Brandstötter, Arthur Angerer, and Michael Hofbaur. - CRX collaborative arms: Fanuc CRX cobots that have their own closed-form solver and optional dual solutions, based on paper
Geometric Approach for Inverse Kinematics of the FANUC CRX Collaborative Robotby Manel Abbes and Gérard Poisson.
When you call KinematicsUtils.InverseKinematics(), it inspects DhParameters.KinematicsCategory:
KinematicsCategory.Opw👉 dispatches toOpw.OpwKinematicsUtils.InverseKinematics(industrial robots).KinematicsCategory.Crx👉 dispatches toCrx.CrxKinematicsUtils.InverseKinematics(CRX cobots).
That means you can rely on a single entry point and let the SDK route the request appropriately. 🚦
Typical workflows
Run forward kinematics (FK)
// Load robot geometryvar dh = DhParameters.FromArmKinematicModel(ArmKinematicModels.CRX10iA);// Joint angles in degrees (Fanuc convention)var jointsDeg = new JointsPosition { J1 = 0, J2 = -30, J3 = 45, J4 = 0, J5 = 60, J6 = 90 };// Compute pose: returns XYZ + WPRCartesianPosition pose = KinematicsUtils.ForwardKinematics(jointsDeg, dh);
Solve inverse kinematics (IK) for an OPW robot
// OPW industrial robotvar dh = DhParameters.FromArmKinematicModel(ArmKinematicModels.ARCMate120iD);var target = new CartesianPosition { X = 800, Y = 0, Z = 450, W = 180, P = 0, R = 90 };JointsPosition[] solutions = KinematicsUtils.InverseKinematics(target, dh);// CRX cobot with dual solutionsvar dhCrx = DhParameters.FromArmKinematicModel(ArmKinematicModels.CRX10iAL);var targetCrx = new CartesianPosition { X = 400, Y = 250, Z = 650, W = 0, P = 90, R = 0 };JointsPosition[] crxSolutions = CrxKinematicsUtils.InverseKinematics(targetCrx, dhCrx,includeDuals: true,seedJoints: new double[] { 0, -20, 40, 0, 60, 0 });
Solve inverse kinematics (IK) for a CRX cobot with dual solutions
Build DH parameters from multiple sources
- Built-in catalog:
DhParameters.FromArmKinematicModel(ArmKinematicModels model)gives you ready-to-use geometry for many Fanuc arms and cobots. - ROBOGUIDE library:
DhParameters.FromDefFile(path)parses robot definitions inProgramData/FANUC/ROBOGUIDE/Robot Library. - Controller variables:
DhParameters.FromSymotnFileandDhParameters.FromMrrGrpconvert live$MRR_GRPorsymotn.vad ata to reusable DH structures. - OPW data:
DhParameters.FromOpwParametersmaps OPW parameters (meters) to Fanuc-style DH while keeping the kinematics cat egory consistent.
Practical tips ✨
- Offline safety: Because all solvers are offline, you can iterate quickly without touching a robot controller.
- Pose normalization:
OpwKinematicsUtils.InverseKinematicsnormalizes angles to(-180, 180]to match Fanuc expectations. - Seeding CRX searches: Pass
seedJointstoCrxKinematicsUtils.InverseKinematicsto bias solutions near a known posture. - Matrix helpers:
KinematicsUtils.Mulmultiplies 2D matrices if you need to compose transforms manually.
Happy path-planning! 🛠️
Online Forward and Inverse Kinematics
Forward kinematics using SNPX position registers
Leverage the controller's built-in FK solver by writing joint angles and reading back the Cartesian pose:
// Forward kinematics via SNPX: joints → CartesianJointsPosition jointsPosition = new JointsPosition(10, 12, 50, 20, 12, 16);_robot.Snpx.PositionRegisters.Write(1, jointsPosition);CartesianPosition cartesianPosition = _robot.Snpx.PositionRegisters.Read(1).CartesianPosition;// Inverse kinematics via SNPX: Cartesian → jointsCartesianPosition targetPosition = new CartesianPosition() { X = 100, Y = 100, Z = 100 };targetPosition.Configuration.WristFlip = WristFlip.Flip;targetPosition.Configuration.ArmUpDown = ArmUpDown.Down;targetPosition.Configuration.ArmLeftRight = ArmLeftRight.Left;_robot.Snpx.PositionRegisters.Write(1, targetPosition);JointsPosition resultJoints = _robot.Snpx.PositionRegisters.Read(1).JointsPosition;
Inverse kinematics using SNPX position registers
Let the controller solve IK by writing a Cartesian pose and reading back joint angles. You will only have 1 solution since the cartesian position contains the configuration.
Demonstration
Have a look at : https://fanuc-kinematics.underautomation.com

You can also take a look at the Winforms Desktop project source which implements all these features. I can be downloaded here.

Core types at a glance
Members of Kinematics.KinematicsUtils :public static class KinematicsUtils {// Compute FK for given joint angles (rad) and DH parameterspublic static CartesianPosition ForwardKinematics(double[] jointAnglesRad, DhParameters dhParameters)// Compute FK for given joint angles (deg) and DH parameterspublic static CartesianPosition ForwardKinematics(JointsPosition jointAnglesDeg, DhParameters parameters)// Compute all inverse kinematics solutions for a desired end effector pose.public static JointsPosition[] InverseKinematics(CartesianPosition position, DhParameters parameters)// Multiply two 4x4 homogeneous transformation matrices.public static double[,] Mul(double[,] A, double[,] B)}
public class DhParameters : IDhParameters {// Initializes a new empty instance of <xref href="UnderAutomation.Fanuc.Kinematics.DhParameters" data-throw-if-not-resolved="false"></xref>.public DhParameters()// Initializes a new instance of <xref href="UnderAutomation.Fanuc.Kinematics.DhParameters" data-throw-if-not-resolved="false"></xref> with the specified values.public DhParameters(double d4, double d5, double d6, double a1, double a2, double a3)// Initializes a new instance of <xref href="UnderAutomation.Fanuc.Kinematics.DhParameters" data-throw-if-not-resolved="false"></xref> by copying from an existing <xref href="UnderAutomation.Fanuc.Kinematics.IDhParameters" data-throw-if-not-resolved="false"></xref>.public DhParameters(IDhParameters parameters)// DH parameter A1 (mm).public double A1 { get; set; }// DH parameter A2 (mm).public double A2 { get; set; }// DH parameter A3 (mm).public double A3 { get; set; }// DH parameter D4 (mm).public double D4 { get; set; }// DH parameter D5 (mm).public double D5 { get; set; }// DH parameter D6 (mm).public double D6 { get; set; }public override bool Equals(object obj)// Returns DH parameters from a known Arm Kinematic Model name. Returns null if not found in enum ArmKinematicModels.public static DhParameters FromArmKinematicModel(string modelName)// Returns DH parameters from a known Arm Kinematic Model.public static DhParameters FromArmKinematicModel(ArmKinematicModels model)// Loads DH parameters of each robots described in a ROBOGUIDE definition file (*.def). By default, this file is located in "C:\ProgramData\FANUC\ROBOGUIDE\Robot Library".public static DhParameters[] FromDefFile(string path)// Loads DH parameters of each robots described in a ROBOGUIDE definition file (*.def). By default, this file is located in "C:\ProgramData\FANUC\ROBOGUIDE\Robot Library".public static DhParameters[] FromDefFile(XDocument doc)// Loads DH parameters from parsed variable $MRR_GRP located in symotn.va.public static DhParameters FromMrrGrp(MrrGrpVariableType mrrGrp)// Creates DH parameters from OPW parameters (in meters)// C1 and B are ignored because B is always 0 and C1 is not used in the DH representation.public static DhParameters FromOpwParameters(double a1, double a2, double c2, double c3, double c4)// Loads DH parameters of each group from a parsed symotn.va file.public static DhParameters[] FromSymotnFile(SymotnFile file)public override int GetHashCode()// Gets the kinematics category determined from the DH parameter values.public KinematicsCategory KinematicsCategory { get; }// User-defined tag for associating additional data with this instance.public object Tagpublic override string ToString()}
public enum KinematicsCategory {// CRX collaborative robot kinematics.Crx = 1// Invalid or unsupported kinematics configuration.Invalid = 0// OPW (ortho-parallel wrist) kinematics for standard industrial robots.Opw = 2}
public interface IDhParameters {// DH parameter A1 (mm).double A1 { get; }// DH parameter A2 (mm).double A2 { get; }// DH parameter A3 (mm).double A3 { get; }// DH parameter D4 (mm).double D4 { get; }// DH parameter D5 (mm).double D5 { get; }// DH parameter D6 (mm).double D6 { get; }}
public class JointsPosition {// Default constructorpublic JointsPosition()// Constructor with 6 joint values in degreespublic JointsPosition(double j1Deg, double j2Deg, double j3Deg, double j4Deg, double j5Deg, double j6Deg)// Constructor with 9 joint values in degreespublic JointsPosition(double j1Deg, double j2Deg, double j3Deg, double j4Deg, double j5Deg, double j6Deg, double j7Deg, double j8Deg, double j9Deg)// Constructor from an array of joint values in degreespublic JointsPosition(double[] values)public override bool Equals(object obj)public override int GetHashCode()// Check if joints position is near to expected joints position with a tolerance valuepublic static bool IsNear(JointsPosition j1, JointsPosition j2, double degreesTolerance)// Gets or sets the joint value at the specified indexpublic double this[int i] { get; set; }// Joint 1 in degreespublic double J1 { get; set; }// Joint 2 in degreespublic double J2 { get; set; }// Joint 3 in degreespublic double J3 { get; set; }// Joint 4 in degreespublic double J4 { get; set; }// Joint 5 in degreespublic double J5 { get; set; }// Joint 6 in degreespublic double J6 { get; set; }// Joint 7 in degreespublic double J7 { get; set; }// Joint 8 in degreespublic double J8 { get; set; }// Joint 9 in degreespublic double J9 { get; set; }public override string ToString()// Numeric values for each jointspublic double[] Values { get; }}
public class CartesianPosition : XYZPosition {// Default constructorpublic CartesianPosition()// Constructor with position and rotationspublic CartesianPosition(double x, double y, double z, double w, double p, double r)// Constructor with position, rotations and configurationpublic CartesianPosition(double x, double y, double z, double w, double p, double r, Configuration configuration)// Copy constructorpublic CartesianPosition(CartesianPosition position)// Constructor from an XYZ position with rotationspublic CartesianPosition(XYZPosition position, double w, double p, double r)// Position configurationpublic Configuration Configuration { get; }public override bool Equals(object obj)// Create a CartesianPosition with unknow configuration from a homogeneous rotation and translation 4x4 matrixpublic static CartesianPosition FromHomogeneousMatrix(double[,] R)public override int GetHashCode()// Check if two Cartesian positions are near each other within specified tolerancespublic static bool IsNear(CartesianPosition a, CartesianPosition b, double mmTolerance, double degreesTolerance)// Normalize an angle to the range ]-180, 180]public static double NormalizeAngle(double angle)// Normalize the W, P, R angles to the range ]-180, 180]public static void NormalizeAngles(CartesianPosition pose)// P rotation in degrees (Ry)public double P { get; set; }// R rotation in degrees (Rz)public double R { get; set; }// Convert position to a homogeneous rotation and translation 4x4 matrixpublic double[,] ToHomogeneousMatrix()public override string ToString()// W rotation in degrees (Rx)public double W { get; set; }}