Skip to content

Commit d2f6502

Browse files
authored
Merge branch 'devel' into pre-commit-ci-update-config
2 parents 2dde34e + 52dec09 commit d2f6502

14 files changed

+616
-8
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Only link against needed pinocchio libraries ([#260](https://github.com/Simple-Robotics/aligator/pull/260))
1313
- Use Pinocchio instantiated functions ([#261](https://github.com/Simple-Robotics/aligator/pull/261))
14+
- Link to pinocchio collision
1415

1516
### Fixed
1617

1718
- `LinesearchVariant::init()` should not be called unless the step accpetance strategy is a linesearch
1819
- Fixed compilation issues with C++20 (resolving [#246](https://github.com/Simple-Robotics/aligator/issues/246) and [#254](https://github.com/Simple-Robotics/aligator/discussions/254))
1920

21+
### Added
22+
23+
- Add a collision distance residual for collision pair
24+
- Add a relaxed log-barrier cost function
25+
2026
## [0.10.0] - 2024-12-09
2127

2228
### Added

CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,10 @@ function(create_library)
398398
)
399399

400400
if(BUILD_WITH_PINOCCHIO_SUPPORT)
401-
target_link_libraries(${PROJECT_NAME} PUBLIC pinocchio::pinocchio_default)
401+
target_link_libraries(
402+
${PROJECT_NAME}
403+
PUBLIC pinocchio::pinocchio_default pinocchio::pinocchio_collision
404+
)
402405
endif()
403406

404407
if(BUILD_WITH_OPENMP_SUPPORT)

bindings/python/src/modelling/expose-composite-costs.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "aligator/modelling/costs/quad-state-cost.hpp"
55
#include "aligator/modelling/costs/log-residual-cost.hpp"
6+
#include "aligator/modelling/costs/relaxed-log-barrier.hpp"
67
#include "aligator/python/polymorphic-convertible.hpp"
78

89
namespace aligator {
@@ -25,6 +26,7 @@ void exposeComposites() {
2526
using QuadStateCost = QuadraticStateCostTpl<Scalar>;
2627
using QuadControlCost = QuadraticControlCostTpl<Scalar>;
2728
using LogResCost = LogResidualCostTpl<Scalar>;
29+
using RelaxedLogCost = RelaxedLogBarrierCostTpl<Scalar>;
2830

2931
PolymorphicMultiBaseVisitor<CostAbstract> visitor;
3032
bp::class_<QuadResCost, bp::bases<CostAbstract>>(
@@ -47,6 +49,18 @@ void exposeComposites() {
4749
.def(CopyableVisitor<LogResCost>())
4850
.def(visitor);
4951

52+
bp::class_<RelaxedLogCost, bp::bases<CostAbstract>>(
53+
"RelaxedLogBarrierCost", "Relaxed log-barrier composite cost.",
54+
bp::init<PolyManifold, PolyFunction, const ConstVectorRef &,
55+
const Scalar>(
56+
bp::args("self", "space", "function", "weights", "threshold")))
57+
.def(bp::init<PolyManifold, PolyFunction, const Scalar, const Scalar>(
58+
bp::args("self", "space", "function", "weights", "threshold")))
59+
.def_readwrite("residual", &RelaxedLogCost::residual_)
60+
.def_readwrite("weights", &RelaxedLogCost::barrier_weights_)
61+
.def(CopyableVisitor<RelaxedLogCost>())
62+
.def(visitor);
63+
5064
bp::class_<CompositeData, bp::bases<CostData>>(
5165
"CompositeCostData",
5266
bp::init<int, int, shared_ptr<context::StageFunctionData>>(

bindings/python/src/modelling/expose-pinocchio-functions.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "aligator/modelling/multibody/frame-placement.hpp"
88
#include "aligator/modelling/multibody/frame-velocity.hpp"
99
#include "aligator/modelling/multibody/frame-translation.hpp"
10+
#include "aligator/modelling/multibody/frame-collision.hpp"
1011
#include "aligator/python/polymorphic-convertible.hpp"
1112
#ifdef ALIGATOR_PINOCCHIO_V3
1213
#include "aligator/modelling/multibody/constrained-rnea.hpp"
@@ -52,6 +53,11 @@ void exposeFrameFunctions() {
5253
using FrameTranslation = FrameTranslationResidualTpl<Scalar>;
5354
using FrameTranslationData = FrameTranslationDataTpl<Scalar>;
5455

56+
using FrameCollision = FrameCollisionResidualTpl<Scalar>;
57+
using FrameCollisionData = FrameCollisionDataTpl<Scalar>;
58+
59+
using pinocchio::GeometryModel;
60+
5561
bp::register_ptr_to_python<shared_ptr<PinData>>();
5662

5763
PolymorphicMultiBaseVisitor<UnaryFunction, StageFunction> unary_visitor;
@@ -119,6 +125,24 @@ void exposeFrameFunctions() {
119125
.def_readonly("fJf", &FrameTranslationData::fJf_)
120126
.def_readonly("pin_data", &FrameTranslationData::pin_data_,
121127
"Pinocchio data struct.");
128+
129+
bp::class_<FrameCollision, bp::bases<UnaryFunction>>(
130+
"FrameCollisionResidual", "Frame collision residual function.",
131+
bp::init<int, int, const PinModel &, const GeometryModel &,
132+
pinocchio::PairIndex>(bp::args("self", "ndx", "nu", "model",
133+
"geom_model", "frame_pair_id")))
134+
.def(FrameAPIVisitor<FrameCollision>())
135+
.def(unary_visitor);
136+
137+
bp::register_ptr_to_python<shared_ptr<FrameCollisionData>>();
138+
139+
bp::class_<FrameCollisionData, bp::bases<context::StageFunctionData>>(
140+
"FrameCollisionData", "Data struct for FrameCollisionResidual.",
141+
bp::no_init)
142+
.def_readonly("pin_data", &FrameCollisionData::pin_data_,
143+
"Pinocchio data struct.")
144+
.def_readonly("geom_data", &FrameCollisionData::geometry_,
145+
"Geometry data struct.");
122146
}
123147

124148
#ifdef ALIGATOR_PINOCCHIO_V3

examples/ur5_reach.py

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from aligator import constraints, manifolds, dynamics # noqa
1010
from pinocchio.visualize import MeshcatVisualizer
11+
import hppfcl
1112

1213
from utils import ArgsBase, get_endpoint_traj
1314

@@ -16,6 +17,7 @@ class Args(ArgsBase):
1617
plot: bool = True
1718
fddp: bool = False
1819
bounds: bool = False
20+
collisions: bool = False
1921

2022

2123
args = Args().parse_args()
@@ -32,6 +34,30 @@ class Args(ArgsBase):
3234
vizer.initViewer(open=args.display, loadModel=True)
3335
vizer.setBackgroundColor()
3436

37+
fr_name = "universe"
38+
fr_id = rmodel.getFrameId(fr_name)
39+
joint_id = rmodel.frames[fr_id].parentJoint
40+
if args.collisions:
41+
obstacle_loc = pin.SE3.Identity()
42+
obstacle_loc.translation[0] = 0.3
43+
obstacle_loc.translation[1] = 0.5
44+
obstacle_loc.translation[2] = 0.3
45+
geom_object = pin.GeometryObject(
46+
"capsule", fr_id, joint_id, hppfcl.Capsule(0.05, 0.4), obstacle_loc
47+
)
48+
49+
fr_id2 = rmodel.getFrameId("wrist_3_joint")
50+
joint_id2 = rmodel.frames[fr_id2].parentJoint
51+
geom_object2 = pin.GeometryObject(
52+
"endeffector", fr_id2, joint_id2, hppfcl.Sphere(0.1), pin.SE3.Identity()
53+
)
54+
55+
geometry = pin.GeometryModel()
56+
ig_frame = geometry.addGeometryObject(geom_object)
57+
ig_frame2 = geometry.addGeometryObject(geom_object2)
58+
geometry.addCollisionPair(pin.CollisionPair(ig_frame, ig_frame2))
59+
60+
vizer.addGeometryObject(geom_object, [1.0, 1.0, 0.5, 1.0])
3561

3662
x0 = space.neutral()
3763

@@ -61,6 +87,12 @@ class Args(ArgsBase):
6187
tool_name = "tool0"
6288
tool_id = rmodel.getFrameId(tool_name)
6389
target_pos = np.array([0.15, 0.65, 0.5])
90+
target_place = pin.SE3.Identity()
91+
target_place.translation = target_pos
92+
target_object = pin.GeometryObject(
93+
"target", fr_id, joint_id, hppfcl.Sphere(0.05), target_place
94+
)
95+
vizer.addGeometryObject(target_object, [0.5, 0.5, 1.0, 1.0])
6496
print(target_pos)
6597

6698
frame_fn = aligator.FrameTranslationResidual(ndx, nu, rmodel, target_pos, tool_id)
@@ -71,14 +103,18 @@ class Args(ArgsBase):
71103
)
72104
wt_x_term = wt_x.copy()
73105
wt_x_term[:] = 1e-4
74-
wt_frame_pos = 10.0 * np.eye(frame_fn.nr)
106+
wt_frame_pos = 100.0 * np.eye(frame_fn.nr)
75107
wt_frame_vel = 100.0 * np.ones(frame_vel_fn.nr)
76108
wt_frame_vel = np.diag(wt_frame_vel)
77109

78110
term_cost = aligator.CostStack(space, nu)
79-
term_cost.addCost(aligator.QuadraticCost(wt_x_term, wt_u * 0))
80-
term_cost.addCost(aligator.QuadraticResidualCost(space, frame_fn, wt_frame_pos))
81-
term_cost.addCost(aligator.QuadraticResidualCost(space, frame_vel_fn, wt_frame_vel))
111+
term_cost.addCost("reg", aligator.QuadraticCost(wt_x_term, wt_u * 0))
112+
term_cost.addCost(
113+
"frame", aligator.QuadraticResidualCost(space, frame_fn, wt_frame_pos)
114+
)
115+
term_cost.addCost(
116+
"vel", aligator.QuadraticResidualCost(space, frame_vel_fn, wt_frame_vel)
117+
)
82118

83119
u_max = rmodel.effortLimit
84120
u_min = -u_max
@@ -103,12 +139,16 @@ def computeQuasistatic(model: pin.Model, x0, a):
103139

104140

105141
stages = []
106-
107142
for i in range(nsteps):
108143
rcost = aligator.CostStack(space, nu)
109-
rcost.addCost(aligator.QuadraticCost(wt_x * dt, wt_u * dt))
144+
rcost.addCost("reg", aligator.QuadraticCost(wt_x * dt, wt_u * dt))
110145

111146
stm = aligator.StageModel(rcost, discrete_dynamics)
147+
if args.collisions:
148+
# Distance to obstacle constrained between 0.1 and 100 m
149+
cstr_set = constraints.BoxConstraint(np.array([0.1]), np.array([100]))
150+
frame_col = aligator.FrameCollisionResidual(ndx, nu, rmodel, geometry, 0)
151+
stm.addConstraint(frame_col, cstr_set)
112152
if args.bounds:
113153
stm.addConstraint(*make_control_bounds())
114154
stages.append(stm)
@@ -119,7 +159,7 @@ def computeQuasistatic(model: pin.Model, x0, a):
119159

120160
mu_init = 1e-7
121161
verbose = aligator.VerboseLevel.VERBOSE
122-
max_iters = 40
162+
max_iters = 500
123163
solver = aligator.SolverProxDDP(tol, mu_init, max_iters=max_iters, verbose=verbose)
124164
solver.rollout_type = aligator.ROLLOUT_NONLINEAR
125165
solver.sa_strategy = aligator.SA_LINESEARCH_NONMONOTONE
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/// @file
2+
/// @copyright Copyright (C) 2025 INRIA
3+
#pragma once
4+
5+
#include "./composite-costs.hpp"
6+
7+
namespace aligator {
8+
9+
/// @brief Log-barrier of an underlying cost function.
10+
template <typename Scalar>
11+
struct RelaxedLogBarrierCostTpl : CostAbstractTpl<Scalar> {
12+
ALIGATOR_DYNAMIC_TYPEDEFS(Scalar);
13+
using CostDataAbstract = CostDataAbstractTpl<Scalar>;
14+
using Data = CompositeCostDataTpl<Scalar>;
15+
using StageFunction = StageFunctionTpl<Scalar>;
16+
using Base = CostAbstractTpl<Scalar>;
17+
using Manifold = ManifoldAbstractTpl<Scalar>;
18+
19+
VectorXs barrier_weights_;
20+
xyz::polymorphic<StageFunction> residual_;
21+
Scalar threshold_;
22+
23+
RelaxedLogBarrierCostTpl(xyz::polymorphic<Manifold> space,
24+
xyz::polymorphic<StageFunction> function,
25+
const ConstVectorRef &weight,
26+
const Scalar threshold);
27+
28+
RelaxedLogBarrierCostTpl(xyz::polymorphic<Manifold> space,
29+
xyz::polymorphic<StageFunction> function,
30+
const Scalar weight, const Scalar threshold);
31+
32+
void evaluate(const ConstVectorRef &x, const ConstVectorRef &u,
33+
CostDataAbstract &data) const;
34+
35+
void computeGradients(const ConstVectorRef &x, const ConstVectorRef &u,
36+
CostDataAbstract &data) const;
37+
38+
void computeHessians(const ConstVectorRef &, const ConstVectorRef &,
39+
CostDataAbstract &data) const;
40+
41+
shared_ptr<CostDataAbstract> createData() const {
42+
return std::make_shared<Data>(this->ndx(), this->nu,
43+
residual_->createData());
44+
}
45+
46+
/// @brief Get a pointer to the underlying type of the residual, by attempting
47+
/// to cast.
48+
template <typename Derived> Derived *getResidual() {
49+
return dynamic_cast<Derived *>(&*residual_);
50+
}
51+
52+
/// @copybrief getResidual().
53+
template <typename Derived> const Derived *getResidual() const {
54+
return dynamic_cast<const Derived *>(&*residual_);
55+
}
56+
};
57+
58+
extern template struct RelaxedLogBarrierCostTpl<context::Scalar>;
59+
60+
} // namespace aligator
61+
62+
#include "./relaxed-log-barrier.hxx"
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/// @file
2+
/// @copyright Copyright (C) 2025 INRIA
3+
#pragma once
4+
5+
#include "./relaxed-log-barrier.hpp"
6+
7+
namespace aligator {
8+
9+
template <typename Scalar>
10+
RelaxedLogBarrierCostTpl<Scalar>::RelaxedLogBarrierCostTpl(
11+
xyz::polymorphic<Manifold> space, xyz::polymorphic<StageFunction> function,
12+
const ConstVectorRef &weight, const Scalar threshold)
13+
: Base(space, function->nu), barrier_weights_(weight), residual_(function),
14+
threshold_(threshold) {
15+
if (weight.size() != function->nr) {
16+
ALIGATOR_RUNTIME_ERROR(
17+
"weight argument dimension ({:d}) != function codimension ({:d})",
18+
weight.size(), function->nr);
19+
}
20+
bool negs = (weight.array() <= 0.0).any();
21+
if (negs) {
22+
ALIGATOR_RUNTIME_ERROR("weight coefficients must be > 0.");
23+
}
24+
if (threshold_ <= 0.) {
25+
ALIGATOR_RUNTIME_ERROR("threshold must be > 0.");
26+
}
27+
}
28+
29+
template <typename Scalar>
30+
RelaxedLogBarrierCostTpl<Scalar>::RelaxedLogBarrierCostTpl(
31+
xyz::polymorphic<Manifold> space, xyz::polymorphic<StageFunction> function,
32+
const Scalar weight, const Scalar threshold)
33+
: RelaxedLogBarrierCostTpl(space, function,
34+
VectorXs::Constant(function->nr, weight),
35+
threshold) {}
36+
37+
template <typename Scalar>
38+
void RelaxedLogBarrierCostTpl<Scalar>::evaluate(const ConstVectorRef &x,
39+
const ConstVectorRef &u,
40+
CostDataAbstract &data) const {
41+
Data &d = static_cast<Data &>(data);
42+
residual_->evaluate(x, u, *d.residual_data);
43+
d.value_ = 0.;
44+
const int nrows = residual_->nr;
45+
for (size_t i = 0; i < nrows; i++) {
46+
if (d.residual_data->value_[i] < threshold_) {
47+
Scalar sq = (d.residual_data->value_[i] - 2 * threshold_) / threshold_;
48+
d.value_ += barrier_weights_(i) * (0.5 * (sq * sq - 1) - log(threshold_));
49+
} else {
50+
d.value_ -= barrier_weights_(i) * log(d.residual_data->value_[i]);
51+
}
52+
}
53+
}
54+
55+
template <typename Scalar>
56+
void RelaxedLogBarrierCostTpl<Scalar>::computeGradients(
57+
const ConstVectorRef &x, const ConstVectorRef &u,
58+
CostDataAbstract &data) const {
59+
Data &d = static_cast<Data &>(data);
60+
StageFunctionDataTpl<Scalar> &res_data = *d.residual_data;
61+
MatrixRef J = res_data.jac_buffer_.leftCols(data.grad_.size());
62+
residual_->computeJacobians(x, u, res_data);
63+
d.grad_.setZero();
64+
VectorXs &v = res_data.value_;
65+
const int nrows = residual_->nr;
66+
for (int i = 0; i < nrows; i++) {
67+
auto g_i = J.row(i);
68+
if (v(i) < threshold_) {
69+
d.grad_.noalias() += barrier_weights_(i) * g_i * (v(i) - 2 * threshold_) /
70+
(threshold_ * threshold_);
71+
} else {
72+
d.grad_.noalias() -= barrier_weights_(i) * g_i / v(i);
73+
}
74+
}
75+
}
76+
77+
template <typename Scalar>
78+
void RelaxedLogBarrierCostTpl<Scalar>::computeHessians(
79+
const ConstVectorRef &, const ConstVectorRef &,
80+
CostDataAbstract &data) const {
81+
Data &d = static_cast<Data &>(data);
82+
StageFunctionDataTpl<Scalar> &res_data = *d.residual_data;
83+
const Eigen::Index size = data.grad_.size();
84+
d.hess_.setZero();
85+
MatrixRef J = res_data.jac_buffer_.leftCols(size);
86+
VectorXs &v = res_data.value_;
87+
const int nrows = residual_->nr;
88+
for (int i = 0; i < nrows; i++) {
89+
auto g_i = J.row(i); // row vector
90+
if (v(i) < threshold_) {
91+
Scalar sq = (v(i) - 2 * threshold_) / (threshold_ * threshold_);
92+
d.hess_.noalias() += barrier_weights_(i) * barrier_weights_(i) *
93+
(g_i.transpose() * g_i) * sq * sq;
94+
} else {
95+
d.hess_.noalias() += barrier_weights_(i) * barrier_weights_(i) *
96+
(g_i.transpose() * g_i) / (v(i) * v(i));
97+
}
98+
}
99+
}
100+
} // namespace aligator

0 commit comments

Comments
 (0)