From 7b963a537ce75679bfbd657928b4dccf51dd9ebb Mon Sep 17 00:00:00 2001
From: mirkowaechter <mirkowaechter@042f3d55-54a8-47e9-b7fb-15903f145c44>
Date: Mon, 2 Dec 2013 16:30:46 +0000
Subject: [PATCH] Added ForceTorqueSensor to Dynamic Simulation and
 VirtualRobot

git-svn-id: http://svn.code.sf.net/p/simox/code/trunk@447 042f3d55-54a8-47e9-b7fb-15903f145c44
---
 .../BulletEngine/BulletCoinQtViewer.cpp       |   3 +-
 .../BulletEngine/BulletOpenGLViewer.cpp       |   1 +
 .../BulletEngine/BulletRobot.cpp              |  99 +++++++++++++++-
 .../DynamicsEngine/BulletEngine/BulletRobot.h |  40 ++++++-
 SimDynamics/DynamicsEngine/DynamicsRobot.cpp  |   2 +
 SimDynamics/DynamicsEngine/DynamicsRobot.h    |   7 +-
 VirtualRobot/CMakeLists.txt                   |   4 +
 VirtualRobot/Nodes/ForceTorqueSensor.cpp      |  63 ++++++++++
 VirtualRobot/Nodes/ForceTorqueSensor.h        |  90 ++++++++++++++
 .../Nodes/ForceTorqueSensorFactory.cpp        | 112 ++++++++++++++++++
 VirtualRobot/Nodes/ForceTorqueSensorFactory.h |  62 ++++++++++
 .../data/robots/ArmarIII/ArmarIII-LeftArm.xml |   3 +-
 .../robots/ArmarIII/ArmarIII-RightArm.xml     |   2 +-
 13 files changed, 477 insertions(+), 11 deletions(-)
 create mode 100644 VirtualRobot/Nodes/ForceTorqueSensor.cpp
 create mode 100644 VirtualRobot/Nodes/ForceTorqueSensor.h
 create mode 100644 VirtualRobot/Nodes/ForceTorqueSensorFactory.cpp
 create mode 100644 VirtualRobot/Nodes/ForceTorqueSensorFactory.h

diff --git a/SimDynamics/DynamicsEngine/BulletEngine/BulletCoinQtViewer.cpp b/SimDynamics/DynamicsEngine/BulletEngine/BulletCoinQtViewer.cpp
index 40e7d36e0..ce4ffa992 100644
--- a/SimDynamics/DynamicsEngine/BulletEngine/BulletCoinQtViewer.cpp
+++ b/SimDynamics/DynamicsEngine/BulletEngine/BulletCoinQtViewer.cpp
@@ -21,7 +21,7 @@ BulletCoinQtViewer::BulletCoinQtViewer(DynamicsWorldPtr world)
 	bulletMaxSubSteps = 50;
     enablePhysicsUpdates = true;
 
-	const float TIMER_MS = 30.0f;
+    const float TIMER_MS = 30.0f;
 
 	SIMDYNAMICS_ASSERT(world);
 
@@ -176,6 +176,7 @@ void BulletCoinQtViewer::updateMotors(float dt)
 	for (size_t i=0;i<robots.size();i++)
 	{
 		robots[i]->actuateJoints(dt);
+        robots[i]->updateSensors();
 	}
 }
 
diff --git a/SimDynamics/DynamicsEngine/BulletEngine/BulletOpenGLViewer.cpp b/SimDynamics/DynamicsEngine/BulletEngine/BulletOpenGLViewer.cpp
index 7df45327e..4576b8896 100644
--- a/SimDynamics/DynamicsEngine/BulletEngine/BulletOpenGLViewer.cpp
+++ b/SimDynamics/DynamicsEngine/BulletEngine/BulletOpenGLViewer.cpp
@@ -56,6 +56,7 @@ void BulletOpenGLViewer::updateMotors(float dt)
 	for (size_t i=0;i<robots.size();i++)
 	{
 		robots[i]->actuateJoints(dt);
+        robots[i]->updateSensors();
 	}
 }
 
