From ad570ead987080f68290a037607bb3308dda4ef5 Mon Sep 17 00:00:00 2001
From: themarex <themarex@042f3d55-54a8-47e9-b7fb-15903f145c44>
Date: Tue, 3 Jun 2014 09:52:36 +0000
Subject: [PATCH] Control motors in robot by PID controller

git-svn-id: http://svn.code.sf.net/p/simox/code/trunk@599 042f3d55-54a8-47e9-b7fb-15903f145c44
---
 SimDynamics/CMakeLists.txt                    |   2 +
 .../BulletEngine/BulletRobot.cpp              | 326 +++++++++---------
 SimDynamics/DynamicsEngine/DynamicsRobot.cpp  |  59 +++-
 SimDynamics/DynamicsEngine/DynamicsRobot.h    |  24 +-
 SimDynamics/DynamicsEngine/DynamicsUtils.cpp  | 133 +++++++
 SimDynamics/DynamicsEngine/DynamicsUtils.h    | 113 ++++++
 .../SimDynamicsViewer/simDynamicsWindow.cpp   |   8 +-
 7 files changed, 466 insertions(+), 199 deletions(-)
 create mode 100644 SimDynamics/DynamicsEngine/DynamicsUtils.cpp
 create mode 100644 SimDynamics/DynamicsEngine/DynamicsUtils.h

diff --git a/SimDynamics/CMakeLists.txt b/SimDynamics/CMakeLists.txt
index 073b01b20..e0750d9e8 100644
--- a/SimDynamics/CMakeLists.txt
+++ b/SimDynamics/CMakeLists.txt
@@ -133,6 +133,7 @@ if (SimDynamics_DYNAMICSENGINE)
     DynamicsEngine/DynamicsObject.cpp
     DynamicsEngine/DynamicsEngine.cpp
     DynamicsEngine/DynamicsRobot.cpp
+    DynamicsEngine/DynamicsUtils.cpp
   )
     
   SET(INCLUDES
@@ -142,6 +143,7 @@ if (SimDynamics_DYNAMICSENGINE)
     DynamicsEngine/DynamicsObject.h
     DynamicsEngine/DynamicsEngine.h
     DynamicsEngine/DynamicsRobot.h
+    DynamicsEngine/DynamicsUtils.h
   )
  #    ${SimDynamics_SimoxDir}/VirtualRobot/definesVR.h
    
diff --git a/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.cpp b/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.cpp
index d8f929cc2..10a1a7040 100644
--- a/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.cpp
+++ b/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.cpp
@@ -899,214 +899,204 @@ void BulletRobot::actuateJoints(btScalar dt)
 
     int jointCounter = 0;
 
