twist_controller.cpp
Go to the documentation of this file.
1 //=================================================================================================
2 // Copyright (c) 2013, Johannes Meyer, TU Darmstadt
3 // All rights reserved.
4 
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above copyright
10 // notice, this list of conditions and the following disclaimer in the
11 // documentation and/or other materials provided with the distribution.
12 // * Neither the name of the Flight Systems and Automatic Control group,
13 // TU Darmstadt, nor the names of its contributors may be used to
14 // endorse or promote products derived from this software without
15 // 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 
31 
33 
34 #include <geometry_msgs/TwistStamped.h>
35 #include <geometry_msgs/WrenchStamped.h>
36 #include <std_srvs/Empty.h>
37 
38 #include <ros/subscriber.h>
39 #include <ros/callback_queue.h>
40 
41 #include <boost/thread.hpp>
42 
43 #include <limits>
44 
46 
47 using namespace controller_interface;
48 
49 class TwistController : public controller_interface::Controller<QuadrotorInterface>
50 {
51 public:
53  {}
54 
56  {}
57 
58  bool init(QuadrotorInterface *interface, ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)
59  {
60  // get interface handles
61  pose_ = interface->getPose();
62  twist_ = interface->getTwist();
63  acceleration_ = interface->getAcceleration();
64  twist_input_ = interface->addInput<TwistCommandHandle>("twist");
65  wrench_output_ = interface->addOutput<WrenchCommandHandle>("wrench");
66  node_handle_ = root_nh;
67 
68  // subscribe to commanded twist (geometry_msgs/TwistStamped) and cmd_vel (geometry_msgs/Twist)
69  twist_subscriber_ = node_handle_.subscribe<geometry_msgs::TwistStamped>("command/twist", 1, boost::bind(&TwistController::twistCommandCallback, this, _1));
70  cmd_vel_subscriber_ = node_handle_.subscribe<geometry_msgs::Twist>("cmd_vel", 1, boost::bind(&TwistController::cmd_velCommandCallback, this, _1));
71 
72  // engage/shutdown service servers
73  engage_service_server_ = node_handle_.advertiseService<std_srvs::Empty::Request, std_srvs::Empty::Response>("engage", boost::bind(&TwistController::engageCallback, this, _1, _2));
74  shutdown_service_server_ = node_handle_.advertiseService<std_srvs::Empty::Request, std_srvs::Empty::Response>("shutdown", boost::bind(&TwistController::shutdownCallback, this, _1, _2));
75 
76  // initialize PID controllers
77  pid_.linear.x.init(ros::NodeHandle(controller_nh, "linear/xy"));
78  pid_.linear.y.init(ros::NodeHandle(controller_nh, "linear/xy"));
79  pid_.linear.z.init(ros::NodeHandle(controller_nh, "linear/z"));
80  pid_.angular.x.init(ros::NodeHandle(controller_nh, "angular/xy"));
81  pid_.angular.y.init(ros::NodeHandle(controller_nh, "angular/xy"));
82  pid_.angular.z.init(ros::NodeHandle(controller_nh, "angular/z"));
83 
84  // load other parameters
85  controller_nh.getParam("auto_engage", auto_engage_ = true);
86  controller_nh.getParam("limits/load_factor", load_factor_limit = 1.5);
87  controller_nh.getParam("limits/force/z", limits_.force.z);
88  controller_nh.getParam("limits/torque/xy", limits_.torque.x);
89  controller_nh.getParam("limits/torque/xy", limits_.torque.y);
90  controller_nh.getParam("limits/torque/z", limits_.torque.z);
91  root_nh.param<std::string>("base_link_frame", base_link_frame_, "base_link");
92 
93  // get mass and inertia from QuadrotorInterface
94  interface->getMassAndInertia(mass_, inertia_);
95 
96  command_given_in_stabilized_frame_ = false;
97 
98  return true;
99  }
100 
101  void reset()
102  {
103  pid_.linear.x.reset();
104  pid_.linear.y.reset();
105  pid_.linear.z.reset();
106  pid_.angular.x.reset();
107  pid_.angular.y.reset();
108  pid_.angular.z.reset();
109 
110  wrench_.wrench.force.x = 0.0;
111  wrench_.wrench.force.y = 0.0;
112  wrench_.wrench.force.z = 0.0;
113  wrench_.wrench.torque.x = 0.0;
114  wrench_.wrench.torque.y = 0.0;
115  wrench_.wrench.torque.z = 0.0;
116 
117  linear_z_control_error_ = 0.0;
118  motors_running_ = false;
119  }
120 
121  void twistCommandCallback(const geometry_msgs::TwistStampedConstPtr& command)
122  {
123  boost::mutex::scoped_lock lock(command_mutex_);
124 
125  command_ = *command;
126  if (command_.header.stamp.isZero()) command_.header.stamp = ros::Time::now();
127  command_given_in_stabilized_frame_ = false;
128 
129  // start controller if it not running
130  if (!isRunning()) this->startRequest(command_.header.stamp);
131  }
132 
133  void cmd_velCommandCallback(const geometry_msgs::TwistConstPtr& command)
134  {
135  boost::mutex::scoped_lock lock(command_mutex_);
136 
137  command_.twist = *command;
138  command_.header.stamp = ros::Time::now();
139  command_given_in_stabilized_frame_ = true;
140 
141  // start controller if it not running
142  if (!isRunning()) this->startRequest(command_.header.stamp);
143  }
144 
145  bool engageCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
146  {
147  boost::mutex::scoped_lock lock(command_mutex_);
148 
149  ROS_INFO_NAMED("twist_controller", "Engaging motors!");
150  motors_running_ = true;
151  return true;
152  }
153 
154  bool shutdownCallback(std_srvs::Empty::Request&, std_srvs::Empty::Response&)
155  {
156  boost::mutex::scoped_lock lock(command_mutex_);
157 
158  ROS_INFO_NAMED("twist_controller", "Shutting down motors!");
159  motors_running_ = false;
160  return true;
161  }
162 
163  void starting(const ros::Time &time)
164  {
165  reset();
166  wrench_output_->start();
167  }
168 
169  void stopping(const ros::Time &time)
170  {
171  wrench_output_->stop();
172  }
173 
174  void update(const ros::Time& time, const ros::Duration& period)
175  {
176  boost::mutex::scoped_lock lock(command_mutex_);
177 
178  // Get twist command input
179  if (twist_input_->connected() && twist_input_->enabled()) {
180  command_.twist = twist_input_->getCommand();
181  command_given_in_stabilized_frame_ = false;
182  }
183 
184  // Get current state and command
185  Twist command = command_.twist;
186  Twist twist = twist_->twist();
187  Twist twist_body;
188  twist_body.linear = pose_->toBody(twist.linear);
189  twist_body.angular = pose_->toBody(twist.angular);
190 
191  // Transform to world coordinates if necessary (yaw only)
192  if (command_given_in_stabilized_frame_) {
193  double yaw = pose_->getYaw();
194  Twist transformed = command;
195  transformed.linear.x = cos(yaw) * command.linear.x - sin(yaw) * command.linear.y;
196  transformed.linear.y = sin(yaw) * command.linear.x + cos(yaw) * command.linear.y;
197  transformed.angular.x = cos(yaw) * command.angular.x - sin(yaw) * command.angular.y;
198  transformed.angular.y = sin(yaw) * command.angular.x + cos(yaw) * command.angular.y;
199  command = transformed;
200  }
201 
202  // Get gravity and load factor
203  const double gravity = 9.8065;
204  double load_factor = 1. / ( pose_->pose().orientation.w * pose_->pose().orientation.w
205  - pose_->pose().orientation.x * pose_->pose().orientation.x
206  - pose_->pose().orientation.y * pose_->pose().orientation.y
207  + pose_->pose().orientation.z * pose_->pose().orientation.z );
208  // Note: load_factor could be NaN or Inf...?
209  if (load_factor_limit > 0.0 && !(load_factor < load_factor_limit)) load_factor = load_factor_limit;
210 
211  // Auto engage/shutdown
212  if (auto_engage_) {
213  if (!motors_running_ && command.linear.z > 0.1 && load_factor > 0.0) {
214  motors_running_ = true;
215  ROS_INFO_NAMED("twist_controller", "Engaging motors!");
216  } else if (motors_running_ && command.linear.z < -0.1 /* && (twist.linear.z > -0.1 && twist.linear.z < 0.1) */) {
217  double shutdown_limit = 0.25 * std::min(command.linear.z, -0.5);
218  if (linear_z_control_error_ > 0.0) linear_z_control_error_ = 0.0; // positive control errors should not affect shutdown
219  if (pid_.linear.z.getFilteredControlError(linear_z_control_error_, 5.0, period) < shutdown_limit) {
220  motors_running_ = false;
221  ROS_INFO_NAMED("twist_controller", "Shutting down motors!");
222  } else {
223  ROS_DEBUG_STREAM_NAMED("twist_controller", "z control error = " << linear_z_control_error_ << " >= " << shutdown_limit);
224  }
225  } else {
226  linear_z_control_error_ = 0.0;
227  }
228 
229  // flip over?
230  if (motors_running_ && load_factor < 0.0) {
231  motors_running_ = false;
232  ROS_WARN_NAMED("twist_controller", "Shutting down motors due to flip over!");
233  }
234  }
235 
236  // Update output
237  if (motors_running_) {
238  Vector3 acceleration_command;
239  acceleration_command.x = pid_.linear.x.update(command.linear.x, twist.linear.x, acceleration_->acceleration().x, period);
240  acceleration_command.y = pid_.linear.y.update(command.linear.y, twist.linear.y, acceleration_->acceleration().y, period);
241  acceleration_command.z = pid_.linear.z.update(command.linear.z, twist.linear.z, acceleration_->acceleration().z, period) + gravity;
242  Vector3 acceleration_command_body = pose_->toBody(acceleration_command);
243 
244  ROS_DEBUG_STREAM_NAMED("twist_controller", "twist.linear: [" << twist.linear.x << " " << twist.linear.y << " " << twist.linear.z << "]");
245  ROS_DEBUG_STREAM_NAMED("twist_controller", "twist_body.angular: [" << twist_body.angular.x << " " << twist_body.angular.y << " " << twist_body.angular.z << "]");
246  ROS_DEBUG_STREAM_NAMED("twist_controller", "twist_command.linear: [" << command.linear.x << " " << command.linear.y << " " << command.linear.z << "]");
247  ROS_DEBUG_STREAM_NAMED("twist_controller", "twist_command.angular: [" << command.angular.x << " " << command.angular.y << " " << command.angular.z << "]");
248  ROS_DEBUG_STREAM_NAMED("twist_controller", "acceleration: [" << acceleration_->acceleration().x << " " << acceleration_->acceleration().y << " " << acceleration_->acceleration().z << "]");
249  ROS_DEBUG_STREAM_NAMED("twist_controller", "acceleration_command_world: [" << acceleration_command.x << " " << acceleration_command.y << " " << acceleration_command.z << "]");
250  ROS_DEBUG_STREAM_NAMED("twist_controller", "acceleration_command_body: [" << acceleration_command_body.x << " " << acceleration_command_body.y << " " << acceleration_command_body.z << "]");
251 
252  wrench_.wrench.torque.x = inertia_[0] * pid_.angular.x.update(-acceleration_command_body.y / gravity, 0.0, twist_body.angular.x, period);
253  wrench_.wrench.torque.y = inertia_[1] * pid_.angular.y.update( acceleration_command_body.x / gravity, 0.0, twist_body.angular.y, period);
254  wrench_.wrench.torque.z = inertia_[2] * pid_.angular.z.update( command.angular.z, twist.angular.z, 0.0, period);
255  wrench_.wrench.force.x = 0.0;
256  wrench_.wrench.force.y = 0.0;
257  wrench_.wrench.force.z = mass_ * ((acceleration_command.z - gravity) * load_factor + gravity);
258 
259  if (limits_.force.z > 0.0 && wrench_.wrench.force.z > limits_.force.z) wrench_.wrench.force.z = limits_.force.z;
260  if (wrench_.wrench.force.z <= std::numeric_limits<double>::min()) wrench_.wrench.force.z = std::numeric_limits<double>::min();
261  if (limits_.torque.x > 0.0) {
262  if (wrench_.wrench.torque.x > limits_.torque.x) wrench_.wrench.torque.x = limits_.torque.x;
263  if (wrench_.wrench.torque.x < -limits_.torque.x) wrench_.wrench.torque.x = -limits_.torque.x;
264  }
265  if (limits_.torque.y > 0.0) {
266  if (wrench_.wrench.torque.y > limits_.torque.y) wrench_.wrench.torque.y = limits_.torque.y;
267  if (wrench_.wrench.torque.y < -limits_.torque.y) wrench_.wrench.torque.y = -limits_.torque.y;
268  }
269  if (limits_.torque.z > 0.0) {
270  if (wrench_.wrench.torque.z > limits_.torque.z) wrench_.wrench.torque.z = limits_.torque.z;
271  if (wrench_.wrench.torque.z < -limits_.torque.z) wrench_.wrench.torque.z = -limits_.torque.z;
272  }
273 
274  ROS_DEBUG_STREAM_NAMED("twist_controller", "wrench_command.force: [" << wrench_.wrench.force.x << " " << wrench_.wrench.force.y << " " << wrench_.wrench.force.z << "]");
275  ROS_DEBUG_STREAM_NAMED("twist_controller", "wrench_command.torque: [" << wrench_.wrench.torque.x << " " << wrench_.wrench.torque.y << " " << wrench_.wrench.torque.z << "]");
276 
277  } else {
278  reset();
279  }
280 
281  // set wrench output
282  wrench_.header.stamp = time;
283  wrench_.header.frame_id = base_link_frame_;
284  wrench_output_->setCommand(wrench_.wrench);
285  }
286 
287 private:
293 
299 
300  geometry_msgs::TwistStamped command_;
301  geometry_msgs::WrenchStamped wrench_;
303  std::string base_link_frame_;
304 
305  struct {
306  struct {
310  } linear, angular;
311  } pid_;
312 
313  geometry_msgs::Wrench limits_;
316  double mass_;
317  double inertia_[3];
318 
321  boost::mutex command_mutex_;
322 
323 };
324 
325 } // namespace hector_quadrotor_controller
326 
virtual bool getMassAndInertia(double &mass, double inertia[3])
boost::shared_ptr< HandleType > addOutput(const std::string &name)
#define ROS_INFO_NAMED(name,...)
#define ROS_DEBUG_STREAM_NAMED(name, args)
#define ROS_WARN_NAMED(name,...)
void update(const ros::Time &time, const ros::Duration &period)
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
boost::shared_ptr< HandleType > addInput(const std::string &name)
virtual AccelerationHandlePtr getAcceleration()
bool engageCallback(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
ROSLIB_DECL std::string command(const std::string &cmd)
void twistCommandCallback(const geometry_msgs::TwistStampedConstPtr &command)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
bool getParam(const std::string &key, std::string &s) const
static Time now()
void cmd_velCommandCallback(const geometry_msgs::TwistConstPtr &command)
bool shutdownCallback(std_srvs::Empty::Request &, std_srvs::Empty::Response &)
bool init(QuadrotorInterface *interface, ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)


hector_quadrotor_controller
Author(s): Johannes Meyer
autogenerated on Sun Jul 10 2016 04:30:48