Skip to content

Commit d7b4950

Browse files
authored
Merge pull request #40 from Simple-Robotics/topic/friction
Friction compensation
2 parents 93f142b + d43c959 commit d7b4950

File tree

9 files changed

+200
-7
lines changed

9 files changed

+200
-7
lines changed

bindings/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ set(
1313
expose-centroidal.cpp
1414
expose-fulldynamics.cpp
1515
expose-kinodynamics.cpp
16+
expose-friction-compensation.cpp
1617
)
1718

1819
Python3_add_library(${PY_NAME}_pywrap MODULE WITH_SOABI ${${PY_NAME}_SOURCES})
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
///////////////////////////////////////////////////////////////////////////////
2+
// BSD 2-Clause License
3+
//
4+
// Copyright (C) 2025, INRIA
5+
// Copyright note valid unless otherwise stated in individual files.
6+
// All rights reserved.
7+
///////////////////////////////////////////////////////////////////////////////
8+
9+
#include <eigenpy/eigenpy.hpp>
10+
#include <pinocchio/multibody/fwd.hpp>
11+
12+
#include "simple-mpc/friction-compensation.hpp"
13+
14+
namespace simple_mpc
15+
{
16+
namespace python
17+
{
18+
namespace bp = boost::python;
19+
20+
Eigen::VectorXd
21+
computeFrictionProxy(FrictionCompensation & self, Eigen::Ref<const VectorXd> velocity, Eigen::Ref<VectorXd> torque)
22+
{
23+
self.computeFriction(velocity, torque);
24+
25+
return torque;
26+
}
27+
28+
void exposeFrictionCompensation()
29+
{
30+
bp::class_<FrictionCompensation>(
31+
"FrictionCompensation", bp::init<const Model &, const bool>(bp::args("self", "model", "with_free_flyer")))
32+
.def("computeFriction", &computeFrictionProxy)
33+
.add_property("dry_friction", &FrictionCompensation::dry_friction_)
34+
.add_property("viscuous_friction", &FrictionCompensation::viscuous_friction_);
35+
}
36+
37+
} // namespace python
38+
} // namespace simple_mpc

bindings/module.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace simple_mpc::python
1717
void exposeMPC();
1818
void exposeIDSolver();
1919
void exposeIKIDSolver();
20+
void exposeFrictionCompensation();
2021

2122
/* PYTHON MODULE */
2223
BOOST_PYTHON_MODULE(simple_mpc_pywrap)
@@ -35,6 +36,7 @@ namespace simple_mpc::python
3536
exposeMPC();
3637
exposeIDSolver();
3738
exposeIKIDSolver();
39+
exposeFrictionCompensation();
3840
}
3941

4042
} // namespace simple_mpc::python

examples/go2_fulldynamics.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import numpy as np
22
from bullet_robot import BulletRobot
3-
from simple_mpc import RobotModelHandler, RobotDataHandler, FullDynamicsOCP, MPC, IDSolver
3+
from simple_mpc import RobotModelHandler, RobotDataHandler, FullDynamicsOCP, MPC, IDSolver, FrictionCompensation
44
import example_robot_data as erd
55
import pinocchio as pin
66
import time
@@ -21,6 +21,9 @@
2121
model_handler.addFoot("RR_foot", base_joint_name, pin.XYZQUATToSE3(np.array([-0.24,-0.15, 0.0, 0,0,0,1])))
2222
data_handler = RobotDataHandler(model_handler)
2323

24+
nq = model_handler.getModel().nq
25+
nv = model_handler.getModel().nv
26+
nu = nv - 6
2427
force_size = 3
2528
nk = len(model_handler.getFeetNames())
2629
gravity = np.array([0, 0, -9.81])
@@ -110,11 +113,10 @@
110113
"RL_foot": False,
111114
"RR_foot": False,
112115
}
113-
contact_phases = [contact_phase_quadru] * int(T_ds / 2)
116+
contact_phases = [contact_phase_quadru] * T_ds
114117
contact_phases += [contact_phase_lift_FL] * T_ss
115118
contact_phases += [contact_phase_quadru] * T_ds
116119
contact_phases += [contact_phase_lift_FR] * T_ss
117-
contact_phases += [contact_phase_quadru] * int(T_ds / 2)
118120

