undefined reference error when adding a user plugin force to a model using cpp

Provide easy-to-use, extensible software for modeling, simulating, controlling, and analyzing the neuromusculoskeletal system.
POST REPLY
User avatar
Ibraheem Al-Dhamari
Posts: 35
Joined: Thu Mar 16, 2017 2:44 am

undefined reference error when adding a user plugin force to a model using cpp

Post by Ibraheem Al-Dhamari » Tue Jan 28, 2020 1:28 pm

Dear all,

I tried to create a new plugin similar to the body drag example. Everything works, the library is created and I can use it in the gui as expected.

When I tried to create the .osim file using cpp I get this error.

Code: Select all

[ 16%] Linking CXX executable myMainModel.cpp
CMakeFiles/myMainModel.cpp.dir/myMainModel.cpp.cpp.o: In function `main':
myMainModel.cpp.cpp:(.text+0x1862): undefined reference to `OpenSim::myPlugin::myPlugin()'
collect2: error: ld returned 1 exit status
CMakeFiles/myMainModel.dir/build.make:111: recipe for target 'myMainModel' failed
make[2]: *** [myMainModel] Error 1
If I disable this part, everything works but I have to add the plugin force manually to the .osim file using a text editor.

Code: Select all

            myPlugin *myForce = new myPlugin();  
            myForce->setName("myForce");
            myForce->setBody_name(myBody->getName()); 
            myForce->setMyParameter(0.5);
            osimModel.addForce(myForce);
Is it possible to create a model that uses a plugin in cpp? I noticed you didn't provide cpp examples to create the .osim in the TestPlugin folder e.g ThreeMasses.osim so I am not sure if this is possible.

Thanks!
Last edited by Ibraheem Al-Dhamari on Fri Jan 31, 2020 6:27 am, edited 2 times in total.

Tags:

User avatar
Ibraheem Al-Dhamari
Posts: 35
Joined: Thu Mar 16, 2017 2:44 am

Re: compiling a plugin produces undefined reference

Post by Ibraheem Al-Dhamari » Wed Jan 29, 2020 9:50 am

Many views and no answer!!! :o

I created a complete simple pendulum example where the user can change many parameters regarding the pendulum, simulation or the drag force. https://drive.google.com/open?id=1neaZj ... 2UOIE-N8yN the example build a simple pendulum with the drag force :ugeek: .

In the build folder, the library and the results generated by the code when the drag force part is commented. Your feedback and help are appreciated. :D

Here is the complete code:

Code: Select all

/*                   VisSim www.Uni-Koblenz.de
 *
 * This example is simulation of a simple pendulum with drag force
 * BodyDragForce is built as a plugin
 *
 * By Ibraheem AL-Dhamari ibr.exg@gmail.com
 *
 * TODOs:
 * 1. create the model .osim file using cpp
 * 2. run the model using forwardtool
 *
*/
#include <OpenSim/OpenSim.h>
#include "OpenSim/Common/STOFileAdapter.h"
#include <time.h>
#include <BodyDragForce.h>

#define PI 3.14159265

using namespace OpenSim;
using namespace std;