diff --git a/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.cpp b/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.cpp
index ac32d0e3e..a13694a7d 100644
--- a/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.cpp
+++ b/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.cpp
@@ -9,6 +9,7 @@
 #include <VirtualRobot/Nodes/RobotNodePrismatic.h>
 #include <VirtualRobot/Nodes/RobotNodeFixed.h>
 #include <VirtualRobot/Nodes/RobotNodeRevolute.h>
+#include <VirtualRobot/Nodes/ForceTorqueSensor.h>
 
 // either hinge or generic6DOF constraints can be used
 //#define USE_BULLET_GENERIC_6DOF_CONSTRAINT
@@ -29,6 +30,23 @@ BulletRobot::BulletRobot(VirtualRobot::RobotPtr rob, bool enableJointMotors)
 	bulletMaxMotorImulse = 50.0f; 
     bulletMotorVelFactor = 10.0f;
 	buildBulletModels(enableJointMotors);
+
+    // activate force torque sensors
+    std::vector<SensorPtr>::iterator it = sensors.begin();
+    for(; it != sensors.end(); it++)
+    {
+//        SensorPtr sensor = *it;
+        ForceTorqueSensorPtr ftSensor = boost::dynamic_pointer_cast<ForceTorqueSensor>(*it);
+        if(ftSensor)
+        {
+            VirtualRobot::RobotNodePtr node = ftSensor->getRobotNode();//boost::dynamic_pointer_cast<VirtualRobot::RobotNode>(ftSensor->getParent());
+            THROW_VR_EXCEPTION_IF(!node, "parent of sensor could not be casted to RobotNode")
+
+            const LinkInfo& link = getLink(node);
+            enableForceTorqueFeedback(link);
+            std::cout << "Found force torque sensor: " << node->getName() << std::endl;
+        }
+    }
 }
 	
 BulletRobot::~BulletRobot()
@@ -980,6 +998,27 @@ void BulletRobot::actuateJoints(float dt)
     setPoseNonActuatedRobotNodes();
 }
 
+void BulletRobot::updateSensors()
+{
+    std::vector<SensorPtr>::iterator it = sensors.begin();
+    for(; it != sensors.end(); it++)
+    {
+//        SensorPtr sensor = *it;
+        ForceTorqueSensorPtr ftSensor = boost::dynamic_pointer_cast<ForceTorqueSensor>(*it);
+        if(ftSensor)
+        {
+            VirtualRobot::RobotNodePtr node = ftSensor->getRobotNode();
+                    //boost::dynamic_pointer_cast<VirtualRobot::RobotNode>(ftSensor->getParent());
+            THROW_VR_EXCEPTION_IF(!node, "parent of sensor could not be casted to RobotNode")
+
+            const LinkInfo& link = getLink(node);
+            Eigen::VectorXf forceTorques = getJointForceTorqueGlobal(link);
+            ftSensor->updateSensors(forceTorques);
+            std::cout << "Updating force torque sensor: " << node->getName() << ": " << forceTorques << std::endl;
+        }
+    }
+}
+
 BulletRobot::LinkInfo BulletRobot::getLink( VirtualRobot::RobotNodePtr node )
 {
 	for (size_t i=0;i<links.size();i++)
@@ -1223,6 +1262,29 @@ float BulletRobot::getNodeTarget( VirtualRobot::RobotNodePtr node )
 
 }
 