-	while (it!=actuationTargets.end())
-	{
-		//BulletObjectPtr drn;
-		//if (it->second.dynNode)
-		//	drn = boost::dynamic_pointer_cast<BulletObject>(it->second.dynNode);
-		//VR_ASSERT(drn);
+    while (it!=actuationTargets.end())
+    {
+        //BulletObjectPtr drn;
+        //if (it->second.dynNode)
+        //	drn = boost::dynamic_pointer_cast<BulletObject>(it->second.dynNode);
+        //VR_ASSERT(drn);
+
+        VelocityMotorController& controller = actuationControllers[it->first];
 
-		if (it->second.node->isRotationalJoint())
+        if (it->second.node->isRotationalJoint())
         {
             LinkInfo link = getLink(it->second.node);
+
+            const ActuationMode& actuation = it->second.actuation;
+
+            btScalar posTarget = btScalar(it->second.jointValueTarget + link.jointValueOffset);
+            btScalar posActual = btScalar(getJointAngle(it->first));
+            btScalar velocityTarget = it->second.jointVelocityTarget;
+
 #ifdef USE_BULLET_GENERIC_6DOF_CONSTRAINT
             boost::shared_ptr<btGeneric6DofConstraint> dof = boost::dynamic_pointer_cast<btGeneric6DofConstraint>(link.joint);
             VR_ASSERT(dof);
             btRotationalLimitMotor *m = dof->getRotationalLimitMotor(0);
             VR_ASSERT(m);
 
-            switch (it->second.actuation)
+            if (actuation.mode == 0) {
+                m->m_enableMotor = false;
+                continue;
+            }
+
+            m->m_enableMotor = true;
+
+            if (actuation.modes.position && actuation.modes.velocity)
             {
-                case ePosition:
-                {
-                    btScalar targ = btScalar(it->second.jointValueTarget+link.jointValueOffset);
-                    //btScalar act = btScalar(it->first->getJointValue());
-                    btScalar act = btScalar(getJointAngle(it->first));
-                    m->m_enableMotor = true;
-                    m->m_targetVelocity = (targ-act); // inverted joint dir?!
-                }
-                    break;
-                case eVelocity:
-                {
-                    m->m_enableMotor = true;
-                    m->m_targetVelocity = it->second.jointVelocityTarget;
-                    break;
-                }
-                case eTorque:
-                {
-                     m->m_enableMotor = true;
-                    m->m_targetVelocity = it->second.jointVelocityTarget;
-                    break;
-                }
-                case ePositionVelocity:
-                {
-                    btScalar pos = btScalar(getJointAngle(it->first));
-					float gain = 0.5;
-                    m->m_targetVelocity = it->second.jointVelocityTarget + gain*(it->second.jointPositionTarget - pos) / dt;
-                }
-                default:
-                    m->m_enableMotor = false;
+                m->m_targetVelocity = controller.update(posTarget - posActual, velocityTarget, actuation, dt);
             }
-#else
-			boost::shared_ptr<btHingeConstraint> hinge = boost::dynamic_pointer_cast<btHingeConstraint>(link.joint);
-            if (!hinge)
+            else if (actuation.modes.position)
             {
-                // hinge2 / universal joint
-                boost::shared_ptr<btUniversalConstraint> hinge2 = boost::dynamic_pointer_cast<btUniversalConstraint>(link.joint);
-                VR_ASSERT(hinge2);
-                btRotationalLimitMotor *m;
-                if (it->second.node==link.nodeJoint)
-                {
-                    m = hinge2->getRotationalLimitMotor(1); // second motor
-                } else
-                {
-                    VR_ASSERT(it->second.node==link.nodeJoint2);
-                    m = hinge2->getRotationalLimitMotor(2); // third motor
-                }
-                VR_ASSERT(m);
-                switch (it->second.actuation)
-                {
-                    case ePosition:
-                    {
-                        btScalar targ = btScalar(it->second.jointValueTarget+link.jointValueOffset);
-                        //btScalar act = btScalar(it->first->getJointValue());
-                        btScalar act = btScalar(getJointAngle(it->first));
-                        m->m_enableMotor = true;
-                        m->m_targetVelocity = (targ-act)*bulletMotorVelFactor;
-                    }
-                    break;
+                m->m_targetVelocity = controller.update(posTarget - posActual, 0, actuation, dt);
+            }
+            else if (actuation.modes.velocity)
+            {
+                m->m_targetVelocity = controller.update(0, velocityTarget, actuation, dt);
+            }
 
-                case eVelocity:
-                {
-                    m->m_enableMotor = true;
-                    m->m_targetVelocity = it->second.jointVelocityTarget;
-                    break;
-                }
-                case eTorque:
-                {
-                    m->m_enableMotor = true;
-                    m->m_targetVelocity = it->second.jointVelocityTarget;
-                    break;
-                }
-                case ePositionVelocity:
-                {
-                    btScalar pos = btScalar(getJointAngle(it->first));
-					float gain = 0.5;
-                    m->m_targetVelocity = it->second.jointVelocityTarget + gain*(it->second.jointValueTarget - pos) / dt;
-					break;
-                }
+            // FIXME torque based control is ignored
+#else
+            boost::shared_ptr<btHingeConstraint> hinge = boost::dynamic_pointer_cast<btHingeConstraint>(link.joint);
 
-                default:
-                    m->m_enableMotor = false;
-                }
-            } else
+            if (actuation.mode == 0) {
+                hinge->enableMotor(false);
+                continue;
+            }
+
+            if (it->second.node->getName() == "LeftLeg_Joint3")
             {
-                switch (it->second.actuation)
-                {
-                    case ePosition:
-                    {
-                        btScalar targ = btScalar(it->second.jointValueTarget+link.jointValueOffset);
-                        //btScalar act = btScalar(it->first->getJointValue());
-                        btScalar act = btScalar(getJointAngle(it->first));
-                        hinge->enableAngularMotor(true,(targ-act)*bulletMotorVelFactor,bulletMaxMotorImulse);
-                        //hinge->enableMotor(true);
-                        //hinge->setMotorTarget(it->second.jointValueTarget+link.jointValueOffset,dt);
-						break;
-                    }
-                    case eVelocity:
-                    {
-                        hinge->enableAngularMotor(true,it->second.jointVelocityTarget,bulletMaxMotorImulse);
+                std::cout <<  "Mode: " << (int) actuation.mode << " : " << posTarget << " " << posActual << std::endl;
+                controller.debug();
+            }
 
-                        jointCounter++;
-                        //cout << "jointVelocityTarget for joint " << it->second.node->getName() << " :" << it->second.jointVelocityTarget << endl;
+            if (actuation.modes.position && actuation.modes.velocity)
+            {
+                hinge->enableAngularMotor(true,
+                        controller.update(posTarget - posActual, velocityTarget, actuation, dt),
+                        bulletMaxMotorImulse);
 
+            }
+            else if (actuation.modes.position)
+            {
+                hinge->enableAngularMotor(true,
+                        controller.update(posTarget - posActual, 0.0, actuation, dt),
+                        bulletMaxMotorImulse);
+            }
+            else if (actuation.modes.velocity)
+            {
+                hinge->enableAngularMotor(true,
+                        controller.update(0.0, velocityTarget, actuation, dt),
+                        bulletMaxMotorImulse);
+            }
+            // FIXME this bypasses the controller (and doesn't work..)
+            else if (actuation.modes.torque)
+            {
+                //Only first try (using torques as velocity targets...)
+                hinge->enableAngularMotor(true,it->second.jointTorqueTarget,bulletMaxMotorImulse);
+                //cout << "jointTorqueTarget for joint " << it->second.node->getName() << " :" << it->second.jointTorqueTarget << endl;
 
-						break;
-                    }
-                    case eTorque:
-                    {
+                /*
+
+                //=======
+                //Here is some code that sets torques directly to the finger joints (bypassing the Bullet motors).
+                //Unfortunately, this does not seem to work, so far.
+                //1. With hinge->enableAngularMotor(true,...), the fingers do not move at all.
+                //2. Wtih hinge->enableAngularMotor(false,...), the fingers are simply actuated by gravity...
+                //=======
 
-                        //Only first try (using torques as velocity targets...)
-                        hinge->enableAngularMotor(true,it->second.jointTorqueTarget,bulletMaxMotorImulse);
-                        //cout << "jointTorqueTarget for joint " << it->second.node->getName() << " :" << it->second.jointTorqueTarget << endl;
+                //cout << " === == === === === > BulletRobot (hinge !): eTorque NEW!!! ====" << endl;
 
-                        /*
+                cout << "Disabling Angular Motor... " << endl;
+                hinge->enableAngularMotor(false,0,bulletMaxMotorImulse);
 
-                        //=======
-                        //Here is some code that sets torques directly to the finger joints (bypassing the Bullet motors).
-                        //Unfortunately, this does not seem to work, so far.
-                        //1. With hinge->enableAngularMotor(true,...), the fingers do not move at all.
-                        //2. Wtih hinge->enableAngularMotor(false,...), the fingers are simply actuated by gravity...
-                        //=======
 
-                        //cout << " === == === === === > BulletRobot (hinge !): eTorque NEW!!! ====" << endl;
+                cout << "=== === === jointCounter: " << jointCounter << " === === ===" << endl;
 
-                        cout << "Disabling Angular Motor... " << endl;
-                        hinge->enableAngularMotor(false,0,bulletMaxMotorImulse);
+                //get the links that are connected by the hinge.
+                btRigidBody rbA = hinge->getRigidBodyA();
+                btRigidBody rbB = hinge->getRigidBodyB();
 
+                //get joint axis from the hinge ...
+                btMatrix3x3 rbAFrameBasis = hinge->getAFrame().getBasis();
+                //z-Achse ist Gelenkachse? (das steht in btHingeConstraint.h; setAxis() bzw. struct btHingeConstraintDoubleData)
+                btVector3 hingeAxis = rbAFrameBasis.getColumn(2);
 
-                        cout << "=== === === jointCounter: " << jointCounter << " === === ===" << endl;
+                //calc 3dim torque by multiplication with joint axis!
+                btVector3 resTorqueA = hingeAxis * it->second.jointTorqueTarget;
 
-                        //get the links that are connected by the hinge.
-                        btRigidBody rbA = hinge->getRigidBodyA();
-                        btRigidBody rbB = hinge->getRigidBodyB();
 
-                        //get joint axis from the hinge ...
-                        btMatrix3x3 rbAFrameBasis = hinge->getAFrame().getBasis();
-                        //z-Achse ist Gelenkachse? (das steht in btHingeConstraint.h; setAxis() bzw. struct btHingeConstraintDoubleData)
-                        btVector3 hingeAxis = rbAFrameBasis.getColumn(2);
+                //TODO (maybe): calc "realistic" torque to be applied (using dt)
 
-                        //calc 3dim torque by multiplication with joint axis!
-                        btVector3 resTorqueA = hingeAxis * it->second.jointTorqueTarget;
+                //apply torques to the bodies connected by the joint
+                rbA.applyTorqueImpulse(resTorqueA);
+                rbB.applyTorqueImpulse(-resTorqueA);
 
+                //DEBUG OUT:
+                //--> TODO!
+                //cout << "==== ==== ==== DEBUG OUT: ==== ==== ==== " << endl;
+                cout << "rbAFrameBasis:" << endl;
+                btVector3 row0 = rbAFrameBasis.getRow(0);
+                btVector3 row1 = rbAFrameBasis.getRow(1);
+                btVector3 row2 = rbAFrameBasis.getRow(2);
+                cout << row0.getX() << " " << row0.getY() << " " << row0.getZ() << endl;
+                cout << row1.getX() << " " << row1.getY() << " " << row1.getZ() << endl;
+                cout << row2.getX() << " " << row2.getY() << " " << row2.getZ() << endl;
 
-                        //TODO (maybe): calc "realistic" torque to be applied (using dt)
+                cout << "hingeAxis: " << hingeAxis.getX() << " " << hingeAxis.getY() << " " << hingeAxis.getZ() << endl;
+                cout << "resTorqueA: " << resTorqueA.getX() << " " << resTorqueA.getY() << " " << resTorqueA.getZ() << endl;
 
-                        //apply torques to the bodies connected by the joint
-                        rbA.applyTorqueImpulse(resTorqueA);
-                        rbB.applyTorqueImpulse(-resTorqueA);
 
-                        //DEBUG OUT:
-                        //--> TODO!
-                        //cout << "==== ==== ==== DEBUG OUT: ==== ==== ==== " << endl;
-                        cout << "rbAFrameBasis:" << endl;
-                        btVector3 row0 = rbAFrameBasis.getRow(0);
-                        btVector3 row1 = rbAFrameBasis.getRow(1);
-                        btVector3 row2 = rbAFrameBasis.getRow(2);
-                        cout << row0.getX() << " " << row0.getY() << " " << row0.getZ() << endl;
-                        cout << row1.getX() << " " << row1.getY() << " " << row1.getZ() << endl;
-                        cout << row2.getX() << " " << row2.getY() << " " << row2.getZ() << endl;
 
-                        cout << "hingeAxis: " << hingeAxis.getX() << " " << hingeAxis.getY() << " " << hingeAxis.getZ() << endl;
-                        cout << "resTorqueA: " << resTorqueA.getX() << " " << resTorqueA.getY() << " " << resTorqueA.getZ() << endl;
+                jointCounter++;
 
+*/
+            }
 
+            // Universal constraint instead of hinge constraint
+            /*
+               boost::shared_ptr<btUniversalConstraint> hinge = boost::dynamic_pointer_cast<btUniversalConstraint>(link.joint);
+               VR_ASSERT(hinge2);
+               btRotationalLimitMotor *m;
+               if (it->second.node==link.nodeJoint)
+               {
+               m = hinge2->getRotationalLimitMotor(1); // second motor
+               } else
+               {
+               VR_ASSERT(it->second.node==link.nodeJoint2);
+               m = hinge2->getRotationalLimitMotor(2); // third motor
+               }
+               VR_ASSERT(m);
+               switch (it->second.actuation)
+               {
+               case ePosition:
+               {
+               btScalar targ = btScalar(it->second.jointValueTarget+link.jointValueOffset);
+            //btScalar act = btScalar(it->first->getJointValue());
+            btScalar act = btScalar(getJointAngle(it->first));
+            m->m_enableMotor = true;
+            m->m_targetVelocity = (targ-act)*bulletMotorVelFactor;
+            }
+            break;
 
-                        jointCounter++;
+            case eVelocity:
+            {
+            m->m_enableMotor = true;
+            m->m_targetVelocity = it->second.jointVelocityTarget;
+            break;
+            }
+            case eTorque:
+            {
+            m->m_enableMotor = true;
+            m->m_targetVelocity = it->second.jointVelocityTarget;
+            break;
+            }
+            case ePositionVelocity:
+            {
+            btScalar pos = btScalar(getJointAngle(it->first));
+            float gain = 0.5;
+            m->m_targetVelocity = it->second.jointVelocityTarget + gain*(it->second.jointValueTarget - pos) / dt;
+            break;
+            }
 
-                        */
-						break;
-                    }
-					case ePositionVelocity:
-					{
-						btScalar pos = btScalar(getJointAngle(it->first));
-						float gain = 0.5;
-						float target = it->second.jointVelocityTarget + gain*(it->second.jointValueTarget - pos) / dt;
-                        hinge->enableAngularMotor(true, target, bulletMaxMotorImulse);
-
-                        jointCounter++;
-						break;
-					}
-
-                    default:
-                        hinge->enableMotor(false);
-                }
+            default:
+            m->m_enableMotor = false;
             }
+            */
 #endif
-		}
+        }
 
