Skip to content

Commit 7cce62b

Browse files
committed
Adds SapFixedTendonConstraint
1 parent 8d39970 commit 7cce62b

File tree

3 files changed

+619
-0
lines changed

3 files changed

+619
-0
lines changed

multibody/contact_solvers/sap/BUILD.bazel

+13
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ drake_cc_package_library(
2525
":sap_coupler_constraint",
2626
":sap_distance_constraint",
2727
":sap_fixed_constraint",
28+
":sap_fixed_tendon_constraint",
2829
":sap_friction_cone_constraint",
2930
":sap_holonomic_constraint",
3031
":sap_hunt_crossley_constraint",
@@ -189,6 +190,18 @@ drake_cc_library(
189190
],
190191
)
191192

193+
drake_cc_library(
194+
name = "sap_fixed_tendon_constraint",
195+
srcs = ["sap_fixed_tendon_constraint.cc"],
196+
hdrs = ["sap_fixed_tendon_constraint.h"],
197+
deps = [
198+
":sap_constraint",
199+
":sap_constraint_jacobian",
200+
"//common:default_scalars",
201+
"//common:essential",
202+
],
203+
)
204+
192205
drake_cc_library(
193206
name = "sap_holonomic_constraint",
194207
srcs = ["sap_holonomic_constraint.cc"],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
#include "drake/multibody/contact_solvers/sap/sap_fixed_tendon_constraint.h"
2+
3+
#include <algorithm>
4+
#include <limits>
5+
#include <utility>
6+
7+
#include "drake/common/default_scalars.h"
8+
#include "drake/common/eigen_types.h"
9+
#include "drake/math/autodiff.h"
10+
11+
namespace drake {
12+
namespace multibody {
13+
namespace contact_solvers {
14+
namespace internal {
15+
16+
template <typename T>
17+
SapFixedTendonConstraint<T>::Parameters::Parameters(const T& lower_limit,
18+
const T& upper_limit,
19+
const T& stiffness,
20+
const T& damping,
21+
double beta)
22+
: lower_limit_(lower_limit),
23+
upper_limit_(upper_limit),
24+
stiffness_(stiffness),
25+
damping_(damping),
26+
beta_(beta) {
27+
constexpr double kInf = std::numeric_limits<double>::infinity();
28+
DRAKE_DEMAND(lower_limit < kInf);
29+
DRAKE_DEMAND(upper_limit > -kInf);
30+
DRAKE_DEMAND(lower_limit <= upper_limit);
31+
DRAKE_DEMAND(stiffness > 0);
32+
DRAKE_DEMAND(damping >= 0);
33+
}
34+
35+
template <typename T>
36+
SapFixedTendonConstraint<T>::Kinematics::Kinematics(
37+
int clique0, int clique1, int clique0_nv, int clique1_nv, VectorX<T> q0,
38+
VectorX<T> q1, VectorX<T> a0, VectorX<T> a1, T offset)
39+
: clique0_(clique0),
40+
clique1_(clique1),
41+
clique0_nv_(clique0_nv),
42+
clique1_nv_(clique1_nv),
43+
q0_(std::move(q0)),
44+
q1_(std::move(q1)),
45+
a0_(std::move(a0)),
46+
a1_(std::move(a1)),
47+
offset_(std::move(offset)) {
48+
DRAKE_DEMAND(clique0_ >= 0);
49+
DRAKE_DEMAND(clique1_ >= -1);
50+
DRAKE_DEMAND(clique0_ != clique1_);
51+
DRAKE_DEMAND(clique0_nv_ >= 0);
52+
DRAKE_DEMAND(q0_.size() == clique0_nv_);
53+
DRAKE_DEMAND(a0_.size() == clique0_nv_);
54+
if (clique1_ >= 0) {
55+
DRAKE_DEMAND(clique1_nv_ >= 0);
56+
DRAKE_DEMAND(q1_.size() == clique1_nv_);
57+
DRAKE_DEMAND(a1_.size() == clique1_nv_);
58+
}
59+
}
60+
61+
template <typename T>
62+
SapFixedTendonConstraint<T>::Kinematics::Kinematics(int clique0, int clique0_nv,
63+
VectorX<T> q0,
64+
VectorX<T> a0, T offset)
65+
: clique0_(clique0),
66+
clique0_nv_(clique0_nv),
67+
q0_(std::move(q0)),
68+
a0_(std::move(a0)),
69+
offset_(std::move(offset)) {
70+
DRAKE_DEMAND(clique0_ >= 0);
71+
DRAKE_DEMAND(clique0_ != clique1_);
72+
DRAKE_DEMAND(clique0_nv_ >= 0);
73+
DRAKE_DEMAND(q0_.size() == clique0_nv_);
74+
DRAKE_DEMAND(a0_.size() == clique0_nv_);
75+
}
76+
77+
template <typename T>
78+
SapFixedTendonConstraint<T>::SapFixedTendonConstraint(Parameters parameters,
79+
Kinematics kinematics)
80+
: SapConstraint<T>(CalcConstraintJacobian(parameters, kinematics), {}),
81+
g_(CalcConstraintFunction(parameters, kinematics)),
82+
parameters_(std::move(parameters)),
83+
kinematics_(std::move(kinematics)) {}
84+
85+
template <typename T>
86+
VectorX<T> SapFixedTendonConstraint<T>::CalcConstraintFunction(
87+
const Parameters& parameters, const Kinematics& kinematics) {
88+
constexpr double kInf = std::numeric_limits<double>::infinity();
89+
const T& ll = parameters.lower_limit();
90+
const T& ul = parameters.upper_limit();
91+
92+
const int nk = ll > -kInf && ul < kInf ? 2 : 1;
93+
VectorX<T> g0(nk);
94+
95+
int i = 0;
96+
if (ll > -kInf) {
97+
g0(i) = kinematics.a0().dot(kinematics.q0()) + kinematics.offset() - ll;
98+
if (kinematics.clique1() >= 0) {
99+
g0(i) += kinematics.a1().dot(kinematics.q1());
100+
}
101+
++i;
102+
}
103+
if (ul < kInf) {
104+
g0(i) = ul - kinematics.a0().dot(kinematics.q0()) - kinematics.offset();
105+
if (kinematics.clique1() >= 0) {
106+
g0(i) -= kinematics.a1().dot(kinematics.q1());
107+
}
108+
}
109+
110+
return g0;
111+
}
112+
113+
template <typename T>
114+
SapConstraintJacobian<T> SapFixedTendonConstraint<T>::CalcConstraintJacobian(
115+
const Parameters& parameters, const Kinematics& kinematics) {
116+
constexpr double kInf = std::numeric_limits<double>::infinity();
117+
const T& ll = parameters.lower_limit();
118+
const T& ul = parameters.upper_limit();
119+
120+
const int nk = ll > -kInf && ul < kInf ? 2 : 1;
121+
MatrixX<T> J0 = MatrixX<T>::Zero(nk, kinematics.clique0_nv());
122+
123+
int i = 0;
124+
if (ll > -kInf) J0.row(i++) += kinematics.a0();
125+
if (ul < kInf) J0.row(i) -= kinematics.a0();
126+
127+
if (kinematics.clique1() >= 0) {
128+
MatrixX<T> J1 = MatrixX<T>::Zero(nk, kinematics.clique1_nv());
129+
130+
i = 0;
131+
if (ll > -kInf) J1.row(i++) += kinematics.a1();
132+
if (ul < kInf) J1.row(i) -= kinematics.a1();
133+
134+
return SapConstraintJacobian<T>(kinematics.clique0(), std::move(J0),
135+
kinematics.clique1(), std::move(J1));
136+
} else {
137+
return SapConstraintJacobian<T>(kinematics.clique0(), std::move(J0));
138+
}
139+
}
140+
141+
template <typename T>
142+
std::unique_ptr<AbstractValue> SapFixedTendonConstraint<T>::DoMakeData(
143+
const T& dt,
144+
const Eigen::Ref<const VectorX<T>>& delassus_estimation) const {
145+
// Estimate regularization based on near-rigid regime threshold.
146+
// Rigid approximation constant: Rₙ = β²/(4π²)⋅wᵢ when the contact frequency
147+
// ωₙ is below the limit ωₙ⋅δt ≤ 2π. That is, the period is Tₙ = β⋅δt. See
148+
// [Castro et al., 2022] for details.
149+
const double beta_factor =
150+
parameters_.beta() * parameters_.beta() / (4.0 * M_PI * M_PI);
151+
152+
T k_eff = parameters_.stiffness();
153+
T taud_eff = parameters_.damping() / k_eff;
154+
155+
// "Effective regularization" [Castro et al., 2022] for this constraint.
156+
const T R_eff = 1.0 / (dt * k_eff * (dt + taud_eff));
157+
158+
// "Near-rigid" regularization, [Castro et al., 2021].
159+
VectorX<T> R = (beta_factor * delassus_estimation).cwiseMax(R_eff);
160+
161+
// Make data.
162+
SapFixedTendonConstraintData<T> data;
163+
typename SapFixedTendonConstraintData<T>::InvariantData& p =
164+
data.invariant_data;
165+
p.dt = dt;
166+
p.R_inv = R.cwiseInverse();
167+
168+
// If the effective relaxation R_eff is smaller than the near-rigid regime
169+
// relaxation R_near_rigid, that means that time_step will not be able to
170+
// resolve the dynamics introduced by this constraint. We call this the
171+
// "near-rigid" regime. Here we clamp taud to the time step, leading to a
172+
// critically damped constraint. Thus if this constraint is in the
173+
// "near-rigid" regime, v̂ = -g₀ / 2⋅δt.
174+
//
175+
// Refer to Section V of [Castro et al., 2022] for further details.
176+
p.v_hat = -g_;
177+
for (int i = 0; i < this->num_constraint_equations(); ++i) {
178+
if (R_eff < R(i)) {
179+
p.v_hat(i) /= 2 * dt;
180+
} else {
181+
p.v_hat(i) /= (dt + taud_eff);
182+
}
183+
}
184+
185+
return AbstractValue::Make(data);
186+
}
187+
188+
template <typename T>
189+
void SapFixedTendonConstraint<T>::DoCalcData(
190+
const Eigen::Ref<const VectorX<T>>& v, AbstractValue* abstract_data) const {
191+
auto& data =
192+
abstract_data->get_mutable_value<SapFixedTendonConstraintData<T>>();
193+
194+
const T& dt = data.invariant_data.dt;
195+
const VectorX<T>& v_hat = data.invariant_data.v_hat;
196+
const VectorX<T>& R_inv = data.invariant_data.R_inv;
197+
198+
// This constraint is formulated such that the cost, impulse, and hessian
199+
// are all zero when the constraint is not active.
200+
data.v_ = v;
201+
data.hessian_.setZero();
202+
data.gamma_.setZero();
203+
data.cost_ = T(0);
204+
205+
for (int i = 0; i < this->num_constraint_equations(); ++i) {
206+
const T v_tilde = std::min(v_hat(i), -g_(i) / dt);
207+
// Constraint is active when v < ṽ
208+
if (v(i) < v_tilde) {
209+
const T dv = v_hat(i) - v(i);
210+
const T dv_tilde = v_hat(i) - v_tilde;
211+
data.hessian_(i) = R_inv(i);
212+
data.gamma_(i) = R_inv(i) * dv;
213+
data.cost_ += 0.5 * R_inv(i) * (dv * dv - dv_tilde * dv_tilde);
214+
}
215+
}
216+
}
217+
218+
template <typename T>
219+
T SapFixedTendonConstraint<T>::DoCalcCost(
220+
const AbstractValue& abstract_data) const {
221+
const auto& data = abstract_data.get_value<SapFixedTendonConstraintData<T>>();
222+
return data.cost_;
223+
}
224+
225+
template <typename T>
226+
void SapFixedTendonConstraint<T>::DoCalcImpulse(
227+
const AbstractValue& abstract_data, EigenPtr<VectorX<T>> gamma) const {
228+
const auto& data = abstract_data.get_value<SapFixedTendonConstraintData<T>>();
229+
*gamma = data.gamma_;
230+
}
231+
232+
template <typename T>
233+
void SapFixedTendonConstraint<T>::DoCalcCostHessian(
234+
const AbstractValue& abstract_data, MatrixX<T>* G) const {
235+
const auto& data = abstract_data.get_value<SapFixedTendonConstraintData<T>>();
236+
*G = data.hessian_.asDiagonal();
237+
}
238+
239+
template <typename T>
240+
void SapFixedTendonConstraint<T>::DoAccumulateGeneralizedImpulses(
241+
int c, const Eigen::Ref<const VectorX<T>>& gamma,
242+
EigenPtr<VectorX<T>> tau) const {
243+
// For this constraint the generalized impulses are simply τ = Jᵀ⋅γ.
244+
if (c == 0) {
245+
this->first_clique_jacobian().TransposeAndMultiplyAndAddTo(gamma, tau);
246+
} else if (c == 1) {
247+
this->second_clique_jacobian().TransposeAndMultiplyAndAddTo(gamma, tau);
248+
} else {
249+
DRAKE_UNREACHABLE();
250+
}
251+
}
252+
253+
template <typename T>
254+
std::unique_ptr<SapConstraint<double>> SapFixedTendonConstraint<T>::DoToDouble()
255+
const {
256+
const typename SapFixedTendonConstraint<T>::Parameters& p = parameters_;
257+
const typename SapFixedTendonConstraint<T>::Kinematics& k = kinematics_;
258+
259+
SapFixedTendonConstraint<double>::Parameters p_to_double(
260+
ExtractDoubleOrThrow(p.lower_limit()),
261+
ExtractDoubleOrThrow(p.upper_limit()),
262+
ExtractDoubleOrThrow(p.stiffness()), ExtractDoubleOrThrow(p.damping()),
263+
p.beta());
264+
265+
SapFixedTendonConstraint<double>::Kinematics k_to_double(
266+
k.clique0(), k.clique1(), k.clique0_nv(), k.clique1_nv(),
267+
math::DiscardGradient(k.q0()), math::DiscardGradient(k.q1()),
268+
math::DiscardGradient(k.a0()), math::DiscardGradient(k.a1()),
269+
ExtractDoubleOrThrow(k.offset()));
270+
271+
return std::make_unique<SapFixedTendonConstraint<double>>(
272+
std::move(p_to_double), std::move(k_to_double));
273+
}
274+
275+
} // namespace internal
276+
} // namespace contact_solvers
277+
} // namespace multibody
278+
} // namespace drake
279+
280+
DRAKE_DEFINE_CLASS_TEMPLATE_INSTANTIATIONS_ON_DEFAULT_NONSYMBOLIC_SCALARS(
281+
class ::drake::multibody::contact_solvers::internal::
282+
SapFixedTendonConstraint);

0 commit comments

Comments
 (0)