diff --git a/source/RobotAPI/components/units/RobotUnit/JointControllers/JointController.h b/source/RobotAPI/components/units/RobotUnit/JointControllers/JointController.h
index bff6b97589e78b69e69d738de8c17a421eb85c70..8d0d3f16b7493276299653314beb44496467545f 100644
--- a/source/RobotAPI/components/units/RobotUnit/JointControllers/JointController.h
+++ b/source/RobotAPI/components/units/RobotUnit/JointControllers/JointController.h
@@ -83,8 +83,8 @@ namespace armarx
          */
         virtual StringVariantBaseMap publish(const DebugDrawerInterfacePrx& draw, const DebugObserverInterfacePrx& observer) const;
 
-    protected:
         ControlDevice& rtGetParent() const;
+    protected:
         //called by the owning ControlDevice
         /// @brief called when this JointController is run
         virtual void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) = 0;
diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.cpp b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.cpp
index 0564980702ec735105c95a668d56cc768b2e9b36..4e5a932446f4a424fa04f72f5abb6471e0df5767 100644
--- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.cpp
+++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.cpp
@@ -103,21 +103,51 @@ namespace armarx::RobotUnitModule
 
         static const std::vector<JointController*>& GetRequestedJointControllers(const ControlThread* p)
         {
+            //do NOT update here!
             return p->_module<ControlThreadDataBuffer>().controllersRequested.getReadBuffer().jointControllers;
         }
         static const std::vector<NJointControllerBase*>& GetRequestedNJointControllers(const ControlThread* p)
         {
+            //do NOT update here!
             return p->_module<ControlThreadDataBuffer>().controllersRequested.getReadBuffer().nJointControllers;
         }
         static const std::vector<std::size_t>& GetRequestedJointToNJointControllerAssignement(const ControlThread* p)
         {
+            //do NOT update here!
             return p->_module<ControlThreadDataBuffer>().controllersRequested.getReadBuffer().jointToNJointControllerAssignement;
         }
 
         static bool RequestedControllersChanged(const ControlThread* p)
         {
+            //only place allowed to update this buffer!
             return p->_module<ControlThreadDataBuffer>().controllersRequested.updateReadBuffer();
         }
