Skip to content

Commit 6c79b2a

Browse files
authored
Port support for Ceres 2.1.0 Manifold class into ROS 2 Rolling (#366)
* Support gcc12 and ceres 2.1.0 * Add support for the Manifold class when using Ceres Solver version 2.1.0 and above * General clean up for Ceres 2.2.0 support * Updated serialization support to be backwards compatible with previously serialized files * Formatting changes required for ROS 2 Rolling / Ubuntu Noble
1 parent 57bdbfa commit 6c79b2a

File tree

96 files changed

+3060
-523
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+3060
-523
lines changed

fuse_constraints/CMakeLists.txt

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,47 @@ include(suitesparse-extras.cmake)
2929
###########
3030
## Build ##
3131
###########
32+
# lint_cmake: -linelength
33+
# Disable warnings about array bounds with -Wno-array-bounds until gcc 12 fixes this bug:
34+
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106247
35+
#
36+
# Also reported in Eigen, and confirmed to be due to gcc 12:
37+
# https://gitlab.com/libeigen/eigen/-/issues/2506
38+
#
39+
# In file included from include/immintrin.h:43,
40+
# from include/eigen3/Eigen/src/Core/util/ConfigureVectorization.h:361,
41+
# from include/eigen3/Eigen/Core:22,
42+
# from include/fuse_core/fuse_macros.h:63,
43+
# from include/fuse_core/loss.h:37,
44+
# from include/fuse_core/constraint.h:37,
45+
# from src/fuse/fuse_constraints/include/fuse_constraints/relative_orientation_3d_stamped_constraint.h:37,
46+
# from src/fuse/fuse_constraints/src/relative_orientation_3d_stamped_constraint.cpp:34:
47+
# In function ‘__m256d _mm256_loadu_pd(const double*)’,
48+
# inlined from ‘Packet Eigen::internal::ploadu(const typename unpacket_traits<T>::type*) [with Packet = __vector(4) double]’ at include/eigen3/Eigen/src/Core/arch/AVX/PacketMath.h:582:129,
49+
# inlined from ‘Packet Eigen::internal::ploadt(const typename unpacket_traits<T>::type*) [with Packet = __vector(4) double; int Alignment = 0]’ at include/eigen3/Eigen/src/Core/GenericPacketMath.h:969:26,
50+
# inlined from ‘PacketType Eigen::internal::evaluator<Eigen::PlainObjectBase<Derived> >::packet(Eigen::Index) const [with int LoadMode = 0; PacketType = __vector(4) double; Derived = Eigen::Matrix<double, 3, 1>]’ at include/eigen3/Eigen/src/Core/CoreEvaluators.h:245:40,
51+
# inlined from ‘void Eigen::internal::generic_dense_assignment_kernel<DstEvaluatorTypeT, SrcEvaluatorTypeT, Functor, Version>::assignPacket(Eigen::Index) [with int StoreMode = 32; int LoadMode = 0; PacketType = __vector(4) double; DstEvaluatorTypeT = Eigen::internal::evaluator<Eigen::Map<Eigen::Matrix<double, -1, 1> > >; SrcEvaluatorTypeT = Eigen::internal::evaluator<Eigen::Matrix<double, 3, 1> >; Functor = Eigen::internal::assign_op<double, double>; int Version = 0]’ at include/eigen3/Eigen/src/Core/AssignEvaluator.h:681:114,
52+
# inlined from ‘static void Eigen::internal::dense_assignment_loop<Kernel, 3, 0>::run(Kernel&) [with Kernel = Eigen::internal::generic_dense_assignment_kernel<Eigen::internal::evaluator<Eigen::Map<Eigen::Matrix<double, -1, 1> > >, Eigen::internal::evaluator<Eigen::Matrix<double, 3, 1> >, Eigen::internal::assign_op<double, double>, 0>]’ at include/eigen3/Eigen/src/Core/AssignEvaluator.h:437:75,
53+
# inlined from ‘void Eigen::internal::call_dense_assignment_loop(DstXprType&, const SrcXprType&, const Functor&) [with DstXprType = Eigen::Map<Eigen::Matrix<double, -1, 1> >; SrcXprType = Eigen::Matrix<double, 3, 1>; Functor = assign_op<double, double>]’ at include/eigen3/Eigen/src/Core/AssignEvaluator.h:785:37,
54+
# inlined from ‘static void Eigen::internal::Assignment<DstXprType, SrcXprType, Functor, Eigen::internal::Dense2Dense, Weak>::run(DstXprType&, const SrcXprType&, const Functor&) [with DstXprType = Eigen::Map<Eigen::Matrix<double, -1, 1> >; SrcXprType = Eigen::Matrix<double, 3, 1>; Functor = Eigen::internal::assign_op<double, double>; Weak = void]’ at include/eigen3/Eigen/src/Core/AssignEvaluator.h:954:31,
55+
# inlined from ‘void Eigen::internal::call_assignment_no_alias(Dst&, const Src&, const Func&) [with Dst = Eigen::Map<Eigen::Matrix<double, -1, 1> >; Src = Eigen::Matrix<double, 3, 1>; Func = assign_op<double, double>]’ at include/eigen3/Eigen/src/Core/AssignEvaluator.h:890:49,
56+
# inlined from ‘void Eigen::internal::call_assignment(Dst&, const Src&, const Func&, typename enable_if<evaluator_assume_aliasing<Src>::value, void*>::type) [with Dst = Eigen::Map<Eigen::Matrix<double, -1, 1> >; Src = Eigen::Product<Eigen::Matrix<double, 3, 3, 1>, Eigen::Map<Eigen::Matrix<double, -1, 1> >, 0>; Func = assign_op<double, double>]’ at include/eigen3/Eigen/src/Core/AssignEvaluator.h:851:27,
57+
# inlined from ‘void Eigen::internal::call_assignment(Dst&, const Src&) [with Dst = Eigen::Map<Eigen::Matrix<double, -1, 1> >; Src = Eigen::Product<Eigen::Matrix<double, 3, 3, 1>, Eigen::Map<Eigen::Matrix<double, -1, 1> >, 0>]’ at include/eigen3/Eigen/src/Core/AssignEvaluator.h:836:18,
58+
# inlined from ‘Derived& Eigen::MatrixBase<Derived>::operator=(const Eigen::DenseBase<OtherDerived>&) [with OtherDerived = Eigen::Product<Eigen::Matrix<double, 3, 3, 1>, Eigen::Map<Eigen::Matrix<double, -1, 1> >, 0>; Derived = Eigen::Map<Eigen::Matrix<double, -1, 1> >]’ at include/eigen3/Eigen/src/Core/Assign.h:66:28,
59+
# inlined from ‘void Eigen::EigenBase<Derived>::applyThisOnTheLeft(Dest&) const [with Dest = Eigen::Map<Eigen::Matrix<double, -1, 1> >; Derived = Eigen::Matrix<double, 3, 3, 1>]’ at include/eigen3/Eigen/src/Core/EigenBase.h:114:9:
60+
# include/avxintrin.h:893:24: error: array subscript ‘__m256d_u[0]’ is partly outside array bounds of ‘Eigen::internal::plain_matrix_type<Eigen::Product<Eigen::Matrix<double, 3, 3, 1>, Eigen::Map<Eigen::Matrix<double, -1, 1> >, 0>, Eigen::Dense>::type [1]’ {aka ‘Eigen::Matrix<double, 3, 1> [1]’} [-Werror=array-bounds]
61+
# 893 | return *(__m256d_u *)__P;
62+
# | ^~~
63+
# In file included from include/eigen3/Eigen/Core:278:
64+
# include/eigen3/Eigen/src/Core/AssignEvaluator.h: In member function ‘void Eigen::EigenBase<Derived>::applyThisOnTheLeft(Dest&) const [with Dest = Eigen::Map<Eigen::Matrix<double, -1, 1> >; Derived = Eigen::Matrix<double, 3, 3, 1>]’:
65+
# include/eigen3/Eigen/src/Core/AssignEvaluator.h:850:41: note: at offset [0, 16] into object ‘tmp’ of size 24
66+
# 850 | typename plain_matrix_type<Src>::type tmp(src);
67+
# | ^~~
68+
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0)
69+
add_compile_options(-Wall -Werror -Wno-array-bounds)
70+
else()
71+
add_compile_options(-Wall -Werror)
72+
endif()
3273

