Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gersemirc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
definitions: [./CMakeLists.txt, ./benchmark, ./bindings, ./cmake, ./tests]
definitions: [./CMakeLists.txt, ./benchmark, ./bindings, ./tests]
line_length: 80
indent: 2
warn_about_unknown_commands: false
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ repos:
rev: 0.19.3
hooks:
- id: gersemi
args: ["--print-config=verbose"]
12 changes: 11 additions & 1 deletion bindings/expose-interpolate.cpp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to compile, self.interpolateConfiguration(delay, timestep, qs, q_interp); should beself.interpolateConfiguration<Eigen::VectorXd>(delay, timestep, qs, q_interp);, same for interpolateState and interpolateLinear.

A proxy for interpolateDiscrete needs to be added.

Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,22 @@ namespace simple_mpc
return v_interp;
}

std::vector<bool> interpolateContactsProxy(
Interpolator & self, const double delay, const double timestep, const std::vector<std::vector<bool>> & cs)
{
std::vector<bool> c_interp(cs[0].size());
self.interpolateContacts(delay, timestep, cs, c_interp);

return c_interp;
}

void exposeInterpolator()
{
bp::class_<Interpolator>("Interpolator", bp::init<const Model &>(bp::args("self", "model")))
.def("interpolateConfiguration", &interpolateConfigurationProxy)
.def("interpolateState", &interpolateStateProxy)
.def("interpolateLinear", &interpolateLinearProxy);
.def("interpolateLinear", &interpolateLinearProxy)
.def("interpolateContacts", &interpolateContactsProxy);
}
} // namespace python
} // namespace simple_mpc
18 changes: 9 additions & 9 deletions include/simple-mpc/interpolator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
* @brief Interpolation class for practical control of the robot
*/

#ifndef SIMPLE_MPC_INTERPOLATOR_HPP_
#define SIMPLE_MPC_INTERPOLATOR_HPP_
#pragma once

#include <pinocchio/algorithm/joint-configuration.hpp>

Expand All @@ -23,7 +22,8 @@ namespace simple_mpc
class Interpolator
{
public:
explicit Interpolator(const Model & model);
explicit Interpolator(const Model & model)
: model_(model) {};

void interpolateConfiguration(
const double delay,
Expand All @@ -43,14 +43,14 @@ namespace simple_mpc
const std::vector<Eigen::VectorXd> & vs,
Eigen::Ref<Eigen::VectorXd> v_interp);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest something like this if you want this templated:

