@@ -122,6 +122,91 @@ class ControlBoardPositionDirectFixture : public ::testing::Test
122
122
yarp::dev::IEncoders* iEncoders = nullptr ;
123
123
};
124
124
125
+ class ControlBoardPositionDirectCoupledPendulumFixture : public ::testing::Test
126
+ {
127
+ protected:
128
+ // void SetUp() override
129
+ ControlBoardPositionDirectCoupledPendulumFixture ()
130
+ : testFixture{(std::filesystem::path (CMAKE_CURRENT_SOURCE_DIR)
131
+ / " coupled_pendulum_two_joints_coupled.sdf" )
132
+ .string ()}
133
+ {
134
+ gz::common::Console::SetVerbosity (4 );
135
+
136
+ testFixture.
137
+ // Use configure callback to get values at startup
138
+ OnConfigure ([&](const gz::sim::Entity& _worldEntity,
139
+ const std::shared_ptr<const sdf::Element>& /* _sdf*/ ,
140
+ gz::sim::EntityComponentManager& _ecm,
141
+ gz::sim::EventManager& /* _eventMgr*/ ) {
142
+ std::cerr << " ========== Configuring test" << std::endl;
143
+
144
+ gz::sim::World world (_worldEntity);
145
+
146
+ // Get model
147
+ auto modelEntity = world.ModelByName (_ecm, " coupled_pendulum" );
148
+ modelEntity = world.ModelByName (_ecm, " coupled_pendulum" );
149
+ EXPECT_NE (gz::sim::kNullEntity , modelEntity);
150
+ model = gz::sim::Model (modelEntity);
151
+
152
+ auto devicesKeys = DeviceRegistry::getHandler ()->getDevicesKeys (_ecm);
153
+ std::cerr << " Number of Devices: " << devicesKeys.size () << std::endl;
154
+ auto cbKey = devicesKeys.at (0 );
155
+ EXPECT_TRUE (DeviceRegistry::getHandler ()->getDevice (_ecm, devicesKeys[0 ], driver));
156
+ std::cerr << " Driver key: " << cbKey << std::endl;
157
+ ASSERT_TRUE (driver != nullptr );
158
+ iPositionDirectControl = nullptr ;
159
+ ASSERT_TRUE (driver->view (iPositionDirectControl));
160
+ iControlMode = nullptr ;
161
+ ASSERT_TRUE (driver->view (iControlMode));
162
+ iEncoders = nullptr ;
163
+ ASSERT_TRUE (driver->view (iEncoders));
164
+
165
+ // Get joint1
166
+ auto jointEntity0 = model.JointByName (_ecm, " upper_joint" );
167
+ EXPECT_NE (gz::sim::kNullEntity , jointEntity0);
168
+ joint0 = gz::sim::Joint (jointEntity0);
169
+
170
+ // Get joint2
171
+ auto jointEntity1 = model.JointByName (_ecm, " lower_joint" );
172
+ EXPECT_NE (gz::sim::kNullEntity , jointEntity1);
173
+ joint1 = gz::sim::Joint (jointEntity1);
174
+
175
+ // Set joint in torque control mode
176
+ ASSERT_TRUE (iControlMode->setControlMode (0 , VOCAB_CM_POSITION_DIRECT));
177
+ ASSERT_TRUE (iControlMode->setControlMode (1 , VOCAB_CM_POSITION_DIRECT));
178
+
179
+ // Print number of joint configured
180
+ int nJointsConfigured{};
181
+ ASSERT_TRUE (iPositionDirectControl->getAxes (&nJointsConfigured));
182
+ std::cerr << " Number of joints configured: " << nJointsConfigured << std::endl;
183
+
184
+ configured = true ;
185
+ std::cerr << " ========== Test configured" << std::endl;
186
+ });
187
+ }
188
+
189
+ // Get SDF model name from test parameter
190
+ gz::sim::TestFixture testFixture;
191
+ double linkMass{1 };
192
+ double linkLength{1.0 };
193
+ double linkInertiaAtLinkEnd{0.3352 }; // Computed with parallel axis theorem
194
+ int plannedIterations{5000 };
195
+ int iterations{0 };
196
+ std::vector<std::vector<double >> trackingErrors{2 };
197
+ double acceptedTolerance{5e-2 };
198
+ bool configured{false };
199
+ gz::math::Vector3d gravity;
200
+ gz::sim::Entity modelEntity;
201
+ gz::sim::Model model;
202
+ gz::sim::Joint joint0;
203
+ gz::sim::Joint joint1;
204
+ yarp::dev::PolyDriver* driver;
205
+ yarp::dev::IPositionDirect* iPositionDirectControl = nullptr ;
206
+ yarp::dev::IControlMode* iControlMode = nullptr ;
207
+ yarp::dev::IEncoders* iEncoders = nullptr ;
208
+ };
209
+
125
210
TEST_F (ControlBoardPositionDirectFixture, CheckPositionTrackingUsingPendulumModel)
126
211
{
127
212
// Generate ref trajectory
@@ -180,5 +265,72 @@ TEST_F(ControlBoardPositionDirectFixture, CheckPositionTrackingUsingPendulumMode
180
265
EXPECT_LT (avgTrackgingError, acceptedTolerance);
181
266
}
182
267
268
+ TEST_F (ControlBoardPositionDirectCoupledPendulumFixture, CheckPositionTrackingUsingCoupledPendulumModel)
269
+ {
270
+ // Generate ref trajectory
271
+ std::vector<double > refTrajectory (plannedIterations);
272
+ double amplitude = 100.0 ; // deg
273
+ for (int i = 0 ; i < plannedIterations; i++)
274
+ {
275
+ double t = static_cast <double >(i) / plannedIterations;
276
+ double value = amplitude * std::sin (gzyarp::pi * t);
277
+ refTrajectory[i] = value;
278
+ }
279
+
280
+ yarp::sig::Vector jointPositions{0.0 , 0.0 };
281
+
282
+ testFixture
283
+ .OnPreUpdate ([&](const gz::sim::UpdateInfo& _info, gz::sim::EntityComponentManager& _ecm) {
284
+ // Set ref position
285
+ iPositionDirectControl->setPosition (0 , refTrajectory[iterations]);
286
+ iPositionDirectControl->setPosition (1 , refTrajectory[iterations]);
287
+ })
288
+ .OnPostUpdate (
289
+ [&](const gz::sim::UpdateInfo& _info, const gz::sim::EntityComponentManager& _ecm) {
290
+ // std::cerr << "========== Iteration: " << iterations << std::endl;
291
+
292
+ iEncoders->getEncoders (jointPositions.data ());
293
+
294
+ // std::cerr << "ref position: " << refTrajectory[iterations] << std::endl;
295
+ // std::cerr << "joint position: " << jointPosition << std::endl;
296
+
297
+ // Tracking error
298
+ // EXPECT_NEAR(jointPosition, refTrajectory[iterations], acceptedTolerance);
299
+ trackingErrors[0 ].push_back (abs (refTrajectory[iterations] - jointPositions[0 ]));
300
+ trackingErrors[1 ].push_back (abs (refTrajectory[iterations] - jointPositions[1 ]));
301
+
302
+ iterations++;
303
+ })
304
+ .
305
+ // The moment we finalize, the configure callback is called
306
+ Finalize ();
307
+
308
+ int modeSet0{};
309
+ int modeSet1{};
310
+ ASSERT_TRUE (iControlMode->getControlMode (0 , &modeSet0));
311
+ ASSERT_TRUE (iControlMode->getControlMode (1 , &modeSet1));
312
+ ASSERT_TRUE (modeSet0 == VOCAB_CM_POSITION_DIRECT);
313
+ ASSERT_TRUE (modeSet1 == VOCAB_CM_POSITION_DIRECT);
314
+
315
+ // Setup simulation server, this will call the post-update callbacks.
316
+ // It also calls pre-update and update callbacks if those are being used.
317
+ testFixture.Server ()->Run (true , plannedIterations, false );
318
+ std::cerr << " Simulation completed" << std::endl;
319
+ // Final assertions
320
+ EXPECT_TRUE (configured);
321
+ // Verify that the post update function was called 1000 times
322
+ EXPECT_EQ (plannedIterations, iterations);
323
+ // Verify that the average tracking error is within the accepted tolerance
324
+ std::cerr << " Tracking error vector size: " << trackingErrors.size () << std::endl;
325
+ auto avgTrackgingError0 = std::accumulate (trackingErrors[0 ].begin (), trackingErrors[0 ].end (), 0.0 )
326
+ / trackingErrors[0 ].size ();
327
+ auto avgTrackgingError1 = std::accumulate (trackingErrors[1 ].begin (), trackingErrors[1 ].end (), 0.0 )
328
+ / trackingErrors[1 ].size ();
329
+ std::cerr << " Average tracking error joint 0: " << avgTrackgingError0 << std::endl;
330
+ std::cerr << " Average tracking error joint 1: " << avgTrackgingError1 << std::endl;
331
+ EXPECT_LT (avgTrackgingError0, acceptedTolerance);
332
+ EXPECT_LT (avgTrackgingError1, acceptedTolerance);
333
+ }
334
+
183
335
} // namespace test
184
336
} // namespace gzyarp
0 commit comments