position_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 
30 #include <control_toolbox/pid.h>
31 #include <geometry_msgs/TwistStamped.h>
33 #include <limits>
34 #include <ros/subscriber.h>
36 #include <boost/thread/mutex.hpp>
37 #include <geometry_msgs/PoseStamped.h>
38 #include <geometry_msgs/Twist.h>
39 #include <tf/transform_listener.h> // for tf::getPrefixParam()
43 #include <visualization_msgs/Marker.h>
44 #include <cstdlib>
45 #include <cmath>
48 
50 {
51 
52 using namespace hector_quadrotor_interface;
53 
54 class PositionController : public controller_interface::Controller<QuadrotorInterface>
55 {
56 public:
58  : pose_command_valid_(false), twist_limit_valid_(false)
59  {
60  }
61 
63  {
64  pose_subscriber_.shutdown();
65  twist_limit_subscriber_.shutdown();
66  }
67 
68  virtual bool init(QuadrotorInterface *interface,
69  ros::NodeHandle &root_nh,
70  ros::NodeHandle &controller_nh)
71  {
72  // get interface handles
73  pose_ = interface->getPose();
74  twist_ = interface->getTwist();
75  motor_status_ = interface->getMotorStatus();
76 
77  root_nh.param<std::string>("base_link_frame", base_link_frame_, "base_link");
78  root_nh.param<std::string>("world_frame", world_frame_, "/world");
79  root_nh.param<std::string>("base_stabilized_frame", base_stabilized_frame_, "base_stabilized");
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  pid_.yaw.init(ros::NodeHandle(controller_nh, "yaw"));
92 
93  position_limiter_.init(controller_nh);
94 
95  // Setup pose visualization marker output
96  initMarker(root_nh.getNamespace());
97  marker_publisher_ = root_nh.advertise<visualization_msgs::Marker>("command/pose_marker", 1);
98 
99  // Initialize inputs/outputs
100  pose_input_ = interface->addInput<PoseCommandHandle>("pose");
101  twist_input_ = interface->addInput<TwistCommandHandle>("pose/twist");
102  twist_limit_input_ = interface->addInput<TwistCommandHandle>("pose/twist_limit");
103  twist_output_ = interface->addOutput<TwistCommandHandle>("twist");
104 
105  // subscribe to commanded pose and velocity
106  pose_subscriber_ = root_nh.subscribe<geometry_msgs::PoseStamped>("command/pose", 1, boost::bind(
108  twist_limit_subscriber_ = root_nh.subscribe<geometry_msgs::Twist>("command/twist_limit", 1, boost::bind(
110 
111  return true;
112  }
113 
114  void reset()
115  {
116  pid_.x.reset();
117  pid_.y.reset();
118  pid_.z.reset();
119  pid_.yaw.reset();
120 
121  // Set commanded pose to robot's current pose
122  updatePoseCommand(pose_->pose());
123  pose_command_valid_ = false;
124  }
125 
126  virtual void starting(const ros::Time &time)
127  {
128  reset();
129  }
130 
131  virtual void stopping(const ros::Time &time)
132  {
133  twist_output_->stop();
134  pose_command_valid_ = false;
135 // twist_limit_valid_ = false;
136  }
137 
138  void poseCommandCallback(const geometry_msgs::PoseStampedConstPtr &command)
139  {
140  boost::mutex::scoped_lock lock(command_mutex_);
141 
142  ros::Time start_time = command->header.stamp;
143  if (start_time.isZero()) start_time = ros::Time::now();
144  if (!isRunning()) this->startRequest(start_time);
145 
146  updatePoseCommand(*command);
147  }
148 
149  void twistLimitCallback(const geometry_msgs::TwistConstPtr &limit)
150  {
151  boost::mutex::scoped_lock lock(command_mutex_);
152 
153  twist_limit_ = *limit;
154  twist_limit_valid_ = true;
155  }
156 
157  virtual void update(const ros::Time &time, const ros::Duration &period)
158  {
159  boost::mutex::scoped_lock lock(command_mutex_);
160  Twist output;
161 
162  // Get pose command command input
163  if (pose_input_->connected() && pose_input_->enabled())
164  {
165  updatePoseCommand(pose_input_->getCommand());
166  }
167 
168  // Get twist limit input
169  if (twist_limit_input_->connected() && twist_limit_input_->enabled())
170  {
171  twist_limit_ = twist_limit_input_->getCommand();
172  twist_limit_valid_ = true;
173  }
174 
175  // check command timeout
176  // TODO
177 
178  // Check if pose control was preempted
179  if (twist_output_->preempted()) {
180  if (pose_command_valid_) {
181  ROS_INFO_NAMED("position_controller", "Position control preempted!");
182  }
183  pose_command_valid_ = false;
184  }
185 
186  // Check if motors are running
187  if (motor_status_->motorStatus().running == false) {
188  if (pose_command_valid_) {
189  ROS_INFO_NAMED("position_controller", "Disabled position control while motors are not running.");
190  }
191  pose_command_valid_ = false;
192  }
193 
194  // Abort if no pose command is available
195  if (!pose_command_valid_) {
196  reset();
197  twist_output_->stop();
198  return;
199  } else {
200  twist_output_->start();
201  }
202 
203  Pose pose = pose_->pose();
204 // Twist twist = twist_->twist();
205 
206  double yaw_command;
207  {
208  tf2::Quaternion q;
209  double temp;
210  tf2::fromMsg(pose_command_.orientation, q);
211  tf2::Matrix3x3(q).getRPY(temp, temp, yaw_command);
212  }
213 
214  double yaw = pose_->getYaw();
215 
216  pose_command_.position = position_limiter_(pose_command_.position);
217 
218  output.linear.x = pid_.x.computeCommand(pose_command_.position.x - pose.position.x, period);
219  output.linear.y = pid_.y.computeCommand(pose_command_.position.y - pose.position.y, period);
220  output.linear.z = pid_.z.computeCommand(pose_command_.position.z - pose.position.z, period);
221 
222  double yaw_error = yaw_command - yaw;
223  // detect wrap around pi and compensate
224  if (yaw_error > M_PI)
225  {
226  yaw_error -= 2 * M_PI;
227  }
228  else if (yaw_error < -M_PI)
229  {
230  yaw_error += 2 * M_PI;
231  }
232  output.angular.z = pid_.yaw.computeCommand(yaw_error, period);
233 
234  // add twist command if available
235  if (twist_input_->connected() && twist_input_->enabled())
236  {
237  output.linear.x += twist_input_->getCommand().linear.x;
238  output.linear.y += twist_input_->getCommand().linear.y;
239  output.linear.z += twist_input_->getCommand().linear.z;
240  output.angular.x += twist_input_->getCommand().angular.x;
241  output.angular.y += twist_input_->getCommand().angular.y;
242  output.angular.z += twist_input_->getCommand().angular.z;
243  }
244 
245  // limit twist
246  if (twist_limit_valid_)
247  {
248  double linear_xy = sqrt(output.linear.x*output.linear.x + output.linear.y*output.linear.y);
249  double limit_linear_xy = std::max(twist_limit_.linear.x, twist_limit_.linear.y);
250  if (limit_linear_xy > 0.0 && linear_xy > limit_linear_xy) {
251  output.linear.x *= limit_linear_xy / linear_xy;
252  output.linear.y *= limit_linear_xy / linear_xy;
253  }
254  if (twist_limit_.linear.z > 0.0 && fabs(output.linear.z) > twist_limit_.linear.z) {
255  output.linear.z *= twist_limit_.linear.z / fabs(output.linear.z);
256  }
257  double angular_xy = sqrt(output.angular.x*output.angular.x + output.angular.y*output.angular.y);
258  double limit_angular_xy = std::max(twist_limit_.angular.x, twist_limit_.angular.y);
259  if (limit_angular_xy > 0.0 && angular_xy > limit_angular_xy) {
260  output.angular.x *= limit_angular_xy / angular_xy;
261  output.angular.y *= limit_angular_xy / angular_xy;
262  }
263  if (twist_limit_.angular.z > 0.0 && fabs(output.angular.z) > twist_limit_.angular.z) {
264  output.angular.z *= twist_limit_.angular.z / fabs(output.angular.z);
265  }
266  }
267 
268  // set twist output
269  twist_output_->setCommand(output);
270  }
271 
272 private:
273  void updatePoseCommand(const geometry_msgs::PoseStamped &new_pose)
274  {
275  // TODO TF to world frame
276  if (new_pose.header.frame_id != world_frame_) {
277  ROS_WARN_STREAM_THROTTLE_NAMED(1.0, "position_controller", "Pose commands must be given in the " << world_frame_ << " frame, ignoring command");
278  }
279  else
280  {
281  updatePoseCommand(new_pose.pose);
282  }
283  }
284 
285  void updatePoseCommand(const geometry_msgs::Pose &new_pose)
286  {
287  {
288  pose_command_.position = new_pose.position;
289  // Strip non-yaw components from orientation
290  tf2::Quaternion q;
291  double roll, pitch, yaw;
292  tf2::fromMsg(new_pose.orientation, q);
293  tf2::Matrix3x3(q).getRPY(roll, pitch, yaw);
294  q.setRPY(0, 0, yaw);
295  pose_command_.orientation = tf2::toMsg(q);
296  pose_command_valid_ = true;
297  }
298  pose_marker_.pose = pose_command_;
299  marker_publisher_.publish(pose_marker_);
300  }
301 
302  void initMarker(std::string name)
303  {
304  pose_marker_.header.frame_id = world_frame_;
305  pose_marker_.ns = name;
306  pose_marker_.id = 0;
307  pose_marker_.type = visualization_msgs::Marker::ARROW;
308  pose_marker_.scale.x = 0.15;
309  pose_marker_.scale.y = pose_marker_.scale.z = 0.03;
310  pose_marker_.color.r = 0.5;
311  pose_marker_.color.g = 0.5;
312  pose_marker_.color.r = 0.5;
313  pose_marker_.color.a = 1.0;
314  }
315 
319 
324 
326 
329 
330  visualization_msgs::Marker pose_marker_;
331 
332  geometry_msgs::Pose pose_command_;
333  geometry_msgs::Twist twist_limit_;
334  bool pose_command_valid_, twist_limit_valid_;
335 
336  std::string base_link_frame_, base_stabilized_frame_, world_frame_;
337  std::string tf_prefix_;
338 
339  struct
340  {
342  } pid_;
343 
344  boost::mutex command_mutex_;
345 };
346 
347 } // namespace hector_quadrotor_controllers
348 
void setRPY(const tf2Scalar &roll, const tf2Scalar &pitch, const tf2Scalar &yaw)
void twistLimitCallback(const geometry_msgs::TwistConstPtr &limit)
void getRPY(tf2Scalar &roll, tf2Scalar &pitch, tf2Scalar &yaw, unsigned int solution_number=1) const
#define ROS_INFO_NAMED(name,...)
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())
hector_quadrotor_interface::PointLimiter position_limiter_
boost::shared_ptr< HandleType > addInput(const std::string &name)
void poseCommandCallback(const geometry_msgs::PoseStampedConstPtr &command)
TFSIMD_FORCE_INLINE const tfScalar & y() const
std::string resolve(const std::string &prefix, const std::string &frame_name)
void updatePoseCommand(const geometry_msgs::Pose &new_pose)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
#define ROS_WARN_STREAM_THROTTLE_NAMED(rate, name, args)
void fromMsg(const A &, B &b)
const std::string & getNamespace() const
TFSIMD_FORCE_INLINE const tfScalar & x() const
boost::shared_ptr< HandleType > addOutput(const std::string &name)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
B toMsg(const A &a)
virtual void update(const ros::Time &time, const ros::Duration &period)
static Time now()
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
virtual bool init(QuadrotorInterface *interface, ros::NodeHandle &root_nh, ros::NodeHandle &controller_nh)
void updatePoseCommand(const geometry_msgs::PoseStamped &new_pose)


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