+
+        /// Activate a joint controller from the rt loop (only works in switch mode RTThread)
+        static void RTSetJointController(ControlThread* p, JointController* c, std::size_t index)
+        {
+            ARMARX_CHECK_NOT_NULL(c);
+            //do NOT update here!
+            auto& readbuf = p->_module<ControlThreadDataBuffer>().controllersRequested._getNonConstReadBuffer();
+            auto& j = readbuf.jointControllers;
+            auto& assig = readbuf.jointToNJointControllerAssignement;
+            auto& nj = readbuf.nJointControllers;
+            ARMARX_CHECK_LESS(index, j.size());
+            const auto assigNJ = assig.at(index);
+            if (assigNJ != JointAndNJointControllers::Sentinel())
+            {
+                //an NJointController is activated! Deactivate it and reset all joit devs
+                nj.at(assigNJ) = nullptr;
+                for (std::size_t i = 0; i < assig.size(); ++i)
+                {
+                    if (assig.at(i) == assigNJ)
+                    {
+                        j.at(i) = j.at(i)->rtGetParent().rtGetJointStopMovementController();
+                    }
+                }
+            }
+            j.at(index) = c;
+        }
     };
     /**
      * \brief This class allows minimal access to private members of \ref Devices in a sane fashion for \ref ControlThread.
@@ -175,21 +205,17 @@ namespace armarx::RobotUnitModule
 
 namespace armarx::RobotUnitModule
 {
-    bool ControlThread::rtSwitchControllerSetup()
+    bool ControlThread::rtSwitchControllerSetup(SwitchControllerMode mode)
     {
         rtGetThreadTimingsSensorDevice().rtMarkRtSwitchControllerSetupStart();
         ARMARX_ON_SCOPE_EXIT { rtGetThreadTimingsSensorDevice().rtMarkRtSwitchControllerSetupEnd(); };
 
+        //store controllers activated before switching controllers
         {
-            const auto& actJC = rtGetActivatedJointControllers();
-            const auto& assig = rtGetActivatedJointToNJointControllerAssignement();
-            const auto& actNJC = rtGetActivatedNJointControllers();
-            for (std::size_t i = 0; i < actJC.size(); ++i)
-            {
-                preSwitchSetup_ActivatedJointControllers.at(i) = actJC.at(i);
-                preSwitchSetup_ActivatedJointToNJointControllerAssignement.at(i) = assig.at(i);
-                preSwitchSetup_ActivatedNJointControllers.at(i) = actNJC.at(i);
-            }
+            preSwitchSetup_ActivatedJointControllers = rtGetActivatedJointControllers();
+            preSwitchSetup_ActivatedJointToNJointControllerAssignement =
+                rtGetActivatedJointToNJointControllerAssignement();
+            preSwitchSetup_ActivatedNJointControllers = rtGetActivatedNJointControllers();
         }
 
         ARMARX_ON_SCOPE_EXIT
@@ -198,16 +224,18 @@ namespace armarx::RobotUnitModule
             const auto& assig = rtGetActivatedJointToNJointControllerAssignement();
             const auto& actNJC = rtGetActivatedNJointControllers();
 
+            //store controllers activated after switching controllers
+            {
+                postSwitchSetup_ActivatedJointControllers = actJC;
+                postSwitchSetup_ActivatedJointToNJointControllerAssignement = assig;
+                postSwitchSetup_ActivatedNJointControllers = actNJC;
+            }
+
             std::size_t numSyncNj = 0;
             std::size_t numAsyncNj = 0;
 
             for (std::size_t i = 0; i < actJC.size(); ++i)
             {
-
-                postSwitchSetup_ActivatedJointControllers.at(i) = actJC.at(i);
-                postSwitchSetup_ActivatedJointToNJointControllerAssignement.at(i) = assig.at(i);
-                postSwitchSetup_ActivatedNJointControllers.at(i) = actNJC.at(i);
-
                 if (actNJC.at(i) == nullptr)
                 {
                     continue;
@@ -258,11 +286,13 @@ namespace armarx::RobotUnitModule
             ARMARX_RT_LOGF_ERROR("Emergency Stop was activated because heartbeat is missing!").deactivateSpam(1);
         }
 
+        const bool rtThreadOverridesControl = mode != SwitchControllerMode::IceRequests;
+        // mode == RTThread                         -> control flow in hand of rt thread (ignoring estop)
         // !emergencyStop && !rtIsInEmergencyStop() -> normal control flow
         // !emergencyStop &&  rtIsInEmergencyStop() -> force switch to reactivate old ( + reset flag)
         //  emergencyStop && !rtIsInEmergencyStop() -> deactivate all + set flag
         //  emergencyStop &&  rtIsInEmergencyStop() -> nothing to do
-        if (emergencyStop)
+        if (emergencyStop && !rtThreadOverridesControl)
         {
             if (rtIsInEmergencyStop())
             {
@@ -302,6 +332,7 @@ namespace armarx::RobotUnitModule
         }
 
         if (
+            !rtThreadOverridesControl &&
             !ControlThreadDataBufferAttorneyForControlThread::RequestedControllersChanged(this) &&
             !rtIsInEmergencyStop()
         )
@@ -312,6 +343,8 @@ namespace armarx::RobotUnitModule
 
         //handle nJointCtrl
         {
+            const auto& allReqNJ = rtGetRequestedNJointControllers();
+            auto& allActdNJ = rtGetActivatedNJointControllers();
             //"merge"
             std::size_t n = rtGetControlDevices().size();
             std::size_t idxAct = 0;
@@ -319,12 +352,12 @@ namespace armarx::RobotUnitModule
             for (std::size_t i = 0; i < 2 * n; ++i)
             {
                 //skip nullptrs in act
-                while (idxAct < n && !rtGetActivatedNJointControllers().at(idxAct))
+                while (idxAct < n && !allActdNJ.at(idxAct))
                 {
                     ++idxAct;
                 }
-                const NJointControllerBasePtr& req = idxReq < n ? rtGetRequestedNJointControllers().at(idxReq) : nullptr; //may be null
-                const NJointControllerBasePtr& act = idxAct < n ? rtGetActivatedNJointControllers().at(idxAct) : nullptr; //may be null if it is the last
+                const NJointControllerBasePtr& req = idxReq < n ? allReqNJ.at(idxReq) : nullptr; //may be null
+                const NJointControllerBasePtr& act = idxAct < n ? allActdNJ.at(idxAct) : nullptr; //may be null if it is the last
                 const auto reqId = reinterpret_cast<std::uintptr_t>(req.get());
                 const auto actId = reinterpret_cast<std::uintptr_t>(act.get());
 
@@ -353,18 +386,20 @@ namespace armarx::RobotUnitModule
                     break;
                 }
             }
-            rtGetActivatedNJointControllers() = rtGetRequestedNJointControllers();
+            allActdNJ = allReqNJ;
         }
 
         //handle Joint Ctrl
         {
-            ARMARX_CHECK_EQUAL(rtGetRequestedJointControllers().size(), rtGetControlDevices().size());
+            const auto& allReqJ = rtGetRequestedJointControllers();
+            auto& allActdJ = rtGetActivatedJointControllers();
+            ARMARX_CHECK_EQUAL(allReqJ.size(), rtGetControlDevices().size());
             for (std::size_t i = 0; i < rtGetControlDevices().size(); ++i)
             {
                 auto& controlDevice = rtGetControlDevices().at(i);
-                const auto requestedJointCtrl = rtGetRequestedJointControllers().at(i);
+                const auto requestedJointCtrl = allReqJ.at(i);
                 controlDevice->rtSetActiveJointController(requestedJointCtrl);
-                rtGetActivatedJointControllers().at(i) = requestedJointCtrl;
+                allActdJ.at(i) = requestedJointCtrl;
             }
         }
         ControlThreadDataBufferAttorneyForControlThread::AcceptRequestedJointToNJointControllerAssignement(this);
@@ -1017,4 +1052,10 @@ namespace armarx::RobotUnitModule
     {
         rtRobot->setGlobalPose(gp, updateRobot);
     }
+
+    void ControlThread::rtSetJointController(JointController* c, std::size_t index)
+    {
+        ControlThreadDataBufferAttorneyForControlThread::RTSetJointController(this, c, index);
+
+    }
 }
diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.h b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.h
index f1adb85fd4e0e2422ee9b06ed6a6880104742e5e..0dcf52aa47c01ddcd9cacd970373cf5e3423edf1 100644
--- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.h
+++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.h
@@ -86,6 +86,16 @@ namespace armarx::RobotUnitModule
             RequestInactive,
             RequestNone,
         };
+    protected:
+        enum class SwitchControllerMode
+        {
+            /**
+             * @brief This modus is used if the RTThread is supported to control
+             * which controllers are running. (e.g. for Calibrating)
+             */
+            RTThread,
+            IceRequests
+        };
     public:
         /**
          * @brief Returns the singleton instance of this class
@@ -165,7 +175,7 @@ namespace armarx::RobotUnitModule
          * Changes can be caused by a new set of requested controllers or emergency stop
          * @return Whether active controllers were changed
          */
