gazebo_wind_plugin.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2015 Fadri Furrer, ASL, ETH Zurich, Switzerland
3  * Copyright 2015 Michael Burri, ASL, ETH Zurich, Switzerland
4  * Copyright 2015 Mina Kamel, ASL, ETH Zurich, Switzerland
5  * Copyright 2015 Janosch Nikolic, ASL, ETH Zurich, Switzerland
6  * Copyright 2015 Markus Achtelik, ASL, ETH Zurich, Switzerland
7  * Copyright 2016 Geoffrey Hunter <gbmhunter@gmail.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
23 
24 #include <fstream>
25 #include <math.h>
26 
27 #include "ConnectGazeboToRosTopic.pb.h"
28 
29 namespace gazebo {
30 
32 
33 }
34 
35 void GazeboWindPlugin::Load(physics::ModelPtr _model, sdf::ElementPtr _sdf) {
36  if (kPrintOnPluginLoad) {
37  gzdbg << __FUNCTION__ << "() called." << std::endl;
38  }
39 
40  // Store the pointer to the model.
41  model_ = _model;
42  world_ = model_->GetWorld();
43 
44  double wind_gust_start = kDefaultWindGustStart;
45  double wind_gust_duration = kDefaultWindGustDuration;
46 
47  //==============================================//
48  //========== READ IN PARAMS FROM SDF ===========//
49  //==============================================//
50 
51  if (_sdf->HasElement("robotNamespace"))
52  namespace_ = _sdf->GetElement("robotNamespace")->Get<std::string>();
53  else
54  gzerr << "[gazebo_wind_plugin] Please specify a robotNamespace.\n";
55 
56  // Create Gazebo Node.
57  node_handle_ = gazebo::transport::NodePtr(new transport::Node());
58 
59  // Initialise with default namespace (typically /gazebo/default/).
60  node_handle_->Init();
61 
62  if (_sdf->HasElement("xyzOffset"))
63  xyz_offset_ = _sdf->GetElement("xyzOffset")->Get<ignition::math::Vector3d >();
64  else
65  gzerr << "[gazebo_wind_plugin] Please specify a xyzOffset.\n";
66 
67  getSdfParam<std::string>(_sdf, "windForcePubTopic", wind_force_pub_topic_,
69  getSdfParam<std::string>(_sdf, "windSpeedPubTopic", wind_speed_pub_topic_,
71  getSdfParam<std::string>(_sdf, "frameId", frame_id_, frame_id_);
72  getSdfParam<std::string>(_sdf, "linkName", link_name_, link_name_);
73  // Get the wind speed params from SDF.
74  getSdfParam<double>(_sdf, "windSpeedMean", wind_speed_mean_,
76  getSdfParam<double>(_sdf, "windSpeedVariance", wind_speed_variance_,
78  getSdfParam<ignition::math::Vector3d >(_sdf, "windDirection", wind_direction_,
80  // Check if a custom static wind field should be used.
81  getSdfParam<bool>(_sdf, "useCustomStaticWindField", use_custom_static_wind_field_,
83  if (!use_custom_static_wind_field_) {
84  gzdbg << "[gazebo_wind_plugin] Using user-defined constant wind field and gusts.\n";
85  // Get the wind params from SDF.
86  getSdfParam<double>(_sdf, "windForceMean", wind_force_mean_,
88  getSdfParam<double>(_sdf, "windForceVariance", wind_force_variance_,
90  // Get the wind gust params from SDF.
91  getSdfParam<double>(_sdf, "windGustStart", wind_gust_start, wind_gust_start);
92  getSdfParam<double>(_sdf, "windGustDuration", wind_gust_duration,
93  wind_gust_duration);
94  getSdfParam<double>(_sdf, "windGustForceMean", wind_gust_force_mean_,
96  getSdfParam<double>(_sdf, "windGustForceVariance", wind_gust_force_variance_,
98  getSdfParam<ignition::math::Vector3d >(_sdf, "windGustDirection", wind_gust_direction_,
100 
101  wind_direction_.Normalize();
102  wind_gust_direction_.Normalize();
103  wind_gust_start_ = common::Time(wind_gust_start);
104  wind_gust_end_ = common::Time(wind_gust_start + wind_gust_duration);
105  } else {
106  gzdbg << "[gazebo_wind_plugin] Using custom wind field from text file.\n";
107  // Get the wind field text file path, read it and save data.
108  std::string custom_wind_field_path;
109  getSdfParam<std::string>(_sdf, "customWindFieldPath", custom_wind_field_path,
110  custom_wind_field_path);
111  ReadCustomWindField(custom_wind_field_path);
112  }
113 
114  link_ = model_->GetLink(link_name_);
115  if (link_ == NULL)
116  gzthrow("[gazebo_wind_plugin] Couldn't find specified link \"" << link_name_
117  << "\".");
118 
119  // Listen to the update event. This event is broadcast every
120  // simulation iteration.
121  update_connection_ = event::Events::ConnectWorldUpdateBegin(
122  boost::bind(&GazeboWindPlugin::OnUpdate, this, _1));
123 }
124 
125 // This gets called by the world update start event.
126 void GazeboWindPlugin::OnUpdate(const common::UpdateInfo& _info) {
127  if (kPrintOnUpdates) {
128  gzdbg << __FUNCTION__ << "() called." << std::endl;
129  }
130 
131  if (!pubs_and_subs_created_) {
133  pubs_and_subs_created_ = true;
134  }
135 
136  // Get the current simulation time.
137  common::Time now = world_->SimTime();
138 
139  ignition::math::Vector3d wind_velocity(0.0, 0.0, 0.0);
140 
141  // Choose user-specified method for calculating wind velocity.
143  // Calculate the wind force.
144  double wind_strength = wind_force_mean_;
145  ignition::math::Vector3d wind = wind_strength * wind_direction_;
146  // Apply a force from the constant wind to the link.
147  link_->AddForceAtRelativePosition(wind, xyz_offset_);
148 
149  ignition::math::Vector3d wind_gust(0.0, 0.0, 0.0);
150  // Calculate the wind gust force.
151  if (now >= wind_gust_start_ && now < wind_gust_end_) {
152  double wind_gust_strength = wind_gust_force_mean_;
153  wind_gust = wind_gust_strength * wind_gust_direction_;
154  // Apply a force from the wind gust to the link.
155  link_->AddForceAtRelativePosition(wind_gust, xyz_offset_);
156  }
157 
158  wrench_stamped_msg_.mutable_header()->set_frame_id(frame_id_);
159  wrench_stamped_msg_.mutable_header()->mutable_stamp()->set_sec(now.sec);
160  wrench_stamped_msg_.mutable_header()->mutable_stamp()->set_nsec(now.nsec);
161 
162  wrench_stamped_msg_.mutable_wrench()->mutable_force()->set_x(wind.X() +
163  wind_gust.X());
164  wrench_stamped_msg_.mutable_wrench()->mutable_force()->set_y(wind.Y() +
165  wind_gust.Y());
166  wrench_stamped_msg_.mutable_wrench()->mutable_force()->set_z(wind.Z() +
167  wind_gust.Z());
168 
169  // No torque due to wind, set x,y and z to 0.
170  wrench_stamped_msg_.mutable_wrench()->mutable_torque()->set_x(0);
171  wrench_stamped_msg_.mutable_wrench()->mutable_torque()->set_y(0);
172  wrench_stamped_msg_.mutable_wrench()->mutable_torque()->set_z(0);
173 
175 
176  // Calculate the wind speed.
177  wind_velocity = wind_speed_mean_ * wind_direction_;
178  } else {
179  // Get the current position of the aircraft in world coordinates.
180  ignition::math::Vector3d link_position = link_->WorldPose().Pos();
181 
182  // Calculate the x, y index of the grid points with x, y-coordinate
183  // just smaller than or equal to aircraft x, y position.
184  std::size_t x_inf = floor((link_position.X() - min_x_) / res_x_);
185  std::size_t y_inf = floor((link_position.Y() - min_y_) / res_y_);
186 
187  // In case aircraft is on one of the boundary surfaces at max_x or max_y,
188  // decrease x_inf, y_inf by one to have x_sup, y_sup on max_x, max_y.
189  if (x_inf == n_x_ - 1u) {
190  x_inf = n_x_ - 2u;
191  }
192  if (y_inf == n_y_ - 1u) {
193  y_inf = n_y_ - 2u;
194  }
195 
196  // Calculate the x, y index of the grid points with x, y-coordinate just
197  // greater than the aircraft x, y position.
198  std::size_t x_sup = x_inf + 1u;
199  std::size_t y_sup = y_inf + 1u;
200 
201  // Save in an array the x, y index of each of the eight grid points
202  // enclosing the aircraft.
203  constexpr unsigned int n_vertices = 8;
204  std::size_t idx_x[n_vertices] = {x_inf, x_inf, x_sup, x_sup, x_inf, x_inf, x_sup, x_sup};
205  std::size_t idx_y[n_vertices] = {y_inf, y_inf, y_inf, y_inf, y_sup, y_sup, y_sup, y_sup};
206 
207  // Find the vertical factor of the aircraft in each of the four surrounding
208  // grid columns, and their minimal/maximal value.
209  constexpr unsigned int n_columns = 4;
210  float vertical_factors_columns[n_columns];
211  for (std::size_t i = 0u; i < n_columns; ++i) {
212  vertical_factors_columns[i] = (
213  link_position.Z() - bottom_z_[idx_x[2u * i] + idx_y[2u * i] * n_x_]) /
214  (top_z_[idx_x[2u * i] + idx_y[2u * i] * n_x_] - bottom_z_[idx_x[2u * i] + idx_y[2u * i] * n_x_]);
215  }
216 
217  // Find maximal and minimal value amongst vertical factors.
218  float vertical_factors_min = std::min(std::min(std::min(
219  vertical_factors_columns[0], vertical_factors_columns[1]),
220  vertical_factors_columns[2]), vertical_factors_columns[3]);
221  float vertical_factors_max = std::max(std::max(std::max(
222  vertical_factors_columns[0], vertical_factors_columns[1]),
223  vertical_factors_columns[2]), vertical_factors_columns[3]);
224 
225  // Check if aircraft is out of wind field or not, and act accordingly.
226  if (x_inf >= 0u && y_inf >= 0u && vertical_factors_max >= 0u &&
227  x_sup <= (n_x_ - 1u) && y_sup <= (n_y_ - 1u) && vertical_factors_min <= 1u) {
228  // Find indices in z-direction for each of the vertices. If link is not
229  // within the range of one of the columns, set to lowest or highest two.
230  std::size_t idx_z[n_vertices] = {0u, static_cast<int>(vertical_spacing_factors_.size()) - 1u,
231  0u, static_cast<int>(vertical_spacing_factors_.size()) - 1u,
232  0u, static_cast<int>(vertical_spacing_factors_.size()) - 1u,
233  0u, static_cast<int>(vertical_spacing_factors_.size()) - 1u};
234  for (std::size_t i = 0u; i < n_columns; ++i) {
235  if (vertical_factors_columns[i] < 0u) {
236  // Link z-position below lowest grid point of that column.
237  idx_z[2u * i + 1u] = 1u;
238  } else if (vertical_factors_columns[i] >= 1u) {
239  // Link z-position above highest grid point of that column.
240  idx_z[2u * i] = vertical_spacing_factors_.size() - 2u;
241  } else {
242  // Link z-position between two grid points in that column.
243  for (std::size_t j = 0u; j < vertical_spacing_factors_.size() - 1u; ++j) {
244  if (vertical_spacing_factors_[j] <= vertical_factors_columns[i] &&
245  vertical_spacing_factors_[j + 1u] > vertical_factors_columns[i]) {
246  idx_z[2u * i] = j;
247  idx_z[2u * i + 1u] = j + 1u;
248  break;
249  }
250  }
251  }
252  }
253 
254  // Extract the wind velocities corresponding to each vertex.
255  ignition::math::Vector3d wind_at_vertices[n_vertices];
256  for (std::size_t i = 0u; i < n_vertices; ++i) {
257  wind_at_vertices[i].X() = u_[idx_x[i] + idx_y[i] * n_x_ + idx_z[i] * n_x_ * n_y_];
258  wind_at_vertices[i].Y() = v_[idx_x[i] + idx_y[i] * n_x_ + idx_z[i] * n_x_ * n_y_];
259  wind_at_vertices[i].Z() = w_[idx_x[i] + idx_y[i] * n_x_ + idx_z[i] * n_x_ * n_y_];
260  }
261 
262  // Extract the relevant coordinate of every point needed for trilinear
263  // interpolation (first z-direction, then x-direction, then y-direction).
264  constexpr unsigned int n_points_interp_z = 8;
265  constexpr unsigned int n_points_interp_x = 4;
266  constexpr unsigned int n_points_interp_y = 2;
267  double interpolation_points[n_points_interp_x + n_points_interp_y + n_points_interp_z];
268  for (std::size_t i = 0u; i < n_points_interp_x + n_points_interp_y + n_points_interp_z; ++i) {
269  if (i < n_points_interp_z) {
270  interpolation_points[i] = (
271  top_z_[idx_x[i] + idx_y[i] * n_x_] - bottom_z_[idx_x[i] + idx_y[i] * n_x_])
272  * vertical_spacing_factors_[idx_z[i]] + bottom_z_[idx_x[i] + idx_y[i] * n_x_];
273  } else if (i >= n_points_interp_z && i < n_points_interp_x + n_points_interp_z) {
274  interpolation_points[i] = min_x_ + res_x_ * idx_x[2u * (i - n_points_interp_z)];
275  } else {
276  interpolation_points[i] = min_y_ + res_y_ * idx_y[4u * (i - n_points_interp_z - n_points_interp_x)];
277  }
278  }
279 
280  // Interpolate wind velocity at aircraft position.
281  wind_velocity = TrilinearInterpolation(
282  link_position, wind_at_vertices, interpolation_points);
283  } else {
284  // Set the wind velocity to the default constant value specified by user.
285  wind_velocity = wind_speed_mean_ * wind_direction_;
286  }
287  }
288 
289  wind_speed_msg_.mutable_header()->set_frame_id(frame_id_);
290  wind_speed_msg_.mutable_header()->mutable_stamp()->set_sec(now.sec);
291  wind_speed_msg_.mutable_header()->mutable_stamp()->set_nsec(now.nsec);
292 
293  wind_speed_msg_.mutable_velocity()->set_x(wind_velocity.X());
294  wind_speed_msg_.mutable_velocity()->set_y(wind_velocity.Y());
295  wind_speed_msg_.mutable_velocity()->set_z(wind_velocity.Z());
296 
298 }
299 
301  // Create temporary "ConnectGazeboToRosTopic" publisher and message.
302  gazebo::transport::PublisherPtr connect_gazebo_to_ros_topic_pub =
303  node_handle_->Advertise<gz_std_msgs::ConnectGazeboToRosTopic>(
304  "~/" + kConnectGazeboToRosSubtopic, 1);
305 
306  gz_std_msgs::ConnectGazeboToRosTopic connect_gazebo_to_ros_topic_msg;
307 
308  // ============================================ //
309  // ========= WRENCH STAMPED MSG SETUP ========= //
310  // ============================================ //
311  wind_force_pub_ = node_handle_->Advertise<gz_geometry_msgs::WrenchStamped>(
312  "~/" + namespace_ + "/" + wind_force_pub_topic_, 1);
313 
314  // connect_gazebo_to_ros_topic_msg.set_gazebo_namespace(namespace_);
315  connect_gazebo_to_ros_topic_msg.set_gazebo_topic("~/" + namespace_ + "/" +
316  wind_force_pub_topic_);
317  connect_gazebo_to_ros_topic_msg.set_ros_topic(namespace_ + "/" +
318  wind_force_pub_topic_);
319  connect_gazebo_to_ros_topic_msg.set_msgtype(
320  gz_std_msgs::ConnectGazeboToRosTopic::WRENCH_STAMPED);
321  connect_gazebo_to_ros_topic_pub->Publish(connect_gazebo_to_ros_topic_msg,
322  true);
323 
324  // ============================================ //
325  // ========== WIND SPEED MSG SETUP ============ //
326  // ============================================ //
327  wind_speed_pub_ = node_handle_->Advertise<gz_mav_msgs::WindSpeed>(
328  "~/" + namespace_ + "/" + wind_speed_pub_topic_, 1);
329 
330  connect_gazebo_to_ros_topic_msg.set_gazebo_topic("~/" + namespace_ + "/" +
331  wind_speed_pub_topic_);
332  connect_gazebo_to_ros_topic_msg.set_ros_topic(namespace_ + "/" +
333  wind_speed_pub_topic_);
334  connect_gazebo_to_ros_topic_msg.set_msgtype(
335  gz_std_msgs::ConnectGazeboToRosTopic::WIND_SPEED);
336  connect_gazebo_to_ros_topic_pub->Publish(connect_gazebo_to_ros_topic_msg,
337  true);
338 }
339 
340 void GazeboWindPlugin::ReadCustomWindField(std::string& custom_wind_field_path) {
341  std::ifstream fin;
342  fin.open(custom_wind_field_path);
343  if (fin.is_open()) {
344  std::string data_name;
345  float data;
346  // Read the line with the variable name.
347  while (fin >> data_name) {
348  // Save data on following line into the correct variable.
349  if (data_name == "min_x:") {
350  fin >> min_x_;
351  } else if (data_name == "min_y:") {
352  fin >> min_y_;
353  } else if (data_name == "n_x:") {
354  fin >> n_x_;
355  } else if (data_name == "n_y:") {
356  fin >> n_y_;
357  } else if (data_name == "res_x:") {
358  fin >> res_x_;
359  } else if (data_name == "res_y:") {
360  fin >> res_y_;
361  } else if (data_name == "vertical_spacing_factors:") {
362  while (fin >> data) {
363  vertical_spacing_factors_.push_back(data);
364  if (fin.peek() == '\n') break;
365  }
366  } else if (data_name == "bottom_z:") {
367  while (fin >> data) {
368  bottom_z_.push_back(data);
369  if (fin.peek() == '\n') break;
370  }
371  } else if (data_name == "top_z:") {
372  while (fin >> data) {
373  top_z_.push_back(data);
374  if (fin.peek() == '\n') break;
375  }
376  } else if (data_name == "u:") {
377  while (fin >> data) {
378  u_.push_back(data);
379  if (fin.peek() == '\n') break;
380  }
381  } else if (data_name == "v:") {
382  while (fin >> data) {
383  v_.push_back(data);
384  if (fin.peek() == '\n') break;
385  }
386  } else if (data_name == "w:") {
387  while (fin >> data) {
388  w_.push_back(data);
389  if (fin.peek() == '\n') break;
390  }
391  } else {
392  // If invalid data name, read the rest of the invalid line,
393  // publish a message and ignore data on next line. Then resume reading.
394  std::string restOfLine;
395  getline(fin, restOfLine);
396  gzerr << " [gazebo_wind_plugin] Invalid data name '" << data_name << restOfLine <<
397  "' in custom wind field text file. Ignoring data on next line.\n";
398  fin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
399  }
400  }
401  fin.close();
402  gzdbg << "[gazebo_wind_plugin] Successfully read custom wind field from text file.\n";
403  } else {
404  gzerr << "[gazebo_wind_plugin] Could not open custom wind field text file.\n";
405  }
406 
407 }
408 
409 ignition::math::Vector3d GazeboWindPlugin::LinearInterpolation(
410  double position, ignition::math::Vector3d * values, double* points) const {
411  ignition::math::Vector3d value = values[0] + (values[1] - values[0]) /
412  (points[1] - points[0]) * (position - points[0]);
413  return value;
414 }
415 
417  double* position, ignition::math::Vector3d * values, double* points) const {
418  ignition::math::Vector3d intermediate_values[2] = { LinearInterpolation(
419  position[0], &(values[0]), &(points[0])),
421  position[0], &(values[2]), &(points[2])) };
422  ignition::math::Vector3d value = LinearInterpolation(
423  position[1], intermediate_values, &(points[4]));
424  return value;
425 }
426 
428  ignition::math::Vector3d link_position, ignition::math::Vector3d * values, double* points) const {
429  double position[3] = {link_position.X(),link_position.Y(),link_position.Z()};
430  ignition::math::Vector3d intermediate_values[4] = { LinearInterpolation(
431  position[2], &(values[0]), &(points[0])),
433  position[2], &(values[2]), &(points[2])),
435  position[2], &(values[4]), &(points[4])),
437  position[2], &(values[6]), &(points[6])) };
438  ignition::math::Vector3d value = BilinearInterpolation(
439  &(position[0]), intermediate_values, &(points[8]));
440  return value;
441 }
442 
444 
445 } // namespace gazebo
ignition::math::Vector3d BilinearInterpolation(double *position, ignition::math::Vector3d *values, double *points) const
Bilinear interpolation.
std::vector< float > u_
event::ConnectionPtr update_connection_
Pointer to the update event connection.
void OnUpdate(const common::UpdateInfo &)
Called when the world is updated.
std::vector< float > vertical_spacing_factors_
static constexpr double kDefaultWindGustStart
uint8_t data[MAX_SIZE]
bool pubs_and_subs_created_
Flag that is set to true once CreatePubsAndSubs() is called, used to prevent CreatePubsAndSubs() from...
void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
Load the plugin.
ignition::math::Vector3d wind_direction_
bool use_custom_static_wind_field_
Variables for custom wind field generation.
std::vector< float > w_
std::vector< float > top_z_
static constexpr double kDefaultWindGustDuration
gazebo::transport::NodePtr node_handle_
ignition::math::Vector3d wind_gust_direction_
gz_mav_msgs::WindSpeed wind_speed_msg_
Gazebo message for sending wind speed data.
static const bool kPrintOnPluginLoad
Definition: common.h:41
ignition::math::Vector3d xyz_offset_
gazebo::transport::PublisherPtr wind_force_pub_
ignition::math::Vector3d LinearInterpolation(double position, ignition::math::Vector3d *values, double *points) const
Functions for trilinear interpolation of wind field at aircraft position.
This gazebo plugin simulates wind acting on a model.
void CreatePubsAndSubs()
Creates all required publishers and subscribers, incl. routing of messages to/from ROS if required...
ignition::math::Vector3d TrilinearInterpolation(ignition::math::Vector3d link_position, ignition::math::Vector3d *values, double *points) const
Trilinear interpolation.
static const bool kPrintOnUpdates
Definition: common.h:42
GZ_REGISTER_MODEL_PLUGIN(GazeboRosP3D)
static const std::string kConnectGazeboToRosSubtopic
Definition: common.h:56
std::vector< float > bottom_z_
gz_geometry_msgs::WrenchStamped wrench_stamped_msg_
Gazebo message for sending wind data.
gazebo::transport::PublisherPtr wind_speed_pub_
void ReadCustomWindField(std::string &custom_wind_field_path)
Reads wind data from a text file and saves it.
std::vector< float > v_


rotors_gazebo_plugins
Author(s): Fadri Furrer, Michael Burri, Mina Kamel, Janosch Nikolic, Markus Achtelik
autogenerated on Mon Feb 28 2022 23:39:04