Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/hard inequality constraints #35

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions ocs2_oc/include/ocs2_oc/oc_problem/OptimalControlProblem.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ struct OptimalControlProblem {
std::unique_ptr<StateConstraintCollection> preJumpEqualityConstraintPtr;
/** Final equality constraints */
std::unique_ptr<StateConstraintCollection> finalEqualityConstraintPtr;
/** (Hard) inequality constraints **/
std::unique_ptr<StateInputConstraintCollection> inequalityConstraintPtr;

/* Lagrangians */
/** Lagrangian for intermediate equality constraints */
Expand Down
8 changes: 8 additions & 0 deletions ocs2_oc/include/ocs2_oc/oc_solver/PerformanceIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ struct PerformanceIndex {
*/
scalar_t equalityConstraintsSSE = 0.0;

/** Sum of Squared Error (SSE) of hard inequality constraints, used by the SQP solver.
* Inequality constraints are satisfied if positive, so there is only error if the constraints are negative.
*/
scalar_t inequalityConstraintsSSE = 0.0;

/** Sum of equality Lagrangians:
* - Final: penalty for violation in state equality constraints
* - PreJumps: penalty for violation in state equality constraints
Expand All @@ -76,6 +81,7 @@ struct PerformanceIndex {
this->cost += rhs.cost;
this->dynamicsViolationSSE += rhs.dynamicsViolationSSE;
this->equalityConstraintsSSE += rhs.equalityConstraintsSSE;
this->inequalityConstraintsSSE += rhs.inequalityConstraintsSSE;
this->equalityLagrangian += rhs.equalityLagrangian;
this->inequalityLagrangian += rhs.inequalityLagrangian;
return *this;
Expand All @@ -93,6 +99,7 @@ inline void swap(PerformanceIndex& lhs, PerformanceIndex& rhs) {
std::swap(lhs.cost, rhs.cost);
std::swap(lhs.dynamicsViolationSSE, rhs.dynamicsViolationSSE);
std::swap(lhs.equalityConstraintsSSE, rhs.equalityConstraintsSSE);
std::swap(lhs.inequalityConstraintsSSE, rhs.inequalityConstraintsSSE);
std::swap(lhs.equalityLagrangian, rhs.equalityLagrangian);
std::swap(lhs.inequalityLagrangian, rhs.inequalityLagrangian);
}
Expand All @@ -109,6 +116,7 @@ inline std::ostream& operator<<(std::ostream& stream, const PerformanceIndex& pe
stream << std::setw(indentation) << "";
stream << "Dynamics violation SSE: " << std::setw(tabSpace) << performanceIndex.dynamicsViolationSSE;
stream << "Equality constraints SSE: " << std::setw(tabSpace) << performanceIndex.equalityConstraintsSSE << '\n';
stream << "Inequality constraints SSE: " << std::setw(tabSpace) << performanceIndex.inequalityConstraintsSSE << '\n';

stream << std::setw(indentation) << "";
stream << "Equality Lagrangian: " << std::setw(tabSpace) << performanceIndex.equalityLagrangian;
Expand Down
7 changes: 7 additions & 0 deletions ocs2_oc/src/oc_problem/OptimalControlProblem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ OptimalControlProblem::OptimalControlProblem()
stateEqualityConstraintPtr(new StateConstraintCollection),
preJumpEqualityConstraintPtr(new StateConstraintCollection),
finalEqualityConstraintPtr(new StateConstraintCollection),
/* Inequality constraints */
inequalityConstraintPtr(new StateInputConstraintCollection),
/* Lagrangians */
equalityLagrangianPtr(new StateInputCostCollection),
stateEqualityLagrangianPtr(new StateCostCollection),
Expand Down Expand Up @@ -82,6 +84,8 @@ OptimalControlProblem::OptimalControlProblem(const OptimalControlProblem& other)
stateEqualityConstraintPtr(other.stateEqualityConstraintPtr->clone()),
preJumpEqualityConstraintPtr(other.preJumpEqualityConstraintPtr->clone()),
finalEqualityConstraintPtr(other.finalEqualityConstraintPtr->clone()),
/* Inequality constraints */
inequalityConstraintPtr(other.inequalityConstraintPtr->clone()),
/* Lagrangians */
equalityLagrangianPtr(other.equalityLagrangianPtr->clone()),
stateEqualityLagrangianPtr(other.stateEqualityLagrangianPtr->clone()),
Expand Down Expand Up @@ -130,6 +134,9 @@ void OptimalControlProblem::swap(OptimalControlProblem& other) noexcept {
preJumpEqualityConstraintPtr.swap(other.preJumpEqualityConstraintPtr);
finalEqualityConstraintPtr.swap(other.finalEqualityConstraintPtr);

/* Inequality constraints */
inequalityConstraintPtr.swap(other.inequalityConstraintPtr);

/* Lagrangians */
equalityLagrangianPtr.swap(other.equalityLagrangianPtr);
stateEqualityLagrangianPtr.swap(other.stateEqualityLagrangianPtr);
Expand Down
44 changes: 44 additions & 0 deletions ocs2_oc/test/include/ocs2_oc/test/circular_kinematics.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,39 @@ class CircularKinematicsConstraints final : public StateInputConstraint {
}
};

/******************************************************************************************************/
/******************************************************************************************************/
/******************************************************************************************************/
/**
* Adds a constraint on the state, such that the first coordinate x(0) >= 0.75, which causes the
* trajectory to deviate from the unconstrained version.
*/
class CircularKinematicsInequalityConstraints final : public StateInputConstraint {
public:
CircularKinematicsInequalityConstraints() : StateInputConstraint(ConstraintOrder::Linear) {}
~CircularKinematicsInequalityConstraints() override = default;

CircularKinematicsInequalityConstraints* clone() const override { return new CircularKinematicsInequalityConstraints(*this); }

size_t getNumConstraints(scalar_t time) const override { return 1; }

vector_t getValue(scalar_t t, const vector_t& x, const vector_t& u, const PreComputation&) const override {
vector_t e(1);
e << x(0) - 0.75;
return e;
}

VectorFunctionLinearApproximation getLinearApproximation(scalar_t t, const vector_t& x, const vector_t& u,
const PreComputation& preComp) const override {
VectorFunctionLinearApproximation e;
e.f = getValue(t, x, u, preComp);
e.dfdx.setZero(1, 2);
e.dfdx(0, 0) = 1;
e.dfdu.setZero(1, 2);
return e;
}
};

/******************************************************************************************************/
/******************************************************************************************************/
/******************************************************************************************************/
Expand All @@ -136,4 +169,15 @@ inline OptimalControlProblem createCircularKinematicsProblem(const std::string&
return problem;
}

/******************************************************************************************************/
/******************************************************************************************************/
/******************************************************************************************************/
inline OptimalControlProblem createCircularKinematicsProblemWithInequality(const std::string& libraryFolder) {
// Add an inequality constraint to the problem
OptimalControlProblem problem = createCircularKinematicsProblem(libraryFolder);
std::unique_ptr<StateInputConstraint> constraint(new CircularKinematicsInequalityConstraints());
problem.inequalityConstraintPtr->add("ineqConstraint", std::move(constraint));
return problem;
}

} // namespace ocs2
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class LeggedRobotInterface final : public RobotInterface {
std::pair<scalar_t, RelaxedBarrierPenalty::Config> loadFrictionConeSettings(const std::string& taskFile, bool verbose) const;
std::unique_ptr<StateInputCost> getFrictionConeConstraint(size_t contactPointIndex, scalar_t frictionCoefficient,
const RelaxedBarrierPenalty::Config& barrierPenaltyConfig);
std::unique_ptr<StateInputConstraint> getHardFrictionConeConstraint(size_t contactPointIndex, scalar_t frictionCoefficient);
std::unique_ptr<StateInputConstraint> getZeroForceConstraint(size_t contactPointIndex);
std::unique_ptr<StateInputConstraint> getZeroVelocityConstraint(const EndEffectorKinematics<scalar_t>& eeKinematics,
size_t contactPointIndex, bool useAnalyticalGradients);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,9 @@ void LeggedRobotInterface::setupOptimalConrolProblem(const std::string& taskFile
modelSettings_.recompileLibrariesCppAd, modelSettings_.verboseCppAd));
}

