diff --git a/VirtualRobot/IK/CompositeDiffIK/CompositeDiffIK.cpp b/VirtualRobot/IK/CompositeDiffIK/CompositeDiffIK.cpp
index f35569f728c3412be25276ca6d6fce59da010786..de9c5116758c403da1fb0d23b3b750d5837bce7f 100644
--- a/VirtualRobot/IK/CompositeDiffIK/CompositeDiffIK.cpp
+++ b/VirtualRobot/IK/CompositeDiffIK/CompositeDiffIK.cpp
@@ -91,7 +91,27 @@ CompositeDiffIK::Result CompositeDiffIK::solve(Parameters params, SolveState &s)
     s.jointRegularization = Eigen::VectorXf::Zero(s.cols);
     for (size_t i = 0; i < rns->getSize(); i++)
     {
-        s.jointRegularization(i) = rns->getNode(i)->isTranslationalJoint() ?  params.jointRegularizationTranslation : params.jointRegularizationRotation;
+        float regularization = 1;
+        if (rns->getNode(i)->isTranslationalJoint())
+        {
+            regularization = params.jointRegularizationTranslation;
+        }
+        else if (rns->getNode(i)->isRotationalJoint())
+        {
+            regularization = params.jointRegularizationRotation;
+        }
+        else if (rns->getNode(i)->isHemisphereJoint())
+        {
+            // FIXME: ToDo?
+            regularization = params.jointRegularizationRotation;
+        }
+        else if (rns->getNode(i)->isFourBarJoint())
+        {
+            // FIXME: ToDo?
+            regularization = params.jointRegularizationRotation;
+        }
+
+        s.jointRegularization(i) = regularization;
     }
 
     s.cartesianRegularization = Eigen::VectorXf::Zero(s.rows);
