Skip to content

Commit

Permalink
Merge pull request #711 from ami-iit/rk4
Browse files Browse the repository at this point in the history
Implement RK4 integrator in ContinuousDynamicalSystem component and expose the python bindings
  • Loading branch information
GiulioRomualdi authored Jul 25, 2023
2 parents be46855 + c162238 commit 157b4f2
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All notable changes to this project are documented in this file.
## [unreleased]
### Added
- 🤖 Add the configuration files to use `YarpRobotLogger` with `ergoCubGazeboV1` (https://github.com/ami-iit/bipedal-locomotion-framework/pull/690)
- Implement `RK4` integrator in `ContinuousDynamicalSystem` component and expose the python bindings (https://github.com/ami-iit/bipedal-locomotion-framework/pull/711)

### Changed
- Use `std::chorno::nanoseconds` in `clock` and `AdvanceableRunner` (https://github.com/ami-iit/bipedal-locomotion-framework/pull/702)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <BipedalLocomotion/ContinuousDynamicalSystem/FixedStepIntegrator.h>
#include <BipedalLocomotion/ContinuousDynamicalSystem/ForwardEuler.h>
#include <BipedalLocomotion/ContinuousDynamicalSystem/Integrator.h>
#include <BipedalLocomotion/ContinuousDynamicalSystem/RK4.h>

#include <pybind11/chrono.h>
#include <pybind11/eigen.h>
Expand All @@ -34,7 +35,7 @@ void CreateIntegrator(pybind11::module& module, const std::string& name)
namespace py = ::pybind11;
using namespace BipedalLocomotion::ContinuousDynamicalSystem;

const std::string completeName = "_" + name + "Integrator";
const std::string completeName = "_" + name + "BaseIntegrator";
py::class_<Integrator<_Derived>, _Args...>(module, completeName.c_str())
.def("set_dynamical_system",
&Integrator<_Derived>::setDynamicalSystem,
Expand Down Expand Up @@ -82,9 +83,8 @@ void CreateFixedStepIntegrator(pybind11::module& module, const std::string& name
namespace py = ::pybind11;
using namespace BipedalLocomotion::ContinuousDynamicalSystem;

CreateIntegrator<FixedStepIntegrator<_Derived>>(module, name);

const std::string completeName = "_" + name + "FixedStepIntegrator";
CreateIntegrator<FixedStepIntegrator<_Derived>>(module, name);
py::class_<FixedStepIntegrator<_Derived>,
Integrator<FixedStepIntegrator<_Derived>>,
_Args...>(module, completeName.c_str())
Expand All @@ -108,15 +108,30 @@ void CreateForwardEulerIntegrator(pybind11::module& module, const std::string& n
namespace py = ::pybind11;
using namespace BipedalLocomotion::ContinuousDynamicalSystem;

CreateFixedStepIntegrator<ForwardEuler<_DynamicalSystem>>(module, name);

const std::string completeName = name + "ForwardEulerIntegrator";
CreateFixedStepIntegrator<ForwardEuler<_DynamicalSystem>>(module, completeName);

py::class_<ForwardEuler<_DynamicalSystem>,
FixedStepIntegrator<ForwardEuler<_DynamicalSystem>>,
_Args...>(module, completeName.c_str())
.def(py::init());
}

template <typename _DynamicalSystem, typename... _Args>
void CreateRK4Integrator(pybind11::module& module, const std::string& name)
{
namespace py = ::pybind11;
using namespace BipedalLocomotion::ContinuousDynamicalSystem;

const std::string completeName = name + "RK4Integrator";
CreateFixedStepIntegrator<RK4<_DynamicalSystem>>(module, completeName);

py::class_<RK4<_DynamicalSystem>,
FixedStepIntegrator<RK4<_DynamicalSystem>>,
_Args...>(module, completeName.c_str())
.def(py::init());
}

} // namespace ContinuousDynamicalSystem
} // namespace bindings
} // namespace BipedalLocomotion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
* distributed under the terms of the BSD-3-Clause license.
*/

#include <BipedalLocomotion/ContinuousDynamicalSystem/DynamicalSystem.h>
#include <BipedalLocomotion/ContinuousDynamicalSystem/CentroidalDynamics.h>
#include <BipedalLocomotion/ContinuousDynamicalSystem/DynamicalSystem.h>

#include <BipedalLocomotion/bindings/ContinuousDynamicalSystem/CentroidalDynamics.h>
#include <BipedalLocomotion/bindings/ContinuousDynamicalSystem/DynamicalSystem.h>
#include <BipedalLocomotion/bindings/ContinuousDynamicalSystem/Integrator.h>
#include <BipedalLocomotion/bindings/ContinuousDynamicalSystem/LinearTimeInvariantSystem.h>
#include <BipedalLocomotion/bindings/ContinuousDynamicalSystem/CentroidalDynamics.h>

#include <pybind11/eigen.h>
#include <pybind11/stl.h>
Expand Down Expand Up @@ -40,6 +40,7 @@ void CreateCentroidalDynamics(pybind11::module& module)
.def(py::init());

CreateForwardEulerIntegrator<CentroidalDynamics>(module, name);
CreateRK4Integrator<CentroidalDynamics>(module, name);
}
} // namespace ContinuousDynamicalSystem
} // namespace bindings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ void CreateLinearTimeInvariantSystem(pybind11::module& module)
py::arg("B"));