-		it++;
-	}
+        it++;
+    }
     setPoseNonActuatedRobotNodes();
 }
 
diff --git a/SimDynamics/DynamicsEngine/DynamicsRobot.cpp b/SimDynamics/DynamicsEngine/DynamicsRobot.cpp
index 569109a2c..e106522f6 100644
--- a/SimDynamics/DynamicsEngine/DynamicsRobot.cpp
+++ b/SimDynamics/DynamicsEngine/DynamicsRobot.cpp
@@ -90,14 +90,19 @@ void DynamicsRobot::actuateNode( VirtualRobot::RobotNodePtr node, float jointVal
 
 
     robotNodeActuationTarget target;
-    target.actuation = ePosition;
+    target.actuation.modes.position = 1;
     target.node = node;
     target.jointValueTarget = jointValue;
-    target.jointVelocityTarget = 0.0f;
-    target.jointTorqueTarget = 0.0f;
-    //target.dynNode = dnyRN;
 
     actuationTargets[node] = target;
+    if (actuationControllers.find(node) == actuationControllers.end())
+    {
+        actuationControllers[node] = VelocityMotorController();
+    }
+    else
+    {
+        actuationControllers[node].reset();
+    }
 }
 
 void DynamicsRobot::actuateNode( VirtualRobot::RobotNodePtr node, float jointValue , float jointVelocity)