@@ -206,39 +226,48 @@ void CompositeDiffIK::step(CompositeDiffIK::Parameters& params, SolveState& s, i
 
     ik->updatePseudoInverseJacobianMatrix(s.invJac, s.jacobi, 0, s.cartesianRegularization);
 
+    Eigen::VectorXf jv = s.invJac * cartesianVel;
 
-    Eigen::VectorXf nullspaceVel = Eigen::VectorXf::Zero(s.cols);
-
-    for (const NullspaceGradientPtr& nsGradient : nullspaceGradients)
+    if (s.cols > s.rows)
     {
-        Eigen::VectorXf nsgrad = nsGradient->kP * nsGradient->getGradientAdjusted(params, stepNr);
-        nullspaceVel += nsgrad;
-    }
-    //LimitInfNormTo(nullspaceVel, params.maxJointAngleStep);
+        Eigen::VectorXf nullspaceVel = Eigen::VectorXf::Zero(s.cols);
 
-    Eigen::JacobiSVD<Eigen::MatrixXf> svd(s.jacobi, Eigen::ComputeFullU | Eigen::ComputeFullV);
-    Eigen::MatrixXf V = svd.matrixV();
-    Eigen::MatrixXf nullspaceSVD = V.block(0, s.rows, s.cols, s.cols - s.rows);
+        for (const NullspaceGradientPtr& nsGradient : nullspaceGradients)
+        {
+            Eigen::VectorXf nsgrad = nsGradient->kP * nsGradient->getGradientAdjusted(params, stepNr);
+            nullspaceVel += nsgrad;
+        }
 
-    s.nullspace = nullspaceSVD; // CalculateNullspaceSVD(s.jacobi);
+        //LimitInfNormTo(nullspaceVel, params.maxJointAngleStep);
 
-    Eigen::VectorXf nsv = Eigen::VectorXf::Zero(s.cols);
-    for (int i = 0; i < s.nullspace.cols(); i++)
-    {
-        float squaredNorm = s.nullspace.col(i).squaredNorm();
-        // Prevent division by zero
-        if (squaredNorm > 1.0e-32f)
+        Eigen::JacobiSVD<Eigen::MatrixXf> svd(s.jacobi, Eigen::ComputeFullU | Eigen::ComputeFullV);
+        Eigen::MatrixXf V = svd.matrixV();
+
+        Eigen::MatrixXf nullspaceSVD = V.block(0, s.rows, s.cols, s.cols - s.rows);
+
+        s.nullspace = nullspaceSVD; // CalculateNullspaceSVD(s.jacobi);
+
+        Eigen::VectorXf nsv = Eigen::VectorXf::Zero(s.cols);
+        for (int i = 0; i < s.nullspace.cols(); i++)
         {
-            nsv += s.nullspace.col(i) * s.nullspace.col(i).dot(nullspaceVel) / s.nullspace.col(i).squaredNorm();
+            float squaredNorm = s.nullspace.col(i).squaredNorm();
+            // Prevent division by zero
+            if (squaredNorm > 1.0e-32f)
+            {
+                nsv += s.nullspace.col(i) * s.nullspace.col(i).dot(nullspaceVel) / s.nullspace.col(i).squaredNorm();
+            }
         }
+        jv = jv + nsv;
     }
 
-    Eigen::VectorXf jv = s.invJac * cartesianVel;
-    jv = jv + nsv;
+    std::cout << "Before: " << jv << std::endl;
+
     jv = jv * params.stepSize;
     jv = LimitInfNormTo(jv, params.maxJointAngleStep, params.maxJointAngleStepIgnore);
     jv = jv.cwiseProduct(s.jointRegularization);
 
+    std::cout << "After: " << jv << std::endl;
+
     Eigen::VectorXf newJointValues = s.jointValues + jv;
 
     rns->setJointValues(newJointValues);
diff --git a/VirtualRobot/IK/CompositeDiffIK/CompositeDiffIK.h b/VirtualRobot/IK/CompositeDiffIK/CompositeDiffIK.h
index 71e27d22469016e0a6a246255140428d4687f6a8..595b3b51f2e8ca0e408762f7473cdb15881c35e3 100644
--- a/VirtualRobot/IK/CompositeDiffIK/CompositeDiffIK.h
+++ b/VirtualRobot/IK/CompositeDiffIK/CompositeDiffIK.h
@@ -38,7 +38,7 @@ namespace VirtualRobot
             // IK params
             size_t steps = 40;
 
-            float maxJointAngleStep = 0.2f;
+            float maxJointAngleStep = 0.01f;
             float stepSize = 0.5f;
             bool resetRnsValues = true;
             bool returnIKSteps = false;
diff --git a/VirtualRobot/IK/DifferentialIK.cpp b/VirtualRobot/IK/DifferentialIK.cpp
index 94da3ade3a5c5e6f151365bb0427c60d21262a58..0e2471ec3d0c768d64bc90cbaa299e13a4ed7a3b 100644
--- a/VirtualRobot/IK/DifferentialIK.cpp
+++ b/VirtualRobot/IK/DifferentialIK.cpp
@@ -6,10 +6,9 @@
 #include "../Nodes/RobotNodeRevolute.h"
 #include "../VirtualRobotException.h"
 #include "../CollisionDetection/CollisionChecker.h"
-#include "Nodes/FourBar/Joint.h"
-#include "Nodes/RobotNodeFourBar.h"
-#include "Nodes/RobotNodeHemisphere.h"
-#include "VirtualRobot.h"
+#include <VirtualRobot/Nodes/RobotNodeHemisphere.h>
+#include <VirtualRobot/Nodes/RobotNodeFourBar.h>
+#include <VirtualRobot/Nodes/RobotNodeHemisphere.h>
 
 #include <Eigen/Geometry>
 #include <Eigen/src/Core/Matrix.h>
@@ -385,8 +384,11 @@ namespace VirtualRobot
 
             //check if the tcp is affected by this DOF
             auto p = parents[tcpRN];
-            if (find(p.begin(), p.end(), dof) != p.end())
+            const bool isParent = std::find(p.begin(), p.end(), dof) != p.end();
+            if (isParent)
             {
+                bool isHemisphere = dof->isHemisphereJoint();
+                std::cout << "isHemisphere: " << isHemisphere << std::endl;
 
                 // Calculus for rotational joints is different as for prismatic joints.
                 if (dof->isRotationalJoint())
@@ -450,8 +452,63 @@ namespace VirtualRobot
                 }
                 else if (dof->isHemisphereJoint())
                 {
-                    const RobotNodeHemisphere* hemisphere = dynamic_cast<RobotNodeHemisphere*>(dof.get());
-                    // FIXME implement
+                    RobotNodeHemispherePtr hemisphere
+                            = std::dynamic_pointer_cast<RobotNodeHemisphere>(dof);
+
+                    if (not (hemisphere->first.has_value() xor hemisphere->second.has_value()))
+                    {
+                        throw "!!!!";
+                    }
+
+                    if (hemisphere->first)
+                    {
+                        std::cout << "First Hemisphere" << std::endl;
+                    }
+                    else if (hemisphere->second)
+                    {
+                        std::cout << "Second Hemisphere" << std::endl;
+
+                        RobotNodeHemispherePtr second = hemisphere;
+                        RobotNodeHemisphere* first = hemisphere->second->first;
+
+                        RobotNodeHemisphere::JointMath& math = first->first->math;
+
+                        Eigen::Vector2f actuators(first->getJointValue(), second->getJointValue());
+                        math.update(actuators);
+
+                        hemisphere::Joint::Jacobian jacobian = math.joint.getJacobian();
+
+                        tmpUpdateJacobianPosition.block<3, 2>(0, i-1) =
+                                jacobian.block<3, 2>(0, 0).cast<float>();
+
+                        {
+                            // Assume we move with (+1, +1)
+                            Eigen::Vector3d eefStateTrans = math.joint.getEndEffectorTranslation();
+
+                            Eigen::Vector2d actuatorVel = Eigen::Vector2d::Ones();
+                            Eigen::Vector3d eefVelTrans = jacobian.block<3, 2>(0, 0) * actuatorVel;
+
+                            Eigen::Vector3d rotAxis = eefStateTrans.cross(eefVelTrans) / eefStateTrans.norm() * 2;
+
+                            // Left column.
+                            for (int column = 0; column < 2; ++column)
+                            {
+                                if (actuatorVel(column) != 0)
+                                {
+                                    tmpUpdateJacobianOrientation.block<3, 1>(0, (i-1) + column) =
+                                            (rotAxis / actuatorVel(column)).cast<float>();
+                                }
+                                else
+                                {
+                                    tmpUpdateJacobianOrientation.block<3, 1>(0, (i-1) + column).setZero();
+                                }
+                            }
+                        }
+                    }
+                    else
+                    {
+                        // Pass
+                    }
                 }
                 else if (dof->isFourBarJoint())
                 {
@@ -512,7 +569,6 @@ namespace VirtualRobot
 
 
                 }
-
             }
 
 #ifdef CHECK_PERFORMANCE
