Skip to content

Commit

Permalink
Merge branch 'main' into moco_contraint_projection
Browse files Browse the repository at this point in the history
  • Loading branch information
nickbianco authored Aug 21, 2024
2 parents de9c0d7 + f187bdd commit 5ac3a8d
Show file tree
Hide file tree
Showing 22 changed files with 639 additions and 84 deletions.
51 changes: 51 additions & 0 deletions Bindings/Python/tests/test_moco.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,27 @@ def createSlidingMassModel():

return model

def createDoubleSlidingMassModel():
model = createSlidingMassModel()
body = osim.Body("body2", 10.0, osim.Vec3(0), osim.Inertia(0))
model.addComponent(body)

joint = osim.SliderJoint("slider2", model.getGround(), body)
coord = joint.updCoordinate(osim.SliderJoint.Coord_TranslationX)
coord.setName("position");
model.addComponent(joint);

actu = osim.CoordinateActuator()
actu.setCoordinate(coord)
actu.setName("actuator2")
actu.setOptimalForce(1)
model.addComponent(actu)

model.finalizeConnections()
model.initSystem()
return model


class TestSwigAddtlInterface(unittest.TestCase):
def test_bounds(self):
model = osim.Model()
Expand Down Expand Up @@ -391,3 +412,33 @@ def test_changing_costs(self):
# Change the weights of the costs.
effort.setWeight(0.1)
assert(study.solve().getFinalTime() < 0.8 * finalTime0)

def test_expression_based_parameter_goal(self):
study = osim.MocoStudy()
mp = study.updProblem()
mp.setModel(createDoubleSlidingMassModel())
mp.setTimeBounds(0, 1)
mp.setStateInfo("/slider/position/value", [-5, 5], 0, [0.2, 0.3])
mp.setStateInfo("/slider/position/speed", [-20, 20])
mp.setStateInfo("/slider2/position/value", [-5, 5], 1, [1.2, 1.3])
mp.setStateInfo("/slider2/position/speed", [-20, 20])

parameter = osim.MocoParameter("sphere_mass", "body", "mass",
osim.MocoBounds(0, 10))
mp.addParameter(parameter)
parameter2 = osim.MocoParameter("sphere2_mass", "body2", "mass",
osim.MocoBounds(0, 10))
mp.addParameter(parameter2)
total_weight = 7
mass_goal = osim.MocoExpressionBasedParameterGoal()
mp.addGoal(mass_goal)
mass_goal.setExpression(f"(p+q-{total_weight})^2")
mass_goal.addParameter(parameter, "p")
mass_goal.addParameter(parameter2, "q")

ms = study.initTropterSolver()
ms.set_num_mesh_intervals(25)
sol = study.solve()