using SimTK::Vec3;
using SimTK::Inertia;
int main(int argc, char* argv[]) {
        std::cout<<"=============================================="<<std::endl;
        std::cout<<"=         Simple Pendulum in OpenSim         ="<<std::endl;
        std::cout<<"=============================================="<<std::endl;

        // Pendulum,Force and Simulation Parameters
        // This helps to experiements with different parameters

                double enablePlugin = 1.0    ; // 1 = pendulum has drag force, 0 = no drag force
                double enableSim    = 1.0    ; // 1 = run the simulation , 0 = only build the model file
                double enableVis    = 1.0    ; // 1 = show visualizer , 0 = no visualisation

                //assuming run inside the build folder
                std::string bodyDragForceLibPath="BodyDragForce/libBodyDragForce.so";

                double bodyDragForceCoefficient = 0.3;
                double bodyDragForceExponent    = 2.0;

                double gravity      = -9.81  ;
                double stepSize     = 0.001  ;
                double initialTime  = 0.0;
                double finalTime    = 5 ;
                double th1Deg       =90 ;               // degrees

                double      rodRd       = 0.1;
                double      rodHeight   = 1;
                double      rodMass     = 1;
                Vec3        rodCoM      = Vec3(0) ;
                Inertia     rodInertia  = Inertia(1);
                SimTK::Vec3 rodColor(1.0, 1.0, 0.0);
                double      sphRd        = 0.3;
                double      sphMass      = 1;
                Vec3        sphCoM       = Vec3(0) ;
                Inertia sphInertia       = Inertia(1);
                SimTK::Vec3 sphColor(1.0, 0.0, 0.0);
                Vec3        sphOrient   = Vec3(0);
        if  (argc >2){
            std::cout<<" using user arguments: "<<std::endl;
            for (int i=0; i<argc ;i++){
                std::cout<<argv[i]<<" " ;
            }
            std::cout<<" "<<std::endl ;

            finalTime    = strtod(argv[1], NULL);
            enableVis    = strtod(argv[2], NULL);
        }else{
          std::cout<<" using default arguments: "<<std::endl;
        }

     double   th1Rad         = th1Deg *((22.0/7.0)/180.0);   // radians
     Vec3     rodPos        = Vec3(0 ,  rodHeight   ,0) ; //it will be under the ground
     Vec3     rodOrient     = Vec3(0,0,th1Rad);

     double   sphX,sphY;
     Vec3     sphPos  ;

     sphX         =   rodHeight * sin( th1Rad);
     sphY         =   rodHeight * cos( th1Rad);
     sphPos        = Vec3(sphX, sphY , 0) ;

     try {

        Model pendulumDF;  // DF =Drag Force
        pendulumDF.setName("pendulumDF");
        pendulumDF.setGravity(SimTK::Vec3(0, gravity, 0));
        Ground& ground = pendulumDF.updGround();


         //                         body name, mass ,massCenter, inertia
        Body* rodBdy = new    Body("rod", rodMass,         rodCoM,     rodInertia );
        Cylinder rodGeom (rodRd , rodHeight);
        rodGeom.setColor(rodColor);    //set the color of the first block so they are different
        SimTK::Transform rodTrsfrm(SimTK::Vec3(0,0,0));
        auto* rodCenter = new PhysicalOffsetFrame("rodCenter", *rodBdy, rodTrsfrm);
        rodBdy->addComponent(rodCenter);     rodCenter->attachGeometry(rodGeom.clone());
        pendulumDF.addBody(rodBdy);

        // Connect the bodies with pin joints. Assume each body is 1 m long.
        auto* groundTrodJnt = new PinJoint("groundTrodJnt", pendulumDF.getGround(), Vec3(0),  Vec3(0),  *rodBdy, rodPos, rodOrient);
        pendulumDF.addJoint(groundTrodJnt);

        Body* sphBdy = new    Body("sph", sphMass,     sphCoM,     sphInertia );
        Sphere sphGeom (sphRd);
        sphGeom.setColor(sphColor); //set the color of the first block so they are different
        SimTK::Transform sphTrsfrm(SimTK::Vec3( 0, 0, 0));
        auto* sphCenter = new PhysicalOffsetFrame("sphCenter", *sphBdy, sphTrsfrm);
        sphBdy->addComponent(sphCenter);        sphCenter->attachGeometry(sphGeom.clone());
        pendulumDF.addBody(sphBdy);

        auto*  rodTsphJnt= new  PinJoint ("rodTsphJnt", *rodBdy,Vec3(0), Vec3(0),     *sphBdy, Vec3(0), Vec3(0));
        rodTsphJnt = new  PinJoint ("rodTsphJnt", *rodBdy, Vec3(sphX-rodHeight,sphY,0), rodOrient,     *sphBdy, sphPos, sphOrient);
        pendulumDF.addJoint(rodTsphJnt);

        //add BodyDragForce
        if (enablePlugin){
            LoadOpenSimLibrary(bodyDragForceLibPath);
            //osimModel.LoadOpenSimLibrary(uForce35LibPath);
            //osimModel.registerType( BodyDragForce());
            //Object::RegisterType( BodyDragForce()) ;
            //OpenSim::RegisterType( BodyDragForce()) ;

            /*
            BodyDragForce *dragForce = new BodyDragForce(); //create disc
            dragForce->setName("dragForce");
            dragForce->set_body_name("sph");
            dragForce->set_coefficient(bodyDragForceCoefficient);
            dragForce->set_exponent(bodyDragForceExponent);
            pendulumDF.addForce(dragForce);
            */
        }

        pendulumDF.finalizeConnections();
        pendulumDF.print(pendulumDF.getName()+".osim");

        //=============================================================
        //                  SIMULATION
        //=============================================================
        if (enableSim==1.0){
            // Set to true to visualize the simulation, which can be useful for
            if (enableVis == 1.0)   pendulumDF.setUseVisualizer(true);

            // Initialize the system and get the default state
            SimTK::State& si = pendulumDF.initSystem();

            rodTsphJnt->getCoordinate().setLocked(si, true);  // the sphere is fixed to rod

            if (enableVis == 1.0){
                std::cout << "Vsualization is enabled .... " << std::endl;
                SimTK::Visualizer& viz = pendulumDF.updVisualizer().updSimbodyVisualizer();
                viz.setBackgroundType(viz.SolidColor);
                viz.setBackgroundType(SimTK::Visualizer::GroundAndSky);
                viz.setShowFrameNumber(true);
                viz.setGroundHeight(-2);


            }else{
                std::cout << "Vsualization is disabled .... " << std::endl;
            }

            // Create the force reporter for obtaining the forces applied to the model
            // during a forward simulation
            ForceReporter* fReporter    = new ForceReporter(&pendulumDF);
            StatesReporter* sReporter   = new StatesReporter(&pendulumDF);
            BodyKinematics* bKinematics = new BodyKinematics(&pendulumDF, true);
            Array<std::string> bodiesToRecord ; //= {"all"};

            bKinematics->setModel(pendulumDF);
            bKinematics->setBodiesToRecord(bodiesToRecord);

            pendulumDF.addAnalysis(fReporter);
            pendulumDF.addAnalysis(sReporter);
            pendulumDF.addAnalysis(bKinematics);

            Manager manager(pendulumDF);
            manager.setIntegratorMethod(Manager::IntegratorMethod::RungeKuttaFeldberg);
            manager.setIntegratorAccuracy(1.0e-12);
            manager.setIntegratorMaximumStepSize(stepSize);

            // Print out details of the model
            pendulumDF.printDetailedInfo(si, std::cout);

            // Integrate from initial time to final time
            si.setTime(initialTime);
            std::cout << "\nIntegrating from " << initialTime << " to " << finalTime << std::endl;
            manager.initialize(si);
            manager.integrate(finalTime);


            int fR = fReporter->printResults(  pendulumDF.getName(),"",stepSize);
            int sR = sReporter->printResults(  pendulumDF.getName(),"",stepSize);
            int kR = bKinematics->printResults(pendulumDF.getName(),"",stepSize);

            //TODO: how to close simbody-visualizer window?
            // for now use: pkill simbody in the terminal
        } //endif simulation
    } catch (OpenSim::Exception ex)	{
        std::cout << ex.getMessage() << std::endl;
        return 1;
    }
    // declaring argument of time()
    time_t my_time = time(NULL);

    // ctime() used to give the present date time
    printf(" All OpenSim tasks are completed on: %s", ctime(&my_time));
}

