Stéphane Caronhttps://scaron.info/2017-12-11T00:00:00+01:00CMake can use a local Boost2017-12-11T00:00:00+01:00Stéphane Carontag:scaron.info,2017-12-11:blog/cmake-can-use-a-local-boost.html<p>One of my current projects relies on <a class="reference external" href="http://www.boost.org/doc/libs/1_64_0/libs/python/doc/html/">Boost.Python</a>, which requires
a more recent version of Boost (1.64) than the one (1.54) provided by my Linux
distribution (Ubuntu 14.04). My first guess was to remove Boost from my package
manager and install its newer version from source... with dramatic
consequences! At first, I only experienced a few glitches in <a class="reference external" href="http://www.ros.org/">ROS</a> and some libraries built with the old version of Boost,
but down the road I ran into serious runtime problems that forced me to revert
to the system's Boost 1.54.</p>
<p>My second guess was to use a local version of Boost 1.64, compiled but not
installed system-wide and only used by the project that needs it. I knew this
is possible because the <a class="reference external" href="http://pid.lirmm.net/pid-framework/">PID framework</a>
does this for breakfast. And yes, the long term solution for me here will be
to switch to PID altogether ;)</p>
<p>In the meanwhile, let's see how to change only the <tt class="docutils literal">CMakeCache.txt</tt>
configuration to switch from a system-wide to a local Boost install.</p>
<div class="section" id="local-changes-to-cmakecache-txt">
<h2>Local changes to CMakeCache.txt</h2>
<p>I'm assuming you already have your <tt class="docutils literal">CMakeLists.txt</tt> file written and
functional. For instance, you can follow these <a class="reference external" href="https://vsamy.github.io/en/blog/boost-python-cmake-build">instructions to use
Boost.Python with CMake</a>. We won't make any
change to the <tt class="docutils literal">CMakeLists.txt</tt>, so that your local Boost install will only
affect you and stay transparent for other project users.</p>
<p>To start with, go to your build directory and call <tt class="docutils literal">ccmake .</tt>, or
alternatively open <tt class="docutils literal">CMakeCache.txt</tt> in your text editor.</p>
<div class="section" id="boost-library-path">
<h3>Boost library path</h3>
<p>If you use the <a class="reference external" href="https://github.com/jrl-umi3218/jrl-cmakemodules">JRL CMake modules</a> to find Boost
automatically, you will already have a number of <tt class="docutils literal">Boost_*</tt> CMake variables.
The first thing is to update them to your local path:</p>
<div class="highlight"><pre><span></span>Boost_INCLUDE_DIR:PATH<span class="o">=</span>/my/local/path/boost/1.64.0/include
Boost_LIBRARY_DIR:PATH<span class="o">=</span>/my/local/path/boost/1.64.0/lib
Boost_PYTHON_LIBRARY_DEBUG:FILEPATH<span class="o">=</span>/my/local/path/boost/1.64.0/lib/libboost_python.so
Boost_PYTHON_LIBRARY_RELEASE:FILEPATH<span class="o">=</span>/my/local/path/boost/1.64.0/lib/libboost_python.so
</pre></div>
<p>Possibly the <tt class="docutils literal">Boost_DIR</tt> variable will revert to <tt class="docutils literal"><span class="pre">Boost_DIR-NOTFOUND</span></tt> after
you run the makefile again. This is not a problem.</p>
</div>
<div class="section" id="update-linker-flags">
<h3>Update linker flags</h3>
<p>While Boost.Python will be linked with its full path, some libraries like
Boost.NumPy may still be linked by a mere <tt class="docutils literal"><span class="pre">-lboost_numpy</span></tt>, in which case the
linker will fail to find the local shared object (.so) file. Let's add a <tt class="docutils literal"><span class="pre">-L</span></tt>
linker flag to help it here:</p>
<div class="highlight"><pre><span></span>CMAKE_SHARED_LINKER_FLAGS:STRING<span class="o">=</span>-L/my/local/path/boost/1.64.0/lib
</pre></div>
<p>At this stage, your executable or library should compile and link. You can
check that the files produced in your <em>build</em> folder (let's assume a .so here)
links properly to your local Boost libraries:</p>
<div class="highlight"><pre><span></span>$ ldd ./build/mylib.so
linux-vdso.so.1 <span class="o">=</span>> <span class="o">(</span>0x0000...<span class="o">)</span>
libboost_numpy.so.1.64.0 <span class="o">=</span>> /my/local/path/boost/1.64.0/lib/libboost_numpy.so.1.64.0 <span class="o">(</span>0x0000...<span class="o">)</span>
libboost_python.so.1.64.0 <span class="o">=</span>> /my/local/path/boost/1.64.0/lib/libboost_python.so.1.64.0 <span class="o">(</span>0x0000...<span class="o">)</span>
</pre></div>
<p>However, you may notice that it is not the case with your <em>installed</em> files:</p>
<div class="highlight"><pre><span></span>$ ldd /install/path/mylib.so
linux-vdso.so.1 <span class="o">=</span>> <span class="o">(</span>0x00007ffc8f5c7000<span class="o">)</span>
libboost_numpy.so.1.64.0 <span class="o">=</span>> not found
libboost_python.so.1.64.0 <span class="o">=</span>> not found
...
</pre></div>
<p>This is caused by the different behaviors of CMake between build and install.</p>
</div>
<div class="section" id="a-tale-of-two-rpaths">
<h3>A Tale of two RPATHs</h3>
<p><tt class="docutils literal">RPATH</tt> is a list of directories that is directly written into your
executable or shared object and tells the linker where to look for libraries.
It has precedence over the <tt class="docutils literal">LD_LIBRARY_PATH</tt> system-wide variable, as
explained in this page on <a class="reference external" href="https://cmake.org/Wiki/CMake_RPATH_handling">CMake RPATH handling</a>.</p>
<p>A dirty solution to the "not found" problem would be to add the local Boost
1.64 libraries to <tt class="docutils literal">LD_LIBRARY_PATH</tt>. But then, they would also be visible in
other projects trying to link to Boost, which can be problematic. We will
rather set <tt class="docutils literal">RPATH</tt> to avoid this and keep things local.</p>
<p>The behavior of CMake with respect to RPATH is controlled by two parameters:</p>
<div class="highlight"><pre><span></span>// If set, runtime paths are not added when installing shared libraries,
// but are added when building.
CMAKE_SKIP_INSTALL_RPATH:BOOL<span class="o">=</span>OFF
// If set, runtime paths are not added when using shared libraries.
CMAKE_SKIP_RPATH:BOOL<span class="o">=</span>OFF
</pre></div>
<p>In our case, we need both of these values to be off. Plus, we want the linker
to behave for the <em>install</em> the way it does for the <em>build</em>, <em>i.e.</em> using full
paths to our local Boost libraries. This is specified by the following
parameter:</p>
<div class="highlight"><pre><span></span>// Use same RPATH at install and build
CMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL<span class="o">=</span>ON
</pre></div>
<p>Once this is set, run <tt class="docutils literal">make install</tt> again. You should see something like:</p>
<div class="highlight"><pre><span></span>$ ldd /install/path/mylib.so
linux-vdso.so.1 <span class="o">=</span>> <span class="o">(</span>0x0000...<span class="o">)</span>
libboost_numpy.so.1.64.0 <span class="o">=</span>> /my/local/path/boost/1.64.0/lib/libboost_numpy.so.1.64.0 <span class="o">(</span>0x0000...<span class="o">)</span>
libboost_python.so.1.64.0 <span class="o">=</span>> /my/local/path/boost/1.64.0/lib/libboost_python.so.1.64.0 <span class="o">(</span>0x0000...<span class="o">)</span>
</pre></div>
</div>
</div>
<div class="section" id="on-a-concluding-note">
<h2>On a concluding note</h2>
<p>These instructions provide an <em>ad hoc</em> way of linking with local libraries
without affecting the <tt class="docutils literal">CMakeLists.txt</tt> of your project (used by other users
who don't want to see your local paths on their side). While CMake is the <em>de
facto</em> standard today, we are feeling its limitations in such scenarios. The
long term solution here is to switch to broader, dependency-handling frameworks
such as <a class="reference external" href="http://pid.lirmm.net/pid-framework/">PID</a>.</p>
<p><strong>Credits:</strong> thanks to Kevin Chappellet for suggesting local changes to the
<tt class="docutils literal">CMakeCache.txt</tt> and to <a class="reference external" href="https://www.lirmm.fr/lirmm_eng/users/utilisateurs-lirmm/robin-passama">Robin Passama</a> for
walking me through the RPATH :)</p>
</div>
When to make a step? Tackling the timing problem in multi-contact locomotion by TOPP-MPC2017-11-17T00:00:00+01:00Stéphane Carontag:scaron.info,2017-11-17:research/humanoids-2017.html<p class="authors"><strong>Stéphane Caron</strong>, <strong>Quang-Cuong Pham</strong>. <a class="reference external" href="http://humanoids2017.loria.fr/">Humanoids 2017</a>, Birmingham, United Kingdom, November 2017.</p>
<div class="section" id="abstract">
<h2>Abstract</h2>
<p>We present a model predictive controller (MPC) for multi-contact locomotion
where predictive optimizations are realized by time-optimal path
parameterization (TOPP). A key feature of this solution is that, contrary to
existing planners where step timings are provided as inputs, here the timing
between contact switches is computed as <em>output</em> of a fast nonlinear
optimization. This is appealing to multi-contact locomotion, where proper
timings depend on terrain topology and suitable heuristics are unknown. We show
how to formulate legged locomotion as a TOPP problem and demonstrate the
behavior of the resulting TOPP-MPC controller in simulations with a model of
the HRP-4 humanoid robot.</p>
</div>
<div class="section" id="video">
<h2>Video</h2>
<p>
<video width="95%" controls>
<source src="https://scaron.info/videos/humanoids-2017.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
</p></div>
<div class="section" id="content">
<h2>Content</h2>
<table border="1" class="colwidths-given files docutils">
<colgroup>
<col width="10%" />
<col width="90%" />
</colgroup>
<tbody valign="top">
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://hal.archives-ouvertes.fr/hal-01363757/document">Pre-print on HAL</a></td>
</tr>
<tr><td><img alt="github" class="icon" src="https://scaron.info/images/icons/github.png" /></td>
<td><a class="reference external" href="https://github.com/stephane-caron/topp-mpc">Source code</a></td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="presentation">
<h2>Presentation</h2>
<table border="1" class="colwidths-given files docutils">
<colgroup>
<col width="10%" />
<col width="90%" />
</colgroup>
<tbody valign="top">
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://scaron.info/files/humanoids-2017/mc-walking-lmpc.pdf">Poster 1 — Linear MPC for Multi-contact Walking</a> (recap of previous work)</td>
</tr>
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://scaron.info/files/humanoids-2017/topp-mpc.pdf">Poster 2 — MPC by Time-optimal Retiming</a> (this paper)</td>
</tr>
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://scaron.info/files/humanoids-2017/dynamic-walking-nmpc.pdf">Poster 3 — Nonlinear MPC for Dynamic Walking</a> (later work, better behavior)</td>
</tr>
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://scaron.info/files/humanoids-2017/boundedness-mpc.pdf">Poster 4 — Boundedness MPC in 3D</a> (latest works, still ongoing!)</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="bibtex">
<h2>BibTeX</h2>
<div class="highlight"><pre><span></span>@inproceedings{caron2017humanoids,
title = {When to make a step? Tackling the timing problem in multi-contact locomotion by TOPP-MPC},
author = {Caron, St{\'e}phane and Pham, Quang-Cuong},
booktitle = {Humanoid Robots, 2017 IEEE-RAS International Conference on},
year = {2017},
month = {November},
url = {https://hal.archives-ouvertes.fr/hal-01363757}
}
</pre></div>
</div>
Walking on Gravel with Soft Soles using Linear Inverted Pendulum Tracking and Reaction Force Distribution2017-11-17T00:00:00+01:00Stéphane Carontag:scaron.info,2017-11-17:research/humanoids-2017-pajon.html<p class="authors"><strong>Adrien Pajon</strong>, <strong>Stéphane Caron</strong>, <strong>Giovanni De Magistris</strong>, <strong>Sylvain
Miossec</strong> and <strong>Abderrahmane Kheddar</strong>. <a class="reference external" href="http://humanoids2017.loria.fr/">Humanoids 2017</a>, Birmingham, United Kingdom, November 2017.</p>
<div class="section" id="abstract">
<h2>Abstract</h2>
<p>Soft soles absorb impacts and cast ground un-evenness during locomotion on
rough terrains. However, they introduce passive degrees of freedom
(deformations under the feet) that complexify the tasks of state estimation and
overall robot stabilization. We address this problem by developing a control
loop that stabilizes humanoid robots when walking with soft soles on flat and
uneven terrain. Our closed-loop controller minimizes the errors on the center
of mass (COM) and the zero moment point (ZMP) with an admittance control of the
feet based on a simple deformation estimator. We demonstrate its effectiveness
in real experiments on the HRP-4 humanoid.</p>
</div>
<div class="section" id="video">
<h2>Video</h2>
<div class="youtube" align="left"><iframe width="638" height="360" src="https://www.youtube.com/embed/Hb6HQbIqII8" frameborder="0"></iframe></div></div>
<div class="section" id="content">
<h2>Content</h2>
<table border="1" class="colwidths-given files docutils">
<colgroup>
<col width="10%" />
<col width="90%" />
</colgroup>
<tbody valign="top">
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://hal.archives-ouvertes.fr/hal-01576516/document">Pre-print on HAL</a></td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="bibtex">
<h2>BibTeX</h2>
<div class="highlight"><pre><span></span>@inproceedings{pajon2017humanoids,
title = {{Walking on Gravel with Soft Soles using Linear Inverted Pendulum Tracking and Reaction Force Distribution}},
author = {Pajon, Adrien and Caron, St{\'e}phane and De Magistris, Giovanni and Miossec, Sylvain and Kheddar, Abderrahmane},
url = {https://hal.archives-ouvertes.fr/hal-01576516},
booktitle = {Humanoid Robots, 2017 IEEE-RAS International Conference on},
year = {2017},
month = {November},
}
</pre></div>
</div>
Post-Impact Adaptive Compliance for Humanoid Falls Using Predictive Control of a Reduced Model2017-11-17T00:00:00+01:00Stéphane Carontag:scaron.info,2017-11-17:research/humanoids-2017-samy.html<p class="authors"><strong>Vincent Samy</strong>, <strong>Stéphane Caron</strong>, <strong>Karim Bouyarmane</strong> and <strong>Abderrahmane
Kheddar</strong>. <a class="reference external" href="http://humanoids2017.loria.fr/">Humanoids 2017</a>, Birmingham,
United Kingdom, November 2017.</p>
<div class="section" id="abstract">
<h2>Abstract</h2>
<p>We consider control of a humanoid robot in active compliance just after the
impact consecutive to a fall. The goal of this post-impact braking is to absorb
undesired linear momentum accumulated during the fall, using a limited supply
of time and actuation power. The gist of our method is an optimal distribution
of undesired momentum between the robot's hand and foot contact points,
followed by the parallel resolution of Linear Model Predictive Control (LMPC)
at each contact. This distribution is made possible thanks to
emph{torque-limited friction polytopes}, an extension of friction cones that
takes actuation limits into account. Individual LMPC results are finally
combined back into a feasible CoM trajectory sent to the robot's whole-body
controller. We validate the solution in full-body dynamics simulation of an
HRP-4 humanoid falling on a wall.</p>
</div>
<div class="section" id="content">
<h2>Content</h2>
<table border="1" class="colwidths-given files docutils">
<colgroup>
<col width="10%" />
<col width="90%" />
</colgroup>
<tbody valign="top">
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://hal.archives-ouvertes.fr/hal-01569819/document">Pre-print on HAL</a></td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="bibtex">
<h2>BibTeX</h2>
<div class="highlight"><pre><span></span>@inproceedings{samy2017humanoids,
title = {Post-Impact Adaptive Compliance for Humanoid Falls Using Predictive Control of a Reduced Model},
author = {Samy, Vincent and Caron, St{\'e}phane and Bouyarmane, Karim and Kheddar, Abderrahmane},
booktitle = {Humanoid Robots, 2017 IEEE-RAS International Conference on},
year = {2017},
month = {November},
note = {to be presented at},
url = {https://hal.archives-ouvertes.fr/hal-01569819}
}
</pre></div>
</div>
Pendular models for walking over rough terrains2017-10-19T00:00:00+02:00Stéphane Carontag:scaron.info,2017-10-19:research/jnrh-2017.html<p class="authors">Talk given at the <a class="reference external" href="http://www.dis.uniroma1.it/node/12929">University of Rome "La Sapienza"</a> on 19 October 2017, and at the
<a class="reference external" href="https://jnrh2017.sciencesconf.org/">Journées Nationales de la Robotique Humanoïde</a> on 20 June 2017.</p>
<div class="section" id="abstract">
<h2>Abstract</h2>
<p>The Newton-Euler equations that govern multi-body systems are not integrable in
general. However, they become so in the pendular mode, a specific way of moving
where conservation of the angular momentum is enforced. This property was
successfully showcased for locomotion over horizontal floors (2D locomotion) by
walking-pattern generators based on the LIPM and CART-table models. In this
talk, we will see how to generalize these two models to 3D locomotion while
taking into account both friction and tilted contacts, resulting into the FIP
(3D version of LIPM) and COM-accel (3D version of CART-table) models. We will
demonstrate both approaches in live simulations with the HRP-4 humanoid model.</p>
<img alt="HRP-4 descendant un escalier elliptique (motif généré avec un modèle pendulaire)" class="noborder max-height-400px align-center" src="https://scaron.info/figures/dynamic-walking.png" />
</div>
<div class="section" id="content">
<h2>Content</h2>
<table border="1" class="colwidths-given files docutils">
<colgroup>
<col width="10%" />
<col width="90%" />
</colgroup>
<tbody valign="top">
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="/slides/uniroma-2017.pdf">Slides of the UniRoma version</a> (October 2017)</td>
</tr>
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://scaron.info/files/jnrh-2017/slides.pdf">Slides of the JNRH version</a> (June 2017)</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="references">
<h2>References</h2>
<table border="1" class="colwidths-given files docutils">
<colgroup>
<col width="10%" />
<col width="90%" />
</colgroup>
<tbody valign="top">
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://hal.archives-ouvertes.fr/hal-01349880/document">Paper on the COM-accel model</a> (Humanoids 2016)</td>
</tr>
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://hal.archives-ouvertes.fr/hal-01481052/document">Paper on the FIP model</a> (IROS 2017)</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="q-a">
<h2>Q & A</h2>
<p>Thanks to all the people who attended the presentations and asked meaningful
questions about it (including those I forgot to write down below!). Feel free
to write me directly if you have any other question related to this talk.</p>
<p><strong>In this presentation, you linearized friction cones using the inner-pyramid
approximation. What is the cost of this approximation? Why not using the full
second-order friction cones?</strong></p>
<blockquote>
<p>To answer the first part of your question, in my opinion the cost of this
approximation is small: the forces we miss in the corner volumes (between
the second-order cone and its inner pyramids) are extreme values anyway,
and we want to avoid them in practice. This is to be balanced with the
benefits of working with polytopes: (1) the class of optimization problems
becomes LP or QP, and these problems are usually solved faster than the
SOCPs you get with second-order cones,* and (2) polyhedral manipulations
allow you to compute more complex objects, such as <a class="reference external" href="/research/humanoids-2017-samy.html">torque-limited friction
cones</a>. By the way, second-order
friction cones are themselves an outer approximation of a more complex
phenomenon, see <em>e.g.</em> Coulomb-Orowan pseudo-codes.</p>
<p>The debate is not closed at any rate. As a matter of fact it is still
ongoing in my team as well, where Hervé Audren uses SOCPs in his
computation of <a class="reference external" href="https://hal-lirmm.ccsd.cnrs.fr/lirmm-01477362/document">robust static-equilibrium polytopes</a>.</p>
<p>* It is not true in general that "SOCP is slower than LP/QP", but in my
experience it is the case in force optimization.</p>
</blockquote>
<p><strong>You added friction to your model but for walking on flat floors it is usually
neglected. What is the motivation for that?</strong></p>
<blockquote>
<p>At the end of the day the motivation is always experimental: with common
friction coefficients (0.7 and more), the most stringent contact-stability
constraint in practice is the ZMP one. When you don't need to model
friction to get your experiments to work, it makes sense not to model it.
Now, this observation becomes less and less true when your contacts are
<em>tilted</em> (especially when they are tilted outward since angular-momentum
conservation tends to get forces pointing to the CoM), so for general
walking over rough terrains you want to consider modelling friction.</p>
<p>The second good reason for doing that is that modelling friction is
<strong>easy</strong> in terms of polyhedra (look e.g. at the condition in the FIP
paper). The hard part is always the ZMP condition, which you cannot do
without at any rate.</p>
</blockquote>
<p><strong>How do you implement swing foot trajectories in your solution?</strong></p>
<blockquote>
We interpolate Hermit curves with parameters selected to avoid sharp foot
accelerations (HOUBA curves, detailed in <a class="reference external" href="/research/humanoids-2017.html">Section IV of this paper</a>). There is nothing special here, actually
I believe a better way to do these polynomial interpolations is the one
given in Section III of the paper <em>Foot-guided Agile Control of a Biped
Robot through ZMP Manipulation</em> by Sugihara and Yamamoto (IROS 2017).</blockquote>
<p><strong>In the pendular model you keep a constant angular momentum, while other
motion generation techniques* also optimize over kinematics to take it into
account. What's missing in your solution to make the angular momentum a proper
control?</strong></p>
<blockquote>
<p>* These other solutions are: <a class="reference external" href="https://doi.org/10.1109/HUMANOIDS.2014.7041375">Dai et al. (2014)</a> and <a class="reference external" href="https://doi.org/10.1109/HUMANOIDS.2015.7363464">Herzog et al.
(2015)</a>.</p>
<p>Angular momentum is definitely one of the next big questions. It is an open
question even for 2D walking, while here we are doing 3D walking over rough
terrains! There are a number of properties that explain why it is not as
straightforward as COM control: its nonholonomy (see <a class="reference external" href="https://hal.inria.fr/inria-00390428/file/Final.pdf">Wieber (2009)</a>) and dependence on
the full kinematic state (joint angles and velocities). Hence the idea of a
kinematics optimization coupled with centroidal dynamics. To the best of my
knowledge today, these solutions have been applied so far to <em>motion
generation</em> (<em>i.e.</em> with more computation time, not in a closed control
loop). When developing the two walking pattern generators presented here,
the first problems were to be (1) general enough for 3D walking and (2)
fast enough for the control loop. Now that we've made a first step here,
I'll be definitely looking forward to the angular momentum in the future.</p>
</blockquote>
</div>
Multi-Contact Interaction Force Sensing from Whole-Body Motion Capture2017-10-10T00:00:00+02:00Stéphane Carontag:scaron.info,2017-10-10:research/tii-2017.html<p class="authors"><a class="reference external" href="https://hoa.pm/">Tu-Hoa Pham</a>, <strong>Stéphane Caron</strong>, <strong>Abderrahmane Kheddar</strong>.
IEEE Transactions on Industrial Informatics. Submitted November 2016, published
October 2017.</p>
<div class="section" id="abstract">
<h2>Abstract</h2>
<p>We present a novel technique that unobtrusively estimates forces exerted by
human participants in multi-contact interaction with rigid environments. Our
method uses motion capture only, thus circumventing the need to setup
cumbersome force transducers at all potential contacts between the human body
and the environment. This problem is particularly challenging, as the knowledge
of a given motion only characterizes the resultant force, which can generally
be caused by an infinity of force distributions over individual contacts. We
collect and release a large-scale dataset on how humans instinctively regulate
interaction forces on diverse multi-contact tasks and motions. The force
estimation framework we propose leverages physics-based optimization and neural
networks to reconstruct force distributions that are physically realistic and
compatible with real interaction force patterns. We show the effectiveness of
our approach on various locomotion and multi-contact scenarios.</p>
</div>
<div class="section" id="content">
<h2>Content</h2>
<table border="1" class="colwidths-given files docutils">
<colgroup>
<col width="10%" />
<col width="90%" />
</colgroup>
<tbody valign="top">
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://hal.archives-ouvertes.fr/hal-01610928/document">Pre-print</a></td>
</tr>
<tr><td><img alt="doi" class="icon" src="https://scaron.info/images/icons/doi.png" /></td>
<td><a class="reference external" href="https://doi.org/10.1109/TII.2017.2760912">10.1109/TII.2017.2760912</a></td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="bibtex">
<h2>BibTeX</h2>
<div class="highlight"><pre><span></span>@article{pham2017tii,
title = {Multi-Contact Interaction Force Sensing from Whole-Body Motion Capture},
author = {Pham, Tu-Hoa and Caron, St{\'e}phane and Kheddar, Abderrahmane},
booktitle = {IEEE Transactions on Industrial Informatics},
year = {2017},
organization = {IEEE},
doi = {10.1109/TII.2017.2760912},
}
</pre></div>
</div>
Polyhedra and polytopes2017-10-03T00:00:00+02:00Stéphane Carontag:scaron.info,2017-10-03:teaching/polyhedra-and-polytopes.html<p>Polyhedra are geometric objects that appear in mechanics to represent power
constraints such as <a class="reference external" href="/teaching/friction-cones.html">friction cones</a> and
maximum torque limits.</p>
<div class="section" id="representations">
<h2>Representations</h2>
<p>A subset <span class="math">\(P \subset \mathbb{R}^d\)</span> is called a (convex) <em>polyhedron</em> when
it is the set of solutions to a finite system of linear inequalities. In matrix
form:</p>
<div class="math">
\begin{equation*}
P = \{x \ | \ A x \leq b\}
\end{equation*}
</div>
<p>where <span class="math">\(A\)</span> is an <span class="math">\(n \times d\)</span> matrix and <span class="math">\(b\)</span> is a
<span class="math">\(d\)</span>-dimensional vector (vector inequalities are componentwise). When a
polyhedron is bounded, it is called a <em>polytope</em>. To mark the difference
between 2D and higher dimensions, 2-dimensional polytopes are called
<em>polygons</em>.</p>
<div class="section" id="halfspace-representation">
<h3>Halfspace representation</h3>
<p>The <em>halfspace representation</em> (<em>H-rep</em> for short) is the description of the
polyhedron by inequalities:</p>
<div class="math">
\begin{equation*}
A x \leq b
\end{equation*}
</div>
<p>It is always possible to normalize the representation so that the components of
<span class="math">\(b\)</span> are either 1 or 0. When all these components are zero, the polyhedron
is a <em>polyhedral convex cone</em> pointed at the origin. When all the components of
<span class="math">\(b\)</span> are ones, nothing can be said for sure... But, the other way round, a
polytope has only ones in the column vector of its H-rep. We therefore end up
with the following vocabulary:</p>
<div class="math">
\begin{equation*}
\begin{array}{rl}
\text{Polyhedron:} & P = \{ x \ | \ A x \leq b\} \\
\text{Polyhedral cone:} & P = \{ x \ | \ A x \leq 0\} \\
\text{Polytope:} & P = \{ x \ | \ A x \leq 1\}
\end{array}
\end{equation*}
</div>
<img alt="Halfspace representation of a polygon" class="noborder float-right" src="https://scaron.info/figures/polygone-hrep.png" style="width: 300px;" />
<p>In the 2D example to the right, the blue polygon is defined by four halfplanes.
Here is a full H-rep of this polygon:</p>
<div class="math">
\begin{equation*}
\begin{bmatrix}
0 & 1 \\
5 & -2 \\
-1 & -3 \\
-4 & -2
\end{bmatrix}
\begin{bmatrix}
x \\
y
\end{bmatrix}
\leq
\begin{bmatrix}
7 \\
36 \\
-14 \\
-26
\end{bmatrix}
\end{equation*}
</div>
<p>Normals corresponding to each supporting halfplane are depicted by red arrows
(note that the length of the arrows is normalized on this drawing and does not
correspond to that of the coordinates written next to it). Each line of the
matrix <span class="math">\(A\)</span> corresponds to one normal, while the corresponding column in
<span class="math">\(b\)</span> represents the affine offset between the halfplane and the origin.</p>
</div>
<div class="section" id="vertex-representation">
<h3>Vertex representation</h3>
<p>The <em>vertex representation</em> (<em>V-rep</em> for short) of a polyhedron describes it in
terms of points (its <em>vertices</em>) and generating vectors called <em>rays</em>.
Informally, vertices are the "finite" boundaries of the polyhedron, while rays
are the "infinite" ones. A polytope has only vertices, while a polyhedral cones
has only rays. Formally, points <span class="math">\(x\)</span> of the polyhedron are described by:</p>
<div class="math">
\begin{equation*}
x = \mathrm{conv}(V) + \mathrm{coni}(R)
\end{equation*}
</div>
<p>where <span class="math">\(\mathrm{conv}\)</span> denotes the <a class="reference external" href="https://en.wikipedia.org/wiki/Convex_hull">convex hull</a> of a set of vertices <span class="math">\(V =
\{ v_1, \ldots, v_p \}\)</span>:</p>
<div class="math">
\begin{equation*}
\mathrm{conv}(V) = \left\{ \sum_{i=1}^p \alpha_i v_i \ \right|\left. \
\forall i, \alpha_i \geq 0, \sum_{i=1}^p \alpha_i = 1 \right\}
\end{equation*}
</div>
<p>while <span class="math">\(\mathrm{coni}\)</span> is the <a class="reference external" href="https://en.wikipedia.org/wiki/Conical_combination">conical hull</a> of a set of rays <span class="math">\(R
= \{ r_1, \ldots, v_q\}\)</span>:</p>
<div class="math">
\begin{equation*}
\mathrm{coni}(R) = \left\{ \sum_{j=1}^q \lambda_j r_j \ \right|\left. \
\forall j, \lambda_j \geq 0\right\}
\end{equation*}
</div>
<img alt="Vertex representation of a polygon" class="noborder float-right" src="https://scaron.info/figures/polygone-vrep.png" style="width: 300px;" />
<p>In our 2D example to the right, the polyhedron is a polytope, so that <span class="math">\(R
= \emptyset\)</span>. The four vertices of its V-rep are given by</p>
<div class="math">
\begin{equation*}
V = \{(3, 7), (10, 7), (8, 2), (5, 3)\}
\end{equation*}
</div>
<p>In this particular case, the representation is unique, contrary to halfspace
representations where each line can be multiplied by a positive constant
without changing the underlying polyhedron. This uniqueness is basically only
true for polytopes. In the general case where <span class="math">\(R \neq \emptyset\)</span>, rays
can be scaled as well by positive constants without changing the underlying
polyhedron.</p>
</div>
</div>
<div class="section" id="double-description">
<h2>Double description</h2>
<p>So far, we formally defined polyhedra from their H-rep, and introduced the
V-rep. A fundamental result in polyhedral geometry states that these two
representations are interchangeable:</p>
<blockquote>
<strong>Theorem (Minkowski and Weyl):</strong> any polyhedron can be equivalently
described in halfspace or vertex representation. That is, for any set
<span class="math">\(P = \{ x \ | \ A x \leq b\}\)</span>, there exists two sets <span class="math">\(V\)</span> and
<span class="math">\(R\)</span> such that <span class="math">\(P = \mathrm{conv}(V) + \mathrm{coni}(R)\)</span>; and
conversely.</blockquote>
<p>From a computational standpoint, however, these two representations are not
exactly equivalent. For example:</p>
<ul class="simple">
<li>With the <em>halfspace</em> representation, it is easy to check whether a point
<span class="math">\(x\)</span> belongs to the polytope <span class="math">\((x \in P)\)</span>, but hard to sample
points inside <span class="math">\(P\)</span>.</li>
<li>With the <em>vertex</em> representation, it is hard to check whether <span class="math">\(x \in
P\)</span> (this amounts to solving a <a class="reference external" href="https://en.wikipedia.org/wiki/Linear_programming">linear program</a>) but easy to sample
points inside <span class="math">\(P\)</span> (using random values for the coefficients
<span class="math">\(\alpha_i\)</span> and <span class="math">\(\lambda_j\)</span> in the convex and conic hulls,
respectively).</li>
</ul>
<p>Converting from a vertex to a halfspace representation is known as the <em>facet
enumeration problem</em>, while conversely converting from a halfspace to a vertex
representation is the <em>vertex enumeration problem</em>. These two problems are
actually equivalent when put under an appropriate mathematical formulation, and
can be solved using the <em>double description method</em>. The <a class="reference external" href="https://www.inf.ethz.ch/personal/fukudak/cdd_home/">cdd</a> library provides a C
implementation of this algorithm.</p>
</div>
<div class="section" id="see-also">
<h2>See also</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.inf.ethz.ch/personal/fukudak/polyfaq/polyfaq.html">Frequently Asked Questions in Polyhedral Computation</a>: the
excellent FAQ by Komei Fukuda.</li>
<li><cite>Lectures on polytopes
<https://ia600207.us.archive.org/34/items/springer_10.1007-978-1-4613-8431-1/10.1007-978-1-4613-8431-1.pdf></cite>:
a reference book by Ziegler.</li>
<li><a class="reference external" href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.116.5630&rep=rep1&type=pdf">Minkowski sums of polytopes: combinatorics and computation</a>:
the PhD thesis of Christophe Weibel. Part I gives a condensed introduction to
polyhedra.</li>
<li>The paper <a class="reference external" href="ftp://ftp.math.ethz.ch/users/fukudak/paper/ddrev960315.ps.gz">Double description method revisited</a> by Fukuda
and Prodon</li>
<li>The <a class="reference external" href="http://bugseng.com/products/ppl/">Parma Polyhedra Library</a> and its
Python wrapper <a class="reference external" href="https://github.com/haudren/pyparma">pyparma</a>, suited to
work with polyhedra using fractional numbers (slower but numerically stabler
than <em>cdd</em>)</li>
</ul>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = '//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: 'center'," +
" displayIndent: '0em'," +
" showMathMenu: true," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: '#333 ! important'} }, linebreaks: { automatic: false, width: 'container' }" +
" }, " +
" 'TeX': { " +
" Macros: { " +
" defeq: \"{\\\\stackrel{\\\\mathrm{def}}{=}}\"," +
" Ld: \"{\\\\dot{L}}\"," +
" LdG: \"{\\\\dot{L}_G}\"," +
" bfA: \"{\\\\boldsymbol{A}}\"," +
" bfB: \"{\\\\boldsymbol{B}}\"," +
" bfC: \"{\\\\boldsymbol{C}}\"," +
" bfD: \"{\\\\boldsymbol{D}}\"," +
" bfE: \"{\\\\boldsymbol{E}}\"," +
" bfF: \"{\\\\boldsymbol{F}}\"," +
" bfG: \"{\\\\boldsymbol{G}}\"," +
" bfH: \"{\\\\boldsymbol{H}}\"," +
" bfI: \"{\\\\boldsymbol{I}}\"," +
" bfJ: \"{\\\\boldsymbol{J}}\"," +
" bfK: \"{\\\\boldsymbol{K}}\"," +
" bfL: \"{\\\\boldsymbol{L}}\"," +
" bfM: \"{\\\\boldsymbol{M}}\"," +
" bfN: \"{\\\\boldsymbol{N}}\"," +
" bfO: \"{\\\\boldsymbol{O}}\"," +
" bfP: \"{\\\\boldsymbol{P}}\"," +
" bfQ: \"{\\\\boldsymbol{Q}}\"," +
" bfR: \"{\\\\boldsymbol{R}}\"," +
" bfS: \"{\\\\boldsymbol{S}}\"," +
" bfT: \"{\\\\boldsymbol{T}}\"," +
" bfU: \"{\\\\boldsymbol{U}}\"," +
" bfV: \"{\\\\boldsymbol{V}}\"," +
" bfW: \"{\\\\boldsymbol{W}}\"," +
" bfX: \"{\\\\boldsymbol{X}}\"," +
" bfY: \"{\\\\boldsymbol{Y}}\"," +
" bfZ: \"{\\\\boldsymbol{Z}}\"," +
" bfa: \"{\\\\boldsymbol{a}}\"," +
" bfb: \"{\\\\boldsymbol{b}}\"," +
" bfc: \"{\\\\boldsymbol{c}}\"," +
" bfd: \"{\\\\boldsymbol{d}}\"," +
" bfe: \"{\\\\boldsymbol{e}}\"," +
" bff: \"{\\\\boldsymbol{f}}\"," +
" bfg: \"{\\\\boldsymbol{g}}\"," +
" bfh: \"{\\\\boldsymbol{h}}\"," +
" bfi: \"{\\\\boldsymbol{i}}\"," +
" bfj: \"{\\\\boldsymbol{j}}\"," +
" bfk: \"{\\\\boldsymbol{k}}\"," +
" bfl: \"{\\\\boldsymbol{l}}\"," +
" bfm: \"{\\\\boldsymbol{m}}\"," +
" bfn: \"{\\\\boldsymbol{n}}\"," +
" bfo: \"{\\\\boldsymbol{o}}\"," +
" bfp: \"{\\\\boldsymbol{p}}\"," +
" bfq: \"{\\\\boldsymbol{q}}\"," +
" bfr: \"{\\\\boldsymbol{r}}\"," +
" bfs: \"{\\\\boldsymbol{s}}\"," +
" bft: \"{\\\\boldsymbol{t}}\"," +
" bfu: \"{\\\\boldsymbol{u}}\"," +
" bfv: \"{\\\\boldsymbol{v}}\"," +
" bfw: \"{\\\\boldsymbol{w}}\"," +
" bfx: \"{\\\\boldsymbol{x}}\"," +
" bfy: \"{\\\\boldsymbol{y}}\"," +
" bfz: \"{\\\\boldsymbol{z}}\"," +
" bfalpha: \"{\\\\boldsymbol{\\\\alpha}}\"," +
" bfbeta: \"{\\\\boldsymbol{\\\\beta}}\"," +
" bfgamma: \"{\\\\boldsymbol{\\\\gamma}}\"," +
" bftau: \"{\\\\boldsymbol{\\\\tau}}\"," +
" bfomega: \"{\\\\boldsymbol{\\\\omega}}\"," +
" calA: \"{\\\\cal A}\"," +
" calB: \"{\\\\cal B}\"," +
" calC: \"{\\\\cal C}\"," +
" calD: \"{\\\\cal D}\"," +
" calE: \"{\\\\cal E}\"," +
" calF: \"{\\\\cal F}\"," +
" calG: \"{\\\\cal G}\"," +
" calH: \"{\\\\cal H}\"," +
" calI: \"{\\\\cal I}\"," +
" calJ: \"{\\\\cal J}\"," +
" calK: \"{\\\\cal K}\"," +
" calL: \"{\\\\cal L}\"," +
" calM: \"{\\\\cal M}\"," +
" calN: \"{\\\\cal N}\"," +
" calO: \"{\\\\cal O}\"," +
" calP: \"{\\\\cal P}\"," +
" calQ: \"{\\\\cal Q}\"," +
" calR: \"{\\\\cal R}\"," +
" calS: \"{\\\\cal S}\"," +
" calT: \"{\\\\cal T}\"," +
" calU: \"{\\\\cal U}\"," +
" calV: \"{\\\\cal V}\"," +
" calW: \"{\\\\cal W}\"," +
" calX: \"{\\\\cal X}\"," +
" calY: \"{\\\\cal Y}\"," +
" calZ: \"{\\\\cal Z}\"," +
" d: [\"{\\\\rm d}{#1}\", 1]," +
" dim: \"{\\\\rm dim}\"," +
" p: \"{\\\\boldsymbol{p}}\"," +
" pd: \"{\\\\dot{\\\\bfp}}\"," +
" pdd: \"{\\\\ddot{\\\\bfp}}\"," +
" q: \"{\\\\boldsymbol{q}}\"," +
" qd: \"{\\\\dot{\\\\bfq}}\"," +
" qdd: \"{\\\\ddot{\\\\bfq}}\"," +
" xd: \"{\\\\dot{x}}\"," +
" xdd: \"{\\\\ddot{x}}\"," +
" yd: \"{\\\\dot{y}}\"," +
" ydd: \"{\\\\ddot{y}}\"," +
" zd: \"{\\\\dot{z}}\"," +
" zdd: \"{\\\\ddot{z}}\"," +
" defeq: \"{\\\\stackrel{\\\\mathrm{def}}{\\\\ =\\\\ }}\"," +
" } " +
" } " +
"}); ";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Projecting polytopes2017-10-03T00:00:00+02:00Stéphane Carontag:scaron.info,2017-10-03:teaching/projecting-polytopes.html<p>Consider a polytope given in halfspace representation by:</p>
<div class="math">
\begin{equation*}
P = \{ x \in \mathbb{R}^n \ | \ A x \leq b, C x = d \}
\end{equation*}
</div>
<p>Define the affine <em>projection</em> of this polytope onto a lower-dimensional space by:</p>
<div class="math">
\begin{equation*}
\pi(P) = \{ y \in \mathbb{R}^m \ | \ \exists x \in P, y = E x + f \}
\end{equation*}
</div>
<p>We are interested in the <em>halfspace</em> representation of <span class="math">\(\pi(P)\)</span>.</p>
<div class="section" id="double-description-method">
<h2>Double description method</h2>
<p>Projecting is easy in vertex representation: for a polytope <span class="math">\(P =
\mathrm{conv}(V)\)</span>, the projection is simply obtained by <span class="math">\(\pi(P) = \{ E v
+ f, v \in V\}\)</span>. One simple algorithm to our problem is then:</p>
<ul class="simple">
<li>Enumerate vertices of the halfspace representation of <span class="math">\(P\)</span> using the
<a class="reference external" href="/teaching/polyhedra-and-polytopes.html#double-description">double description
method</a></li>
<li>Project the vertices to <span class="math">\(\pi(V) = \{ E v + f, v \in V \}\)</span></li>
<li>Apply the double description method again to convert the vertex
representation <span class="math">\(\pi(V)\)</span> into a halfspace representation</li>
</ul>
<p>The <a class="reference external" href="https://www.inf.ethz.ch/personal/fukudak/cdd_home/">cdd</a> library can be
used for this purpose. One problem with this approach is that it can be
numerically unstable, and becomes significantly slower as the dimension of the
polytope increases.</p>
<div class="section" id="implementation">
<h3>Implementation</h3>
<p>A Python implementation of this method is given in the <cite>project_polyhedron()</cite>
function of the <a class="reference external" href="https://github.com/stephane-caron/pymanoid/blob/master/pymanoid/geometry.py">geometry.py</a>
submodule in <em>pymanoid</em>.</p>
</div>
</div>
<div class="section" id="ray-shooting-methods">
<h2>Ray shooting methods</h2>
<p>Ray shooting methods provide a better alternative when the size of the
<span class="math">\(\pi(P)\)</span> is small, and are typically used when the dimension <span class="math">\(m\)</span> of
the projected polytope is 2 or 3. They are <a class="reference external" href="https://en.wikipedia.org/wiki/Output-sensitive_algorithm">output-sensitive</a> algorithms: their
complexity depends on the number of facets of <span class="math">\(\pi(P)\)</span>, which is
reasonably bounded in 2D or 3D from <a class="reference external" href="https://www.inf.ethz.ch/personal/fukudak/polyfaq/node12.html">McMullen's Upper Bound Theorem</a>.</p>
<div class="section" id="projecting-to-a-2d-polygon">
<h3>Projecting to a 2D polygon</h3>
<p>The 2D ray shooting algorithm was introduced in robotics by Bretl and Lall to
<a class="reference external" href="https://doi.org/10.1109/TRO.2008.2001360">compute the static-equilibrium polygon of a legged robots in multi-contact</a>. It was used in subsequent
humanoid works to compute other criteria such as <a class="reference external" href="/research/tro-2016.html">multi-contact ZMP support
areas</a> and <a class="reference external" href="/research/humanoids-2016.html">CoM acceleration cones</a>.</p>
<p>The core idea of the algorithm goes as follows: let <span class="math">\(\theta\)</span> denote an
angle in the 2D plane, with <span class="math">\(u_\theta = [\cos(\theta) \ \sin(\theta)]\)</span>
the corresponding unit vector. We solve the following linear program (LP):</p>
<div class="math">
\begin{equation*}
\begin{array}{rl}
\underset{x \in \mathbb{R}^n, y \in \mathbb{R}^m}{\text{maximize}} & u_\theta^T y \\
\text{subject to} & A x \leq b \\
& C x = d \\
& y = E x + f
\end{array}
\end{equation*}
</div>
<p>This LP computes simultaneously <span class="math">\(x \in P\)</span> and its projection <span class="math">\(y =
\pi(x)\)</span> via its inequality and inequality constraints. With the choice of
<span class="math">\(u_\theta\)</span> in its objective function, the output <span class="math">\(y^*\)</span> will be <em>the
point of</em> <span class="math">\(\pi(P)\)</span> <em>furthest away in the direction of</em> <span class="math">\(u_\theta\)</span>.
This has two important consequences:</p>
<ul class="simple">
<li><span class="math">\(y^*\)</span> is a <em>vertex</em> of <span class="math">\(\pi(P)\)</span></li>
<li><span class="math">\(u_\theta\)</span> is the normal vector of a <em>supporting hyperplane</em> of
<span class="math">\(\pi(P)\)</span> at <span class="math">\(y^*\)</span></li>
</ul>
<p>A supporting hyperplane (or line, since we are in 2D) is a plane tangent to the
polygon at a given point. Formally, it separates the plane in two: one side
<span class="math">\(S_0\)</span> that contains no point of <span class="math">\(\pi(P)\)</span>, and its complement that
contains the polygon fully. In our present case,</p>
<div class="math">
\begin{equation*}
S_0 = \left\{z \in \mathbb{R}^2 \ \right|\left. \ u_\theta^T (z - y^*) > 0 \right\}
\end{equation*}
</div>
<p>Supporting hyperplanes are members of the <em>dual</em> of a polytope. They are
generated by conic combination of the <a class="reference external" href="/teaching/polyhedra-and-polytopes.html#halfspace-representation">halfspace representation</a>: in
<span class="math">\(A x \leq b\)</span>, lines of <span class="math">\(A\)</span> correspond to normal vectors of
supporting hyperplanes, while the corresponding entries in <span class="math">\(b\)</span> correspond
to their respective affine coordinates. Hence, using the output of the LP
above, we have obtained one line of an H-rep of <span class="math">\(\pi(P)\)</span>:
<span class="math">\(u_\theta^T y \leq u_\theta^T y^*\)</span>. By repeatedly solving LPs with
different values of the angle <span class="math">\(\theta\)</span>, we will thus obtain a halfspace
representation of an <em>outer approximation</em> of the projected polygon.</p>
<p>Meanwhile, <span class="math">\(y^*\)</span> gives us one vertex of <span class="math">\(\pi(P)\)</span>. By repeatedly
solving LPs with different angles <span class="math">\(\theta\)</span>, we obtain a subset <span class="math">\(\{
y_1^*, \ldots, y_k^*\} \subset \pi(V)\)</span> which gives us a vertex representation
for an <em>inner approximation</em> of the projected polygon. Bretl and Lall showed
how, as the number LPs solved for different angles <span class="math">\(\theta\)</span> increases,
these two approximations converge toward <span class="math">\(\pi(P)\)</span>. Furthermore, the
number of iterations <span class="math">\(k\)</span> required to reach a desired precision can be
computed exlicitly (by looking at the surface between the inner and outer
approximations), and the algorithm is <a class="reference external" href="https://en.wikipedia.org/wiki/Anytime_algorithm">anytime</a>, <em>i.e.</em>, computations can
be interrupted after solving any number of LPs and still return a valid
solution.</p>
</div>
<div class="section" id="id2">
<h3>Implementation</h3>
<p>A Python implementation of this algorithm was written by Quang-Cuong Pham in
<a class="reference external" href="https://github.com/stephane-caron/pymanoid/blob/master/pymanoid/thirdparty/bretl.py">bretl.py</a>.
You can call it via <tt class="docutils literal">project_polytope_bretl()</tt> in <em>pymanoid</em>.</p>
</div>
<div class="section" id="projecting-to-a-3d-polytope">
<h3>Projecting to a 3D polytope</h3>
<p>Bretl and Lall's algorithm has been recently extended to 3D by Audren and
Kheddar, used this time to <a class="reference external" href="https://hal-lirmm.ccsd.cnrs.fr/lirmm-01477362/document">compute the 3D robust static-equilibrium polyhedron</a>.</p>
</div>
</div>
<div class="section" id="fourier-motzkin-elimination">
<h2>Fourier-Motzkin elimination</h2>
<p>If the projection is done offline and once and for all, one may consider
<a class="reference external" href="https://en.wikipedia.org/wiki/Fourier–Motzkin_elimination">Fourier-Motzkin Elimination</a> (FME). Contrary
to the previous two approaches, this method works directly and exclusively on
the halfspace representation of the input polytope. One of its benefit is that
it can be applied <em>analytically</em> when the signs of the linear coefficients are
known in advance. This feature was <em>e.g.</em> used to calculate the <a class="reference external" href="/research/icra-2015.html">analytical
formula of single-contact wrench cones</a>.</p>
<div class="section" id="pivotting-rule">
<h3>Pivotting rule</h3>
<p>FME relies on a pivotting rule similar to Gaussian elimination, it "eliminates"
<span class="math">\(x\)</span> dimensions from the combined polytope on <span class="math">\([x \ y]\)</span>:</p>
<div class="math">
\begin{equation*}
P_{\times} = \left\{[x\ y] \ \right|\left. \ A x \leq b, C x = d, y = E x + f \right\}
\end{equation*}
</div>
<p>Its core idea goes as follows. First, use the equality constraints <span class="math">\(C x =
d\)</span> and <span class="math">\(y = E x + f\)</span> to eliminate as many coordinates <span class="math">\(x_i\)</span> from
<span class="math">\(x\)</span> as possible. This leaves us with a reduced polytope:</p>
<div class="math">
\begin{equation*}
P' = \left\{[x_0 \ \cdots \ x_k \ y] \ \right|\left. \ A' [x_0 \ \cdots \ x_k \ y] \leq b'\right\}
\end{equation*}
</div>
<p>Now, consider the first coordinate <span class="math">\(x_0\)</span>. Each line of the inequality
constraint can be written as:</p>
<div class="math">
\begin{equation*}
A'_{i, 0} x_0 + \cdots + A'_{i, k} x_k + A'_{i,k+1} y_0 + A'_{i, k+2} y_1 \leq b'_i
\end{equation*}
</div>
<p>One of three outcomes can happen:</p>
<ul class="simple">
<li><span class="math">\(A'_{i, 0} < 0\)</span>: in this case, dividing by this number yields a <em>lower
bound</em> <span class="math">\(x_0 \geq l_i\)</span>, with</li>
</ul>
<div class="math">
\begin{equation*}
l_i := \frac{b'_i}{A'_{i, 0}} - \sum_{j=1}^k \frac{A'_{i, j}}{A'_{i, 0}}
x_j - \frac{A'_{i, k+1}}{A'_{i, 0}} y_0 - \frac{A'_{i, k+2}}{A'_{i, 0}} y_1
\end{equation*}
</div>
<ul class="simple">
<li><span class="math">\(A'_{i, 0} > 0\)</span>: then, doing the same yields an <em>upper bound</em>
<span class="math">\(x_0 \leq u_i\)</span></li>
<li><span class="math">\(A'_{i, 0} = 0\)</span>: in this case, the inequality is independent from
<span class="math">\(x_0\)</span>, <em>i.e.</em> it is already part of the projection where <span class="math">\(x_0\)</span>
has been eliminated.</li>
</ul>
<p>By construction, <span class="math">\(x_0 \in [l^*, u^*]\)</span> where <span class="math">\(l^*\)</span> is the highest of
all lower bounds obtained by the above process, and <span class="math">\(u^*\)</span> is similarly
the lowest of all upper bounds. Therefore, the system of linear inequalities
has solutions if and only if this interval is nonempty, that is to say,</p>
<div class="math">
\begin{equation*}
\forall i, j,\ l_i \leq u_j
\end{equation*}
</div>
<p>The key idea here is that the expressions of <span class="math">\(l_i\)</span>'s and <span class="math">\(u_j\)</span>'s
only depend on <span class="math">\(x_1, \ldots, x_k, y_0, y_1\)</span>. To eliminate <span class="math">\(x_0\)</span>, we
will therefore:</p>
<ul class="simple">
<li>remove from <span class="math">\(A'\)</span> and <span class="math">\(b'\)</span> all lines <span class="math">\(i\)</span> for which
<span class="math">\(A'_{i, 0} \neq 0\)</span></li>
<li>add one new line for <em>each pair (i, j)</em> of lower and upper bounds on
<span class="math">\(x_0\)</span> corresponding to <span class="math">\(l_i - u_j \leq 0\)</span>.</li>
</ul>
<p>The result from this operation is a new polytope over <span class="math">\([x_1 \ \cdots \
x_k \ y_0 \ y_1]\)</span>, from which the elimination can be repeated until the only
remaining coordinates are <span class="math">\(y_0\)</span> and <span class="math">\(y_1\)</span>.</p>
<p>As can be readily seen from the combinatorics of its update rule,
Fourier-Motzkin elimination has a (doubly) exponential memory complexity.
Imbert has proposed a <a class="reference external" href="https://en.wikipedia.org/wiki/Fourier–Motzkin_elimination#Imbert's_acceleration_theorems">set of syntactic rules</a>
to avoid considering pairs of lower-upper bounds that yield redundant
inequalities, but without lowering the worst-case complexity of the overall
algorithm.</p>
</div>
<div class="section" id="id4">
<h3>Implementation</h3>
<p>The <a class="reference external" href="https://github.com/stephane-caron/SymPyFME">SymPyFME</a> toolbox provides a
Python implementation of Fourier-Motzkin elimination, using Imbert's
acceleration theorems and multiprocessing for parallel computations.</p>
</div>
</div>
<div class="section" id="see-also">
<h2>See also</h2>
<ul class="simple">
<li>An extensive discussion of Fourier-Motzkin elimination is given in the
reference book <cite>Lectures on polytopes
<https://ia600207.us.archive.org/34/items/springer_10.1007-978-1-4613-8431-1/10.1007-978-1-4613-8431-1.pdf></cite>:
by Ziegler.</li>
<li>The paper <a class="reference external" href="https://infoscience.epfl.ch/record/169768/files/resp_mar_04_15.pdf">Equality set projection: A new algorithm for the projection of
polytopes in halfspace representation</a>
proposes an iterative projection algorithm from the ray shooting family. Its
Section 8 compares a number of alternative methods.</li>
<li>A dual reformulation of Bretl & Lall's algorithm was proposed by Del Prete
<em>et al.</em> to <a class="reference external" href="https://hal.archives-ouvertes.fr/hal-01201060/document">compute static-equilibrium conditions for legged robots</a>.</li>
<li>Fourier-Motzkin elimination was used in a grasping paper by Ponce <em>et al.</em> to
<a class="reference external" href="http://journals.sagepub.com/doi/abs/10.1177/027836499701600102">compute force-closure grasps of polyhedral objects</a>.</li>
</ul>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = '//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: 'center'," +
" displayIndent: '0em'," +
" showMathMenu: true," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: '#333 ! important'} }, linebreaks: { automatic: false, width: 'container' }" +
" }, " +
" 'TeX': { " +
" Macros: { " +
" defeq: \"{\\\\stackrel{\\\\mathrm{def}}{=}}\"," +
" Ld: \"{\\\\dot{L}}\"," +
" LdG: \"{\\\\dot{L}_G}\"," +
" bfA: \"{\\\\boldsymbol{A}}\"," +
" bfB: \"{\\\\boldsymbol{B}}\"," +
" bfC: \"{\\\\boldsymbol{C}}\"," +
" bfD: \"{\\\\boldsymbol{D}}\"," +
" bfE: \"{\\\\boldsymbol{E}}\"," +
" bfF: \"{\\\\boldsymbol{F}}\"," +
" bfG: \"{\\\\boldsymbol{G}}\"," +
" bfH: \"{\\\\boldsymbol{H}}\"," +
" bfI: \"{\\\\boldsymbol{I}}\"," +
" bfJ: \"{\\\\boldsymbol{J}}\"," +
" bfK: \"{\\\\boldsymbol{K}}\"," +
" bfL: \"{\\\\boldsymbol{L}}\"," +
" bfM: \"{\\\\boldsymbol{M}}\"," +
" bfN: \"{\\\\boldsymbol{N}}\"," +
" bfO: \"{\\\\boldsymbol{O}}\"," +
" bfP: \"{\\\\boldsymbol{P}}\"," +
" bfQ: \"{\\\\boldsymbol{Q}}\"," +
" bfR: \"{\\\\boldsymbol{R}}\"," +
" bfS: \"{\\\\boldsymbol{S}}\"," +
" bfT: \"{\\\\boldsymbol{T}}\"," +
" bfU: \"{\\\\boldsymbol{U}}\"," +
" bfV: \"{\\\\boldsymbol{V}}\"," +
" bfW: \"{\\\\boldsymbol{W}}\"," +
" bfX: \"{\\\\boldsymbol{X}}\"," +
" bfY: \"{\\\\boldsymbol{Y}}\"," +
" bfZ: \"{\\\\boldsymbol{Z}}\"," +
" bfa: \"{\\\\boldsymbol{a}}\"," +
" bfb: \"{\\\\boldsymbol{b}}\"," +
" bfc: \"{\\\\boldsymbol{c}}\"," +
" bfd: \"{\\\\boldsymbol{d}}\"," +
" bfe: \"{\\\\boldsymbol{e}}\"," +
" bff: \"{\\\\boldsymbol{f}}\"," +
" bfg: \"{\\\\boldsymbol{g}}\"," +
" bfh: \"{\\\\boldsymbol{h}}\"," +
" bfi: \"{\\\\boldsymbol{i}}\"," +
" bfj: \"{\\\\boldsymbol{j}}\"," +
" bfk: \"{\\\\boldsymbol{k}}\"," +
" bfl: \"{\\\\boldsymbol{l}}\"," +
" bfm: \"{\\\\boldsymbol{m}}\"," +
" bfn: \"{\\\\boldsymbol{n}}\"," +
" bfo: \"{\\\\boldsymbol{o}}\"," +
" bfp: \"{\\\\boldsymbol{p}}\"," +
" bfq: \"{\\\\boldsymbol{q}}\"," +
" bfr: \"{\\\\boldsymbol{r}}\"," +
" bfs: \"{\\\\boldsymbol{s}}\"," +
" bft: \"{\\\\boldsymbol{t}}\"," +
" bfu: \"{\\\\boldsymbol{u}}\"," +
" bfv: \"{\\\\boldsymbol{v}}\"," +
" bfw: \"{\\\\boldsymbol{w}}\"," +
" bfx: \"{\\\\boldsymbol{x}}\"," +
" bfy: \"{\\\\boldsymbol{y}}\"," +
" bfz: \"{\\\\boldsymbol{z}}\"," +
" bfalpha: \"{\\\\boldsymbol{\\\\alpha}}\"," +
" bfbeta: \"{\\\\boldsymbol{\\\\beta}}\"," +
" bfgamma: \"{\\\\boldsymbol{\\\\gamma}}\"," +
" bftau: \"{\\\\boldsymbol{\\\\tau}}\"," +
" bfomega: \"{\\\\boldsymbol{\\\\omega}}\"," +
" calA: \"{\\\\cal A}\"," +
" calB: \"{\\\\cal B}\"," +
" calC: \"{\\\\cal C}\"," +
" calD: \"{\\\\cal D}\"," +
" calE: \"{\\\\cal E}\"," +
" calF: \"{\\\\cal F}\"," +
" calG: \"{\\\\cal G}\"," +
" calH: \"{\\\\cal H}\"," +
" calI: \"{\\\\cal I}\"," +
" calJ: \"{\\\\cal J}\"," +
" calK: \"{\\\\cal K}\"," +
" calL: \"{\\\\cal L}\"," +
" calM: \"{\\\\cal M}\"," +
" calN: \"{\\\\cal N}\"," +
" calO: \"{\\\\cal O}\"," +
" calP: \"{\\\\cal P}\"," +
" calQ: \"{\\\\cal Q}\"," +
" calR: \"{\\\\cal R}\"," +
" calS: \"{\\\\cal S}\"," +
" calT: \"{\\\\cal T}\"," +
" calU: \"{\\\\cal U}\"," +
" calV: \"{\\\\cal V}\"," +
" calW: \"{\\\\cal W}\"," +
" calX: \"{\\\\cal X}\"," +
" calY: \"{\\\\cal Y}\"," +
" calZ: \"{\\\\cal Z}\"," +
" d: [\"{\\\\rm d}{#1}\", 1]," +
" dim: \"{\\\\rm dim}\"," +
" p: \"{\\\\boldsymbol{p}}\"," +
" pd: \"{\\\\dot{\\\\bfp}}\"," +
" pdd: \"{\\\\ddot{\\\\bfp}}\"," +
" q: \"{\\\\boldsymbol{q}}\"," +
" qd: \"{\\\\dot{\\\\bfq}}\"," +
" qdd: \"{\\\\ddot{\\\\bfq}}\"," +
" xd: \"{\\\\dot{x}}\"," +
" xdd: \"{\\\\ddot{x}}\"," +
" yd: \"{\\\\dot{y}}\"," +
" ydd: \"{\\\\ddot{y}}\"," +
" zd: \"{\\\\dot{z}}\"," +
" zdd: \"{\\\\ddot{z}}\"," +
" defeq: \"{\\\\stackrel{\\\\mathrm{def}}{\\\\ =\\\\ }}\"," +
" } " +
" } " +
"}); ";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Dynamic Walking over Rough Terrains by Nonlinear Predictive Control of the Floating-base Inverted Pendulum2017-09-27T00:00:00+02:00Stéphane Carontag:scaron.info,2017-09-27:research/iros-2017.html<p class="authors"><strong>Stéphane Caron</strong>, <strong>Abderrahmane Kheddar</strong>. IROS 2017, Vancouver, Canada,
September 2017.</p>
<div class="section" id="abstract">
<h2>Abstract</h2>
<p>We present a real-time rough-terrain dynamic walking pattern generator. Our
method automatically finds step durations, which is a critical issue over rough
terrains where they depend on terrain topology. To achieve this level of
generality, we introduce the Floating-base Inverted Pendulum (FIP) model where
the center of mass can translate freely and the zero-tilting moment point is
allowed to leave the contact surface. We show that this model is equivalent to
the linear-inverted pendulum mode with variable center of mass height, aside
from the fact that its equations of motion remain linear. Our design then
follows three steps: (i) we characterize the FIP contact-stability condition;
(ii) we compute feedforward controls by solving a nonlinear optimization over
receding-horizon FIP trajectories. Despite running at 30 Hz in a
model-predictive fashion, simulations show that the latter is too slow to
stabilize dynamic motions. To remedy this, we (iii) linearize FIP feedback
control computations into a quadratic program, resulting in a constrained
linear-quadratic regulator that runs at 300 Hz. We finally demonstrate our
solution in simulations with a model of the HRP-4 humanoid robot, including
noise and delays over both state estimation and foot force control.</p>
</div>
<div class="section" id="video">
<h2>Video</h2>
<p>
<video width="95%" controls>
<source src="https://scaron.info/videos/iros-2017.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
</p></div>
<div class="section" id="content">
<h2>Content</h2>
<table border="1" class="colwidths-given files docutils">
<colgroup>
<col width="10%" />
<col width="90%" />
</colgroup>
<tbody valign="top">
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://hal.archives-ouvertes.fr/hal-01481052/document">Paper</a>
with all technical details</td>
</tr>
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://scaron.info/files/iros-2017/slides.pdf">Slides</a>
with a more mature discussion on forward integration</td>
</tr>
<tr><td><img alt="github" class="icon" src="https://scaron.info/images/icons/github.png" /></td>
<td><a class="reference external" href="https://github.com/stephane-caron/dynamic-walking">Source code</a></td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="bibtex">
<h2>BibTeX</h2>
<div class="highlight"><pre><span></span>@inproceedings{caron2017iros,
title = {Dynamic Walking over Rough Terrains by Nonlinear Predictive Control of the Floating-base Inverted Pendulum},
author = {Caron, St{\'e}phane and Kheddar, Abderrahmane},
booktitle = {Intelligent Robots and Systems (IROS), 2017 IEEE/RSJ International Conference on},
year = {2017},
month = {September},
url = {https://hal.archives-ouvertes.fr/hal-01481052},
}
</pre></div>
</div>
<div class="section" id="q-a">
<h2>Q & A</h2>
<p>Feel free to write me directly about any question you have on this work.</p>
</div>
Balance control using both ZMP and COM height variations: A convex boundedness approach2017-09-15T00:00:00+02:00Stéphane Carontag:scaron.info,2017-09-15:research/3d-balance.html<p class="authors"><strong>Stéphane Caron</strong>, <a class="reference external" href="http://www.math.univ-paris13.fr/~mallein/">Bastien Mallein</a>.</p>
<div class="section" id="abstract">
<h2>Abstract</h2>
<p>Developments for 3D control of the center of mass (CoM) are currently located
in two local minima: on the one hand, methods that allow CoM height variations
but only work in the 2D sagittal plane; on the other hand, nonconvex centroidal
models that are delicate to handle. This paper presents an alternative that
controls the CoM in 3D by predictive control of a model with <em>convex</em>
constraints. The key to this development is the notion of boundedness
condition, which quantifies convexly the viability of CoM trajectories.</p>
</div>
<div class="section" id="video">
<h2>Video</h2>
<p>
<video width="95%" controls>
<source src="https://scaron.info/videos/3d-balance.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
</p></div>
<div class="section" id="content">
<h2>Content</h2>
<table border="1" class="colwidths-given files docutils">
<colgroup>
<col width="10%" />
<col width="90%" />
</colgroup>
<tbody valign="top">
<tr><td><img alt="pdf" class="icon" src="https://scaron.info/images/icons/pdf.png" /></td>
<td><a class="reference external" href="https://hal.archives-ouvertes.fr/hal-01590509/document">Pre-print</a></td>
</tr>
<tr><td><img alt="github" class="icon" src="https://scaron.info/images/icons/github.png" /></td>
<td><a class="reference external" href="https://github.com/stephane-caron/3d-balance">Source code</a></td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="bibtex">
<h2>BibTeX</h2>
<div class="highlight"><pre><span></span>@unpublished{caron2017balance,
title = {{Balance control using both ZMP and COM height variations: A convex boundedness approach}},
author = {Caron, St{\'e}phane and Mallein, Bastien},
url = {https://hal.archives-ouvertes.fr/hal-01590509},
note = {working paper or preprint},
year = {2017},
month = {September},
}
</pre></div>
</div>
<div class="section" id="q-a">
<h2>Q & A</h2>
<p>Thanks a lot to all the readers who took the time to ask the meaningful
questions below. Feel free to write me directly if you have any other question
related to this work.</p>
<p><strong>It seems that dt/ds goes to infinity as s goes to zero. Does this mean that
the integration accuracy of the discretization is also arbitrarily bad over
[s0, s1]?</strong></p>
<blockquote>
That's right. The user can control the accuracy of <em>spatial</em> integration
through the choice of <span class="math">\((s_1, \ldots, s_{N-1})\)</span>, but <em>time</em>
integration will by essence worsen close to <span class="math">\(s=0\)</span> (because the system
only converges for <span class="math">\(t \to \infty\)</span>). To say it the other way round,
the best accuracy is obtained close to the current robot state
<span class="math">\((t=0)\)</span>. This is a rather desired feature in the MPC framework, where
precision is more important for immediate controls (the only part of the
solution actually used for control) than at the terminal/asymptotic
boundaries.</blockquote>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = '//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: 'center'," +
" displayIndent: '0em'," +
" showMathMenu: true," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: '#333 ! important'} }, linebreaks: { automatic: false, width: 'container' }" +
" }, " +
" 'TeX': { " +
" Macros: { " +
" defeq: \"{\\\\stackrel{\\\\mathrm{def}}{=}}\"," +
" Ld: \"{\\\\dot{L}}\"," +
" LdG: \"{\\\\dot{L}_G}\"," +
" bfA: \"{\\\\boldsymbol{A}}\"," +
" bfB: \"{\\\\boldsymbol{B}}\"," +
" bfC: \"{\\\\boldsymbol{C}}\"," +
" bfD: \"{\\\\boldsymbol{D}}\"," +
" bfE: \"{\\\\boldsymbol{E}}\"," +
" bfF: \"{\\\\boldsymbol{F}}\"," +
" bfG: \"{\\\\boldsymbol{G}}\"," +
" bfH: \"{\\\\boldsymbol{H}}\"," +
" bfI: \"{\\\\boldsymbol{I}}\"," +
" bfJ: \"{\\\\boldsymbol{J}}\"," +
" bfK: \"{\\\\boldsymbol{K}}\"," +
" bfL: \"{\\\\boldsymbol{L}}\"," +
" bfM: \"{\\\\boldsymbol{M}}\"," +
" bfN: \"{\\\\boldsymbol{N}}\"," +
" bfO: \"{\\\\boldsymbol{O}}\"," +
" bfP: \"{\\\\boldsymbol{P}}\"," +
" bfQ: \"{\\\\boldsymbol{Q}}\"," +
" bfR: \"{\\\\boldsymbol{R}}\"," +
" bfS: \"{\\\\boldsymbol{S}}\"," +
" bfT: \"{\\\\boldsymbol{T}}\"," +
" bfU: \"{\\\\boldsymbol{U}}\"," +
" bfV: \"{\\\\boldsymbol{V}}\"," +
" bfW: \"{\\\\boldsymbol{W}}\"," +
" bfX: \"{\\\\boldsymbol{X}}\"," +
" bfY: \"{\\\\boldsymbol{Y}}\"," +
" bfZ: \"{\\\\boldsymbol{Z}}\"," +
" bfa: \"{\\\\boldsymbol{a}}\"," +
" bfb: \"{\\\\boldsymbol{b}}\"," +
" bfc: \"{\\\\boldsymbol{c}}\"," +
" bfd: \"{\\\\boldsymbol{d}}\"," +
" bfe: \"{\\\\boldsymbol{e}}\"," +
" bff: \"{\\\\boldsymbol{f}}\"," +
" bfg: \"{\\\\boldsymbol{g}}\"," +
" bfh: \"{\\\\boldsymbol{h}}\"," +
" bfi: \"{\\\\boldsymbol{i}}\"," +
" bfj: \"{\\\\boldsymbol{j}}\"," +
" bfk: \"{\\\\boldsymbol{k}}\"," +
" bfl: \"{\\\\boldsymbol{l}}\"," +
" bfm: \"{\\\\boldsymbol{m}}\"," +
" bfn: \"{\\\\boldsymbol{n}}\"," +
" bfo: \"{\\\\boldsymbol{o}}\"," +
" bfp: \"{\\\\boldsymbol{p}}\"," +
" bfq: \"{\\\\boldsymbol{q}}\"," +
" bfr: \"{\\\\boldsymbol{r}}\"," +
" bfs: \"{\\\\boldsymbol{s}}\"," +
" bft: \"{\\\\boldsymbol{t}}\"," +
" bfu: \"{\\\\boldsymbol{u}}\"," +
" bfv: \"{\\\\boldsymbol{v}}\"," +
" bfw: \"{\\\\boldsymbol{w}}\"," +
" bfx: \"{\\\\boldsymbol{x}}\"," +
" bfy: \"{\\\\boldsymbol{y}}\"," +
" bfz: \"{\\\\boldsymbol{z}}\"," +
" bfalpha: \"{\\\\boldsymbol{\\\\alpha}}\"," +
" bfbeta: \"{\\\\boldsymbol{\\\\beta}}\"," +
" bfgamma: \"{\\\\boldsymbol{\\\\gamma}}\"," +
" bftau: \"{\\\\boldsymbol{\\\\tau}}\"," +
" bfomega: \"{\\\\boldsymbol{\\\\omega}}\"," +
" calA: \"{\\\\cal A}\"," +
" calB: \"{\\\\cal B}\"," +
" calC: \"{\\\\cal C}\"," +
" calD: \"{\\\\cal D}\"," +
" calE: \"{\\\\cal E}\"," +
" calF: \"{\\\\cal F}\"," +
" calG: \"{\\\\cal G}\"," +
" calH: \"{\\\\cal H}\"," +
" calI: \"{\\\\cal I}\"," +
" calJ: \"{\\\\cal J}\"," +
" calK: \"{\\\\cal K}\"," +
" calL: \"{\\\\cal L}\"," +
" calM: \"{\\\\cal M}\"," +
" calN: \"{\\\\cal N}\"," +
" calO: \"{\\\\cal O}\"," +
" calP: \"{\\\\cal P}\"," +
" calQ: \"{\\\\cal Q}\"," +
" calR: \"{\\\\cal R}\"," +
" calS: \"{\\\\cal S}\"," +
" calT: \"{\\\\cal T}\"," +
" calU: \"{\\\\cal U}\"," +
" calV: \"{\\\\cal V}\"," +
" calW: \"{\\\\cal W}\"," +
" calX: \"{\\\\cal X}\"," +
" calY: \"{\\\\cal Y}\"," +
" calZ: \"{\\\\cal Z}\"," +
" d: [\"{\\\\rm d}{#1}\", 1]," +
" dim: \"{\\\\rm dim}\"," +
" p: \"{\\\\boldsymbol{p}}\"," +
" pd: \"{\\\\dot{\\\\bfp}}\"," +
" pdd: \"{\\\\ddot{\\\\bfp}}\"," +
" q: \"{\\\\boldsymbol{q}}\"," +
" qd: \"{\\\\dot{\\\\bfq}}\"," +
" qdd: \"{\\\\ddot{\\\\bfq}}\"," +
" xd: \"{\\\\dot{x}}\"," +
" xdd: \"{\\\\ddot{x}}\"," +
" yd: \"{\\\\dot{y}}\"," +
" ydd: \"{\\\\ddot{y}}\"," +
" zd: \"{\\\\dot{z}}\"," +
" zdd: \"{\\\\ddot{z}}\"," +
" defeq: \"{\\\\stackrel{\\\\mathrm{def}}{\\\\ =\\\\ }}\"," +
" } " +
" } " +
"}); ";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>