119121
""" contact_phases = [contact_phase_quadru] * int(T_ds / 2)
120122
contact_phases += [contact_phase_lift] * T_ss
@@ -140,6 +142,9 @@
140142

141143
qp = IDSolver(id_conf, model_handler.getModel())
142144

145+
""" Friction """
146+
fcompensation = FrictionCompensation(model_handler.getModel(), True)
147+
143148
""" Initialize simulation"""
144149
device = BulletRobot(
145150
model_handler.getModel().names,
@@ -187,7 +192,7 @@
187192
L_measured = []
188193

189194
v = np.zeros(6)
190-
v[0] = 0.2
195+
v[0] = 0.
191196
mpc.velocity_base = v
192197
for t in range(500):
193198
print("Time " + str(t))
@@ -270,9 +275,11 @@
270275
mpc.getDataHandler().getData().M,
271276
)
272277

273-
device.execute(qp.solved_torque)
278+
qp_torque = qp.solved_torque.copy()
279+
friction_torque = fcompensation.computeFriction(x_interp[nq + 6:], qp_torque)
280+
device.execute(friction_torque)
274281

275-
u_multibody.append(copy.deepcopy(qp.solved_torque))
282+
u_multibody.append(copy.deepcopy(friction_torque))
276283
x_multibody.append(x_measured)
277284

278285
force_FL = np.array(force_FL)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
///////////////////////////////////////////////////////////////////////////////
2+
// BSD 2-Clause License
3+
//
4+
// Copyright (C) 2025, INRIA
5+
// Copyright note valid unless otherwise stated in individual files.
6+
// All rights reserved.
7+
///////////////////////////////////////////////////////////////////////////////
8+
#pragma once
9+
10+
#include <pinocchio/fwd.hpp>
11+
// Include pinocchio first
12+
#include <Eigen/Dense>
13+
14+
#include "simple-mpc/fwd.hpp"
15+
#include <pinocchio/multibody/model.hpp>
16+
17+
namespace simple_mpc
18+
{
19+
using namespace pinocchio;
20+
21+
/**
22+
* @brief Class managing the friction compensation for torque
23+
*
24+
* It applies a compensation term to a torque depending on dry and
25+
* viscuous frictions.
26+
*/
27+
class FrictionCompensation
28+
{
29+
public:
30+
/**
31+
* @brief Construct a new Friction Compensation object
32+
*
33+
* @param model Pinocchio model containing friction coefficients
34+
@param with_free_flyer Bool indicating if model has free flyer joint
35+
*/
36+
FrictionCompensation(const Model & model, const bool with_free_flyer = true);
37+
38+
/**
39+
* @brief Add friction correction to joint torque
40+
*
41+
* @param[in] velocity Joint velocity
42+
* @param[in] torque Joint torque
43+
*/
44+
void computeFriction(Eigen::Ref<const VectorXd> velocity, Eigen::Ref<VectorXd> torque);
45+
46+
// Sign function for internal computation
47+
static double signFunction(double x);
48+
49+
// Actuation size
50+
int nu_;
51+
52+
// Friction coefficients
53+
Eigen::VectorXd dry_friction_;
54+
Eigen::VectorXd viscuous_friction_;
55+
};
56+
57+
} // namespace simple_mpc

include/simple-mpc/fwd.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ namespace simple_mpc
3838
class OCPHandler;
3939
class IDSolver;
4040
class IKIDSolver;
41+
class FrictionCompensation;
4142

4243
/// EIGEN TYPEDEFS
4344

src/friction-compensation.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include "simple-mpc/friction-compensation.hpp"
2+
#include <iostream>
3+
namespace simple_mpc
4+
{
5+
6+
FrictionCompensation::FrictionCompensation(const Model & model, const bool with_free_flyer)
7+
{
8+
if (with_free_flyer)
9+
{
10+
// Ignore free flyer joint
11+
nu_ = model.nv - 6;
12+
}
13+
else
14+
{
15+
// If no free flyer
16+
nu_ = model.nv;
17+
}
18+
dry_friction_ = model.friction.tail(nu_);
19+
viscuous_friction_ = model.damping.tail(nu_);
20+
}
21+
22+
void FrictionCompensation::computeFriction(Eigen::Ref<const VectorXd> velocity, Eigen::Ref<VectorXd> torque)
23+
{
24+
if (velocity.size() != nu_)
25+
throw std::runtime_error("Velocity has wrong size");
26+
if (torque.size() != nu_)
27+
throw std::runtime_error("Torque has wrong size");
28+
29+
torque += viscuous_friction_.cwiseProduct(velocity)
30+
+ dry_friction_.cwiseProduct(velocity.unaryExpr(std::function(signFunction)));
31+
}
32+
33+
double FrictionCompensation::signFunction(double x)
34+
{
35+
return (x > 0) - (x < 0);
36+
}
37+
38+
} // namespace simple_mpc

tests/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,14 @@ function(add_aligator_test name)
4949
_add_test_prototype(${name} "" ${PROJECT_NAME})
5050
endfunction()
5151

52-
set(TEST_NAMES robot_handler problem mpc lowlevel)
52+
set(
53+
TEST_NAMES
54+
robot_handler
55+
problem
56+
mpc
57+
lowlevel
58+
friction
59+
)
5360

5461
foreach(test_name ${TEST_NAMES})
5562
add_aligator_test(${test_name})

tests/friction.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
#include <boost/test/unit_test.hpp>
3+
4+
#include "simple-mpc/friction-compensation.hpp"
5+
#include "test_utils.cpp"
6+
7+
BOOST_AUTO_TEST_SUITE(friction)
8+
9+
using namespace simple_mpc;
10+
11+
BOOST_AUTO_TEST_CASE(dry_viscuous_friction)
12+
{
13+
Model model;
14+
15+
const std::string urdf_path = EXAMPLE_ROBOT_DATA_MODEL_DIR "/solo_description/robots/solo12.urdf";
16+
17+
pinocchio::urdf::buildModel(urdf_path, JointModelFreeFlyer(), model);
18+
long nu = model.nv - 6;
19+
model.friction.tail(nu) = Eigen::VectorXd::Constant(nu, .5);
20+
model.damping.tail(nu) = Eigen::VectorXd::Constant(nu, .05);
21+
22+
FrictionCompensation friction = FrictionCompensation(model);
23+
24+
BOOST_CHECK_EQUAL(model.friction.tail(nu), friction.dry_friction_);
25+
BOOST_CHECK_EQUAL(model.damping.tail(nu), friction.viscuous_friction_);
26+
27+
Eigen::VectorXd velocity = Eigen::VectorXd::Random(nu);
28+
Eigen::VectorXd torque = Eigen::VectorXd::Random(nu);
29+
30+
Eigen::VectorXd ctorque(nu);
31+
for (long i = 0; i < nu; i++)
32+
{
33+
double sgn = (velocity[i] > 0) - (velocity[i] < 0);
34+
ctorque[i] = torque[i] + model.friction[i + 6] * sgn + model.damping[i + 6] * velocity[i];
35+
}
36+
37+
friction.computeFriction(velocity, torque);
38+
39+
BOOST_CHECK(torque.isApprox(ctorque));
40+
}
41+
42+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)