Skip to content

Commit cc1fa5a

Browse files
committed
Merge branch 'wide_angle_camera' of github.com:ignitionrobotics/ign-gazebo into wide_angle_camera
2 parents d6a39d0 + 303a6e8 commit cc1fa5a

File tree

4 files changed

+288
-40
lines changed

4 files changed

+288
-40
lines changed

src/systems/thruster/Thruster.cc

Lines changed: 122 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ using namespace systems;
4545

4646
class ignition::gazebo::systems::ThrusterPrivateData
4747
{
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+
4858
/// \brief Mutex for read/write access to class
4959
public: std::mutex mtx;
5060

@@ -55,25 +65,28 @@ class ignition::gazebo::systems::ThrusterPrivateData
5565
public: double propellerAngVel = 0.0;
5666

5767
/// \brief The link entity which will spin
58-
public: ignition::gazebo::Entity linkEntity;
68+
public: Entity linkEntity;
5969

6070
/// \brief Axis along which the propeller spins. Expressed in the joint
6171
/// frame. Addume this doesn't change during simulation.
62-
public: ignition::math::Vector3d jointAxis;
72+
public: math::Vector3d jointAxis;
6373

6474
/// \brief Joint pose in the child link frame. Assume this doesn't change
6575
/// during the simulation.
6676
public: math::Pose3d jointPose;
6777

6878
/// \brief Propeller koint entity
69-
public: ignition::gazebo::Entity jointEntity;
79+
public: Entity jointEntity;
7080

7181
/// \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;
7386

7487
/// \brief The PID which controls the propeller. This isn't used if
7588
/// velocityControl is true.
76-
public: ignition::math::PID propellerController;
89+
public: math::PID propellerController;
7790

7891
/// \brief Velocity Control mode - this disables the propellerController
7992
/// and writes the angular velocity directly to the joint. default: false
@@ -100,10 +113,18 @@ class ignition::gazebo::systems::ThrusterPrivateData
100113
/// \brief callback for handling thrust update
101114
public: void OnCmdThrust(const ignition::msgs::Double &_msg);
102115

116+
/// \brief callback for handling angular velocity update
117+
public: void OnCmdAngVel(const ignition::msgs::Double &_msg);
118+
103119
/// \brief function which computes angular velocity from thrust
104120
/// \param[in] _thrust Thrust in N
105121
/// \return Angular velocity in rad/s
106122
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);
107128
};
108129