    template<int Rows, int Cols, typename MatType
      = Eigen::Matrix<double, Rows, Cols>  
    >
    void interp(
      const double delay,
      const double timestep,
      const std::vector<MatType>& qs,
      Eigen::Ref<MatType> q_interp
    );

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So apparently the problems remains

<...>/simple-mpc/tests/interpolator.cpp: In member function 'void interpolator::interpolate::test_method()':
<...>/simple-mpc/tests/interpolator.cpp:38:42: error: no matching function for call to 'simple_mpc::Interpolator::interpolateConfiguration(double&, double&, std::vector<Eigen::Matrix<double, -1, 1> >&, Eigen::VectorXd&)'
   38 |     interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<...>/simple-mpc/include/simple-mpc/interpolator.hxx:29:10: note: candidate: 'template<class Type, int Rows, int Cols, class MatType> void simple_mpc::Interpolator::interpolateConfiguration(double, double, const std::vector<MatType>&, Eigen::Ref<MatType>)'
   29 |     void interpolateConfiguration(
      |          ^~~~~~~~~~~~~~~~~~~~~~~~
<...>/simple-mpc/include/simple-mpc/interpolator.hxx:29:10: note:   template argument deduction/substitution failed:
<...>/simple-mpc/tests/interpolator.cpp:38:42: note:   'Eigen::Matrix<double, -1, 1>' is not derived from 'Eigen::Ref<MatType>'
   38 |     interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you show the calling code and the current implementation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  pinocchio::urdf::buildModel(urdf_path, JointModelFreeFlyer(), model);
  double timestep = 0.01;
  Interpolator interpolator = Interpolator(model);

  // Test configuration interpolation method
  {
    std::vector<Eigen::VectorXd> qs;
    for (std::size_t i = 0; i < 4; i++)
    {
      Eigen::VectorXd q0(model.nq);
      Eigen::VectorXd q1(model.nq);
      q0 = pinocchio::neutral(model);
      Eigen::VectorXd dq(model.nv);
      dq.setRandom();
      pinocchio::integrate(model, q0, dq, q1);
      qs.push_back(q1);
    }
    Eigen::VectorXd q_interp(model.nq);

    double delay = 0.02;
    interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the interpolateConfiguration function itself?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    template<typename Type, int Rows, int Cols, typename MatType = Eigen::Matrix<Type, Rows, Cols>>
    void interpolateConfiguration(
      const double delay, const double timestep, const std::vector<MatType> & qs, Eigen::Ref<MatType> q_interp)
    {
      assert(("Configuration is not of the right size", qs[0].size() == model_.nq));

      // Compute the time knot corresponding to the current delay
      size_t step_nb = static_cast<size_t>(delay / timestep);
      double step_progress = (delay - (double)step_nb * timestep) / timestep;

      // Interpolate configuration trajectory
      if (step_nb >= qs.size() - 1)
        q_interp = qs.back();
      else
      {
        q_interp = pinocchio::interpolate(model_, qs[step_nb], qs[step_nb + 1], step_progress);
      }
    }

I tried with and without the type template

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange. You could remove MatType as an argument and write Eigen::Matrix<...> everywhere in its place. Perhaps this messed up the function template parameter deduction...


void interpolateContacts(
const double delay,
const double timestep,
const std::vector<std::vector<bool>> & cs,
std::vector<bool> & c_interp);

// Pinocchio model
Model model_;
};

} // namespace simple_mpc

/* --- Details -------------------------------------------------------------- */
/* --- Details -------------------------------------------------------------- */
/* --- Details -------------------------------------------------------------- */

#endif // SIMPLE_MPC_INTERPOLATOR_HPP_
23 changes: 10 additions & 13 deletions src/interpolator.cpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
///////////////////////////////////////////////////////////////////////////////
// BSD 2-Clause License
//
// Copyright (C) 2025, INRIA
// Copyright note valid unless otherwise stated in individual files.
// All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#include "simple-mpc/interpolator.hpp"

namespace simple_mpc
{

Interpolator::Interpolator(const Model & model)
{
model_ = model;
}

void Interpolator::interpolateConfiguration(
const double delay,
const double timestep,
Expand Down Expand Up @@ -79,4 +66,14 @@ namespace simple_mpc
}
}

void Interpolator::interpolateContacts(
const double delay, const double timestep, const std::vector<std::vector<bool>> & cs, std::vector<bool> & c_interp)
{
// Compute the time knot corresponding to the current delay
size_t step_nb = static_cast<size_t>(delay / timestep);
step_nb = std::clamp(step_nb, 0UL, cs.size() - 1);

// Set the output arg
c_interp = cs[step_nb];
}
} // namespace simple_mpc
233 changes: 141 additions & 92 deletions tests/interpolator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,117 +20,166 @@ BOOST_AUTO_TEST_CASE(interpolate)
Interpolator interpolator = Interpolator(model);

// Test configuration interpolation method
std::vector<Eigen::VectorXd> qs;
for (std::size_t i = 0; i < 4; i++)
{
Eigen::VectorXd q0(model.nq);
Eigen::VectorXd q1(model.nq);
q0 = pinocchio::neutral(model);
std::vector<Eigen::VectorXd> qs;
for (std::size_t i = 0; i < 4; i++)
{
Eigen::VectorXd q0(model.nq);
Eigen::VectorXd q1(model.nq);
q0 = pinocchio::neutral(model);
Eigen::VectorXd dq(model.nv);
dq.setRandom();
pinocchio::integrate(model, q0, dq, q1);
qs.push_back(q1);
}
Eigen::VectorXd q_interp(model.nq);

double delay = 0.02;
interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);
interpolator.interpolateConfiguration<Eigen::VectorXd>(delay, timestep, qs, q_interp);


