Skip to content

Commit 511775e

Browse files
authored
Merge pull request #2699 from opensim-org/ActivationCoordinateActuator
Add ActivationCoordinateActuator.h
2 parents 60dd70d + 3c23345 commit 511775e

File tree

7 files changed

+173
-3
lines changed

7 files changed

+173
-3
lines changed

Bindings/OpenSimHeaders_actuators.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <OpenSim/Actuators/osimActuatorsDLL.h>
77
#include <OpenSim/Actuators/CoordinateActuator.h>
8+
#include <OpenSim/Actuators/ActivationCoordinateActuator.h>
89
#include <OpenSim/Actuators/PointActuator.h>
910
#include <OpenSim/Actuators/TorqueActuator.h>
1011
#include <OpenSim/Actuators/BodyActuator.h>

Bindings/actuators.i

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//osimActuators
22
%include <OpenSim/Actuators/osimActuatorsDLL.h>
33
%include <OpenSim/Actuators/CoordinateActuator.h>
4+
%include <OpenSim/Actuators/ActivationCoordinateActuator.h>
45
%include <OpenSim/Actuators/PointActuator.h>
56
%include <OpenSim/Actuators/TorqueActuator.h>
67
%include <OpenSim/Actuators/BodyActuator.h>

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This is not a comprehensive list of changes but rather a hand-curated collection
88