109130
/////////////////////////////////////////////////
@@ -158,6 +179,14 @@ void Thruster::Configure(
158179
this->dataPtr->fluidDensity = _sdf->Get<double>("fluid_density");
159180
}
160181

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+
161190
this->dataPtr->jointEntity = model.JointByName(_ecm, jointName);
162191
if (kNullEntity == this->dataPtr->jointEntity)
163192
{
@@ -173,34 +202,65 @@ void Thruster::Configure(
173202
this->dataPtr->jointPose = _ecm.Component<components::Pose>(
174203
this->dataPtr->jointEntity)->Data();
175204

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-
198205
// Get link entity
199206
auto childLink =
200207
_ecm.Component<ignition::gazebo::components::ChildLinkName>(
201208
this->dataPtr->jointEntity);
202209
this->dataPtr->linkEntity = model.LinkByName(_ecm, childLink->Data());
203210

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+
204264
// Create necessary components if not present.
205265
enableComponent<components::AngularVelocity>(_ecm, this->dataPtr->linkEntity);
206266
enableComponent<components::WorldAngularVelocity>(_ecm,
@@ -265,6 +325,19 @@ void ThrusterPrivateData::OnCmdThrust(const ignition::msgs::Double &_msg)
265325
this->propellerAngVel = this->ThrustToAngularVec(this->thrust);
266326
}
267327

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+
268341
/////////////////////////////////////////////////
269342
double ThrusterPrivateData::ThrustToAngularVec(double _thrust)
270343
{
@@ -280,6 +353,15 @@ double ThrusterPrivateData::ThrustToAngularVec(double _thrust)
280353
return propAngularVelocity;
281354
}
282355

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+
283365
/////////////////////////////////////////////////
284366
void Thruster::PreUpdate(
285367
const ignition::gazebo::UpdateInfo &_info,
@@ -307,6 +389,7 @@ void Thruster::PreUpdate(
307389
desiredPropellerAngVel = this->dataPtr->propellerAngVel;
308390
}
309391

392+
msgs::Double angvel;
310393
// PID control
311394
double torque = 0.0;
312395
if (!this->dataPtr->velocityControl)
@@ -318,6 +401,7 @@ void Thruster::PreUpdate(
318401
torque = this->dataPtr->propellerController.Update(angularError,
319402
_info.dt);
320403
}
404+
angvel.set_data(currentAngular);
321405
}
322406
// Velocity control
323407
else
@@ -334,8 +418,19 @@ void Thruster::PreUpdate(
334418
{
335419
velocityComp->Data()[0] = desiredPropellerAngVel;
336420
}
421+
angvel.set_data(desiredPropellerAngVel);
337422
}
338423

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+
}
339434
// Force: thrust
340435
// Torque: propeller rotation, if using PID
341436
link.AddWorldWrench(
@@ -350,4 +445,3 @@ IGNITION_ADD_PLUGIN(
350445
Thruster::ISystemPreUpdate)
351446

352447
IGNITION_ADD_PLUGIN_ALIAS(Thruster, "ignition::gazebo::systems::Thruster")
353-

src/systems/thruster/Thruster.hh

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,28 @@ namespace systems
3434

3535
/// \brief This plugin simulates a maritime thruster for
3636
/// boats and underwater vehicles. It uses the equations described in Fossen's
37-
/// "Guidance and Control of Ocean Vehicles" in page 246. This plugin takes in
37+
/// "Guidance and Control of Ocean Vehicles" in page 246. This plugin has two
38+
/// modes of operation. In the default mode it takes in a
3839
/// force in Newtons and applies it to the thruster. It also calculates the
3940
/// theoretical angular velocity of the blades and spins them accordingly.
41+
/// Alternatively, one may send angular velocity commands to calculate the
42+
/// force to be applied using the said equation. In the default mode the
43+
/// plugin will publish angular velocity in radians per second on
44+
/// `/model/{ns}/joint/{joint_name}/ang_vel` as an ignition.msgs.double. If
45+
/// <use_angvel_cmd> is set to true it publishes force in Newtons instead to
46+
/// `/model/{ns}/joint/{joint_name}/force`.
4047
///
4148
/// ## System Parameters
4249
/// - <namespace> - The namespace in which the robot exists. The plugin will
43-
/// listen on the topic `/model/{namespace}/joint/{joint_name}/cmd_thrust`.
50+
/// listen on the topic `/model/{namespace}/joint/{joint_name}/cmd_thrust`or
51+
/// `/model/{namespace}/joint/{joint_name}/cmd_vel` depending on the mode of
52+
/// operation.
4453
/// [Optional]
4554
/// - <joint_name> - This is the joint in the model which corresponds to the
4655
/// propeller. [Required]
56+
/// - <use_angvel_cmd> - Default false, if set to true will make the thruster
57+
/// plugin accept commands in angular velocity in radians per seconds in
58+
/// terms of newtons. [Optional, Boolean, Default True]
4759
/// - <fluid_density> - The fluid density of the liquid in which the thruster
4860
/// is operating in. [Optional, kg/m^3, defaults to 1000 kg/m^3]
4961
/// - <propeller_diameter> - The diameter of the propeller in meters.

test/integration/thruster.cc

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,19 @@ class ThrusterTest : public InternalFixture<::testing::Test>
4646
/// \param[in] _density Fluid density
4747
/// \param[in] _diameter Propeller diameter
4848
/// \param[in] _baseTol Base tolerance for most quantities
49+
/// \param[in] _useAngVelCmd Send commands in angular velocity instead of
50+
/// force
51+
/// \param[in] _mass Mass of the body being propelled.
4952
public: void TestWorld(const std::string &_world,
5053
const std::string &_namespace, double _coefficient, double _density,
51-
double _diameter, double _baseTol);
54+
double _diameter, double _baseTol, bool _useAngVelCmd = false,
55+
double _mass = 100.1);
5256
};
5357