+Eigen::Vector3f BulletRobot::getJointTorques(RobotNodePtr rn)
+{
+    VR_ASSERT(rn);
+    Eigen::Vector3f result;
+    result.setZero();
+    if (!hasLink(rn))
+    {
+        VR_ERROR << "No link with node " << rn->getName() << endl;
+        return result;
+    }
+    LinkInfo link = getLink(rn);
+
+
+
+
+    if(rn->isRotationalJoint())
+    {
+        result = getJointForceTorqueGlobal(link).tail(3);
+    }
+
+    return result;
+}
+
 Eigen::Matrix4f BulletRobot::getComGlobal( VirtualRobot::RobotNodePtr rn )
 {
     BulletObjectPtr bo = boost::dynamic_pointer_cast<BulletObject>(getDynamicsRobotNode(rn));
@@ -1280,11 +1342,11 @@ void BulletRobot::setPoseNonActuatedRobotNodes()
     }
 }
 
-void BulletRobot::enableForceTorqueFeedback( const LinkInfo& link )
+void BulletRobot::enableForceTorqueFeedback(const LinkInfo& link , bool enable)
 {
-	if (!link.joint->needsFeedback())
+    if (!link.joint->needsFeedback() && enable)
 	{
-		link.joint->enableFeedback(true);
+        link.joint->enableFeedback(true);
 		btJointFeedback* feedback = new btJointFeedback;
 		feedback->m_appliedForceBodyA = btVector3(0, 0, 0);
 		feedback->m_appliedForceBodyB = btVector3(0, 0, 0);
@@ -1292,6 +1354,10 @@ void BulletRobot::enableForceTorqueFeedback( const LinkInfo& link )
 		feedback->m_appliedTorqueBodyB = btVector3(0, 0, 0);
 		link.joint->setJointFeedback(feedback);
 	}
+    else if(link.joint->needsFeedback() && !enable)
+    {
+        link.joint->enableFeedback(false);
+    }
 }
 
 Eigen::VectorXf BulletRobot::getForceTorqueFeedbackA( const LinkInfo& link )
@@ -1322,7 +1388,32 @@ Eigen::VectorXf BulletRobot::getForceTorqueFeedbackB( const LinkInfo& link )
 	if (!feedback)
 		return r;
 	r << feedback->m_appliedForceBodyB[0],feedback->m_appliedForceBodyB[1],feedback->m_appliedForceBodyB[2],feedback->m_appliedTorqueBodyB[0],feedback->m_appliedTorqueBodyB[1],feedback->m_appliedTorqueBodyB[2];
-	return r;
+    return r;
+}
+
+Eigen::VectorXf BulletRobot::getJointForceTorqueGlobal(const BulletRobot::LinkInfo &link)
+{
+    Eigen::VectorXf ftA = getForceTorqueFeedbackA(link);
+    Eigen::VectorXf ftB = getForceTorqueFeedbackB(link);
+
+    Eigen::Vector3f jointGlobal = link.nodeJoint->getGlobalPose().block(0,3,3,1);
+    Eigen::Vector3f comBGlobal = link.nodeB->getCoMGlobal();
+
+    // force that is applied on objectA by objectB -> so the force that object B applies on the joint
+    Eigen::Vector3f forceOnBGlobal =  ftB.head(3);
+
+    Eigen::Vector3f torqueBGlobal =  ftB.tail(3);
+
+    // the lever from Object B CoM to Joint
+    Eigen::Vector3f leverOnJoint = (comBGlobal-jointGlobal) * 0.001;
+    // Calculate the torque in Joint by taking the torque that presses on the CoM of BodyB and the Torque of BodyB on the joint
+    // forceOnBGlobal is inverted in next line because it is the force of A on B to hold it in position
+    // torqueBGlobal is inverted in next line because it is the torque on B from A to compensate torque of other objects (which is the torque we would like) to hold it in place and therefore needs to be inverted as well
+    Eigen::Vector3f torqueJointGlobal =  (leverOnJoint).cross(-forceOnBGlobal)  + (-1)*torqueBGlobal;
+    Eigen::VectorXf result(6);
+    result.head(3) = ftA.head(3); // force in joint is same as force on CoM of A
+    result.tail(3) = torqueJointGlobal;
+    return result;
 }
 
 } // namespace VirtualRobot