CreateForwardEulerIntegrator<LinearTimeInvariantSystem>(module, name);
CreateRK4Integrator<LinearTimeInvariantSystem>(module, name);
}
} // namespace ContinuousDynamicalSystem
} // namespace bindings
Expand Down
2 changes: 1 addition & 1 deletion src/ContinuousDynamicalSystem/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ if(FRAMEWORK_COMPILE_ContinuousDynamicalSystem)
${H_PREFIX}/FloatingBaseSystemKinematics.h ${H_PREFIX}/FloatingBaseDynamicsWithCompliantContacts.h ${H_PREFIX}/FixedBaseDynamics.h ${H_PREFIX}/FirstOrderSmoother.h
${H_PREFIX}/CentroidalDynamics.h
${H_PREFIX}/LieGroupDynamics.h ${H_PREFIX}/SO3Dynamics.h
${H_PREFIX}/Integrator.h ${H_PREFIX}/FixedStepIntegrator.h ${H_PREFIX}/ForwardEuler.h
${H_PREFIX}/Integrator.h ${H_PREFIX}/FixedStepIntegrator.h ${H_PREFIX}/ForwardEuler.h ${H_PREFIX}/RK4.h
${H_PREFIX}/CompliantContactWrench.h
${H_PREFIX}/MultiStateWeightProvider.h
PRIVATE_HEADERS ${H_PREFIX}/impl/traits.h
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
/**
* @file RK4.h
* @authors Giulio Romualdi
* @copyright 2021 Istituto Italiano di Tecnologia (IIT). This software may be modified and
* distributed under the terms of the BSD-3-Clause license.
*/

#ifndef BIPEDAL_LOCOMOTION_CONTINUOUS_DYNAMICAL_SYSTEM_RK4_H
#define BIPEDAL_LOCOMOTION_CONTINUOUS_DYNAMICAL_SYSTEM_RK4_H

#include <chrono>
#include <tuple>
#include <type_traits>

#include <iDynTree/Core/EigenHelpers.h>

#include <BipedalLocomotion/ContinuousDynamicalSystem/FixedStepIntegrator.h>
#include <BipedalLocomotion/GenericContainer/NamedTuple.h>

namespace BipedalLocomotion
{
namespace ContinuousDynamicalSystem
{
template <typename _DynamicalSystem> class RK4;
}
} // namespace BipedalLocomotion

BLF_DEFINE_INTEGRATOR_STRUCTURE(RK4, _DynamicalSystemType)

