@@ -45,6 +45,16 @@ using namespace systems;
45
45
46
46
class ignition ::gazebo::systems::ThrusterPrivateData
47
47
{
48
+ // / \brief The mode of operation
49
+ public: enum OperationMode {
50
+ // / \brief Takes in a force commmand and spins the propeller at an
51
+ // / appropriate rate.
52
+ ForceCmd = 0 ,
53
+ // / \brief Takes in angular velocity commands in radians per second and
54
+ // / calculates the appropriate force.
55
+ AngVelCmd
56
+ } opmode = OperationMode::ForceCmd;
57
+
48
58
// / \brief Mutex for read/write access to class
49
59
public: std::mutex mtx;
50
60
@@ -55,25 +65,28 @@ class ignition::gazebo::systems::ThrusterPrivateData
55
65
public: double propellerAngVel = 0.0 ;
56
66
57
67
// / \brief The link entity which will spin
58
- public: ignition::gazebo:: Entity linkEntity;
68
+ public: Entity linkEntity;
59
69
60
70
// / \brief Axis along which the propeller spins. Expressed in the joint
61
71
// / frame. Addume this doesn't change during simulation.
62
- public: ignition:: math::Vector3d jointAxis;
72
+ public: math::Vector3d jointAxis;
63
73
64
74
// / \brief Joint pose in the child link frame. Assume this doesn't change
65
75
// / during the simulation.
66
76
public: math::Pose3d jointPose;
67
77
68
78
// / \brief Propeller koint entity
69
- public: ignition::gazebo:: Entity jointEntity;
79
+ public: Entity jointEntity;
70
80
71
81
// / \brief ignition node for handling transport
72
- public: ignition::transport::Node node;
82
+ public: transport::Node node;
83
+
84
+ // / \brief Publisher for feedback of data
85
+ public: transport::Node::Publisher pub;
73
86
74
87
// / \brief The PID which controls the propeller. This isn't used if
75
88
// / velocityControl is true.
76
- public: ignition:: math::PID propellerController;
89
+ public: math::PID propellerController;
77
90
78
91
// / \brief Velocity Control mode - this disables the propellerController
79
92
// / and writes the angular velocity directly to the joint. default: false
@@ -100,10 +113,18 @@ class ignition::gazebo::systems::ThrusterPrivateData
100
113
// / \brief callback for handling thrust update
101
114
public: void OnCmdThrust (const ignition::msgs::Double &_msg);
102
115
116
+ // / \brief callback for handling angular velocity update
117
+ public: void OnCmdAngVel (const ignition::msgs::Double &_msg);
118
+
103
119
// / \brief function which computes angular velocity from thrust
104
120
// / \param[in] _thrust Thrust in N
105
121
// / \return Angular velocity in rad/s
106
122
public: double ThrustToAngularVec (double _thrust);
123
+
124
+ // / \brief function which computers thrust from angular velocity
125
+ // / \param[in] _angVel Angular Velocity in rad/s
126
+ // / \return Thrust in Newtons
127
+ public: double AngularVelToThrust (double _angVel);
107
128
};
108
129
109
130
// ///////////////////////////////////////////////
@@ -158,6 +179,14 @@ void Thruster::Configure(
158
179
this ->dataPtr ->fluidDensity = _sdf->Get <double >(" fluid_density" );
159
180
}
160
181
182
+ // Get the operation mode
183
+ if (_sdf->HasElement (" use_angvel_cmd" ))
184
+ {
185
+ this ->dataPtr ->opmode = _sdf->Get <bool >(" use_angvel_cmd" ) ?
186
+ ThrusterPrivateData::OperationMode::AngVelCmd :
187
+ ThrusterPrivateData::OperationMode::ForceCmd;
188
+ }
189
+
161
190
this ->dataPtr ->jointEntity = model.JointByName (_ecm, jointName);
162
191
if (kNullEntity == this ->dataPtr ->jointEntity )
163
192
{
@@ -173,34 +202,65 @@ void Thruster::Configure(
173
202
this ->dataPtr ->jointPose = _ecm.Component <components::Pose>(
174
203
this ->dataPtr ->jointEntity )->Data ();
175
204
176
- // Keeping cmd_pos for backwards compatibility
177
- // TODO(chapulina) Deprecate cmd_pos, because the commands aren't positions
178
- std::string thrusterTopicOld = ignition::transport::TopicUtils::AsValidTopic (
179
- " /model/" + ns + " /joint/" + jointName + " /cmd_pos" );
180
-
181
- this ->dataPtr ->node .Subscribe (
182
- thrusterTopicOld,
183
- &ThrusterPrivateData::OnCmdThrust,
184
- this ->dataPtr .get ());
185
-
186
- // Subscribe to force commands
187
- std::string thrusterTopic = ignition::transport::TopicUtils::AsValidTopic (
188
- " /model/" + ns + " /joint/" + jointName + " /cmd_thrust" );
189
-
190
- this ->dataPtr ->node .Subscribe (
191
- thrusterTopic,
192
- &ThrusterPrivateData::OnCmdThrust,
193
- this ->dataPtr .get ());
194
-
195
- ignmsg << " Thruster listening to commands in [" << thrusterTopic << " ]"
196
- << std::endl;
197
-
198
205
// Get link entity
199
206
auto childLink =
200
207
_ecm.Component <ignition::gazebo::components::ChildLinkName>(
201
208
this ->dataPtr ->jointEntity );
202
209
this ->dataPtr ->linkEntity = model.LinkByName (_ecm, childLink->Data ());
203
210
211
+ if (this ->dataPtr ->opmode == ThrusterPrivateData::OperationMode::ForceCmd)
212
+ {
213
+ // Keeping cmd_pos for backwards compatibility
214
+ // TODO(chapulina) Deprecate cmd_pos, because the commands aren't positions
215
+ std::string thrusterTopicOld =
216
+ ignition::transport::TopicUtils::AsValidTopic (
217
+ " /model/" + ns + " /joint/" + jointName + " /cmd_pos" );
218
+
219
+ this ->dataPtr ->node .Subscribe (
220
+ thrusterTopicOld,
221
+ &ThrusterPrivateData::OnCmdThrust,
222
+ this ->dataPtr .get ());
223
+
224
+ // Subscribe to force commands
225
+ std::string thrusterTopic = ignition::transport::TopicUtils::AsValidTopic (
226
+ " /model/" + ns + " /joint/" + jointName + " /cmd_thrust" );
227
+
228
+ this ->dataPtr ->node .Subscribe (
229
+ thrusterTopic,
230
+ &ThrusterPrivateData::OnCmdThrust,
231
+ this ->dataPtr .get ());
232
+
233
+ ignmsg << " Thruster listening to commands in [" << thrusterTopic << " ]"
234
+ << std::endl;
235
+
236
+ std::string feedbackTopic = ignition::transport::TopicUtils::AsValidTopic (
237
+ " /model/" + ns + " /joint/" + jointName + " /ang_vel" );
238
+ this ->dataPtr ->pub = this ->dataPtr ->node .Advertise <msgs::Double>(
239
+ feedbackTopic
240
+ );
241
+ }
242
+ else
243
+ {
244
+ igndbg << " Using angular velocity mode" << std::endl;
245
+ // Subscribe to angvel commands
246
+ std::string thrusterTopic = ignition::transport::TopicUtils::AsValidTopic (
247
+ " /model/" + ns + " /joint/" + jointName + " /cmd_vel" );
248
+
249
+ this ->dataPtr ->node .Subscribe (
250
+ thrusterTopic,
251
+ &ThrusterPrivateData::OnCmdAngVel,
252
+ this ->dataPtr .get ());
253
+
254
+ ignmsg << " Thruster listening to commands in [" << thrusterTopic << " ]"
255
+ << std::endl;
256
+
257
+ std::string feedbackTopic = ignition::transport::TopicUtils::AsValidTopic (
258
+ " /model/" + ns + " /joint/" + jointName + " /force" );
259
+ this ->dataPtr ->pub = this ->dataPtr ->node .Advertise <msgs::Double>(
260
+ feedbackTopic
261
+ );
262
+ }
263
+
204
264
// Create necessary components if not present.
205
265
enableComponent<components::AngularVelocity>(_ecm, this ->dataPtr ->linkEntity );
206
266
enableComponent<components::WorldAngularVelocity>(_ecm,
@@ -265,6 +325,19 @@ void ThrusterPrivateData::OnCmdThrust(const ignition::msgs::Double &_msg)
265
325
this ->propellerAngVel = this ->ThrustToAngularVec (this ->thrust );
266
326
}
267
327
328
+ // ///////////////////////////////////////////////
329
+ void ThrusterPrivateData::OnCmdAngVel (const ignition::msgs::Double &_msg)
330
+ {
331
+ std::lock_guard<std::mutex> lock (mtx);
332
+ this ->propellerAngVel =
333
+ ignition::math::clamp (ignition::math::fixnan (_msg.data ()),
334
+ this ->cmdMin , this ->cmdMax );
335
+
336
+ // Thrust is proportional to the Rotation Rate squared
337
+ // See Thor I Fossen's "Guidance and Control of ocean vehicles" p. 246
338
+ this ->thrust = this ->AngularVelToThrust (this ->propellerAngVel );
339
+ }
340
+
268
341
// ///////////////////////////////////////////////
269
342
double ThrusterPrivateData::ThrustToAngularVec (double _thrust)
270
343
{
@@ -280,6 +353,15 @@ double ThrusterPrivateData::ThrustToAngularVec(double _thrust)
280
353
return propAngularVelocity;
281
354
}
282
355
356
+ // ///////////////////////////////////////////////
357
+ double ThrusterPrivateData::AngularVelToThrust (double _angVel)
358
+ {
359
+ // Thrust is proportional to the Rotation Rate squared
360
+ // See Thor I Fossen's "Guidance and Control of ocean vehicles" p. 246
361
+ return this ->thrustCoefficient * pow (this ->propellerDiameter , 4 )
362
+ * abs (_angVel) * _angVel * this ->fluidDensity ;
363
+ }
364
+
283
365
// ///////////////////////////////////////////////
284
366
void Thruster::PreUpdate (
285
367
const ignition::gazebo::UpdateInfo &_info,
@@ -307,6 +389,7 @@ void Thruster::PreUpdate(
307
389
desiredPropellerAngVel = this ->dataPtr ->propellerAngVel ;
308
390
}
309
391
392
+ msgs::Double angvel;
310
393
// PID control
311
394
double torque = 0.0 ;
312
395
if (!this ->dataPtr ->velocityControl )
@@ -318,6 +401,7 @@ void Thruster::PreUpdate(
318
401
torque = this ->dataPtr ->propellerController .Update (angularError,
319
402
_info.dt );
320
403
}
404
+ angvel.set_data (currentAngular);
321
405
}
322
406
// Velocity control
323
407
else
@@ -334,8 +418,19 @@ void Thruster::PreUpdate(
334
418
{
335
419
velocityComp->Data ()[0 ] = desiredPropellerAngVel;
336
420
}
421
+ angvel.set_data (desiredPropellerAngVel);
337
422
}
338
423
424
+ if (this ->dataPtr ->opmode == ThrusterPrivateData::OperationMode::ForceCmd)
425
+ {
426
+ this ->dataPtr ->pub .Publish (angvel);
427
+ }
428
+ else
429
+ {
430
+ msgs::Double force;
431
+ force.set_data (desiredThrust);
432
+ this ->dataPtr ->pub .Publish (force);
433
+ }
339
434
// Force: thrust
340
435
// Torque: propeller rotation, if using PID
341
436
link.AddWorldWrench (
@@ -350,4 +445,3 @@ IGNITION_ADD_PLUGIN(
350
445
Thruster::ISystemPreUpdate)
351
446
352
447
IGNITION_ADD_PLUGIN_ALIAS(Thruster, " ignition::gazebo::systems::Thruster" )
353
-
0 commit comments