diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..367d52393f33bb9d6c7a68b3ceda1e8cd51f1724 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,76 @@ +# +# Copyright 2014 CNRS +# + +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +INCLUDE(cmake/base.cmake) +INCLUDE(cmake/boost.cmake) +INCLUDE(cmake/cpack.cmake) +INCLUDE(cmake/eigen.cmake) +INCLUDE(cmake/python.cmake) + +SET(PROJECT_NAME pinocchio) +SET(PROJECT_DESCRIPTION "Rigid multi body dynamics algorithms") +SET(PROJECT_URL "http://github.com/stac-of-tasks/pinocchio") + +# Disable -Werror on Unix for now. +SET(CXX_DISABLE_WERROR True) +SET(CMAKE_VERBOSE_MAKEFILE True) + +project(${PROJECT_NAME}) +SETUP_PROJECT() + +# ---------------------------------------------------- +# --- DEPENDANCIES ----------------------------------- +# ---------------------------------------------------- +ADD_REQUIRED_DEPENDENCY("eigenpy >= v1.0.1") + +# ---------------------------------------------------- +# --- INCLUDE ---------------------------------------- +# ---------------------------------------------------- +SET(${PROJECT_NAME}_HEADERS + spatial/se3.hpp + spatial/motion.hpp + spatial/force.hpp + spatial/inertia.hpp + spatial/fwd.hpp + spatial/skew.hpp + multibody/joint.hpp + multibody/model.hpp + tools/timer.hpp +) + +MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio") +MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/spatial") +MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/multibody") +MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio/tools") + +FOREACH(header ${${PROJECT_NAME}_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} + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE) +ENDFOREACH(header) + +# ---------------------------------------------------- +# --- TARGETS ---------------------------------------- +# ---------------------------------------------------- +ADD_EXECUTABLE(tspatial EXCLUDE_FROM_ALL unittest/tspatial.cpp) +PKG_CONFIG_USE_DEPENDENCY(tspatial eigenpy) + +ADD_EXECUTABLE(rnea EXCLUDE_FROM_ALL unittest/rnea.cpp) +PKG_CONFIG_USE_DEPENDENCY(rnea eigenpy) + +SETUP_PROJECT_FINALIZE() +SETUP_PROJECT_CPACK() + diff --git a/COPYING.LESSER b/COPYING.LESSER new file mode 100644 index 0000000000000000000000000000000000000000..65c5ca88a67c30becee01c5a8816d964b03862f9 --- /dev/null +++ b/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md index fced57885fe637eea7a3981c87ec7fd60c0ed500..2670ad6c74934c7187036ceb74c77970cb761837 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,31 @@ -pinocchio -========= +eigenpy +=========== -Dynamic computations using Spatial Algebra +Setup +----- + +To compile this package, it is recommended to create a separate build +directory: + + mkdir _build + cd _build + cmake [OPTIONS] .. + make install + +Please note that CMake produces a `CMakeCache.txt` file which should +be deleted to reconfigure a package from scratch. + + +### Dependencies + +The matrix abstract layer depends on several packages which +have to be available on your machine. + + - Libraries: + - eigen3 + - System tools: + - CMake (>=2.6) + - pkg-config + - usual compilation tools (GCC/G++, make, etc.) + - Python 2.7 + - Boost python diff --git a/doc/Doxyfile.extra.in b/doc/Doxyfile.extra.in new file mode 100644 index 0000000000000000000000000000000000000000..733ef2e24209ec5b24b9b6d59a4de4106805ccbf --- /dev/null +++ b/doc/Doxyfile.extra.in @@ -0,0 +1,7 @@ +INPUT = @CMAKE_SOURCE_DIR@/src \ + @CMAKE_SOURCE_DIR@/doc/additionalDoc +IMAGE_PATH = @CMAKE_SOURCE_DIR@/doc/pictures + +FILE_PATTERNS = *.cc *.cpp *.h *.hpp *.hxx + +TAGFILES = \ diff --git a/doc/additionalDoc/package.hh b/doc/additionalDoc/package.hh new file mode 100644 index 0000000000000000000000000000000000000000..2ce3915d1857ce660f704ab0e3bd2a636e8930aa --- /dev/null +++ b/doc/additionalDoc/package.hh @@ -0,0 +1,26 @@ +/* + * Copyright 2010, + * François Bleibel, + * Olivier Stasse, + * + * CNRS/AIST + * + * This file is part of sot-dynamic. + * sot-dynamic 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-dynamic 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-dynamic. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \mainpage +\section soth_section_introduction Introduction + +The soth package is ... + +**/ diff --git a/doc/footer.html b/doc/footer.html new file mode 100644 index 0000000000000000000000000000000000000000..535d304e7f00b9b77241c4bf6e63e883164c570c --- /dev/null +++ b/doc/footer.html @@ -0,0 +1,11 @@ + <br><br> + <hr> + <center> + <img src="./pictures/footer.jpg" Height=100> + <br>soth library documentation</br> + </center> + <hr> + </center> + </body> + </head> + diff --git a/doc/header.html b/doc/header.html new file mode 100644 index 0000000000000000000000000000000000000000..ca3e578a0a37b115093297273260a2729989cf19 --- /dev/null +++ b/doc/header.html @@ -0,0 +1,7 @@ +<HTML> + <HEAD> + <TITLE>soth library documentation</TITLE> + <LINK HREF="package.css" REL="stylesheet" TYPE="text/css"> + </HEAD> + <BODY> + diff --git a/doc/latex/se3.tex b/doc/latex/se3.tex new file mode 100644 index 0000000000000000000000000000000000000000..470a9aa2db4976d90cc2a1ffc7e82f7206ee47ce --- /dev/null +++ b/doc/latex/se3.tex @@ -0,0 +1,193 @@ +\documentclass[11pt,twoside,a4paper]{article} +\usepackage{amssymb} +\usepackage{amsmath} + + +\newcommand{\BIN}{\begin{bmatrix}} +\newcommand{\BOUT}{\end{bmatrix}} +\newcommand{\calR}{\mathcal{R}} +\newcommand{\calE}{\mathcal{E}} +\newcommand{\repr}{\cong} +\newcommand{\dpartial}[2]{\frac{\partial{#1}}{\partial{#2}}} +\newcommand{\ddpartial}[2]{\frac{\partial^2{#1}}{\partial{#2}^2}} + +\newcommand{\aRb}{\ \leftidx{^A}R_B} +\newcommand{\aMb}{\ \leftidx{^A}M_B} +\newcommand{\amb}{\ \leftidx{^A}m_B} +\newcommand{\apb}{{\ \leftidx{^A}{AB}{}}} +\newcommand{\aXb}{\ \leftidx{^A}X_B} + +\newcommand{\bRa}{\ \leftidx{^B}R_A} +\newcommand{\bMa}{\ \leftidx{^B}M_A} +\newcommand{\bma}{\ \leftidx{^B}m_A} +\newcommand{\bpa}{\ \leftidx{^B}{BA}{}} +\newcommand{\bXa}{\ \leftidx{^B}X_A} + +\newcommand{\ap}{\ \leftidx{^A}p} +\newcommand{\bp}{\ \leftidx{^B}p} + +\newcommand{\afs}{\ \leftidx{^A}\phi} +\newcommand{\bfs}{\ \leftidx{^B}\phi} +\newcommand{\af}{\ \leftidx{^A}f} +\renewcommand{\bf}{\ \leftidx{^B}f} +\newcommand{\an}{\ \leftidx{^A}\tau} +\newcommand{\bn}{\ \leftidx{^B}\tau} + +\newcommand{\avs}{\ \leftidx{^A}\nu} +\newcommand{\bvs}{\ \leftidx{^B}\nu} +\newcommand{\w}{\omega} +\newcommand{\av}{\ \leftidx{^A}v} +\newcommand{\bv}{\ \leftidx{^B}v} +\newcommand{\aw}{\ \leftidx{^A}\w} +\newcommand{\bw}{\ \leftidx{^B}\w} + +\newcommand{\aI}{\ \leftidx{^A}I} +\newcommand{\bI}{\ \leftidx{^B}I} +\newcommand{\cI}{\ \leftidx{^C}I} +\newcommand{\aY}{\ \leftidx{^A}Y} +\newcommand{\bY}{\ \leftidx{^B}Y} +\newcommand{\cY}{\ \leftidx{^c}Y} +\newcommand{\aXc}{\ \leftidx{^A}X_C} +\newcommand{\aMc}{\ \leftidx{^A}M_C} +\newcommand{\aRc}{\ \leftidx{^A}R_C} +\newcommand{\apc}{\ \leftidx{^A}{AC}{}} +\newcommand{\bXc}{\ \leftidx{^B}X_C} +\newcommand{\bRc}{\ \leftidx{^B}R_C} +\newcommand{\bMc}{\ \leftidx{^B}M_C} +\newcommand{\bpc}{\ \leftidx{^B}{BC}{}} + +\usepackage{leftidx} + +\begin{document} +\title{SE(3) operations} +\author{N. Mansard} +\date{} +\maketitle + +\section{Rigid transformation} +$$m : p \in \calE(3) \rightarrow m(p) \in E(3)$$ +Transformation from B to A: +$$\amb : \bp \in \calR^3 \repr \calE(3) \ \rightarrow\ \ap = \amb(\bp) = \aMb\ \bp$$ +$$ \ap = \aRb \bp + \apb$$ +$$\aMb = \BIN \aRb & \apb \\ 0 & 1 \BOUT $$ +Transformation from A to B: +$$\bp = \aRb^T \ap + \bpa, \quad\textrm{with }\bpa = - \aRb^T \apb$$ +$$\bMa = \BIN \aRb^T & - \aRb^T \apb \\ 0 & 1 \BOUT $$ +For Featherstone, $E = \bRa =\aRb^T$ and $r = \apb$. Then: +$$\bMa = \BIN \bRa & -\bRa \apb \\ 0 & 1 \BOUT = \BIN E & -E r \\ 0 & 1 \BOUT $$ +$$\aMb = \BIN \bRa^T & \apb \\ 0 & 1 \BOUT = \BIN E^T & r \\ 0 & 1 \BOUT $$ + +\section{Composition} +$$ \aMb \bMc = \BIN \aRb \bRc & \apb + \aRb \bpc \\ 0 & 1 \BOUT $$ +$$ \aMb^{-1} \aMc = \BIN \aRb^T \aRc & \aRb^T (\apc - \apb) \\ 0 & 1 \BOUT $$ + + + +\section{Motion Application} +$$\avs = \BIN \av \\ \aw \BOUT$$ +$$\bvs = \bXa\avs$$ +$$ \aXb = \BIN \aRb & \apb_\times \aRb \\ 0 & \aRb \BOUT $$ +$$ \aXb^{-1} = \bXa = \BIN \aRb^T & -\aRb^T \apb_\times \\ 0 & \aRb^T \BOUT $$ +For Featherstone, $E = \bRa =\aRb^T$ and $r = \apb$. Then: +$$ \bXa = \BIN \bRa & - \bRa \apb_\times \\ 0 & \bRa \BOUT = \BIN E & -E r_\times \\ 0 & E \BOUT$$ +$$ \aXb = \BIN \bRa^T & \apb_\times \bRa^T \\ 0 & \bRa^T \BOUT = \BIN E^T & r_\times E^T \\ 0 & E^T \BOUT$$ + +\section{Force Application} +$$\afs = \BIN \af \\ \an \BOUT$$ +$$\bfs = \bXa^* \afs$$ +For any $\phi,\nu$, $\phi\dot\nu = \afs^T \avs = \bfs^T \bvs$ and then: +$$\aXb^* = \aXb^{-T} = \BIN \aRb & 0 \\ \apb_\times \aRb & \aRb \BOUT$$ +(because $\apb_\times^T = - \apb_\times$). +$$\aXb^{-*} = \bXa^* = \BIN \aRb^T & 0 \\ -\aRb^T \apb_\times & \aRb^T \BOUT$$ +For Featherstone, $E = \bRa =\aRb^T$ and $r = \apb$. Then: +$$\bXa^* = \BIN \bRa & 0 \\ -\bRa \apb_\times & \bRa \BOUT = \BIN E & 0 \\ - E r_\times & E \BOUT $$ +$$\aXb^* = \BIN \bRa^T & 0 \\ \apb_\times \bRa^T & \bRa^T \BOUT = \BIN E^T & 0 \\ r_\times E^T & E^T \BOUT $$ + +\section{Inertia application} + +$$\aY: \avs \rightarrow \afs = \aY \avs$$ + +Coordinate transform: +$$\bY = \bXa^{*} \aY \bXa^{-1}$$ +since: +$$\bfs = \bXa^* \bfs = \bXa^* \aI \aXb \bvs$$ +Cannonical form. The inertia about the center of mass $c$ is: +$$\cY = \BIN m & 0 \\ 0 & \cI \BOUT$$ +Expressed in any non-centered coordinate system $A$: +$$\aY = \aXc^* \cI \aXc^{-1} = \BIN m & m\ ^AAC_\times^T \\ m\ ^AAC_\times & \aI + m \apc_\times \apc\times^T \BOUT $$ +Changing the coordinates system from $B$ to $A$: +$$\aY = \aXb^* \bXc^* \cI \bXc^{-1} \aXb^{-1} $$ +$$ = \BIN m & m [\apb + \aRb \bpc]_\times^T \\ m [\apb + \aRb \bpc]_\times & \aRb \bI \aRb^T - m [\apb + \aRb \bpc]_\times^2 \BOUT$$ +Representing the spatial inertia in $B$ by the triplet $(m,\bpc,\bI)$, the expression in $A$ is: +$$ \amb: \bY = (m,\bpc,\bI) \rightarrow \aY = (m,\apb+\aRb \bpc,\aRb \bI \aRb^T)$$ +Similarly, the inverse action is: +$$ \amb^{-1}: \aY \rightarrow \bY = (m,\aRb^T(^AAC-\apb),\aRb^T\aI \aRb) $$ + +Motion-to-force map: +$$ Y \nu = \BIN m & mc_\times^T \\ mc_\times & I+mc_\times c_\times^T \BOUT \BIN v \\ \omega \BOUT + = \BIN m v - mc \times \omega \\ mc \times v + I \omega - mc \times ( c\times \omega) \BOUT$$ + +Nota: the square of the cross product is: +$$\BIN x\\y\\z\BOUT_ \times^2 = \BIN 0&-z&y \\ z&0&-x \\ -y&x&0 \BOUT^2 = \BIN -y^2-z^2&xy&xz \\ xy&-x^2-z^2&yz \\ xz&yz&-x^2-y^2 \BOUT$$ +There is no computational interest in using it. + +\section{Cross products} + +Motion-motion product: +$$\nu_1 \times \nu_2 = \BIN v_1\\\omega_1\BOUT \times \BIN v_2\\\omega_2\BOUT = \BIN v_1 \times \omega_2 + \omega_1 \times v_2 \\ \omega_1 \times \omega_2 \BOUT $$ +Motion-force product: +$$\nu \times \phi = \BIN v\\\omega\BOUT \times \BIN f\\ \tau \BOUT = \BIN \omega \times f \\ \omega \times \tau + v \times f \BOUT $$ +A special form of the motion-force product is often used: +\begin{align*}\nu \times (Y \nu) &= \nu \times \BIN mv - mc\times \omega \\ mc\times v + I \omega - mc\times(c\times \omega) \BOUT \\&= \BIN m \omega\times v - \omega\times(mc\times \omega) \\ \omega \times ( mc \times v) + \omega \times (I\omega) -\omega \times(c \times( mc\times \omega)) -v\times(mc \times \omega)\BOUT\end{align*} +Setting $\beta=mc \times \omega$, this product can be written: +$$\nu \times (Y \nu) = \BIN \omega \times (m v - \beta) \\ \omega \times( c \times (mv-\beta)+I\omega) - v \times \beta \BOUT$$ +This last form cost five $\times$, four $+$ and one $3\times3$ matrix-vector multiplication. + +\section{Joint} + +We denote by $1$ the coordinate system attached to the parent (predecessor) body at the joint input, ad by $2$ the coordinate system attached to the (child) successor body at the joint output. We neglect the possible time variation of the joint model (ie the bias velocity $\sigma = \nu(q,0)$ is null). + +The joint geometry is expressed by the rigid transformation from the input to the ouput, parametrized by the joint coordinate system $q \in \mathcal{Q}$: +$$ ^2m_1 \repr \ ^2M_1(q)$$ + +The joint velocity (i.e. the velocity of the child wrt. the parent in the child coordinate system) is: +$$^2\nu_{12} = \nu_J(q,v_q) = \ ^2S(q) v_q $$ +where $^2S$ is the joint Jacobian (or constraint matrix) that define the motion subspace allowed by the joint, and $v_q$ is the joint coordinate velocity (i.e. an element of the Lie algebra associated with the joint coordinate manifold), which would be $v_q=\dot q$ when $\dot q$ exists. + +The joint acceleration is: +$$^2\alpha_{12} = S \dot v_q + c_J + \ ^2\nu_{1} \times \ ^2\nu_{12}$$ +where $c_J = \sum_{i=1}^{n_q} \dpartial{S}{q_i} \dot q_i$ (null in the usual cases) and $^2\nu_{1}$ is the velocity of the parent body with respect to an absolute (Galilean) coordinate system\footnote{The abosulte velocity $\nu_{1}$ is also the relative velocity wrt. the Galilean coordinate system $\Omega$. The exhaustive notation should be $\nu_{\Omega1}$ but $\nu_1$ is prefered for simplicity.}. + +The joint calculations take as input the joint position $q$ and velocity $v_q$ and should output $^2M_1$, $^2\nu_{12}$ and $^2c$ (this last vector being often a trivial $0_6$ vector). In addition, the joint model should store the position of the joint input in the central coordinate system of the previous joint $^0m_1$ which is a constant value. + +The joint integrator computes the exponential map associated with the joint manifold. The function inputs are the initial position $q_0$, the velocity $v_q$ and the length of the integration interval $t$. It computes $q_t$ as: +$$ q_t = q_0 + \int_0^t v_q dt$$ +For the simple vectorial case where $v_q=\dot q$, we have $q_t=q_0 + t v_q$. Written in the more general case of a Lie groupe, we have $q_t = q_0 exp(t v_q)$ where $exp$ denotes the exponential map (i.e. integration of a constant vector field from the Lie algebra into the Lie group). This integration only consider first order explicit Euler. More general integrators (e.g. Runge-Kutta in Lie groupes) remains to be written. Adequate references are welcome. + +\section{RNEA} + +\subsection{Initialization} +$$^0\nu_0 = 0 ; \ ^0\alpha_0 = -g$$ + +In the following, the coordinate system $i$ is attached to the output of the joint (child body), while $lambda(i)$ is the central coordinate system attached to the parent joint. The coordinated system associated with the joint input is denoted by $i_0$. The constant rigid transformation from $\lambda(i)$ to the joint input is then $^{\lambda(i)}M_{i_0}$. + + +\subsection{Forward loop} +For each joint $i$, update the joint calculation $\mathbf j_i$.calc($q,v_q$). This compute $\mathbf{j}.M = \ ^{\lambda(i)}M_{i_0}(q)$, $\mathbf{j}.\nu = \ ^i\nu_{{\lambda(i)}i}(q,v_q)$, $\mathbf{j}.S = \ ^iS(q)$ and $\mathbf{j}.c = \sum_{k=1}^{n_q} \dpartial{^iS}{q_k} \dot q_k$. Attached to the joint is also its placement in body $\lambda(i)$ denoted by $\mathbf{j}.M_0 =\ ^{\lambda(i)}M_{i_0}$. Then: + +$$^{\lambda(i)}M_i = \mathbf{j}.M_0 \ \mathbf{j}.M $$ +$$^0M_i = \ ^0M_{\lambda(i)} \ ^{\lambda(i)}M_i$$ +$$^i\nu_{i}= \ ^{\lambda(i)}X_i^{-1} \ ^{\lambda(i)}\nu_{{\lambda(i)}} + \mathbf{j}.\nu$$ +$$^i\alpha_{i}= \ ^{\lambda(i)}X_i^{-1} \ ^{\lambda(i)}\alpha_{{\lambda(i)}} + \mathbf{j}.S \dot v_q + \mathbf{j}.c + \ ^i\nu_{i} \times \mathbf{j}.\nu$$ +$$^i\phi_i= \ ^iY_i \ ^i\alpha_i + \ ^i\nu_i \times \ ^iY_i \ ^i\nu_i - \ ^0X_i^{-*}\ ^0\phi_i^{ext}$$ + +\subsection{Backward loop} +For each joint $i$ from leaf to root, do: +$$\tau_i = \mathbf{j}.S^T \ ^i\phi_i$$ +$$^{\lambda(i)}\phi_{\lambda(i)} \ +\!\!= \ ^{\lambda(i)}X_i^{*} \ ^i\phi_i$$ + +\subsection{Nota} +It is more efficient to apply $X^{-1}$ than $X$. Similarly, it is more efficient to apply $X^{-*}$ than $X^*$. Therefore, it is better to store the transformations $^{\lambda(i)}m_i$ and $^0m_i$ than $^im_{\lambda(i)}$ and $^im_0$. + + +\end{document} diff --git a/doc/package.css b/doc/package.css new file mode 100644 index 0000000000000000000000000000000000000000..f992392b8d5f99929d049a86a66bfaf807cdc431 --- /dev/null +++ b/doc/package.css @@ -0,0 +1,230 @@ +body { + font-family: 'Lucida Grande','Lucida Sans Unicode',Verdana,Sans-Serif; + color: #5D5D5D; +} + +dl { + border: 1.5px #82b6d7 solid; + width: 97%; + padding: 5px; + color: #330077; +} + +code { + color: #3C9A35; +} + +td.md { + color: #0066CC; +} + +h1 { + padding-top: 50px; + padding: 0px; + font-family: 'Lucida Grande','Lucida Sans Unicode',Verdana,Sans-Serif; + font-variant: small-caps; + color:#0066CC; + text-align: center; +} + +h2,h3,hr { + magin-top: 15px; + padding: 0px; + font-family: 'Lucida Grande','Lucida Sans Unicode',Verdana,Sans-Serif; + font-variant: small-caps; + color:#0066CC; +} + +h4 { + color: #3C9A35; +} + +a:link { + font-weight: bold; + text-decoration: none; + color:#0066CC +} + +a:hover, a:active { + text-decoration: underline; + color: #3C9A35; +} + +a:visited { + font-weight: bold; + color: #3C9A35; + text-decoration: none; +} + +DIV.memitem +{ + spacing: 10px; + width:100%; + background:#FFFFFF; + font-size:100%; + line-height:normal; + border-width: 1px; + border-style: solid; + border-color: #808080; + -moz-border-radius: 8px 8px 8px 8px; +} + +DIV.memproto +{ + width:100%; + background:#F0F0F0; + font-size:100%; + line-height:normal; + border-width: 1px; + border-style: solid; + border-color: #808080; + -moz-border-radius: 8px 8px 8px 8px; +} + +DIV.memdoc +{ + padding: 10px; + width:100%; + font-size:100%; + line-height:normal; +} + +DIV.tabs +{ + float : left; + width : 100%; + background : url("tab_b.gif") repeat-x bottom; + margin-bottom : 4px; +} + +DIV.tabs UL +{ + margin : 0px; + padding-left : 10px; + list-style : none; +} + +DIV.tabs LI, DIV.tabs FORM +{ + display : inline; + margin : 0px; + padding : 0px; +} + +DIV.tabs FORM +{ + float : right; +} + +DIV.tabs A +{ + float : left; + background : url("tab_r.gif") no-repeat right top; + border-bottom : 1px solid #84B0C7; + font-size : x-small; + font-weight : bold; + text-decoration : none; +} + +DIV.tabs A:hover +{ + background-position: 100% -150px; +} + +DIV.tabs A:link, DIV.tabs A:visited, +DIV.tabs A:active, DIV.tabs A:hover +{ + color: #1A419D; +} + +DIV.tabs SPAN +{ + float : left; + display : block; + background : url("tab_l.gif") no-repeat left top; + padding : 5px 9px; + white-space : nowrap; +} + +DIV.tabs INPUT +{ + float : right; + display : inline; + font-size : 1em; +} + +DIV.tabs TD +{ + font-size : x-small; + font-weight : bold; + text-decoration : none; +} + + + +DIV.tabs SPAN {float : none;} + +DIV.tabs A:hover SPAN +{ + background-position: 0% -150px; +} + +DIV.tabs LI#current A +{ + background-position: 100% -150px; + border-width : 0px; +} + +DIV.tabs LI#current SPAN +{ + background-position: 0% -150px; + padding-bottom : 6px; +} + +DIV.nav +{ + background : none; + border : none; + border-bottom : 1px solid #84B0C7; +} + +DIV.groupHeader +{ + padding-top: 30px; + padding-bottom: 20px; + background : none; + border : none; + border-bottom : 1px solid #84B0C7; + font-family: 'Lucida Grande','Lucida Sans Unicode',Verdana,Sans-Serif; + font-variant: small-caps; + font-size: 14pt; + color:#0066CC; +} + +.directory p +{ + margin: 0px; + white-space: nowrap; + font-family: 'Lucida Grande','Lucida Sans Unicode',Verdana,Sans-Serif; + font-size: 10pt; + font-weight: normal; +} + + +.directory h3 +{ + font-family: 'Lucida Grande','Lucida Sans Unicode',Verdana,Sans-Serif; + margin: 0px; + margin-top: 1em; + padding-bottom: 20px; + font-size: 12pt; + font-variant: small-caps; + text-align: center; +} + +.directory a:visited { + font-weight: bold; + text-decoration: none; + color:#0066CC +} + diff --git a/doc/pictures/HRP2.jpg b/doc/pictures/HRP2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..377681b20575b32c765e0ad9403723a0e3ea5c75 Binary files /dev/null and b/doc/pictures/HRP2.jpg differ diff --git a/doc/pictures/footer.jpg b/doc/pictures/footer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..377681b20575b32c765e0ad9403723a0e3ea5c75 Binary files /dev/null and b/doc/pictures/footer.jpg differ diff --git a/doc/pictures/footer.txt b/doc/pictures/footer.txt new file mode 100644 index 0000000000000000000000000000000000000000..5c1322c49a0ff9e84f4bd2384f25dafb52e252b0 --- /dev/null +++ b/doc/pictures/footer.txt @@ -0,0 +1,2 @@ +Copy in this directory the image you wish to use in the footer of the documentation or edit file sot-core/doc/footer.html and +remove this file. diff --git a/doc/pictures/soth.tif b/doc/pictures/soth.tif new file mode 100644 index 0000000000000000000000000000000000000000..3f93b62ae3164d17aa3f31dba849de7c09735af3 Binary files /dev/null and b/doc/pictures/soth.tif differ diff --git a/pinocchio.pc.cmake b/pinocchio.pc.cmake new file mode 100644 index 0000000000000000000000000000000000000000..1810450ffa0d87196719a704b0093aa199bd87c5 --- /dev/null +++ b/pinocchio.pc.cmake @@ -0,0 +1,13 @@ +prefix=${CMAKE_INSTALL_PREFIX} +exec_prefix=${install_pkg_prefix} +libdir=${install_pkg_exec_prefix}/lib +includedir=${install_pkg_prefix}/include +datarootdir=${install_pkg_prefix}/share +docdir=${install_pkg_datarootdir}/doc/${PROJECT_NAME} + +Name: ${PROJECT_NAME} +Description: +Version: ${PROJECT_VERSION} +Requires: ${PACKAGE_REQUIREMENTS} +Libs: ${LIBDIR_KW}${install_pkg_libdir} ${${PROJECT_NAME}_LDFLAGS} +Cflags: -I${install_pkg_include_dir} ${${PROJECT_NAME}_CXXFLAGS} diff --git a/src/multibody/joint.hpp b/src/multibody/joint.hpp new file mode 100644 index 0000000000000000000000000000000000000000..680f950ef15a13fb344f21be82e2747f362e61c3 --- /dev/null +++ b/src/multibody/joint.hpp @@ -0,0 +1,174 @@ +#ifndef __se3_joint_hpp__ +#define __se3_joint_hpp__ + + +#include "pinocchio/spatial/fwd.hpp" +#include "pinocchio/spatial/motion.hpp" +#include <Eigen/Geometry> + +namespace se3 +{ + template<class C> + struct traits {}; + + template<typename JointData> + struct JointDataBase + { + typedef typename traits<JointData>::Constraint_t Constraint_t; + typedef typename traits<JointData>::Transformation_t Transformation_t; + typedef typename traits<JointData>::Velocity_t Velocity_t; + typedef typename traits<JointData>::Bias_t Bias_t; + + const Constraint_t & S() { return static_cast<JointData*>(this)->S; } + const Transformation_t & M() { return static_cast<JointData*>(this)->M; }; + const Velocity_t & v() { return static_cast<JointData*>(this)->v; }; + const Bias_t & c() { return static_cast<JointData*>(this)->c; }; + }; + + + + template<typename Joint> + struct JointBase + { + typedef typename traits<Joint>::JointData JointData; + typedef typename traits<Joint>::Placement_t Placement_t; + typedef typename traits<Joint>::Configuration_t Configuration_t; + typedef typename traits<Joint>::Velocity_t Velocity; + + JointData createData() { return static_cast<Joint*>(this)->createData(); } + }; + + + + // struct JointRXData; + // template<> + // struct traits<JointRXData> + // { + // typedef Eigen::Matrix<double,6,1> Constraint_t; + // typedef se3::SE3 Transformation_t; + // typedef Eigen::Matrix<double,6,1> Velocity_t; + // typedef BiasZero Bias_t; + // }; + + // struct JointRXData : public JointDataBase<JointRXData> + // { + // typedef typename traits<JointRXData>::Constraint_t Constraint_t; + // typedef typename traits<JointRXData>::Transformation_t Transformation_t; + // typedef typename traits<JointRXData>::Velocity_t Velocity_t; + // typedef typename traits<JointRXData>::Bias_t Bias_t; + + // Constraint_t S; + // Transformation_t M; + // Velocity_t v; + // Bias_t c; + + // JointRXData() { S << 0,0,0,1,0,0; } + // }; + + // struct JointRX; + // template<> + // struct traits<JointRX> + // { + // typedef JointRXData JointData; + // typedef SE3 Placement_t; + // typedef double Configuration_t; + // typedef double Velocity_t; + // }; + + // struct JointRX : public JointBase<JointRX> + // { + // typedef traits<JointRX>::JointData JointData; + + // JointData createData() const { return JointData(); } + // void calc( JointData& data, Configuration_t q, Configuration_t v ) + // { + + + + // } + // }; + + struct JointDataRX; + struct JointModelRX; + + template<> + struct traits<JointDataRX> + { + struct BiasZero {}; + template<typename D> + friend const Eigen::MatrixBase<D> & operator+ ( const BiasZero&, const Eigen::MatrixBase<D>& v) { return v; } + template<typename D> + friend const Eigen::MatrixBase<D> & operator+ ( const Eigen::MatrixBase<D>& v, const BiasZero&) { return v; } + friend const Motion & operator+ ( const Motion& v, const BiasZero&) { return v; } + + typedef Eigen::Matrix<double,6,1> Constraint_t; + typedef SE3 Transformation_t; + typedef Motion Velocity_t; + typedef BiasZero Bias_t; + typedef JointModelRX JointModel; + }; + + struct JointDataRX + { + typedef typename traits<JointDataRX>::Constraint_t Constraint_t; + typedef typename traits<JointDataRX>::Transformation_t Transformation_t; + typedef typename traits<JointDataRX>::Velocity_t Velocity_t; + typedef typename traits<JointDataRX>::Bias_t Bias_t; + typedef typename traits<JointDataRX>::JointModel JointModel; + //typedef typename traits<JointDataRX>::; + + Constraint_t S; + Transformation_t M; + Velocity_t v; + Bias_t c; + + JointDataRX() : M(1) + { + S << 0,0,0,1,0,0; + } + }; + + template<> + struct traits<JointModelRX> + { + typedef JointDataRX JointData; + typedef SE3 Placement_t; + }; + + struct JointModelRX + { + typedef traits<JointModelRX>::JointData JointData; + + static const int nq = 1; + static const int nv = 1; + int idx_q,idx_v; + + JointModelRX() : idx_q(-1),idx_v(-1) {} // Default constructor for std::vector + JointModelRX( int index_q,int index_v ) : idx_q(index_q),idx_v(index_v) {} + JointModelRX( int index_q,int index_v, const JointModelRX& ) : idx_q(index_q),idx_v(index_v) {} + + JointData createData() const { return JointData(); } + void calc( JointData& data, const Eigen::VectorXd & qs, const Eigen::VectorXd & vs ) + { + const double & q = qs[idx_q]; + const double & v = vs[idx_v]; + + //data.M.rotation( Eigen::AngleAxis<double>(q, Eigen::Vector3d::UnitX()).matrix() ); + double ca,sa; sincos(q,&sa,&ca); + Eigen::Matrix3d R3; + R3 << + 1,0,0, + 0,ca,sa, + 0,-sa,ca; + data.M.rotation(R3); + Eigen::Vector3d v3; v3 << v,0,0; + data.v.angular(v3); + } + }; + + + + +} // namespace se3 + +#endif // ifndef __se3_joint_hpp__ diff --git a/src/multibody/model.hpp b/src/multibody/model.hpp new file mode 100644 index 0000000000000000000000000000000000000000..67e6f3a2dfe0255e7fc9f2f8bb4afe58f45bfe63 --- /dev/null +++ b/src/multibody/model.hpp @@ -0,0 +1,153 @@ +#ifndef __se3_model_hpp__ +#define __se3_model_hpp__ + + +#include "pinocchio/spatial/fwd.hpp" +#include "pinocchio/spatial/se3.hpp" +#include "pinocchio/spatial/inertia.hpp" +#include "pinocchio/spatial/motion.hpp" +#include "pinocchio/spatial/force.hpp" +#include "pinocchio/multibody/joint.hpp" +#include <Eigen/StdVector> + +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::SE3); +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::Inertia); +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::Force); +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::Motion); +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::JointModelRX); +EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::JointDataRX); + +namespace se3 +{ + struct Model; + struct Data; + + class Model + { + public: + typedef int Index; + + int nq; + int nv; + int nbody; + + std::vector<Inertia> inertias; + std::vector<SE3> jointPlacements; + std::vector<JointModelRX> joints; + std::vector<Index> parents; + std::vector<std::string> names; + + Motion gravity; + static const Eigen::Vector3d gravity981; + + Model() + : nq(0) + , nv(0) + , nbody(1) + , inertias(1) + , jointPlacements(1) + , joints(1) + , parents(1) + , names(1) + , gravity( gravity981,Eigen::Vector3d::Zero() ) + { + names[0] = "universe"; + } + Index addBody( Index parent,const JointModelRX & j,const SE3 & placement, + const Inertia & Y,const std::string & name = "" ); + Index getBodyId( const std::string & name ) const; + const std::string& getBodyName( Index index ) const; + + }; + + class Data + { + public: + + const Model& model; + std::vector<JointDataRX> joints; + std::vector<Motion> a; // Body acceleration + std::vector<Motion> v; // Body velocity + std::vector<Force> f; // Body force + std::vector<SE3> oMi; // Body absolute placement (wrt world) + std::vector<SE3> liMi; // Body relative placement (wrt parent) + Eigen::VectorXd tau; // Joint forces + + Data( const Model& ref ); + + + }; + + const Eigen::Vector3d Model::gravity981 (0,0,9.81); + +} // namespace se3 + + +/* --- Details -------------------------------------------------------------- */ +/* --- Details -------------------------------------------------------------- */ +/* --- Details -------------------------------------------------------------- */ +namespace se3 +{ + + std::string random(const int len) + { + std::string res; + static const char alphanum[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + for (int i=0; i<len;++i) + res += alphanum[std::rand() % (sizeof(alphanum) - 1)]; + return res; +} + + Model::Index Model::addBody( Index parent,const JointModelRX & j,const SE3 & placement, + const Inertia & Y,const std::string & name ) + { + assert( (nbody==(int)joints.size())&&(nbody==(int)inertias.size()) + &&(nbody==(int)parents.size())&&(nbody==(int)jointPlacements.size()) ); + assert( (j.nq>=0)&&(j.nv>=0) ); + + Index idx = nbody ++; + + joints .push_back(JointModelRX(nq,nv,j)); + inertias .push_back(Y); + parents .push_back(parent); + jointPlacements.push_back(placement); + names .push_back( (name!="")?name:random(8) ); + + nq += j.nq; + nv += j.nv; + return idx; + } + Model::Index Model::getBodyId( const std::string & name ) const + { + Index res = std::find(names.begin(),names.end(),name) - names.begin(); + assert( (res>=0)&&(res<nbody) ); + return res; + } + + const std::string& Model::getBodyName( Model::Index index ) const + { + assert( (index>=0)&&(index<nbody) ); + return names[index]; + } + + Data::Data( const Model& ref ) + :model(ref) + ,joints(0) + ,a(ref.nbody) + ,v(ref.nbody) + ,f(ref.nbody) + ,oMi(ref.nbody) + ,liMi(ref.nbody) + ,tau(ref.nbody) + { + for(int i=0;i<model.nbody;++i) joints.push_back(model.joints[i].createData()); + } + + +} // namespace se3 + +#endif // ifndef __se3_model_hpp__ diff --git a/src/spatial/force.hpp b/src/spatial/force.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c2f28763decacf677136e6a92cf4436a60ba5a03 --- /dev/null +++ b/src/spatial/force.hpp @@ -0,0 +1,121 @@ +#ifndef __se3_force_hpp__ +#define __se3_force_hpp__ + +#include <Eigen/Core> +#include <Eigen/Geometry> +#include "pinocchio/spatial/fwd.hpp" + +namespace se3 +{ + template<typename _Scalar, int _Options> + class ForceTpl + { + public: + typedef _Scalar Scalar; + enum { Options = _Options }; + typedef Eigen::Matrix<Scalar,3,1,Options> Vector3; + typedef Eigen::Matrix<Scalar,3,3,Options> Matrix3; + typedef Eigen::Matrix<Scalar,6,1,Options> Vector6; + typedef Eigen::Matrix<Scalar,6,6,Options> Matrix6; + typedef MotionTpl<Scalar,Options> Motion; + typedef SE3Tpl<Scalar,Options> SE3; + enum { LINEAR = 0, ANGULAR = 3 }; + + public: + // Constructors + ForceTpl() : m_n(), m_f() {} + ForceTpl(const Vector3 & f,const Vector3 & n) : m_n(n), m_f(f) {} + ForceTpl(const Vector6 & v) : m_n(v.template segment<3>(ANGULAR)), + m_f(v.template segment<3>(LINEAR)) {} + + // initializers + static ForceTpl Zero() { return ForceTpl(Vector3::Zero(), Vector3::Zero()); } + static ForceTpl Random() { return ForceTpl(Vector3::Random(), Vector3::Random()); } + + Vector6 toVector() const + { + Vector6 f; + f.template segment<3>(ANGULAR) = m_n; + f.template segment<3>(LINEAR) = m_f; + return f; + } + operator Vector6 () const { return toVector(); } + + // Getters + const Vector3 & linear() const { return m_f; } + const Vector3 & angular() const { return m_n; } + + // Arithmetic operators + ForceTpl & operator=(const Vector6 & phi) + { + m_n = phi.template segment<3>(ANGULAR); + m_f = phi.template segment<3>(LINEAR); + return *this; + } + + ForceTpl operator+(const ForceTpl & phi) const + { + return ForceTpl(m_f+phi.m_f,m_n+phi.m_n); + } + + ForceTpl& operator+= (const ForceTpl & phi) + { + m_f += phi.m_f; + m_n += phi.m_n; + return *this; + } + + ForceTpl operator-() const + { + return ForceTpl(-m_f, -m_n); + } + + ForceTpl operator-(const ForceTpl & phi) const + { + return ForceTpl(m_f-phi.m_f,m_n-phi.m_n); + } + + // ForceTpl operator*(Scalar a) const + // { + // return ForceTpl(m_n*a, m_f*a); + // } + + // friend ForceTpl operator*(Scalar a, const ForceTpl & phi) + // { + // return ForceTpl(phi.n()*a, phi.f()*a); + // } + + + /// af = aXb.act(bf) + ForceTpl se3Action(const SE3 & m) const + { + Vector3 Rf = static_cast<Vector3>(m.rotation()*linear()); + return ForceTpl(Rf,m.translation().cross(Rf)+m.rotation()*angular()); + } + /// bf = aXb.actInv(af) + ForceTpl se3ActionInverse(const SE3 & m) const + { + return ForceTpl(m.rotation().transpose()*linear(), + m.rotation().transpose()*(angular() - m.translation().cross(linear())) ); + } + + friend std::ostream & operator << (std::ostream & os, const ForceTpl & phi) + { + os + << "f =\n" << phi.linear() << std::endl + << "tau =\n" << phi.angular() << std::endl; + return os; + } + + public: // + private: + Vector3 m_n; + Vector3 m_f; + }; + + typedef ForceTpl<double> Force; + +} // namespace se3 + +#endif // ifndef __se3_force_hpp__ + diff --git a/src/spatial/fwd.hpp b/src/spatial/fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..980553c215d8139296b577fbd4a6fb9caf3c81bd --- /dev/null +++ b/src/spatial/fwd.hpp @@ -0,0 +1,15 @@ +#ifndef __se3_fwd_hpp__ +#define __se3_fwd_hpp__ + +#include <Eigen/Core> + +namespace se3 +{ + template<typename _Scalar, int _Options=0> class SE3Tpl; + template<typename _Scalar, int _Options=0> class MotionTpl; + template<typename _Scalar, int _Options=0> class ForceTpl; + template<typename _Scalar, int _Options=0> class InertiaTpl; + +} // namespace se3 + +#endif // ifndef __se3_fwd_hpp__ diff --git a/src/spatial/inertia.hpp b/src/spatial/inertia.hpp new file mode 100644 index 0000000000000000000000000000000000000000..87291b894bc19bfc992a8501bd41ab790492c4af --- /dev/null +++ b/src/spatial/inertia.hpp @@ -0,0 +1,152 @@ +#ifndef __se3_inertia_hpp__ +#define __se3_inertia_hpp__ + +#include "pinocchio/spatial/force.hpp" +#include "pinocchio/spatial/motion.hpp" + + +namespace se3 +{ + template<typename _Scalar, int _Options> + class InertiaTpl + { + public: + typedef _Scalar Scalar; + enum { Options = _Options }; + typedef Eigen::Matrix<Scalar,3,1,Options> Vector3; + typedef Eigen::Matrix<Scalar,3,3,Options> Matrix3; + typedef Eigen::Matrix<Scalar,6,1,Options> Vector6; + typedef Eigen::Matrix<Scalar,6,6,Options> Matrix6; + typedef MotionTpl<Scalar,Options> Motion; + typedef ForceTpl<Scalar,Options> Force; + typedef SE3Tpl<Scalar,Options> SE3; + typedef InertiaTpl<Scalar,Options> Inertia; + enum { LINEAR = 0, ANGULAR = 3 }; + typedef Eigen::SelfAdjointView<Matrix3,Eigen::Upper> Symmetric3; + + public: + // Constructors + InertiaTpl() : m(), c(), dense_I(), I(dense_I) {} + InertiaTpl(Scalar m_, + const Vector3 &c_, + const Matrix3 &I_) + : m(m_), + c(c_), + dense_I(I_), + I(dense_I) {} + InertiaTpl(Scalar _m, + const Vector3 &_c, + const Symmetric3 &_I) + : m(_m), + c(_c), + dense_I(), + I(dense_I) { I = _I; } + InertiaTpl(const InertiaTpl & clone) // Clone constructor for std::vector + : m(clone.m), + c(clone.c), + dense_I(clone.dense_I), + I(dense_I) {} + InertiaTpl& operator= (const InertiaTpl& clone) // Copy operator for std::vector + { + m=clone.m; c=clone.c; dense_I=clone.dense_I; + return *this; + } + + // Initializers + static Inertia Zero() + { + return InertiaTpl(0., + Vector3::Zero(), + Matrix3::Zero()); + } + static Inertia Identity() + { + return InertiaTpl(1., + Vector3::Zero(), + Matrix3::Identity()); + } + static Inertia Random() + { + return InertiaTpl(Eigen::internal::random<Scalar>(), + Vector3::Random(), + Matrix3::Random().template selfadjointView<Eigen::Upper>()); + } + + // Getters + Scalar mass() const { return m; } + const Vector3 & lever() const { return c; } + const Symmetric3 & inertia() const { return I; } + + Matrix6 toMatrix() const + { + Matrix6 M; + M.template block<3,3>(LINEAR, LINEAR ) = m*Matrix3::Identity(); + M.template block<3,3>(LINEAR, ANGULAR) = -m*skew(c); + M.template block<3,3>(ANGULAR,LINEAR ) = m*skew(c); + M.template block<3,3>(ANGULAR,ANGULAR) = (Matrix3)I - m*skew(c)*skew(c); + return M; + } + operator Matrix6 () const { return toMatrix(); } + + // Arithmetic operators + Inertia operator+(const InertiaTpl &other) const + { + return InertiaTpl(m+other.m, c+other.c, I+other.I); + } + + Force operator*(const Motion &v) const + { + Vector3 mcxw = m*c.cross(v.angular()); + return Force( m*v.linear()-mcxw, + m*c.cross(v.linear()) + I*v.angular() - c.cross(mcxw) ); + } + + /// aI = aXb.act(bI) + Inertia se3Action(const SE3 & M) const + { + /* The multiplication RIR' has a particular form that could be used, however it + * does not seems to be more efficient, see http://stackoverflow.com/questions/ + * 13215467/eigen-best-way-to-evaluate-asa-transpose-and-store-the-result-in-a-symmetric .*/ + return Inertia( m, + M.translation()+M.rotation()*c, + M.rotation()*I*M.rotation().transpose()); + } + /// bI = aXb.actInv(aI) + Inertia se3ActionInverse(const SE3 & M) const + { + return Inertia( m, + M.rotation().transpose()*(c-M.translation()), + M.rotation().transpose()*I*M.rotation()); + } + + // + Force vxiv( const Motion& v ) const + { + Vector3 mcxw = m*c.cross(v.angular()); + Vector3 mv_mcxw = m*v.linear()-mcxw; + return Force( v.angular().cross(mv_mcxw), + v.angular().cross(c.cross(mv_mcxw)+I*v.angular())-v.linear().cross(mcxw) ); + } + + friend std::ostream & operator<< (std::ostream &os, const Inertia &I) + { + os << "m =\n" << I.m << "\n" + << "c =\n" << I.c << "\n" + << "I =\n" << (Matrix3)I.I; + return os; + } + + public: + private: + Scalar m; + Vector3 c; + Matrix3 dense_I; + Symmetric3 I; + }; + + + typedef InertiaTpl<double,0> Inertia; + +} // namespace se3 + +#endif // ifndef __se3_inertia_hpp__ diff --git a/src/spatial/motion.hpp b/src/spatial/motion.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b70b0c6f7e3a27450e2a78fc1845547361cea06f --- /dev/null +++ b/src/spatial/motion.hpp @@ -0,0 +1,136 @@ +#ifndef __se3_motion_hpp__ +#define __se3_motion_hpp__ + +#include <Eigen/Core> +#include <Eigen/Geometry> +#include "pinocchio/spatial/fwd.hpp" +#include "pinocchio/spatial/force.hpp" + +namespace se3 +{ + template<typename _Scalar, int _Options> + class MotionTpl + { + public: + typedef _Scalar Scalar; + enum { Options = _Options }; + typedef Eigen::Matrix<Scalar,3,1,Options> Vector3; + typedef Eigen::Matrix<Scalar,3,3,Options> Matrix3; + typedef Eigen::Matrix<Scalar,6,1,Options> Vector6; + typedef Eigen::Matrix<Scalar,6,6,Options> Matrix6; + typedef ForceTpl<Scalar,Options> Force; + typedef SE3Tpl<Scalar,Options> SE3; + enum { LINEAR = 0, ANGULAR = 3 }; + + public: + // Constructors + MotionTpl() : m_w(), m_v() {} + MotionTpl(const Vector3 & v, const Vector3 & w) : m_w(w), m_v(v) {} + MotionTpl(const Vector6 & v) : + m_w(v.template segment<3>(ANGULAR)), + m_v(v.template segment<3>(LINEAR)) {} + + static MotionTpl Zero() { return MotionTpl(Vector3::Zero(), Vector3::Zero()); } + static MotionTpl Random() { return MotionTpl(Vector3::Random(),Vector3::Random()); } + Vector6 toVector() const + { + Vector6 v; + v.template segment<3>(ANGULAR) = m_w; + v.template segment<3>(LINEAR) = m_v; + return v; + } + operator Vector6 () const { return toVector(); } + + // Getters + const Vector3 & angular() const { return m_w; } + const Vector3 & linear() const { return m_v; } + void angular(const Vector3 & w) { m_w=w; } + void linear (const Vector3 & v) { m_v=v; } + + // Arithmetic operators + MotionTpl & operator=(const Vector6 & v) + { + m_w = v.template segment<3>(ANGULAR); + m_v = v.template segment<3>(LINEAR); + return *this; + } + + MotionTpl operator-() const + { + return MotionTpl(-m_v, -m_w); + } + + MotionTpl operator+(const MotionTpl & v2) const + { + return MotionTpl(m_v+v2.m_v,m_w+v2.m_w); + } + MotionTpl& operator+=(const MotionTpl & v2) + { + m_v+=v2.m_v; + m_w+=v2.m_w; + return *this; + } + + + // MotionTpl operator*(Scalar a) const + // { + // return MotionTpl(m_w*a, m_v*a); + // } + + // friend MotionTpl operator*(Scalar a, const MotionTpl & mv) + // { + // return MotionTpl(mv.w()*a, mv.v()*a); + // } + + MotionTpl cross(const MotionTpl& v2) const + { + return MotionTpl( m_v.cross(v2.m_w)+m_w.cross(v2.m_v), + m_w.cross(v2.m_w) ); + } + + ForceTpl<Scalar,Options> cross(const ForceTpl<Scalar,Options>& phi) const + { + return ForceTpl<Scalar,Options> + ( m_w.cross(phi.linear()), + m_w.cross(phi.angular())+m_v.cross(phi.linear()) ); + } + + + // template<typename S,int O> + // friend MotionTpl<S,O> operator^( const MotionTpl<S,O> &, const MotionTpl<S,O> & ); + // template<typename S,int O> + // friend ForceTpl<S,O> operator^( const MotionTpl<S,O> &, const ForceTpl<S,O> & ); + + MotionTpl se3Action(const SE3 & m) const + { + Vector3 Rw = static_cast<Vector3>(m.rotation() * angular()); + return MotionTpl(m.rotation()*linear() + m.translation().cross(Rw), Rw); + } + /// bv = aXb.actInv(av) + MotionTpl se3ActionInverse(const SE3 & m) const + { + return MotionTpl(m.rotation().transpose()*(linear()-m.translation().cross(angular())), + m.rotation().transpose()*angular()); + } + + + + + friend std::ostream & operator << (std::ostream & os, const MotionTpl & mv) + { + os << "v =\n" << mv.linear() << std::endl + << "w =\n" << mv.angular() << std::endl; + return os; + } + + public: + private: + Vector3 m_w; + Vector3 m_v; + }; + + typedef MotionTpl<double> Motion; + +} // namespace se3 + +#endif // ifndef __se3_motion_hpp__ diff --git a/src/spatial/se3.hpp b/src/spatial/se3.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a1454f4ee97596a70c823c0518dd4049ddb7d948 --- /dev/null +++ b/src/spatial/se3.hpp @@ -0,0 +1,130 @@ +#ifndef __se3_se3_hpp__ +#define __se3_se3_hpp__ + +#include <Eigen/Geometry> +#include "pinocchio/spatial/fwd.hpp" +#include "pinocchio/spatial/skew.hpp" + +namespace se3 +{ + + /** The rigid transform aMb can be seen in two ways: + * + * - given a point p expressed in frame B by its coordinate vector Bp, aMb + * computes its coordinates in frame A by Ap = aMb Bp. + * - aMb displaces a solid S centered at frame A into the solid centered in + * B. In particular, the origin of A is displaced at the origin of B: $^aM_b + * ^aA = ^aB$. + + * The rigid displacement is stored as a rotation matrix and translation vector by: + * aMb (x) = aRb*x + aAB + * where aAB is the vector from origin A to origin B expressed in coordinates A. + */ + + template<typename _Scalar, int _Options> + class SE3Tpl + { + public: + + typedef _Scalar Scalar; + enum { Options = _Options }; + typedef Eigen::Matrix<Scalar,3,1,Options> Vector3; + typedef Eigen::Matrix<Scalar,4,1,Options> Vector4; + typedef Eigen::Matrix<Scalar,3,3,Options> Matrix3; + typedef Eigen::Matrix<Scalar,6,1,Options> Vector6; + typedef Eigen::Matrix<Scalar,6,6,Options> Matrix6; + typedef Eigen::Quaternion<Scalar,Options> Quaternion; + typedef MotionTpl<Scalar,Options> Motion; + typedef ForceTpl<Scalar,Options> Force; + //typedef ActionTpl<Scalar,Options> Action; + enum { LINEAR = 0, ANGULAR = 3 }; + + public: + // Constructors + SE3Tpl() : rot(), trans() {} + SE3Tpl(const Matrix3 & R, const Vector3 & p) : rot(R), trans(p) {} + SE3Tpl(int) : rot(Matrix3::Identity()), trans(Vector3::Zero()) {} + + const Matrix3 & rotation() const { return rot; } + const Vector3 & translation() const { return trans; } + void rotation(const Matrix3 & R) { rot=R; } + void translation(const Vector3 & p) { trans=p; } + + static SE3Tpl Identity() + { + return SE3Tpl(1); + } + static SE3Tpl Random() + { + Eigen::Quaternion<Scalar,Options> q(Vector4::Random()); + q.normalize(); + return SE3Tpl(q.matrix(),Vector3::Random()); + } + + Eigen::Matrix<Scalar,4,4,Options> toHomogeneousMatrix() const + { + Eigen::Matrix<Scalar,4,4,Options> M; + M.template block<3,3>(0,0) = rot; + M.template block<3,1>(0,3) = trans; + M.template block<1,3>(3,0).setZero(); + M(3,3) = 1; + return M; + } + + /// Vb.toVector() = bXa.toMatrix() * Va.toVector() + Matrix6 toActionMatrix() const + { + Matrix6 M; + M.template block<3,3>(ANGULAR,ANGULAR) + = M.template block<3,3>(LINEAR,LINEAR) = rot; + M.template block<3,3>(ANGULAR,LINEAR).setZero(); + M.template block<3,3>(LINEAR,ANGULAR) + = skew(trans) * M.template block<3,3>(ANGULAR,ANGULAR); + return M; + } + + /// aXb = bXa.inverse() + SE3Tpl inverse() const + { + return SE3Tpl(rot.transpose(), -rot.transpose()*trans); + } + + void disp(std::ostream & os) + { + os << " R =\n" << rot << std::endl + << " p =\n" << trans.transpose() << std::endl; + } + + + /* --- GROUP ACTIONS ON M6, F6 and I6 --- */ + + /// ay = aXb.act(by) + template<typename D> D act (const D & d) const { return d.se3Action(*this); } + /// by = aXb.actInv(ay) + template<typename D> D actInv(const D & d) const { return d.se3ActionInverse(*this); } + + Vector3 act (const Vector3& p) const { return (rot*p+trans).eval(); } + Vector3 actInv(const Vector3& p) const { return (rot.transpose()*(p-trans)).eval(); } + + SE3Tpl act (const SE3Tpl& m2) const { return SE3Tpl( rot*m2.rot,trans+rot*m2.trans);} + SE3Tpl actInv (const SE3Tpl& m2) const { return SE3Tpl( rot.transpose()*m2.rot, + rot.transpose()*(m2.trans-trans));} + + /* --- OPERATORS -------------------------------------------------------- */ + operator Eigen::Matrix<Scalar,4,4,Options> () const { return toHomogeneousMatrix(); } + operator Matrix6() const { return toActionMatrix(); } + SE3Tpl operator*(const SE3Tpl & m2) const { return this->act(m2); } + friend std::ostream & operator << (std::ostream & os,const SE3Tpl & X) { X.disp(os); return os; } + + public: + private: + Matrix3 rot; + Vector3 trans; + }; + + typedef SE3Tpl<double,0> SE3; + +} // namespace se3 + +#endif // ifndef __se3_se3_hpp__ + diff --git a/src/spatial/skew.hpp b/src/spatial/skew.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1c0241613f2907138768366c5c762e17a3a71917 --- /dev/null +++ b/src/spatial/skew.hpp @@ -0,0 +1,14 @@ +namespace se3 +{ + template <typename D> + inline Eigen::Matrix<typename D::Scalar,3,3,D::Options> + skew(const Eigen::MatrixBase<D> & v) + { + EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(D,3); + Eigen::Matrix<typename D::Scalar,3,3,D::Options> m; + m(0,0) = 0 ; m(0,1) = -v[2]; m(0,2) = v[1]; + m(1,0) = v[2]; m(1,1) = 0 ; m(1,2) = -v[0]; + m(2,0) = -v[1]; m(2,1) = v[0]; m(2,2) = 0 ; + return m; + } +} // namespace se3 diff --git a/src/tools/timer.hpp b/src/tools/timer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9b741e2c2c1560da2222ec9b1eba913ab59cc33f --- /dev/null +++ b/src/tools/timer.hpp @@ -0,0 +1,42 @@ +#include <sys/time.h> +#include <iostream> +#include <stack> + +#define SMOOTH(s) for(int _smooth=0;_smooth<s;++_smooth) + +/* Return the time spent in secs. */ +inline double operator- ( const struct timeval & t1,const struct timeval & t0) +{ + return (t1.tv_sec - t0.tv_sec)+1e-6*(t1.tv_usec - t0.tv_usec); +} + +struct StackTicToc +{ + enum Unit { S = 1, MS = 1000, US = 1000000 }; + Unit DEFAULT_UNIT; + static std::string unitName(Unit u) + { switch(u) { case S: return "s"; case MS: return "ms"; case US: return "us"; } return ""; } + + std::stack<struct timeval> stack; + mutable struct timeval t0; + +StackTicToc( Unit def = MS ) : DEFAULT_UNIT(def) {} + + inline void tic() { + stack.push(t0); + gettimeofday(&(stack.top()),NULL); + } + + inline double toc(const Unit factor) + { + gettimeofday(&t0,NULL); + double dt = (t0-stack.top())*factor; + stack.pop(); + return dt; + } + inline void toc( std::ostream& os, double SMOOTH=1 ) + { + os << toc(DEFAULT_UNIT)/SMOOTH << " " << unitName(DEFAULT_UNIT) << std::endl; + } +}; + diff --git a/unittest/rnea.cpp b/unittest/rnea.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30907ea1d25bc570c18574e3e0a76d3d2d47c33d --- /dev/null +++ b/unittest/rnea.cpp @@ -0,0 +1,106 @@ +#include "pinocchio/spatial/fwd.hpp" +#include "pinocchio/spatial/se3.hpp" +#include "pinocchio/multibody/joint.hpp" +#include "pinocchio/multibody/model.hpp" + +#include <iostream> + +#include "pinocchio/tools/timer.hpp" + + + +int main() +{ + using namespace Eigen; + using namespace se3; + + + se3::Model model; + model.addBody(model.getBodyId("universe"),JointModelRX(),SE3::Random(),Inertia::Random(),"root"); + + + model.addBody(model.getBodyId("root"),JointModelRX(),SE3::Random(),Inertia::Random(),"rleg1"); + model.addBody(model.getBodyId("rleg1"),JointModelRX(),SE3::Random(),Inertia::Random(),"rleg2"); + model.addBody(model.getBodyId("rleg2"),JointModelRX(),SE3::Random(),Inertia::Random(),"rleg3"); + model.addBody(model.getBodyId("rleg3"),JointModelRX(),SE3::Random(),Inertia::Random(),"rleg4"); + model.addBody(model.getBodyId("rleg4"),JointModelRX(),SE3::Random(),Inertia::Random(),"rleg5"); + model.addBody(model.getBodyId("rleg5"),JointModelRX(),SE3::Random(),Inertia::Random(),"rleg6"); + + model.addBody(model.getBodyId("root"),JointModelRX(),SE3::Random(),Inertia::Random(),"lleg1"); + model.addBody(model.getBodyId("lleg1"),JointModelRX(),SE3::Random(),Inertia::Random(),"lleg2"); + model.addBody(model.getBodyId("lleg2"),JointModelRX(),SE3::Random(),Inertia::Random(),"lleg3"); + model.addBody(model.getBodyId("lleg3"),JointModelRX(),SE3::Random(),Inertia::Random(),"lleg4"); + model.addBody(model.getBodyId("lleg4"),JointModelRX(),SE3::Random(),Inertia::Random(),"lleg5"); + model.addBody(model.getBodyId("lleg5"),JointModelRX(),SE3::Random(),Inertia::Random(),"lleg6"); + + model.addBody(model.getBodyId("root"),JointModelRX(),SE3::Random(),Inertia::Random(),"torso1"); + model.addBody(model.getBodyId("torso1"),JointModelRX(),SE3::Random(),Inertia::Random(),"torso2"); + model.addBody(model.getBodyId("torso2"),JointModelRX(),SE3::Random(),Inertia::Random(),"torso3"); + model.addBody(model.getBodyId("torso3"),JointModelRX(),SE3::Random(),Inertia::Random(),"neck1"); + model.addBody(model.getBodyId("neck1"),JointModelRX(),SE3::Random(),Inertia::Random(),"neck2"); + model.addBody(model.getBodyId("neck2"),JointModelRX(),SE3::Random(),Inertia::Random(),"neck3"); + + model.addBody(model.getBodyId("torso3"),JointModelRX(),SE3::Random(),Inertia::Random(),"rarm1"); + model.addBody(model.getBodyId("rarm1"),JointModelRX(),SE3::Random(),Inertia::Random(),"rarm2"); + model.addBody(model.getBodyId("rarm2"),JointModelRX(),SE3::Random(),Inertia::Random(),"rarm3"); + model.addBody(model.getBodyId("rarm3"),JointModelRX(),SE3::Random(),Inertia::Random(),"rarm4"); + model.addBody(model.getBodyId("rarm4"),JointModelRX(),SE3::Random(),Inertia::Random(),"rarm5"); + model.addBody(model.getBodyId("rarm5"),JointModelRX(),SE3::Random(),Inertia::Random(),"rarm6"); + model.addBody(model.getBodyId("rarm6"),JointModelRX(),SE3::Random(),Inertia::Random(),"rgrip"); + + model.addBody(model.getBodyId("torso3"),JointModelRX(),SE3::Random(),Inertia::Random(),"larm1"); + model.addBody(model.getBodyId("larm1"),JointModelRX(),SE3::Random(),Inertia::Random(),"larm2"); + model.addBody(model.getBodyId("larm2"),JointModelRX(),SE3::Random(),Inertia::Random(),"larm3"); + model.addBody(model.getBodyId("larm3"),JointModelRX(),SE3::Random(),Inertia::Random(),"larm4"); + model.addBody(model.getBodyId("larm4"),JointModelRX(),SE3::Random(),Inertia::Random(),"larm5"); + model.addBody(model.getBodyId("larm5"),JointModelRX(),SE3::Random(),Inertia::Random(),"larm6"); + model.addBody(model.getBodyId("larm6"),JointModelRX(),SE3::Random(),Inertia::Random(),"lgrip"); + + se3::Data data(model); + + VectorXd q = VectorXd::Random(model.nq); + VectorXd v = VectorXd::Random(model.nv); + VectorXd a = VectorXd::Random(model.nv); + + data.v[0] = Motion::Zero(); + data.a[0] = -model.gravity; + + StackTicToc timer(StackTicToc::US); timer.tic(); + SMOOTH(1000) + { + for( int i=1;i<model.nbody;++i ) + { + JointModelRX & jmodel = model.joints[i]; + JointDataRX & jdata = data.joints[i]; + jmodel.calc(jdata,q,v); + VectorBlock<VectorXd> qdd = a.segment(jmodel.idx_v,jmodel.nv); + + const Model::Index & parent = model.parents[i]; + const SE3 & liMi = data.liMi[i] = model.jointPlacements[i]*jdata.M; + + if(parent>0) data.oMi[i] = data.oMi[parent]*liMi; + else data.oMi[i] = liMi; + + data.v[i] = jdata.v; + if(parent>0) data.v[i] += liMi.actInv(data.v[parent]); + + data.a[i] = Motion(jdata.S*qdd) + jdata.c + data.v[i].cross(jdata.v); + if(parent>0) data.a[i] += liMi.actInv(data.a[parent]); + + data.f[i] = model.inertias[i]*data.a[i] + model.inertias[i].vxiv(data.v[i]); // -f_ext + } + + for( int i=model.nbody-1;i>0;--i ) + { + const Model::Index & parent = model.parents[i]; + JointModelRX & jmodel = model.joints[i]; + + data.tau.segment(jmodel.idx_v,jmodel.nv) = data.joints[i].S.transpose()*data.f[i].toVector(); + if(parent>0) data.f[parent] += data.liMi[i].act(data.f[i]); + + } + } + timer.toc(std::cout,1000); + + return 0; +} diff --git a/unittest/tspatial.cpp b/unittest/tspatial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d771a42d811c9c3cbe10e308bc5347e596569c2c --- /dev/null +++ b/unittest/tspatial.cpp @@ -0,0 +1,235 @@ +#include <iostream> + +#include "pinocchio/spatial/force.hpp" +#include "pinocchio/spatial/motion.hpp" +#include "pinocchio/spatial/se3.hpp" +#include "pinocchio/spatial/inertia.hpp" + +bool testSE3() +{ + using namespace se3; + typedef Eigen::Matrix<double,4,4> Matrix4; + typedef SE3::Matrix6 Matrix6; + typedef SE3::Vector3 Vector3; + + SE3 amb = SE3::Random(); + SE3 bmc = SE3::Random(); + SE3 amc = amb*bmc; + + Matrix4 aMb = amb; + Matrix4 bMc = bmc; + + // Test internal product + Matrix4 aMc = amc; + assert(aMc.isApprox(aMb*bMc)); + + Matrix4 bMa = amb.inverse(); + assert(bMa.isApprox(aMb.inverse())); + + { // Test point action + Vector3 p = Vector3::Random(); + Eigen::Matrix<double,4,1> p4; p4.head(3) = p; p4[3] = 1; + + Vector3 Mp = (aMb*p4).head(3); + assert(amb.act(p).isApprox(Mp)); + Vector3 Mip = (aMb.inverse()*p4).head(3); + assert(amb.actInv(p).isApprox(Mip)); + } + + { // Test action matrix + Matrix6 aXb = amb; + Matrix6 bXc = bmc; + Matrix6 aXc = amc; + assert(aXc.isApprox(aXb*bXc)); + + Matrix6 bXa = amb.inverse(); + assert(bXa.isApprox(aXb.inverse())); + } + + + return true; +} + +bool testMotion() +{ + using namespace se3; + typedef Eigen::Matrix<double,4,4> Matrix4; + typedef SE3::Matrix6 Matrix6; + typedef SE3::Vector3 Vector3; + typedef Motion::Vector6 Vector6; + + SE3 amb = SE3::Random(); + SE3 bmc = SE3::Random(); + SE3 amc = amb*bmc; + + Motion bv = Motion::Random(); + Motion bv2 = Motion::Random(); + + Vector6 bv_vec = bv; + Vector6 bv2_vec = bv2; + + // Test .+. + Vector6 bvPbv2_vec = bv+bv2; + assert( bvPbv2_vec.isApprox(bv_vec+bv2_vec) ); + // Test -. + Vector6 Mbv_vec = -bv; + assert( Mbv_vec.isApprox(-bv_vec) ); + // Test .+=. + Motion bv3 = bv; bv3 += bv2; + assert(bv3.toVector().isApprox(bv_vec+bv2_vec)); + // Test .=V6 + bv3 = bv2_vec; + assert(bv3.toVector().isApprox(bv2_vec)); + // Test constructor from V6 + Motion bv4(bv2_vec); + assert(bv4.toVector().isApprox(bv2_vec)); + + // Test action + Matrix6 aXb = amb; + assert( amb.act(bv).toVector().isApprox(aXb*bv_vec)); + // Test action inverse + Matrix6 bXc = bmc; + assert( bmc.actInv(bv).toVector().isApprox(bXc.inverse()*bv_vec)); + + // Test double action + Motion cv = Motion::Random(); + bv = bmc.act(cv); + assert( amb.act(bv).toVector().isApprox(amc.act(cv).toVector()) ); + + // Simple test for cross product vxv + Motion vxv = bv.cross(bv); + assert(vxv.toVector().tail(3).isMuchSmallerThan(1e-3)); + + // Simple test for cross product vxf + Force f = bv.toVector(); + Force vxf = bv.cross(f); + assert( vxf.linear().isApprox( bv.angular().cross(f.linear()))); + assert( vxf.angular().isMuchSmallerThan(1e-3)); + + // Test frame change for vxf + Motion av = Motion::Random(); + Force af = Force::Random(); + bv = amb.actInv(av); + Force bf = amb.actInv(af); + Force avxf = av.cross(af); + Force bvxf = bv.cross(bf); + assert( avxf.toVector().isApprox( amb.act(bvxf).toVector()) ); + + // Test frame change for vxv + av = Motion::Random(); + Motion aw = Motion::Random(); + bv = amb.actInv(av); + Motion bw = amb.actInv(aw); + Motion avxw = av.cross(aw); + Motion bvxw = bv.cross(bw); + assert( avxw.toVector().isApprox( amb.act(bvxw).toVector()) ); + + return true; +} + + +bool testForce() +{ + using namespace se3; + typedef Eigen::Matrix<double,4,4> Matrix4; + typedef SE3::Matrix6 Matrix6; + typedef SE3::Vector3 Vector3; + typedef Force::Vector6 Vector6; + + SE3 amb = SE3::Random(); + SE3 bmc = SE3::Random(); + SE3 amc = amb*bmc; + + Force bf = Force::Random(); + Force bf2 = Force::Random(); + + Vector6 bf_vec = bf; + Vector6 bf2_vec = bf2; + + // Test .+. + Vector6 bfPbf2_vec = bf+bf2; + assert( bfPbf2_vec.isApprox(bf_vec+bf2_vec) ); + // Test -. + Vector6 Mbf_vec = -bf; + assert( Mbf_vec.isApprox(-bf_vec) ); + // Test .+=. + Force bf3 = bf; bf3 += bf2; + assert(bf3.toVector().isApprox(bf_vec+bf2_vec)); + // Test .= V6 + bf3 = bf2_vec; + assert(bf3.toVector().isApprox(bf2_vec)); + // Test constructor from V6 + Force bf4(bf2_vec); + assert(bf4.toVector().isApprox(bf2_vec)); + + // Test action + Matrix6 aXb = amb; + assert( amb.act(bf).toVector().isApprox(aXb.inverse().transpose()*bf_vec)); + // Test action inverse + Matrix6 bXc = bmc; + assert( bmc.actInv(bf).toVector().isApprox(bXc.transpose()*bf_vec)); + // Test double action + Force cf = Force::Random(); + bf = bmc.act(cf); + assert( amb.act(bf).toVector().isApprox(amc.act(cf).toVector()) ); + + // Simple test for cross product + // Force vxv = bf.cross(bf); + // assert(vxv.toVector().isMuchSmallerThan(bf.toVector())); + + return true; +} + +bool testInertia() +{ + using namespace se3; + typedef Inertia::Matrix6 Matrix6; + typedef Inertia::Matrix3 Matrix3; + typedef Inertia::Vector3 Vector3; + typedef Eigen::Matrix<double,4,4> Matrix4; + + Inertia aI = Inertia::Random(); + Matrix6 matI = aI; + assert( (matI(0,0) == aI.mass()) + && (matI(1,1) == aI.mass()) + && (matI(1,1) == aI.mass()) ); + assert( (matI-matI.transpose()).isMuchSmallerThan(matI) ); + assert( (matI.topRightCorner<3,3>()*aI.lever()).isMuchSmallerThan(aI.lever()) ); + + Inertia I1 = Inertia::Identity(); + assert( I1.toMatrix() == Matrix6::Identity() ); + + // Test motion-to-force map + Motion v = Motion::Random(); + Force f = I1 * v; + assert( f.toVector() == v.toVector() ); + + // Test Inertia group application + SE3 bma = SE3::Random(); + Inertia bI = bma.act(aI); + Matrix6 bXa = bma; + assert( (bma.rotation()*aI.inertia()*bma.rotation().transpose()).isApprox((Matrix3)bI.inertia()) ); + assert( (bXa.transpose().inverse() * aI.toMatrix() * bXa.inverse()).isApprox( bI.toMatrix()) ); + + // Test inverse action + assert( (bXa.transpose() * bI.toMatrix() * bXa).isApprox( bma.actInv(bI).toMatrix()) ); + + // Test vxIv cross product + v = Motion::Random(); + f = aI*v; + Force vxf = v.cross(f); + Force vxIv = aI.vxiv(v); + assert( vxf.toVector().isApprox(vxIv.toVector()) ); + + return true; +} + +int main() +{ + testSE3(); + testMotion(); + testForce(); + testInertia(); + return 1; +} +