You are viewing the documentation for OpenSim 2.4. Are you looking for the latest OpenSim 4.0 Documentation?

Creating an Actuator Part One

The steps covered in part one are:

Creating Your New Class

Setting up a working directory

Before we examine the code, you will need to set up a working directory. This process is very similar to that described in Section 3.2.

  • Launch CMake. Set the /CustomActuatorExample directory as the source code location, and create any directory you wish for the build location.
  • Click Configure. Be sure to point the OpenSim installation property to the correct location of your OpenSim 2.0 installation folder. By default, the CMAKE_INSTALL_PREFIX (this flag shows up if you set CMake to show "Advanced View") is set to the same directory as your source code. This will ensure that you will not have to move any associated files to visualize your results in the GUI later on.
  • Click Configure again and then click Generate. Then close CMake.

Defining the ControllableSpring class (ControllableSpring.h)

The following instructions will outline ALL the steps for defining the ControllableSpring class. The ControllableSpring class is defined by the file ControllableSpring.h. It only contains a partial definition of the class, though. You will need to fill in a few key lines that have been omitted.

At the top of the header file, we include the header for the base class, call the OpenSim namespace, and begin defining the class as a derived class of PistonActuator.

#include "PistonActuator.h"
namespace OpenSim { 

class ControllableSpring : public PistonActuator
{

Defining properties

Our new actuator will have all of the properties of the PistonActuator class, plus one more for defining the rest length of the spring.

protected: 
  /** rest length of the spring */
  PropertyDbl _propRestLength; 

  // REFERENCES
  /** rest length */
  double &_restLength;

The constructors

Next we define the constructors. The constructors take the same form as the PistonActuator constructors for consistency. Both the constructor and copy constructor call the setNull method (to be defined later) which initializes some of the basic elements of the class. The copy constructor also copies the rest length from the existing ControllableSpring. The default destructor is used.

/* _restLength reference must be initialized in the initialization list */
ControllableSpring( std::string aBodyNameA="", std::string aBodyNameB="") :
    PistonActuator(aBodyNameA, aBodyNameB),_restLength(_propRestLength.getValueDbl())
{
  setNull();
}

/* The copy constructor must also copy the _restLength since the base class 
** version doesn't know about it. */
ControllableSpring(const ControllableSpring &aControllableSpring) :
    PistonActuator(aControllableSpring),_restLength(_propRestLength.getValueDbl())
{
  setNull();
  _restLength = aControllableSpring.getRestLength();
}

/* use the default destructor */
virtual ~ControllableSpring() {}; 

Setup methods

We will define two private member methods that are used during construction to initialize the ControllableSpring instance. First, setupProperties() is used to set up the properties of the ControllableSpring from values read in from an XML file. The only property added in this class is the rest length.

/* define private utilities to be used by the constructors. */
private:
void setupProperties()
{
  _propRestLength.setName("rest_length");
  _propRestLength.setValue(1.0);
  _propRestLength.setComment("The equilibrium length of the spring.");
  _propertySet.append( &_propRestLength);
}

Next we define setNull(), which is called when a ControllableSpring object is constructed. It calls setupProperties() and sets some other basic elements of the actuator class, such as its type ("ControllableSpring") and its number of states. 

void setNull()
{
  setType("ControllableSpring");
  setupProperties();
  setNumStateVariables(0); 
}

Get and Set methods

Since the rest length was defined as a private member variable, we must define some public methods to get and set its value.

public:
// REST LENGTH 
void setRestLength(double aLength) { _restLength = aLength; };
double getRestLength() const { return _restLength; }; 

computeForce()

The computeForce() method is the heart of any actuator class. It is called by OpenSim to calculate and apply any loads associated with the actuator. The computeForce() method is defined to be purely virtual in the CustomActuator base class, so any derived classes must define its behavior. PistonActuator has already defined its own implementation of computeForce(), but we will redefine it here so that the ControllableSpring actuator behaves like a spring instead of like an ideal actuator. This method begins by checking that the model and bodies are defined.

void computeForce(const SimTK::State& s) const
{
  // make sure the model and bodies are instantiated
  if (_model==NULL) return;
  
  const SimbodyEngine& engine = getModel().getSimbodyEngine(); 
  
  if(_bodyA ==NULL || _bodyB ==NULL)
    return; 

Next, it determines the locations of the application points in both the body and ground frames by doing some transformations. _pointA and _pointB, as well as the bool _pointsAreGlobal, are defined in the PistonActuator base class.

  /* store _pointA and _pointB positions in the global frame. If not
  ** alread in the body frame, transform _pointA and _pointB into their
  ** respective body frames. */ 
  SimTK::Vec3 pointA_inGround, pointB_inGround; 
  if (_pointsAreGlobal)
  {
    pointA_inGround = _pointA;
    pointB_inGround = _pointB;
    engine.transformPosition(s, engine.getGroundBody(), _pointA, *_bodyA, _pointA);
    engine.transformPosition(s, engine.getGroundBody(), _pointB, *_bodyB, _pointB);
  }

  else
  {
    engine.transformPosition(s, *_bodyA, _pointA, engine.getGroundBody(), pointA_inGround);
    engine.transformPosition(s, *_bodyB, _pointB, engine.getGroundBody(), pointB_inGround);
  }

Now we find the vector pointing from point B to point A expressed in the ground frame and then decompose it into its magnitude and direction.

 

  // find the dirrection along which the actuator applies its force
  SimTK::Vec3 r = pointA_inGround - pointB_inGround;
  SimTK::UnitVec3 direction(r);
  double length = sqrt(~r*r);

To compute the magnitude of the force, we first must know the spring stiffness. Since we want stiffness to be the product of optimalForce and the control value, we simply use the computeActuation() method from the base class, which outputs exactly this calculation.

  double stiffness = computeActuation(s);

Now we find the magnitude of the force from the stiffness and the deflection of the spring. We then form the force vector.

  // find the force magnitude and set it. then form the force vector
  double forceMagnitude = (_restLength - length)*stiffness;
  setForce(s, forceMagnitude );a
  SimTK::Vec3 force = forceMagnitude*direction; 

The last operation computeForce() performs is to apply the equal and opposite point forces to the two bodies.

 

  // appy equal and opposite forces to the bodies
  applyForceToPoint(*_bodyA, _pointA, force);
  applyForceToPoint(*_bodyB, _pointB, -force);
}

Finish the class definition and close the namespace

//========================================================================
}; // END of class ControllableSpring 
} //Namespace
//========================================================================
//======================================================================== 

OpenSim is supported by the Mobilize Center , an NIH Biomedical Technology Resource Center (grant P41 EB027060); the Restore Center , an NIH-funded Medical Rehabilitation Research Resource Network Center (grant P2C HD101913); and the Wu Tsai Human Performance Alliance through the Joe and Clara Tsai Foundation. See the People page for a list of the many people who have contributed to the OpenSim project over the years. ©2010-2024 OpenSim. All rights reserved.