BOOST_CHECK(qs[2].isApprox(q_interp));

delay = 0.5;
interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);
interpolator.interpolateConfiguration<Eigen::VectorXd>(delay, timestep, qs, q_interp);

BOOST_CHECK(qs.back().isApprox(q_interp));

delay = 0.005;
interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);
interpolator.interpolateConfiguration<Eigen::VectorXd>(delay, timestep, qs, q_interp);

Eigen::VectorXd q_interp2(model.nq);
Eigen::VectorXd dq(model.nv);
dq.setRandom();
pinocchio::integrate(model, q0, dq, q1);

qs.push_back(q1);
}
double delay = 0.02;

Eigen::VectorXd q_interp(model.nq);
interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);

BOOST_CHECK(qs[2].isApprox(q_interp));

delay = 0.5;
interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);
BOOST_CHECK(qs.back().isApprox(q_interp));

delay = 0.005;
interpolator.interpolateConfiguration(delay, timestep, qs, q_interp);
Eigen::VectorXd q_interp2(model.nq);
Eigen::VectorXd dq(model.nv);
pinocchio::difference(model, qs[0], qs[1], dq);
pinocchio::integrate(model, qs[0], dq * 0.5, q_interp2);

BOOST_CHECK(q_interp2.isApprox(q_interp));

