gazebo_gimbal_controller_plugin.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012-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 <gazebo/physics/physics.hh>
19 #include <gazebo/transport/transport.hh>
21 
22 using namespace gazebo;
23 using namespace std;
24 
26 
29  :status("closed")
30 {
32  this->pitchPid.Init(5, 0, 0, 0, 0, 0.3, -0.3);
33  this->rollPid.Init(5, 0, 0, 0, 0, 0.3, -0.3);
34  this->yawPid.Init(1.0, 0, 0, 0, 0, 1.0, -1.0);
35  this->pitchCommand = 0.5* M_PI;
36  this->rollCommand = 0;
37  this->yawCommand = 0;
38 }
39 
42  physics::ModelPtr _model,
43  sdf::ElementPtr _sdf)
44 {
45 
46  if(kPrintOnPluginLoad) {
47  gzdbg << __FUNCTION__ << "() called." << std::endl;
48  }
49 
50  this->model = _model;
51 
52  this->sdf = _sdf;
53 
54  std::string yawJointName = "cgo3_vertical_arm_joint";
55  this->yawJoint = this->model->GetJoint(yawJointName);
56  if (this->sdf->HasElement("joint_yaw"))
57  {
58  // Add names to map
59  yawJointName = sdf->Get<std::string>("joint_yaw");
60  if (this->model->GetJoint(yawJointName))
61  {
62  this->yawJoint = this->model->GetJoint(yawJointName);
63  }
64  else
65  {
66  gzwarn << "joint_yaw [" << yawJointName << "] does not exist?\n";
67  }
68  }
69  if (!this->yawJoint)
70  {
71  gzerr << "GimbalControllerPlugin::Load ERROR! Can't get yaw joint '"
72  << yawJointName << "' " << endl;
73  }
74 
75  std::string rollJointName = "cgo3_horizontal_arm_joint";
76  this->rollJoint = this->model->GetJoint(rollJointName);
77  if (this->sdf->HasElement("joint_roll"))
78  {
79  // Add names to map
80  rollJointName = sdf->Get<std::string>("joint_roll");
81  if (this->model->GetJoint(rollJointName))
82  {
83  this->rollJoint = this->model->GetJoint(rollJointName);
84  }
85  else
86  {
87  gzwarn << "joint_roll [" << rollJointName << "] does not exist?\n";
88  }
89  }
90  if (!this->rollJoint)
91  {
92  gzerr << "GimbalControllerPlugin::Load ERROR! Can't get roll joint '"
93  << rollJointName << "' " << endl;
94  }
95 
96 
97  std::string pitchJointName = "cgo3_camera_joint";
98  this->pitchJoint = this->model->GetJoint(pitchJointName);
99  if (this->sdf->HasElement("joint_pitch"))
100  {
101  // Add names to map
102  pitchJointName = sdf->Get<std::string>("joint_pitch");
103  if (this->model->GetJoint(pitchJointName))
104  {
105  this->pitchJoint = this->model->GetJoint(pitchJointName);
106  }
107  else
108  {
109  gzwarn << "joint_pitch [" << pitchJointName << "] does not exist?\n";
110  }
111  }
112  if (!this->pitchJoint)
113  {
114  gzerr << "GimbalControllerPlugin::Load ERROR! Can't get pitch joint '"
115  << pitchJointName << "' " << endl;
116  }
117 
118 
119  // get imu sensor
120  std::string imuSensorName = "camera_imu";
121  if (this->sdf->HasElement("imu"))
122  {
123  // Add names to map
124  imuSensorName = sdf->Get<std::string>("imu");
125  }
126 
127  this->imuSensor = std::static_pointer_cast<sensors::ImuSensor>(
128  sensors::SensorManager::Instance()->GetSensor(imuSensorName));
129 
130  if (!this->imuSensor)
131  {
132  gzerr << "GimbalControllerPlugin::Load ERROR! Can't get imu sensor '"
133  << imuSensorName << "' " << endl;
134  }
135 }
136 
139 {
140  this->node = transport::NodePtr(new transport::Node());
141  this->node->Init(this->model->GetWorld()->GetName());
142 
143  this->lastUpdateTime = this->model->GetWorld()->GetSimTime();
144 
145  // receive pitch command via gz transport
146  std::string pitchTopic = std::string("~/") + this->model->GetName() +
147  "/gimbal_pitch_cmd";
148  this->pitchSub = this->node->Subscribe(pitchTopic,
150  // receive roll command via gz transport
151  std::string rollTopic = std::string("~/") + this->model->GetName() +
152  "/gimbal_roll_cmd";
153  this->rollSub = this->node->Subscribe(rollTopic,
155  // receive yaw command via gz transport
156  std::string yawTopic = std::string("~/") + this->model->GetName() +
157  "/gimbal_yaw_cmd";
158  this->yawSub = this->node->Subscribe(yawTopic,
160 
161  // plugin update
162  this->connections.push_back(event::Events::ConnectWorldUpdateBegin(
163  boost::bind(&GimbalControllerPlugin::OnUpdate, this)));
164 
165  // publish pitch status via gz transport
166  pitchTopic = std::string("~/") + this->model->GetName()
167  + "/gimbal_pitch_status";
168  this->pitchPub = node->Advertise<gazebo::msgs::Any>(pitchTopic);
169 
170  // publish roll status via gz transport
171  rollTopic = std::string("~/") + this->model->GetName()
172  + "/gimbal_roll_status";
173  this->rollPub = node->Advertise<gazebo::msgs::Any>(rollTopic);
174 
175 
176  // publish yaw status via gz transport
177  yawTopic = std::string("~/") + this->model->GetName()
178  + "/gimbal_yaw_status";
179  this->yawPub = node->Advertise<gazebo::msgs::Any>(yawTopic);
180 
181  gzmsg << "GimbalControllerPlugin::Init" << std::endl;
182 }
183 
186 {
187 // gzdbg << "pitch command received " << _msg->double_value() << std::endl;
188  this->pitchCommand = _msg->double_value();
189 }
190 
193 {
194 // gzdbg << "roll command received " << _msg->double_value() << std::endl;
195  this->rollCommand = _msg->double_value();
196 }
197 
200 {
201 // gzdbg << "yaw command received " << _msg->double_value() << std::endl;
202  this->yawCommand = _msg->double_value();
203 }
204 
206 ignition::ignition::math::Vector3d GimbalControllerPlugin::ThreeAxisRot(
207  double r11, double r12, double r21, double r31, double r32)
208 {
209  return ignition::ignition::math::Vector3d d(
210  atan2( r31, r32 ),
211  asin ( r21 ),
212  atan2( r11, r12 ));
213 }
214 
216 ignition::ignition::math::Vector3d GimbalControllerPlugin::QtoZXY(
217  const ignition::math::Quaterniond &_q)
218 {
219  // taken from
220  // http://bediyap.com/programming/convert-quaternion-to-euler-rotations/
221  // case zxy:
222  ignition::ignition::math::Vector3d result = this->ThreeAxisRot(
223  -2*(_q.X()*_q.Y() - _q.W()*_q.Z()),
224  _q.W()*_q.W() - _q.X()*_q.X() + _q.Y()*_q.Y() - _q.Z()*_q.Z(),
225  2*(_q.Y()*_q.Z() + _q.W()*_q.X()),
226  -2*(_q.X()*_q.Z() - _q.W()*_q.Y()),
227  _q.W()*_q.W() - _q.X()*_q.X() - _q.Y()*_q.Y() + _q.Z()*_q.Z());
228  return result;
229 }
230 
233 {
234  if (!this->pitchJoint || !this->rollJoint || !this->yawJoint)
235  return;
236 
237  common::Time time = this->model->GetWorld()->GetSimTime();
238  if (time < this->lastUpdateTime)
239  {
240  gzerr << "time reset event\n";
241  this->lastUpdateTime = time;
242  return;
243  }
244  else if (time > this->lastUpdateTime)
245  {
246  double dt = (this->lastUpdateTime - time).Double();
247 
248  // anything to do with gazebo joint has
249  // hardcoded negative joint axis for pitch and roll
250  // TODO: make joint direction a parameter
251  const double rDir = -1;
252  const double pDir = -1;
253  const double yDir = 1;
254 
255  // truncate command inside joint angle limits
256  double rollLimited = ignition::math::clamp(this->rollCommand,
257  rDir*this->rollJoint->GetUpperLimit(0).Radian(),
258  rDir*this->rollJoint->GetLowerLimit(0).Radian());
259  double pitchLimited = ignition::math::clamp(this->pitchCommand,
260  pDir*this->pitchJoint->GetUpperLimit(0).Radian(),
261  pDir*this->pitchJoint->GetLowerLimit(0).Radian());
262  double yawLimited = ignition::math::clamp(this->yawCommand,
263  yDir*this->yawJoint->GetLowerLimit(0).Radian(),
264  yDir*this->yawJoint->GetUpperLimit(0).Radian());
265 
266  ignition::math::Quaterniond commandRPY(
267  rollLimited, pitchLimited, yawLimited);
268 
269 
271 
274  ignition::ignition::math::Vector3d currentAngleYPRVariable(
275  this->imuSensor->Orientation().Euler());
276  ignition::ignition::math::Vector3d currentAnglePRYVariable(
277  this->QtoZXY(currentAngleYPRVariable));
278 
281  ignition::ignition::math::Vector3d lowerLimitsPRY
282  (pDir*this->pitchJoint->GetLowerLimit(0).Radian(),
283  rDir*this->rollJoint->GetLowerLimit(0).Radian(),
284  yDir*this->yawJoint->GetLowerLimit(0).Radian());
285  ignition::ignition::math::Vector3d upperLimitsPRY
286  (pDir*this->pitchJoint->GetUpperLimit(0).Radian(),
287  rDir*this->rollJoint->GetUpperLimit(0).Radian(),
288  yDir*this->yawJoint->GetUpperLimit(0).Radian());
289 
290  // normalize errors
291  double pitchError = this->ShortestAngularDistance(
292  pitchLimited, currentAnglePRYVariable.X());
293  double rollError = this->ShortestAngularDistance(
294  rollLimited, currentAnglePRYVariable.Y());
295  double yawError = this->ShortestAngularDistance(
296  yawLimited, currentAnglePRYVariable.Z());
297 
298  // Clamp errors based on current angle and estimated errors from rotations:
299  // given error = current - target, then
300  // if target (current angle - error) is outside joint limit, truncate error
301  // so that current angle - error is within joint limit, i.e.:
302  // lower limit < current angle - error < upper limit
303  // or
304  // current angle - lower limit > error > current angle - upper limit
305  // re-expressed as clamps:
306  // hardcoded negative joint axis for pitch and roll
307  if (lowerLimitsPRY.X() < upperLimitsPRY.X())
308  {
309  pitchError = ignition::math::clamp(pitchError,
310  currentAnglePRYVariable.X() - upperLimitsPRY.X(),
311  currentAnglePRYVariable.X() - lowerLimitsPRY.X());
312  }
313  else
314  {
315  pitchError = ignition::math::clamp(pitchError,
316  currentAnglePRYVariable.X() - lowerLimitsPRY.X(),
317  currentAnglePRYVariable.X() - upperLimitsPRY.X());
318  }
319  if (lowerLimitsPRY.Y() < upperLimitsPRY.Y())
320  {
321  rollError = ignition::math::clamp(rollError,
322  currentAnglePRYVariable.Y() - upperLimitsPRY.Y(),
323  currentAnglePRYVariable.Y() - lowerLimitsPRY.Y());
324  }
325  else
326  {
327  rollError = ignition::math::clamp(rollError,
328  currentAnglePRYVariable.Y() - lowerLimitsPRY.Y(),
329  currentAnglePRYVariable.Y() - upperLimitsPRY.Y());
330  }
331  if (lowerLimitsPRY.Z() < upperLimitsPRY.Z())
332  {
333  yawError = ignition::math::clamp(yawError,
334  currentAnglePRYVariable.Z() - upperLimitsPRY.Z(),
335  currentAnglePRYVariable.Z() - lowerLimitsPRY.Z());
336  }
337  else
338  {
339  yawError = ignition::math::clamp(yawError,
340  currentAnglePRYVariable.Z() - lowerLimitsPRY.Z(),
341  currentAnglePRYVariable.Z() - upperLimitsPRY.Z());
342  }
343 
344  // apply forces to move gimbal
345  double pitchForce = this->pitchPid.Update(pitchError, dt);
346  this->pitchJoint->SetForce(0, pDir*pitchForce);
347 
348  double rollForce = this->rollPid.Update(rollError, dt);
349  this->rollJoint->SetForce(0, rDir*rollForce);
350 
351  double yawForce = this->yawPid.Update(yawError, dt);
352  this->yawJoint->SetForce(0, yDir*yawForce);
353 
354  // ignition::ignition::math::Vector3d angles = this->imuSensor->Orientation().Euler();
355  // gzerr << "ang[" << angles.X() << ", " << angles.Y() << ", " << angles.Z()
356  // << "] cmd[ " << this->rollCommand
357  // << ", " << this->pitchCommand << ", " << this->yawCommand
358  // << "] err[ " << rollError
359  // << ", " << pitchError << ", " << yawError
360  // << "] frc[ " << rollForce
361  // << ", " << pitchForce << ", " << yawForce << "]\n";
362 
363 
364  this->lastUpdateTime = time;
365  }
366 
367  static int i =1000;
368  if (++i>100)
369  {
370  i = 0;
371 
372  gazebo::msgs::Any m;
373  m.set_type(gazebo::msgs::Any_ValueType_DOUBLE);
374 
375  m.set_double_value(this->pitchJoint->GetAngle(0).Radian());
376  this->pitchPub->Publish(m);
377 
378  m.set_double_value(this->rollJoint->GetAngle(0).Radian());
379  this->rollPub->Publish(m);
380 
381  m.set_double_value(this->yawJoint->GetAngle(0).Radian());
382  this->yawPub->Publish(m);
383  }
384 }
385 
387 double GimbalControllerPlugin::NormalizeAbout(double _angle, double reference)
388 {
389  double diff = _angle - reference;
390  // normalize diff about (-pi, pi], then add reference
391  while (diff <= -M_PI)
392  {
393  diff += 2.0*M_PI;
394  }
395  while (diff > M_PI)
396  {
397  diff -= 2.0*M_PI;
398  }
399  return diff + reference;
400 }
401 
403 double GimbalControllerPlugin::ShortestAngularDistance(double _from, double _to)
404 {
405  return this->NormalizeAbout(_to, _from) - _from;
406 }
d
#define M_PI
ignition::ignition::math::Vector3d ThreeAxisRot(double r11, double r12, double r21, double r31, double r32)
uint8_t result
INLINE Rall1d< T, V, S > asin(const Rall1d< T, V, S > &x)
double NormalizeAbout(double _angle, double _reference)
returns _angle1 normalized about (_reference - M_PI, _reference + M_PI]
static const bool kPrintOnPluginLoad
Definition: common.h:41
double ShortestAngularDistance(double _from, double _to)
returns shortest angular distance from _from to _to
INLINE Rall1d< T, V, S > atan2(const Rall1d< T, V, S > &y, const Rall1d< T, V, S > &x)
GZ_REGISTER_MODEL_PLUGIN(GazeboRosP3D)
virtual void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
ignition::ignition::math::Vector3d QtoZXY(const ignition::math::Quaterniond &_q)


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