@@ -107,20 +112,29 @@ void DynamicsRobot::actuateNode( VirtualRobot::RobotNodePtr node, float jointVal
     VR_ASSERT(robot->hasRobotNode(node));
 
     robotNodeActuationTarget target;
-    target.actuation = ePositionVelocity;
+    target.actuation.modes.position = 1;
+    target.actuation.modes.velocity = 1;
     target.node = node;
     target.jointValueTarget = jointValue;
     target.jointVelocityTarget = jointVelocity;
-    target.jointTorqueTarget = 0.0f;
 
     actuationTargets[node] = target;
+    if (actuationControllers.find(node) == actuationControllers.end())
+    {
+        actuationControllers[node] = VelocityMotorController();
+    }
+    else
+    {
+        actuationControllers[node].reset();
+    }
 }
 
 void DynamicsRobot::actuateNodeVel(const std::string &node, float jointVelocity )
 {
     VR_ASSERT(robot);
     VR_ASSERT(robot->hasRobotNode(node));
-    actuateNodeVel(robot->getRobotNode(node),jointVelocity);
+
+    actuateNodeVel(robot->getRobotNode(node), jointVelocity);
 }
 
 void DynamicsRobot::actuateNodeVel( VirtualRobot::RobotNodePtr node, float jointVelocity )
