Skip to content

Commit

Permalink
Merge pull request #4 from inria-paris-robotics-lab/wbmpc/test-flyhigh
Browse files Browse the repository at this point in the history
Fix fly-high residual
  • Loading branch information
ManifoldFR authored Feb 12, 2024
2 parents 2a86e17 + e82743f commit aae0727
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 25 deletions.
1 change: 1 addition & 0 deletions .cmake-format.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ first_comment_is_literal: true
line_width: 90
disabled_codes:
- C0103
- C0111
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,21 @@ if(NOT INSTALL_PYTHON_INTERFACE_ONLY)
endif()

if(BUILD_WITH_ROS_SUPPORT)
# ROS macros temper with some variables (that affect add_executable for tests), save it
if(DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(REMEMBER_CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif(DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)

find_package(catkin REQUIRED message_generation std_msgs)
add_service_files(DIRECTORY ros_qrw_msgs FILES MPCInit.srv MPCSolve.srv MPCStop.srv)
generate_messages(DEPENDENCIES std_msgs)

# Restore saved variable to previous state
if(DEFINED REMEMBER_CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${REMEMBER_CMAKE_RUNTIME_OUTPUT_DIRECTORY})
else(DEFINED REMEMBER_CMAKE_RUNTIME_OUTPUT_DIRECTORY)
unset(CMAKE_RUNTIME_OUTPUT_DIRECTORY)
endif(DEFINED REMEMBER_CMAKE_RUNTIME_OUTPUT_DIRECTORY)
endif()

# Project dependencies
Expand Down Expand Up @@ -126,6 +138,10 @@ target_link_libraries(${PROJECT_NAME} PUBLIC crocoddyl::crocoddyl)
# Link master board library
# target_link_libraries(${PROJECT_NAME} PUBLIC master_board_sdk::master_board_sdk)

if(BUILD_TESTING)
add_subdirectory(tests)
endif()

add_project_dependency(yaml-cpp CONFIG REQUIRED)
if(TARGET yaml-cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC yaml-cpp)
Expand Down
2 changes: 1 addition & 1 deletion config/walk_parameters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ robot:
task:
walk: &base_task_pms
friction_mu: 0.4
fly_high_slope: 2000.
fly_high_sigma_height: 0.03 # m
fly_high_w: 50000.
ground_collision_w: 1000.
vertical_velocity_reg_w: 1.
Expand Down
22 changes: 14 additions & 8 deletions include/qrw/ResidualFlyHigh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,17 @@ using namespace crocoddyl;
/**
* @brief Cost penalizing high horizontal velocity near zero altitude.
*
* The cost is r(q,v) = v_foot[:2] / exp(slope*z_foot)
* The cost is r(q,v) = v_foot[:2] * exp(- z_foot^2/(2*sigma_height^2))
* with v_foot = J_foot(q) vq the local-world-aligned linear velocity of the
* considered frame velocity and z_foot(q) the altitude
* oMfoot[frameId].translation[2] of the considered frame wrt world.
*
* Because the cost follows a gaussian in z, the rest of the cost is
* multiplied by a factor:
* * ~0.30 for z=sigma_height
* * ~0.07 for z=3*sigma_height
* * ~0.006 for z=3*sigma_height
*
* \sa `ResidualModelAbstractTpl`, `calc()`, `calcDiff()`, `createData()`
*/
template <typename _Scalar>
Expand All @@ -58,12 +64,12 @@ class ResidualModelFlyHighTpl : public ResidualModelAbstractTpl<_Scalar> {
* @param[in] state State of the multibody system
* @param[in] frame_id ID of the frame that should be considered for altitude
* and velocity
* @param[in] slope Slope value, ie altitude multiplier.
* @param[in] sigma_height Height reference value.
* @param[in] nu Dimension of the control vector
*/
ResidualModelFlyHighTpl(boost::shared_ptr<StateMultibody> state,
const pinocchio::FrameIndex frame_id,
const Scalar slope, const std::size_t nu);
const Scalar sigma_height, const std::size_t nu);

/**
* @brief Initialize the residual model
Expand All @@ -73,11 +79,11 @@ class ResidualModelFlyHighTpl : public ResidualModelAbstractTpl<_Scalar> {
* @param[in] state State of the multibody system
* @param[in] frame_id ID of the frame that should be considered for altitude
* and velocity
* @param[in] slope Slope value, ie altitude multiplier.
* @param[in] sigma_height Height reference value.
*/
ResidualModelFlyHighTpl(boost::shared_ptr<StateMultibody> state,
const pinocchio::FrameIndex frame_id,
const Scalar slope);
const Scalar sigma_height);
virtual ~ResidualModelFlyHighTpl();

/**
Expand Down Expand Up @@ -114,8 +120,8 @@ class ResidualModelFlyHighTpl : public ResidualModelAbstractTpl<_Scalar> {
*/
void set_frame_id(const pinocchio::FrameIndex& fid);

const Scalar getSlope() const { return slope; }
void setSlope(const Scalar s) { slope = s; }
const Scalar getSigmaHeight() const { return sigma_height; }
void setSigmaHeight(const Scalar s) { sigma_height = s; }

protected:
using Base::nu_;
Expand All @@ -126,7 +132,7 @@ class ResidualModelFlyHighTpl : public ResidualModelAbstractTpl<_Scalar> {

private:
pinocchio::FrameIndex frame_id;
Scalar slope; // multiplication in front of the altitude in the cost
Scalar sigma_height; // multiplication in front of the altitude in the cost
typename StateMultibody::PinocchioModel
pin_model_; //!< Pinocchio model used for internal computations
};
Expand Down
18 changes: 9 additions & 9 deletions include/qrw/ResidualFlyHigh.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ using namespace crocoddyl;
template <typename Scalar>
ResidualModelFlyHighTpl<Scalar>::ResidualModelFlyHighTpl(
boost::shared_ptr<StateMultibody> state,
const pinocchio::FrameIndex frame_id, const Scalar slope,
const pinocchio::FrameIndex frame_id, const Scalar sigma_height,
const std::size_t nu)
: Base(state, 2, nu, true, true, false),
frame_id(frame_id),
slope(slope),
sigma_height(sigma_height),
pin_model_(*state->get_pinocchio()) {}

template <typename Scalar>
ResidualModelFlyHighTpl<Scalar>::ResidualModelFlyHighTpl(
boost::shared_ptr<StateMultibody> state,
const pinocchio::FrameIndex frame_id, const Scalar slope)
const pinocchio::FrameIndex frame_id, const Scalar sigma_height)
: Base(state, 2, true, true, false),
frame_id(frame_id),
slope(slope),
sigma_height(sigma_height),
pin_model_(*state->get_pinocchio()) {}