-        bool rtSwitchControllerSetup();
+        bool rtSwitchControllerSetup(SwitchControllerMode mode = SwitchControllerMode::IceRequests);
 
         /**
          * @brief Searches for the \ref NJointControllerBase responsible for the given \ref JointController and deactivates it.
@@ -265,6 +275,9 @@ namespace armarx::RobotUnitModule
         void rtSetEmergencyStopState(EmergencyStopState state);
 
         void rtSetRobotGlobalPose(const Eigen::Matrix4f& gp, bool updateRobot = true);
+
+        /// Activate a joint controller from the rt loop (only works in switch mode RTThread)
+        void rtSetJointController(JointController* c, std::size_t index);
         // //////////////////////////////////////////////////////////////////////////////////////// //
         // //////////////////////////////////// implementation //////////////////////////////////// //
         // //////////////////////////////////////////////////////////////////////////////////////// //
diff --git a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.cpp b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.cpp
index 295d8fddead24eabdb583287681971faf5b4ade7..c4dd0f79ab5d8b0b953f7933c2d8e9fcceddd89f 100644
--- a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.cpp
+++ b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.cpp
@@ -549,7 +549,7 @@ void EtherCATRTUnit::controlLoopRTThread()
 
         IceUtil::Time startTimestamp = armarx::rtNow();
 
-        while (taskRunning)
+        for (; taskRunning; ++ _iterationCount)
         {
             const IceUtil::Time loopStartTime = armarx::rtNow();
             rtGetRTThreadTimingsSensorDevice().rtMarkRtLoopStart();
@@ -569,6 +569,16 @@ void EtherCATRTUnit::controlLoopRTThread()
             auto cappedDeltaT = IceUtil::Time::microSeconds(boost::algorithm::clamp<long>(realDeltaT.toMicroSeconds(), 1, 2000)); // TODO: Make property
             //            TIMING_START(load);
             //            if (controllerLoaded)
+            if (rtIsCalibrating())
+            {
+                rtCalibrateActivateControlers();
+                rtSwitchControllerSetup(SwitchControllerMode::RTThread);
+                rtResetAllTargets();
+                _calibrationStatus = rtCalibrate();
+                rtHandleInvalidTargets();
+                rtRunJointControllers(currentPDOUpdateTimestamp, cappedDeltaT);
+            }
+            else
             {
                 RT_TIMING_START(RunControllers);
                 RT_TIMING_START(SwitchControllers);
diff --git a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.h b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.h
index 37a1b310014e67c708e97e5a33a8206520396688..dcda1ceb3cb6907ef3facd1ef7ce5301b6c502bf 100644
--- a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.h
+++ b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.h
@@ -146,6 +146,48 @@ namespace armarx
 
         void controlLoopRTThread();
 
+        enum class CalibrationStatus
+        {
+            Calibrating, Done
+        };
+        /**
+         * @brief Allows to switch controllers while calibrating
+         *
+         * use
+         * rtSetJointController(JointController* c, std::size_t index)
+         * to switch controllers
+         */
+        virtual void rtCalibrateActivateControlers()
+        {
+        }
+        /**
+         * @brief Hook to add calibration code
+         *
+         * This function is called in the rt loop! So you should not take too long!
+         *
+         * read sensors and write targets
+         * while calibrating return CalibrationStatus::Calibrating
+         * if done return CalibrationStatus::Done
+         *
+         * @return Whether done or still calibrating
+         */
+        virtual CalibrationStatus rtCalibrate()
+        {
+            return CalibrationStatus::Done;
+        }
+        bool rtIsCalibrating() const
+        {
+            return _calibrationStatus == CalibrationStatus::Calibrating;
+        }
+        std::uintmax_t getIterationCount()
+        {
+            return _iterationCount;
+        }
+    private:
+        CalibrationStatus _calibrationStatus = CalibrationStatus::Calibrating;
+        std::atomic_uintmax_t _iterationCount = 0;
+    protected:
+
         void computeInertiaTorque();
         DebugDrawerInterfacePrx dd;