gazebo_rsv_balance.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Copyright (c) 2015 Robosavvy Ltd.
3  * Author: Vitor Matos
4  *
5  * Based on diff_drive_plugin
6  *********************************************************************/
7 
9 
10 #include <string>
11 #include <map>
12 
13 #include <ros/ros.h>
14 #include <sdf/sdf.hh>
15 
16 namespace gazebo
17 {
18 
20 
22 
23 void GazeboRsvBalance::Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf)
24 {
25  this->parent_ = _parent;
26  this->sdf_ = _sdf;
27  this->gazebo_ros_ = GazeboRosPtr(new GazeboRos(_parent, _sdf, "RsvBalancePlugin"));
28 
29  this->gazebo_ros_->isInitialized();
30 
31  // Obtain parameters from rosparam
32  this->gazebo_ros_->getParameter<bool>(this->publish_odom_tf_, "publishOdomTF", true);
33  this->gazebo_ros_->getParameter<bool>(this->publish_wheel_joint_, "publishWheelJointState", true);
34 
35  this->gazebo_ros_->getParameter<std::string>(this->command_topic_, "commandTopic", "cmd_vel");
36  this->gazebo_ros_->getParameter<std::string>(this->odom_topic_, "odomTopic", "odom");
37  this->gazebo_ros_->getParameter<std::string>(this->base_frame_id_, "baseFrameId", "base_link");
38  this->gazebo_ros_->getParameter<std::string>(this->odom_frame_id_, "odomFrameId", "odom");
39 
40  this->gazebo_ros_->getParameter<bool>(this->publish_state_, "publishState", true);
41  this->gazebo_ros_->getParameter<double>(this->update_rate_, "updateRate", 50.0);
42  this->gazebo_ros_->getParameter<double>(this->publish_state_rate_, "publishStateRate", 50);
43  this->gazebo_ros_->getParameter<double>(this->publish_diagnostics_rate_, "publishDiagnosticsRate", 1);
44 
45  std::map<std::string, OdomSource> odom_options;
46  odom_options["encoder"] = ENCODER;
47  odom_options["world"] = WORLD;
48  this->gazebo_ros_->getParameter<OdomSource>(this->odom_source_, "odomSource", odom_options, WORLD);
49 
50  this->mode_map_["park"] = PARK;
51  this->mode_map_["tractor"] = TRACTOR;
52  this->mode_map_["balance"] = BALANCE;
53  this->gazebo_ros_->getParameter<Mode>(this->current_mode_, "startMode", this->mode_map_, BALANCE);
54 
55  if (!this->sdf_->HasElement("wheelSeparation") || !this->sdf_->HasElement("wheelRadius") )
56  {
57  ROS_ERROR("RsvBalancePlugin - Missing <wheelSeparation> or <wheelDiameter>, Aborting");
58  return;
59  }
60  else
61  {
62  this->gazebo_ros_->getParameter<double>(this->wheel_separation_, "wheelSeparation");
63  this->gazebo_ros_->getParameter<double>(this->wheel_radius_, "wheelRadius");
64  }
65 
66  this->joints_.resize(2);
67  this->joints_[LEFT] = this->gazebo_ros_->getJoint(this->parent_, "leftJoint", "left_joint");
68  this->joints_[RIGHT] = this->gazebo_ros_->getJoint(this->parent_, "rightJoint", "right_joint");
69 
70  // Control loop timing
71  if (this->update_rate_ > 0.0)
72  {
73  this->update_period_ = 1.0 / this->update_rate_;
74  }
75  else
76  {
77  ROS_WARN("RsvBalancePlugin - Update rate < 0. Update period set to: 0.1. ");
78  this->update_period_ = 0.1;
79  }
80  this->last_update_time_ = this->parent_->GetWorld()->GetSimTime();
81 
82 
83  // Command velocity subscriber
84  ros::SubscribeOptions so = ros::SubscribeOptions::create<geometry_msgs::Twist>(this->command_topic_, 5,
85  boost::bind(&GazeboRsvBalance::cmdVelCallback, this, _1),
86  ros::VoidPtr(), &this->queue_);
87  this->cmd_vel_subscriber_ = this->gazebo_ros_->node()->subscribe(so);
88  ROS_INFO("%s: Subscribed to %s!", this->gazebo_ros_->info(), this->command_topic_.c_str());
89  // Tilt equilibrium subscriber
90  so = ros::SubscribeOptions::create<std_msgs::Float64>("tilt_equilibrium", 5,
91  boost::bind(&GazeboRsvBalance::cmdTiltCallback, this, _1),
92  ros::VoidPtr(), &this->queue_);
93  this->cmd_tilt_subscriber_ = this->gazebo_ros_->node()->subscribe(so);
94  ROS_INFO("%s: Subscribed to %s!", this->gazebo_ros_->info(), "tilt_equilibrium");
95 
96  // Odometry publisher
97  this->odometry_publisher_ = this->gazebo_ros_->node()->advertise<nav_msgs::Odometry>(this->odom_topic_, 10);
98  ROS_INFO("%s: Advertise odom on %s !", this->gazebo_ros_->info(), this->odom_topic_.c_str());
99  // State publisher
100  this->state_publisher_ = this->gazebo_ros_->node()->advertise<rsv_balance_msgs::State>("state", 10);
101  ROS_INFO("%s: Advertise system state on %s !", this->gazebo_ros_->info(), "state");
102  // Joint state publisher
103  this->joint_state_publisher_ = this->gazebo_ros_->node()->advertise<sensor_msgs::JointState>("joint_states", 10);
104  ROS_INFO("%s: Advertise joint_states!", gazebo_ros_->info());
105 
106  // Service for changing operating mode
107  ros::AdvertiseServiceOptions ao = ros::AdvertiseServiceOptions::create<rsv_balance_msgs::SetMode>("set_mode",
108  boost::bind(&GazeboRsvBalance::setMode, this, _1),
109  ros::VoidPtr(),
110  &this->queue_);
111  this->set_mode_server_ = this->gazebo_ros_->node()->advertiseService(ao);
112  // Service for chaning input source
113  ao = ros::AdvertiseServiceOptions::create<rsv_balance_msgs::SetInput>("set_input",
114  boost::bind(&GazeboRsvBalance::setInput, this, _1),
115  ros::VoidPtr(),
116  &this->queue_);
117  this->set_input_server_ = this->gazebo_ros_->node()->advertiseService(ao);
118  // Reset override service
119  ao = ros::AdvertiseServiceOptions::create<std_srvs::Empty>("reset_override",
120  boost::bind(&GazeboRsvBalance::resetOverride, this, _1),
121  ros::VoidPtr(),
122  &this->queue_);
123  this->reset_override_server_ = this->gazebo_ros_->node()->advertiseService(ao);
124  // Reset odom service
125  ao = ros::AdvertiseServiceOptions::create<std_srvs::Empty>("reset_odom",
126  boost::bind(&GazeboRsvBalance::resetOdom, this, _1),
127  ros::VoidPtr(),
128  &this->queue_);
129  this->reset_odom_server_ = this->gazebo_ros_->node()->advertiseService(ao);
130 
131  // Broadcaster for TF
133 
134  this->resetVariables();
136  this->x_desired_ = 0;
137  this->rot_desired_ = 0;
138  this->tilt_desired_ = 0;
139  this->u_control_ = this->state_control_.getControl();
140 
141  this->alive_ = true;
142  // start custom queue
143  this->callback_queue_thread_ = boost::thread(boost::bind(&GazeboRsvBalance::QueueThread, this));
144  // listen to the update event (broadcast every simulation iteration)
145  this->update_connection_ = event::Events::ConnectWorldUpdateBegin(boost::bind(&GazeboRsvBalance::UpdateChild, this));
146 }
147 
154 {
155  this->x_desired_ = 0;
156  this->rot_desired_ = 0;
157  this->odom_offset_pos_ = math::Vector3(0, 0, 0);
158  this->odom_offset_rot_ = math::Vector3(0, 0, 0);
159 }
160 
164 bool GazeboRsvBalance::setMode(rsv_balance_msgs::SetMode::Request &req)
165 {
166  // Ugly implementation means bad concept
167  switch (req.mode)
168  {
169  case rsv_balance_msgs::SetModeRequest::PARK:
170  ROS_INFO("%s: Mode: park", this->gazebo_ros_->info());
171  this->current_mode_ = PARK;
172  break;
173  case rsv_balance_msgs::SetModeRequest::TRACTOR:
174  ROS_INFO("%s: Mode: tractor", this->gazebo_ros_->info());
175  this->current_mode_ = TRACTOR;
176  break;
177  case rsv_balance_msgs::SetModeRequest::BALANCE:
178  ROS_INFO("%s: Mode: balance", this->gazebo_ros_->info());
179  this->current_mode_ = BALANCE;
180  break;
181  default:
182  return false;
183  };
184  return true;
185 }
186 
190 bool GazeboRsvBalance::setInput(rsv_balance_msgs::SetInput::Request &req)
191 {
192  // In simulation input should always be serial communication.
193  ROS_INFO("%s: Input: %d", this->gazebo_ros_->info(), req.input);
194  return true;
195 }
196 
200 bool GazeboRsvBalance::resetOverride(std_srvs::Empty::Request &req)
201 {
202  // In simulation we don't have RC override, nothing to reset
203  ROS_INFO("%s: Reset Override", this->gazebo_ros_->info());
204  return true;
205 }
206 
210 bool GazeboRsvBalance::resetOdom(std_srvs::Empty::Request &req)
211 {
212  ROS_INFO("%s: Reset Odom", this->gazebo_ros_->info());
213  this->resetOdometry();
214  return true;
215 }
216 
220 void GazeboRsvBalance::cmdVelCallback(const geometry_msgs::Twist::ConstPtr& cmd_msg)
221 {
222  this->x_desired_ = cmd_msg->linear.x;
223  this->rot_desired_ = cmd_msg->angular.z;
224 }
225 
229 void GazeboRsvBalance::cmdTiltCallback(const std_msgs::Float64::ConstPtr& cmd_tilt)
230 {
231  this->tilt_desired_ = cmd_tilt->data;
232 }
233 
238 {
239  // Store pitch and dpitch
240  math::Pose pose = this->parent_->GetWorldPose();
241  math::Vector3 veul = this->parent_->GetRelativeAngularVel();
242  this->imu_pitch_ = pose.rot.GetPitch();
243  this->imu_dpitch_ = veul.y;
244 }
245 
251 {
252  math::Pose pose = this->parent_->GetWorldPose();
253  this->odom_offset_pos_ = pose.pos;
254  this->odom_offset_rot_.z = pose.rot.GetYaw();
255 }
256 
262 {
263  double ang_velocity_left = -this->joints_[LEFT]->GetVelocity(0);
264  double ang_velocity_right = this->joints_[RIGHT]->GetVelocity(0);
265 
266  this->feedback_v_ = this->wheel_radius_/2.0 * (ang_velocity_right + ang_velocity_left);
267  this->feedback_w_ = this->wheel_radius_/this->wheel_separation_ * (ang_velocity_right - ang_velocity_left);
268 
269  if (odom_source_ == WORLD)
270  {
271  math::Pose pose = this->parent_->GetWorldPose();
272  math::Vector3 velocity_linear = this->parent_->GetRelativeLinearVel();
273  math::Vector3 velocity_angular = this->parent_->GetRelativeAngularVel();
274  // TODO(vmatos): reset odometry by setting an offset in world
275  // this->odom_.pose.pose.position.x = pose.pos[0] - this->odom_offset_pos_[0];
276  // this->odom_.pose.pose.position.y = pose.pos[1] - this->odom_offset_pos_[1];
277  // this->odom_.pose.pose.position.z = pose.pos[2] - this->odom_offset_pos_[2];
278  this->odom_.pose.pose.position.x = pose.pos[0];
279  this->odom_.pose.pose.position.y = pose.pos[1];
280  this->odom_.pose.pose.position.z = pose.pos[2];
281  // tf::Quaternion qt;
282  // qt.setRPY(pose.rot.GetRoll(), pose.rot.GetPitch(), pose.rot.GetYaw() - this->odom_offset_rot_[2]);
283  // qt.setRPY(pose.rot.GetRoll(), pose.rot.GetPitch(), pose.rot.GetYaw());
284  this->odom_.pose.pose.orientation.x = pose.rot.x;
285  this->odom_.pose.pose.orientation.y = pose.rot.y;
286  this->odom_.pose.pose.orientation.z = pose.rot.z;
287  this->odom_.pose.pose.orientation.w = pose.rot.w;
288  this->odom_.twist.twist.linear.x = velocity_linear[0];
289  this->odom_.twist.twist.linear.y = velocity_linear[1];
290  this->odom_.twist.twist.angular.z = velocity_angular.z;
291  }
292  else
293  {
294  ROS_WARN("%s - Odometry from other sources not yet supported.", this->gazebo_ros_->info());
295  }
296 }
297 
303 {
304  ros::Time current_time = ros::Time::now();
305  std::string odom_frame = this->gazebo_ros_->resolveTF(this->odom_frame_id_);
306  std::string base_frame = this->gazebo_ros_->resolveTF(this->base_frame_id_);
307 
308  // set odometry covariance
309  this->odom_.pose.covariance[0] = 0.00001;
310  this->odom_.pose.covariance[7] = 0.00001;
311  this->odom_.pose.covariance[14] = 1000000000000.0;
312  this->odom_.pose.covariance[21] = 1000000000000.0;
313  this->odom_.pose.covariance[28] = 1000000000000.0;
314  this->odom_.pose.covariance[35] = 0.001;
315  // set header
316  this->odom_.header.stamp = current_time;
317  this->odom_.header.frame_id = odom_frame;
318  this->odom_.child_frame_id = base_frame;
319  // Publish odometry
320  this->odometry_publisher_.publish(this->odom_);
321 
322  if (this->publish_odom_tf_)
323  {
324  tf::Vector3 vt;
325  tf::Quaternion qt;
326  vt = tf::Vector3(this->odom_.pose.pose.position.x,
327  this->odom_.pose.pose.position.y,
328  this->odom_.pose.pose.position.z);
329  qt = tf::Quaternion(this->odom_.pose.pose.orientation.x, this->odom_.pose.pose.orientation.y,
330  this->odom_.pose.pose.orientation.z, this->odom_.pose.pose.orientation.w);
331  tf::Transform base_to_odom(qt, vt);
332  this->transform_broadcaster_->sendTransform(
333  tf::StampedTransform(base_to_odom, current_time, odom_frame, base_frame));
334  }
335 }
336 
341 {
342  ros::Time current_time = ros::Time::now();
343 
344  sensor_msgs::JointState joint_state;
345  joint_state.header.stamp = current_time;
346  joint_state.name.resize(joints_.size());
347  joint_state.position.resize(joints_.size());
348 
349  for (int i = 0; i < 2; i++)
350  {
351  physics::JointPtr joint = this->joints_[i];
352  math::Angle angle = joint->GetAngle(0);
353  joint_state.name[i] = joint->GetName();
354  joint_state.position[i] = angle.Radian();
355  }
356  this->joint_state_publisher_.publish(joint_state);
357 }
358 
363 {
364  this->resetVariables();
366  // Reset control
367  this->last_update_time_ = this->parent_->GetWorld()->GetSimTime();
368  this->current_mode_ = BALANCE;
369  this->imu_pitch_ = 0;
370  this->imu_dpitch_ = 0;
371  this->feedback_v_ = 0;
372  this->feedback_w_ = 0;
373 }
374 
379 {
380  common::Time current_time = this->parent_->GetWorld()->GetSimTime();
381  double seconds_since_last_update = (current_time - this->last_update_time_).Double();
382 
383  // Only execute control loop on specified rate
384  if (seconds_since_last_update >= this->update_period_)
385  {
386  if (fabs(this->imu_pitch_) > .9 && this->current_mode_ == BALANCE) {
387  this->current_mode_ = TRACTOR;
388  }
389 
390  this->updateIMU();
391  this->updateOdometry();
392  this->publishOdometry();
393  this->publishWheelJointState();
394 
395  double x_desired[4];
397  x_desired[balance_control::dx] = x_desired_;
398  x_desired[balance_control::dphi] = rot_desired_;
399  x_desired[balance_control::dtheta] = 0;
400 
401  double y_fbk[4];
402  y_fbk[balance_control::theta] = this->imu_pitch_;
403  y_fbk[balance_control::dx] = this->feedback_v_;
404  y_fbk[balance_control::dphi] = this->feedback_w_;
405  y_fbk[balance_control::dtheta] = this->imu_dpitch_;
406 
407  this->state_control_.stepControl(seconds_since_last_update, x_desired, y_fbk);
408 
409  this->last_update_time_ += common::Time(this->update_period_);
410  }
411 
412  switch (this->current_mode_)
413  {
414  case BALANCE:
415  this->joints_[LEFT]->SetForce(0, -this->u_control_[balance_control::tauL]);
416  this->joints_[RIGHT]->SetForce(0, this->u_control_[balance_control::tauR]);
417  break;
418  case TRACTOR:
419  this->joints_[LEFT]->SetVelocity(0, -( (2.0*this->x_desired_ - this->rot_desired_*this->wheel_separation_)
420  / (this->wheel_radius_*2.0)));
421  this->joints_[RIGHT]->SetVelocity(0, ( (2.0*this->x_desired_ + this->rot_desired_*this->wheel_separation_)
422  / (this->wheel_radius_*2.0)));
423  break;
424  case PARK:
425  this->joints_[LEFT]->SetVelocity(0, 0);
426  this->joints_[RIGHT]->SetVelocity(0, 0);
427  this->joints_[LEFT]->SetForce(0, 0);
428  this->joints_[RIGHT]->SetForce(0, 0);
429  break;
430  };
431 }
432 
437 {
438  this->alive_ = false;
439  this->queue_.clear();
440  this->queue_.disable();
441  this->gazebo_ros_->node()->shutdown();
442  this->callback_queue_thread_.join();
443 }
444 
446 {
447  static const double timeout = 0.01;
448  while (this->alive_ && this->gazebo_ros_->node()->ok())
449  {
450  this->queue_.callAvailable(ros::WallDuration(timeout));
451  }
452 }
453 
455 } // namespace gazebo
std::map< std::string, Mode > mode_map_
void Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf)
bool setMode(rsv_balance_msgs::SetMode::Request &req)
Sets platform operating mode.
void publish(const boost::shared_ptr< M > &message) const
boost::thread callback_queue_thread_
ros::ServiceServer reset_odom_server_
void updateOdometry()
Updates odometry, from Gazebo world or from encoders.
void stepControl(double dt, const double(&x_desired)[4], const double(&y_fbk)[4])
void resetOdometry()
Resets odometry by adding offset to WORLD odometry, and resetting odometry values.
ros::ServiceServer set_input_server_
#define ROS_WARN(...)
ros::Subscriber cmd_tilt_subscriber_
ros::ServiceServer reset_override_server_
ros::Subscriber cmd_vel_subscriber_
bool resetOverride(std_srvs::Empty::Request &req)
Just exposes service. Not used in simulation.
void Reset()
Called when Gazebo resets world.
#define ROS_INFO(...)
void publishOdometry()
Publishes odometry and desired tfs.
ros::Publisher odometry_publisher_
event::ConnectionPtr update_connection_
ros::ServiceServer set_mode_server_
bool setInput(rsv_balance_msgs::SetInput::Request &req)
Just exposes service. Not used in simulation.
ros::CallbackQueue queue_
void publishWheelJointState()
Publishes wheel joint_states.
void resetVariables()
Resets simulation variables.
void cmdTiltCallback(const std_msgs::Float64::ConstPtr &cmd_tilt)
Callback to cmd_tilt.
ros::Publisher joint_state_publisher_
static Time now()
GZ_REGISTER_MODEL_PLUGIN(GazeboRosP3D)
void updateIMU()
Gets pitch angle values directly from Gazebo world.
std::vector< physics::JointPtr > joints_
balance_control::BalanceControl state_control_
virtual void UpdateChild()
Gazebo step update.
void cmdVelCallback(const geometry_msgs::Twist::ConstPtr &cmd_msg)
Callback to cmd_vel.
boost::shared_ptr< void > VoidPtr
#define ROS_ERROR(...)
bool resetOdom(std_srvs::Empty::Request &req)
Service to reset odometry.
boost::shared_ptr< tf::TransformBroadcaster > transform_broadcaster_
boost::shared_ptr< GazeboRos > GazeboRosPtr
virtual void FiniChild()
Called by gazebo upon exiting.


rsv_balance_gazebo
Author(s): Vitor Matos
autogenerated on Mon Jun 10 2019 15:06:42