buoyancy_gazebo_plugin.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2015 Open Source Robotics Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16 */
17 
18 #include <functional>
19 #include <string>
20 #include <gazebo/common/Assert.hh>
21 #include <gazebo/common/Events.hh>
23 
24 using namespace asv;
25 using namespace gazebo;
26 using namespace gazebo::buoyancy;
27 using namespace ::buoyancy;
28 
30 BuoyancyObject::BuoyancyObject()
31  : linkId(-1),
32  linkName(""),
33  pose(0, 0, 0, 0, 0, 0),
34  mass(0.0),
35  shape(nullptr)
36 {
37 }
38 
41  : linkId(obj.linkId),
42  linkName(obj.linkName),
43  pose(obj.pose),
44  mass(obj.mass),
45  shape(std::move(obj.shape))
46 {
47 }
48 
50 void BuoyancyObject::Load(const physics::ModelPtr model,
51  const sdf::ElementPtr elem)
52 {
53  // parse link
54  if (elem->HasElement("link_name"))
55  {
56  this->linkName = elem->GetElement("link_name")->Get<std::string>();
57  physics::LinkPtr link = model->GetLink(linkName);
58  if (!link)
59  {
60  throw ParseException("link_name", "invalid link name");
61  }
62  this->linkId = link->GetId();
63  }
64  else
65  {
66  throw ParseException("link_name", "missing element");
67  }
68 
69  // parse pose (optional)
70  if (elem->HasElement("pose"))
71  {
72  this->pose = elem->GetElement("pose")->Get<ignition::math::Pose3d>();
73  }
74 
75  // parse geometry
76  if (elem->HasElement("geometry"))
77  {
78  sdf::ElementPtr geometry = elem->GetElement("geometry");
79  try
80  {
81  this->shape = std::move(ShapeVolume::makeShape(geometry));
82  }
83  catch (...)
84  {
85  throw;
86  }
87  }
88  else
89  {
90  throw ParseException("geometry", "missing element");
91  }
92 }
93 
95 std::string BuoyancyObject::Disp() {
96  std::stringstream ss;
97  ss << "Buoyancy object\n"
98  << "\tlink: " << linkName << "[" << linkId << "]\n"
99  << "\tpose: " << pose << '\n'
100  << "\tgeometry " << shape->Display() << '\n'
101  << "\tmass " << mass;
102  return ss.str();
103 }
104 
107  : fluidDensity(997),
108  fluidLevel(0.0),
109  linearDrag(0.0),
110  angularDrag(0.0),
111  waveModelName(""),
112  lastSimTime(0.0)
113 {
114 }
115 
117 void BuoyancyPlugin::Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
118 {
119  GZ_ASSERT(_model != nullptr, "Received NULL model pointer");
120  GZ_ASSERT(_sdf != nullptr, "Received NULL SDF pointer");
121 
122  // Capture the model and world pointers.
123  this->model = _model;
124  this->world = this->model->GetWorld();
125 
126  // Capture the wave model
127  if (_sdf->HasElement("wave_model"))
128  {
129  this->waveModelName = _sdf->Get<std::string>("wave_model");
130  }
131  this->waveParams = nullptr;
132 
133  if (_sdf->HasElement("fluid_density"))
134  {
135  this->fluidDensity = _sdf->Get<double>("fluid_density");
136  }
137  if (_sdf->HasElement("fluid_level"))
138  {
139  this->fluidLevel = _sdf->Get<double>("fluid_level");
140  }
141  if (_sdf->HasElement("linear_drag"))
142  {
143  this->linearDrag = _sdf->Get<double>("linear_drag");
144  }
145  if (_sdf->HasElement("angular_drag"))
146  {
147  this->angularDrag = _sdf->Get<double>("angular_drag");
148  }
149 
150  if (_sdf->HasElement("buoyancy"))
151  {
152  gzmsg << "Found buoyancy element(s), looking at each element..."
153  << std::endl;
154  for (sdf::ElementPtr buoyancyElem = _sdf->GetElement("buoyancy");
155  buoyancyElem;
156  buoyancyElem = buoyancyElem->GetNextElement("buoyancy")) {
157  try
158  {
159  BuoyancyObject buoyObj = BuoyancyObject();
160  buoyObj.Load(_model, buoyancyElem);
161 
162  // add link to linkMap if it is not in the map
163  if (this->linkMap.find(buoyObj.linkId) == this->linkMap.end())
164  {
165  this->linkMap[buoyObj.linkId] = _model->GetLink(buoyObj.linkName);
166  // initialize link height
167  if (!this->waveModelName.empty())
168  {
169  this->linkHeights[linkMap[buoyObj.linkId]] = this->fluidLevel;
170  }
171  else
172  {
173  this->linkHeights[linkMap[buoyObj.linkId]] = 0;
174  }
175  // initialize link height velocity
176  this->linkHeightDots[linkMap[buoyObj.linkId]] = 0;
177  }
178 
179  // get mass
180  #if GAZEBO_MAJOR_VERSION >= 8
181  buoyObj.mass = this->linkMap[buoyObj.linkId]->GetInertial()->Mass();
182  #else
183  buoyObj.mass =
184  this->linkMap[buoyObj.linkId]->GetInertial()->GetMass();
185  #endif
186 
187  // add buoyancy object to list and display stats
188  gzmsg << buoyObj.Disp() << std::endl;
189  buoyancyObjects.push_back(std::move(buoyObj));
190  }
191  catch (const std::exception& e)
192  {
193  gzwarn << e.what() << std::endl;
194  }
195  }
196  }
197 
198  // Initialize sim time memory
199  #if GAZEBO_MAJOR_VERSION >= 8
200  this->lastSimTime = this->world->SimTime().Double();
201  #else
202  this->lastSimTime = this->world->GetSimTime().Double();
203  #endif
204 }
205 
208 {
209  this->updateConnection = event::Events::ConnectWorldUpdateBegin(
210  std::bind(&BuoyancyPlugin::OnUpdate, this));
211 }
212 
215 {
216  // update wave height if wave model specified
217  if (!this->waveModelName.empty())
218  {
219  // If we haven't yet, retrieve the wave parameters from ocean model plugin.
220  if (!this->waveParams)
221  {
222  gzmsg << "usv_gazebo_dynamics_plugin: waveParams is null. "
223  << "Trying to get wave parameters from ocean model" << std::endl;
224  this->waveParams = WavefieldModelPlugin::GetWaveParams(
225  this->world, this->waveModelName);
226  }
227 
228  #if GAZEBO_MAJOR_VERSION >= 8
229  double simTime = this->world->SimTime().Double();
230  #else
231  double simTime = this->world->GetSimTime().Double();
232  #endif
233 
234  double dt = simTime - this->lastSimTime;
235  this->lastSimTime = simTime;
236 
237  // get wave height for each link
238  for (auto& link : this->linkMap) {
239  auto linkPtr = link.second;
240  #if GAZEBO_MAJOR_VERSION >= 8
241  ignition::math::Pose3d linkFrame = linkPtr->WorldPose();
242  #else
243  ignition::math::Pose3d linkFrame = linkPtr->GetWorldPose().Ign();
244  #endif
245 
246  // Compute the wave displacement at the centre of the link frame.
247  // Wave field height at the link, relative to the mean water level.
248  double waveHeight = WavefieldSampler::ComputeDepthSimply(
249  *waveParams, linkFrame.Pos(), simTime);
250 
251  this->linkHeightDots[linkPtr] =
252  (waveHeight - this->linkHeights[linkPtr]) / dt;
253  this->linkHeights[linkPtr] = waveHeight;
254  }
255  }
256 
257  for (auto& buoyancyObj : this->buoyancyObjects)
258  {
259  auto link = this->linkMap[buoyancyObj.linkId];
260  #if GAZEBO_MAJOR_VERSION >= 8
261  ignition::math::Pose3d linkFrame = link->WorldPose();
262  #else
263  ignition::math::Pose3d linkFrame = link->GetWorldPose().Ign();
264  #endif
265  linkFrame = linkFrame * buoyancyObj.pose;
266 
267  auto submergedVolume = buoyancyObj.shape->CalculateVolume(linkFrame,
268  this->linkHeights[link] + this->fluidLevel);
269 
270  GZ_ASSERT(submergedVolume.volume >= 0,
271  "Non-positive volume found in volume properties!");
272 
273  // calculate buoyancy and drag forces
274  if (submergedVolume.volume > 1e-6)
275  {
276  // By Archimedes' principle,
277  // buoyancy = -(mass*gravity)*fluid_density/object_density
278  // object_density = mass/volume, so the mass term cancels.
279  ignition::math::Vector3d buoyancy = -this->fluidDensity
280  * submergedVolume.volume * model->GetWorld()->Gravity();
281 
282  #if GAZEBO_MAJOR_VERSION >= 8
283  ignition::math::Vector3d linVel = link->WorldLinearVel();
284  ignition::math::Vector3d angVel = link->RelativeAngularVel();
285  #else
286  ignition::math::Vector3d linVel= link->GetWorldLinearVel().Ign();
287  ignition::math::Vector3d angVel = link->GetRelativeAngularVel().Ign();
288  #endif
289 
290  // partial mass = total_mass * submerged_vol / total_vol
291  float partialMass = buoyancyObj.mass * submergedVolume.volume
292  / buoyancyObj.shape->volume;
293 
294  // drag (based on Exact Buoyancy for Polyhedra by Eric Catto)
295  // linear drag
296  ignition::math::Vector3d relVel =
297  ignition::math::Vector3d(0, 0, this->linkHeightDots[link]) - linVel;
298  ignition::math::Vector3d dragForce = linearDrag * partialMass * relVel;
299  buoyancy += dragForce;
300  if (buoyancy.Z() < 0.0)
301  {
302  buoyancy.Z() = 0.0;
303  }
304  // apply force
305  link->AddForceAtWorldPosition(buoyancy, submergedVolume.centroid);
306 
307  // drag torque
308  double averageLength2 = ::pow(buoyancyObj.shape->averageLength, 2);
309  ignition::math::Vector3d dragTorque = (-partialMass * angularDrag
310  * averageLength2) * angVel;
311  link->AddRelativeTorque(dragTorque);
312  }
313  }
314 }
315 
ignition::math::Pose3d pose
Pose of buoyancy relative to link.
double fluidLevel
The height of the fluid/air interface [m]. Defaults to 0.
This plugin simulates buoyancy of an object in fluid. <wave_model>: Name of the wave model object (op...
void Load(const physics::ModelPtr model, const sdf::ElementPtr elem)
Load buoyancy object from SDF.
std::vector< buoyancy::BuoyancyObject > buoyancyObjects
List of buoyancy objects for model.
::buoyancy::ShapeVolumePtr shape
Buoyancy object&#39;s shape properties.
GZ_REGISTER_MODEL_PLUGIN(AcousticPinger)
std::map< gazebo::physics::LinkPtr, double > linkHeightDots
Map of water velocity at each link.
std::map< gazebo::physics::LinkPtr, double > linkHeights
Map of water height at each link from previous timestep.
std::string Disp()
Display string for buoyancy object.
virtual void OnUpdate()
Callback for World Update events.
std::shared_ptr< const asv::WaveParameters > waveParams
The wave parameters.
std::map< int, gazebo::physics::LinkPtr > linkMap
Map of <link ID, link pointer>
double lastSimTime
Previous update time.
double angularDrag
Angular drag coefficient. Defaults to 0.
double mass
Object mass (from inertial elem)
virtual void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
std::string linkName
Associated link name.
std::string waveModelName
The name of the wave model.
event::ConnectionPtr updateConnection
Connection to World Update events.
double fluidDensity
The density of the fluid in which the object is submerged in kg/m^3. Defaults to 1000, the fluid density of water at 15 Celsius.
double linearDrag
Linear drag coefficient. Defaults to 0.
physics::ModelPtr model
Pointer to base model.
A class for storing buoyancy object properties.
physics::WorldPtr world
Pointer to the Gazebo world Retrieved when the model is loaded.


usv_gazebo_plugins
Author(s): Brian Bingham , Carlos Aguero
autogenerated on Thu May 7 2020 03:54:47