@@ -135,14 +149,19 @@ void DynamicsRobot::actuateNodeVel( VirtualRobot::RobotNodePtr node, float joint
     //DynamicsObjectPtr dnyRN = getDynamicsRobotNode(node);
 
     robotNodeActuationTarget target;
-    target.actuation = eVelocity;
+    target.actuation.modes.velocity = 1;
     target.node = node;
-    target.jointValueTarget = 0.0f;
     target.jointVelocityTarget = jointVelocity;
-    target.jointTorqueTarget = 0.0f;
-    //target.dynNode = dnyRN;
 
     actuationTargets[node] = target;
+    if (actuationControllers.find(node) == actuationControllers.end())
+    {
+        actuationControllers[node] = VelocityMotorController();
+    }
+    else
+    {
+        actuationControllers[node].reset();
+    }
 }
 
 void DynamicsRobot::actuateNodeTorque(const std::string &node, float jointTorque )
@@ -164,14 +183,20 @@ void DynamicsRobot::actuateNodeTorque( VirtualRobot::RobotNodePtr node, float jo
     //DynamicsObjectPtr dnyRN = getDynamicsRobotNode(node);
 
     robotNodeActuationTarget target;
-    target.actuation = eTorque;
+    target.actuation.modes.torque = 1;
     target.node = node;
-    target.jointValueTarget = 0.0f;
-    target.jointVelocityTarget = 0.0f;
     target.jointTorqueTarget = jointTorque;
     //target.dynNode = dnyRN;
 
     actuationTargets[node] = target;
+    if (actuationControllers.find(node) == actuationControllers.end())
+    {
+        actuationControllers[node] = VelocityMotorController();
+    }
+    else
+    {
+        actuationControllers[node].reset();
+    }
 }
 
 void DynamicsRobot::disableNodeActuation( VirtualRobot::RobotNodePtr node )