3374
# fuse_constraints library
3475
add_library(${PROJECT_NAME}
@@ -116,7 +157,6 @@ ament_export_dependencies(
116157
geometry_msgs
117158
pluginlib
118159
rclcpp
119-
120160
Ceres
121161
Eigen3
122162
glog

fuse_constraints/include/fuse_constraints/marginal_constraint.hpp

Lines changed: 112 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,21 @@
3838

3939
#include <algorithm>
4040
#include <cassert>
41+
#include <functional>
42+
#include <iterator>
4143
#include <ostream>
44+
#include <stdexcept>
4245
#include <string>
46+
#include <utility>
4347
#include <vector>
4448

49+
#include <fuse_core/ceres_macros.hpp>
4550
#include <fuse_core/constraint.hpp>
4651
#include <fuse_core/eigen.hpp>
47-
#include <fuse_core/local_parameterization.hpp>
4852
#include <fuse_core/fuse_macros.hpp>
53+
#include <fuse_core/local_parameterization.hpp>
54+
#include <fuse_core/manifold.hpp>
55+
#include <fuse_core/manifold_adapter.hpp>
4956
#include <fuse_core/serialization.hpp>
5057
#include <fuse_core/variable.hpp>
5158

@@ -125,13 +132,20 @@ class MarginalConstraint : public fuse_core::Constraint
125132
*/
126133
const std::vector<fuse_core::VectorXd> & x_bar() const {return x_bar_;}
127134

135+
#if !CERES_SUPPORTS_MANIFOLDS
128136
/**
129137
* @brief Read-only access to the variable local parameterizations
130138
*/
131139
const std::vector<fuse_core::LocalParameterization::SharedPtr> & localParameterizations() const
132140
{
133141
return local_parameterizations_;
134142
}
143+
#else
144+
/**
145+
* @brief Read-only access to the variable manifolds
146+
*/
147+
const std::vector<fuse_core::Manifold::SharedPtr> & manifolds() const {return manifolds_;}
148+
#endif
135149

136150
/**
137151
* @brief Print a human-readable description of the constraint to the provided stream.
@@ -155,32 +169,88 @@ class MarginalConstraint : public fuse_core::Constraint
155169
protected:
156170
std::vector<fuse_core::MatrixXd> A_; //!< The A matrices of the marginal constraint
157171
fuse_core::VectorXd b_; //!< The b vector of the marginal constraint
158-
172+
#if !CERES_SUPPORTS_MANIFOLDS
159173
//!< The local parameterizations
160174
std::vector<fuse_core::LocalParameterization::SharedPtr> local_parameterizations_;
161-
175+
#else
176+
std::vector<fuse_core::Manifold::SharedPtr> manifolds_; //!< Manifolds
177+
#endif
162178
std::vector<fuse_core::VectorXd> x_bar_; //!< The linearization point of each involved variable
163179

164180
private:
165181
// Allow Boost Serialization access to private methods
166182
friend class boost::serialization::access;
167183

168184
/**
169-
* @brief The Boost Serialize method that serializes all of the data members in to/out of the
170-
* archive
185+
* @brief The Boost Serialize method that serializes all of the data members in to the archive
171186
*
172-
* @param[in/out] archive - The archive object that holds the serialized class members
173-
* @param[in] version - The version of the archive being read/written. Generally unused.
187+
* @param[out] archive - The archive object into which class members will be serialized
188+
* @param[in] version - The version of the archive being written.
174189
*/
175190
template<class Archive>
176-
void serialize(Archive & archive, const unsigned int /* version */)
191+
void save(Archive & archive, const unsigned int /* version */) const
177192
{
178-
archive & boost::serialization::base_object<fuse_core::Constraint>(*this);
179-
archive & A_;
180-
archive & b_;
181-
archive & local_parameterizations_;
182-
archive & x_bar_;
193+
archive << boost::serialization::base_object<fuse_core::Constraint>(*this);
194+
archive << A_;
195+
archive << b_;
196+
#if !CERES_SUPPORTS_MANIFOLDS
197+
archive << local_parameterizations_;
198+
#else
199+
archive << manifolds_;
200+
#endif
201+
archive << x_bar_;
183202
}
203+
204+
/**
205+
* @brief The Boost Serialize method that serializes all of the data members out of the archive
206+
*
207+
* @param[in] archive - The archive object that holds the serialized class members
208+
* @param[in] version - The version of the archive being read.
209+
*/
210+
template<class Archive>
211+
void load(Archive & archive, const unsigned int version)
212+
{
213+
archive >> boost::serialization::base_object<fuse_core::Constraint>(*this);
214+
archive >> A_;
215+
archive >> b_;
216+
if (version == 0) {
217+
// Version 0 serialization files will contain a std::vector of LocalParameterization
218+
// shared pointers. If the current version of Ceres Solver does not support Manifolds,
219+
// then the serialized LocalParameterization pointers can be deserialized directly into
220+
// the class member. But if the current version of Ceres Solver supports manifolds, then
221+
// the serialized LocalParameterization pointers must be wrapped in a Manifold adapter first.
222+
#if !CERES_SUPPORTS_MANIFOLDS
223+
archive >> local_parameterizations_;
224+
#else
225+
auto local_parameterizations = std::vector<fuse_core::LocalParameterization::SharedPtr>();
226+
archive >> local_parameterizations;
227+
std::transform(
228+
std::make_move_iterator(local_parameterizations.begin()),
229+
std::make_move_iterator(local_parameterizations.end()),
230+
std::back_inserter(manifolds_),
231+
[](fuse_core::LocalParameterization::SharedPtr local_parameterization)
232+
{return fuse_core::ManifoldAdapter::make_shared(std::move(local_parameterization));});
233+
#endif
234+
} else { // (version >= 1)
235+
// Version 1 serialization files will contain a std::vector of Manifold shared pointers. If
236+
// the current version of Ceres Solver does not support Manifolds, then there is no way to
237+
// deserialize the requested data. But if the current version of Ceres Solver does support
238+
// manifolds, then the serialized Manifold pointers can be deserialized directly into the
239+
// class member.
240+
#if !CERES_SUPPORTS_MANIFOLDS
241+
throw std::runtime_error(
242+
"Attempting to deserialize an archive saved in Version " +
243+
std::to_string(
244+
version) + " format. However, the current version of Ceres Solver (" +
245+
CERES_VERSION_STRING + ") does not support manifolds. Ceres Solver version 2.1.0 "
246+
"or later is required to load this file.");
247+
#else
248+
archive >> manifolds_;
249+
#endif
250+
}
251+
archive >> x_bar_;
252+
}
253+
BOOST_SERIALIZATION_SPLIT_MEMBER()
184254
};
185255

186256
namespace detail
@@ -202,14 +272,24 @@ inline const fuse_core::VectorXd getCurrentValue(const fuse_core::Variable & var
202272
return Eigen::Map<const fuse_core::VectorXd>(variable.data(), variable.size());
203273
}
204274

275+
#if !CERES_SUPPORTS_MANIFOLDS
205276
/**
206277
* @brief Return the local parameterization of the provided variable
207278
*/
208-
inline fuse_core::LocalParameterization::SharedPtr const getLocalParameterization(
279+
inline fuse_core::LocalParameterization::SharedPtr getLocalParameterization(
209280
const fuse_core::Variable & variable)
210281
{
211282
return fuse_core::LocalParameterization::SharedPtr(variable.localParameterization());
212283
}
284+
#else
285+
/**
286+
* @brief Return the manifold of the provided variable
287+
*/
288+
inline fuse_core::Manifold::SharedPtr getManifold(const fuse_core::Variable & variable)
289+
{
290+
return fuse_core::Manifold::SharedPtr(variable.manifold());
291+
}
292+
#endif
213293

214294
} // namespace detail
215295

@@ -226,16 +306,26 @@ MarginalConstraint::MarginalConstraint(
226306
boost::make_transform_iterator(last_variable, &fuse_constraints::detail::getUuid)),
227307
A_(first_A, last_A),
228308
b_(b),
309+
#if !CERES_SUPPORTS_MANIFOLDS
229310
local_parameterizations_(boost::make_transform_iterator(first_variable,
230311
&fuse_constraints::detail::getLocalParameterization),
231312
boost::make_transform_iterator(last_variable,
232313
&fuse_constraints::detail::getLocalParameterization)),
314+
#else
315+
manifolds_(
316+
boost::make_transform_iterator(first_variable, &fuse_constraints::detail::getManifold),
317+
boost::make_transform_iterator(last_variable, &fuse_constraints::detail::getManifold)),
318+
#endif
233319
x_bar_(boost::make_transform_iterator(first_variable, &fuse_constraints::detail::getCurrentValue),
234320
boost::make_transform_iterator(last_variable, &fuse_constraints::detail::getCurrentValue))
235321
{
236322
assert(!A_.empty());
237323
assert(A_.size() == x_bar_.size());
324+
#if !CERES_SUPPORTS_MANIFOLDS
238325
assert(A_.size() == local_parameterizations_.size());
326+
#else
327+
assert(A_.size() == manifolds_.size());
328+
#endif
239329
assert(b_.rows() > 0);
240330
assert(
241331
std::all_of(
@@ -255,5 +345,13 @@ MarginalConstraint::MarginalConstraint(
255345
} // namespace fuse_constraints
256346

257347
BOOST_CLASS_EXPORT_KEY(fuse_constraints::MarginalConstraint);
348+
// Since the contents of the serialized file will change depending on the CeresSolver version,
349+
// also set the Boost Serialization version to allow code reading serialized file to know what
350+
// data to expect.
351+
#if !CERES_SUPPORTS_MANIFOLDS
352+
BOOST_CLASS_VERSION(fuse_constraints::MarginalConstraint, 0);
353+
#else
354+
BOOST_CLASS_VERSION(fuse_constraints::MarginalConstraint, 1);
355+
#endif
258356

259357
#endif // FUSE_CONSTRAINTS__MARGINAL_CONSTRAINT_HPP_

fuse_constraints/include/fuse_constraints/marginal_cost_function.hpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@
3838

3939
#include <vector>
4040

41+
#include <fuse_core/ceres_macros.hpp>
4142
#include <fuse_core/eigen.hpp>
4243
#include <fuse_core/local_parameterization.hpp>
43-
44+
#include <fuse_core/manifold.hpp>
4445

4546
namespace fuse_constraints
4647
{
@@ -66,6 +67,7 @@ namespace fuse_constraints
6667
class MarginalCostFunction : public ceres::CostFunction
6768
{
6869
public:
70+
#if !CERES_SUPPORTS_MANIFOLDS
6971
/**
7072
* @brief Construct a cost function instance
7173
*
@@ -81,6 +83,21 @@ class MarginalCostFunction : public ceres::CostFunction
8183
const fuse_core::VectorXd & b,
8284
const std::vector<fuse_core::VectorXd> & x_bar,
8385
const std::vector<fuse_core::LocalParameterization::SharedPtr> & local_parameterizations);
86+
#else
87+
/**
88+
* @brief Construct a cost function instance
89+
*
90+
* @param[in] A The A matrix of the marginal cost (of the form A*(x - x_bar) + b)
91+
* @param[in] b The b vector of the marginal cost (of the form A*(x - x_bar) + b)
92+
* @param[in] x_bar The linearization point of the involved variables
93+
* @param[in] manifolds The manifold associated with the variable
94+
*/
95+
MarginalCostFunction(
96+
const std::vector<fuse_core::MatrixXd> & A,
97+
const fuse_core::VectorXd & b,
98+
const std::vector<fuse_core::VectorXd> & x_bar,
99+
const std::vector<fuse_core::Manifold::SharedPtr> & manifolds);
100+
#endif
84101

85102
/**
86103
* @brief Destructor
@@ -99,10 +116,12 @@ class MarginalCostFunction : public ceres::CostFunction
99116
private:
100117
const std::vector<fuse_core::MatrixXd> & A_; //!< The A matrices of the marginal cost
101118
const fuse_core::VectorXd & b_; //!< The b vector of the marginal cost
102-
119+
#if !CERES_SUPPORTS_MANIFOLDS
103120
//!< Parameterizations
104121
const std::vector<fuse_core::LocalParameterization::SharedPtr> & local_parameterizations_;
105-
122+
#else
123+
const std::vector<fuse_core::Manifold::SharedPtr> & manifolds_; //!< Manifolds
124+
#endif
106125
const std::vector<fuse_core::VectorXd> & x_bar_; //!< The linearization point of each variable
107126
};
108127

fuse_constraints/include/fuse_constraints/marginalize_variables.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@
4747
#include <fuse_constraints/uuid_ordering.hpp>
4848
#include <fuse_core/constraint.hpp>
4949
#include <fuse_core/eigen.hpp>
50-
#include <fuse_core/graph.hpp>
51-
#include <fuse_core/local_parameterization.hpp>
5250
#include <fuse_core/fuse_macros.hpp>
51+
#include <fuse_core/graph.hpp>
5352
#include <fuse_core/transaction.hpp>
5453
#include <fuse_core/variable.hpp>
5554

fuse_constraints/src/marginal_constraint.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <boost/serialization/export.hpp>
3939
#include <fuse_constraints/marginal_constraint.hpp>
4040
#include <fuse_constraints/marginal_cost_function.hpp>
41+
#include <fuse_core/ceres_macros.hpp>
4142
#include <fuse_core/constraint.hpp>
4243
#include <pluginlib/class_list_macros.hpp>
4344

@@ -68,7 +69,11 @@ void MarginalConstraint::print(std::ostream & stream) const
6869

6970
ceres::CostFunction * MarginalConstraint::costFunction() const
7071
{
72+
#if !CERES_SUPPORTS_MANIFOLDS
7173
return new MarginalCostFunction(A_, b_, x_bar_, local_parameterizations_);
74+
#else
75+
return new MarginalCostFunction(A_, b_, x_bar_, manifolds_);
76+
#endif
7277
}
7378

7479
} // namespace fuse_constraints

0 commit comments

Comments
 (0)