From 1caabe21e2a10773a782e49a9152d10887123f80 Mon Sep 17 00:00:00 2001 From: Mansard <nmansard@laas.fr> Date: Tue, 23 Sep 2014 15:54:33 +0200 Subject: [PATCH] First complete wrapping of the model. --- CMakeLists.txt | 2 + python/test_model.py | 17 +++- src/python/eigen_container.hpp | 56 ++++++++++++ src/python/handler.hpp | 76 ++++++++++++++++ src/python/model.hpp | 161 +++++++-------------------------- src/python/python.cpp | 4 + 6 files changed, 183 insertions(+), 133 deletions(-) create mode 100644 src/python/eigen_container.hpp create mode 100644 src/python/handler.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index edd3d06a1..2dce72f8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,8 @@ SET(HEADERS algorithm/cholesky.hpp algorithm/kinematics.hpp algorithm/center-of-mass.hpp + python/eigen_container.hpp + python/handler.hpp python/python.hpp python/se3.hpp python/force.hpp diff --git a/python/test_model.py b/python/test_model.py index 36cf3e675..1f750db01 100644 --- a/python/test_model.py +++ b/python/test_model.py @@ -2,11 +2,20 @@ import pinocchio as se3 from pinocchio.utils import * model = se3.Model.BuildEmptyModel() -print model +assert(model.nbody==1 and model.nq==0 and model.nv==0) + model = se3.Model.BuildHumanoidSimple() -print model -print "Bye bye" +nb=28 # We should have 28 bodies, thus 27 joints, one of them a free-flyer. +assert(model.nbody==nb and model.nq==nb-1+6 and model.nv==nb-1+5) +model.inertias[1] = model.inertias[2] +assert( isapprox(model.inertias[1].np,model.inertias[2].np) ) +model.jointPlacements[1] = model.jointPlacements[2] +assert( isapprox(model.jointPlacements[1].np,model.jointPlacements[2].np) ) +assert(model.parents[0]==0 and model.parents[1] == 0) +model.parents[2] = model.parents[1] +assert( model.parents[2] == model.parents[1] ) +assert(model.names[0] == "universe" ) +assert( isapprox(model.gravity.np,np.matrix('0; 0; -9.81; 0; 0; 0')) ) data = model.createData() -print model.inertias() diff --git a/src/python/eigen_container.hpp b/src/python/eigen_container.hpp new file mode 100644 index 000000000..296d2c3bb --- /dev/null +++ b/src/python/eigen_container.hpp @@ -0,0 +1,56 @@ +#ifndef __se3_python_eigen_container_hpp__ +#define __se3_python_eigen_container_hpp__ + +#include <eigenpy/exception.hpp> +#include <eigenpy/eigenpy.hpp> + +#include <boost/shared_ptr.hpp> +#include <boost/python/return_internal_reference.hpp> + +namespace se3 +{ + namespace python + { + namespace bp = boost::python; + + /* Expose a std::vector containing aligned (Eigen) objects, whose unaligned + * correspondance is defined in eigenpy::UnalignedEquivalent. + * Simply call the "expose" method from inside the python module. + */ + template< typename EigenObject > + struct PyWraperForAlignedStdVector + : public boost::python::def_visitor< PyWraperForAlignedStdVector<EigenObject> > + { + typedef typename eigenpy::UnalignedEquivalent<EigenObject>::type EigenObject_fx; + typedef std::vector<EigenObject> stdVectorAligned; + + template<class PyClass> + void visit(PyClass& cl) const + { + cl + .def("__getitem__", &PyWraperForAlignedStdVector::getItem) + .def("__setitem__", &PyWraperForAlignedStdVector::setItem) + .def("__len__", &PyWraperForAlignedStdVector::length) + ; + } + + static EigenObject getItem( const stdVectorAligned & Ys,int i) + { return Ys[i]; } + + static void setItem( stdVectorAligned & Ys, + int i,const EigenObject_fx & Y) + { Ys[i] = Y; } + static int length( const stdVectorAligned & Ys ) + { return Ys.size(); } + + static void expose(const std::string & className) + { + bp::class_<stdVectorAligned>(className.c_str()) + .def(PyWraperForAlignedStdVector()); + } + }; + + }} // namespace se3::python + +#endif // ifndef __se3_python_eigen_container_hpp__ + diff --git a/src/python/handler.hpp b/src/python/handler.hpp new file mode 100644 index 000000000..1800d02a6 --- /dev/null +++ b/src/python/handler.hpp @@ -0,0 +1,76 @@ +#ifndef __se3_python_handler_hpp__ +#define __se3_python_handler_hpp__ + +#include <boost/shared_ptr.hpp> + +namespace se3 +{ + namespace python + { + + /* This handler is designed first for holding the C++ Model object, then + * generalized for the C++ Data object. It might be used as well for + * handling other object whose ownership is shared between python and C++. + * + * There might be several way of building and owning a model object: + * - build from C++ (as static or dynamic, wathever), and owned by + * C++. In that case, use a ModelHandler with no ownership ModelHandler( + * Model*,false). + * - build from C++ but owned by python. Python would be in charge of + * destroying it when it is done. In that case, use + * ModelHandler(Model*,true). Take care not to destroy the object by + * yourself, otherwise there might have access to deallocated memory and + * double destruction. + * - build and managed by shared_ptr. It is the best way, in the sense + * that the object is manage both by C++ and python. It will be released + * only when both are done with it. In that case, simply give Python the + * shared_ptr ModelHandler( shared_ptr<Model> ). + * + * It is not possible to construct a Model from python, because of Eigen + * memory alignement. The python visitor does not define any + * constructor. Instead, some static makers (function allocating memory and + * returning a pointer Model *) are implemented. When Python is making + * such a Model, it is stored in a shared_ptr and can be access directly + * from C++. By default, when passing a Model to python, it is kept as a + * raw pointer. Access is then done from python to the C++ memory without + * any guarantee. If you want to enforce memory access, prefer transmitting + * a shared_ptr to python. + */ + template<typename CppObject> + struct Handler + { + typedef boost::shared_ptr<CppObject> SmartPtr_t; + typedef CppObject * Ptr_t; + + SmartPtr_t smptr; + Ptr_t rawptr; + bool smart; + + Handler(CppObject * cppobj,bool transmitOwnership=false) + : smptr( transmitOwnership ? cppobj : NULL ) + , rawptr( cppobj ) + , smart( transmitOwnership ) {} + Handler( SmartPtr_t cppobj ) + : smptr(cppobj), rawptr(NULL), smart(true) {} + ~Handler() + { + std::cout << "Destroy cppobj handler " << std::endl; + if( (!smart) && (rawptr!=NULL) ) delete rawptr; + } + + CppObject * ptr() { return smart ? smptr.get() : rawptr; } + const CppObject * ptr() const { return smart ? smptr.get() : rawptr; } + CppObject * operator->() { return ptr(); } + const CppObject * operator->() const { return ptr(); } + + CppObject & get() { return smart ? *smptr : *rawptr; } + const CppObject & get() const { return smart ? *smptr : *rawptr; } + CppObject & operator*() { return get(); } + const CppObject& operator*() const { return get(); } + }; + + + }} // namespace se3::python + +#endif // ifndef __se3_python_handler_hpp__ + diff --git a/src/python/model.hpp b/src/python/model.hpp index 6d8a689c9..8ba728078 100644 --- a/src/python/model.hpp +++ b/src/python/model.hpp @@ -1,136 +1,30 @@ #ifndef __se3_python_model_hpp__ #define __se3_python_model_hpp__ +#include <boost/python/suite/indexing/vector_indexing_suite.hpp> #include <eigenpy/exception.hpp> #include <eigenpy/eigenpy.hpp> + #include "pinocchio/multibody/model.hpp" #include "pinocchio/multibody/parser/sample-models.hpp" +#include "pinocchio/python/eigen_container.hpp" +#include "pinocchio/python/handler.hpp" -#include <boost/shared_ptr.hpp> -#include <boost/python/suite/indexing/vector_indexing_suite.hpp> -#include <boost/python/return_internal_reference.hpp> namespace se3 { namespace python { - - /* There might be several way of building and owning a model object: - - * - build from C++ (as static or dynamic, wathever), and owned by - * C++. In that case, use a ModelHandler with no ownership ModelHandler( - * Model*,false). - * - build from C++ but owned by python. Python would be in charge of - * destroying it when it is done. In that case, use - * ModelHandler(Model*,true). Take care not to destroy the object by - * yourself, otherwise there might have access to deallocated memory and - * double destruction. - * - build and managed by shared_ptr. It is the best way, in the sense - * that the object is manage both by C++ and python. It will be released - * only when both are done with it. In that case, simply give Python the - * shared_ptr ModelHandler( shared_ptr<Model> ). - * - * It is not possible to construct a Model from python, because of Eigen - * memory alignement. The python visitor does not define any - * constructor. Instead, some static makers (function allocating memory and - * returning a pointer Model *) are implemented. When Python is making - * such a Model, it is stored in a shared_ptr and can be access directly - * from C++. By default, when passing a Model to python, it is kept as a - * raw pointer. Access is then done from python to the C++ memory without - * any guarantee. If you want to enforce memory access, prefer transmitting - * a shared_ptr to python. - */ - struct ModelHandler - { - typedef boost::shared_ptr<Model> SmartPtr_t; - typedef Model * Ptr_t; - - SmartPtr_t smptr; - Ptr_t rawptr; - bool smart; - - ModelHandler(Model * model,bool transmitOwnership=false) - : smptr( transmitOwnership ? model : NULL ) - , rawptr( model ) - , smart( transmitOwnership ) {} - ModelHandler( SmartPtr_t model ) - : smptr(model), rawptr(NULL), smart(true) {} - ~ModelHandler() - { - std::cout << "Destroy model handler " << std::endl; - if( (!smart) && (rawptr!=NULL) ) delete rawptr; - } - - Model * ptr() { return smart ? smptr.get() : rawptr; } - const Model * ptr() const { return smart ? smptr.get() : rawptr; } - Model * operator->() { return ptr(); } - const Model * operator->() const { return ptr(); } - - Model & get() { return smart ? *smptr : *rawptr; } - const Model & get() const { return smart ? *smptr : *rawptr; } - Model & operator*() { return get(); } - const Model& operator*() const { return get(); } - }; - - namespace bp = boost::python; - struct InertiasVisitor - : public boost::python::def_visitor< InertiasVisitor > - { - typedef typename eigenpy::UnalignedEquivalent<Inertia>::type Inertia_fx; - typedef std::vector<Inertia> Inertias; - - template<class PyClass> - void visit(PyClass& cl) const - { - cl - .def("__getitem__", &InertiasVisitor::getItem) - .def("__setitem__", &InertiasVisitor::setItem) - .def("__len__",&InertiasVisitor::length) - ; - } - - static Inertia getItem( const Inertias & Ys,int i) { return Ys[i]; } - static void setItem( Inertias & Ys,int i,const Inertia_fx & Y) - { - std::cout << "Y = " << Y << std::endl; - Ys[i] = Y; - } - static int length( const Inertias & Ys ) { return Ys.size(); } - - }; - - - struct ParentsVisitor - : public boost::python::def_visitor< ParentsVisitor > - { - typedef Model::Index Index; - typedef std::vector<Index> Parents; - - template<class PyClass> - void visit(PyClass& cl) const - { - cl - .def("__getitem__", &ParentsVisitor::getItem) - .def("__setitem__", &ParentsVisitor::setItem) - .def("__len__",&ParentsVisitor::length) - ; - } - - static Index getItem( const Parents & Ys,int i) { return Ys[i]; } - static void setItem( Parents & Ys,int i,const Index & Y) - { - std::cout << "p = " << Y << std::endl; - Ys[i] = Y; - } - static int length( const Parents & Ys ) { return Ys.size(); } - }; - + typedef Handler<Model> ModelHandler; struct ModelPythonVisitor : public boost::python::def_visitor< ModelPythonVisitor > { + public: + typedef Model::Index Index; + typedef typename eigenpy::UnalignedEquivalent<Motion>::type Motion_fx; public: @@ -146,7 +40,7 @@ namespace se3 } /* --- Exposing C++ API to python through the handler ----------------- */ - template<class PyClass> + template<class PyClass> void visit(PyClass& cl) const { cl @@ -155,13 +49,22 @@ namespace se3 .def("__str__",&ModelPythonVisitor::toString) + .add_property("nq", &ModelPythonVisitor::nq) + .add_property("nv", &ModelPythonVisitor::nv) + .add_property("nbody", &ModelPythonVisitor::nbody) .add_property("inertias", bp::make_function(&ModelPythonVisitor::inertias, bp::return_internal_reference<>()) ) - + .add_property("jointPlacements", + bp::make_function(&ModelPythonVisitor::jointPlacements, + bp::return_internal_reference<>()) ) .add_property("parents", bp::make_function(&ModelPythonVisitor::parents, bp::return_internal_reference<>()) ) + .add_property("names", + bp::make_function(&ModelPythonVisitor::names, + bp::return_internal_reference<>()) ) + .add_property("gravity",&ModelPythonVisitor::gravity,&ModelPythonVisitor::setGravity) .def("BuildEmptyModel",&ModelPythonVisitor::maker_empty) .staticmethod("BuildEmptyModel") @@ -175,13 +78,15 @@ namespace se3 static boost::shared_ptr<Data> createData(const ModelHandler& m ) { return boost::shared_ptr<Data>( new Data(*m) ); } - typedef std::vector<Inertia> Inertias_t; - static Inertias_t & inertias( ModelHandler & m ) { return m->inertias; } - static void set_inertias( ModelHandler & m,const Inertias_t & Ys ) - { m->inertias = Ys; } - static ParentsVisitor::Parents & parents( ModelHandler & m ) { return m->parents; } - - + static int nq( ModelHandler & m ) { return m->nq; } + static int nv( ModelHandler & m ) { return m->nv; } + static int nbody( ModelHandler & m ) { return m->nbody; } + static std::vector<Inertia> & inertias( ModelHandler & m ) { return m->inertias; } + static std::vector<SE3> & jointPlacements( ModelHandler & m ) { return m->jointPlacements; } + static std::vector<Model::Index> & parents( ModelHandler & m ) { return m->parents; } + static std::vector<std::string> & names ( ModelHandler & m ) { return m->names; } + static Motion gravity( ModelHandler & m ) { return m->gravity; } + static void setGravity( ModelHandler & m,const Motion_fx & g ) { m->gravity = g; } static ModelHandler maker_empty() { @@ -200,12 +105,10 @@ namespace se3 /* --- Expose --------------------------------------------------------- */ static void expose() { - bp::class_<InertiasVisitor::Inertias>("ListInertia") - .def(InertiasVisitor()); - - bp::class_<ParentsVisitor::Parents>("ListInertia") - .def(ParentsVisitor()); - //.def(bp::vector_indexing_suite<Inertias_t>()); + bp::class_< std::vector<Index> >("StdVec_Index") + .def(bp::vector_indexing_suite< std::vector<Index> >()); + bp::class_< std::vector<std::string> >("StdVec_StdString") + .def(bp::vector_indexing_suite< std::vector<std::string> >()); bp::class_<ModelHandler>("Model", "Articulated rigid body model (const)", diff --git a/src/python/python.cpp b/src/python/python.cpp index 58337655b..8545b6171 100644 --- a/src/python/python.cpp +++ b/src/python/python.cpp @@ -14,18 +14,22 @@ namespace se3 void exposeSE3() { SE3PythonVisitor<SE3>::expose(); + PyWraperForAlignedStdVector<SE3>::expose("StdVect_SE3"); } void exposeForce() { ForcePythonVisitor<Force>::expose(); + PyWraperForAlignedStdVector<Force>::expose("StdVec_Force"); } void exposeMotion() { MotionPythonVisitor<Motion>::expose(); + PyWraperForAlignedStdVector<Motion>::expose("StdVec_Motion"); } void exposeInertia() { InertiaPythonVisitor<Inertia>::expose(); + PyWraperForAlignedStdVector<Inertia>::expose("StdVec_Inertia"); } void exposeModel() { -- GitLab