diff --git a/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.h b/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.h
index 5445900c2..a3e9893db 100644
--- a/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.h
+++ b/SimDynamics/DynamicsEngine/BulletEngine/BulletRobot.h
@@ -59,10 +59,32 @@ public:
 	};
 
 
-	void enableForceTorqueFeedback(const LinkInfo& link);
+    /**
+     * @brief The force torque output needs to be activated before use
+     * @param link Link for which the force torque should be enabled/disabled
+     * @param enable
+     */
+    void enableForceTorqueFeedback(const LinkInfo& link, bool enable = true);
+
+    /**
+     * @brief getForceTorqueFeedbackA retrieves the force torque in the first body of the link
+     * @param link  Link for which the force torque should be retrieved
+     * @return 6 Dim. vector. First 3 values are the forces, last 3 are torques.
+     * Values are in N and N*m. Position of the values is the center of mass of first body of the link
+     * in the global coordinate system.
+     */
 	Eigen::VectorXf getForceTorqueFeedbackA(const LinkInfo& link);
-	Eigen::VectorXf getForceTorqueFeedbackB(const LinkInfo& link);
-	
+    Eigen::VectorXf getForceTorqueFeedbackB(const LinkInfo& link);
+    /**
+     * @brief getJointForceTorqueGlobal retrieves the force torque in the joint between the bodies
+     * @param link  Link for which the force torque should be retrieved
+     * @return 6 Dim. vector. First 3 values are the forces, last 3 are torques.
+     * Values are in N and N*m. Position of the values is in the middle of the joint
+     * in the global coordinate system.
+     */
+    Eigen::VectorXf getJointForceTorqueGlobal(const LinkInfo& link);
+
+
 	// We do not allow to re-adjust the robot. 
 	// The position of the robot is queried once on construction. 
 	// Then the physics simulation takes over.
@@ -85,11 +107,23 @@ public:
 	*/
 	virtual void actuateJoints(float dt);
 
+    virtual void updateSensors();
+
 	virtual float getJointAngle(VirtualRobot::RobotNodePtr rn);
     virtual float getJointSpeed(VirtualRobot::RobotNodePtr rn);
     virtual float getJointTargetSpeed(VirtualRobot::RobotNodePtr rn);
     virtual float getNodeTarget(VirtualRobot::RobotNodePtr node);
 
+    /*!
+     * \brief getJointTorques retrieves the torques in the given joint.
+     * \param rn
+     * \return Returns the torques in the given joint. If rn is not a
+     * rotational joint.
+     * Values are in N*m. Position of the values is in the middle of the joint
+     * in the global coordinate system.
+     */
+    Eigen::Vector3f getJointTorques(VirtualRobot::RobotNodePtr rn);
+
     /*!
         Returns the CoM pose, which is reported by bullet
     */
diff --git a/SimDynamics/DynamicsEngine/DynamicsRobot.cpp b/SimDynamics/DynamicsEngine/DynamicsRobot.cpp
index 437b5a0f9..eba2353e8 100644
--- a/SimDynamics/DynamicsEngine/DynamicsRobot.cpp
+++ b/SimDynamics/DynamicsEngine/DynamicsRobot.cpp
@@ -9,6 +9,8 @@ DynamicsRobot::DynamicsRobot(VirtualRobot::RobotPtr rob)
 {
 	THROW_VR_EXCEPTION_IF(!rob,"NULL object");
 	robot = rob;
+    sensors = rob->getSensors();
+
 }
 	
 DynamicsRobot::~DynamicsRobot()
