VLA · Prerequisite · Robotics Fundamentals
M5.2 — Joint Space and Cartesian Space
Describing a robot’s pose by its joint angles or by the position of its hand.
Motors work in joint angles, tasks are stated in terms of where the hand should be, and robot learning has to move between the two. This lesson uses a two-link arm to show the relationship, including the surprising fact that it is not one-to-one.
Learning objectives
After this lesson, you should be able to:
- Explain joint space, degrees of freedom and joint limits.
- Explain Cartesian space and what a pose contains.
- Compute the hand position of a planar two-link arm from its joint angles.
- Explain why several joint configurations can give one hand position.
- Compute whether a target is inside a two-link arm’s workspace.
- Compare joint-space and Cartesian-space actions.
- Joint space: angles of each joint
- Forward mapping: one hand pose for each configuration
- Cartesian space: position and orientation of the hand
- Reverse mapping: none, one or many joint configurations
- Joint limits and workspace bound what is possible
The one-sentence idea
The same arm pose can be written as joint angles or as the hand’s position and orientation, and the mapping from joints to hand is unique while the mapping back is not.
Joints in, hand out is easy. Hand in, joints out is the hard part.
Two ways to describe where a robot is
A robot arm’s pose can be described in two different ways, and robot learning moves between them all the time:
- Joint space
- A list of the joint angles (or extensions, for sliding joints):
q = [q₁, q₂, …, qₙ]. This is what the motors are told. - Cartesian space
- The position and orientation of a chosen point on the robot, usually the hand or gripper, in the world:
x, y, zand a rotation. This is what the task cares about.
“Move the cup to the shelf” is a Cartesian statement. The robot has to execute it by turning joints. The first space is also called the configuration space, and the second the task space or operational space. This lesson explains how the two relate. The calculations that convert between them are the subject of the next lesson, M5.3.
Joint space and degrees of freedom
The number of independent joint values needed to specify the arm’s configuration is its number of degrees of freedom (DOF). A common research arm has 7 joints, so its joint space is 7-dimensional: every configuration is a point q ∈ ℝ⁷. Joints are limited, so only part of that space is allowed. A typical elbow joint cannot rotate through a full circle, and each joint has a limit on its range. Joint angles are almost always stored in radians, not degrees, so mixing the two is a classic mistake:
import numpy as np q = np.deg2rad([30.0, -45.0, 100.0]) # 30, -45 and 100 degrees as radians print(q.round(3)) lower = np.array([-1.0, -1.0, -1.5]) # joint limits in radians upper = np.array([ 1.0, 1.0, 1.5]) print(np.clip(q, lower, upper).round(3))
Output:
[ 0.524 -0.785 1.745] [ 0.524 -0.785 1.5 ]
The third joint was commanded to 100° (1.745 rad), beyond its limit of 1.5 rad, so it is stopped at the limit. A policy that outputs joint targets has to respect these limits, as M5.1 described for actions.
Cartesian space and pose
In Cartesian space a hand has a pose: a position (three numbers, x, y and z) and an orientation (three more degrees of freedom, expressed as roll-pitch-yaw angles, a rotation matrix or a quaternion). That makes six numbers for a full 3-D pose. A flat, two-dimensional task, such as a planar arm moving on a table, has a position (x, y) and a single angle.
To keep things drawable, the rest of this lesson uses a planar two-link arm: two rigid links of lengths l₁ and l₂ joined by two rotating joints, all moving in a plane. Joint 1 is at the origin and measures the first link’s angle from the x-axis. Joint 2 measures the second link’s angle relative to the first. From geometry (M5.3 derives it), the hand position is:
def hand(q, l1=1.0, l2=1.0):
q1, q2 = q
return np.array([l1 * np.cos(q1) + l2 * np.cos(q1 + q2),
l1 * np.sin(q1) + l2 * np.sin(q1 + q2)])
print(hand([0.0, 0.0]).round(3)) # both links along the x-axis: fully stretched
print(hand([np.pi / 2, 0.0]).round(3)) # stretched straight up
print(hand([0.0, np.pi / 2]).round(3)) # first link along x, second turned upOutput:
[2. 0.] [0. 2.] [1. 1.]
With both joints at zero the arm lies stretched along the x-axis and the hand is at (2, 0). Rotating joint 1 by 90° swings the whole arm upward. Bending joint 2 by 90° leaves the first link on the x-axis and turns the second one up, so the hand is at (1, 1). Each joint-space point has exactly one Cartesian result.
The mapping is not one-to-one
Going from joints to Cartesian space always gives one answer. The reverse does not. Several different joint configurations can put the hand at the same place. The classic case for a two-link arm is “elbow up” versus “elbow down”:
elbow_a = [0.0, np.pi / 2] # first link along x, second link up elbow_b = [np.pi / 2, -np.pi / 2] # first link up, second link along x print(hand(elbow_a).round(3), hand(elbow_b).round(3)) mid = (np.array(elbow_a) + np.array(elbow_b)) / 2 # halfway in joint space print(hand(mid).round(3))
Output:
[1. 1.] [1. 1.] [1.414 1.414]
Both configurations put the hand at (1, 1), yet they are very different poses of the arm. Moving in a straight line in joint space from one to the other, passing through the halfway configuration, drives the hand out to (1.414, 1.414) and back, an arc, even though it starts and ends at the same Cartesian point. Straight in one space is generally curved in the other.
This has two practical consequences. First, a Cartesian target does not tell you which joint configuration to use, so choosing among the options is part of the problem (M5.3). Second, a robot with more joints than the task needs, such as a 7-joint arm doing a 6-D pose task, has a whole family of configurations for each pose. That extra freedom is called redundancy, and it lets the arm avoid obstacles or joint limits while keeping the hand where it is:
joints, task_dims = 7, 6 print(joints - task_dims) # extra degrees of freedom of a 7-joint arm doing a 6-D pose task print(2 - 2) # the planar two-link arm reaching a point (x, y): none left over
Output:
1 0
Workspace: where the hand can reach
The set of Cartesian positions the hand can reach is the workspace. For the planar two-link arm it is a ring: the farthest the hand can be is l₁ + l₂ (arm stretched) and the closest is |l₁ − l₂| (arm folded back). Targets outside the ring have no joint solution at all:
def reachable(target, l1, l2):
r = np.hypot(*target)
return abs(l1 - l2) <= r <= l1 + l2
print(reachable((1.5, 0.5), 1.0, 1.0)) # distance 1.58, inside the ring
print(reachable((2.5, 0.0), 1.0, 1.0)) # distance 2.5, beyond the reach of 2
print(reachable((0.2, 0.0), 1.0, 0.5)) # too close in: the inner radius is 0.5Output:
True False False
Joint limits shrink the workspace further, and obstacles carve holes in it. For a VLA this matters because a language command such as “pick up the cup on the far shelf” can describe a place the robot cannot reach, and a well-behaved system has to notice that rather than command an impossible motion.
Which space should a policy use?
Both spaces are used for actions in robot learning, with different trade-offs:
| Joint-space actions | Cartesian-space actions | |
|---|---|---|
| Matches the task | Indirectly: the hand’s motion is a nonlinear result of the joints | Directly: “move the hand 2 cm forward” |
| Transfers between robots | Poorly: each robot has different joints | Better: a hand pose means the same on any arm |
| Needs kinematics | No: values go straight to the motors | Yes: inverse kinematics must turn poses into joints |
| Redundancy and limits | The policy sees and controls them | Handled, or hidden, by the solver below |
Neither is always better, and the roadmap’s action-representation phase (Phase 4) compares them directly for VLA models. What you need to carry forward is the distinction itself, and that converting between the two spaces is a computation with its own limits: forward conversion is unique, inverse conversion can have many answers or none.
Common misconceptions
Misconception 1 “Each hand position has exactly one joint configuration.”
Correction A two-link arm already has two (elbow up and elbow down) for most reachable points, and arms with more joints have infinitely many.
Misconception 2 “A straight line in joint space is a straight line of the hand.”
Correction Joint interpolation generally moves the hand along a curve. Straight-line hand motion has to be planned in Cartesian space.
Misconception 3 “If the target is in the workspace, the robot can always reach it.”
Correction Joint limits, obstacles, singularities and the arm’s own links can still block a particular configuration.
Misconception 4 “Degrees and radians are interchangeable in code.”
Correction Libraries almost always use radians. Passing degrees silently gives wrong motions.
Engineering takeaways
- Joint space is the list of joint values and Cartesian space is the pose of the hand.
- Degrees of freedom count the independent joint values.
- A full 3-D pose has six numbers: three for position and three for orientation.
- Forward mapping from joints to hand is unique.
- The reverse can have no solution, one, several or infinitely many.
- Redundancy is joints minus task dimensions.
- The workspace is the set of reachable hand positions, and joint limits shrink it.
- Joint-space and Cartesian actions each have trade-offs, and the choice shapes a policy.
Motors speak joints, tasks speak space, and robotics is mostly about translating between them.
Knowledge check
Try each question first, then reveal the explanation.
Question 1
A planar two-link arm has links of 0.6 m and 0.4 m. What are the largest and smallest distances from the base that the hand can reach?
Explanation
1.0 m and 0.2 m. The farthest is 0.6 + 0.4 = 1.0 when stretched, and the closest is |0.6 − 0.4| = 0.2 when folded back.
Question 2
A 7-joint arm is asked only to place its hand at a position (3 numbers), ignoring orientation. How many redundant degrees of freedom does it have?
Explanation
4. The task has 3 dimensions, and 7 − 3 = 4, so there is a four-dimensional family of joint configurations for each hand position.
Question 3
Why can two arms with different joints still follow the same Cartesian action, such as “move the hand 2 cm to the left”?
Explanation
Because a Cartesian action describes the hand, not the joints. Each arm’s own inverse kinematics converts it to its own joint motions, which is why Cartesian actions transfer between robots better than joint actions.
What’s next
You can now describe a pose in either space. Next is the mathematics that converts between them: forward and inverse kinematics.