liftdrag_plugin.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014-2016 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 <algorithm>
19 #include <string>
20 
21 #include "gazebo/common/Assert.hh"
22 #include "gazebo/physics/physics.hh"
23 #include "gazebo/sensors/SensorManager.hh"
24 #include "gazebo/transport/transport.hh"
25 #include "gazebo/msgs/msgs.hh"
27 
28 using namespace gazebo;
29 
31 
32 LiftDragPlugin::LiftDragPlugin() : cla(1.0), cda(0.01), cma(0.01), rho(1.2041)
34 {
35  this->cp = ignition::math::Vector3d (0, 0, 0);
36  this->forward = ignition::math::Vector3d (1, 0, 0);
37  this->upward = ignition::math::Vector3d (0, 0, 1);
38  this->area = 1.0;
39  this->alpha0 = 0.0;
40  this->alpha = 0.0;
41  this->sweep = 0.0;
42  this->velocityStall = 0.0;
43 
44  // 90 deg stall
45  this->alphaStall = 0.5*M_PI;
46  this->claStall = 0.0;
47 
48  this->radialSymmetry = false;
49 
51  this->cdaStall = 1.0;
52  this->cmaStall = 0.0;
53 
55  this->controlJointRadToCL = 4.0;
56 }
57 
59 LiftDragPlugin::~LiftDragPlugin()
60 {
61 }
62 
64 void LiftDragPlugin::Load(physics::ModelPtr _model,
65  sdf::ElementPtr _sdf)
66 {
67  GZ_ASSERT(_model, "LiftDragPlugin _model pointer is NULL");
68  GZ_ASSERT(_sdf, "LiftDragPlugin _sdf pointer is NULL");
69  this->model = _model;
70  this->sdf = _sdf;
71 
72  this->world = this->model->GetWorld();
73  GZ_ASSERT(this->world, "LiftDragPlugin world pointer is NULL");
74 
75  this->physics = this->world->Physics();
76  GZ_ASSERT(this->physics, "LiftDragPlugin physics pointer is NULL");
77 
78  GZ_ASSERT(_sdf, "LiftDragPlugin _sdf pointer is NULL");
79 
80  if (_sdf->HasElement("radial_symmetry"))
81  this->radialSymmetry = _sdf->Get<bool>("radial_symmetry");
82 
83  if (_sdf->HasElement("a0"))
84  this->alpha0 = _sdf->Get<double>("a0");
85 
86  if (_sdf->HasElement("cla"))
87  this->cla = _sdf->Get<double>("cla");
88 
89  if (_sdf->HasElement("cda"))
90  this->cda = _sdf->Get<double>("cda");
91 
92  if (_sdf->HasElement("cma"))
93  this->cma = _sdf->Get<double>("cma");
94 
95  if (_sdf->HasElement("alpha_stall"))
96  this->alphaStall = _sdf->Get<double>("alpha_stall");
97 
98  if (_sdf->HasElement("cla_stall"))
99  this->claStall = _sdf->Get<double>("cla_stall");
100 
101  if (_sdf->HasElement("cda_stall"))
102  this->cdaStall = _sdf->Get<double>("cda_stall");
103 
104  if (_sdf->HasElement("cma_stall"))
105  this->cmaStall = _sdf->Get<double>("cma_stall");
106 
107  if (_sdf->HasElement("cp"))
108  this->cp = _sdf->Get<ignition::math::Vector3d >("cp");
109 
110  // blade forward (-drag) direction in link frame
111  if (_sdf->HasElement("forward"))
112  this->forward = _sdf->Get<ignition::math::Vector3d >("forward");
113  this->forward.Normalize();
114 
115  // blade upward (+lift) direction in link frame
116  if (_sdf->HasElement("upward"))
117  this->upward = _sdf->Get<ignition::math::Vector3d >("upward");
118  this->upward.Normalize();
119 
120  if (_sdf->HasElement("area"))
121  this->area = _sdf->Get<double>("area");
122 
123  if (_sdf->HasElement("air_density"))
124  this->rho = _sdf->Get<double>("air_density");
125 
126  if (_sdf->HasElement("link_name"))
127  {
128  sdf::ElementPtr elem = _sdf->GetElement("link_name");
129  // GZ_ASSERT(elem, "Element link_name doesn't exist!");
130  std::string linkName = elem->Get<std::string>();
131  this->link = this->model->GetLink(linkName);
132  // GZ_ASSERT(this->link, "Link was NULL");
133 
134  if (!this->link)
135  {
136  gzerr << "Link with name[" << linkName << "] not found. "
137  << "The LiftDragPlugin will not generate forces\n";
138  }
139  else
140  {
141  this->updateConnection = event::Events::ConnectWorldUpdateBegin(
142  boost::bind(&LiftDragPlugin::OnUpdate, this));
143  }
144  }
145 
146  if (_sdf->HasElement("control_joint_name"))
147  {
148  std::string controlJointName = _sdf->Get<std::string>("control_joint_name");
149  this->controlJoint = this->model->GetJoint(controlJointName);
150  if (!this->controlJoint)
151  {
152  gzerr << "Joint with name[" << controlJointName << "] does not exist.\n";
153  }
154  }
155 
156  if (_sdf->HasElement("control_joint_rad_to_cl"))
157  this->controlJointRadToCL = _sdf->Get<double>("control_joint_rad_to_cl");
158 }
159 
162 {
163  GZ_ASSERT(this->link, "Link was NULL");
164  // get linear velocity at cp in inertial frame
165  ignition::math::Vector3d vel = this->link->WorldLinearVel(this->cp);
166  ignition::math::Vector3d velI = vel;
167  velI.Normalize();
168 
169  // smoothing
170  // double e = 0.8;
171  // this->velSmooth = e*vel + (1.0 - e)*velSmooth;
172  // vel = this->velSmooth;
173 
174  if (vel.Length() <= 0.01)
175  return;
176 
177  // pose of body
178  ignition::math::Pose3d pose = this->link->WorldPose();
179 
180  // rotate forward and upward vectors into inertial frame
181  ignition::math::Vector3d forwardI = pose.Rot().RotateVector(this->forward);
182 
183  ignition::math::Vector3d upwardI;
184  if (this->radialSymmetry)
185  {
186  // use inflow velocity to determine upward direction
187  // which is the component of inflow perpendicular to forward direction.
188  ignition::math::Vector3d tmp = forwardI.Cross(velI);
189  upwardI = forwardI.Cross(tmp).Normalize();
190  }
191  else
192  {
193  upwardI = pose.Rot().RotateVector(this->upward);
194  }
195 
196  // spanwiseI: a vector normal to lift-drag-plane described in inertial frame
197  ignition::math::Vector3d spanwiseI = forwardI.Cross(upwardI).Normalize();
198 
199  const double minRatio = -1.0;
200  const double maxRatio = 1.0;
201  // check sweep (angle between velI and lift-drag-plane)
202  double sinSweepAngle = ignition::math::clamp(
203  spanwiseI.Dot(velI), minRatio, maxRatio);
204 
205  // get cos from trig identity
206  double cosSweepAngle = 1.0 - sinSweepAngle * sinSweepAngle;
207  this->sweep = asin(sinSweepAngle);
208 
209  // truncate sweep to within +/-90 deg
210  while (fabs(this->sweep) > 0.5 * M_PI)
211  this->sweep = this->sweep > 0 ? this->sweep - M_PI
212  : this->sweep + M_PI;
213 
214  // angle of attack is the angle between
215  // velI projected into lift-drag plane
216  // and
217  // forward vector
218  //
219  // projected = spanwiseI Xcross ( vector Xcross spanwiseI)
220  //
221  // so,
222  // removing spanwise velocity from vel
223  ignition::math::Vector3d velInLDPlane = vel - vel.Dot(spanwiseI)*velI;
224 
225  // get direction of drag
226  ignition::math::Vector3d dragDirection = -velInLDPlane;
227  dragDirection.Normalize();
228 
229  // get direction of lift
230  ignition::math::Vector3d liftI = spanwiseI.Cross(velInLDPlane);
231  liftI.Normalize();
232 
233  // get direction of moment
234  ignition::math::Vector3d momentDirection = spanwiseI;
235 
236  // compute angle between upwardI and liftI
237  // in general, given vectors a and b:
238  // cos(theta) = a.Dot(b)/(a.Length()*b.Lenghth())
239  // given upwardI and liftI are both unit vectors, we can drop the denominator
240  // cos(theta) = a.Dot(b)
241  double cosAlpha = ignition::math::clamp(liftI.Dot(upwardI), minRatio, maxRatio);
242 
243  // Is alpha positive or negative? Test:
244  // forwardI points toward zero alpha
245  // if forwardI is in the same direction as lift, alpha is positive.
246  // liftI is in the same direction as forwardI?
247  if (liftI.Dot(forwardI) >= 0.0)
248  this->alpha = this->alpha0 + acos(cosAlpha);
249  else
250  this->alpha = this->alpha0 - acos(cosAlpha);
251 
252  // normalize to within +/-90 deg
253  while (fabs(this->alpha) > 0.5 * M_PI)
254  this->alpha = this->alpha > 0 ? this->alpha - M_PI
255  : this->alpha + M_PI;
256 
257  // compute dynamic pressure
258  double speedInLDPlane = velInLDPlane.Length();
259  double q = 0.5 * this->rho * speedInLDPlane * speedInLDPlane;
260 
261  // compute cl at cp, check for stall, correct for sweep
262  double cl;
263  if (this->alpha > this->alphaStall)
264  {
265  cl = (this->cla * this->alphaStall +
266  this->claStall * (this->alpha - this->alphaStall))
267  * cosSweepAngle;
268  // make sure cl is still great than 0
269  cl = std::max(0.0, cl);
270  }
271  else if (this->alpha < -this->alphaStall)
272  {
273  cl = (-this->cla * this->alphaStall +
274  this->claStall * (this->alpha + this->alphaStall))
275  * cosSweepAngle;
276  // make sure cl is still less than 0
277  cl = std::min(0.0, cl);
278  }
279  else
280  cl = this->cla * this->alpha * cosSweepAngle;
281 
282  // modify cl per control joint value
283  if (this->controlJoint)
284  {
285  double controlAngle = this->controlJoint->Position(0);
286  cl = cl + this->controlJointRadToCL * controlAngle;
288  }
289 
290  // compute lift force at cp
291  ignition::math::Vector3d lift = cl * q * this->area * liftI;
292 
293  // compute cd at cp, check for stall, correct for sweep
294  double cd;
295  if (this->alpha > this->alphaStall)
296  {
297  cd = (this->cda * this->alphaStall +
298  this->cdaStall * (this->alpha - this->alphaStall))
299  * cosSweepAngle;
300  }
301  else if (this->alpha < -this->alphaStall)
302  {
303  cd = (-this->cda * this->alphaStall +
304  this->cdaStall * (this->alpha + this->alphaStall))
305  * cosSweepAngle;
306  }
307  else
308  cd = (this->cda * this->alpha) * cosSweepAngle;
309 
310  // make sure drag is positive
311  cd = fabs(cd);
312 
313  // drag at cp
314  ignition::math::Vector3d drag = cd * q * this->area * dragDirection;
315 
316  // compute cm at cp, check for stall, correct for sweep
317  double cm;
318  if (this->alpha > this->alphaStall)
319  {
320  cm = (this->cma * this->alphaStall +
321  this->cmaStall * (this->alpha - this->alphaStall))
322  * cosSweepAngle;
323  // make sure cm is still great than 0
324  cm = std::max(0.0, cm);
325  }
326  else if (this->alpha < -this->alphaStall)
327  {
328  cm = (-this->cma * this->alphaStall +
329  this->cmaStall * (this->alpha + this->alphaStall))
330  * cosSweepAngle;
331  // make sure cm is still less than 0
332  cm = std::min(0.0, cm);
333  }
334  else
335  cm = this->cma * this->alpha * cosSweepAngle;
336 
339  cm = 0.0;
340 
341  // compute moment (torque) at cp
342  ignition::math::Vector3d moment = cm * q * this->area * momentDirection;
343 
344  // moment arm from cg to cp in inertial plane
345  ignition::math::Vector3d momentArm = pose.Rot().RotateVector(
346  this->cp - this->link->GetInertial()->CoG());
347  // gzerr << this->cp << " : " << this->link->GetInertial()->GetCoG() << "\n";
348 
349  // force and torque about cg in inertial frame
350  ignition::math::Vector3d force = lift + drag;
351  // + moment.Cross(momentArm);
352 
353  ignition::math::Vector3d torque = moment;
354  // - lift.Cross(momentArm) - drag.Cross(momentArm);
355 
356  // debug
357  //
358  // if ((this->link->GetName() == "wing_1" ||
359  // this->link->GetName() == "wing_2") &&
360  // (vel.GetLength() > 50.0 &&
361  // vel.GetLength() < 50.0))
362  if (0)
363  {
364  gzdbg << "=============================\n";
365  gzdbg << "sensor: [" << this->GetHandle() << "]\n";
366  gzdbg << "Link: [" << this->link->GetName()
367  << "] pose: [" << pose
368  << "] dynamic pressure: [" << q << "]\n";
369  gzdbg << "spd: [" << vel.Length()
370  << "] vel: [" << vel << "]\n";
371  gzdbg << "LD plane spd: [" << velInLDPlane.Length()
372  << "] vel : [" << velInLDPlane << "]\n";
373  gzdbg << "forward (inertial): " << forwardI << "\n";
374  gzdbg << "upward (inertial): " << upwardI << "\n";
375  gzdbg << "lift dir (inertial): " << liftI << "\n";
376  gzdbg << "Span direction (normal to LD plane): " << spanwiseI << "\n";
377  gzdbg << "sweep: " << this->sweep << "\n";
378  gzdbg << "alpha: " << this->alpha << "\n";
379  gzdbg << "lift: " << lift << "\n";
380  gzdbg << "drag: " << drag << " cd: "
381  << cd << " cda: " << this->cda << "\n";
382  gzdbg << "moment: " << moment << "\n";
383  gzdbg << "cp momentArm: " << momentArm << "\n";
384  gzdbg << "force: " << force << "\n";
385  gzdbg << "torque: " << torque << "\n";
386  }
387 
388  // Correct for nan or inf
389  force.Correct();
390  this->cp.Correct();
391  torque.Correct();
392 
393  // apply forces at cg (with torques for position shift)
394  this->link->AddForceAtRelativePosition(force, this->cp);
395  this->link->AddTorque(torque);
396 }
physics::LinkPtr link
Pointer to link currently targeted by mud joint.
LiftDragPlugin()
Constructor.
double area
effective planeform surface area
double alpha
angle of attack
ignition::math::Vector3d upward
A vector in the lift/drag plane, perpendicular to the forward vector. Inflow velocity orthogonal to f...
double sweep
angle of sweep
physics::ModelPtr model
Pointer to model containing plugin.
double cla
Coefficient of Lift / alpha slope. Lift = C_L * q * S where q (dynamic pressure) = 0...
virtual void OnUpdate()
Callback for World Update events.
sdf::ElementPtr sdf
SDF for this plugin;.
double alphaStall
angle of attach when airfoil stalls
ignition::math::Vector3d forward
Normally, this is taken as a direction parallel to the chord of the airfoil in zero angle of attack f...
double controlJointRadToCL
how much to change CL per radian of control surface joint value.
#define M_PI
double rho
air density at 25 deg C it&#39;s about 1.1839 kg/m^3 At 20 °C and 101.325 kPa, dry air has a density of 1...
virtual void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
double cdaStall
Cd-alpha rate after stall.
physics::WorldPtr world
Pointer to world.
event::ConnectionPtr updateConnection
Connection to World Update events.
physics::JointPtr controlJoint
Pointer to a joint that actuates a control surface for this lifting body.
double cma
Coefficient of Moment / alpha slope. Moment = C_M * q * S where q (dynamic pressure) = 0...
double cmaStall
Cm-alpha rate after stall.
ignition::math::Vector3d cp
center of pressure in link local coordinates
INLINE Rall1d< T, V, S > asin(const Rall1d< T, V, S > &x)
A plugin that simulates lift and drag.
bool radialSymmetry
if the shape is aerodynamically radially symmetric about the forward direction. Defaults to false for...
double cda
Coefficient of Drag / alpha slope. Drag = C_D * q * S where q (dynamic pressure) = 0...
INLINE Rall1d< T, V, S > acos(const Rall1d< T, V, S > &x)
GZ_REGISTER_MODEL_PLUGIN(GazeboRosP3D)
double claStall
Cl-alpha rate after stall.
physics::PhysicsEnginePtr physics
Pointer to physics engine.
double alpha0
initial angle of attack


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