diff --git a/SimDynamics/DynamicsEngine/DynamicsRobot.h b/SimDynamics/DynamicsEngine/DynamicsRobot.h
index b5760285e..dce08aa5b 100644
--- a/SimDynamics/DynamicsEngine/DynamicsRobot.h
+++ b/SimDynamics/DynamicsEngine/DynamicsRobot.h
@@ -26,6 +26,8 @@
 #include "../SimDynamics.h"
 #include "DynamicsObject.h"
 #include <VirtualRobot/Robot.h>
+#include <VirtualRobot/Nodes/Sensor.h>
+
 
 namespace SimDynamics
 {
@@ -80,7 +82,8 @@ public:
 		Usually this method is called by the framework in every tick to perform joint actuation.
 		\param dt Timestep
 	*/
-	virtual void actuateJoints(float dt);
+    virtual void actuateJoints(float dt);
+    virtual void updateSensors(){}
 
 	// experimental...
 	virtual void ensureKinematicConstraints();
@@ -120,6 +123,8 @@ protected:
 
 	VirtualRobot::RobotPtr robot;
 
+    std::vector<VirtualRobot::SensorPtr> sensors;
+
 	std::vector<VirtualRobot::RobotNodePtr> robotNodes;
 	std::map<VirtualRobot::RobotNodePtr, DynamicsObjectPtr> dynamicRobotNodes;
 };
diff --git a/VirtualRobot/CMakeLists.txt b/VirtualRobot/CMakeLists.txt
index 58cd392c2..3a1998f8c 100644
--- a/VirtualRobot/CMakeLists.txt
+++ b/VirtualRobot/CMakeLists.txt
@@ -31,8 +31,10 @@ Nodes/RobotNodeFixedFactory.cpp
 Nodes/RobotNodeActuator.cpp
 Nodes/Sensor.cpp
 Nodes/PositionSensor.cpp
+Nodes/ForceTorqueSensor.cpp
 Nodes/CameraSensor.cpp
 Nodes/PositionSensorFactory.cpp
+Nodes/ForceTorqueSensorFactory.cpp
 Nodes/CameraSensorFactory.cpp
 Visualization/Visualization.cpp
 Visualization/VisualizationNode.cpp
@@ -100,9 +102,11 @@ Nodes/RobotNodeActuator.h
 Nodes/ConditionedLock.h
 Nodes/Sensor.h
 Nodes/PositionSensor.h
+Nodes/ForceTorqueSensor.h
 Nodes/CameraSensor.h
 Nodes/SensorFactory.h
 Nodes/PositionSensorFactory.h
+Nodes/ForceTorqueSensorFactory.h
 Nodes/CameraSensorFactory.h
 Transformation/DHParameter.h
 Visualization/VisualizationFactory.h