99
v4.2
1010
====
11-
-
11+
- Add the ActivationCoordinateActuator component, which is a CoordinateActuator with simple activation dynamics (PR #2699).
1212

1313
v4.1
1414
====
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#ifndef OPENSIM_ACTIVATION_COORDINATE_ACTUATOR_H
2+
#define OPENSIM_ACTIVATION_COORDINATE_ACTUATOR_H
3+
/* -------------------------------------------------------------------------- *
4+
* OpenSim: ActivationCoordinateActuator.h *
5+
* -------------------------------------------------------------------------- *
6+
* The OpenSim API is a toolkit for musculoskeletal modeling and simulation. *
7+
* See http://opensim.stanford.edu and the NOTICE file for more information. *
8+
* OpenSim is developed at Stanford University and supported by the US *
9+
* National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA *
10+
* through the Warrior Web program. *
11+
* *
12+
* Copyright (c) 2005-2020 Stanford University and the Authors *
13+
* Author(s): Christopher Dembia *
14+
* *
15+
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
16+
* not use this file except in compliance with the License. You may obtain a *
17+
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
18+
* *
19+
* Unless required by applicable law or agreed to in writing, software *
20+
* distributed under the License is distributed on an "AS IS" BASIS, *
21+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
22+
* See the License for the specific language governing permissions and *
23+
* limitations under the License. *
24+
* -------------------------------------------------------------------------- */
25+
26+
#include "osimActuatorsDLL.h"
27+
28+
#include "CoordinateActuator.h"
29+
30+
namespace OpenSim {
31+
32+
/// Similar to CoordinateActuator (simply produces a generalized force) but
33+
/// with first-order linear activation dynamics. This actuator has one state
34+
/// variable, `activation`, with \f$ \dot{a} = (x - a) / \tau \f$, where
35+
/// \f$ a \f$ is activation, \f$ x \f$ is excitation, and \f$ \tau \f$ is the
36+
/// activation time constant (there is no separate deactivation time constant).
37+
/// The statebounds_activation output is used in Moco to set default values for
38+
/// the activation state variable.
39+
/// <b>Default %Property Values</b>
40+
/// @verbatim
41+
/// activation_time_constant: 0.01
42+
/// default_activation: 0.5
43+
/// @endverbatim
44+
class OSIMACTUATORS_API ActivationCoordinateActuator
45+
: public CoordinateActuator {
46+
OpenSim_DECLARE_CONCRETE_OBJECT(ActivationCoordinateActuator,
47+
CoordinateActuator);
48+
public:
49+
OpenSim_DECLARE_PROPERTY(activation_time_constant, double,
50+
"Larger value means activation can change more rapidly "
51+
"(units: seconds; default: 0.01 seconds).");
52+
53+
OpenSim_DECLARE_PROPERTY(default_activation, double,
54+
"Value of activation in the default state returned by initSystem() "
55+
"(default: 0.5).");
56+
57+
OpenSim_DECLARE_OUTPUT(statebounds_activation, SimTK::Vec2,
58+
getBoundsActivation, SimTK::Stage::Model);
59+
60+
ActivationCoordinateActuator() {
61+
constructProperties();
62+
}
63+
64+
/// Provide the coordinate name.
65+
explicit ActivationCoordinateActuator(const std::string& coordinateName)
66+
: ActivationCoordinateActuator() {
67+
if (!coordinateName.empty()) {
68+
set_coordinate(coordinateName);
69+
}
70+
}
71+
72+
/// The lower bound on activation is getMinControl() and the upper bound is
73+
/// getMaxControl().
74+
/// Whether these bounds are enforced is determined by the solver used.
75+
SimTK::Vec2 getBoundsActivation(const SimTK::State&) const {
76+
return SimTK::Vec2(getMinControl(), getMaxControl());
77+
}
78+
79+
protected:
80+
void extendAddToSystem(SimTK::MultibodySystem& system) const override {
81+
Super::extendAddToSystem(system);
82+
addStateVariable("activation", SimTK::Stage::Dynamics);
83+
}
84+
85+
void extendInitStateFromProperties(SimTK::State& s) const override {
86+
Super::extendInitStateFromProperties(s);
87+
setStateVariableValue(s, "activation", get_default_activation());
88+
}
89+
90+
void extendSetPropertiesFromState(const SimTK::State& s) override {
91+
Super::extendSetPropertiesFromState(s);
92+
set_default_activation(getStateVariableValue(s, "activation"));
93+
}
94+
95+
void computeStateVariableDerivatives(const SimTK::State& s) const override {
96+
// No need to do clamping, etc; CoordinateActuator is bidirectional.
97+
const auto& tau = get_activation_time_constant();
98+
const auto& x = getControl(s);
99+
const auto& a = getStateVariableValue(s, "activation");
100+
const SimTK::Real adot = (x - a) / tau;
101+
setStateVariableDerivativeValue(s, "activation", adot);
102+
}
103+
104+
double computeActuation(const SimTK::State& s) const override {
105+
return getStateVariableValue(s, "activation") * getOptimalForce();
106+
}
107+
private:
108+
void constructProperties() {
109+
constructProperty_activation_time_constant(0.010);
110+
constructProperty_default_activation(0.5);
111+
}
112+
};
113+
114+
} // namespace OpenSim
115+
116+
#endif // OPENSIM_ACTIVATION_COORDINATE_ACTUATOR_H

OpenSim/Actuators/RegisterTypes_osimActuators.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "RegisterTypes_osimActuators.h"
2828

2929
#include "CoordinateActuator.h"
30+
#include "ActivationCoordinateActuator.h"
3031
#include "PointActuator.h"
3132
#include "TorqueActuator.h"
3233
#include "BodyActuator.h"
@@ -78,6 +79,7 @@ OSIMACTUATORS_API void RegisterTypes_osimActuators()
7879
try {
7980

8081
Object::registerType( CoordinateActuator() );
82+
Object::registerType( ActivationCoordinateActuator() );
8183
Object::registerType( PointActuator() );
8284
Object::registerType( TorqueActuator() );
8385
Object::registerType( BodyActuator() );

OpenSim/Actuators/Test/testActuators.cpp

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
// 3. testClutchedPathSpring()
2929
// 4. testMcKibbenActuator()
3030
// 5. testActuatorsCombination()
31-
//
31+
// 6. testActivationCoordinateActuator()
32+
//
3233
// Add tests here as Actuators are added to OpenSim
3334
//
3435
//=============================================================================
@@ -55,6 +56,7 @@ void testBodyActuator();
5556
void testClutchedPathSpring();
5657
void testMcKibbenActuator();
5758
void testActuatorsCombination();
59+
void testActivationCoordinateActuator();
5860

5961

6062
int main()
@@ -81,6 +83,10 @@ int main()
8183
catch (const std::exception& e) {
8284
cout << e.what() << endl; failures.push_back("testActuatorsCombination");
8385
}
86+
try { testActivationCoordinateActuator(); }
87+
catch (const std::exception& e) {
88+
cout << e.what() << endl; failures.push_back("testActivationCoordinateActuator");
89+
}
8490
if (!failures.empty()) {
8591
cout << "Done, with failure(s): " << failures << endl;
8692
return 1;
@@ -709,7 +715,7 @@ void testBodyActuator()
709715
//==================================================================================
710716
/**
711717
* This test verifies if using a BodyActuator generates equivalent result in the body
712-
* acceleration compared to when using a combination of PointActuaor, TorqueActuaor
718+
* acceleration compared to when using a combination of PointActuaor, TorqueActuator
713719
* and BodyActuator.
714720
* It therefore also verifies model consistency when user defines and uses a
715721
* combination of these 3 actuators.
@@ -918,3 +924,46 @@ void testActuatorsCombination()
918924
std::cout << " ********** Test Actuator Combination time = ********** " <<
919925
1.e3*(std::clock() - startTime) / CLOCKS_PER_SEC << "ms\n" << endl;
920926
}
927+
928+
// Test de/serialization and numerical integration of
929+
// ActivationCoordinateActautor.
930+
void testActivationCoordinateActuator() {
931+
Model model;
932+
auto* body = new Body("body", 1, SimTK::Vec3(0), SimTK::Inertia(0));
933+
auto* joint = new PinJoint("joint", *body, model.getGround());
934+
joint->updCoordinate().setName("coord");
935+
model.addBody(body);
936+
model.addJoint(joint);
937+
auto* aca = new ActivationCoordinateActuator("coord");
938+
aca->setName("aca");
939+
const double a0 = 0.7;
940+
const double tau = 0.20;
941+
aca->set_default_activation(a0);
942+
aca->set_activation_time_constant(tau);
943+
model.addForce(aca);
944+
model.finalizeFromProperties();
945+
model.finalizeConnections();
946+
model.print("Model_ActivationCoordinateActuator.osim");
947+
948+
Model modelDeserialized("Model_ActivationCoordinateActuator.osim");
949+
ASSERT(model == modelDeserialized);
950+
951+
auto* controller = new PrescribedController();
952+
controller->addActuator(*aca);
953+
const double x = 0.15;
954+
controller->prescribeControlForActuator("aca", new Constant(x));
955+
model.addController(controller);
956+
957+
auto state = model.initSystem();
958+
959+
Manager manager(model);
960+
manager.initialize(state);
961+
const double tf = 0.10;
962+
state = manager.integrate(tf);
963+
964+
const double expectedFinalActivation =
965+
(a0 - x) * exp(-tf / tau) + x;
966+
const double foundFinalActivation =
967+
aca->getStateVariableValue(state, "activation");
968+
ASSERT_EQUAL(expectedFinalActivation, foundFinalActivation, 1e-4);
969+
}

OpenSim/Actuators/osimActuators.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* -------------------------------------------------------------------------- */
2525

2626
#include "CoordinateActuator.h"
27+
#include "ActivationCoordinateActuator.h"
2728
#include "PointActuator.h"
2829
#include "TorqueActuator.h"
2930
#include "BodyActuator.h"

0 commit comments

Comments
 (0)