problemPtr_->softConstraintPtr->add(footName + "_frictionCone",
getFrictionConeConstraint(i, frictionCoefficient, barrierPenaltyConfig));
// problemPtr_->softConstraintPtr->add(footName + "_frictionCone",
// getFrictionConeConstraint(i, frictionCoefficient, barrierPenaltyConfig));
problemPtr_->inequalityConstraintPtr->add(footName + "_frictionCone", getHardFrictionConeConstraint(i, frictionCoefficient));
problemPtr_->equalityConstraintPtr->add(footName + "_zeroForce", getZeroForceConstraint(i));
problemPtr_->equalityConstraintPtr->add(footName + "_zeroVelocity",
getZeroVelocityConstraint(*eeKinematicsPtr, i, useAnalyticalGradientsConstraints));
Expand Down Expand Up @@ -321,6 +322,13 @@ std::unique_ptr<StateInputCost> LeggedRobotInterface::getFrictionConeConstraint(
return std::unique_ptr<StateInputCost>(new StateInputSoftConstraint(std::move(frictionConeConstraintPtr), std::move(penalty)));
}

std::unique_ptr<StateInputConstraint> LeggedRobotInterface::getHardFrictionConeConstraint(size_t contactPointIndex, scalar_t frictionCoefficient) {
FrictionConeConstraint::Config frictionConeConConfig(frictionCoefficient);
std::unique_ptr<FrictionConeConstraint> frictionConeConstraintPtr(
new FrictionConeConstraint(*referenceManagerPtr_, std::move(frictionConeConConfig), contactPointIndex, centroidalModelInfo_));
return frictionConeConstraintPtr;
}