diff --git a/VirtualRobot/Nodes/ForceTorqueSensor.cpp b/VirtualRobot/Nodes/ForceTorqueSensor.cpp
new file mode 100644
index 000000000..405dc90ee
--- /dev/null
+++ b/VirtualRobot/Nodes/ForceTorqueSensor.cpp
@@ -0,0 +1,63 @@
+
+#include "ForceTorqueSensor.h"
+#include "ForceTorqueSensorFactory.h"
+#include "../XML/BaseIO.h"
+
+using namespace boost;
+
+namespace VirtualRobot {
+
+ForceTorqueSensor::ForceTorqueSensor(RobotNodeWeakPtr robotNode,
+                const std::string &name) : Sensor(robotNode,name),
+    forceTorqueValues(6)
+{
+    forceTorqueValues.setZero();
+}
+
+
+ForceTorqueSensor::~ForceTorqueSensor()
+{
+}
+
+const Eigen::VectorXf &ForceTorqueSensor::getForceTorque()
+{
+    return forceTorqueValues;
+}
+
+
+
+void ForceTorqueSensor::print( bool printChildren, bool printDecoration ) const
+{
+	if (printDecoration)
+        cout << "******** ForceTorqueSensor ********" << endl;
+	Sensor::print(printChildren,false);
+}
+
+
+SensorPtr ForceTorqueSensor::_clone(const RobotNodePtr newRobotNode, const VisualizationNodePtr visualizationModel, float scaling)
+{
+    SensorPtr result(new ForceTorqueSensor(newRobotNode,name));
+	return result;
+}
+
+
+std::string ForceTorqueSensor::toXML(const std::string &modelPath, int tabs)
+{
+    std::stringstream ss;
+    std::string t = "\t";
+    std::string pre = "";
+    for (int i=0;i<tabs;i++)
+        pre += t;
+    ss << pre << "<Sensor type='" << ForceTorqueSensorFactory::getName() << "'/>" << endl;
+	std::string pre2 = pre + t;
+
+
+    return ss.str();
+}
+
+void ForceTorqueSensor::updateSensors(const Eigen::VectorXf &newForceTorque)
+{
+    forceTorqueValues = newForceTorque;
+}
+
+} // namespace VirtualRobot
diff --git a/VirtualRobot/Nodes/ForceTorqueSensor.h b/VirtualRobot/Nodes/ForceTorqueSensor.h
new file mode 100644
index 000000000..50c0e094e
--- /dev/null
+++ b/VirtualRobot/Nodes/ForceTorqueSensor.h
@@ -0,0 +1,90 @@
+/**
+* This file is part of Simox.
+*
+* Simox is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2 of
+* the License, or (at your option) any later version.
+*
+* Simox is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+* @package    VirtualRobot
+* @author     Nikolaus Vahrenkamp
+* @copyright  2013 Nikolaus Vahrenkamp
+*             GNU Lesser General Public License
+*
+*/
+#ifndef _VirtualRobot_ForceTorqueSensor_h_
+#define _VirtualRobot_ForceTorqueSensor_h_
+
+#include "Sensor.h"
+
+
+namespace VirtualRobot
+{
+
+class ForceTorqueSensor;
+typedef boost::shared_ptr<ForceTorqueSensor> ForceTorqueSensorPtr;
+
+
+/*!
+    This is a force torque sensor in a joint
+    The force torque values are in world coordinates
+*/
+class VIRTUAL_ROBOT_IMPORT_EXPORT ForceTorqueSensor : public Sensor
+{
+public:
+	friend class Robot;
+	friend class RobotIO;
+
+	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+
+	/*!
+		Constructor with settings.
+	*/
+    ForceTorqueSensor(	RobotNodeWeakPtr robotNode,
+            const std::string &name
+			);
+
+	/*!
+	*/
+    virtual ~ForceTorqueSensor();
+
+    void updateSensors(const Eigen::VectorXf &newForceTorque);
+
+
+    const Eigen::VectorXf &getForceTorque();
+    const Eigen::Vector3f &getForce();
+    const Eigen::Vector3f &getTorque();
+
+	/*!
+		Print status information.
+	*/
+	virtual void print(bool printChildren = false, bool printDecoration = true) const;
+
+
+    virtual std::string toXML(const std::string &modelPath, int tabs);
+
+protected:
+
+    ForceTorqueSensor(){}
+
+
+	/*!
+	Derived classes must implement their clone method here.
+	*/
+	virtual SensorPtr _clone(const RobotNodePtr newRobotNode, const VisualizationNodePtr visualizationModel, float scaling);
+    Eigen::VectorXf forceTorqueValues;
+};
+
+typedef boost::shared_ptr<ForceTorqueSensor> ForceTorqueSensorPtr;
+
+} // namespace VirtualRobot
+
+#endif // _VirtualRobot_ForceTorqueSensor_h_
diff --git a/VirtualRobot/Nodes/ForceTorqueSensorFactory.cpp b/VirtualRobot/Nodes/ForceTorqueSensorFactory.cpp
new file mode 100644
index 000000000..cd24947e6
--- /dev/null
+++ b/VirtualRobot/Nodes/ForceTorqueSensorFactory.cpp
@@ -0,0 +1,112 @@
+
+
+#include "ForceTorqueSensorFactory.h"
+#include "Sensor.h"
+#include "../XML/BaseIO.h"
+#include "../XML/rapidxml.hpp"
+#include "ForceTorqueSensor.h"
+
+
+namespace VirtualRobot {
+
+ForceTorqueSensorFactory::ForceTorqueSensorFactory()
+{
+}
+
+
+ForceTorqueSensorFactory::~ForceTorqueSensorFactory()
+{
+}
+
+
+/**
+ * This method creates a VirtualRobot::ForceTorqueSensor.
+ *
+ * \return instance of VirtualRobot::ForceTorqueSensor.
+ */
+SensorPtr ForceTorqueSensorFactory::createSensor(RobotNodePtr node, const std::string& name, VisualizationNodePtr visualization,
+											  const Eigen::Matrix4f &rnTrafo) const
+{
+    SensorPtr Sensor(new ForceTorqueSensor(node, name));
+
+	return Sensor;
+}
+
+SensorPtr ForceTorqueSensorFactory::createSensor(RobotNodePtr node, rapidxml::xml_node<char>* sensorXMLNode, RobotIO::RobotDescription loadMode, const std::string basePath) const
+{
+	THROW_VR_EXCEPTION_IF(!sensorXMLNode,"NULL data");
+	THROW_VR_EXCEPTION_IF(!node,"NULL data");
+
+
+	// get name
+	std::string sensorName = BaseIO::processNameAttribute(sensorXMLNode,true);
+	if (sensorName.empty())
+	{
+		std::stringstream ss;
+        ss << node->getName() << "_ForceTorqueSensor";
+		sensorName = ss.str();
+	}
+
+
+	// visu data
+	bool visuProcessed = false;
+	bool enableVisu = true;
+	bool useAsColModel = false;
+
+	VisualizationNodePtr visualizationNode;
+
+	Eigen::Matrix4f transformMatrix = Eigen::Matrix4f::Identity();
+
+	rapidxml::xml_node<>* nodeXML = sensorXMLNode->first_node();
+	while (nodeXML)
+	{
+		std::string nodeName = BaseIO::getLowerCase(nodeXML->name());
+//		if (nodeName == "visualization")
+//		{
+//			if (loadMode==RobotIO::eFull)
+//			{
+//				THROW_VR_EXCEPTION_IF(visuProcessed, "Two visualization tags defined in Sensor '" << sensorName << "'." << endl);
+//				visualizationNode = BaseIO::processVisualizationTag(nodeXML, sensorName, basePath, useAsColModel);
+//				visuProcessed = true;
+//			}// else silently ignore tag
+//		} else if (nodeName == "transform")
+//		{
+//			BaseIO::processTransformNode(sensorXMLNode,sensorName,transformMatrix);
+//		} else
+		{
+			THROW_VR_EXCEPTION("XML definition <" << nodeName << "> not supported in Sensor <" << sensorName << ">." << endl);
+		}
+
+		nodeXML = nodeXML->next_sibling();
+	}
+
+
+
+    SensorPtr Sensor(new ForceTorqueSensor(node, sensorName));
+
+	return Sensor;
+}
+
+
+/**
+ * register this class in the super class factory
+ */
+SensorFactory::SubClassRegistry ForceTorqueSensorFactory::registry(ForceTorqueSensorFactory::getName(), &ForceTorqueSensorFactory::createInstance);
+
+
+/**
+ * \return "position"
+ */
+std::string ForceTorqueSensorFactory::getName() {return "forcetorque";}
+
+
+/**
+ * \return new instance of ForceTorqueSensorFactory.
+ */
+boost::shared_ptr<SensorFactory> ForceTorqueSensorFactory::createInstance(void*)
+{
+    boost::shared_ptr<ForceTorqueSensorFactory> psFactory(new ForceTorqueSensorFactory());
+    return psFactory;
+}
+
+} // namespace VirtualRobot
diff --git a/VirtualRobot/Nodes/ForceTorqueSensorFactory.h b/VirtualRobot/Nodes/ForceTorqueSensorFactory.h
new file mode 100644
index 000000000..3ecddca07
--- /dev/null
+++ b/VirtualRobot/Nodes/ForceTorqueSensorFactory.h
@@ -0,0 +1,62 @@
+/**
+* This file is part of Simox.
+*
+* Simox is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2 of
+* the License, or (at your option) any later version.
+*
+* Simox is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+* @package    VirtualRobot
+* @author     Nikolaus Vahrenkamp
+* @copyright  2013 Nikolaus Vahrenkamp
+*             GNU Lesser General Public License
+*
+*/
+#ifndef _VirtualRobot_ForceTorqueSensorFactory_h_
+#define _VirtualRobot_ForceTorqueSensorFactory_h_
+
+#include "../VirtualRobotImportExport.h"
+#include "../SceneObject.h"
+#include "SensorFactory.h"
+
+#include <boost/shared_ptr.hpp>
+
+
+namespace VirtualRobot
+{
+class Sensor;
+
+class VIRTUAL_ROBOT_IMPORT_EXPORT ForceTorqueSensorFactory  : public SensorFactory
+{
+public:
+    ForceTorqueSensorFactory();
+    virtual ~ForceTorqueSensorFactory();
+
+	//! Standard init method
+	virtual SensorPtr createSensor(RobotNodePtr node, const std::string& name, VisualizationNodePtr visualization = VisualizationNodePtr(),
+		const Eigen::Matrix4f &rnTrafo = Eigen::Matrix4f::Identity() ) const;
+
+	/*!
+		Create sensor from XML tag. 
+	*/
+	virtual SensorPtr createSensor(RobotNodePtr node, rapidxml::xml_node<char>* sensorXMLNode, RobotIO::RobotDescription loadMode = RobotIO::eFull, const std::string basePath = std::string() ) const;
+
+// AbstractFactoryMethod
+public:
+	static std::string getName();
+	static boost::shared_ptr<SensorFactory> createInstance(void*);
+private:
+	static SubClassRegistry registry;
+};
+
+} // namespace VirtualRobot
+
+#endif // _VirtualRobot_ForceTorqueSensorFactory_h_
diff --git a/VirtualRobot/data/robots/ArmarIII/ArmarIII-LeftArm.xml b/VirtualRobot/data/robots/ArmarIII/ArmarIII-LeftArm.xml
index 3718c0b6f..5f9bf2a6e 100644
--- a/VirtualRobot/data/robots/ArmarIII/ArmarIII-LeftArm.xml
+++ b/VirtualRobot/data/robots/ArmarIII/ArmarIII-LeftArm.xml
@@ -173,8 +173,9 @@
             <CoM location="VisualizationBBoxCenter"/>
             <Mass value="2.59945309" units="kg" />
             <IgnoreCollision name="Underarm L"/>
+
         </Physics>
-        
+        <Sensor type="forcetorque"/>
 		<Child name="EndArmL"/>
 	</RobotNode>
 	
diff --git a/VirtualRobot/data/robots/ArmarIII/ArmarIII-RightArm.xml b/VirtualRobot/data/robots/ArmarIII/ArmarIII-RightArm.xml
index 20e4f37f8..78e88f4c9 100644
--- a/VirtualRobot/data/robots/ArmarIII/ArmarIII-RightArm.xml
+++ b/VirtualRobot/data/robots/ArmarIII/ArmarIII-RightArm.xml
@@ -170,7 +170,7 @@
             <Mass value="2.59945309" units="kg" />
             <IgnoreCollision name="Underarm R"/>
         </Physics>
-        
+        <Sensor type="forcetorque"/>
 		<Child name="EndArmR"/>
 	</RobotNode>
 	
-- 
GitLab