From 6839d1224810a47b982eefc271368c4416824662 Mon Sep 17 00:00:00 2001 From: Mansard <nmansard@laas.fr> Date: Mon, 22 Sep 2014 21:33:56 +0200 Subject: [PATCH] First complete bindings of spatial algerba. --- CMakeLists.txt | 80 +++++++++++++++++++++------- python/bindings.py | 53 +++++++++++++++++++ src/python/__init__.py | 52 ++++++++++++++++++ src/python/force.hpp | 98 ++++++++++++++++++++++++++++++++++ src/python/inertia.hpp | 116 +++++++++++++++++++++++++++++++++++++++++ src/python/module.cpp | 26 +++++++++ src/python/motion.hpp | 108 ++++++++++++++++++++++++++++++++++++++ src/python/python.cpp | 35 +++++++++++++ src/python/python.hpp | 16 ++++++ src/python/se3.hpp | 112 +++++++++++++++++++++++++++++++++++++++ src/python/utils.py | 16 ++++++ 11 files changed, 692 insertions(+), 20 deletions(-) create mode 100644 python/bindings.py create mode 100644 src/python/__init__.py create mode 100644 src/python/force.hpp create mode 100644 src/python/inertia.hpp create mode 100644 src/python/module.cpp create mode 100644 src/python/motion.hpp create mode 100644 src/python/python.cpp create mode 100644 src/python/python.hpp create mode 100644 src/python/se3.hpp create mode 100644 src/python/utils.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c7593842..ef510c34c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,13 @@ SET(CMAKE_VERBOSE_MAKEFILE True) project(${PROJECT_NAME}) SETUP_PROJECT() +IF(WIN32) + SET(LINK copy_if_different) +ELSE(WIN32) + SET(LINK create_symlink) +ENDIF(WIN32) + + # ---------------------------------------------------- # --- DEPENDANCIES ----------------------------------- # ---------------------------------------------------- @@ -30,7 +37,7 @@ ADD_REQUIRED_DEPENDENCY("urdfdom >= v0.3.0") # ---------------------------------------------------- # --- INCLUDE ---------------------------------------- # ---------------------------------------------------- -SET(${PROJECT_NAME}_HEADERS +SET(HEADERS exception.hpp assert.hpp spatial/symmetric3.hpp @@ -76,31 +83,64 @@ MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/tools") MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/algorithm") MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/python") -FOREACH(header ${${PROJECT_NAME}_HEADERS}) +FOREACH(header ${HEADERS}) GET_FILENAME_COMPONENT(headerName ${header} NAME) - IF(WIN32) - execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${${PROJECT_NAME}_SOURCE_DIR}/src/${header} - ${${PROJECT_NAME}_BINARY_DIR}/include/${PROJECT_NAME}/${header}) - ELSE(WIN32) - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink - ${${PROJECT_NAME}_SOURCE_DIR}/src/${header} - ${${PROJECT_NAME}_BINARY_DIR}/include/${PROJECT_NAME}/${header}) - ENDIF(WIN32) - INSTALL(FILES ${${PROJECT_NAME}_SOURCE_DIR}/${header} - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${PROJECT_NAME} + GET_FILENAME_COMPONENT(headerPath ${header} PATH) + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E ${LINK} + ${${PROJECT_NAME}_SOURCE_DIR}/src/${header} + ${${PROJECT_NAME}_BINARY_DIR}/include/${PROJECT_NAME}/${header}) + INSTALL(FILES ${${PROJECT_NAME}_SOURCE_DIR}/src/${header} + DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${PROJECT_NAME}/${headerPath} PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE) ENDFOREACH(header) # ---------------------------------------------------- -# --- TARGETS ---------------------------------------- +# --- PYTHON ----------------------------------------- # ---------------------------------------------------- -MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/lib/python") - -ADD_LIBRARY(pynocchio SHARED src/python/module.cpp src/python/python.cpp) -PKG_CONFIG_USE_DEPENDENCY(pynocchio eigenpy) -SET_TARGET_PROPERTIES(pynocchio PROPERTIES PREFIX "") -TARGET_LINK_LIBRARIES(pynocchio ${Boost_LIBRARIES} eigenpy) +FINDPYTHON() +MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/lib/python/${PROJECT_NAME}") + +# --- COMPILE WRAPPER +SET(PYWRAP ${PROJECT_NAME}_pywrap) +ADD_LIBRARY(${PYWRAP} SHARED src/python/module.cpp src/python/python.cpp) +PKG_CONFIG_USE_DEPENDENCY(${PYWRAP} eigenpy) +TARGET_LINK_LIBRARIES(${PYWRAP} ${Boost_LIBRARIES} eigenpy) +SET_TARGET_PROPERTIES(${PYWRAP} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/python/${PROJECT_NAME}") + +# --- INSTALL SCRIPTS +SET(PYTHON_FILES + python/__init__.py + python/utils.py +) +FOREACH(python ${PYTHON_FILES}) + GET_FILENAME_COMPONENT(pythonFile ${python} NAME) + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E ${LINK} + ${${PROJECT_NAME}_SOURCE_DIR}/src/${python} + ${${PROJECT_NAME}_BINARY_DIR}/lib/python/${PROJECT_NAME}/${pythonFile}) + + # Tag pyc file as generated. + #SET_SOURCE_FILES_PROPERTIES( + # "${CMAKE_CURRENT_BINARY_DIR}/lib/python/${pythonFile}c" + # PROPERTIES GENERATED TRUE) + + # Clean generated files. + #SET_PROPERTY( + # DIRECTORY APPEND PROPERTY + # ADDITIONAL_MAKE_CLEAN_FILES + # "${CMAKE_CURRENT_BINARY_DIR}/lib/python/${pythonFile}c") + + #INSTALL(CODE + # "EXECUTE_PROCESS(COMMAND + # \"${PYTHON_EXECUTABLE}\" + # \"-m py_compile\" + # \"${CMAKE_CURRENT_BINARY_DIR}/lib/python/${pythonFile}\") ") + + INSTALL(FILES + "${CMAKE_CURRENT_BINARY_DIR}/lib/python/${PROJECT_NAME}/${pythonFile}" + # "${CMAKE_CURRENT_BINARY_DIR}/lib/python/${pythonFile}c" + DESTINATION ${PYTHON_SITELIB}/${PROJECT_NAME}) +ENDFOREACH(python) # ---------------------------------------------------- # --- UNITTESTS -------------------------------------- diff --git a/python/bindings.py b/python/bindings.py new file mode 100644 index 000000000..8f37b9e4a --- /dev/null +++ b/python/bindings.py @@ -0,0 +1,53 @@ +import pinocchio as se3 +from pinocchio.utils import * + +# --- SE3 +R = rand([3,3]); [R,tmp,tmp] = npl.svd(R) +p = rand(3) +m = se3.SE3(R,p) +X = np.vstack( [ np.hstack([ R, skew(p)*R ]), np.hstack([ zero([3,3]), R ]) ]) +assert( isapprox(m.action(),X)) +M = np.vstack( [ np.hstack([R,p]), np.matrix('0 0 0 1',np.double) ] ) +assert( isapprox(m.homogeneous(),M) ) + + +p = rand(3) +assert(isapprox(m*p,m.rotation*p+m.translation)) +assert(isapprox(m.actInv(p),m.rotation.T*p-m.rotation.T*m.translation)) + +p = np.vstack([p,1]) +assert(isapprox(m*p,m.homogeneous()*p)) +assert(isapprox(m.actInv(p),npl.inv(m.homogeneous())*p)) + +p = rand(6) +assert(isapprox(m*p,m.action()*p)) +assert(isapprox(m.actInv(p),npl.inv(m.action())*p)) + +# --- Motion +assert(isapprox(se3.Motion.Zero().vector(),zero(6))) +v = se3.Motion.Random() +assert(isapprox((m*v).vector(),m.action()*v.vector())) +assert(isapprox((m.actInv(v)).vector(),npl.inv(m.action())*v.vector())) +vv = v.linear; vw = v.angular +assert(isapprox( v.vector(),np.vstack([vv,vw]) )) +assert(isapprox((v**v).vector(),zero(6))) + +# --- Force --- +assert(isapprox(se3.Force.Zero().vector(),zero(6))) +f = se3.Force.Random() +ff = f.linear; ft = f.angular +assert(isapprox( f.vector(),np.vstack([ff,ft]) )) + +assert(isapprox((m*f).vector(),npl.inv(m.action().T)*f.vector())) +assert(isapprox((m.actInv(f)).vector(),m.action().T*f.vector())) +f = se3.Force( np.vstack([ v.vector()[3:], v.vector()[:3] ]) ) +assert(isapprox( (v**f).vector(),zero(6) )) + +# --- Inertia --- +Y1 = se3.Inertia.Random() +Y2 = se3.Inertia.Random() +Y=Y1+Y2 +assert(isapprox(Y1.matrix()+Y2.matrix(),Y.matrix())) +assert(isapprox((Y*v).vector(),Y.matrix()*v.vector())) +assert(isapprox( (m*Y).matrix(),m.inverse().action().T*Y.matrix()*m.inverse().action())) +assert(isapprox( (m.actInv(Y)).matrix(),m.action().T*Y.matrix()*m.action())) diff --git a/src/python/__init__.py b/src/python/__init__.py new file mode 100644 index 000000000..03bbca1e6 --- /dev/null +++ b/src/python/__init__.py @@ -0,0 +1,52 @@ +import numpy as np +import libpinocchio_pywrap as se3 +import utils + +# --- SE3 action --- +def SE3act(m,x): + assert(isinstance(m,se3.SE3)) + if isinstance(x,np.ndarray): + if x.shape[0]==3: + return m.rotation*x+m.translation + elif x.shape[0] == 4: + return m.homogeneous()*x + elif x.shape[0] == 6: + return m.action()*x + else: raise Exception('Error: m can only act on linear object of size 3, 4 and 6.') + elif 'se3Action' in x.__class__.__dict__: + return x.se3Action(m) + else: + print 'Error: SE3 cannot act on the given object' +setattr(se3.SE3,'__mul__',SE3act) +setattr(se3.SE3,'act',SE3act) + +def SE3actinv(m,x): + assert(isinstance(m,se3.SE3)) + if isinstance(x,np.ndarray): + if x.shape[0]==3: + return m.rotation.T*x-m.rotation.T*m.translation + elif x.shape[0] == 4: + return m.inverse().homogeneous()*x + elif x.shape[0] == 6: + return m.inverse().action()*x + else: raise Exception('Error: m can only act on linear object of size 3, 4 and 6.') + elif 'se3Action' in x.__class__.__dict__: + return x.se3ActionInverse(m) + else: + print 'Error: SE3 cannot act on the given object' +setattr(se3.SE3,'actInv',SE3actinv) + +# --- M6/F6 cross product -- +def SE3cross(self,y): + assert(isinstance(self,se3.Motion)) + if isinstance(y,se3.Motion): + return self.cross_motion(y) + elif isinstance(y,se3.Force): + return self.cross_force(y) + else: raise Exception('Error: SE3 cross product only apply on M6xM6 or M6xF6.') +setattr(se3.Motion,'__pow__',SE3cross) +setattr(se3.Motion,'cross',SE3cross) + +from libpinocchio_pywrap import * + + diff --git a/src/python/force.hpp b/src/python/force.hpp new file mode 100644 index 000000000..13eb0b61f --- /dev/null +++ b/src/python/force.hpp @@ -0,0 +1,98 @@ +#ifndef __se3_python_force_hpp__ +#define __se3_python_force_hpp__ + +#include <eigenpy/exception.hpp> +#include <eigenpy/eigenpy.hpp> +#include "pinocchio/spatial/force.hpp" + + +namespace eigenpy +{ + template<> + struct UnalignedEquivalent<se3::Force> + { + typedef se3::ForceTpl<double,Eigen::DontAlign> type; + }; +} // namespace eigenpy + +namespace se3 +{ + namespace python + { + namespace bp = boost::python; + + template<typename Force> + struct ForcePythonVisitor + : public boost::python::def_visitor< ForcePythonVisitor<Force> > + { + typedef typename eigenpy::UnalignedEquivalent<Force>::type Force_fx; + typedef typename Force::Matrix3 Matrix3; + typedef typename Force::Matrix6 Matrix6; + typedef typename Force::Vector6 Vector6; + typedef typename Force::Vector3 Vector3; + + typedef typename Force_fx::Matrix3 Matrix3_fx; + typedef typename Force_fx::Matrix6 Matrix6_fx; + typedef typename Force_fx::Vector6 Vector6_fx; + typedef typename Force_fx::Vector3 Vector3_fx; + + public: + + static PyObject* convert(Force const& m) + { + Force_fx m_fx = m; + return boost::python::incref(boost::python::object(m_fx).ptr()); + } + + template<class PyClass> + void visit(PyClass& cl) const + { + cl + .def(bp::init<Vector3_fx,Vector3_fx> + ((bp::arg("linear"),bp::arg("angular")), + "Initialize from linear and angular components (dont mix the order).")) + .def(bp::init<Vector6_fx>((bp::arg("Vector 6d")),"Init from vector 6 [f,n]")) + + .add_property("linear",&ForcePythonVisitor::getLinear,&ForcePythonVisitor::setLinear) + .add_property("angular",&ForcePythonVisitor::getAngular,&ForcePythonVisitor::setAngular) + .def("vector",&Force_fx::toVector) + .def("se3Action",&Force_fx::se3Action) + .def("se3ActionInverse",&Force_fx::se3ActionInverse) + .def("__str__",&ForcePythonVisitor::toString) + + .def("Random",&Force_fx::Random) + .staticmethod("Random") + .def("Zero",&Force_fx::Zero) + .staticmethod("Zero") + ; + } + + static Vector3_fx getLinear( const Force_fx & self ) { return self.linear(); } + static void setLinear( Force_fx & self, const Vector3_fx & R ) { self.linear(R); } + static Vector3_fx getAngular( const Force_fx & self ) { return self.angular(); } + static void setAngular( Force_fx & self, const Vector3_fx & R ) { self.angular(R); } + static std::string toString(const Force_fx& m) + { std::ostringstream s; s << m; return s.str(); } + + + static void expose() + { + bp::class_<Force_fx>("Force", + "Force vectors, in se3* == F^6.\n\n" + "Supported operations ...", + bp::init<>()) + .def(ForcePythonVisitor<Force>()) + ; + + bp::to_python_converter< Force,ForcePythonVisitor<Force> >(); + } + + + }; + + + + }} // namespace se3::python + +#endif // ifndef __se3_python_se3_hpp__ + diff --git a/src/python/inertia.hpp b/src/python/inertia.hpp new file mode 100644 index 000000000..f0d96275d --- /dev/null +++ b/src/python/inertia.hpp @@ -0,0 +1,116 @@ +#ifndef __se3_python_inertia_hpp__ +#define __se3_python_inertia_hpp__ + +#include <eigenpy/exception.hpp> +#include <eigenpy/eigenpy.hpp> +#include "pinocchio/spatial/inertia.hpp" + + +namespace eigenpy +{ + template<> + struct UnalignedEquivalent<se3::Inertia> + { + typedef se3::InertiaTpl<double,Eigen::DontAlign> type; + }; + +} // namespace eigenpy + +namespace se3 +{ + namespace python + { + namespace bp = boost::python; + + template<typename Inertia> + struct InertiaPythonVisitor + : public boost::python::def_visitor< InertiaPythonVisitor<Inertia> > + { + typedef typename eigenpy::UnalignedEquivalent<Inertia>::type Inertia_fx; + typedef typename Inertia::Matrix3 Matrix3; + typedef typename Inertia::Matrix6 Matrix6; + typedef typename Inertia::Vector6 Vector6; + typedef typename Inertia::Vector3 Vector3; + + typedef typename Inertia_fx::Matrix3 Matrix3_fx; + typedef typename Inertia_fx::Matrix6 Matrix6_fx; + typedef typename Inertia_fx::Vector6 Vector6_fx; + typedef typename Inertia_fx::Vector3 Vector3_fx; + typedef typename Inertia_fx::Motion Motion_fx ; + public: + + static PyObject* convert(Inertia const& m) + { + Inertia_fx m_fx = m; + return bp::incref(bp::object(m_fx).ptr()); + } + + template<class PyClass> + void visit(PyClass& cl) const + { + cl + .def("__init__", + bp::make_constructor(&InertiaPythonVisitor::makeFromMCI, + bp::default_call_policies(), + (bp::arg("mass"),bp::arg("lever"),bp::arg("inertia"))), + "Initialize from mass, lever and 3d inertia.") + + .add_property("mass",&Inertia_fx::mass) + .add_property("lever",&InertiaPythonVisitor::lever) + .add_property("inertia",&InertiaPythonVisitor::inertia) + + .def("matrix",&Inertia_fx::matrix) + .def("se3Action",&Inertia_fx::se3Action) + .def("se3ActionInverse",&Inertia_fx::se3ActionInverse) + + .def("__str__",&InertiaPythonVisitor::toString) + .def( bp::self + bp::self) + .def( bp::self * bp::other<Motion_fx>() ) + + .def("Identity",&Inertia_fx::Identity) + .staticmethod("Identity") + .def("Zero",&Inertia_fx::Zero) + .staticmethod("Zero") + .def("Random",&Inertia_fx::Random) + .staticmethod("Random") + ; + } + + static Inertia_fx* makeFromMCI(const double & mass, + const Vector3_fx & lever, + const Matrix3_fx & inertia) + { + if(! inertia.isApprox(inertia.transpose()) ) + throw eigenpy::Exception("The 3d inertia should be symmetric."); + if( (Eigen::Vector3d::UnitX().transpose()*inertia*Eigen::Vector3d::UnitX()<0) + || (Eigen::Vector3d::UnitY().transpose()*inertia*Eigen::Vector3d::UnitY()<0) + || (Eigen::Vector3d::UnitZ().transpose()*inertia*Eigen::Vector3d::UnitZ()<0) ) + throw eigenpy::Exception("The 3d inertia should be positive."); + return new Inertia_fx(mass,lever,inertia); + } + static Matrix3_fx inertia(const Inertia_fx& Y) { return Y.inertia().matrix(); } + static Vector3_fx lever(const Inertia_fx& Y) { return Y.lever(); } + static std::string toString(const Inertia_fx& m) + { std::ostringstream s; s << m; return s.str(); } + + static void expose() + { + bp::class_<Inertia_fx>("Inertia", + "Inertia matrix, in L(se3,se3*) == R^6x6.\n\n" + "Supported operations ...", + bp::init<>()) + .def(InertiaPythonVisitor<Inertia>()) + ; + + bp::to_python_converter< Inertia,InertiaPythonVisitor<Inertia> >(); + } + + + }; + + + + }} // namespace se3::python + +#endif // ifndef __se3_python_se3_hpp__ + diff --git a/src/python/module.cpp b/src/python/module.cpp new file mode 100644 index 000000000..2e089477a --- /dev/null +++ b/src/python/module.cpp @@ -0,0 +1,26 @@ +#include <eigenpy/eigenpy.hpp> +#include <eigenpy/geometry.hpp> +#include "pinocchio/python/python.hpp" + +#include <iostream> + +namespace bp = boost::python; + +BOOST_PYTHON_MODULE(libpinocchio_pywrap) +{ + eigenpy::enableEigenPy(); + eigenpy::exposeAngleAxis(); + eigenpy::exposeQuaternion(); + + typedef Eigen::Matrix<double,6,6> Matrix6d; + typedef Eigen::Matrix<double,6,1> Vector6d; + + eigenpy::enableEigenPySpecific<Matrix6d,Matrix6d>(); + eigenpy::enableEigenPySpecific<Vector6d,Vector6d>(); + + se3::python::exposeSE3(); + se3::python::exposeForce(); + se3::python::exposeMotion(); + se3::python::exposeInertia(); +} + diff --git a/src/python/motion.hpp b/src/python/motion.hpp new file mode 100644 index 000000000..425e42fbe --- /dev/null +++ b/src/python/motion.hpp @@ -0,0 +1,108 @@ +#ifndef __se3_python_motion_hpp__ +#define __se3_python_motion_hpp__ + +#include <eigenpy/exception.hpp> +#include <eigenpy/eigenpy.hpp> +#include "pinocchio/spatial/motion.hpp" +#include "pinocchio/spatial/force.hpp" + + +namespace eigenpy +{ + template<> + struct UnalignedEquivalent<se3::Motion> + { + typedef se3::MotionTpl<double,Eigen::DontAlign> type; + }; +} // namespace eigenpy + +namespace se3 +{ + namespace python + { + namespace bp = boost::python; + + template<typename Motion> + struct MotionPythonVisitor + : public boost::python::def_visitor< MotionPythonVisitor<Motion> > + { + typedef typename Motion::Force Force; + typedef typename Motion::Matrix3 Matrix3; + typedef typename Motion::Matrix6 Matrix6; + typedef typename Motion::Vector6 Vector6; + typedef typename Motion::Vector3 Vector3; + + typedef typename eigenpy::UnalignedEquivalent<Motion>::type Motion_fx; + typedef typename Motion_fx::Force Force_fx; + typedef typename Motion_fx::Matrix3 Matrix3_fx; + typedef typename Motion_fx::Matrix6 Matrix6_fx; + typedef typename Motion_fx::Vector6 Vector6_fx; + typedef typename Motion_fx::Vector3 Vector3_fx; + + public: + + static PyObject* convert(Motion const& m) + { + Motion_fx m_fx = m; + return boost::python::incref(boost::python::object(m_fx).ptr()); + } + + template<class PyClass> + void visit(PyClass& cl) const + { + cl + .def(bp::init<Vector3_fx,Vector3_fx> + ((bp::arg("linear"),bp::arg("angular")), + "Initialize from linear and angular components (dont mix the order).")) + .def(bp::init<Vector6_fx>((bp::arg("Vector 6d")),"Init from vector 6 [f,n]")) + + .add_property("linear",&MotionPythonVisitor::getLinear,&MotionPythonVisitor::setLinear) + .add_property("angular",&MotionPythonVisitor::getAngular,&MotionPythonVisitor::setAngular) + .def("vector",&Motion_fx::toVector) + .def("se3Action",&Motion_fx::se3Action) + .def("se3ActionInverse",&Motion_fx::se3ActionInverse) + + .def("cross_motion",&MotionPythonVisitor::cross_motion) + .def("cross_force",&MotionPythonVisitor::cross_force) + + .def("__str__",&MotionPythonVisitor::toString) + + .def("Random",&Motion_fx::Random) + .staticmethod("Random") + .def("Zero",&Motion_fx::Zero) + .staticmethod("Zero") + ; + } + + static Vector3_fx getLinear( const Motion_fx & self ) { return self.linear(); } + static void setLinear( Motion_fx & self, const Vector3_fx & R ) { self.linear(R); } + static Vector3_fx getAngular( const Motion_fx & self ) { return self.angular(); } + static void setAngular( Motion_fx & self, const Vector3_fx & R ) { self.angular(R); } + + static Motion_fx cross_motion( const Motion_fx& m1,const Motion_fx& m2 ) { return m1.cross(m2); } + static Force_fx cross_force( const Motion_fx& m,const Force_fx& f ) { return m.cross(f); } + + static std::string toString(const Motion_fx& m) + { std::ostringstream s; s << m; return s.str(); } + + static void expose() + { + bp::class_<Motion_fx>("Motion", + "Motion vectors, in se3* == F^6.\n\n" + "Supported operations ...", + bp::init<>()) + .def(MotionPythonVisitor<Motion>()) + ; + + bp::to_python_converter< Motion,MotionPythonVisitor<Motion> >(); + } + + + }; + + + + }} // namespace se3::python + +#endif // ifndef __se3_python_se3_hpp__ + diff --git a/src/python/python.cpp b/src/python/python.cpp new file mode 100644 index 000000000..0b33429e5 --- /dev/null +++ b/src/python/python.cpp @@ -0,0 +1,35 @@ +#include "pinocchio/python/python.hpp" +#include "pinocchio/python/se3.hpp" +#include "pinocchio/python/force.hpp" +#include "pinocchio/python/motion.hpp" +#include "pinocchio/python/inertia.hpp" + +namespace se3 +{ + namespace python + { + void exposeSE3() + { + SE3PythonVisitor<SE3>::expose(); + } + void exposeForce() + { + ForcePythonVisitor<Force>::expose(); + } + void exposeMotion() + { + MotionPythonVisitor<Motion>::expose(); + } + void exposeInertia() + { + InertiaPythonVisitor<Inertia>::expose(); + } + void exposeStatics() + { + + + } + + + + }} // namespace se3::python diff --git a/src/python/python.hpp b/src/python/python.hpp new file mode 100644 index 000000000..e82dec694 --- /dev/null +++ b/src/python/python.hpp @@ -0,0 +1,16 @@ +#ifndef __se3_python_python_hpp__ +#define __se3_python_python_hpp__ + +namespace se3 +{ + namespace python + { + void exposeSE3(); + void exposeForce(); + void exposeMotion(); + void exposeInertia(); + + }} // namespace se3::python + +#endif // ifndef __se3_python_python_hpp__ + diff --git a/src/python/se3.hpp b/src/python/se3.hpp new file mode 100644 index 000000000..08660c838 --- /dev/null +++ b/src/python/se3.hpp @@ -0,0 +1,112 @@ +#ifndef __se3_python_se3_hpp__ +#define __se3_python_se3_hpp__ + +#include <eigenpy/exception.hpp> +#include <eigenpy/eigenpy.hpp> +#include "pinocchio/spatial/se3.hpp" + + +namespace eigenpy +{ + template<> + struct UnalignedEquivalent<se3::SE3> + { + typedef se3::SE3Tpl<double,Eigen::DontAlign> type; + }; +} // namespace eigenpy + +namespace se3 +{ + namespace python + { + namespace bp = boost::python; + + template<typename SE3> + struct SE3PythonVisitor + : public boost::python::def_visitor< SE3PythonVisitor<SE3> > + { + typedef typename eigenpy::UnalignedEquivalent<SE3>::type SE3_fx; + typedef typename SE3::Matrix3 Matrix3; + typedef typename SE3::Matrix6 Matrix6; + typedef typename SE3::Matrix4 Matrix4; + typedef typename SE3::Vector6 Vector6; + typedef typename SE3::Vector3 Vector3; + + typedef typename SE3_fx::Matrix3 Matrix3_fx; + typedef typename SE3_fx::Matrix6 Matrix6_fx; + typedef typename SE3_fx::Matrix4 Matrix4_fx; + typedef typename SE3_fx::Vector6 Vector6_fx; + typedef typename SE3_fx::Vector3 Vector3_fx; + + public: + + static PyObject* convert(SE3 const& m) + { + SE3_fx m_fx = m; + return boost::python::incref(boost::python::object(m_fx).ptr()); + } + + template<class PyClass> + void visit(PyClass& cl) const + { + cl + .def(bp::init<Matrix3_fx,Vector3_fx> + ((bp::arg("Rotation"),bp::arg("translation")), + "Initialize from rotation and translation.")) + .def(bp::init<int> + ((bp::arg("trivial arg (should be 1)")), + "Init to identity.")) + + .add_property("rotation",&SE3PythonVisitor::getRotation,&SE3PythonVisitor::setRotation) + .add_property("translation",&SE3PythonVisitor::getTranslation,&SE3PythonVisitor::setTranslation) + .def("homogeneous",&SE3_fx::toHomogeneousMatrix) + .def("action",&SE3_fx::toActionMatrix) + + .def("inverse", &SE3_fx::inverse) + .def("act_point", &SE3PythonVisitor::act_point) + .def("actInv_point", &SE3PythonVisitor::actInv_point) + .def("act_se3", &SE3PythonVisitor::act_se3) + .def("actInv_se3", &SE3PythonVisitor::actInv_se3) + + .def("__str__",&SE3PythonVisitor::toString) + + .def("Identity",&SE3_fx::Identity) + .staticmethod("Identity") + .def("Random",&SE3_fx::Random) + .staticmethod("Random") + ; + } + + static Matrix3_fx getRotation( const SE3_fx & self ) { return self.rotation(); } + static void setRotation( SE3_fx & self, const Matrix3_fx & R ) { self.rotation(R); } + static Vector3_fx getTranslation( const SE3_fx & self ) { return self.translation(); } + static void setTranslation( SE3_fx & self, const Vector3_fx & p ) { self.translation(p); } + + static Vector3_fx act_point( const SE3_fx & self, const Vector3_fx & pt ) { return self.act(pt); } + static Vector3_fx actInv_point( const SE3_fx & self, const Vector3_fx & pt ) { return self.actInv(pt); } + static SE3_fx act_se3( const SE3_fx & self, const SE3_fx & x ) { return self.act(x); } + static SE3_fx actInv_se3( const SE3_fx & self, const SE3_fx & x ) { return self.actInv(x); } + static std::string toString(const SE3_fx& m) + { std::ostringstream s; s << m; return s.str(); } + + static void expose() + { + bp::class_<SE3_fx>("SE3", + "SE3 transformations, composing rotation and translation.\n\n" + "Supported operations ...", + bp::init<>()) + .def(SE3PythonVisitor<SE3>()) + ; + + bp::to_python_converter< SE3,SE3PythonVisitor<SE3> >(); + } + + + }; + + + + }} // namespace se3::python + +#endif // ifndef __se3_python_se3_hpp__ + diff --git a/src/python/utils.py b/src/python/utils.py new file mode 100644 index 000000000..0b2152f0e --- /dev/null +++ b/src/python/utils.py @@ -0,0 +1,16 @@ +import numpy as np +import numpy.linalg as npl + +eye = lambda n: np.matrix(np.eye(n),np.double) +zero = lambda n: np.matrix(np.zeros([n,1] if isinstance(n,int) else n),np.double) +rand = lambda n: np.matrix(np.random.rand(n,1) if isinstance(n,int) else np.random.rand(n[0],n[1]),np.double) + +def skew(p): + x=p[0];y=p[1];z=p[2] + return np.matrix([ [ 0,-z,y ], [ z,0,-x ], [ -y,x,0 ] ],np.double) + +def isapprox(a,b,epsilon=1e-6): + if issubclass(a.__class__,np.ndarray) and issubclass(b.__class__,np.ndarray): + return np.allclose(a,b,epsilon) + else: + return abs(a-b)<epsilon -- GitLab