Skip to content

Commit 3cde5f2

Browse files
authored
Merge pull request #3683 from opensim-org/controller_actuators_list_socket
Manage `Controller` actuators with a list `Socket`
2 parents f291e0b + 408a792 commit 3cde5f2

30 files changed

+672
-585
lines changed

Applications/Analyze/test/testAnalyzeTool.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -489,19 +489,12 @@ void testIMUDataReporter() {
489489
pendulum.getComponent<CoordinateActuator>("/tau0"));
490490
controller->addActuator(
491491
pendulum.getComponent<CoordinateActuator>("/tau1"));
492-
pendulum.addController(controller);
493-
pendulum.initSystem();
494-
495-
// Specify constant torque functions to the torque acutators via the
496-
// PrescribedController we added previously.
492+
// Specify constant torque functions to the torque actuators
497493
Constant* constantTorque0 = new Constant(10.0);
498494
Constant* constantTorque1 = new Constant(10.0);
499-
pendulum.updComponent<PrescribedController>(
500-
"/controllerset/torque_controller")
501-
.prescribeControlForActuator("tau0", constantTorque0);
502-
pendulum.updComponent<PrescribedController>(
503-
"/controllerset/torque_controller")
504-
.prescribeControlForActuator("tau1", constantTorque1);
495+
controller->prescribeControlForActuator("tau0", constantTorque0);
496+
controller->prescribeControlForActuator("tau1", constantTorque1);
497+
pendulum.addController(controller);
505498
state = pendulum.initSystem();
506499

507500
// Set a horizontal default pendulum position.

Applications/Forward/test/testForward.cpp

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ using namespace std;
3535