/******************************************************************************************************/
/******************************************************************************************************/
/******************************************************************************************************/
Expand Down
6 changes: 4 additions & 2 deletions ocs2_sqp/hpipm_catkin/include/hpipm_catkin/HpipmInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ class HpipmInterface {
* @param x0 : Initial state (deviation).
* @param dynamics : Linearized approximation of the discrete dynamics.
* @param cost : Quadratic approximation of the cost.
* @param constraints : Linearized approximation of constraints, all constraints are mapped to inequality constraints in HPIPM.
* @param constraints : Linearized approximation of equality constraints, all constraints are mapped to inequality constraints in HPIPM.
* @param ineqConstraints : Linearized approximation of inequality constraints.
* @param [out] stateTrajectory : Solution state (deviation) trajectory.
* @param [out] inputTrajectory : Solution input (deviation) trajectory.
* @param verbose : Prints the HPIPM iteration statistics if true.
Expand All @@ -85,7 +86,8 @@ class HpipmInterface {
*/
hpipm_status solve(const vector_t& x0, std::vector<VectorFunctionLinearApproximation>& dynamics,
std::vector<ScalarFunctionQuadraticApproximation>& cost, std::vector<VectorFunctionLinearApproximation>* constraints,
vector_array_t& stateTrajectory, vector_array_t& inputTrajectory, bool verbose = false);
std::vector<VectorFunctionLinearApproximation>* ineqConstraints, vector_array_t& stateTrajectory,
vector_array_t& inputTrajectory, bool verbose = false);

/**
* Return the Riccati cost-to-go for the previously solved problem.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,20 @@ struct Settings {
int warm_start = 0;
int pred_corr = 1;
int ric_alg = 0; // square root ricatti recursion

// Slacks - soften constraints
// TODO we probably want more control over which constraints get slacks,
// perhaps in its own structure
bool use_slack = false;
scalar_t slack_upper_L2_penalty = 1e2;
scalar_t slack_lower_L2_penalty = 1e2;
scalar_t slack_upper_L1_penalty = 0;
scalar_t slack_lower_L1_penalty = 0;
scalar_t slack_upper_low_bound = 0;
scalar_t slack_lower_low_bound = 0;
};

std::ostream& operator<<(std::ostream& stream, const Settings& settings);

} // namespace hpipm_interface
} // namespace ocs2
} // namespace ocs2
10 changes: 7 additions & 3 deletions ocs2_sqp/hpipm_catkin/include/hpipm_catkin/OcpSize.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,16 @@ bool operator==(const OcpSize& lhs, const OcpSize& rhs) noexcept;
*
* @param dynamics : Linearized approximation of the discrete dynamics.
* @param cost : Quadratic approximation of the cost.
* @param constraints : Linearized approximation of constraints, all constraints are mapped to inequality constraints in HPIPM.
* @param constraints : Linearized approximation of equality constraints, all constraints are mapped to inequality constraints in HPIPM.
* @param ineqConstraints : Linearized approximation of inequality constraints.
* @param useSlack : True if slack variables should be used.
* @return Derived sizes
*/
OcpSize extractSizesFromProblem(const std::vector<VectorFunctionLinearApproximation>& dynamics,
const std::vector<ScalarFunctionQuadraticApproximation>& cost,
const std::vector<VectorFunctionLinearApproximation>* constraints);
const std::vector<VectorFunctionLinearApproximation>* constraints,
const std::vector<VectorFunctionLinearApproximation>* ineqConstraints,
bool useSlack);

} // namespace hpipm_interface
} // namespace ocs2
} // namespace ocs2
Loading