User avatar
Ibraheem Al-Dhamari
Posts: 35
Joined: Thu Mar 16, 2017 2:44 am

Re: compiling a plugin produces undefined reference

Post by Ibraheem Al-Dhamari » Wed Jan 29, 2020 10:15 am

Update:

removing the library extension ".so"

Code: Select all

                std::string bodyDragForceLibPath="BodyDragForce/libBodyDragForce";
resulting the library is loaded, I get this message in the terminal:

Code: Select all

Loaded library BodyDragForce/libBodyDragForce.so
but I still get the same error

Code: Select all

/BodyDragExampleComplete/pendulumDF.cpp:-1: error: undefined reference to `OpenSim::BodyDragForce::BodyDragForce()'

when un-comment this part

Code: Select all

             /*
            BodyDragForce *dragForce = new BodyDragForce(); //create disc
            dragForce->setName("dragForce");
            dragForce->set_body_name("sph");
            dragForce->set_coefficient(bodyDragForceCoefficient);
            dragForce->set_exponent(bodyDragForceExponent);
            pendulumDF.addForce(dragForce);
             */

User avatar
Ayman Habib
Posts: 2238
Joined: Fri Apr 01, 2005 12:24 pm

Re: compiling a plugin produces undefined reference

Post by Ayman Habib » Wed Jan 29, 2020 11:29 am

Hello Ibraheem,

Let me explain the issue with using Plugins in this context, and how it's solved on Windows (since the solution is likely platform dependent).
- Symbols defined in a dynamic library/dll/so are "exported" when building the plugin and "imported" when using the plugin.
- On Windows this is done using a Macro that translates to Export (in case of building the plugin) or Import (in case of using the plugin), both builds use the same header but a pre-processor symbol is defined in one case (to export) and not the other (to import).
- Your main program needs to include the header, indicate that the symbols are imported somehow and then at run time the symbols are resolved based on path/load-library-path.

If you're on Windows you can check these settings, if not please post what you find out or if you need further help resolving the issue.

Best regards,
-Ayman

User avatar
Ibraheem Al-Dhamari
Posts: 35
Joined: Thu Mar 16, 2017 2:44 am

Re: compiling a plugin produces undefined reference

Post by Ibraheem Al-Dhamari » Wed Jan 29, 2020 11:39 am

Thanks, Ayman for your explanation.
If you're on Windows you can check these settings,
Could you please explain more or provide a reference (e.g. wiki, post) about this.

I will try to do a similar thing in Linux.

My current work around this is to create a small function that opens the model file after model.print() then add the force (a block of xml object) then save the file again.

User avatar
Ibraheem Al-Dhamari
Posts: 35
Joined: Thu Mar 16, 2017 2:44 am

Re: undefined reference error when adding a user plugin force to a model using cpp

Post by Ibraheem Al-Dhamari » Fri Jan 31, 2020 6:36 am

Here is how I solved the issue using an extra function, probably a similar function could be added to opensim

Code: Select all

#include "rapidxml.hpp"
#include "rapidxml_utils.hpp"
#include "rapidxml_print.hpp"
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
void addPluginForce( std::string modelPath ,  xml_node<>*  forcePluginNode ){


    ifstream inputStream (modelPath);
    xml_document<> doc;     xml_node<> * root_node;


    if (inputStream.is_open()){
        std::ifstream inputModelFile (modelPath.c_str());
        std::vector<char> buffer((std::istreambuf_iterator<char>(inputModelFile)), std::istreambuf_iterator<char>());
        buffer.push_back('\0');       doc.parse<0>(&buffer[0]);

        xml_node<> *root = doc.first_node();
        xml_node<> *forceSetNode = root->first_node()->first_node("ForceSet");
        if(forceSetNode != 0){
            forceSetNode->first_node()->append_node(forcePluginNode);
        }else{
            xml_node<>* forceSetNewNode = doc.allocate_node( node_element       ,"ForceSet");     //ForceSet
            forceSetNewNode->append_attribute(doc.allocate_attribute("name",  "forceset"));
            std::cout << "   ........... creating: objects node" << std::endl;
            xml_node<>* forceSetObjectsNewNode = doc.allocate_node(node_element , "objects");  //   body_name
            forceSetNewNode->append_node(forceSetObjectsNewNode);
            forceSetNewNode->first_node()->append_node(forcePluginNode);
            doc.append_node( forceSetNewNode);
        }
        ofstream outputStream(modelPath,  ios::out  ) ;

        if (outputStream.is_open()){
           outputStream<< "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
           outputStream <<doc;
           outputStream.close();
        }
       std::cout << " Plugin force is added ....................................."    << std::endl;
    }else
        std::cout << "..................... file is NOT oppened"    << std::endl;
}
the xml force node is created in the model e.g.

Code: Select all

            xml_document<> doc;
            xml_node<>* forcePluginNode = doc.allocate_node( node_element       , "BodyDragForce");

             forcePluginNode->append_attribute(doc.allocate_attribute("name",  "dragForce"));
             xml_node<>* forcePar1Node = doc.allocate_node(node_element , "body_name");  //   body_name
             forcePar1Node->value("sph");
             forcePluginNode->append_node(forcePar1Node);
             xml_node<>* forcePar2Node = doc.allocate_node(node_element ,"coefficient");  //   coefficient
             std::ostringstream strs1; strs1 << bodyDragForceCoefficient; std::string str1 = strs1.str(); const char *bodyDragForceCoefficientSt = str1.c_str();
             forcePar2Node->value(bodyDragForceCoefficientSt);
             forcePluginNode->append_node(forcePar2Node);

             xml_node<>* forcePar3Node = doc.allocate_node(node_element , "exponent");  //   exponent
             std::ostringstream strs2; strs2 <<bodyDragForceExponent;  std::string str2 = strs2.str(); const char *bodyDragForceExponentSt = str2.c_str();
             forcePar3Node->value(bodyDragForceExponentSt );
             forcePluginNode->append_node(forcePar3Node);
then we call our function after creating the model file e.g.

Code: Select all

        pendulumDF.print(pendulumDF.getName()+".osim");
        addPluginForce(pendulumDF.getName()+".osim" , forcePluginNode );

User avatar
Ibraheem Al-Dhamari
Posts: 35
Joined: Thu Mar 16, 2017 2:44 am

Re: undefined reference error when adding a user plugin force to a model using cpp

Post by Ibraheem Al-Dhamari » Tue Feb 11, 2020 5:01 am

I have two related questions:

Code: Select all

    
    for (int i=0; i<3;i++)    {
        if (bodyCoMVelGround[i]>0) oppVelSign[i] = -1;   // get opposite sign of CoM velocity (GROUND coordinate system)
        if (bodyCoMVelGround[i]<0) oppVelSign[i] = 1;
        if (bodyCoMVelGround[i]==0) oppVelSign[i] = 0;

        dragForceGround[i] = oppVelSign[i] * get_coefficient() * std::pow(bodyCoMVelGround[i], get_exponent()); // calculate drag force in the GROUND coordinate system
    }
1. Do we need the above 3 if statements? can't we just write:

Code: Select all

        
        dragForceGround[i] = (-bodyCoMVelGround[i]) * get_coefficient() * std::pow(bodyCoMVelGround[i], get_exponent()); // calculate drag force in the GROUND coordinate system
2. Why we have to write the computation part two times one computation and one for reporting?

Code: Select all

void BodyDragForce::computeForce(const SimTK::State& s, SimTK::Vector_<SimTK::SpatialVec>& bodyForces, SimTK::Vector& generalizedForces) 
OpenSim::Array<double> BodyDragForce::getRecordValues(const SimTK::State& s) 

User avatar
Ibraheem Al-Dhamari
Posts: 35
Joined: Thu Mar 16, 2017 2:44 am

Re: undefined reference error when adding a user plugin force to a model using cpp

Post by Ibraheem Al-Dhamari » Wed Mar 18, 2020 3:28 am

The code for adding the force is updated using SimTK::Xml class,

viewtopicPhpbb.php?f=91&t=11667&p=32743&start=0&view=

POST REPLY