<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vinayaka Raju</title>
    <description>The latest articles on DEV Community by Vinayaka Raju (@vinayaka_raju_46).</description>
    <link>https://dev.to/vinayaka_raju_46</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4132013%2F3baed2c3-1a3c-450e-a550-412dc69ba15e.jpeg</url>
      <title>DEV Community: Vinayaka Raju</title>
      <link>https://dev.to/vinayaka_raju_46</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vinayaka_raju_46"/>
    <language>en</language>
    <item>
      <title>Calculus Is Fun When You Can Make a Robot Move: Understanding Differential Kinematics</title>
      <dc:creator>Vinayaka Raju</dc:creator>
      <pubDate>Sat, 19 Sep 2026 08:05:41 +0000</pubDate>
      <link>https://dev.to/vinayaka_raju_46/calculus-is-fun-when-you-can-make-a-robot-move-understanding-differential-kinematics-2ip9</link>
      <guid>https://dev.to/vinayaka_raju_46/calculus-is-fun-when-you-can-make-a-robot-move-understanding-differential-kinematics-2ip9</guid>
      <description>&lt;p&gt;let's start with two fundamental ways of describing a robot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Joint space&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Task space&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Joint Space
&lt;/h2&gt;

&lt;p&gt;When a robot's current configuration is represented in terms of its &lt;strong&gt;joint positions&lt;/strong&gt;, we call it &lt;strong&gt;joint space&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Consider a simple 2-link planar robot.&lt;/p&gt;

&lt;p&gt;Suppose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q₁ = 5°
q₂ = 45°
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then its joint configuration can be represented as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q = [5°, 45°]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;Now consider something larger, like a &lt;strong&gt;UR10&lt;/strong&gt;, which has six degrees of freedom.&lt;/p&gt;

&lt;p&gt;Its joint configuration can be represented as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q = [q₁, q₂, q₃, q₄, q₅, q₆]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each value tells us the position of one joint.&lt;/p&gt;

&lt;p&gt;So, in joint space, we describe the robot by asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What are the current positions of my joints?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Task Space
&lt;/h2&gt;

&lt;p&gt;There is another way to describe the same robot.&lt;/p&gt;

&lt;p&gt;Instead of caring about every individual joint, suppose I only care about the robot's &lt;strong&gt;end effector&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Where is it?&lt;/p&gt;

&lt;p&gt;How is it oriented?&lt;/p&gt;