diff --git a/VirtualRobot/Nodes/HemisphereJoint/Joint.cpp b/VirtualRobot/Nodes/HemisphereJoint/Joint.cpp
index 1d5a26ad82b6099338568961f1008b3cd415cf9d..b795fc833fd41d27f7c64996e08e700752182393 100644
--- a/VirtualRobot/Nodes/HemisphereJoint/Joint.cpp
+++ b/VirtualRobot/Nodes/HemisphereJoint/Joint.cpp
@@ -38,13 +38,13 @@ namespace VirtualRobot::hemisphere
     }
 
 
-    void Joint::computeFkOfPosition(const Eigen::Vector2d& p12)
+    void Joint::computeFkOfPosition(const ActuatorPosition& p12)
     {
         computeFkOfPosition(p12(0), p12(1));
     }
 
 
-    void Joint::computeFkOfAngle(const Eigen::Vector2d& alpha12)
+    void Joint::computeFkOfAngle(const ActuatorAngle& alpha12)
     {
         computeFkOfPosition(angleToPosition(alpha12));
     }
@@ -89,7 +89,8 @@ namespace VirtualRobot::hemisphere
         return jacobian;
     }
 
-    Eigen::Vector2d Joint::angleToPosition(const Eigen::Vector2d& alpha) const
+
+    Joint::ActuatorPosition Joint::angleToPosition(const Joint::ActuatorAngle& alpha) const
     {
         return lever * Eigen::sin((alpha + Eigen::Vector2d::Constant(theta0)).array());
     }
