velocity_controller.cpp
Go to the documentation of this file.
1 //=================================================================================================
2 // Copyright (c) 2012-2016, Institute of Flight Systems and Automatic Control,
3 // Technische Universität Darmstadt.
4 // All rights reserved.
5 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of hector_quadrotor nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 //=================================================================================================
28 
29 #include <ros/ros.h>
31 
33 #include <control_toolbox/pid.h>
34 
35 #include <geometry_msgs/TwistStamped.h>
36 #include <geometry_msgs/WrenchStamped.h>
37 
38 #include <tf/transform_listener.h> // for tf::getPrefixParam()
39 
43 
44 #include <boost/thread/mutex.hpp>
45 #include <limits>
46 
48 {
49 
50 using namespace hector_quadrotor_interface;
51 
52 class VelocityController : public controller_interface::Controller<hector_quadrotor_interface::QuadrotorInterface>
53 {
54 public:
56  {
57  }
58 
60  {
61  twist_subscriber_.shutdown();
62  cmd_vel_subscriber_.shutdown();
63  }
64 
66  ros::NodeHandle &controller_nh)
67  {
68  // get interface handles
69  pose_ = interface->getPose();
70  twist_ = interface->getTwist();
71 // acceleration_ = interface->getAcceleration();
72  motor_status_ = interface->getMotorStatus();
73 
74  // load parameters
75  root_nh.param<std::string>("base_link_frame", base_link_frame_, "base_link");
76  root_nh.param<std::string>("world_frame", world_frame_, "/world");
77  root_nh.param<std::string>("base_stabilized_frame", base_stabilized_frame_, "base_stabilized");
78  controller_nh.param("limits/load_factor", load_factor_limit_, 1.5);
79  getMassAndInertia(root_nh, mass_, inertia_);
80 
81  // resolve frames
82  tf_prefix_ = tf::getPrefixParam(root_nh);
83  world_frame_ = tf::resolve(tf_prefix_, world_frame_);
84  base_link_frame_ = tf::resolve(tf_prefix_, base_link_frame_);
85  base_stabilized_frame_ = tf::resolve(tf_prefix_, base_stabilized_frame_);
86 
87  // initialize PID controllers
88  pid_.x.init(ros::NodeHandle(controller_nh, "x"));
89  pid_.y.init(ros::NodeHandle(controller_nh, "y"));
90  pid_.z.init(ros::NodeHandle(controller_nh, "z"));
91 
92  twist_limiter_.linear.init(controller_nh);
93  twist_limiter_.angular.init(controller_nh, "yawrate");
94 
95  // initialize inputs/outputs
96  twist_input_ = interface->addInput<TwistCommandHandle>("twist");
97  attitude_output_ = interface->addOutput<AttitudeCommandHandle>("attitude");
98  yawrate_output_ = interface->addOutput<YawrateCommandHandle>("yawrate");
99  thrust_output_ = interface->addOutput<ThrustCommandHandle>("thrust");
100 
101  // Subscribe to commanded twist (geometry_msgs/TwistStamped) and cmd_vel (geometry_msgs/Twist)
102  twist_subscriber_ = root_nh.subscribe<geometry_msgs::TwistStamped>("command/twist", 1, boost::bind(
104  cmd_vel_subscriber_ = root_nh.subscribe<geometry_msgs::Twist>("cmd_vel", 1, boost::bind(
106 
107  return true;
108  }
109 
110  void reset()
111  {
112  pid_.x.reset();
113  pid_.y.reset();
114  pid_.z.reset();
115 
116  // Reset command
117  twist_command_ = TwistStamped();
118  }
119 
120  virtual void starting(const ros::Time &time)
121  {
122  reset();
123  }
124 
125  virtual void stopping(const ros::Time &time)
126  {
127  attitude_output_->stop();
128  yawrate_output_->stop();
129  thrust_output_->stop();
130  }
131 
132  void twistCommandCallback(const geometry_msgs::TwistStampedConstPtr &command)
133  {
134  boost::mutex::scoped_lock lock(command_mutex_);
135 
136  // start controller if it not running
137  if (!isRunning()) this->startRequest(command->header.stamp);
138 
139  std::string frame_id = tf::resolve(tf_prefix_, command->header.frame_id);
140  if (frame_id != base_stabilized_frame_ && frame_id != world_frame_) {
141  ROS_WARN_STREAM_THROTTLE_NAMED(1.0, "velocity_controller", "Velocity commands must be given in either the "
142  << base_stabilized_frame_ << " or the "
143  << world_frame_ << " frame, ignoring command");
144  return;
145  }
146 
147  twist_command_ = *command;
148  twist_command_.header.frame_id = frame_id; // == resolved frame_id
149 
150  // disable position control if command is non-zero
151  if ((twist_command_.twist.linear.x != 0.0) ||
152  (twist_command_.twist.linear.y != 0.0) ||
153  (twist_command_.twist.linear.z != 0.0) ||
154  (twist_command_.twist.angular.x != 0.0) ||
155  (twist_command_.twist.angular.y != 0.0) ||
156  (twist_command_.twist.angular.z != 0.0)) {
157  twist_input_->preempt();
158  }
159  }
160 
161  void cmd_velCommandCallback(const geometry_msgs::TwistConstPtr &command)
162  {
163  boost::mutex::scoped_lock lock(command_mutex_);
164  ros::Time now = ros::Time::now();
165 
166  // start controller if it not running
167  if (!isRunning()) this->startRequest(now);
168 
169  twist_command_.twist = *command;
170  twist_command_.header.stamp = now;
171  twist_command_.header.frame_id = base_stabilized_frame_;
172 
173  // disable position control if command is non-zero
174  if ((twist_command_.twist.linear.x != 0.0) ||
175  (twist_command_.twist.linear.y != 0.0) ||
176  (twist_command_.twist.linear.z != 0.0) ||
177  (twist_command_.twist.angular.x != 0.0) ||
178  (twist_command_.twist.angular.y != 0.0) ||
179  (twist_command_.twist.angular.z != 0.0)) {
180  twist_input_->preempt();
181  }
182  }
183 
184  virtual void update(const ros::Time &time, const ros::Duration &period)
185  {
186  boost::mutex::scoped_lock lock(command_mutex_);
187 
188  // Get twist command input
189  if (twist_input_->connected() && twist_input_->enabled())
190  {
191  twist_command_.twist = twist_input_->getCommand();
192  twist_command_.header.stamp = time;
193  twist_command_.header.frame_id = world_frame_;
194  }
195 
196  // Reset if motors are not running
197  if (motor_status_->motorStatus().running == false) {
198  reset();
199  }
200 
201  // Start outputs
202  attitude_output_->start();
203  yawrate_output_->start();
204  thrust_output_->start();
205 
206  // Limit twist input
207  Twist command = twist_limiter_(twist_command_.twist);
208 
209  // Get current yaw and twist in body frame
210  double yaw = pose_->getYaw(), sin_yaw, cos_yaw;
211  sincos(yaw, &sin_yaw, &cos_yaw);
212  Twist twist = twist_->twist(), twist_body;
213  twist_body.linear = pose_->toBody(twist.linear);
214  twist_body.angular = pose_->toBody(twist.angular);
215 
216  // Velocity is controlled in the world frame!
217  // Transform to world coordinates if necessary (yaw only)
218  if (twist_command_.header.frame_id == base_stabilized_frame_) {
219  Twist transformed = command;
220  transformed.linear.x = cos_yaw * command.linear.x - sin_yaw * command.linear.y;
221  transformed.linear.y = sin_yaw * command.linear.x + cos_yaw * command.linear.y;
222  transformed.angular.x = cos_yaw * command.angular.x - sin_yaw * command.angular.y;
223  transformed.angular.y = sin_yaw * command.angular.x + cos_yaw * command.angular.y;
224  command = transformed;
225  }
226 
227  // Get gravity and load factor
228  const double gravity = 9.8065;
229  double load_factor = 1. / ( pose_->pose().orientation.w * pose_->pose().orientation.w
230  - pose_->pose().orientation.x * pose_->pose().orientation.x
231  - pose_->pose().orientation.y * pose_->pose().orientation.y
232  + pose_->pose().orientation.z * pose_->pose().orientation.z );
233  // Note: load_factor could be NaN or Inf...?
234  if (load_factor_limit_ > 0.0 && !(load_factor < load_factor_limit_)) load_factor = load_factor_limit_;
235 
236  // Run PID loops
237  Vector3 acceleration_command;
238  acceleration_command.x = pid_.x.computeCommand(command.linear.x - twist.linear.x, period);
239  acceleration_command.y = pid_.y.computeCommand(command.linear.y - twist.linear.y, period);
240  acceleration_command.z = pid_.z.computeCommand(command.linear.z - twist.linear.z, period) + gravity;
241 
242  // Transform acceleration command to base_stabilized frame
243  Vector3 acceleration_command_base_stabilized;
244  acceleration_command_base_stabilized.x = cos_yaw * acceleration_command.x + sin_yaw * acceleration_command.y;
245  acceleration_command_base_stabilized.y = -sin_yaw * acceleration_command.x + cos_yaw * acceleration_command.y;
246  acceleration_command_base_stabilized.z = acceleration_command.z;
247 
248  ROS_DEBUG_STREAM_NAMED("velocity_controller", "twist.linear: [" << twist.linear.x << " " << twist.linear.y << " " << twist.linear.z << "]");
249  ROS_DEBUG_STREAM_NAMED("velocity_controller", "twist_body.angular: [" << twist_body.angular.x << " " << twist_body.angular.y << " " << twist_body.angular.z << "]");
250  ROS_DEBUG_STREAM_NAMED("velocity_controller", "twist_command.linear: [" << command.linear.x << " " << command.linear.y << " " << command.linear.z << "]");
251  ROS_DEBUG_STREAM_NAMED("velocity_controller", "twist_command.angular: [" << command.angular.x << " " << command.angular.y << " " << command.angular.z << "]");
252 // ROS_DEBUG_STREAM_NAMED("velocity_controller", "acceleration: [" << acceleration_->acceleration().x << " " << acceleration_->acceleration().y << " " << acceleration_->acceleration().z << "]");
253  ROS_DEBUG_STREAM_NAMED("velocity_controller", "acceleration_command_world: [" << acceleration_command.x << " " << acceleration_command.y << " " << acceleration_command.z << "]");
254  ROS_DEBUG_STREAM_NAMED("velocity_controller", "acceleration_command_body: [" << acceleration_command_base_stabilized.x << " " << acceleration_command_base_stabilized.y << " " << acceleration_command_base_stabilized.z << "]");
255 
256  hector_uav_msgs::AttitudeCommand attitude_control;
257  hector_uav_msgs::YawrateCommand yawrate_control;
258  hector_uav_msgs::ThrustCommand thrust_control;
259  attitude_control.roll = -asin(std::min(std::max(acceleration_command_base_stabilized.y / gravity, -1.0), 1.0));
260  attitude_control.pitch = asin(std::min(std::max(acceleration_command_base_stabilized.x / gravity, -1.0), 1.0));
261  yawrate_control.turnrate = command.angular.z;
262  thrust_control.thrust = mass_ * ((acceleration_command.z - gravity) * load_factor + gravity);
263 
264  // pass down time stamp from twist command
265  attitude_control.header.stamp = twist_command_.header.stamp;
266  yawrate_control.header.stamp = twist_command_.header.stamp;
267  thrust_control.header.stamp = twist_command_.header.stamp;
268 
269  // Update output from controller
270  attitude_output_->setCommand(attitude_control);
271  yawrate_output_->setCommand(yawrate_control);
272  thrust_output_->setCommand(thrust_control);
273  }
274 
275 private:
279 
284 
287 
288  geometry_msgs::TwistStamped twist_command_;
289 
291  std::string base_link_frame_, base_stabilized_frame_, world_frame_;
292  std::string tf_prefix_;
293 
294  struct
295  {
297  } pid_;
298 
300  double mass_;
301  double inertia_[3];
302 
303  boost::mutex command_mutex_;
304 };
305 
306 } // namespace hector_quadrotor_controllers
307 
virtual void update(const ros::Time &time, const ros::Duration &period)
void cmd_velCommandCallback(const geometry_msgs::TwistConstPtr &command)
#define ROS_DEBUG_STREAM_NAMED(name, args)
std::string getPrefixParam(ros::NodeHandle &nh)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
boost::shared_ptr< HandleType > addInput(const std::string &name)
TFSIMD_FORCE_INLINE const tfScalar & y() const
std::string resolve(const std::string &prefix, const std::string &frame_name)
void twistCommandCallback(const geometry_msgs::TwistStampedConstPtr &command)
ROSLIB_DECL std::string command(const std::string &cmd)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
TFSIMD_FORCE_INLINE Vector3()
INLINE Rall1d< T, V, S > asin(const Rall1d< T, V, S > &x)
#define ROS_WARN_STREAM_THROTTLE_NAMED(rate, name, args)
virtual bool init(hector_quadrotor_interface::QuadrotorInterface *interface, ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)
TFSIMD_FORCE_INLINE const tfScalar & x() const
boost::shared_ptr< HandleType > addOutput(const std::string &name)
hector_quadrotor_interface::TwistLimiter twist_limiter_
static Time now()
bool getMassAndInertia(const ros::NodeHandle &nh, double &mass, double inertia[3])
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)


hector_quadrotor_controllers
Author(s): Johannes Meyer , Paul Bovbel
autogenerated on Mon Jun 10 2019 13:36:53