&lt;p&gt;For a robot operating in 3D space, we can represent the end-effector pose as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X = [x, y, z, ϕ, θ, ψ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x, y, z   → Position

ϕ, θ, ψ   → Orientation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called &lt;strong&gt;task space&lt;/strong&gt; or &lt;strong&gt;Cartesian space&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So now we have two different ways of looking at the same robot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JOINT SPACE                         TASK SPACE

[q₁, q₂, ... qₙ]            [x, y, z, orientation]

   Robot joints                  End effector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction is extremely important because a lot of robot manipulation is essentially about moving between these two representations.&lt;/p&gt;




&lt;h1&gt;
  
  
  Forward Kinematics
&lt;/h1&gt;

&lt;p&gt;Suppose I know all the joint angles of my robot.&lt;/p&gt;

&lt;p&gt;The question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where is my end effector?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's exactly what &lt;strong&gt;Forward Kinematics (FK)&lt;/strong&gt; answers.&lt;/p&gt;

&lt;p&gt;We can write this relationship as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = f(q)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In simple terms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Joint angles  ────── FK ──────&amp;gt;  End-effector pose

     q                             x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give forward kinematics the robot's joint configuration, and it calculates where the end effector is.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q = [q₁, q₂, q₃, q₄, q₅, q₆]

            ↓
     Forward Kinematics
            ↓

x = [x, y, z, ϕ, θ, ψ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Inverse Kinematics
&lt;/h1&gt;

&lt;p&gt;Now let's reverse the problem.&lt;/p&gt;

&lt;p&gt;Suppose I know where I want the robot's end effector to be.&lt;/p&gt;

&lt;p&gt;The question becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What should my joint angles be to reach that pose?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the &lt;strong&gt;Inverse Kinematics (IK)&lt;/strong&gt; problem.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q = f⁻¹(x)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;End-effector pose  ────── IK ──────&amp;gt;  Joint angles

        x                              q
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is an important detail here.&lt;/p&gt;

&lt;p&gt;Writing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q = f⁻¹(x)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is useful for understanding the idea, but inverse kinematics is not always a simple mathematical inverse.&lt;/p&gt;

&lt;p&gt;For a particular end-effector pose, a robot may have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one solution&lt;/li&gt;
&lt;li&gt;multiple solutions&lt;/li&gt;
&lt;li&gt;infinitely many solutions&lt;/li&gt;
&lt;li&gt;or no solution at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But conceptually, IK answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where should my joints be if I want my end effector here?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Great. We Know FK and IK.
&lt;/h1&gt;

&lt;p&gt;But there's a problem.&lt;/p&gt;

&lt;p&gt;My robot still doesn't move.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BORING.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We know where the robot is.&lt;/p&gt;

&lt;p&gt;We know what joint angles can produce a particular pose.&lt;/p&gt;

&lt;p&gt;But how do we actually describe &lt;strong&gt;motion&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;How fast should each joint move?&lt;/p&gt;

&lt;p&gt;And if I move my joints at certain velocities, how will my end effector move?&lt;/p&gt;

&lt;p&gt;That's where things start getting interesting.&lt;/p&gt;

&lt;h1&gt;
  
  
  Differential Kinematics
&lt;/h1&gt;

&lt;p&gt;Let's go back to our forward kinematics equation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = f(q)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells us that the end-effector pose &lt;strong&gt;x&lt;/strong&gt; depends on the robot's joint configuration &lt;strong&gt;q&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But now we're interested in how these quantities &lt;strong&gt;change with time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So let's differentiate the forward kinematics equation with respect to time.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = f(q)

      ↓ differentiate with respect to time

ẋ = J(q) q̇
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And suddenly, we get one of the most important equations in robot manipulation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;╔══════════════════╗
║                  ║
║   ẋ = J(q) q̇    ║
║                  ║
╚══════════════════╝
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's understand what each term means.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q   → Joint positions

q̇   → Joint velocities

x   → End-effector pose

ẋ   → End-effector velocity

J(q) → Jacobian
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So this equation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ẋ = J(q) q̇
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;answers a very practical question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If I move my joints at these velocities, how will my end effector move?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the key idea behind &lt;strong&gt;differential kinematics&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  But Where Did This Jacobian Come From?
&lt;/h1&gt;

&lt;p&gt;This was one of the things that initially confused me.&lt;/p&gt;

&lt;p&gt;The Jacobian can look like some mysterious matrix that suddenly appears in a robotics textbook.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;Let's derive it.&lt;/p&gt;

&lt;p&gt;And for that, we'll use one of the simplest robots possible.&lt;/p&gt;




&lt;h1&gt;
  
  
  A 2-Link Planar Manipulator
&lt;/h1&gt;

&lt;p&gt;Consider a robot with two links:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                     ● End Effector
                    /
                   / l₂
                  /
                 ● q₂
                /
               / l₁
              /
             ● q₁
             |
           Base
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgxwsigyy0onggjbjc84p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgxwsigyy0onggjbjc84p.jpg" alt=" " width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The robot has two joints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q = [q₁, q₂]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and two link lengths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;l₁
l₂
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the robot moves in a 2D plane, its end-effector position can be represented using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = horizontal position
y = vertical position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using basic trigonometry, we can calculate the end-effector position.&lt;/p&gt;




&lt;h2&gt;
  
  
  Forward Kinematics of the 2R Robot
&lt;/h2&gt;

&lt;p&gt;The x-coordinate is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = l₁ cos(q₁) + l₂ cos(q₁ + q₂)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The y-coordinate is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;y = l₁ sin(q₁) + l₂ sin(q₁ + q₂)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's our forward kinematics.&lt;/p&gt;

&lt;p&gt;Joint angles go in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[q₁, q₂]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the end-effector position comes out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[x, y]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, nothing unusual.&lt;/p&gt;

&lt;p&gt;Now comes the interesting part.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Happens When the Joints Move?
&lt;/h1&gt;

&lt;p&gt;Suppose both joints start moving.&lt;/p&gt;

&lt;p&gt;Their angular velocities are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q̇₁
q̇₂
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the joints are moving, the end effector also moves.&lt;/p&gt;

&lt;p&gt;Its Cartesian velocity becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ẋ
ẏ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we want to find the relationship:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[q̇₁, q̇₂]  ──────────&amp;gt;  [ẋ, ẏ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's differentiate our forward kinematics equations.&lt;/p&gt;

&lt;p&gt;We had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = l₁ cos(q₁) + l₂ cos(q₁ + q₂)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;y = l₁ sin(q₁) + l₂ sin(q₁ + q₂)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we differentiate these equations with respect to time, the result can be arranged as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌ ẋ ┐       ┌ ∂x/∂q₁   ∂x/∂q₂ ┐ ┌ q̇₁ ┐
│   │   =   │                 │ │    │
└ ẏ ┘       └ ∂y/∂q₁   ∂y/∂q₂ ┘ └ q̇₂ ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look carefully at the matrix in the middle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        ┌ ∂x/∂q₁   ∂x/∂q₂ ┐
J(q) = │                 │
        └ ∂y/∂q₁   ∂y/∂q₂ ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;That's the Jacobian.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nothing magical happened. We simply differentiated the forward kinematics with respect to the joint variables.&lt;/p&gt;

&lt;p&gt;But let's look at what this matrix is actually telling us.&lt;/p&gt;

&lt;p&gt;Consider the &lt;strong&gt;first column&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌ ∂x/∂q₁ ┐
│        │
└ ∂y/∂q₁ ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It tells us how the end-effector position in &lt;strong&gt;x and y changes when joint q₁ changes&lt;/strong&gt;, while the other joint is held fixed.&lt;/p&gt;

&lt;p&gt;Similarly, the second column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌ ∂x/∂q₂ ┐
│        │
└ ∂y/∂q₂ ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;tells us how &lt;strong&gt;q₂ influences the end-effector position&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So each &lt;strong&gt;column of the Jacobian represents the influence of one joint on the motion of the end effector&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For our 2-link planar robot, we are only interested in motion along &lt;strong&gt;x and y&lt;/strong&gt;, so the Jacobian is a &lt;strong&gt;2 × 2 matrix&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Joints
                  q₁      q₂
               ┌               ┐
x direction  → │ ∂x/∂q₁  ∂x/∂q₂ │
y direction  → │ ∂y/∂q₁  ∂y/∂q₂ │
               └               ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's generalize this idea.&lt;/p&gt;

&lt;p&gt;For a robot with &lt;strong&gt;n joints&lt;/strong&gt;, the joint velocity vector is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q̇ = [q̇₁, q̇₂, ... , q̇ₙ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A robot moving freely in 3D space can have an end-effector velocity consisting of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Linear velocity:   [ẋ, ẏ, ż]

Angular velocity:  [ωₓ, ωᵧ, ω_z]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the full geometric Jacobian has the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    n joints
          ┌───────────────────────┐
          │                       │
Linear    │         Jᵥ(q)         │  3 rows
          │                       │
          ├───────────────────────┤
          │                       │
Angular   │         Jω(q)         │  3 rows
          │                       │
          └───────────────────────┘

                     6 × n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore, for an &lt;strong&gt;n-DOF robot&lt;/strong&gt;, the full Jacobian is generally a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;6 × n matrix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;n columns&lt;/strong&gt; correspond to the robot's joints.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;first three rows&lt;/strong&gt; describe how those joints contribute to the end effector's &lt;strong&gt;linear velocity&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ẋ, ẏ, ż]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;last three rows&lt;/strong&gt; describe how they contribute to its &lt;strong&gt;angular velocity&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ωₓ, ωᵧ, ω_z]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we can think of the full Jacobian as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                      q̇₁   q̇₂   q̇₃  ...  q̇ₙ
                       ↓     ↓     ↓         ↓

                  ┌─────────────────────────┐
       ẋ     ←    │                         │
       ẏ     ←    │       Linear part       │
       ż     ←    │           Jᵥ            │
                  ├─────────────────────────┤
       ωₓ    ←    │                         │
       ωᵧ    ←    │       Angular part      │
       ω_z   ←    │           Jω            │
                  └─────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this article, however, we'll keep things simple.&lt;/p&gt;

&lt;p&gt;We are mainly interested in how the joints influence the &lt;strong&gt;position&lt;/strong&gt; of the end effector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x, y, z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we'll focus on the &lt;strong&gt;linear part of the Jacobian&lt;/strong&gt; and leave orientation and angular velocity for later.&lt;/p&gt;

&lt;p&gt;With that intuition in place, let's actually calculate the Jacobian for our 2-link robot.&lt;/p&gt;




&lt;h1&gt;
  
  
  Let's Actually Calculate It
&lt;/h1&gt;

&lt;p&gt;From:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = l₁ cos(q₁) + l₂ cos(q₁ + q₂)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;∂x/∂q₁ = -l₁ sin(q₁) - l₂ sin(q₁ + q₂)

∂x/∂q₂ = -l₂ sin(q₁ + q₂)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;y = l₁ sin(q₁) + l₂ sin(q₁ + q₂)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;∂y/∂q₁ = l₁ cos(q₁) + l₂ cos(q₁ + q₂)

∂y/∂q₂ = l₂ cos(q₁ + q₂)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting everything into the matrix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       ┌ -l₁sin(q₁)-l₂sin(q₁+q₂)    -l₂sin(q₁+q₂) ┐
J(q) = │                                            │
       └  l₁cos(q₁)+l₂cos(q₁+q₂)     l₂cos(q₁+q₂) ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's our Jacobian.&lt;/p&gt;

&lt;p&gt;Now our velocity relationship becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌ ẋ ┐                 ┌ q̇₁ ┐
│   │   =   J(q)      │    │
└ ẏ ┘                 └ q̇₂ ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ẋ = J(q)q̇
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This equation now has a very intuitive meaning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Give me the current robot configuration and its joint velocities, and the Jacobian tells me how the end effector is moving.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Enough With the Equations. Let's Make the Robot Move.
&lt;/h1&gt;

&lt;p&gt;We cannot learn swimming by learning about water.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let's just dive in.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So far, we've talked about joint space, task space, forward kinematics, and the Jacobian.&lt;/p&gt;

&lt;p&gt;Now let's actually use them on a robot.&lt;/p&gt;

&lt;p&gt;For this experiment, I'm using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ROS 2 Jazzy&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RViz&lt;/strong&gt; for visualization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UR10&lt;/strong&gt; as the robot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MPU6050&lt;/strong&gt; as a gesture sensor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raspberry Pi 4&lt;/strong&gt; to read and publish the sensor data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The setup looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      Hand Gesture
           │
           ▼
       MPU6050
           │
           │  Roll / Pitch
           ▼
   Raspberry Pi 4
           │
           │  ROS 2
           ▼
      Host Machine
           │
           ▼
    Differential
     Kinematics
           │
           │ Joint velocities
           ▼
         UR10
           │
           ▼
         RViz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MPU6050 isn't really necessary.&lt;/p&gt;

&lt;p&gt;I just had one lying around for a long time and finally wanted to put it to use. 😄&lt;/p&gt;

&lt;p&gt;You could achieve exactly the same thing using four keyboard keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;W → +X
S → -X

A → +Y
D → -Y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sensor just makes the experiment more interactive.&lt;/p&gt;




&lt;h2&gt;
  
  
  Converting Gestures Into Cartesian Velocity
&lt;/h2&gt;

&lt;p&gt;For simplicity, I'm only controlling the end effector in the &lt;strong&gt;X-Y plane&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Roll   → Velocity along X

Pitch  → Velocity along Y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not trying to make the velocity proportional to the exact tilt angle yet.&lt;/p&gt;

&lt;p&gt;Instead, once the tilt crosses a threshold, I command a small &lt;strong&gt;constant Cartesian velocity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tilt forward     →  +ẋ

Tilt backward    →  -ẋ

Tilt left        →  +ẏ

Tilt right       →  -ẏ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So after reading the MPU6050, I have something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ẋ = 0.05 m/s
ẏ = 0.00 m/s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ẋ = 0.00 m/s
ẏ = -0.05 m/s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great.&lt;/p&gt;

&lt;p&gt;Now I know &lt;strong&gt;how I want the end effector to move&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But the UR10 doesn't accept:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Move your end effector at +0.05 m/s in X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as a joint command.&lt;/p&gt;

&lt;p&gt;The robot ultimately needs to know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;How should each joint move?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that brings us straight back to our differential kinematics equation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Back to the Jacobian
&lt;/h1&gt;

&lt;p&gt;We previously derived:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ẋ = J(q)q̇
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember what this equation told us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Joint velocities  ───── J(q) ─────&amp;gt;  End-effector velocity

      q̇                              ẋ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But now we have the &lt;strong&gt;opposite problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;From the MPU6050, we already know the desired end-effector velocity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ẋ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we need are the corresponding joint velocities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q̇
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words, we want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;End-effector velocity  ───── ??? ─────&amp;gt;  Joint velocities

          ẋ                                q̇
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ẋ = J(q)q̇
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;our goal is to solve for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q̇
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a simple square and invertible Jacobian, we might be tempted to write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q̇ = J⁻¹(q)ẋ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But with a real robot like the &lt;strong&gt;UR10&lt;/strong&gt;, things aren't always that simple.&lt;/p&gt;

&lt;p&gt;Remember, the UR10 has six joints.&lt;/p&gt;

&lt;p&gt;And in our experiment, we're currently asking it to perform only a small part of its possible task-space motion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Desired motion:

[ẋ, ẏ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Joint velocities:

[q̇₁, q̇₂, q̇₃, q̇₄, q̇₅, q̇₆]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So how do we go from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ẋ, ẏ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[q̇₁, q̇₂, q̇₃, q̇₄, q̇₅, q̇₆]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;?&lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;Jacobian pseudoinverse&lt;/strong&gt; enters the picture.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                J⁺(q)

[ẋ, ẏ]  ───────────────────&amp;gt;  [q̇₁, q̇₂, q̇₃, q̇₄, q̇₅, q̇₆]

Desired Cartesian                    Required
    velocity                     joint velocities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we're finally ready to write some code.&lt;/p&gt;

&lt;p&gt;Instead, we need a &lt;strong&gt;pseudoinverse&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In my implementation, I'm using a slightly more robust version called the &lt;strong&gt;Damped Least-Squares pseudoinverse&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The equation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q̇ = Jᵀ (J Jᵀ + λ²I)⁻¹ v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;J   → 3 × 6 position Jacobian
Jᵀ  → 6 × 3

v   → desired Cartesian velocity [ẋ, ẏ, ż]

λ   → damping factor

I   → 3 × 3 identity matrix

q̇   → resulting 6 joint velocities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is what it looks like in Eigen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Eigen&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Matrix3d&lt;/span&gt; &lt;span class="n"&gt;regularized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;xyz_jacobian&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;xyz_jacobian&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;damping_squared_&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Eigen&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Matrix3d&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Eigen&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;joint_velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;xyz_jacobian&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;regularized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ldlt&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cartesian_velocity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's map the code directly to the equation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;xyz_jacobian
        ↓
        J


xyz_jacobian.transpose()
        ↓
        Jᵀ


xyz_jacobian * xyz_jacobian.transpose()
        ↓
        JJᵀ


damping_squared_ * Identity()
        ↓
        λ²I


cartesian_velocity
        ↓
        v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So these few lines are essentially implementing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q̇ = Jᵀ (JJᵀ + λ²I)⁻¹ v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;h1&gt;
  
  
  We Have Joint Velocities. Now What?
&lt;/h1&gt;

&lt;p&gt;At this point, our damped least-squares solution gives us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q̇ = [q̇₁, q̇₂, q̇₃, q̇₄, q̇₅, q̇₆]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But remember what these values represent.&lt;/p&gt;

&lt;p&gt;They are &lt;strong&gt;velocities&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They tell us how fast each joint should move, not where the joints should move to.&lt;/p&gt;

&lt;p&gt;What we actually need for the next control step is a small change in joint position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Δq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Given these joint velocities, how much should each joint move during this control cycle?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's straightforward.&lt;/p&gt;

&lt;p&gt;Velocity is change in position over time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q̇ = Δq / Δt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Δq = q̇ Δt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q̇   → Joint velocity

Δt  → Time between two control updates

Δq  → Change in joint position
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we know &lt;code&gt;Δq&lt;/code&gt;, we can update the current joint configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q_new = q_current + Δq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q_new = q_current + q̇ Δt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For all six joints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q₁_new = q₁_current + q̇₁ Δt
q₂_new = q₂_current + q̇₂ Δt
q₃_new = q₃_current + q̇₃ Δt
q₄_new = q₄_current + q̇₄ Δt
q₅_new = q₅_current + q̇₅ Δt
q₆_new = q₆_current + q̇₆ Δt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We calculate this repeatedly inside the control loop.&lt;/p&gt;

&lt;p&gt;So the robot doesn't make one giant movement.&lt;/p&gt;

&lt;p&gt;Instead, it makes many tiny movements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current q
   │
   ▼
Calculate J(q)
   │
   ▼
Calculate q̇
   │
   ▼
Δq = q̇ Δt
   │
   ▼
q_new = q + Δq
   │
   ▼
Robot moves slightly
   │
   └──────────────► Repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is essentially numerical integration.&lt;/p&gt;

&lt;p&gt;We're integrating joint velocity over time to obtain joint position.&lt;/p&gt;

&lt;p&gt;Now that we can actually update the robot's joints, let's try something interesting.&lt;/p&gt;




&lt;h1&gt;
  
  
  Experiment 1: Control Only X
&lt;/h1&gt;

&lt;p&gt;Let's start with the simplest possible task.&lt;/p&gt;

&lt;p&gt;I only care about the end effector's velocity along the &lt;strong&gt;X direction&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Desired:

ẋ = 0.05 m/s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;I'm &lt;strong&gt;not asking the controller to maintain Y&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'm &lt;strong&gt;not asking the controller to maintain Z&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So our task only contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ẋ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ẋ = 0.05
ẏ = 0
ż = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, it means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X → Controlled

Y → Unconstrained

Z → Unconstrained
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The controller's job is simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Find joint velocities that produce the requested X velocity.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What happens in Y and Z is not part of the task.&lt;/p&gt;

&lt;p&gt;Now let's see what the UR10 actually does.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmckttvy4iy0pgazovrhx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmckttvy4iy0pgazovrhx.gif" alt=" " width="720" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Watch the end effector carefully.&lt;/p&gt;

&lt;p&gt;We asked it to move along X, but we never told it what to do in Y or Z.&lt;/p&gt;

&lt;p&gt;So those directions are free to change as a consequence of the joint motion.&lt;/p&gt;

&lt;p&gt;This is our first glimpse of what an &lt;strong&gt;unconstrained direction&lt;/strong&gt; actually means.&lt;/p&gt;




&lt;h1&gt;
  
  
  Experiment 2: Control X and Y
&lt;/h1&gt;

&lt;p&gt;Now let's add another requirement.&lt;/p&gt;

&lt;p&gt;Instead of controlling only X, let's control both X and Y.&lt;/p&gt;

&lt;p&gt;Our task-space velocity becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌ ẋ ┐
│   │
└ ẏ ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X → Controlled

Y → Controlled

Z → Unconstrained
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This changes our Jacobian too.&lt;/p&gt;

&lt;p&gt;Instead of using only the row corresponding to X:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Jx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we now use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     ┌ Jx ┐
J =  │    │
     └ Jy ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our solver must now find joint velocities that satisfy &lt;strong&gt;both&lt;/strong&gt; requested Cartesian velocities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              X constraint
                    +
              Y constraint
                    │
                    ▼
              Jacobian
                    │
                    ▼
          Joint velocities
                    │
                    ▼
                  UR10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But Z is still not part of the task.&lt;/p&gt;

&lt;p&gt;We haven't said:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ż = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We simply haven't constrained Z at all.&lt;/p&gt;

&lt;p&gt;Let's see how that changes the robot's motion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdaofje28d7zf0cl5u39g.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdaofje28d7zf0cl5u39g.gif" alt=" " width="720" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compare this with the previous experiment.&lt;/p&gt;

&lt;p&gt;The solver now has fewer freedoms because it has another Cartesian requirement to satisfy.&lt;/p&gt;

&lt;p&gt;But the Z direction is still free.&lt;/p&gt;




&lt;h1&gt;
  
  
  Experiment 3: Now Constrain Z
&lt;/h1&gt;

&lt;p&gt;Finally, let's add Z to our task.&lt;/p&gt;

&lt;p&gt;But here's the interesting part:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I don't actually want the robot to move in Z.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ż = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our complete desired linear velocity is now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        ┌ ẋ ┐
v   =   │ ẏ │
        └ 0 ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X → Controlled

Y → Controlled

Z → Controlled at zero velocity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fundamentally different from simply leaving Z out of the task.&lt;/p&gt;

&lt;p&gt;Previously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Z → Do whatever is necessary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Z → Do NOT move
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Jacobian used by our controller now contains all three linear velocity rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     ┌ Jx ┐
     │    │
J =  │ Jy │
     │    │
     └ Jz ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and our desired Cartesian velocity is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        ┌ ẋ ┐
v   =   │ ẏ │
        └ 0 ┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The damped least-squares solver must therefore find joint velocities that produce the requested X and Y motion &lt;strong&gt;while simultaneously trying to keep the Z velocity at zero&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwt1p0y9mopvgslz4r02x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwt1p0y9mopvgslz4r02x.gif" alt=" " width="600" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now compare all three experiments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Experiment 1

X → Controlled
Y → Unconstrained
Z → Unconstrained

Experiment 2

X → Controlled
Y → Controlled
Z → Unconstrained

Experiment 3

X → Controlled
Y → Controlled
Z → Controlled at 0 m/s

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this demonstrates an important idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Not controlling a direction is not the same as commanding zero velocity in that direction.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If Z isn't included in our task, the solver is free to produce motion in Z.&lt;/p&gt;

&lt;p&gt;If Z is included with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ż = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we are explicitly asking the solver to maintain zero velocity along Z.&lt;/p&gt;

&lt;p&gt;The more task-space directions we constrain, the fewer freedoms remain for the robot to use elsewhere.&lt;/p&gt;

&lt;p&gt;And that observation leads us toward something much more interesting:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What can the robot do with the freedoms that remain?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's where the &lt;strong&gt;null space&lt;/strong&gt; starts to become useful.&lt;/p&gt;

</description>
      <category>hardware</category>
      <category>robotics</category>
      <category>science</category>
    </item>
  </channel>
</rss>
