matlab/matlab-model-robot-kinematics
> Use whenever a MATLAB robot model (rigidBodyTree) is needed — whether for simulation, visualization, IK, motion planning, pick-and-place, or trajectory UR10, UR10e, UR16e, UR20, KUKA iiwa, Fanuc, ABB, Panda, Kinova, Sawyer, Baxter), robot modeling verbs (load, create, build, import, simulate, model), tasks implying a robot model (pick, place, lift, reach, move, grasp, plan motion for, animate), kinematics keywords (IK, FK, inverse kinematics, forward kinematics, joint configuration, end-effector pose, gripper), or working with rigidBodyTree, loadrobot, importrobot, URDF, DH parameters, addVisual, addCollision.
npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-model-robot-kinematics
Build manipulator models correctly and validate every kinematic solution.
inverseKinematics vs generalizedInverseKinematicscontopptraj, cubicpolytraj, trapveltraj) — use a trajectory skillreferences/parallel-robot-guidance.mdinverseDynamics, forwardDynamics) — separate domainChoose the highest-fidelity source available:
| Priority | Source | When to Use | Why |
|----------|--------|-------------|-----|
| 1 | loadrobot | Robot exists in the library | Includes collision meshes, inertias, visuals |
| 2 | importrobot | User has URDF/Xacro/SDF file | Preserves mesh references |
| 3 | Build from DH | Only DH parameters available | No collision meshes unless added manually |
Critical: A robot without collision meshes will cause downstream motion planning to silently report "collision-free" paths that actually collide. Always prefer sources that include meshes.
If the user says "build a robot" but names a known robot (UR5e, KUKA iiwa, Panda, etc.), suggest loadrobot first.
After importing from URDF, Simscape, or CAD: Check if collision meshes are present. Simscape Multibody models and many URDFs/CAD exports only provide visual geometry — no collision meshes. If collision meshes are missing, read references/import-mesh-handling.md for the decision tree on generating them from visuals.
Always set DataFormat="row" — this is required for planning and dynamics workflows.
Option A — loadrobot:
robot = loadrobot("universalUR5e", DataFormat="row");
Option B — importrobot (URDF/Xacro/SDF):
robot = importrobot("myRobot.urdf", DataFormat="row");
If the URDF has only visual geometry (no <collision> tags), add VHACD collision decomposition at import time:
opts = vhacdOptions("RigidBodyTree");
opts.SourceMesh = "VisualGeometry";
robot = importrobot("myRobot.urdf", DataFormat="row", ...
MeshPath="path/to/meshes", CollisionDecomposition=opts);
Option C — Build from DH parameters:
robot = rigidBodyTree(DataFormat="row");
body = rigidBody("link1");
jnt = rigidBodyJoint("joint1", "revolute");
setFixedTransform(jnt, [a alpha d 0], "dh");
jnt.PositionLimits = [-pi, pi];
body.Joint = jnt;
addVisual(body, "Cylinder", [0.04, 0.3]);
addCollision(body, "Cylinder", [0.05, 0.3]);
addBody(robot, body, "base");
tool0 Exists with Z = OutwardBefore attaching a gripper, the robot MUST have a tool0 body whose local Z-axis points along the arm's outward direction (away from the wrist). Most loadrobot models satisfy this already — but KUKA iiwa models do not.
Check references/ee-frame-alignment.md for the full compatibility table and diagnostic procedure.
If the robot already has tool0 with Z = outward (all UR, ABB, Techman, FANUC, Kinova, Yaskawa): proceed directly to gripper attachment.
If the robot lacks tool0 or EE Z is misaligned (KUKA iiwa 7/14): add a corrective tool0 frame:
% KUKA iiwa: iiwa_link_ee Z is perpendicular to arm — fix with Ry(90°)
tool0 = rigidBody("tool0");
tool0Joint = rigidBodyJoint("tool0_joint", "fixed");
R_correction = [0 0 1; 0 1 0; -1 0 0]; % Ry(90°)
setFixedTransform(tool0Joint, rotm2tform(R_correction));
tool0.Joint = tool0Joint;
addBody(robot, tool0, "iiwa_link_ee");
For unknown robots (URDF imports): Run the diagnostic check from references/ee-frame-alignment.md to verify alignment before attaching anything.
Visuals vs collisions — both are needed, for different purposes:
| Function | Purpose | Used By |
|----------|---------|---------|
| addVisual(body, shape, dims) | Display appearance | show for visualization |
| addCollision(body, shape, dims) | Planning geometry | checkCollision, motion planners |
To color a visual: addVisual(body, "Mesh", stlFile, tform, FaceColor=[1 0.8 0])
Robots from loadrobot already have both. When building from scratch, add both explicitly.
Attach a gripper (MANDATORY for manipulation tasks):
If the task involves object interaction (picking, placing, lifting, grasping, manipulating), a gripper is required — do not skip this step. Only omit for pure visualization or reachability studies with no object contact.
Before building ANY custom gripper, you MUST read references/gripper-models.md. Available models include both parallel-jaw (robotiq2F85, 85mm opening) AND vacuum grippers (robotiqEPick variants for large/flat objects). Do NOT assume robotiq2F85 is the only option.
references/gripper-models.mdgripper = loadrobot("robotiq2F85", DataFormat="row");
addSubtree(robot, "tool0", gripper, ReplaceBase=false);
Critical: Always use ReplaceBase=false. The default (true) merges the gripper's base link into the parent body, silently discarding the base visual mesh (the gripper housing/coupling). All provided grippers have a base visual that is lost without this flag.
After attaching a gripper, add a contact frame at the gripper's contact point (fixed joint, zero DOF added), then solve IK to that frame. This avoids confusing tip frame orientations and manual offset math. See references/gripper-models.md for per-gripper offsets and the full pattern.
contactFrame = rigidBody("contact_point");
contactJoint = rigidBodyJoint("contact_joint", "fixed");
setFixedTransform(contactJoint, trvec2tform([0, 0, gripperOffset]));
contactFrame.Joint = contactJoint;
addBody(robot, contactFrame, "tool0");
Add a custom end-effector frame (when no gripper is attached or you need a specific offset):
ee = rigidBody("tool_tip");
eeJoint = rigidBodyJoint("tool_tip_joint", "fixed");
setFixedTransform(eeJoint, trvec2tform([0.1, 0, 0]));
ee.Joint = eeJoint;
addBody(robot, ee, "tool0");
Use FK and IK to confirm the model is built correctly — correct link lengths, joint axes, limits, and end-effector frame placement.
Verify with FK: Move to a known configuration and check the end-effector reaches the expected position.
q = homeConfiguration(robot);
tform = getTransform(robot, q, "tool0");
fprintf("Home position: [%.3f, %.3f, %.3f] m\n", tform(1:3,4));
Visually confirm the pose makes sense:
figure;
show(robot, q, Frames="off");
axis auto;
title("Home configuration");
Verify with IK: Pick a target you know is reachable and confirm the solver converges.
Decision — IK vs GIK:
| Use | When |
|-----|------|
| inverseKinematics | Single target pose, no extra constraints |
| generalizedInverseKinematics | Multiple constraints (position + aiming, joint bounds, orientation, etc.) |
See references/gik-constraints.md for all available GIK constraint types.
ik = inverseKinematics(RigidBodyTree=robot);
weights = [0.25 0.25 0.25 1 1 1];
targetPose = trvec2tform([0.4, 0.1, 0.3]) * eul2tform([0 pi 0], "ZYX");
[qSol, solnInfo] = ik("tool0", targetPose, weights, homeConfiguration(robot));
Never skip this step. Agents consistently skip validation and deliver solutions that silently failed.
After inverseKinematics:
if solnInfo.ExitFlag <= 0
warning("IK did not converge. ExitFlag: %d, Status: %s", ...
solnInfo.ExitFlag, solnInfo.Status);
end
tformActual = getTransform(robot, qSol, "tool0");
posError = norm(tformActual(1:3,4)' - targetPose(1:3,4)');
if posError > 1e-3
warning("IK position error %.4f m exceeds threshold.", posError);
end
After generalizedInverseKinematics:
if solInfo.ExitFlag <= 0
warning("GIK did not converge. ExitFlag: %d, Status: %s", ...
solInfo.ExitFlag, solInfo.Status);
end
for i = 1:numel(solInfo.ConstraintViolations)
cv = solInfo.ConstraintViolations(i);
if cv.Violation > 1e-3
warning("Constraint %d (%s) violated: %.4f", i, cv.Type, cv.Violation);
end
end
For sequential waypoints — check joint continuity:
for i = 2:size(qAll, 1)
maxJump = max(abs(qAll(i,:) - qAll(i-1,:)));
if maxJump > deg2rad(30)
warning("Joint jump of %.1f deg between waypoints %d and %d.", ...
rad2deg(maxJump), i-1, i);
end
end
| Function | Purpose | Toolbox | Available From |
|----------|---------|---------|----------------|
| loadrobot | Load built-in robot with meshes | Robotics System Toolbox | R2019b |
| importrobot | Import from URDF/Xacro/SDF/Simscape | Robotics System Toolbox | R2017a |
| rigidBodyTree | Create empty robot model | Robotics System Toolbox | R2016b |
| setFixedTransform | Set joint transform (DH or homogeneous) | Robotics System Toolbox | R2016b |
| addVisual | Add visual geometry for display | Robotics System Toolbox | R2019a |
| addCollision | Add collision geometry for planning | Robotics System Toolbox | R2019b |
| addSubtree | Attach subtree (gripper) to body | Robotics System Toolbox | R2016b |
| getTransform | Compute FK for a configuration | Robotics System Toolbox | R2016b |
| inverseKinematics | Solve IK for single target pose | Robotics System Toolbox | R2016b |
| generalizedInverseKinematics | Solve IK with multiple constraints | Robotics System Toolbox | R2017a |
| show | Visualize robot configuration | Robotics System Toolbox | R2016b |
figure;
show(robot, q, Frames="off");
axis auto;
view(45, 30);
title("Robot at configuration q");
For multiple configurations, use tiledlayout/nexttile:
figure;
tiledlayout(1, 3);
for i = 1:3
nexttile;
show(robot, qAll(i,:), Frames="off", PreservePlot=false);
axis auto;
title(sprintf("Waypoint %d", i));
end
When solving IK for sequential waypoints, use the previous solution as the initial guess:
qPrev = homeConfiguration(robot);
for i = 1:numWaypoints
[qSol, solnInfo] = ik("tool0", targetPoses(:,:,i), weights, qPrev);
% ... validate ...
qPrev = qSol;
end
DataFormat="row" on creation or importdeg2rad when specifying in degrees)Frames="off" and axis auto for clean visualizationtiledlayout/nexttile instead of subplot"tool_tip", "bucket_tip")inverseKinematics: [orientation(3) position(3)] — set position weights higher for position-priority tasks| Mistake | Why It's Wrong | Correct Approach |
|---------|---------------|-----------------|
| Building from DH when loadrobot has the robot | No collision meshes → planning silently fails | Check loadrobot library first |
| Using addCollision for visual appearance | Collision geometry is simplified, not rendered by show | Use addVisual for display, addCollision for planning |
| Skipping IK validation | Solution may not converge; ExitFlag <= 0 means failure | Always check ExitFlag and verify position error via FK |
| Using DataFormat="column" or "struct" | Incompatible with planning/trajectory functions that expect row vectors | Always use DataFormat="row" |
| Skipping gripper for manipulation tasks | Object interaction (pick, lift, grasp) requires a gripper on the model | Always attach a gripper when the task involves object contact |
| Solving IK to "tool0" or tip body without a contact frame | "tool0" is the flange (offset from contact point); tip body frames have confusing 90° rotations | Add a fixed "contact_point" frame at the gripper offset, solve IK to that |
| Building a custom gripper from scratch | Provided models have accurate geometry and are ready to attach | Check references/gripper-models.md for available grippers |
| Using robotiq2F85 for objects > 85 mm | Jaw cannot open wide enough to grasp the object | Compare object dimensions to gripper max opening; use vacuum for large objects |
| Using IK when multiple constraints are needed | inverseKinematics only handles a single pose target | Use generalizedInverseKinematics with constraint objects |
| Not checking joint continuity for waypoint sequences | Large jumps between solutions cause unsafe trajectories | Compare consecutive solutions, flag jumps > 30 deg |
| Using addSubtree without ReplaceBase=false | Gripper base visual mesh is silently discarded; housing disappears from visualization | Always pass ReplaceBase=false when attaching grippers |
| Attaching gripper to KUKA iiwa without frame correction | iiwa_link_ee Z is perpendicular to arm; gripper points sideways | Add tool0 with Ry(90°) correction before attaching — see references/ee-frame-alignment.md |
| Assuming all loadrobot models have tool0 with Z = outward | Some robots (KUKA iiwa) lack tool0 or have misaligned EE frames | Check references/ee-frame-alignment.md table or run diagnostic before gripper attachment |
| Using rigidBodyTree for closed-loop parallel robots | Cannot represent closed kinematic chains | See references/parallel-robot-guidance.md |
----
Copyright 2026 The MathWorks, Inc.
----
Take matlab/matlab-model-robot-kinematics from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.