template <typename Scalar>
Expand All @@ -52,8 +52,7 @@ void ResidualModelFlyHighTpl<Scalar>::calc(
.linear()
.head(2);
Scalar z = d->pinocchio->oMf[frame_id].translation()[2];
// d->ez = exp(-z* z * slope / 2);
d->ez = exp(-z * z * slope / 2);
d->ez = exp(-z * z / (2 * sigma_height * sigma_height));
data->r *= d->ez;
}

Expand Down Expand Up @@ -96,9 +95,10 @@ void ResidualModelFlyHighTpl<Scalar>::calcDiff(
data->Rx *= d->ez;

// Second term with derivative of z
data->Rx.leftCols(nv).row(0) -= data->r[0] * slope * d->o_dv_dv.row(2);
data->Rx.leftCols(nv).row(1) -= data->r[1] * slope * d->o_dv_dv.row(2);
data->Rx.leftCols(nv).topRows(2) *= z;
data->Rx.leftCols(nv).row(0) -=
z / (sigma_height * sigma_height) * data->r[0] * d->o_dv_dv.row(2);
data->Rx.leftCols(nv).row(1) -=
z / (sigma_height * sigma_height) * data->r[1] * d->o_dv_dv.row(2);
}

template <typename Scalar>
Expand Down
10 changes: 5 additions & 5 deletions python/expose-residual-fly-high.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ void exposeResidualFlyHigh() {
"ResidualModelFlyHigh",
bp::init<boost::shared_ptr<StateMultibody>, pinocchio::FrameIndex, double,
std::size_t>(
bp::args("self", "state", "frame_id", "slope", "nu"),
bp::args("self", "state", "frame_id", "sigma_height", "nu"),
"Initialize the residual model.\n\n"
":param state: state of the multibody system\n"
":param frame_id: reference colliding frame\n"
":param slope: slope ie altitude multiplier."
":param sigma_height: height reference value."
":param nu: dimension of control vector"))
.def<void (ResidualModelFlyHigh::*)(
const boost::shared_ptr<ResidualDataAbstract>&,
Expand Down Expand Up @@ -75,9 +75,9 @@ void exposeResidualFlyHigh() {
// &ResidualModelFlyHigh::get_frame_id,
// &ResidualModelFlyHigh::set_frame_id,
// "Frame ID")
.add_property("slope", &ResidualModelFlyHigh::getSlope,
&ResidualModelFlyHigh::setSlope,
"Set slope (ie altitude multiplicator)");
.add_property("sigma_height", &ResidualModelFlyHigh::getSigmaHeight,
&ResidualModelFlyHigh::setSigmaHeight,
"Set sigma_height (ie the height reference value)");

bp::register_ptr_to_python<boost::shared_ptr<ResidualDataFlyHigh> >();

Expand Down
2 changes: 1 addition & 1 deletion python/quadruped_reactive_walking/ocp_defs/walking.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ def _add_fly_high_cost(self, i: int, costs: CostModelSum):
nu = costs.nu
fly_high_cost = CostModelResidual(
self.state,
ResidualModelFlyHigh(self.state, i, self.task.fly_high_slope / 2.0, nu),
ResidualModelFlyHigh(self.state, i, self.task.fly_high_sigma_height, nu),
)
name = "{}_flyHigh".format(self.rmodel.frames[i].name)
costs.addCost(
Expand Down
2 changes: 1 addition & 1 deletion python/quadruped_reactive_walking/wb_mpc/task_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def __init__(self, params: Params):

# Cost function weights
self.friction_mu = task_pms["friction_mu"]
self.fly_high_slope = task_pms["fly_high_slope"]
self.fly_high_sigma_height = task_pms["fly_high_sigma_height"]
self.fly_high_w = task_pms["fly_high_w"]
self.ground_collision_w = task_pms["ground_collision_w"]
self.vertical_velocity_reg_w = task_pms["vertical_velocity_reg_w"]
Expand Down
39 changes: 39 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#
# Copyright (C) 2022-2023 LAAS-CNRS, INRIA
#

find_package(Boost REQUIRED COMPONENTS unit_test_framework)

macro(add_test_cflags test_name flag)
set_property(
TARGET ${test_name}
APPEND_STRING
PROPERTY COMPILE_FLAGS " ${flag}")
endmacro()

function(add_qrw_test name dependencies)
set(test_name "test-cpp-${name}")
set(test_file ${name}.cpp)
message(STATUS "Adding C++ test: ${test_file} (${test_name})")

add_unit_test(${test_name} ${test_file})
set_target_properties(${test_name} PROPERTIES LINKER_LANGUAGE CXX)
target_include_directories(${test_name} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

# define macros required by boost_test
# define module name, replace '-' by '_'
set(MODULE_NAME "${name}Test")
string(REGEX REPLACE "-" "_" MODULE_NAME ${MODULE_NAME})

add_test_cflags(${test_name} "-DBOOST_TEST_DYN_LINK")
add_test_cflags(${test_name} "-DBOOST_TEST_MODULE=${MODULE_NAME}")

target_link_libraries(${test_name} PUBLIC ${dependencies})
target_link_libraries(${test_name} PRIVATE Boost::unit_test_framework)
endfunction()

set(TEST_NAMES residual-models)

foreach(test_name ${TEST_NAMES})
add_qrw_test(${test_name} ${PROJECT_NAME})
endforeach(test_name)
107 changes: 107 additions & 0 deletions tests/residual-models.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include "pinocchio/parsers/urdf.hpp"

#include "pinocchio/algorithm/joint-configuration.hpp"
#include "pinocchio/algorithm/kinematics.hpp"
#include <pinocchio/algorithm/frames.hpp>
#include <pinocchio/algorithm/kinematics-derivatives.hpp>
#include <pinocchio/algorithm/rnea-derivatives.hpp>
#include "qrw/ResidualFlyHigh.hpp"
#include "crocoddyl/core/numdiff/residual.hpp"
#include "crocoddyl/multibody/data/multibody.hpp"
#include "crocoddyl/multibody/residuals/state.hpp"
#include "crocoddyl/multibody/actuations/floating-base.hpp"
#include <boost/function.hpp>

#include <iostream>

#include <boost/test/unit_test.hpp>

void updatePinocchio(pinocchio::Model* const model, pinocchio::Data* data,
const Eigen::VectorXd& x, const Eigen::VectorXd&) {
const Eigen::VectorXd& q = x.segment(0, model->nq);
const Eigen::VectorXd& v = x.segment(model->nq, model->nv);
Eigen::VectorXd a = Eigen::VectorXd::Zero(model->nv);
pinocchio::forwardKinematics(*model, *data, q, v, a);
pinocchio::computeForwardKinematicsDerivatives(*model, *data, q, v, a);
pinocchio::computeJointJacobians(*model, *data, q);
pinocchio::updateFramePlacements(*model, *data);
pinocchio::computeRNEADerivatives(*model, *data, q, v, a);
}

void updateActuation(
const boost::shared_ptr<crocoddyl::ActuationModelAbstract>& model,
const boost::shared_ptr<crocoddyl::ActuationDataAbstract>& data,
const Eigen::VectorXd& x, const Eigen::VectorXd& u) {
model->calc(data, x, u);
}

BOOST_AUTO_TEST_SUITE(residual_models)

BOOST_AUTO_TEST_CASE(test_partial_derivatives_against_numdiff) {
// Pinocchio model and data
const std::string urdf_filename = std::string(
EXAMPLE_ROBOT_DATA_MODEL_DIR "/solo_description/robots/solo12.urdf");
pinocchio::Model pin_model;
pinocchio::urdf::buildModel(urdf_filename, pin_model);
pinocchio::Data pin_data(pin_model);

pinocchio::FrameIndex id = pin_model.getFrameId("FL_FOOT");

// Crocoddyl state, actuation and residual models
boost::shared_ptr<crocoddyl::StateMultibody> state =
boost::make_shared<crocoddyl::StateMultibody>(
boost::make_shared<pinocchio::Model>(pin_model));
boost::shared_ptr<crocoddyl::ActuationModelFloatingBase> actuation_model =
boost::make_shared<crocoddyl::ActuationModelFloatingBase>(state);
boost::shared_ptr<qrw::ResidualModelFlyHigh> flyhigh_cost_model =
boost::make_shared<qrw::ResidualModelFlyHigh>(state, id, 0.1,
actuation_model->get_nu());

// create corresponding datas
const boost::shared_ptr<crocoddyl::ActuationDataAbstract>& actuation_data =
actuation_model->createData();
crocoddyl::DataCollectorActMultibody shared_data(&pin_data, actuation_data);
const boost::shared_ptr<crocoddyl::ResidualDataAbstract>& flyhigh_cost_data =
flyhigh_cost_model->createData(&shared_data);

// Starting with null state and control
Eigen::VectorXd x = state->zero();
Eigen::VectorXd u = Eigen::VectorXd::Zero(actuation_model->get_nu());

for (int i = 0; i < 2; i++) {
updatePinocchio(&pin_model, &pin_data, x, u);
actuation_model->calc(actuation_data, x, u);

// Create the equivalent num diff model and data.
crocoddyl::ResidualModelNumDiff model_num_diff(flyhigh_cost_model);
const boost::shared_ptr<crocoddyl::ResidualDataAbstract>& data_num_diff =
model_num_diff.createData(&shared_data);

// set the function that needs to be called at every step of the numdiff
std::vector<crocoddyl::ResidualModelNumDiff::ReevaluationFunction> reevals;
reevals.push_back(boost::bind(&updatePinocchio, &pin_model, &pin_data,
boost::placeholders::_1,
boost::placeholders::_2));
reevals.push_back(boost::bind(&updateActuation, actuation_model,
actuation_data, boost::placeholders::_1,
boost::placeholders::_2));
model_num_diff.set_reevals(reevals);

// Computing the cost derivatives
flyhigh_cost_model->calc(flyhigh_cost_data, x, u);
flyhigh_cost_model->calcDiff(flyhigh_cost_data, x, u);
model_num_diff.calc(data_num_diff, x, u);
model_num_diff.calcDiff(data_num_diff, x, u);
// Tolerance defined as in
// http://www.it.uom.gr/teaching/linearalgebra/NumericalRecipiesInC/c5-7.pdf
double tol = std::pow(model_num_diff.get_disturbance(), 1. / 3.);
BOOST_CHECK((flyhigh_cost_data->Rx - data_num_diff->Rx).isZero(tol));
BOOST_CHECK((flyhigh_cost_data->Ru - data_num_diff->Ru).isZero(tol));

// Pick new random state and control
x = state->rand();
u = Eigen::VectorXd::Random(actuation_model->get_nu());
}
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit aae0727

Please sign in to comment.