Line |
Branch |
Exec |
Source |
1 |
|
|
// Copyright (c) 2015, Joseph Mirabel |
2 |
|
|
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr) |
3 |
|
|
// |
4 |
|
|
|
5 |
|
|
// Redistribution and use in source and binary forms, with or without |
6 |
|
|
// modification, are permitted provided that the following conditions are |
7 |
|
|
// met: |
8 |
|
|
// |
9 |
|
|
// 1. Redistributions of source code must retain the above copyright |
10 |
|
|
// notice, this list of conditions and the following disclaimer. |
11 |
|
|
// |
12 |
|
|
// 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
// notice, this list of conditions and the following disclaimer in the |
14 |
|
|
// documentation and/or other materials provided with the distribution. |
15 |
|
|
// |
16 |
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 |
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 |
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 |
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
20 |
|
|
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 |
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 |
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 |
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 |
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 |
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 |
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
27 |
|
|
// DAMAGE. |
28 |
|
|
|
29 |
|
|
#include "hpp/constraints/qp-static-stability.hh" |
30 |
|
|
|
31 |
|
|
#include <hpp/pinocchio/device.hh> |
32 |
|
|
#include <hpp/pinocchio/joint.hh> |
33 |
|
|
#include <limits> |
34 |
|
|
|
35 |
|
|
#include "hpp/constraints/tools.hh" |
36 |
|
|
|
37 |
|
|
namespace hpp { |
38 |
|
|
namespace constraints { |
39 |
|
|
namespace { |
40 |
|
✗ |
std::size_t forceDatasToNbContacts( |
41 |
|
|
const std::vector<ConvexShapeContact::ForceData>& fds) { |
42 |
|
✗ |
std::size_t nb = 0; |
43 |
|
✗ |
for (std::vector<ConvexShapeContact::ForceData>::const_iterator it = |
44 |
|
✗ |
fds.begin(); |
45 |
|
✗ |
it != fds.end(); ++it) |
46 |
|
✗ |
nb += it->points.size(); |
47 |
|
✗ |
return nb; |
48 |
|
|
} |
49 |
|
|
} // namespace |
50 |
|
|
|
51 |
|
|
const Eigen::Matrix<value_type, 6, 1> QPStaticStability::Gravity = |
52 |
|
|
(Eigen::Matrix<value_type, 6, 1>() << 0, 0, -1, 0, 0, 0).finished(); |
53 |
|
|
const Eigen::Matrix<value_type, 6, 1> QPStaticStability::MinusGravity = |
54 |
|
|
(Eigen::Matrix<value_type, 6, 1>() << 0, 0, +1, 0, 0, 0).finished(); |
55 |
|
|
|
56 |
|
✗ |
QPStaticStability::QPStaticStability(const std::string& name, |
57 |
|
|
const DevicePtr_t& robot, |
58 |
|
|
const Contacts_t& contacts, |
59 |
|
✗ |
const CenterOfMassComputationPtr_t& com) |
60 |
|
|
: DifferentiableFunction(robot->configSize(), robot->numberDof(), |
61 |
|
✗ |
LiegroupSpace::R1(), name), |
62 |
|
✗ |
Zeros(new qpOASES::real_t[contacts.size()]), |
63 |
|
✗ |
nWSR(40), |
64 |
|
✗ |
robot_(robot), |
65 |
|
✗ |
nbContacts_(contacts.size()), |
66 |
|
✗ |
com_(com), |
67 |
|
✗ |
H_(nbContacts_, nbContacts_), |
68 |
|
✗ |
G_(nbContacts_), |
69 |
|
✗ |
qp_((qpOASES::int_t)nbContacts_, qpOASES::HST_SEMIDEF), |
70 |
|
✗ |
phi_(Eigen::Matrix<value_type, 6, Eigen::Dynamic>::Zero(6, nbContacts_), |
71 |
|
✗ |
Eigen::Matrix<value_type, 6, Eigen::Dynamic>::Zero( |
72 |
|
✗ |
6, nbContacts_ * robot->numberDof())), |
73 |
|
✗ |
primal_(vector_t::Zero(nbContacts_)), |
74 |
|
✗ |
dual_(vector_t::Zero(nbContacts_)) { |
75 |
|
✗ |
VectorMap_t zeros(Zeros, nbContacts_); |
76 |
|
✗ |
zeros.setZero(); |
77 |
|
|
|
78 |
|
✗ |
qpOASES::Options options; |
79 |
|
✗ |
qp_.setOptions(options); |
80 |
|
|
|
81 |
|
✗ |
qp_.setPrintLevel(qpOASES::PL_NONE); |
82 |
|
✗ |
phi_.setSize(2, nbContacts_); |
83 |
|
✗ |
Traits<PointCom>::Ptr_t OG = PointCom::create(com); |
84 |
|
✗ |
for (std::size_t i = 0; i < contacts.size(); ++i) { |
85 |
|
|
Traits<PointInJoint>::Ptr_t OP2 = PointInJoint::create( |
86 |
|
✗ |
contacts[i].joint, contacts[i].point, robot->numberDof()); |
87 |
|
|
Traits<VectorInJoint>::Ptr_t n2 = VectorInJoint::create( |
88 |
|
✗ |
contacts[i].joint, contacts[i].normal, robot->numberDof()); |
89 |
|
|
|
90 |
|
✗ |
phi_(0, i) = n2; |
91 |
|
✗ |
phi_(1, i) = (OG - OP2) ^ n2; |
92 |
|
|
} |
93 |
|
|
} |
94 |
|
|
|
95 |
|
✗ |
QPStaticStability::QPStaticStability(const std::string& name, |
96 |
|
|
const DevicePtr_t& robot, |
97 |
|
|
const std::vector<ForceData>& contacts, |
98 |
|
✗ |
const CenterOfMassComputationPtr_t& com) |
99 |
|
|
: DifferentiableFunction(robot->configSize(), robot->numberDof(), 1, name), |
100 |
|
✗ |
Zeros(new qpOASES::real_t[forceDatasToNbContacts(contacts)]), |
101 |
|
✗ |
nWSR(40), |
102 |
|
✗ |
robot_(robot), |
103 |
|
✗ |
nbContacts_(forceDatasToNbContacts(contacts)), |
104 |
|
✗ |
com_(com), |
105 |
|
✗ |
H_(nbContacts_, nbContacts_), |
106 |
|
✗ |
G_(nbContacts_), |
107 |
|
✗ |
qp_((qpOASES::int_t)nbContacts_, qpOASES::HST_SEMIDEF), |
108 |
|
✗ |
phi_(Eigen::Matrix<value_type, 6, Eigen::Dynamic>::Zero(6, nbContacts_), |
109 |
|
✗ |
Eigen::Matrix<value_type, 6, Eigen::Dynamic>::Zero( |
110 |
|
✗ |
6, nbContacts_ * robot->numberDof())), |
111 |
|
✗ |
primal_(vector_t::Zero(nbContacts_)), |
112 |
|
✗ |
dual_(vector_t::Zero(nbContacts_)) { |
113 |
|
✗ |
VectorMap_t zeros(Zeros, nbContacts_); |
114 |
|
✗ |
zeros.setZero(); |
115 |
|
|
|
116 |
|
✗ |
qpOASES::Options options; |
117 |
|
✗ |
qp_.setOptions(options); |
118 |
|
|
|
119 |
|
✗ |
qp_.setPrintLevel(qpOASES::PL_NONE); |
120 |
|
✗ |
phi_.setSize(2, nbContacts_); |
121 |
|
✗ |
Traits<PointCom>::Ptr_t OG = PointCom::create(com); |
122 |
|
✗ |
std::size_t col = 0; |
123 |
|
✗ |
for (std::size_t i = 0; i < contacts.size(); ++i) { |
124 |
|
|
Traits<VectorInJoint>::Ptr_t n = VectorInJoint::create( |
125 |
|
✗ |
contacts[i].joint, contacts[i].normal, robot->numberDof()); |
126 |
|
✗ |
for (std::size_t j = 0; j < contacts[i].points.size(); ++j) { |
127 |
|
|
Traits<PointInJoint>::Ptr_t OP = PointInJoint::create( |
128 |
|
✗ |
contacts[i].joint, contacts[i].points[j], robot->numberDof()); |
129 |
|
|
|
130 |
|
✗ |
phi_(0, col) = n; |
131 |
|
✗ |
phi_(1, col) = (OG - OP) ^ n; |
132 |
|
✗ |
col++; |
133 |
|
|
} |
134 |
|
|
} |
135 |
|
|
} |
136 |
|
|
|
137 |
|
✗ |
QPStaticStabilityPtr_t QPStaticStability::create( |
138 |
|
|
const std::string& name, const DevicePtr_t& robot, |
139 |
|
|
const Contacts_t& contacts, const CenterOfMassComputationPtr_t& com) { |
140 |
|
|
return QPStaticStabilityPtr_t( |
141 |
|
✗ |
new QPStaticStability(name, robot, contacts, com)); |
142 |
|
|
} |
143 |
|
|
|
144 |
|
✗ |
QPStaticStabilityPtr_t QPStaticStability::create( |
145 |
|
|
const std::string& name, const DevicePtr_t& robot, |
146 |
|
|
const std::vector<ForceData>& contacts, |
147 |
|
|
const CenterOfMassComputationPtr_t& com) { |
148 |
|
|
return QPStaticStabilityPtr_t( |
149 |
|
✗ |
new QPStaticStability(name, robot, contacts, com)); |
150 |
|
|
} |
151 |
|
|
|
152 |
|
✗ |
QPStaticStabilityPtr_t QPStaticStability::create( |
153 |
|
|
const DevicePtr_t& robot, const Contacts_t& contacts, |
154 |
|
|
const CenterOfMassComputationPtr_t& com) { |
155 |
|
✗ |
return create("QPStaticStability", robot, contacts, com); |
156 |
|
|
} |
157 |
|
|
|
158 |
|
✗ |
void QPStaticStability::impl_compute(LiegroupElementRef result, |
159 |
|
|
ConfigurationIn_t argument) const { |
160 |
|
✗ |
robot_->currentConfiguration(argument); |
161 |
|
✗ |
robot_->computeForwardKinematics(pinocchio::JOINT_POSITION); |
162 |
|
|
|
163 |
|
✗ |
phi_.invalidate(); |
164 |
|
✗ |
phi_.computeValue(argument); |
165 |
|
|
// phi_.computeSVD (argument); |
166 |
|
|
|
167 |
|
✗ |
qpOASES::returnValue ret = solveQP(result.vector()); |
168 |
|
|
if (ret != qpOASES::SUCCESSFUL_RETURN) { |
169 |
|
|
hppDout(error, "QP could not be solved. Error is " << ret); |
170 |
|
|
} |
171 |
|
✗ |
if (!checkQPSol()) { |
172 |
|
|
hppDout(error, "QP solution does not satisfies the constraints"); |
173 |
|
|
} |
174 |
|
|
} |
175 |
|
|
|
176 |
|
✗ |
void QPStaticStability::impl_jacobian(matrixOut_t jacobian, |
177 |
|
|
ConfigurationIn_t argument) const { |
178 |
|
✗ |
robot_->currentConfiguration(argument); |
179 |
|
✗ |
robot_->computeForwardKinematics(pinocchio::JOINT_POSITION | |
180 |
|
|
pinocchio::JACOBIAN); |
181 |
|
|
|
182 |
|
✗ |
phi_.invalidate(); |
183 |
|
|
// phi_.computeSVD (argument); |
184 |
|
✗ |
phi_.computeJacobian(argument); |
185 |
|
|
|
186 |
|
✗ |
vector_t res(1); |
187 |
|
✗ |
qpOASES::returnValue ret = solveQP(res); |
188 |
|
|
if (ret != qpOASES::SUCCESSFUL_RETURN) { |
189 |
|
|
hppDout(error, "QP could not be solved. Error is " << ret); |
190 |
|
|
} |
191 |
|
✗ |
if (!checkQPSol()) { |
192 |
|
|
hppDout(error, "QP solution does not satisfies the constraints"); |
193 |
|
|
} |
194 |
|
✗ |
if (!checkStrictComplementarity()) { |
195 |
|
|
hppDout(error, |
196 |
|
|
"Strict complementary slackness does not hold. " |
197 |
|
|
"Jacobian WILL be wrong."); |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
// TODO preallocate this. |
201 |
|
✗ |
matrix_t JT_phi_F(nbContacts_, robot_->numberDof()); |
202 |
|
✗ |
matrix_t J_F(6, robot_->numberDof()); |
203 |
|
✗ |
phi_.jacobianTransposeTimes(argument, phi_.value() * primal_, JT_phi_F); |
204 |
|
✗ |
phi_.jacobianTimes(argument, primal_, J_F); |
205 |
|
|
|
206 |
|
✗ |
jacobian = 0.5 * primal_.transpose() * JT_phi_F + |
207 |
|
✗ |
(0.5 * phi_.value() * primal_ + Gravity).transpose() * J_F; |
208 |
|
|
} |
209 |
|
|
|
210 |
|
✗ |
inline qpOASES::returnValue QPStaticStability::solveQP( |
211 |
|
|
vectorOut_t result) const { |
212 |
|
|
// TODO: Use the SVD to solve a smaller quadratic problem |
213 |
|
|
// Try to find a positive solution |
214 |
|
|
using qpOASES::SUCCESSFUL_RETURN; |
215 |
|
|
|
216 |
|
✗ |
H_ = phi_.value().transpose() * phi_.value(); |
217 |
|
✗ |
G_ = phi_.value().transpose() * Gravity; |
218 |
|
|
|
219 |
|
✗ |
qpOASES::int_t nwsr = nWSR; |
220 |
|
✗ |
qp_.reset(); |
221 |
|
✗ |
qp_.setHessianType(qpOASES::HST_SEMIDEF); |
222 |
|
|
qpOASES::returnValue ret; |
223 |
|
✗ |
ret = qp_.init(H_.data(), G_.data(), Zeros, 0, nwsr, 0); |
224 |
|
✗ |
qp_.getPrimalSolution(primal_.data()); |
225 |
|
✗ |
qp_.getDualSolution(dual_.data()); |
226 |
|
✗ |
result[0] = 2 * qp_.getObjVal() + MinusGravity.squaredNorm(); |
227 |
|
✗ |
return ret; |
228 |
|
|
} |
229 |
|
|
|
230 |
|
✗ |
bool QPStaticStability::checkQPSol() const { |
231 |
|
✗ |
return (primal_.array() >= -1e-8).all(); |
232 |
|
|
} |
233 |
|
|
|
234 |
|
✗ |
bool QPStaticStability::checkStrictComplementarity() const { |
235 |
|
✗ |
qpOASES::real_t eps = qp_.getOptions().boundTolerance; |
236 |
|
✗ |
return ((primal_.array() > eps && dual_.cwiseAbs().array() <= eps) || |
237 |
|
✗ |
(dual_.array() > eps && primal_.cwiseAbs().array() <= eps)) |
238 |
|
✗ |
.all(); |
239 |
|
|
} |
240 |
|
|
} // namespace constraints |
241 |
|
|
} // namespace hpp |
242 |
|
|
|