@@ -181,7 +206,7 @@ void DynamicsRobot::disableNodeActuation( VirtualRobot::RobotNodePtr node )
 		actuationTargets.erase(node);
 	}
 }
-void DynamicsRobot::enableActuation(JointActuation mode)
+void DynamicsRobot::enableActuation(ActuationMode mode)
 {
 	std::map<VirtualRobot::RobotNodePtr, robotNodeActuationTarget>::iterator it = actuationTargets.begin();
 	while (it!=actuationTargets.end())
@@ -195,7 +220,7 @@ void DynamicsRobot::disableActuation()
 	std::map<VirtualRobot::RobotNodePtr, robotNodeActuationTarget>::iterator it = actuationTargets.begin();
 	while (it!=actuationTargets.end())
 	{
-        it->second.actuation = eDisabled;
+        it->second.actuation.mode = 0;
 		it++;
 	}
 }
@@ -209,7 +234,7 @@ bool DynamicsRobot::isNodeActuated( VirtualRobot::RobotNodePtr node )
 	VR_ASSERT(node);
 	if (actuationTargets.find(node) == actuationTargets.end())
 		return false;
-    return actuationTargets[node].actuation!=eDisabled;
+    return actuationTargets[node].actuation.mode != 0;
 }
 
 float DynamicsRobot::getNodeTarget( VirtualRobot::RobotNodePtr node )
diff --git a/SimDynamics/DynamicsEngine/DynamicsRobot.h b/SimDynamics/DynamicsEngine/DynamicsRobot.h
index 9d13bbbdd..0f93b39c8 100644
--- a/SimDynamics/DynamicsEngine/DynamicsRobot.h
+++ b/SimDynamics/DynamicsEngine/DynamicsRobot.h
@@ -25,6 +25,7 @@
 
 #include "../SimDynamics.h"
 #include "DynamicsObject.h"
+#include "DynamicsUtils.h"
 #include <VirtualRobot/Robot.h>
 #include <VirtualRobot/Nodes/Sensor.h>
 
@@ -58,15 +59,6 @@ public:
 	*/
 	DynamicsObjectPtr getDynamicsRobotNode(VirtualRobot::RobotNodePtr node);
 
-    enum JointActuation
-    {
-        eDisabled,
-        ePosition,
-        eVelocity,
-        ePositionVelocity,
-        eTorque
-    };
-
 	/*!
 		Enable joint actuation for given node.
 	*/
@@ -80,7 +72,7 @@ public:
     virtual void disableNodeActuation(VirtualRobot::RobotNodePtr node);
 	virtual bool isNodeActuated(VirtualRobot::RobotNodePtr node);
 	virtual float getNodeTarget(VirtualRobot::RobotNodePtr node);
