Skip to content
Snippets Groups Projects
controller-pd.cpp 11 KiB
Newer Older
Nicolas Mansard's avatar
Nicolas Mansard committed
/*
 * Copyright 2011, Nicolas Mansard, LAAS-CNRS
 *
 * This file is part of sot-dyninv.
 * sot-dyninv 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 3 of
 * the License, or (at your option) any later version.
 * sot-dyninv 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 sot-dyninv.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <sot-dyninv/controller-pd.h>
#include <sot-core/debug.h>
#include <dynamic-graph/factory.h>
#include <dynamic-graph/command.h>
#include <dynamic-graph/command-setter.h>
#include <dynamic-graph/command-getter.h>

#include <jrl/mal/matrixabstractlayer.hh>

namespace sot
{
  namespace dyninv
  {

    using namespace dg;

    DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(ControllerPD,"ControllerPD");

    ControllerPD::
    ControllerPD( const std::string & name )
      :Entity(name)
      ,sensorWorldRotationSIN(NULL,"sotControllerPD("+name
			      +")::input(MatrixRotation)::sensorWorldRotation")
      ,sensorEmbeddedPositionSIN(NULL,"sotControllerPD("+name
				 +")::input(MatrixHomo)::sensorEmbeddedPosition")
      ,contactWorldPositionSIN(NULL,"sotControllerPD("+name
			       +")::input(MatrixHomo)::contactWorldPosition")
      ,contactEmbeddedPositionSIN(NULL,"sotControllerPD("+name
				  +")::input(MatrixHomo)::contactEmbeddedPosition")

      ,anglesSOUT( boost::bind(&ControllerPD::computeAngles,this,_1,_2),
		   sensorWorldRotationSIN<<sensorEmbeddedPositionSIN
		   <<contactWorldPositionSIN<<contactEmbeddedPositionSIN,
		   "sotControllerPD("+name+")::output(Vector)::angles" )
      ,flexibilitySOUT( boost::bind(&ControllerPD::computeFlexibilityFromAngles
				    ,this,_1,_2),
			anglesSOUT,
			"sotControllerPD("+name+")::output(matrixRotation)::flexibility" )
      ,driftSOUT( boost::bind(&ControllerPD::computeDriftFromAngles,this,_1,_2),
		  anglesSOUT,
		  "sotControllerPD("+name+")::output(matrixRotation)::drift" )
      ,sensorWorldRotationSOUT( boost::bind(&ControllerPD::computeSensorWorldRotation
					    ,this,_1,_2),
				anglesSOUT<<sensorWorldRotationSIN,
				"sotControllerPD("+name
				+")::output(matrixRotation)::sensorCorrectedRotation" )
      ,waistWorldRotationSOUT( boost::bind(&ControllerPD::computeWaistWorldRotation
					   ,this,_1,_2),
			       sensorWorldRotationSOUT<<sensorEmbeddedPositionSIN,
			       "sotControllerPD("+name
			       +")::output(matrixRotation)::waistWorldRotation" )
      ,waistWorldPositionSOUT( boost::bind(&ControllerPD::computeWaistWorldPosition
					   ,this,_1,_2),
			       flexibilitySOUT << contactEmbeddedPositionSIN,
			       "sotControllerPD("+name
			       +")::output(MatrixHomogeneous)::waistWorldPosition" )
      ,waistWorldPoseRPYSOUT( boost::bind(&ControllerPD::computeWaistWorldPoseRPY
					  ,this,_1,_2),
			      waistWorldPositionSOUT,
			      "sotControllerPD("+name
			      +")::output(vectorRollPitchYaw)::waistWorldPoseRPY" )

      ,fromSensor_(true)
    {
      sotDEBUGIN(5);

      signalRegistration( sensorWorldRotationSIN     << sensorEmbeddedPositionSIN
			  << contactWorldPositionSIN << contactEmbeddedPositionSIN
			  << anglesSOUT              << flexibilitySOUT
			  << driftSOUT               << sensorWorldRotationSOUT
			  << waistWorldRotationSOUT
			  << waistWorldPositionSOUT  << waistWorldPoseRPYSOUT  );

      // Commands
      std::string docstring;
      docstring = "    \n"
	"    Set flag specifying whether angle is measured from sensors or simulated.\n"
	"    \n"
	"      Input:\n"
	"        - a boolean value.\n"
	"    \n";
      addCommand("setFromSensor",
		 new ::dynamicgraph::command::Setter<ControllerPD, bool>
		 (*this, &ControllerPD::fromSensor, docstring));

      docstring = "    \n"
	"    Get flag specifying whether angle is measured from sensors or simulated.\n"
	"    \n"
	"      No input,\n"
	"      return a boolean value.\n"
	"    \n";
      addCommand("getFromSensor",
		 new ::dynamicgraph::command::Getter<ControllerPD, bool>
		 (*this, &ControllerPD::fromSensor, docstring));

      sotDEBUGOUT(5);
    }


    ControllerPD::
    ~ControllerPD( void )
    {
      sotDEBUGIN(5);

      sotDEBUGOUT(5);
      return;
    }

    /* --- SIGNALS -------------------------------------------------------------- */
    /* --- SIGNALS -------------------------------------------------------------- */
    /* --- SIGNALS -------------------------------------------------------------- */
    ml::Vector& ControllerPD::
    computeAngles( ml::Vector& res,
		   const int& time )
    {
      sotDEBUGIN(15);

      res.resize(3);

      const MatrixRotation &worldestRchest = sensorWorldRotationSIN( time );
      sotDEBUG(35) << "worldestRchest = " << std::endl << worldestRchest;
      const MatrixHomogeneous &waistMchest = sensorEmbeddedPositionSIN( time );
      MatrixRotation waistRchest; waistMchest.extract( waistRchest );

      const MatrixHomogeneous & waistMleg = contactEmbeddedPositionSIN( time );
      MatrixRotation waistRleg; waistMleg.extract( waistRleg );
      MatrixRotation chestRleg; waistRchest.transpose().multiply( waistRleg,chestRleg );
      MatrixRotation worldestRleg; worldestRchest.multiply( chestRleg,worldestRleg );

      sotDEBUG(35) << "worldestRleg = " << std::endl << worldestRleg;

      /* Euler angles with following code: (-z)xy, -z being the yaw drift, x the
       * first flexibility and y the second flexibility. */
      const double TOLERANCE_TH = 1e-6;

      const MatrixRotation &R = worldestRleg;
      if( (fabs(R(0,1))<TOLERANCE_TH)&&(fabs(R(1,1))<TOLERANCE_TH) ) 
	{
	  /* This means that cos(X) is very low, ie flex1 is close to 90deg.
	   * I take the case into account, but it is bloody never going 
	   * to happens. */
	  if( R(2,1)>0 ) res(0)=M_PI/2; else res(0) = -M_PI/2;
	  res(2) = atan2( -R(0,2),R(0,0) );
	  res(1) = 0;

	  /* To sum up: if X=PI/2, then Y and Z are in singularity.
	   * we cannot decide both of then. I fixed Y=0, which
	   * means that all the measurement coming from the sensor
	   * is assumed to be drift of the gyro. */
	}
      else
	{
	  double &X = res(0);
	  double &Y = res(1);
	  double &Z = res(2);

	  Y = atan2( R(2,0),R(2,2) );
	  Z = atan2( R(0,1),R(1,1) );
	  if( fabs(R(2,0))>fabs(R(2,2)) )
	    { X=atan2( R(2,1)*sin(Y),R(2,0) ); }
	  else 
	    { X=atan2( R(2,1)*cos(Y),R(2,2) ); }
	}
  
      sotDEBUG(35) << "angles = " << res;


      sotDEBUGOUT(15);
      return res;
    }

    /* compute the transformation matrix of the flexibility, ie
     * feetRleg. 
     */
    MatrixRotation& ControllerPD::
    computeFlexibilityFromAngles( MatrixRotation& res,
				  const int& time )
    {
      sotDEBUGIN(15);

      const ml::Vector & angles = anglesSOUT( time );
      double cx= cos( angles(0) );
      double sx= sin( angles(0) );
      double cy= cos( angles(1) );
      double sy= sin( angles(1) );
  
      res(0,0) = cy;
      res(0,1) = 0;
      res(0,2) = -sy;

      res(1,0) = -sx*sy;
      res(1,1) = cx;
      res(1,2) = -sx*cy;

      res(2,0) = cx*sy;
      res(2,1) = sx;
      res(2,2) = cx*cy;

      sotDEBUGOUT(15);
      return res;
    }

    /* Compute the rotation matrix of the drift, ie the
     * transfo from the world frame to the estimated (drifted) world
     * frame: worldRworldest.
     */
    MatrixRotation& ControllerPD::
    computeDriftFromAngles( MatrixRotation& res,
			    const int& time )
    {
      sotDEBUGIN(15);
  
      const ml::Vector & angles = anglesSOUT( time );
      double cz = cos( angles(2) );
      double sz = sin( angles(2) );
  
      res(0,0) = cz;
      res(0,1) = -sz;
      res(0,2) = 0;

      // z is the positive angle (R_{-z} has been computed
      // in the <angles> function). Thus, the /-/sin(z) is in 0,1
      res(1,0) = sz;  
      res(1,1) = cz;
      res(1,2) = 0;

      res(2,0) = 0;
      res(2,1) = 0;
      res(2,2) = 1;

      sotDEBUGOUT(15);
      return res;
    }

    MatrixRotation& ControllerPD::
    computeSensorWorldRotation( MatrixRotation& res,
				const int& time )
    {
      sotDEBUGIN(15);
  
      const MatrixRotation & worldRworldest = driftSOUT( time );
      const MatrixRotation & worldestRsensor = sensorWorldRotationSIN( time );
  
      worldRworldest.multiply( worldestRsensor,res );

      sotDEBUGOUT(15);
      return res;
    }
 
    MatrixRotation& ControllerPD::
    computeWaistWorldRotation( MatrixRotation& res,
			       const int& time )
    {
      sotDEBUGIN(15);
  
      // chest = sensor
      const MatrixRotation & worldRsensor = sensorWorldRotationSOUT( time );
      const MatrixHomogeneous & waistMchest = sensorEmbeddedPositionSIN( time );
      MatrixRotation waistRchest; waistMchest.extract( waistRchest );
  
      worldRsensor .multiply( waistRchest.transpose(),res );

      sotDEBUGOUT(15);
      return res;
    }



    MatrixHomogeneous& ControllerPD::
    computeWaistWorldPosition( MatrixHomogeneous& res,
			       const int& time )
    {
      sotDEBUGIN(15);
  
      const MatrixHomogeneous & waistMleg = contactEmbeddedPositionSIN( time );
      const MatrixHomogeneous& contactPos = contactWorldPositionSIN( time );
      MatrixHomogeneous legMwaist; waistMleg.inverse( legMwaist );
      MatrixHomogeneous tmpRes;
      if( fromSensor_ )
	{ 
	  const MatrixRotation & Rflex = flexibilitySOUT( time ); // footRleg
	  ml::Vector zero(3); zero.fill(0.);
	  MatrixHomogeneous footMleg; footMleg.buildFrom( Rflex,zero );
				    
	  footMleg.multiply( legMwaist,tmpRes );
	}
      else { tmpRes = legMwaist; }

      contactPos.multiply( tmpRes, res );
      sotDEBUGOUT(15);
      return res;
    }

    ml::Vector& ControllerPD::
    computeWaistWorldPoseRPY( ml::Vector& res,
			      const int& time )
    {
      const MatrixHomogeneous & M = waistWorldPositionSOUT( time );

      MatrixRotation R; M.extract(R);
      VectorRollPitchYaw r; r.fromMatrix(R);
      ml::Vector t(3); M.extract(t);

      res.resize(6);
      for( int i=0;i<3;++i ) { res(i)=t(i); res(i+3)=r(i); }

      return res;
    }

    /* --- PARAMS --------------------------------------------------------------- */
    /* --- PARAMS --------------------------------------------------------------- */
    /* --- PARAMS --------------------------------------------------------------- */
    void ControllerPD::
    commandLine( const std::string& cmdLine,
		 std::istringstream& cmdArgs,
		 std::ostream& os )
    {
      sotDEBUG(25) << "Cmd " << cmdLine <<std::endl;

      if( cmdLine == "help" )
	{
	  Entity::commandLine(cmdLine,cmdArgs,os);
	}
      else if( cmdLine == "fromSensor" )
	{
	  std::string val; cmdArgs>>val;
	  if( ("true"==val)||("false"==val) )
	    {
	      fromSensor_ = ( val=="true" );
	    } else {
	    os << "fromSensor = " << (fromSensor_?"true":"false") << std::endl;
	  }
	}
      else { Entity::commandLine( cmdLine,cmdArgs,os); }
    }

  } // namespace dyninv
} // namespace sot