diff --git a/VirtualRobot/Nodes/HemisphereJoint/Joint.h b/VirtualRobot/Nodes/HemisphereJoint/Joint.h
index 7d2bda4861ccf44c13f02d513a3f38378d6eafd1..56fa0c3c199b96e532cea65fcb5ea6bf2a031fd5 100644
--- a/VirtualRobot/Nodes/HemisphereJoint/Joint.h
+++ b/VirtualRobot/Nodes/HemisphereJoint/Joint.h
@@ -8,10 +8,19 @@
 namespace VirtualRobot::hemisphere
 {
 
+    /**
+     * @brief Hemisphere joint mathematics for FK and IK.
+     *
+     * This class is a wrapper about `Expressions`, which contains the actual
+     * math code that was generated from Python sympy expressions
+     * using `python/hemisphere-joint-demo/hemisphere_joint_demo/sympy_to_code.py`.
+     */
     class Joint
     {
     public:
 
+        using ActuatorPosition = Eigen::Vector2d;
+        using ActuatorAngle = Eigen::Vector2d;
         using Jacobian = Eigen::Matrix<double, 6, 2>;
 
     public:
@@ -23,18 +32,19 @@ namespace VirtualRobot::hemisphere
         void setConstants(double lever, double theta0);
 
 
-        void computeFkOfAngle(const Eigen::Vector2d& alpha12);
+        void computeFkOfAngle(const ActuatorAngle& alpha12);
 
-        void computeFkOfPosition(const Eigen::Vector2d& p12);
+        void computeFkOfPosition(const ActuatorPosition& p12);
         void computeFkOfPosition(double p1, double p2);
 
 
         Eigen::Vector3d getEndEffectorTranslation() const;
         Eigen::Matrix3d getEndEffectorRotation() const;
         Eigen::Matrix4d getEndEffectorTransform() const;
+
         Jacobian getJacobian() const;
 
-        Eigen::Vector2d angleToPosition(const Eigen::Vector2d& alpha) const;
+        ActuatorPosition angleToPosition(const ActuatorAngle& alpha) const;
 
 
     public:
diff --git a/VirtualRobot/Nodes/RobotNodeHemisphere.cpp b/VirtualRobot/Nodes/RobotNodeHemisphere.cpp
index 38e672841b0411295b0aa76d945ec60907474f6b..86a9fe6ffc6cf3bbc8500dec3697b744625b8099 100644
--- a/VirtualRobot/Nodes/RobotNodeHemisphere.cpp
+++ b/VirtualRobot/Nodes/RobotNodeHemisphere.cpp
@@ -109,8 +109,6 @@ namespace VirtualRobot
 
     void RobotNodeHemisphere::setXmlInfo(const XmlInfo& info)
     {
-        this->xmlInfo = info;
-
         VR_ASSERT(second.has_value());
         switch (info.role)
         {
@@ -265,10 +263,10 @@ namespace VirtualRobot
         ReadLockPtr lock = getRobot()->getReadLock();
         Physics physics = this->physics.scale(scaling);
 
-        RobotNodeHemispherePtr result;
+        RobotNodeHemispherePtr cloned;
         if (optionalDHParameter.isSet)
         {
-            result.reset(new RobotNodeHemisphere(
+            cloned.reset(new RobotNodeHemisphere(
                              newRobot, name, jointLimitLo, jointLimitHi,
                              optionalDHParameter.aMM() * scaling,
                              optionalDHParameter.dMM() * scaling,
@@ -282,7 +280,7 @@ namespace VirtualRobot
         {
             Eigen::Matrix4f localTransform = getLocalTransformation();
             simox::math::position(localTransform) *= scaling;
-            result.reset(new RobotNodeHemisphere(
+            cloned.reset(new RobotNodeHemisphere(
                              newRobot, name,
                              jointLimitLo, jointLimitHi,
                              localTransform, Eigen::Vector3f::Zero(),
@@ -290,12 +288,18 @@ namespace VirtualRobot
                              jointValueOffset, physics, colChecker, nodeType));
         }
 
-        if(xmlInfo)
+        if (this->first)
+        {
+            // We can just copy the math object.
+            cloned->first = first;
+        }
+        else if (this->second)
         {
-            result->setXmlInfo(xmlInfo.value());
+            cloned->second.emplace(Second{});
+            // initialize() takes care of hooking up the second to the first.
         }
 
-        return result;
+        return cloned;
     }
 
 
diff --git a/VirtualRobot/Nodes/RobotNodeHemisphere.h b/VirtualRobot/Nodes/RobotNodeHemisphere.h
index 29dac84bec20133532f8a11527f89958beef5333..91e32bd11ac6ccc7c90461ce9dacd19860746e06 100644
--- a/VirtualRobot/Nodes/RobotNodeHemisphere.h
+++ b/VirtualRobot/Nodes/RobotNodeHemisphere.h
@@ -140,16 +140,15 @@ namespace VirtualRobot
                 ) override;
 
         RobotNodePtr
-        _clone(
-                const RobotPtr newRobot,
-                const VisualizationNodePtr visualizationModel,
-                const CollisionModelPtr collisionModel,
-                CollisionCheckerPtr colChecker,
-                float scaling
-                ) override;
+        _clone(const RobotPtr newRobot,
+               const VisualizationNodePtr visualizationModel,
+               const CollisionModelPtr collisionModel,
+               CollisionCheckerPtr colChecker,
+               float scaling
+               ) override;
 
 
-    protected:
+    public:
 
         struct JointMath
         {
@@ -183,9 +182,6 @@ namespace VirtualRobot
         };
         std::optional<Second> second;
 
-
-        std::optional<XmlInfo> xmlInfo;
-
     };
 
 } // namespace VirtualRobot
diff --git a/VirtualRobot/data/robots/examples/HemisphereJoint/Joint3Hemisphere.xml b/VirtualRobot/data/robots/examples/HemisphereJoint/Joint3Hemisphere.xml
index 7cdabb74a2d65856611b5a7f909a84ea42811026..4b4ac38761e6f15152e6b4d7b475658bb66f3e83 100644
--- a/VirtualRobot/data/robots/examples/HemisphereJoint/Joint3Hemisphere.xml
+++ b/VirtualRobot/data/robots/examples/HemisphereJoint/Joint3Hemisphere.xml
@@ -1,26 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 
 <Robot Type="Simple3DoFRobotWithHemisphereJoint" StandardName="Simple3DoFRobotWithHemisphereJoint" RootNode="First">
-	
+
     <RobotNode name="First">
         <Visualization enable="true">
-			<CoordinateAxis enable="true" scaling="1" text="Root"/>
+            <CoordinateAxis enable="true" scaling="1" text="Root"/>
             <File type="Inventor">end.iv</File>
-		</Visualization>
+        </Visualization>
         <Child name="Joint1_Revolute"/>
-	</RobotNode>
+    </RobotNode>
 
     <RobotNode name="Joint1_Revolute">
-		<Joint type="revolute">
-			<Limits unit="degree" lo="-90" hi="90"/>
+        <Joint type="revolute">
+            <Limits unit="degree" lo="-90" hi="90"/>
             <Axis x="1" y="0" z="0"/>
         </Joint>
-		<Visualization enable="true">
+        <Visualization enable="true">
              <File type="Inventor">joint_01_base.iv</File>
-			 <UseAsCollisionModel/>
-		</Visualization>
+             <UseAsCollisionModel/>
+        </Visualization>
         <Child name="Joint2_Hemisphere_A"/>
-	</RobotNode>
+    </RobotNode>
 
     <RobotNode name="Joint2_Hemisphere_A">
         <Transform>
@@ -31,10 +31,10 @@
         </Joint>
         <Visualization enable="true">
              <File type="Inventor">joint_02_hemisphere_a.iv</File>
-			 <UseAsCollisionModel/>
+             <UseAsCollisionModel/>
         </Visualization>
         <Child name="Joint2_Hemisphere_B"/>
-	</RobotNode>
+    </RobotNode>
 
     <RobotNode name="Joint2_Hemisphere_B">
         <Joint type="hemisphere">
@@ -48,44 +48,49 @@
     </RobotNode>
 
     <RobotNode name="Joint3_Revolute">
-		<Transform>
+        <Transform>
             <Translation x="0" y="0" z="50" />
-		</Transform>
+        </Transform>
         <Joint type="revolute">
             <Axis x="1" y="0" z="0"/>
-			<Limits unit="degree" lo="-90" hi="90"/>
+            <Limits unit="degree" lo="-90" hi="90"/>
         </Joint>
-		<Visualization enable="true">
+        <Visualization enable="true">
              <File type="Inventor">joint_03_finger.iv</File>
-			 <UseAsCollisionModel/>
-		</Visualization>
+             <UseAsCollisionModel/>
+        </Visualization>
         <Child name="Last"/>
-	</RobotNode>
+    </RobotNode>
 
     <RobotNode name="Last">
-		<Transform>
+        <Transform>
             <Translation x="0" y="0" z="300" />
-		</Transform>
-		<Visualization enable="true">
+        </Transform>
+        <Visualization enable="true">
              <File type="Inventor">end.iv</File>
-			 <UseAsCollisionModel/>
-		</Visualization>
-	</RobotNode>
+             <UseAsCollisionModel/>
+        </Visualization>
+    </RobotNode>
 
     <RobotNodeSet name="AllJoints" kinematicRoot="Joint1_Revolute">
         <Node name="Joint1_Revolute"/>
         <Node name="Joint2_Hemisphere_A"/>
         <Node name="Joint2_Hemisphere_B"/>
         <Node name="Joint3_Revolute"/>
-	</RobotNodeSet>
+    </RobotNodeSet>
+
+    <RobotNodeSet name="HemisphereJoints" kinematicRoot="Joint2_Hemisphere_A" tcp="Last">
+        <Node name="Joint2_Hemisphere_A"/>
+        <Node name="Joint2_Hemisphere_B"/>
+    </RobotNodeSet>
 
-	<RobotNodeSet name="CollisionModel">
+    <RobotNodeSet name="CollisionModel">
         <Node name="First"/>
         <Node name="Joint1_Revolute"/>
         <Node name="Joint2_Hemisphere_A"/>
         <Node name="Joint2_Hemisphere_B"/>
         <Node name="Joint3_Revolute"/>
         <Node name="Last"/>
-	</RobotNodeSet>
+    </RobotNodeSet>
 
 </Robot>