5458
//////////////////////////////////////////////////
5559
void ThrusterTest::TestWorld(const std::string &_world,
5660
const std::string &_namespace, double _coefficient, double _density,
57-
double _diameter, double _baseTol)
61+
double _diameter, double _baseTol, bool _useAngVelCmd, double _mass)
5862
{
5963
// Start server
6064
ServerConfig serverConfig;
@@ -121,8 +125,17 @@ void ThrusterTest::TestWorld(const std::string &_world,
121125

122126
// Publish command and check that vehicle moved
123127
transport::Node node;
128+
std::string cmdTopic;
129+
if (!_useAngVelCmd)
130+
{
131+
cmdTopic = "/model/" + _namespace + "/joint/propeller_joint/cmd_thrust";
132+
}
133+
else
134+
{
135+
cmdTopic = "/model/" + _namespace + "/joint/propeller_joint/cmd_vel";
136+
}
124137
auto pub = node.Advertise<msgs::Double>(
125-
"/model/" + _namespace + "/joint/propeller_joint/cmd_thrust");
138+
cmdTopic);
126139

127140
int sleep{0};
128141
int maxSleep{30};
@@ -134,8 +147,21 @@ void ThrusterTest::TestWorld(const std::string &_world,
134147
EXPECT_TRUE(pub.HasConnections());
135148

136149
double force{300.0};
150+
151+
// See Thor I Fossen's "Guidance and Control of ocean vehicles" p. 246
152+
// omega = sqrt(thrust /
153+
// (fluid_density * thrust_coefficient * propeller_diameter ^ 4))
154+
auto omega = sqrt(force / (_density * _coefficient * pow(_diameter, 4)));
155+
137156
msgs::Double msg;
138-
msg.set_data(force);
157+
if(!_useAngVelCmd)
158+
{
159+
msg.set_data(force);
160+
}
161+
else
162+
{
163+
msg.set_data(omega);
164+
}
139165
pub.Publish(msg);
140166

141167
// Check movement
@@ -155,13 +181,12 @@ void ThrusterTest::TestWorld(const std::string &_world,
155181
// s = a * t^2 / 2
156182
// F = m * 2 * s / t^2
157183
// s = F * t^2 / 2m
158-
double mass{100.1};
159184
double xTol{1e-2};
160185
for (unsigned int i = 0; i < modelPoses.size(); ++i)
161186
{
162187
auto pose = modelPoses[i];
163188
auto time = dt * i;
164-
EXPECT_NEAR(force * time * time / (2 * mass), pose.Pos().X(), xTol);
189+
EXPECT_NEAR(force * time * time / (2 * _mass), pose.Pos().X(), xTol);
165190
EXPECT_NEAR(0.0, pose.Pos().Y(), _baseTol);
166191
EXPECT_NEAR(0.0, pose.Pos().Z(), _baseTol);
167192
EXPECT_NEAR(0.0, pose.Rot().Pitch(), _baseTol);
@@ -175,10 +200,6 @@ void ThrusterTest::TestWorld(const std::string &_world,
175200
EXPECT_NEAR(0.0, pose.Rot().Roll(), _baseTol);
176201
}
177202

178-
// See Thor I Fossen's "Guidance and Control of ocean vehicles" p. 246
179-
// omega = sqrt(thrust /
180-
// (fluid_density * thrust_coefficient * propeller_diameter ^ 4))
181-
auto omega = sqrt(force / (_density * _coefficient * pow(_diameter, 4)));
182203
double omegaTol{1e-1};
183204
for (unsigned int i = 0; i < propellerAngVels.size(); ++i)
184205
{
@@ -193,6 +214,16 @@ void ThrusterTest::TestWorld(const std::string &_world,
193214
}
194215
}
195216

217+
/////////////////////////////////////////////////
218+
TEST_F(ThrusterTest, AngVelCmdControl)
219+
{
220+
auto world = common::joinPaths(std::string(PROJECT_SOURCE_PATH),
221+
"test", "worlds", "thruster_ang_vel_cmd.sdf");
222+
223+
// Tolerance is high because the joint command disturbs the vehicle body
224+
this->TestWorld(world, "custom", 0.005, 950, 0.2, 1e-2, true, 100.01);
225+
}
226+
196227
/////////////////////////////////////////////////
197228
TEST_F(ThrusterTest, PIDControl)
198229
{

0 commit comments

Comments
 (0)