namespace BipedalLocomotion
{
namespace ContinuousDynamicalSystem
{

/**
* RK4 implements the runge-kutta 4 integration method.
* It implements the following equation
* \f[
* x_{k+1} = x_k + \frac{1}{6} \left( k_1 + 2 k_2 + 2 k_3 + k_4 \right)
* \f]
* where
* \f[
* \begin{cases}
* k_1 = f(x_k, u_k, t_k) \\
* k_2 = f(x_k + \frac{dt}{2} k_1, u_k, t_k + \frac{dt}{2}) \\
* k_3 = f(x_k + \frac{dt}{2} k_2, u_k, t_k + \frac{dt}{2}) \\
* k_4 = f(x_k + dt k_3, u_k, t_k + dt)
* \end{cases}
* \f]
* where \f$x_k\f$ is the state at time \f$t_k\f$, \f$u_k\f$ is the input at time \f$t_k\f$ and
* \f$dt\f$ is the sampling time.
* @tparam _DynamicalSystem a class derived from DynamicalSystem
* @warning We assume that the operator + is defined for the objects contained in the
* DynamicalSystem::State and DynamicalSystem::StateDerivative.
* @warning The RK4 integrator is compatible only with tuple containing vectors belonging to the
* \f$R^n\f$ space. It is not compatible with Lie groups. If you want to integrate a dynamical
* system defined on a Lie group please use the ForwardEuler integrator.
*/
template <class _DynamicalSystem> class RK4 : public FixedStepIntegrator<RK4<_DynamicalSystem>>
{
public:
using DynamicalSystem = typename internal::traits<RK4<_DynamicalSystem>>::DynamicalSystem;
using State = typename internal::traits<RK4<_DynamicalSystem>>::State;
using StateDerivative = typename internal::traits<RK4<_DynamicalSystem>>::StateDerivative;

private:
/** Temporary buffer usefully to avoid continuous memory allocation */
StateDerivative m_k1;
StateDerivative m_k2;
StateDerivative m_k3;
StateDerivative m_k4;

/** Temporary buffer usefully to avoid continuous memory allocation */
State m_computationalBufferState1;
State m_computationalBufferState2;
State m_computationalBufferState3;
State m_computationalBufferState4;

template <std::size_t I = 0>
inline typename std::enable_if<I == std::tuple_size<State>::value, void>::type
computeNextState(const StateDerivative& k, const std::chrono::nanoseconds& dT, State& x)
{
}

template <std::size_t I = 0>
inline typename std::enable_if<(I < std::tuple_size<State>::value), void>::type
computeNextState(const StateDerivative& k, const std::chrono::nanoseconds& dT, State& x)
{
static_assert(std::tuple_size<State>::value == std::tuple_size<StateDerivative>::value);

using std::get;

// convert the dT in seconds and compute the next state
get<I>(x) = (get<I>(k) * std::chrono::duration<double>(dT).count()) + get<I>(x);
computeNextState<I + 1>(k, dT, x);
}

template <std::size_t I = 0>
inline typename std::enable_if<I == std::tuple_size<State>::value, void>::type
integrateRK4(const StateDerivative& k1,
const StateDerivative& k2,
const StateDerivative& k3,
const StateDerivative& k4,
const std::chrono::nanoseconds& dT,
State& x)
{
}

template <std::size_t I = 0>
inline typename std::enable_if<(I < std::tuple_size<State>::value), void>::type
integrateRK4(const StateDerivative& k1,
const StateDerivative& k2,
const StateDerivative& k3,
const StateDerivative& k4,
const std::chrono::nanoseconds& dT,
State& x)
{
static_assert(std::tuple_size<State>::value == std::tuple_size<StateDerivative>::value);
using std::get;

// convert the dT in seconds
const double dTInSeconds = std::chrono::duration<double>(dT).count();

// complete the RK4 integration
get<I>(x) = get<I>(x)
+ dTInSeconds / 6 * (get<I>(k1) + 2 * get<I>(k2) + 2 * get<I>(k3) + get<I>(k4));
this->integrateRK4<I + 1>(k1, k2, k3, k4, dT, x);
}

public:
/**
* Integrate one step.
* @param t0 initial time.
* @param dT sampling time.
* @return true in case of success, false otherwise.
*/
bool oneStepIntegration(const std::chrono::nanoseconds& t0, const std::chrono::nanoseconds& dT);
};

template <class _DynamicalSystem>
bool RK4<_DynamicalSystem>::oneStepIntegration(const std::chrono::nanoseconds& t0,
const std::chrono::nanoseconds& dT)
{
constexpr auto errorPrefix = "[RK4::oneStepIntegration]";
if (this->m_dynamicalSystem == nullptr)
{
log()->error("{} Please specify the dynamical system.", errorPrefix);
return false;
}

// copy the state 4 times since we need to compute the k1, k2, k3 and k4
this->m_computationalBufferState1 = this->m_dynamicalSystem->getState();
this->m_computationalBufferState2 = this->m_dynamicalSystem->getState();
this->m_computationalBufferState3 = this->m_dynamicalSystem->getState();
this->m_computationalBufferState4 = this->m_dynamicalSystem->getState();

// evaluate k1
// k1 = f(x0, u0)
if (!this->m_dynamicalSystem->dynamics(t0, this->m_k1))
{
log()->error("{} Unable to compute the system dynamics while evaluating k1.", errorPrefix);
return false;
}

// evaluate k2
// k2 = f(x0 + dt / 2 * k1, u0);
this->computeNextState(this->m_k1, dT / 2, this->m_computationalBufferState2);
if (!this->m_dynamicalSystem->setState(this->m_computationalBufferState2))
{
log()->error("{} Unable to set the new state in the dynamical system required to evaluate "
"k2.",
errorPrefix);
return false;
}

if (!this->m_dynamicalSystem->dynamics(t0, this->m_k2))
{
log()->error("{} Unable to compute the system dynamics while evaluating k2.", errorPrefix);
return false;
}

// evaluate k3
// k3 = f(x0 + dt / 2 * k2, u0);
this->computeNextState(this->m_k2, dT / 2, this->m_computationalBufferState3);
if (!this->m_dynamicalSystem->setState(this->m_computationalBufferState3))
{
log()->error("{} Unable to set the new state in the dynamical system required to evaluate "
"k3.",
errorPrefix);
return false;
}

if (!this->m_dynamicalSystem->dynamics(t0, this->m_k3))
{
log()->error("{} Unable to compute the system dynamics while evaluating k3.", errorPrefix);
return false;
}

// evaluate k4
// k4 = f(x0 + dt * k3, u0);
this->computeNextState(this->m_k3, dT, this->m_computationalBufferState4);
if (!this->m_dynamicalSystem->setState(this->m_computationalBufferState4))
{
log()->error("{} Unable to set the new state in the dynamical system required to evaluate "
"k4.",
errorPrefix);
return false;
}

if (!this->m_dynamicalSystem->dynamics(t0, this->m_k4))
{
log()->error("{} Unable to compute the system dynamics while evaluating k4.", errorPrefix);
return false;
}

// compute the integration
this->integrateRK4(this->m_k1,
this->m_k2,
this->m_k3,
this->m_k4,
dT,
this->m_computationalBufferState1);
if (!this->m_dynamicalSystem->setState(this->m_computationalBufferState1))
{
log()->error("{} Unable to set the new state in the dynamical system.", errorPrefix);
return false;
}

return true;
}

} // namespace ContinuousDynamicalSystem
} // namespace BipedalLocomotion

#endif // BIPEDAL_LOCOMOTION_CONTINUOUS_DYNAMICAL_SYSTEM_RK4_H
Loading

0 comments on commit 157b4f2

Please sign in to comment.