std::vector<Eigen::VectorXd> qs2;
for (std::size_t i = 0; i < 2; i++)
{
qs2.push_back(qs[0]);
pinocchio::difference(model, qs[0], qs[1], dq);
pinocchio::integrate(model, qs[0], dq * 0.5, q_interp2);

BOOST_CHECK(q_interp2.isApprox(q_interp));

std::vector<Eigen::VectorXd> qs2;
for (std::size_t i = 0; i < 2; i++)
{
qs2.push_back(qs[0]);
}
interpolator.interpolateConfiguration(delay, timestep, qs2, q_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateConfiguration(delay, timestep, qs2, q_interp);
interpolator.interpolateConfiguration<Eigen::VectorXd>(delay, timestep, qs2, q_interp);

BOOST_CHECK(qs2[0].isApprox(q_interp));
}
interpolator.interpolateConfiguration(delay, timestep, qs2, q_interp);
BOOST_CHECK(qs2[0].isApprox(q_interp));

// Test state interpolation method
std::vector<Eigen::VectorXd> xs;
for (std::size_t i = 0; i < 4; i++)
{
Eigen::VectorXd x0(model.nq + model.nv);
x0.head(model.nq) = pinocchio::neutral(model);
std::vector<Eigen::VectorXd> xs;
for (std::size_t i = 0; i < 4; i++)
{
Eigen::VectorXd x0(model.nq + model.nv);
x0.head(model.nq) = pinocchio::neutral(model);
Eigen::VectorXd dq(model.nv);
dq.setRandom();
pinocchio::integrate(model, x0.head(model.nq), dq, x0.head(model.nq));
x0.tail(model.nv).setRandom();
xs.push_back(x0);
}
Eigen::VectorXd x_interp(model.nq + model.nv);
Eigen::VectorXd dq(model.nv);
dq.setRandom();
pinocchio::integrate(model, x0.head(model.nq), dq, x0.head(model.nq));
x0.tail(model.nv).setRandom();

xs.push_back(x0);
double delay = 0.02;
interpolator.interpolateState(delay, timestep, xs, x_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateState(delay, timestep, xs, x_interp);
interpolator.interpolateState<Eigen::VectorXd>(delay, timestep, xs, x_interp);

BOOST_CHECK(xs[2].isApprox(x_interp));

delay = 0.5;
interpolator.interpolateState(delay, timestep, xs, x_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateState(delay, timestep, xs, x_interp);
interpolator.interpolateState<Eigen::VectorXd>(delay, timestep, xs, x_interp);

BOOST_CHECK(xs.back().isApprox(x_interp));

delay = 0.005;
interpolator.interpolateState(delay, timestep, xs, x_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateState(delay, timestep, xs, x_interp);
interpolator.interpolateState<Eigen::VectorXd>(delay, timestep, xs, x_interp);

Eigen::VectorXd x_interp2(model.nq + model.nv);
pinocchio::difference(model, xs[0].head(model.nq), xs[1].head(model.nq), dq);
pinocchio::integrate(model, xs[0].head(model.nq), dq * 0.5, x_interp2.head(model.nq));
x_interp2.tail(model.nv) = (xs[0].tail(model.nv) + xs[1].tail(model.nv)) * 0.5;
BOOST_CHECK(x_interp2.isApprox(x_interp));

std::vector<Eigen::VectorXd> xs2;
for (std::size_t i = 0; i < 2; i++)
{
xs2.push_back(xs[0]);
}
interpolator.interpolateState(delay, timestep, xs2, x_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateState(delay, timestep, xs2, x_interp);
interpolator.interpolateState<Eigen::VectorXd>(delay, timestep, xs2, x_interp);

BOOST_CHECK(xs2[0].isApprox(x_interp));
}
delay = 0.02;

Eigen::VectorXd x_interp(model.nq + model.nv);
interpolator.interpolateState(delay, timestep, xs, x_interp);
BOOST_CHECK(xs[2].isApprox(x_interp));

delay = 0.5;
interpolator.interpolateState(delay, timestep, xs, x_interp);
BOOST_CHECK(xs.back().isApprox(x_interp));

delay = 0.005;
interpolator.interpolateState(delay, timestep, xs, x_interp);
Eigen::VectorXd x_interp2(model.nq + model.nv);
pinocchio::difference(model, xs[0].head(model.nq), xs[1].head(model.nq), dq);
pinocchio::integrate(model, xs[0].head(model.nq), dq * 0.5, x_interp2.head(model.nq));
x_interp2.tail(model.nv) = (xs[0].tail(model.nv) + xs[1].tail(model.nv)) * 0.5;
BOOST_CHECK(x_interp2.isApprox(x_interp));

std::vector<Eigen::VectorXd> xs2;
for (std::size_t i = 0; i < 2; i++)

// Test linear interpolation method
{
xs2.push_back(xs[0]);
std::vector<Eigen::VectorXd> vs;
for (std::size_t i = 0; i < 4; i++)
{
Eigen::VectorXd v0(model.nv);
v0.setRandom();
vs.push_back(v0);
}
Eigen::VectorXd v_interp(model.nv);

double delay = 0.02;
interpolator.interpolateLinear(delay, timestep, vs, v_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateLinear(delay, timestep, vs, v_interp);
interpolator.interpolateLinear<Eigen::VectorXd>(delay, timestep, vs, v_interp);

BOOST_CHECK(vs[2].isApprox(v_interp));

delay = 0.5;
interpolator.interpolateLinear(delay, timestep, vs, v_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateLinear(delay, timestep, vs, v_interp);
interpolator.interpolateLinear<Eigen::VectorXd>(delay, timestep, vs, v_interp);

BOOST_CHECK(vs.back().isApprox(v_interp));

delay = 0.005;
interpolator.interpolateLinear(delay, timestep, vs, v_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateLinear(delay, timestep, vs, v_interp);
interpolator.interpolateLinear<Eigen::VectorXd>(delay, timestep, vs, v_interp);

Eigen::VectorXd v_interp2(model.nv);
v_interp2 = (vs[0] + vs[1]) * 0.5;

BOOST_CHECK(v_interp2.isApprox(v_interp));

std::vector<Eigen::VectorXd> vs2;
for (std::size_t i = 0; i < 2; i++)
{
vs2.push_back(vs[0]);
}
interpolator.interpolateLinear(delay, timestep, vs2, v_interp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interpolator.interpolateLinear(delay, timestep, vs2, v_interp);
interpolator.interpolateLinear<Eigen::VectorXd>(delay, timestep, vs2, v_interp);

BOOST_CHECK(vs2[0].isApprox(v_interp));
}
interpolator.interpolateState(delay, timestep, xs2, x_interp);
BOOST_CHECK(xs2[0].isApprox(x_interp));

// Test linear interpolation method
std::vector<Eigen::VectorXd> vs;
for (std::size_t i = 0; i < 4; i++)
// Test contacts interpolation method
{
Eigen::VectorXd v0(model.nv);
v0.setRandom();
const size_t n_contacts = 4;

vs.push_back(v0);
}
delay = 0.02;
Eigen::VectorXd v_interp(model.nv);
interpolator.interpolateLinear(delay, timestep, vs, v_interp);
BOOST_CHECK(vs[2].isApprox(v_interp));
std::vector<std::vector<bool>> cs;
for (std::size_t i = 0; i < n_contacts; i++)
{
std::vector<bool> c(n_contacts, false);
c[i] = true;
cs.push_back(c);
}
std::vector<bool> c_interp(n_contacts);

delay = 0.5;
interpolator.interpolateLinear(delay, timestep, vs, v_interp);
BOOST_CHECK(vs.back().isApprox(v_interp));
// First element
interpolator.interpolateContacts(-0.001, 0.1, cs, c_interp);
BOOST_CHECK(std::equal(c_interp.begin(), c_interp.end(), cs[0].begin()));

delay = 0.005;
interpolator.interpolateLinear(delay, timestep, vs, v_interp);
Eigen::VectorXd v_interp2(model.nv);
v_interp2 = (vs[0] + vs[1]) * 0.5;
interpolator.interpolateContacts(0, 0.1, cs, c_interp);
BOOST_CHECK(std::equal(c_interp.begin(), c_interp.end(), cs[0].begin()));

BOOST_CHECK(v_interp2.isApprox(v_interp));
interpolator.interpolateContacts(0.001, 0.1, cs, c_interp);
BOOST_CHECK(std::equal(c_interp.begin(), c_interp.end(), cs[0].begin()));

std::vector<Eigen::VectorXd> vs2;
for (std::size_t i = 0; i < 2; i++)
{
vs2.push_back(vs[0]);
// Middle element
interpolator.interpolateContacts(0.1, 0.1, cs, c_interp);
BOOST_CHECK(std::equal(c_interp.begin(), c_interp.end(), cs[1].begin()));

interpolator.interpolateContacts(0.15, 0.1, cs, c_interp);
BOOST_CHECK(std::equal(c_interp.begin(), c_interp.end(), cs[1].begin()));

interpolator.interpolateContacts(0.2, 0.1, cs, c_interp);
BOOST_CHECK(std::equal(c_interp.begin(), c_interp.end(), cs[2].begin()));

// Last element
interpolator.interpolateContacts(0.39, 0.1, cs, c_interp);
BOOST_CHECK(std::equal(c_interp.begin(), c_interp.end(), cs[3].begin()));

interpolator.interpolateContacts(0.4, 0.1, cs, c_interp);
BOOST_CHECK(std::equal(c_interp.begin(), c_interp.end(), cs[3].begin()));

interpolator.interpolateContacts(0.5, 0.1, cs, c_interp);
BOOST_CHECK(std::equal(c_interp.begin(), c_interp.end(), cs[3].begin()));
}
interpolator.interpolateLinear(delay, timestep, vs2, v_interp);
BOOST_CHECK(vs2[0].isApprox(v_interp));
}

BOOST_AUTO_TEST_SUITE_END()
Loading