-    virtual void enableActuation(JointActuation mode);
+    virtual void enableActuation(ActuationMode mode);
 	virtual void disableActuation();
 
 	/*!
@@ -108,8 +100,6 @@ public:
 
     virtual void setGlobalPose(Eigen::Matrix4f &gp);
 
-
-
 protected:
 
 	virtual void createDynamicsNode(VirtualRobot::RobotNodePtr node);
@@ -117,15 +107,23 @@ protected:
 
 	struct robotNodeActuationTarget
 	{
+        robotNodeActuationTarget()
+        : jointValueTarget(0)
+        , jointVelocityTarget(0)
+        , jointTorqueTarget(0)
+        {
+			actuation.mode = 0;
+		}
         float jointValueTarget;
         float jointVelocityTarget;
         float jointTorqueTarget;
         VirtualRobot::RobotNodePtr node;
 		//DynamicsObjectPtr dynNode; // if node is a joint without model, there is no dyn node!
-        JointActuation actuation;
+        ActuationMode actuation;
 	};
 
 	std::map<VirtualRobot::RobotNodePtr, robotNodeActuationTarget> actuationTargets;
+	std::map<VirtualRobot::RobotNodePtr, VelocityMotorController> actuationControllers;
 
 	VirtualRobot::RobotPtr robot;
 
diff --git a/SimDynamics/DynamicsEngine/DynamicsUtils.cpp b/SimDynamics/DynamicsEngine/DynamicsUtils.cpp
new file mode 100644
index 000000000..6ef55800f
--- /dev/null
+++ b/SimDynamics/DynamicsEngine/DynamicsUtils.cpp
@@ -0,0 +1,133 @@
+
+#include "DynamicsUtils.h"
+
+#include <iostream>
+
+namespace SimDynamics {
+PIDController::PIDController(float gainP, float gainI, float gainD)
+: gainP(gainP)
+, gainI(gainI)
+, gainD(gainD)
+, errorSum(0)
+, lastError(0)
+{
+}
+
+float PIDController::update(float error, float dt)
+{
+	float p = error * gainP;
+	errorSum += error * dt;
+	float i = errorSum * gainI;
+	float d = (error - lastError)/dt * gainD;
+	lastError = error;
+
+	float output = (p + i + d);
+	lastOutput = output;
+	return output;
+}
+
+void PIDController::reset()
+{
+	errorSum = 0.0;
+	lastError = 0.0;
+}
+
+void PIDController::debug()
+{
+	std::cout << "error sum: " << errorSum
+			  << " last error: " << lastError
+			  << " last output: " << lastOutput
+			  << std::endl;
+}
+
+TorqueMotorController::TorqueMotorController()
+: positionController(0.5, 0.05, 0.0)
+, velocityController(1.0, 0.05, 0.0)
+, torqueController(1.0, 0.05, 0.0)
+{
+}
+
+TorqueMotorController::TorqueMotorController(const PIDController& positionController,
+				const PIDController& velocityController,
+				const PIDController& torqueController)
+: positionController(positionController)
+, velocityController(velocityController)
+, torqueController(torqueController)
+{
+}
+
+
+float TorqueMotorController::update(float positionError, float velocityError, float torqueError, ActuationMode actuation, float dt)
+{
+	float posUpdate = 0.0;
+	float velUpdate = 0.0;
+	float torqueUpdate = 0.0;
+	if (actuation.modes.position)
+		posUpdate = positionController.update(positionError, dt);
+	else
+		positionController.reset();
+
+	if (actuation.modes.velocity)
+		velUpdate = velocityController.update(velocityError + posUpdate, dt);
+	else
+		velocityController.reset();
+
+	if (actuation.modes.torque)
+		torqueUpdate = torqueController.update(torqueError + velUpdate, dt);
+	else
+		torqueController.reset();
+
+	if (actuation.modes.position && actuation.modes.velocity && actuation.modes.torque)
+		return torqueUpdate;
+	if (actuation.modes.position && actuation.modes.velocity)
+		return velUpdate;
+	if (actuation.modes.position)
+		return posUpdate;
+	if (actuation.modes.velocity)
+		return velUpdate;
+	if (actuation.modes.torque)
+		return torqueUpdate;
+
+	return 0.0f;
+}
+
+VelocityMotorController::VelocityMotorController()
+: positionController(100.0, 50.0, 0.0)
+{
+}
+
+VelocityMotorController::VelocityMotorController(const PIDController& positionController)
+: positionController(positionController)
+{
+}
+
+
+float VelocityMotorController::update(float positionError, float targetVelocity, ActuationMode actuation, float dt)
+{
+	float posUpdate = 0.0;
+	if (actuation.modes.position)
+		posUpdate = positionController.update(positionError, dt);
+	else
+		positionController.reset();
+
+	float output = 0.0f;
+	if (actuation.modes.position && actuation.modes.velocity)
+		output = posUpdate + targetVelocity;
+	else if (actuation.modes.position)
+		output = posUpdate;
+	else if (actuation.modes.velocity)
+		output = targetVelocity;
+
+	return output;
+}
+
+void VelocityMotorController::reset()
+{
+	positionController.reset();
+}
+
+void VelocityMotorController::debug()
+{
+	positionController.debug();
+}
+}
diff --git a/SimDynamics/DynamicsEngine/DynamicsUtils.h b/SimDynamics/DynamicsEngine/DynamicsUtils.h
new file mode 100644
index 000000000..d5d3d5686
--- /dev/null
+++ b/SimDynamics/DynamicsEngine/DynamicsUtils.h
@@ -0,0 +1,113 @@
+#ifndef __DYNAMICS_UTILS__H__
+#define __DYNAMICS_UTILS__H__
+
+#include <iostream>
+
+namespace SimDynamics {
+class PIDController {
+public:
+	PIDController(float gainP, float gainI, float gainD);
+
+	float update(float error, float dt);
+
+	void reset();
+
+	void debug();
+
+private:
+	float gainP;
+	float gainI;
+	float gainD;
+	float errorSum;
+	float lastError;
+	float lastOutput;
+};
+
+// use bit field because enums are a pain
+union ActuationMode {
+	struct {
+		unsigned char position:1;
+		unsigned char velocity:1;
+		unsigned char torque:1;
+	} modes;
+	unsigned char mode;
+};
+
+/**
+ * For *torque* based motors.
+ *
+ * Position only:
+ * position error --> [PID] --> joint
+ *
+ * Velocity only:
+ * velocity error --> [PID] --> joint
+ *
+ * Torque only:
+ * torque error --> [PID] --> joint
+ *
+ * Position + Velocity:
+ *                       velocity error
+ *                             |
+ *                             v
+ * position error -> [PID] -> (+) -> [PID] -> joint
+ *
+ * Position + Velocity + Torque:
+ *                       velocity error  torque error
+ *                             |               |
+ *                             v               v
+ * position error -> [PID] -> (+) -> [PID] -> (+) -> [PID] -> joint
+ *
+ */
+class TorqueMotorController{
+public:
+	TorqueMotorController();
+	TorqueMotorController(const PIDController& positionController,
+					const PIDController& velocityController,
+					const PIDController& torqueController);
+
+	float update(float positionError, float velocityError, float torqueError, ActuationMode actuation, float dt);
+
+private:
+	PIDController positionController;
+	PIDController velocityController;
+	PIDController torqueController;
+};
+
+/**
+ * For *velocity* based motors (where target velocity == actual velocity).
+ * We use this for Bullet motors.
+ *
+ * Note: Torque is ignored. This controler returns *velocities*.
+ *
+ * Position only:
+ * position error --> [PID] --> joint
+ *
+ * Velocity only:
+ * target velocity --> joint
+ *
+ * Position + Velocity:
+ *                       target velocity
+ *                             |
+ *                             v
+ * position error -> [PID] -> (+) -> joint
+ *
+ */
+class VelocityMotorController {
+public:
+	VelocityMotorController();
+
+	VelocityMotorController(const PIDController& positionController);
+
+
+	float update(float positionError, float targetVelocity, ActuationMode actuation, float dt);
+
+	void reset();
+
+	void debug();
+
+private:
+	PIDController positionController;
+};
+}
+
+#endif
diff --git a/SimDynamics/examples/SimDynamicsViewer/simDynamicsWindow.cpp b/SimDynamics/examples/SimDynamicsViewer/simDynamicsWindow.cpp
index c6623f99c..c71c0ad82 100644
--- a/SimDynamics/examples/SimDynamicsViewer/simDynamicsWindow.cpp
+++ b/SimDynamics/examples/SimDynamicsViewer/simDynamicsWindow.cpp
@@ -173,9 +173,15 @@ void SimDynamicsWindow::actuation()
 	bool actuate = UI.checkBoxActuation->checkState() == Qt::Checked;
 
 	if (actuate)
-        dynamicsRobot->enableActuation(SimDynamics::DynamicsRobot::ePosition);
+    {
+        ActuationMode actuation;
+        actuation.modes.position = 1;
+        dynamicsRobot->enableActuation(actuation);
+    }
 	else
+    {
 		dynamicsRobot->disableActuation();
+    }
 }
 
 void SimDynamicsWindow::buildVisualization()
-- 
GitLab