self.assertAlmostEqual(sol.getParameter("sphere_mass") + sol.getParameter("sphere2_mass"),
total_weight)
1 change: 1 addition & 0 deletions Bindings/moco.i
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace OpenSim {
%include <OpenSim/Moco/MocoGoal/MocoControlTrackingGoal.h>
%include <OpenSim/Moco/MocoGoal/MocoContactTrackingGoal.h>
%include <OpenSim/Moco/MocoGoal/MocoContactImpulseTrackingGoal.h>
%include <OpenSim/Moco/MocoGoal/MocoExpressionBasedParameterGoal.h>
%include <OpenSim/Moco/MocoGoal/MocoInitialActivationGoal.h>
%include <OpenSim/Moco/MocoGoal/MocoJointReactionGoal.h>
%include <OpenSim/Moco/MocoGoal/MocoSumSquaredStateGoal.h>
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ v4.6
- Fixed bugs in `convertToMocoTrajectory()` and `MocoTrajectory::resampleWithFrequency()` by updating `interpolate()` to
allow extrapolation using the `extrapolate` flag. Combined with the `ignoreNaNs` flag, this prevents NaNs from
occurring in the output. (#3867)
- Added `Output`s to `ExpressionBasedCoordinateForce`, `ExpressionBasedPointToPointForce`, and `ExpressionBasedBushingForce` for accessing force values. (#3872)

v4.5.1
======
Expand Down Expand Up @@ -78,6 +79,7 @@ pointer to avoid crashes in scripting due to invalid pointer ownership (#3781).
- Fixed bugs in `MocoCasOCProblem` and `CasOC::Problem` with incorrect string formatting. (#3828)
- Fixed `MocoOrientationTrackingGoal::initializeOnModelImpl` to check for missing kinematic states, but allow other missing columns. (#3830)
- Improved exception handling for internal errors in `MocoCasADiSolver`. Problems will now abort and print a descriptive error message (rather than fail due to an empty trajectory). (#3834)
- Upgraded the Ipopt dependency Metis to version 5.1.0 on Unix and macOS to enable building on `osx-arm64` (#3874).

v4.5
====
Expand Down
7 changes: 5 additions & 2 deletions CHANGELOG_MOCO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ Moco Change Log

1.4.0
-----
- 2024-07-29: Added support for the 'projection' method for enforcing kinematic
- 2024-08-21: Added support for the 'projection' method for enforcing kinematic
constraints from Bordalba et al. (2023) to `MocoCasADiSolver`.
This method allows enforcing kinematic constraints with any
transcription scheme and can be enabled using by setting the
property `kinematic_constraint_method` to `'Bordalba2023'`. The
existing default method from Posa et al. (2016) can also be
specified using `'Posa2016'`.

- 2024-07-29: The `MocoSolver` properties `interpolate_control_midpoints` and
- 2024-08-21: The `MocoSolver` properties `interpolate_control_midpoints` and
`enforce_path_constraint_midpoints` have been renamed to
`interpolate_control_mesh_interior_points` and
`enforce_path_constraint_mesh_interior_points`, respectively. This
Expand All @@ -21,6 +21,9 @@ Moco Change Log
necessarily coincide with the mesh interval midpoints (unlike
Hermite-Simpson transcription).

- 2024-08-15: Added `MocoExpressionBasedParameterGoal` to enable minimizing any arithmetic expression
of parameter values.

- 2024-07-26: Added `MocoStateBoundConstraint` and `MocoOutputBoundConstraint` to enable bounding
state variables or output values by one or two `Function`s, similar to
`MocoControlBoundConstraint`.
Expand Down
20 changes: 12 additions & 8 deletions OpenSim/Auxiliary/auxiliaryTestFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,14 +268,18 @@ OpenSim::Object* randomize(OpenSim::Object* obj)
double Ixy = 0.01*Ixx;
prop = SimTK::Vec6(Ixx, Ixx, Ixx, Ixy, Ixy, Ixy);
} else if (ts == "string") {
string base("ABCXYZ");
if (isList){
stringstream val;
val << base << "_" << ap.size();
ap.appendValue<string>(val.str());
}
else{
ap.updValue<string>() = base;
// We cannot use an arbitrary string for ExpressionBasedBushingForce
// properties since they must contain specific variable names (e.g.,
// "theta_x", "delta_x", etc.).
if (obj->getConcreteClassName() != "ExpressionBasedBushingForce") {
string base("ABCXYZ");
if (isList) {
stringstream val;
val << base << "_" << ap.size();
ap.appendValue<string>(val.str());
} else {
ap.updValue<string>() = base;
}
}
} else if (ts == "double" && isList && ap.getMaxListSize() < 20) {
for (int i=0; i< ap.getMaxListSize(); ++i)
Expand Down
6 changes: 5 additions & 1 deletion OpenSim/Moco/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ set(MOCO_SOURCES
MocoGoal/MocoControlGoal.cpp
MocoGoal/MocoControlTrackingGoal.h
MocoGoal/MocoControlTrackingGoal.cpp
MocoGoal/MocoExpressionBasedParameterGoal.h
MocoGoal/MocoExpressionBasedParameterGoal.cpp
MocoGoal/MocoJointReactionGoal.h
MocoGoal/MocoJointReactionGoal.cpp
MocoGoal/MocoOrientationTrackingGoal.h
Expand Down Expand Up @@ -175,7 +177,9 @@ target_compile_definitions(osimMoco PRIVATE
OpenSimAddInstallRPATHSelf(TARGET osimMoco LOADER)
OpenSimAddInstallRPATHSimbody(TARGET osimMoco LOADER
FROM "${CMAKE_INSTALL_LIBDIR}")

if (OPENSIM_WITH_TROPTER)
OpenSimAddInstallRPATHSelf(TARGET tropter LOADER)
endif()
install(TARGETS osimMoco EXPORT OpenSimTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
Expand Down
125 changes: 125 additions & 0 deletions OpenSim/Moco/MocoGoal/MocoExpressionBasedParameterGoal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/* -------------------------------------------------------------------------- *
* OpenSim: MocoExpressionBasedParameterGoal.cpp *
* -------------------------------------------------------------------------- *
* Copyright (c) 2024 Stanford University and the Authors *
* *
* Author(s): Allison John *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain a *
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* -------------------------------------------------------------------------- */

#include "MocoExpressionBasedParameterGoal.h"

#include <lepton/Exception.h>
#include <lepton/ParsedExpression.h>
#include <lepton/Parser.h>
#include <OpenSim/Simulation/Model/Model.h>

using namespace OpenSim;

void MocoExpressionBasedParameterGoal::constructProperties() {
constructProperty_expression("");
constructProperty_parameters();
constructProperty_variable_names();
}

void MocoExpressionBasedParameterGoal::initializeOnModelImpl(const Model& model)
const {
OPENSIM_THROW_IF_FRMOBJ(get_expression() == "", Exception,
"The expression has not been set. Use setExpression().")
m_program = Lepton::Parser::parse(get_expression()).optimize()
.createProgram();
setRequirements(0, 1, SimTK::Stage::Instance);

for (int i = 0; i < getProperty_parameters().size(); i++) {
// only taking the first one since they should all be the same value
std::string componentPath = get_parameters(i).getComponentPaths()[0];
const auto& component = model.getComponent(componentPath);
const auto* ap = &component.getPropertyByName(
get_parameters(i).getPropertyName());
m_property_refs.emplace_back(ap);

// get the type and element of the property
if (dynamic_cast<const Property<double>*>(ap)) {
m_data_types.emplace_back(Type_double);
} else {
if (dynamic_cast<const Property<SimTK::Vec3>*>(ap)) {
m_data_types.emplace_back(Type_Vec3);
m_indices.emplace_back(get_parameters(i).getPropertyElement());
}
else if (dynamic_cast<const Property<SimTK::Vec6>*>(ap)) {
m_data_types.emplace_back(Type_Vec6);
m_indices.emplace_back(get_parameters(i).getPropertyElement());
}
else {
OPENSIM_THROW_FRMOBJ(Exception,
"Data type of specified model property not supported.");
}
}
}

// test to make sure all variables are there
try
{
std::map<std::string, double> parameterVars;
for (int i = 0; i < getProperty_variable_names().size(); ++i) {
parameterVars[get_variable_names(i)] = getPropertyValue(i);
}
m_program.evaluate(parameterVars);
}
catch (Lepton::Exception& ex)
{
std::string msg = ex.what();
std::string help = "";
if (msg.compare(0, 30, "No value specified for variable")) {
help = " Use addParameter() to add a parameter for this variable, "
"or remove the variable from the expression for this goal.";
}
OPENSIM_THROW_FRMOBJ(Exception, fmt::format("Expression evaluate error:"
" {}.{}", msg, help));
}
}

double MocoExpressionBasedParameterGoal::getPropertyValue(int i) const {
OPENSIM_ASSERT_FRMOBJ(m_property_refs.size() > i);
const auto& propRef = m_property_refs[i];
if (m_data_types[i] == Type_double) {
return static_cast<const Property<double>*>(propRef.get())->getValue();
}
int elt = m_indices[i];
if (m_data_types[i] == Type_Vec3) {
return static_cast<const Property<SimTK::Vec3>*>(propRef.get())
->getValue()[elt];
}
if (m_data_types[i] == Type_Vec6) {
return static_cast<const Property<SimTK::Vec6>*>(propRef.get())
->getValue()[elt];
}
OPENSIM_THROW_FRMOBJ(Exception, fmt::format("Property at index {} is not of"
" a recognized type."));
}

void MocoExpressionBasedParameterGoal::calcGoalImpl(
const GoalInput& input, SimTK::Vector& values) const {
std::map<std::string, double> parameterVars;
for (int i = 0; i < getProperty_variable_names().size(); ++i) {
parameterVars[get_variable_names(i)] = getPropertyValue(i);
}
values[0] = m_program.evaluate(parameterVars);
}

void MocoExpressionBasedParameterGoal::printDescriptionImpl() const {
log_cout(" expression: {}", get_expression());
for (int i = 0; i < getProperty_parameters().size(); ++i) {
log_cout(" variable {}: {}", get_variable_names(i),
get_parameters(i).getName());
}
}
Loading

0 comments on commit 5ac3a8d

Please sign in to comment.