Skip to content

Commit 24d08ee

Browse files
committed
fixes after rebase
1 parent 66ebf2f commit 24d08ee

12 files changed

Lines changed: 123 additions & 64 deletions

File tree

.bazelrc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,3 @@ build --action_env=LD_LIBRARY_PATH=
5252
build --python_path=python3
5353

5454
build --define=WITH_GUROBI=ON
55-
56-
build --local_resources=cpu=8

core/BUILD.bazel

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ filegroup(
2929
],
3030
)
3131

32+
cc_library(
33+
name = "logging",
34+
hdrs = ["common/logging_utils.hpp"],
35+
)
36+
3237
cc_library(
3338
name = "c3",
3439
srcs = [
@@ -49,11 +54,15 @@ cc_library(
4954
"c3_qp.h",
5055
"c3_plus.h",
5156
],
57+
data = [
58+
":default_solver_options",
59+
],
5260
copts = ["-fopenmp"],
5361
linkopts = ["-fopenmp"],
5462
deps = [
5563
":lcs",
5664
":options",
65+
":logging",
5766
"@drake//:drake_shared_library",
5867
] + select({
5968
"//tools:with_gurobi": ["@gurobi//:gurobi_cxx"],

core/c3.cc

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <drake/common/find_runfiles.h>
88
#include <omp.h>
99

10+
#include "common/logging_utils.hpp"
1011
#include "lcs.h"
1112
#include "solver_options_io.h"
1213

@@ -89,7 +90,6 @@ C3::C3(const LCS& lcs, const CostMatrices& costs,
8990
w_sol_ = std::make_unique<std::vector<VectorXd>>();
9091
delta_sol_ = std::make_unique<std::vector<VectorXd>>();
9192
for (int i = 0; i < N_; ++i) {
92-
z_sol_->push_back(Eigen::VectorXd::Zero(n_z_));
9393
x_sol_->push_back(Eigen::VectorXd::Zero(n_x_));
9494
lambda_sol_->push_back(Eigen::VectorXd::Zero(n_lambda_));
9595
u_sol_->push_back(Eigen::VectorXd::Zero(n_u_));
@@ -267,6 +267,7 @@ const std::vector<drake::solvers::QuadraticCost*>& C3::GetTargetCost() {
267267
}
268268

269269
void C3::Solve(const VectorXd& x0) {
270+
drake::log()->debug("C3::Solve called");
270271
auto start = std::chrono::high_resolution_clock::now();
271272
// Set the initial state constraint
272273
if (initial_state_constraint_) {
@@ -317,6 +318,8 @@ void C3::Solve(const VectorXd& x0) {
317318
std::vector<VectorXd> w(N_, VectorXd::Zero(n_z_));
318319
vector<MatrixXd> G = cost_matrices_.G;
319320

321+
drake::log()->debug("C3::Solve starting ADMM iterations.");
322+
320323
for (int iter = 0; iter < options_.admm_iter; iter++) {
321324
ADMMStep(x0, &delta, &w, &G, iter);
322325
}
@@ -332,6 +335,7 @@ void C3::Solve(const VectorXd& x0) {
332335
*delta_sol_ = delta;
333336

334337
if (!options_.end_on_qp_step) {
338+
drake::log()->debug("C3::Solve compute a half step.");
335339
*z_sol_ = delta;
336340
z_sol_->at(0).segment(0, n_x_) = x0;
337341
x_sol_->at(0) = x0;
@@ -355,6 +359,7 @@ void C3::Solve(const VectorXd& x0) {
355359
solve_time_ =
356360
std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count() /
357361
1e6;
362+
drake::log()->debug("C3::Solve completed in {} seconds.", solve_time_);
358363
}
359364

360365
void C3::ADMMStep(const VectorXd& x0, vector<VectorXd>* delta,
@@ -373,6 +378,7 @@ void C3::ADMMStep(const VectorXd& x0, vector<VectorXd>* delta,
373378
ZW[i] = w->at(i) + z[i];
374379
}
375380

381+
drake::log()->debug("C3::ADMMStep SolveProjection step.");
376382
if (cost_matrices_.U[0].isZero(0)) {
377383
*delta = SolveProjection(*G, ZW, admm_iteration);
378384
} else {
@@ -417,10 +423,17 @@ void C3::StoreQPResults(const MathematicalProgramResult& result,
417423
z_sol_->at(i).segment(0, n_x_) = result.GetSolution(x_[i]);
418424
z_sol_->at(i).segment(n_x_, n_lambda_) = result.GetSolution(lambda_[i]);
419425
z_sol_->at(i).segment(n_x_ + n_lambda_, n_u_) = result.GetSolution(u_[i]);
426+
427+
drake::log()->trace(
428+
"C3::StoreQPResults storing solution for time step {}: "
429+
"lambda = {}",
430+
i, EigenToString(lambda_sol_->at(i).transpose()));
420431
}
421432

422433
if (!warm_start_)
423434
return; // No warm start, so no need to update warm start parameters
435+
436+
drake::log()->trace("C3::StoreQPResults storing warm start values.");
424437
for (int i = 0; i < N_ + 1; ++i) {
425438
if (i < N_) {
426439
warm_start_x_[admm_iteration][i] = result.GetSolution(x_[i]);
@@ -438,16 +451,17 @@ vector<VectorXd> C3::SolveQP(const VectorXd& x0, const vector<MatrixXd>& G,
438451
AddAugmentedCost(G, WD, delta, is_final_solve);
439452
SetInitialGuessQP(x0, admm_iteration);
440453

454+
drake::log()->trace("C3::SolveQP calling solver.");
441455
try {
442456
MathematicalProgramResult result = osqp_.Solve(prog_);
457+
if (!result.is_success()) {
458+
drake::log()->warn("C3::SolveQP failed to solve the QP with status: {}",
459+
result.get_solution_result());
460+
}
461+
StoreQPResults(result, admm_iteration, is_final_solve);
443462
} catch (const std::exception& e) {
444463
drake::log()->error("C3::SolveQP failed with exception: {}", e.what());
445464
}
446-
if (!result.is_success()) {
447-
drake::log()->warn("C3::SolveQP failed to solve the QP with status: {}",
448-
result.get_solution_result());
449-
}
450-
StoreQPResults(result, admm_iteration, is_final_solve);
451465

452466
return *z_sol_;
453467
}

core/c3_options.h

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct C3Options {
7676
std::vector<double> g_lambda_t;
7777
std::vector<double> g_lambda;
7878
std::vector<double> g_u;
79+
std::vector<double> g_eta_vector;
7980
std::optional<std::vector<double>> g_eta_slack;
8081
std::optional<std::vector<double>> g_eta_n;
8182
std::optional<std::vector<double>> g_eta_t;
@@ -88,6 +89,7 @@ struct C3Options {
8889
std::vector<double> u_lambda_t;
8990
std::vector<double> u_lambda;
9091
std::vector<double> u_u;
92+
std::vector<double> u_eta_vector;
9193
std::optional<std::vector<double>> u_eta_slack;
9294
std::optional<std::vector<double>> u_eta_n;
9395
std::optional<std::vector<double>> u_eta_t;
@@ -150,16 +152,14 @@ struct C3Options {
150152
}
151153
g_vector.insert(g_vector.end(), g_lambda.begin(), g_lambda.end());
152154
g_vector.insert(g_vector.end(), g_u.begin(), g_u.end());
153-
if (g_eta != std::nullopt || g_eta_slack != std::nullopt) {
154-
if (g_eta == std::nullopt || g_eta->empty()) {
155-
g_vector.insert(g_vector.end(), g_eta_slack->begin(),
156-
g_eta_slack->end());
157-
g_vector.insert(g_vector.end(), g_eta_n->begin(), g_eta_n->end());
158-
g_vector.insert(g_vector.end(), g_eta_t->begin(), g_eta_t->end());
159-
} else {
160-
g_vector.insert(g_vector.end(), g_eta->begin(), g_eta->end());
161-
}
155+
g_eta_vector = g_eta.value_or(std::vector<double>());
156+
if (g_eta_vector.empty() && g_eta_slack.has_value()) {
157+
g_eta_vector.insert(g_eta_vector.end(), g_eta_slack->begin(),
158+
g_eta_slack->end());
159+
g_eta_vector.insert(g_eta_vector.end(), g_eta_n->begin(), g_eta_n->end());
160+
g_eta_vector.insert(g_eta_vector.end(), g_eta_t->begin(), g_eta_t->end());
162161
}
162+
g_vector.insert(g_vector.end(), g_eta_vector.begin(), g_eta_vector.end());
163163

164164
u_vector = std::vector<double>();
165165
u_vector.insert(u_vector.end(), u_x.begin(), u_x.end());
@@ -170,16 +170,14 @@ struct C3Options {
170170
}
171171
u_vector.insert(u_vector.end(), u_lambda.begin(), u_lambda.end());
172172
u_vector.insert(u_vector.end(), u_u.begin(), u_u.end());
173-
if (u_eta != std::nullopt || u_eta_slack != std::nullopt) {
174-
if (u_eta == std::nullopt || u_eta->empty()) {
175-
u_vector.insert(u_vector.end(), u_eta_slack->begin(),
176-
u_eta_slack->end());
177-
u_vector.insert(u_vector.end(), u_eta_n->begin(), u_eta_n->end());
178-
u_vector.insert(u_vector.end(), u_eta_t->begin(), u_eta_t->end());
179-
} else {
180-
u_vector.insert(u_vector.end(), u_eta->begin(), u_eta->end());
181-
}
173+
u_eta_vector = u_eta.value_or(std::vector<double>());
174+
if (u_eta_vector.empty() && u_eta_slack.has_value()) {
175+
u_eta_vector.insert(u_eta_vector.end(), u_eta_slack->begin(),
176+
u_eta_slack->end());
177+
u_eta_vector.insert(u_eta_vector.end(), u_eta_n->begin(), u_eta_n->end());
178+
u_eta_vector.insert(u_eta_vector.end(), u_eta_t->begin(), u_eta_t->end());
182179
}
180+
u_vector.insert(u_vector.end(), u_eta_vector.begin(), u_eta_vector.end());
183181

184182
Eigen::VectorXd q = Eigen::Map<Eigen::VectorXd, Eigen::Unaligned>(
185183
this->q_vector.data(), this->q_vector.size());
@@ -190,8 +188,6 @@ struct C3Options {
190188
Eigen::VectorXd u = Eigen::Map<Eigen::VectorXd, Eigen::Unaligned>(
191189
this->u_vector.data(), this->u_vector.size());
192190

193-
DRAKE_DEMAND(g.size() == u.size());
194-
195191
Q = w_Q * q.asDiagonal();
196192
R = w_R * r.asDiagonal();
197193
G = w_G * g.asDiagonal();

core/c3_plus.cc

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
#include <Eigen/Dense>
66

77
#include "c3_options.h"
8+
#include "common/logging_utils.hpp"
89
#include "lcs.h"
910

11+
#include "drake/common/text_logging.h"
12+
1013
namespace c3 {
1114

1215
using Eigen::MatrixXd;
@@ -19,6 +22,16 @@ C3Plus::C3Plus(const LCS& lcs, const CostMatrices& costs,
1922
const vector<VectorXd>& xdesired, const C3Options& options)
2023
: C3(lcs, costs, xdesired, options,
2124
lcs.num_states() + 2 * lcs.num_lambdas() + lcs.num_inputs()) {
25+
if (warm_start_) {
26+
warm_start_eta_.resize(options_.admm_iter + 1);
27+
for (int iter = 0; iter < options_.admm_iter + 1; ++iter) {
28+
warm_start_eta_[iter].resize(N_);
29+
for (int i = 0; i < N_; ++i) {
30+
warm_start_eta_[iter][i] = VectorXd::Zero(n_lambda_);
31+
}
32+
}
33+
}
34+
2235
// Initialize eta as optimization variables
2336
eta_ = vector<drake::solvers::VectorXDecisionVariable>();
2437
eta_sol_ = std::make_unique<std::vector<VectorXd>>();
@@ -40,10 +53,7 @@ C3Plus::C3Plus(const LCS& lcs, const CostMatrices& costs,
4053
EtaLinEq.block(0, n_x_ + n_lambda_, n_lambda_, n_u_) = lcs_.H().at(i);
4154

4255
eta_constraints_[i] =
43-
prog_
44-
.AddLinearEqualityConstraint(
45-
EtaLinEq, -lcs_.c().at(i),
46-
{x_.at(i), lambda_.at(i), u_.at(i), eta_.at(i)})
56+
prog_.AddLinearEqualityConstraint(EtaLinEq, -lcs_.c().at(i), z_.at(i))
4757
.evaluator()
4858
.get();
4959
}
@@ -81,16 +91,23 @@ void C3Plus::SetInitialGuessQP(const Eigen::VectorXd& x0, int admm_iteration) {
8191
void C3Plus::StoreQPResults(const MathematicalProgramResult& result,
8292
int admm_iteration, bool is_final_solve) {
8393
C3::StoreQPResults(result, admm_iteration, is_final_solve);
94+
drake::log()->trace("C3Plus::StoreQPResults storing eta results.");
8495
for (int i = 0; i < N_; i++) {
8596
if (is_final_solve) {
8697
eta_sol_->at(i) = result.GetSolution(eta_[i]);
8798
}
8899
z_sol_->at(i).segment(n_x_ + n_lambda_ + n_u_, n_lambda_) =
89100
result.GetSolution(eta_[i]);
101+
drake::log()->trace(
102+
"C3Plus::StoreQPResults storing solution for time step {}: "
103+
"eta = {}",
104+
i, EigenToString(eta_sol_->at(i).transpose()));
90105
}
91106

92107
if (!warm_start_)
93108
return; // No warm start, so no need to update warm start parameters
109+
110+
drake::log()->trace("C3Plus::StoreQPResults storing warm start eta.");
94111
for (int i = 0; i < N_; ++i) {
95112
warm_start_eta_[admm_iteration][i] = result.GetSolution(eta_[i]);
96113
}
@@ -156,6 +173,12 @@ VectorXd C3Plus::SolveSingleProjection(const MatrixXd& U,
156173
VectorXd lambda_c = delta_c.segment(n_x_, n_lambda_);
157174
VectorXd eta_c = delta_c.segment(n_x_ + n_lambda_ + n_u_, n_lambda_);
158175

176+
drake::log()->trace(
177+
"C3Plus::SolveSingleProjection ADMM iteration {}: pre-projection "
178+
"lambda = {}, eta = {}",
179+
admm_iteration, EigenToString(lambda_c.transpose()),
180+
EigenToString(eta_c.transpose()));
181+
159182
// Set the smaller of lambda and eta to zero
160183
Eigen::Array<bool, Eigen::Dynamic, 1> eta_larger =
161184
eta_c.array() * w_eta_vec.array().sqrt() >

core/common/logging_utils.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <sstream>
2+
#include <string>
3+
4+
#include <Eigen/Dense>
5+
6+
/**
7+
* Converts an Eigen matrix to a string representation.
8+
* This function is useful for logging or debugging purposes.
9+
*
10+
* @tparam Derived The type of the Eigen matrix.
11+
* @param mat The Eigen matrix to convert.
12+
* @return A string representation of the matrix.
13+
*/
14+
template <typename Derived>
15+
std::string EigenToString(const Eigen::MatrixBase<Derived>& mat) {
16+
std::stringstream ss;
17+
ss << mat;
18+
return ss.str();
19+
}

multibody/geom_geom_collider.cc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,11 @@ GeomGeomCollider<T>::CalcForceBasisInWorldFrame(
280280
const auto query_result = GetGeometryQueryResult(context);
281281
if (num_friction_directions < 1) {
282282
// Planar contact: basis is constructed from the contact and planar normals.
283-
return ComputePlanarForceBasis(-query_result.signed_distance_pair.nhat_BA_W,
284-
planar_normal);
283+
return ComputePlanarForceBasis(-query_result.nhat_BA_W, planar_normal);
285284
} else {
286285
// 3D contact: build polytope basis and rotate using contact normal.
287286
auto R_WC = drake::math::RotationMatrix<T>::MakeFromOneVector(
288-
-query_result.signed_distance_pair.nhat_BA_W, 0);
287+
-query_result.nhat_BA_W, 0);
289288
return ComputePolytopeForceBasis(num_friction_directions) *
290289
R_WC.matrix().transpose();
291290
}

multibody/lcs_factory.cc

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,8 @@ std::vector<LCSContactDescription> LCSFactory::GetContactDescriptions() {
416416
for (int i = 0; i < n_contacts_; i++) {
417417
multibody::GeomGeomCollider collider(plant_, contact_pairs_[i]);
418418
auto [p_WCa, p_WCb] = collider.CalcWitnessPoints(context_);
419-
auto force_basis =
420-
collider.CalcForceBasisInWorldFrame(context_, n_friction_directions_);
419+
auto force_basis = collider.CalcForceBasisInWorldFrame(
420+
context_, n_friction_directions_per_contact_[i]);
421421

422422
for (int j = 0; j < force_basis.rows(); j++) {
423423
LCSContactDescription contact_description = {
@@ -452,16 +452,19 @@ std::vector<LCSContactDescription> LCSFactory::GetContactDescriptions() {
452452
contact_descriptions.insert(contact_descriptions.end(),
453453
tangential_contact_descriptions.begin(),
454454
tangential_contact_descriptions.end());
455-
DRAKE_ASSERT(n_friction_directions_ > 0);
456-
for (int i = 0; i < tangential_contact_descriptions.size(); i++) {
457-
int normal_index = i / (2 * n_friction_directions_);
458-
DRAKE_ASSERT(
459-
contact_descriptions.at(i).witness_point_A ==
460-
normal_contact_descriptions.at(normal_index).witness_point_A);
461-
contact_descriptions.at(i).force_basis =
462-
contact_descriptions.at(i).force_basis +
463-
mu_[normal_index] *
464-
normal_contact_descriptions.at(normal_index).force_basis;
455+
for (int normal_index = 0; normal_index < n_contacts_; normal_index++) {
456+
// Jt_row_sizes_ gives number of friction directions per contact
457+
for (int i = 0; i < Jt_row_sizes_(normal_index); i++) {
458+
int tangential_index = Jt_row_sizes_.segment(0, normal_index).sum() + i;
459+
DRAKE_ASSERT(
460+
contact_descriptions.at(tangential_index).witness_point_A ==
461+
normal_contact_descriptions.at(normal_index).witness_point_A);
462+
contact_descriptions.at(tangential_index).force_basis =
463+
mu_[normal_index] *
464+
contact_descriptions.at(tangential_index).force_basis +
465+
normal_contact_descriptions.at(normal_index).force_basis;
466+
contact_descriptions.at(tangential_index).force_basis.normalize();
467+
}
465468
}
466469
}
467470

0 commit comments

Comments
 (0)