-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
cspace_free_polytope.h
624 lines (547 loc) · 26.7 KB
/
cspace_free_polytope.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
#pragma once
#include <array>
#include <map>
#include <memory>
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <fmt/format.h>
#include "drake/geometry/optimization/c_iris_collision_geometry.h"
#include "drake/geometry/optimization/cspace_free_polytope_base.h"
#include "drake/geometry/optimization/cspace_free_structs.h"
#include "drake/geometry/optimization/hpolyhedron.h"
#include "drake/multibody/rational/rational_forward_kinematics.h"
#include "drake/solvers/mathematical_program.h"
#include "drake/solvers/mathematical_program_result.h"
#include "drake/solvers/mosek_solver.h"
namespace drake {
namespace geometry {
namespace optimization {
/**
This class tries to find large convex polytopes in the tangential-configuration
space, such that all configurations in the convex polytopes is collision free.
By tangential-configuration space, we mean the revolute joint angle θ is
replaced by t = tan(θ/2). We refer to the algorithm as C-IRIS.
For more details, refer to the paper
Certified Polyhedral Decomposition of Collision-Free Configuration Space
by Hongkai Dai*, Alexandre Amice*, Peter Werner, Annan Zhang and Russ Tedrake.
A conference version is published at
Finding and Optimizing Certified, Collision-Free Regions in Configuration Space
for Robot Manipulators
by Alexandre Amice*, Hongkai Dai*, Peter Werner, Annan Zhang and Russ Tedrake.
*/
class CspaceFreePolytope : public CspaceFreePolytopeBase {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(CspaceFreePolytope)
using CspaceFreePolytopeBase::IgnoredCollisionPairs;
~CspaceFreePolytope() override = default;
using CspaceFreePolytopeBase::Options;
/**
@param plant The plant for which we compute the C-space free polytopes. It
must outlive this CspaceFreePolytope object.
@param scene_graph The scene graph that has been connected with `plant`. It
must outlive this CspaceFreePolytope object.
@param plane_order The order of the polynomials in the plane to separate a
pair of collision geometries.
@param q_star Refer to RationalForwardKinematics for its meaning.
@note CspaceFreePolytope knows nothing about contexts. The plant and
scene_graph must be fully configured before instantiating this class.
*/
CspaceFreePolytope(const multibody::MultibodyPlant<double>* plant,
const geometry::SceneGraph<double>* scene_graph,
SeparatingPlaneOrder plane_order,
const Eigen::Ref<const Eigen::VectorXd>& q_star,
const Options& options = Options{});
/**
When searching for the separating plane, we want to certify that the
numerator of a rational is non-negative in the C-space region C*s<=d,
s_lower <= s <= s_upper. Hence for each of the rational we will introduce
Lagrangian multipliers for the polytopic constraint d-C*s >= 0, s - s_lower
>= 0, s_upper - s >= 0.
*/
class SeparatingPlaneLagrangians {
public:
SeparatingPlaneLagrangians(int C_rows, int s_size)
: polytope_(C_rows), s_lower_(s_size), s_upper_(s_size) {}
/** Substitutes the decision variables in each Lagrangians with its value in
* result, returns the substitution result.
*/
[[nodiscard]] SeparatingPlaneLagrangians GetSolution(
const solvers::MathematicalProgramResult& result) const;
/// The Lagrangians for d - C*s >= 0.
const VectorX<symbolic::Polynomial>& polytope() const { return polytope_; }
/// The Lagrangians for d - C*s >= 0.
VectorX<symbolic::Polynomial>& mutable_polytope() { return polytope_; }
/// The Lagrangians for s - s_lower >= 0.
const VectorX<symbolic::Polynomial>& s_lower() const { return s_lower_; }
/// The Lagrangians for s - s_lower >= 0.
VectorX<symbolic::Polynomial>& mutable_s_lower() { return s_lower_; }
/// The Lagrangians for s_upper - s >= 0.
const VectorX<symbolic::Polynomial>& s_upper() const { return s_upper_; }
/// The Lagrangians for s_upper - s >= 0.
VectorX<symbolic::Polynomial>& mutable_s_upper() { return s_upper_; }
private:
// The Lagrangians for d - C*s >= 0.
VectorX<symbolic::Polynomial> polytope_;
// The Lagrangians for s - s_lower >= 0.
VectorX<symbolic::Polynomial> s_lower_;
// The Lagrangians for s_upper - s >= 0.
VectorX<symbolic::Polynomial> s_upper_;
};
/**
We certify that a pair of geometries is collision free in the C-space region
{s | Cs<=d, s_lower<=s<=s_upper} by finding the separating plane and the
Lagrangian multipliers. This struct contains the certificate, that the
separating plane {x | aᵀx+b=0 } separates the two geometries in
separating_planes()[plane_index] in the C-space polytope.
*/
struct SeparationCertificateResult final : SeparationCertificateResultBase {
DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(SeparationCertificateResult)
SeparationCertificateResult() {}
~SeparationCertificateResult() override = default;
const std::vector<SeparatingPlaneLagrangians>& lagrangians(
PlaneSide plane_side) const {
return plane_side == PlaneSide::kPositive
? positive_side_rational_lagrangians
: negative_side_rational_lagrangians;
}
std::vector<SeparatingPlaneLagrangians> positive_side_rational_lagrangians;
std::vector<SeparatingPlaneLagrangians> negative_side_rational_lagrangians;
};
/**
This struct stores the necessary information to search for the separating
plane for the polytopic C-space region C*s <= d, s_lower <= s <= s_upper.
We need to impose that N rationals are non-negative in this C-space polytope.
The denominator of each rational is always positive hence we need to impose
the N numerators are non-negative in this C-space region.
We impose the condition
numerator_i(s) - λ(s)ᵀ * (d - C*s) - λ_lower(s)ᵀ * (s - s_lower)
-λ_upper(s)ᵀ * (s_upper - s) is sos
λ(s) are sos, λ_lower(s) are sos, λ_upper(s) are sos.
*/
struct SeparationCertificate {
SeparationCertificate() {}
[[nodiscard]] SeparationCertificateResult GetSolution(
int plane_index, const Vector3<symbolic::Polynomial>& a,
const symbolic::Polynomial& b,
const VectorX<symbolic::Variable>& plane_decision_vars,
const solvers::MathematicalProgramResult& result) const;
std::vector<SeparatingPlaneLagrangians>& mutable_lagrangians(
PlaneSide plane_side) {
return plane_side == PlaneSide::kPositive
? positive_side_rational_lagrangians
: negative_side_rational_lagrangians;
}
// positive_side_rational_lagrangians[i] is the Lagrangian multipliers for
// PlaneSeparatesGeometries::positive_side_rationals[i].
std::vector<SeparatingPlaneLagrangians> positive_side_rational_lagrangians;
// negative_side_rational_lagrangians[i] is the Lagrangian multipliers for
// PlaneSeparatesGeometries::negative_side_rationals[i].
std::vector<SeparatingPlaneLagrangians> negative_side_rational_lagrangians;
};
struct SeparationCertificateProgram final : SeparationCertificateProgramBase {
DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(SeparationCertificateProgram)
SeparationCertificateProgram() = default;
virtual ~SeparationCertificateProgram() = default;
SeparationCertificate certificate;
};
struct FindSeparationCertificateGivenPolytopeOptions final
: public FindSeparationCertificateOptions {
~FindSeparationCertificateGivenPolytopeOptions() override = default;
// If a row in C*s<=d is redundant (this row is implied by other rows in
// C*s<=d, s_lower<=s<=s_upper), then we don't search for the Lagrangian
// multiplier for this row.
bool ignore_redundant_C{false};
};
/** Finds the certificates that the C-space polytope {s | C*s<=d, s_lower <= s
* <= s_upper} is collision free.
*
* @param C The C-space polytope is {s | C*s<=d, s_lower<=s<=s_upper}
* @param d The C-space polytope is {s | C*s<=d, s_lower<=s<=s_upper}
* @param ignored_collision_pairs We will ignore the pair of geometries in
* `ignored_collision_pairs`.
* @param[out] certificates Contains the certificate we successfully found for
* each pair of geometries. Notice that depending on `options`, the program
* could search for the certificate for each geometry pair in parallel, and
* will terminate the search once it fails to find the certificate for any
* pair.
* @retval success If true, then we have certified that the C-space polytope
* {s | C*s<=d, s_lower<=s<=s_upper} is collision free. Otherwise
* success=false.
*/
[[nodiscard]] bool FindSeparationCertificateGivenPolytope(
const Eigen::Ref<const Eigen::MatrixXd>& C,
const Eigen::Ref<const Eigen::VectorXd>& d,
const IgnoredCollisionPairs& ignored_collision_pairs,
const FindSeparationCertificateGivenPolytopeOptions& options,
std::unordered_map<SortedPair<geometry::GeometryId>,
SeparationCertificateResult>* certificates) const;
/**
Adds the constraint that each column of s_inner_pts is in the polytope {s |
C*s<=d}.
*/
void AddCspacePolytopeContainment(solvers::MathematicalProgram* prog,
const MatrixX<symbolic::Variable>& C,
const VectorX<symbolic::Variable>& d,
const Eigen::MatrixXd& s_inner_pts) const;
/**
The cost used when fixing the Lagrangian multiplier and search for C and d in
the C-space polytope {s | C*s <=d, s_lower<=s<=s_upper}. We denote δᵢ as the
margin between the i'th face C.row(i)<=d(i) to the inscribed ellipsoid.
*/
enum EllipsoidMarginCost {
/** Maximize ∑ᵢδᵢ */
kSum,
/** Maximize the geometric mean power(∏ᵢ (δᵢ + ε), 1/n) where n is C.rows(),
ε=FindPolytopeGivenLagrangianOptions.ellipsoid_margin_epsilon. */
kGeometricMean,
};
/**
Options for finding polytope with given Lagrangians.
*/
struct FindPolytopeGivenLagrangianOptions {
std::optional<double> backoff_scale{std::nullopt};
/** We will maximize the cost ∏ᵢ (δᵢ + ε) where δᵢ is the margin from each
face of the polytope {s | Cs<=d} to the inscribed ellipsoid, ε is
ellipsoid_margin_epsilon, a small positive constant to make sure δᵢ + ε
being strictly positive.
*/
double ellipsoid_margin_epsilon{1E-5};
/** ID for the solver */
solvers::SolverId solver_id{solvers::MosekSolver::id()};
/** options for solving the MathematicalProgram */
std::optional<solvers::SolverOptions> solver_options{std::nullopt};
/** We can constrain the C-space polytope {s | C*s<=d, s_lower<=s<=s_upper}
to contain some sampled s. Each column of s_inner_pts is a sample of s.
*/
std::optional<Eigen::MatrixXd> s_inner_pts;
/** If set to true, then we will also search for the Lagrangian multipliers
for the constraint s_lower <= s <= s_upper; otherwise we fix the
Lagrangian multiplier to the solution found when we fix the C-space
polytope {s | C*s<=d, s_lower<=s<=s_upper}.
*/
bool search_s_bounds_lagrangians{true};
/** Type of cost on the ellipsoid margin */
EllipsoidMarginCost ellipsoid_margin_cost{
EllipsoidMarginCost::kGeometricMean};
};
/** Result on searching the C-space polytope and separating planes. */
class SearchResult {
public:
SearchResult() {}
[[nodiscard]] const Eigen::MatrixXd& C() const { return C_; }
[[nodiscard]] const Eigen::VectorXd& d() const { return d_; }
[[nodiscard]] const HPolyhedron& certified_polytope() const {
return certified_polytope_;
}
/** Each plane index is mapped to a vector of polynomials. */
[[nodiscard]] const std::unordered_map<int, Vector3<symbolic::Polynomial>>&
a() const {
return a_;
}
/** Each plane index is mapped to a polynomial, */
[[nodiscard]] const std::unordered_map<int, symbolic::Polynomial>& b()
const {
return b_;
}
/** The number of iterations taken to search for the result. */
[[nodiscard]] int num_iter() const { return num_iter_; }
private:
friend class CspaceFreePolytope;
void SetPolytope(const Eigen::Ref<const Eigen::MatrixXd>& C,
const Eigen::Ref<const Eigen::VectorXd>& d,
const CspaceFreePolytope& cspace_free_polytope);
void SetSeparatingPlanes(
std::unordered_map<int, Vector3<symbolic::Polynomial>> a,
std::unordered_map<int, symbolic::Polynomial> b);
// Clear this->a and this->b and reset their values.
// Each entry in certificates_result should have a value (cannot be
// nullopt).
void SetSeparatingPlanes(
const std::vector<std::optional<SeparationCertificateResult>>&
certificates_result);
// Update this->a and this->b with the values in certificates_result.
void UpdateSeparatingPlanes(
const std::vector<std::optional<SeparationCertificateResult>>&
certificates_results);
Eigen::MatrixXd C_;
Eigen::VectorXd d_;
// This is the certified C-space polytope {s | C * s <= d, s_lower <= s <=
// s_upper}.
HPolyhedron certified_polytope_;
// a[i].dot(x) + b[i]=0 is the separation plane for separating_planes()[i].
std::unordered_map<int, Vector3<symbolic::Polynomial>> a_;
std::unordered_map<int, symbolic::Polynomial> b_;
// The number of iterations at termination.
int num_iter_{};
};
/** Options for bilinear alternation. */
struct BilinearAlternationOptions {
/** The maximum number of bilinear alternation iterations. Must be
non-negative.
*/
int max_iter{10};
/** When the change of the cost function between two consecutive iterations
in bilinear alternation is no larger than this number, stop the bilinear
alternation. Must be non-negative.
*/
double convergence_tol{1E-3};
FindPolytopeGivenLagrangianOptions find_polytope_options;
FindSeparationCertificateGivenPolytopeOptions find_lagrangian_options;
/** After finding the maximal inscribed ellipsoid in C-space polytope {s |
C*s<=d, s_lower<=s<=s_upper}, we scale this ellipsoid by
ellipsoid_scaling, and require the new C-space polytope to contain this
scaled ellipsoid. ellipsoid_scaling=1 corresponds to no scaling.
Must be strictly positive and no greater than 1.
*/
double ellipsoid_scaling{0.99};
};
/** Search for a collision-free C-space polytope.
{s | C*s<=d, s_lower<=s<=s_upper} through bilinear alternation.
The goal is to maximize the volume the C-space polytope. Since we can't
compute the polytope volume in the closed form, we use the volume of the
maximal inscribed ellipsoid as a surrogate function of the polytope volume.
@param ignored_collision_pairs The pairs of geometries that we ignore when
searching for separation certificates.
@param C_init The initial value of C.
@param d_init The initial value of d.
@param options The options for the bilinear alternation.
@retval results Stores the certification result in each iteration of the
bilinear alternation.
*/
[[nodiscard]] std::vector<SearchResult> SearchWithBilinearAlternation(
const IgnoredCollisionPairs& ignored_collision_pairs,
const Eigen::Ref<const Eigen::MatrixXd>& C_init,
const Eigen::Ref<const Eigen::VectorXd>& d_init,
const BilinearAlternationOptions& options) const;
/** Options for binary search. */
struct BinarySearchOptions {
/** The maximal value of the scaling factor.
Must be finite and no less than scale_min. */
double scale_max{1};
/** The minimal value of the scaling factor.
Must be non-negative. */
double scale_min{0.01};
/** The maximal number of iterations in binary search.
Must be non-negative. */
int max_iter{10};
/** When the gap between the upper bound and the lower bound of the scaling
factor is below this `convergence_tol`, stops the binary search.
Must be strictly positive.
*/
double convergence_tol{1E-3};
FindSeparationCertificateGivenPolytopeOptions find_lagrangian_options;
};
/** Binary search on d such that the C-space polytope {s | C*s<=d,
s_lower<=s<=s_upper} is collision free.
We scale the polytope {s | C*s<=d_init} about its center `s_center` and
search the scaling factor.
@pre s_center is in the polytope {s | C*s<=d_init, s_lower<=s<=s_upper}
*/
[[nodiscard]] std::optional<SearchResult> BinarySearch(
const IgnoredCollisionPairs& ignored_collision_pairs,
const Eigen::Ref<const Eigen::MatrixXd>& C,
const Eigen::Ref<const Eigen::VectorXd>& d_init,
const Eigen::Ref<const Eigen::VectorXd>& s_center,
const BinarySearchOptions& options) const;
/**
Constructs a program to search for the C-space polytope {s | C*s<=d,
s_lower<=s<=s_upper} such that this polytope is collision free.
This program treats C and d as decision variables, and searches for the
separating planes between each pair of geometries.
Note that this program doesn't contain any cost yet.
@param certificates The return of
FindSeparationCertificateGivenPolytope().
@param search_s_bounds_lagrangians Set to true if we search for the
Lagrangian multiplier for the bounds s_lower <=s<=s_upper.
@param[out] C The C-space polytope is parameterized as {s | C*s<=d,
s_lower<=s<=s_upper}.
@param[out] d The C-space polytope is parameterized as {s | C*s<=d,
s_lower<=s<=s_upper}.
@param[out] new_certificates The new certificates to certify the new C-space
polytope {s | C*s<=d, s_lower<=s<=s_upper} is collision free. If
new_certificates=nullptr, then we don't update it. This is used for testing.
*/
[[nodiscard]] std::unique_ptr<solvers::MathematicalProgram>
InitializePolytopeSearchProgram(
const IgnoredCollisionPairs& ignored_collision_pairs,
const std::unordered_map<SortedPair<geometry::GeometryId>,
SeparationCertificateResult>& certificates,
bool search_s_bounds_lagrangians, MatrixX<symbolic::Variable>* C,
VectorX<symbolic::Variable>* d,
std::unordered_map<int, SeparationCertificate>* new_certificates =
nullptr) const;
/**
Constructs the MathematicalProgram which searches for a separation
certificate for a pair of geometries for a C-space polytope. Search for the
separation certificate for a pair of geometries for a C-space polytope
{s | C*s<=d, s_lower<=s<=s_upper}.
@throws an error if this `geometry_pair` doesn't need separation certificate
(for example, they are on the same body).
*/
[[nodiscard]] SeparationCertificateProgram MakeIsGeometrySeparableProgram(
const SortedPair<geometry::GeometryId>& geometry_pair,
const Eigen::Ref<const Eigen::MatrixXd>& C,
const Eigen::Ref<const Eigen::VectorXd>& d) const;
/**
Solves a SeparationCertificateProgram with the given options
@return result If we find the separation certificate, then `result` contains
the separation plane and the Lagrangian polynomials; otherwise result is
empty.
*/
[[nodiscard]] std::optional<SeparationCertificateResult>
SolveSeparationCertificateProgram(
const SeparationCertificateProgram& certificate_program,
const FindSeparationCertificateGivenPolytopeOptions& options) const;
private:
// Forward declaration the tester class. This tester class will expose the
// private members of CspaceFreePolytope for unit test.
friend class CspaceFreePolytopeTester;
[[nodiscard]] std::vector<PlaneSeparatesGeometries>&
get_mutable_plane_geometries() {
return plane_geometries_;
}
// Find the redundant inequalities in C*s <= d, s_lower <= s <= s_upper
void FindRedundantInequalities(
const Eigen::MatrixXd& C, const Eigen::VectorXd& d,
const Eigen::VectorXd& s_lower, const Eigen::VectorXd& s_upper,
double tighten, std::unordered_set<int>* C_redundant_indices,
std::unordered_set<int>* s_lower_redundant_indices,
std::unordered_set<int>* s_upper_redundant_indices) const;
// Computes d - C*s as a vector of polynomials on indeterminate s.
template <typename T>
[[nodiscard]] VectorX<symbolic::Polynomial> CalcDminusCs(
const Eigen::Ref<const MatrixX<T>>& C,
const Eigen::Ref<const VectorX<T>>& d) const;
/*
Constructs the program which searches for the plane separating a pair of
geometries, for all configuration in the set {s | C * s <= d, s_lower <= s
<= s_upper}.
@param[in] plane_geometries Contain the conditions that need to be
non-negative on the region C * s <= d and s_lower <= s <= s_upper.
@param[in] d_minus_Cs d - C*s.
@param[in] s_minus_s_lower s - s_lower.
@param[in] s_upper_minus_s s_upper - s.
@param[in] C_redundant_indices In the polyhedron C*s <= d, s_lower <= s <=
s_upper, some rows of C*s<=d might be redundant. We store the indices of the
redundant rows in C_redundant_indices.
@param[in] s_lower_redundant_indices. Store the indices of the redundant rows
in s >= s_lower.
@param[in] s_upper_redundant_indices. Store the indices of the redundant rows
in s <= s_upper.
*/
[[nodiscard]] SeparationCertificateProgram ConstructPlaneSearchProgram(
const PlaneSeparatesGeometries& plane_geometries,
const VectorX<symbolic::Polynomial>& d_minus_Cs,
const std::unordered_set<int>& C_redundant_indices,
const std::unordered_set<int>& s_lower_redundant_indices,
const std::unordered_set<int>& s_upper_redundant_indices) const;
/*
For each pair of geometries, find the certificate that the pair is collision
free in the C-space region {s | C*s<=d, s_lower<=s<=s_upper}.
@retval certificates certificates[i] is the separation certificate for
a pair of geometries. If we cannot certify or haven't certified
the separation for this pair, then certificates[i] contains std::nullopt.
Note that when we run this function in parallel and
options.terminate_at_failure=true, we will terminate all the remaining
certification programs that have been launched, so certificates[i] =
std::nullopt could be either because that we have attempted to find the
certificate for this pair of geometry but failed, or it could be that we fail
to find the certificate for another pair and haven't attempted to find the
certificate for this pair.
*/
[[nodiscard]] std::vector<std::optional<SeparationCertificateResult>>
FindSeparationCertificateGivenPolytope(
const IgnoredCollisionPairs& ignored_collision_pairs,
const Eigen::Ref<const Eigen::MatrixXd>& C,
const Eigen::Ref<const Eigen::VectorXd>& d,
const FindSeparationCertificateGivenPolytopeOptions& options) const;
/* When we fix the Lagrangian multipliers and search for the C-space polytope
{s | C*s<=d, s_lower<=s<=s_upper}, we count the total size of all Gram
matrices in the SOS program.
*/
[[nodiscard]] int GetGramVarSizeForPolytopeSearchProgram(
const IgnoredCollisionPairs& ignored_collision_pairs,
bool search_s_bounds_lagrangians) const;
/*
Overloads InitializePolytopeSearchProgram, but with C, d, d_minus_Cs,
certificates_vec as the input arguments. This function will be called
repeatedly (for example in bilinear alternation) with the same arguments C,
d, d_minus_Cs; hence it is better to construct these arguments beforehand and
call this overloaded function.
@param certificates_vec The return of
FindSeparationCertificateGivenPolytope().
@param search_s_bounds_lagrangians Set to true if we search for the
Lagrangian multiplier for the bounds s_lower <=s<=s_upper.
@param gram_total_size The return of
GetGramVarSizeForPolytopeSearchProgram().
@param[out] new_certificates The new certificates to certify the new C-space
polytope {s | C*s<=d, s_lower<=s<=s_upper} is collision free. If
new_certificates=nullptr, then we don't update it. This is used for testing.
*/
[[nodiscard]] std::unique_ptr<solvers::MathematicalProgram>
InitializePolytopeSearchProgram(
const IgnoredCollisionPairs& ignored_collision_pairs,
const MatrixX<symbolic::Variable>& C,
const VectorX<symbolic::Variable>& d,
const VectorX<symbolic::Polynomial>& d_minus_Cs,
const std::vector<std::optional<SeparationCertificateResult>>&
certificates_vec,
bool search_s_bounds_lagrangians, int gram_total_size,
std::unordered_map<int, SeparationCertificate>* new_certificates =
nullptr) const;
/* Adds the constraint that the ellipsoid {Q*u+s₀ | uᵀu≤1} is inside the
polytope {s | C*s <= d} with margin δ. Namely for the i'th face cᵢᵀs≤dᵢ, we
have |cᵢᵀQ|₂ ≤ dᵢ − cᵢᵀs₀ − δᵢ and |cᵢ|₂≤1
cᵢᵀ is the i'th row of C, dᵢ is the i'th entry of d, δᵢ is the i'th entry
of ellipsoid_margins.
Note that this function does NOT add the constraint δ>=0
*/
void AddEllipsoidContainmentConstraint(
solvers::MathematicalProgram* prog, const Eigen::MatrixXd& Q,
const Eigen::VectorXd& s0, const MatrixX<symbolic::Variable>& C,
const VectorX<symbolic::Variable>& d,
const VectorX<symbolic::Variable>& ellipsoid_margins) const;
struct FindPolytopeGivenLagrangianResult {
Eigen::MatrixXd C;
Eigen::MatrixXd d;
// a[i].dot(x) + b[i] = 0 is the separation plane for
// this->separating_planes()[i].
std::unordered_map<int, Vector3<symbolic::Polynomial>> a;
std::unordered_map<int, symbolic::Polynomial> b;
Eigen::VectorXd ellipsoid_margins;
};
/*
@param[out] certificates_result If certificates_result=nullptr, then we don't
update its value; otherwise we set it to map the plane index to the
separation certificates result for the plane.
*/
[[nodiscard]] std::optional<FindPolytopeGivenLagrangianResult>
FindPolytopeGivenLagrangian(
const IgnoredCollisionPairs& ignored_collision_pairs,
const MatrixX<symbolic::Variable>& C,
const VectorX<symbolic::Variable>& d,
const VectorX<symbolic::Polynomial>& d_minus_Cs,
const std::vector<std::optional<SeparationCertificateResult>>&
certificates_vec,
const Eigen::MatrixXd& Q, const Eigen::VectorXd& s0,
const VectorX<symbolic::Variable>& ellipsoid_margins, int gram_total_size,
const FindPolytopeGivenLagrangianOptions& options,
std::unordered_map<int, SeparationCertificateResult>* certificates_result)
const;
/* Gets the H-polyhedron {s | C*s<=d, s_lower<=s<=s_upper}. */
HPolyhedron GetPolyhedronWithJointLimits(const Eigen::MatrixXd& C,
const Eigen::VectorXd& d) const;
Eigen::VectorXd q_star_;
Eigen::VectorXd s_lower_;
Eigen::VectorXd s_upper_;
VectorX<symbolic::Polynomial> s_minus_s_lower_;
VectorX<symbolic::Polynomial> s_upper_minus_s_;
// We have the invariant plane_geometries_[i].plane_index == i.
std::vector<PlaneSeparatesGeometries> plane_geometries_;
};
} // namespace optimization
} // namespace geometry
} // namespace drake