3636
void testPendulum();
3737
void testPendulumExternalLoad();
38-
void testPendulumExternalLoadWithPointInGround();
38+
void testPendulumExternalLoadWithPointInGround();
3939
void testArm26();
4040
void testGait2354();
4141
void testGait2354WithController();
@@ -95,7 +95,7 @@ void testPendulumExternalLoad() {
9595
ASSERT(results.getLastTime() == 1.0);
9696

9797
Storage standard("Results/pendulum_states.sto");
98-
98+
9999

100100
Array<double> data;
101101
int i = results.getSize() - 1;
@@ -104,10 +104,10 @@ void testPendulumExternalLoad() {
104104
data.setSize(state->getSize());
105105
standard.getDataAtTime(time, state->getSize(), data);
106106
int nc = forward.getModel().getNumCoordinates();
107-
for (int j = 0; j < nc; ++j) {
107+
for (int j = 0; j < nc; ++j) {
108108
stringstream message;
109-
message << "t=" << time <<" state# "<< j << " "
110-
<< standard.getColumnLabels()[j+1] << " std=" << data[j]
109+
message << "t=" << time <<" state# "<< j << " "
110+
<< standard.getColumnLabels()[j+1] << " std=" << data[j]
111111
<<" computed=" << state->getData()[j];
112112
ASSERT_EQUAL(data[j], state->getData()[j], 1e-2,
113113
__FILE__, __LINE__, "ASSERT_EQUAL FAILED " + message.str());
@@ -132,7 +132,7 @@ void testPendulumExternalLoadWithPointInGround() {
132132
data.setSize(state->getSize());
133133
standard.getDataAtTime(time, state->getSize(), data);
134134
int nc = forward.getModel().getNumCoordinates();
135-
for (int j = 0; j < nc; ++j) {
135+
for (int j = 0; j < nc; ++j) {
136136
stringstream message;
137137
message << "t=" << time <<" state# "<< j << " " << standard.getColumnLabels()[j+1]
138138
<< " std=" << data[j] <<" computed=" << state->getData()[j];
@@ -160,7 +160,7 @@ void testArm26() {
160160
for (int j = 0; j < state->getSize(); ++j) {
161161
stringstream message;
162162
message << "t=" << time <<" state# "<< j << " " << standard->getColumnLabels()[j+1] << " std=" << data[j] <<" computed=" << state->getData()[j] << endl;
163-
ASSERT_EQUAL(data[j], state->getData()[j], 5.0e-3,
163+
ASSERT_EQUAL(data[j], state->getData()[j], 5.0e-3,
164164
__FILE__, __LINE__, "ASSERT_EQUAL FAILED " + message.str());
165165
cout << "ASSERT_EQUAL PASSED " << message.str();
166166
}
@@ -173,7 +173,7 @@ void testArm26() {
173173
for (int j = 0; j < state->getSize(); ++j) {
174174
stringstream message;
175175
message << "t=" << time <<" state# "<< j << " " << standard->getColumnLabels()[j+1] << " std=" << data[j] <<" computed=" << state->getData()[j] << endl;
176-
ASSERT_EQUAL(data[j], state->getData()[j], 5.0e-3,
176+
ASSERT_EQUAL(data[j], state->getData()[j], 5.0e-3,
177177
__FILE__, __LINE__, "ASSERT_EQUAL FAILED " + message.str());
178178
cout << "ASSERT_EQUAL PASSED " << message.str();
179179
}
@@ -189,7 +189,7 @@ void testGait2354()
189189
Storage* standard = new Storage();
190190
string statesFileName("std_subject01_walk1_states.sto");
191191
forward.loadStatesStorage( statesFileName, standard );
192-
192+
193193
int nstates = forward.getModel().getNumStateVariables();
194194
int nq = forward.getModel().getNumCoordinates();
195195
std::vector<double> rms_tols(2*nstates, 0.001); //activations and fiber-lengths
@@ -198,11 +198,12 @@ void testGait2354()
198198
rms_tols[2*i] = 0.035; // coordinates at less than 2degrees
199199
rms_tols[2*i+1] = 2.5; // speeds can deviate by a lot due to open-loop test
200200
}
201-
202-
CHECK_STORAGE_AGAINST_STANDARD(results, *standard, rms_tols,
201+
202+
CHECK_STORAGE_AGAINST_STANDARD(results, *standard, rms_tols,
203203
__FILE__, __LINE__, "testGait2354 failed");
204204
}
205205

206+
206207
void testGait2354WithController() {
207208
ForwardTool forward("subject01_Setup_Forward_Controller.xml");
208209
forward.run();
@@ -220,7 +221,7 @@ void testGait2354WithController() {
220221
rms_tols[2*i] = 0.01; // coordinates at less than 0.6 degree
221222
rms_tols[2*i+1] = 0.1; // speeds should deviate less with feedback controller
222223
}
223-
224+
224225
CHECK_STORAGE_AGAINST_STANDARD(results, *standard, rms_tols,
225226
__FILE__, __LINE__, "testGait2354WithController failed");
226227
}
@@ -242,8 +243,6 @@ void testGait2354WithControllerGUI() {
242243
forward.updateModelForces(*model, "");
243244
forward.setModel(*model);
244245

245-
model->initSystem();
246-
247246
forward.run();
248247

249248
// For good measure we'll make sure we still get the identical results

Bindings/Java/tests/TestModelBuilding.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,13 @@ public static void main(String[] args) {
3232
PrescribedController cns = new PrescribedController();
3333
cns.addActuator(biceps);
3434
StepFunction testFunction = new StepFunction(0.5, 3.0, 0.3, 1.0);
35-
cns.prescribeControlForActuator(0, //Index in controller set
36-
testFunction);
35+
cns.prescribeControlForActuator("biceps", testFunction);
3736
System.gc(); // Request gc could free testFunction and crash
3837
arm.addBody(hum);
3938
arm.addBody(rad);
4039
arm.addJoint(shoulder);
4140
arm.addJoint(elbow);
42-
41+
4342
arm.addForce(biceps);
4443
arm.addController(cns);
4544
/*

Bindings/Python/tests/test_swig_additional_interface.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,16 @@ def test_PrescribedController_prescribeControlForActuator(self):
291291
# Controller.
292292
contr = osim.PrescribedController()
293293
contr.addActuator(actu)
294-
self.assertRaises(RuntimeError,
295-
contr.prescribeControlForActuator, 1, osim.Constant(3))
296-
# The following calls should not cause a memory leak:
297-
contr.prescribeControlForActuator(0, osim.Constant(2))
298294
contr.prescribeControlForActuator('actu', osim.Constant(4))
295+
model.addController(contr)
296+
# Should not throw.
297+
model.initSystem()
298+
299+
contr2 = osim.PrescribedController()
300+
contr2.addActuator(actu)
301+
contr2.prescribeControlForActuator('notAnActu', osim.Constant(5))
302+
model.addController(contr2)
303+
self.assertRaises(RuntimeError, model.initSystem)
299304

300305
def test_set_iterator(self):
301306
fs = osim.FunctionSet()

CHANGELOG.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ This is not a comprehensive list of changes but rather a hand-curated collection
99
v4.6
1010
====
1111
- Added support for list `Socket`s via the macro `OpenSim_DECLARE_LIST_SOCKET`. The macro-generated method
12-
`appendSocketConnectee_*` can be used to connect `Object`s to a list `Socket`. In addiion, `Component` and Socket have
13-
new `getConnectee` overloads that take an index to a desired object in the list `Socket` (#3652).
12+
`appendSocketConnectee_*` can be used to connect `Object`s to a list `Socket`. In addition, `Component` and Socket have
13+
new `getConnectee` overloads that take an index to a desired object in the list `Socket` (#3652).
1414
- Added `ComponentPath::root()`, which returns a `ComponentPath` equivalent to "/"
1515
- `ComponentPath` is now less-than (`<`) comparable, making it usable in (e.g.) `std::map`
1616
- `ComponentPath` now has a `std::hash<T>` implementation, making it usable in (e.g.) `std::unordered_map`
@@ -24,6 +24,12 @@ new `getConnectee` overloads that take an index to a desired object in the list
2424
- Calling `getConnectee` no longer strongly requires that `finalizeConnection` has been called on the socket. The
2525
implementation will now fall back to the (slower) method of following the socket's connectee path property. This
2626
is useful if (e.g.) following sockets *during* a call to `Component::finalizeConnections`
27+
- `Controller` now manages the list of controlled actuators using a list `Socket` instead of a `Set<Actuators>` (#3683).
28+
The `actuator_list` property has been removed from `Controller` in lieu of the list `Socket`, which appears as
29+
`socket_actuators` in the XML. This change also necessitated the addition of an added `initSystem()` call in
30+
`AbstractTool::updateModelForces()` so that connected actuators have the same root component as the `Model`
31+
at the time of `Socket` connection. Finally, `PrescribedController::prescribeControlForActuator(int, Function*)` is
32+
now deprecated in favor of `PrescribedController::prescribeControlForActuator(const std::string&, Function*)`.
2733

2834
v4.5
2935
====

OpenSim/Common/ComponentSocket.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class OSIMCOMMON_API AbstractSocket {
180180
* property to satisfy the socket. */
181181
virtual void disconnect() = 0;
182182

183-
/** %Set connectee path. This function can only be used if this socket is
183+
/** Set the connectee path. This function can only be used if this socket is
184184
* not a list socket. If a connectee reference is set (with connect()) the
185185
* connectee path is ignored; call disconnect() if you want the socket to be
186186
* connected using the connectee path.
@@ -426,7 +426,7 @@ class Socket : public AbstractSocket {
426426
* connect to that component.
427427
*
428428
* Throws an exception If you provide only a component name and the
429-
* model has multiple components with that nume.
429+
* model has multiple components with that name.
430430
* */
431431
void findAndConnect(const ComponentPath& connectee) override;
432432

OpenSim/Common/XMLDocument.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ using namespace std;
6767
// 30516 for GeometryPath default_color -> Appearance
6868
// 30517 for removal of _connectee_name suffix to shorten XML for socket, input
6969
// 40000 for OpenSim 4.0 release 40000
70-
// 40500 for updating 'GeometryPath' nodes to have property name 'path'.
70+
// 40500 for updating GeometryPath nodes to have property name 'path'.
71+
// 40600 for converting Controller actuators to a list Socket.
7172

72-
const int XMLDocument::LatestVersion = 40500;
73+
const int XMLDocument::LatestVersion = 40600;
7374
//=============================================================================
7475
// DESTRUCTOR AND CONSTRUCTOR(S)
7576
//=============================================================================

OpenSim/ExampleComponents/ToyReflexController.cpp

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,14 @@ void ToyReflexController::extendConnectToModel(Model &model)
6666
{
6767
Super::extendConnectToModel(model);
6868

69-
// get the list of actuators assigned to the reflex controller
70-
Set<const Actuator>& actuators = updActuators();
71-
72-
int cnt=0;
73-
74-
while(cnt < actuators.getSize()){
75-
const Muscle *musc = dynamic_cast<const Muscle*>(&actuators[cnt]);
76-
// control muscles only
77-
if(!musc){
78-
log_warn("ToyReflexController assigned a non-muscle actuator '{}', "
79-
"which will be ignored.", actuators[cnt].getName());
80-
actuators.remove(cnt);
81-
}else
82-
cnt++;
69+
const auto& socket = getSocket<Actuator>("actuators");
70+
for (int i = 0; i < (int)socket.getNumConnectees(); ++i) {
71+
const auto& actu = socket.getConnectee(i);
72+
const auto* musc = dynamic_cast<const Muscle*>(&actu);
73+
OPENSIM_THROW_IF_FRMOBJ(!musc, Exception,
74+
"Expected only muscle actuators assigned to this controller's "
75+
"'actuators' socket, but the non-muscle actuator '{}' was found.",
76+
actu.getName());
8377
}
8478
}
8579

@@ -94,12 +88,10 @@ void ToyReflexController::extendConnectToModel(Model &model)
9488
* @param controls system wide controls to which this controller can add
9589
*/
9690
void ToyReflexController::computeControls(const State& s,
97-
Vector &controls) const {
98-
// get time
99-
s.getTime();
91+
Vector &controls) const {
10092

101-
// get the list of actuators assigned to the reflex controller
102-
const Set<const Actuator>& actuators = getActuatorSet();
93+
// Get the Socket to the list of actuators assigned to the reflex controller.
94+
const auto& socket = getSocket<Actuator>("actuators");
10395

10496
// muscle lengthening speed
10597
double speed = 0;
@@ -108,8 +100,9 @@ void ToyReflexController::computeControls(const State& s,
108100
//reflex control
109101
double control = 0;
110102

111-
for(int i=0; i<actuators.getSize(); ++i){
112-
const Muscle *musc = dynamic_cast<const Muscle*>(&actuators[i]);
103+
for (int i = 0; i < (int)socket.getNumConnectees(); ++i) {
104+
const auto& actu = socket.getConnectee(i);
105+
const Muscle *musc = dynamic_cast<const Muscle*>(&actu);
113106
speed = musc->getLengtheningSpeed(s);
114107
// un-normalize muscle's maximum contraction velocity (fib_lengths/sec)
115108
max_speed =

OpenSim/Examples/ControllerExample/ControllerExample.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,9 @@ OpenSim_DECLARE_CONCRETE_OBJECT(TugOfWarController, Controller);
111111
double blockMass = getModel().getBodySet().get( "block" ).getMass();
112112

113113
// Get pointers to each of the muscles in the model.
114-
auto leftMuscle = dynamic_cast<const Muscle*> ( &getActuatorSet().get(0) );
115-
auto rightMuscle = dynamic_cast<const Muscle*> ( &getActuatorSet().get(1) );
114+
const auto& socket = getSocket<Actuator>("actuators");
115+
auto leftMuscle = dynamic_cast<const Muscle*>(&socket.getConnectee(0));
116+
auto rightMuscle = dynamic_cast<const Muscle*>(&socket.getConnectee(1));
116117

117118
// Compute the desired position of the block in the tug-of-war
118119
// model.

OpenSim/Examples/ExampleLuxoMuscle/LuxoMuscle_create_and_simulate.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -786,16 +786,20 @@ void createLuxoJr(OpenSim::Model& model){
786786
PrescribedController* kneeController = new PrescribedController();
787787
kneeController->addActuator(*kneeExtensorLeft);
788788
kneeController->addActuator(*kneeExtensorRight);
789-
kneeController->prescribeControlForActuator(0, x_of_t);
790-
kneeController->prescribeControlForActuator(1, x_of_t->clone());
789+
kneeController->prescribeControlForActuator(
790+
kneeExtensorLeft->getAbsolutePathString(), x_of_t);
791+
kneeController->prescribeControlForActuator(
792+
kneeExtensorRight->getAbsolutePathString(), x_of_t->clone());
791793

792794
model.addController(kneeController);
793795

794796
PrescribedController* backController = new PrescribedController();
795797
backController->addActuator(*backExtensorLeft);
796798
backController->addActuator(*backExtensorRight);
797-
backController->prescribeControlForActuator(0, x_of_t->clone());
798-
backController->prescribeControlForActuator(1, x_of_t->clone());
799+
backController->prescribeControlForActuator(
800+
backExtensorLeft->getAbsolutePathString(), x_of_t->clone());
801+
backController->prescribeControlForActuator(
802+
backExtensorRight->getAbsolutePathString(), x_of_t->clone());
799803

800804
model.addController(backController);
801805

0 commit comments

Comments
 (0)