controller.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2015 Fetch Robotics Inc
3  * Author: Michael Ferguson
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
20 
21 #include <angles/angles.h>
22 
23 #include <algorithm>
24 #include <list>
25 #include <vector>
26 #include <cmath>
27 
29 {
30  // Create publishers
31  path_pub_ = nh.advertise<nav_msgs::Path>("path", 10);
32  cmd_vel_pub_ = nh.advertise<geometry_msgs::Twist>("cmd_vel", 10);
33 
34  // TODO(enhancement): these should be loaded from ROS params
35  k1_ = 3;
36  k2_ = 2;
37  min_velocity_ = 0.15;
38  max_velocity_ = 0.15;
40  beta_ = 0.2;
41  lambda_ = 2.0;
42 }
43 
44 bool BaseController::approach(const geometry_msgs::PoseStamped& target)
45 {
46  // Transform pose by -dist_ in the X direction of the dock
47  geometry_msgs::PoseStamped pose = target;
48  {
50  tf::quaternionMsgToTF(pose.pose.orientation, q);
51  double theta = angles::normalize_angle(tf::getYaw(q));
52  // If the target has an invalid orientation then don't approach it.
53  if (!std::isfinite(theta))
54  {
55  ROS_ERROR_STREAM_NAMED("controller", "Invalid approach target for docking.");
56  stop();
57  return true;
58  }
59  pose.pose.position.x += cos(theta) * -dist_;
60  pose.pose.position.y += sin(theta) * -dist_;
61  }
62 
63  // Transform target into base frame
64  try
65  {
66  pose.header.stamp = ros::Time(0);
67  listener_.transformPose("base_link", pose, pose);
68  }
69  catch (tf::TransformException const &ex)
70  {
71  ROS_WARN_STREAM_THROTTLE(1.0, "Couldn't get transform from dock to base_link");
72  stop();
73  return false;
74  }
75 
76  // Distance to goal
77  double r = std::sqrt(pose.pose.position.x * pose.pose.position.x +
78  pose.pose.position.y * pose.pose.position.y);
79 
80  // Once we get close, reduce d_
81  if (r < 0.3)
82  {
83  // This part is trying to bring the target point into the goal but right now it just jumps from
84  // some virtual pose to the actual pose. We want to bring it in more gradually.
85  dist_ = 0.0;
86 
87  // dist_ = 1.6*r - 0.08;
88  // if (dist_ < 0.0) dist_ = 0.0;
89  }
90 
91  // If within distance tolerance, return true
92  if (r < 0.01)
93  {
94  // stop();
95  return true;
96  }
97 
98  // Orientation base frame relative to r_
99  double delta = std::atan2(-pose.pose.position.y, pose.pose.position.x);
100 
101  // Determine orientation of goal frame relative to r_
102  tf::Quaternion q;
103  tf::quaternionMsgToTF(pose.pose.orientation, q);
104  double theta = angles::normalize_angle(tf::getYaw(q) + delta);
105 
106  // Compute the virtual control
107  double a = atan(-k1_ * theta);
108  // Compute curvature (k)
109  double k = -1.0/r * (k2_ * (delta - a) + (1 + (k1_/1+((k1_*theta)*(k1_*theta))))*sin(delta));
110 
111  // Compute max_velocity based on curvature
112  double v = max_velocity_ / (1 + beta_ * std::pow(fabs(k), lambda_));
113  // Limit max velocity based on approaching target (avoids overshoot)
114  if (r < 0.75)
115  {
116  v = std::max(min_velocity_, std::min(std::min(r, max_velocity_), v));
117  }
118  else
119  {
120  v = std::min(max_velocity_, std::max(min_velocity_, v));
121  }
122 
123  // Compute angular velocity
124  double w = k * v;
125  // Bound angular velocity
126  double bounded_w = std::min(max_angular_velocity_, std::max(-max_angular_velocity_, w));
127  // Make sure that if we reduce w, we reduce v so that kurvature is still followed
128  if (w != 0.0)
129  {
130  v *= (bounded_w/w);
131  }
132 
133  // Send command to base
134  command_.linear.x = v;
135  command_.angular.z = bounded_w;
137 
138  // Create debugging view of path
139  nav_msgs::Path plan;
140  plan.header.stamp = ros::Time::now();
141  plan.header.frame_id = "base_link";
142  // Add origin
143  geometry_msgs::PoseStamped path_pose;
144  path_pose.header.frame_id = "base_link";
145  path_pose.pose.orientation.w = 1.0;
146  plan.poses.push_back(path_pose);
147  double yaw = 0.0;
148  for (int i = 0; i < 20; i++) // 2 sec
149  {
150  path_pose.pose.position.x += 0.1 * command_.linear.x * cos(yaw);
151  path_pose.pose.position.y += 0.1 * command_.linear.x * sin(yaw);
152  yaw += 0.1 * command_.angular.z;
153  path_pose.pose.orientation.z = sin(theta/2.0);
154  path_pose.pose.orientation.w = cos(theta/2.0);
155 
156  double dx = path_pose.pose.position.x - pose.pose.position.x;
157  double dy = path_pose.pose.position.y - pose.pose.position.y;
158  if ((dx * dx + dy * dy) < 0.005)
159  {
160  break;
161  }
162 
163  plan.poses.push_back(path_pose);
164  }
165  // Push goal pose onto path
166  plan.poses.push_back(pose);
167  // Publish path
168  path_pub_.publish(plan);
169 
170  return false;
171 }
172 
173 bool BaseController::backup(double distance, double rotate_distance)
174 {
175  // If the inputs are invalid then don't backup.
176  if (!std::isfinite(distance) ||
177  !std::isfinite(rotate_distance))
178  {
179  ROS_ERROR_STREAM_NAMED("controller", "Backup parameters are not valid.");
180  stop();
181  return true;
182  }
183 
184  // Get current base pose in odom
185  geometry_msgs::PoseStamped pose;
186  pose.header.frame_id = "base_link";
187  pose.pose.orientation.w = 1.0;
188 
189  try
190  {
192  pose.header.frame_id,
193  pose.header.stamp,
194  ros::Duration(0.1));
195  listener_.transformPose("odom", pose, pose);
196  }
197  catch (tf::TransformException const &ex)
198  {
199  ROS_WARN_STREAM_THROTTLE(1.0, "Couldn't get transform from base_link to odom");
200  stop();
201  return false;
202  }
203 
204  // If just getting started, stow starting pose
205  if (!ready_)
206  {
207  start_ = pose;
208  turning_ = false;
209  ready_ = true;
210  }
211 
212  if (turning_)
213  {
214  // Get yaw angles
215  tf::Quaternion q1, q2;
216  tf::quaternionMsgToTF(start_.pose.orientation, q1);
217  tf::quaternionMsgToTF(pose.pose.orientation, q2);
218  double theta = angles::normalize_angle(tf::getYaw(q2) - tf::getYaw(q1));
219  double error = angles::normalize_angle(rotate_distance - theta);
220 
221  if (fabs(error) < 0.05)
222  {
223  stop();
224  return true;
225  }
226  else if (rotate_distance > 0.0)
227  {
228  command_.angular.z = std::min(2.0, fabs(error)*2.0);
229  }
230  else
231  {
232  command_.angular.z = std::max(-2.0, -fabs(error)*2.0);
233  }
234  }
235  else
236  {
237  // Check if have backed up enough
238  double dx = pose.pose.position.x - start_.pose.position.x;
239  double dy = pose.pose.position.y - start_.pose.position.y;
240  if ((dx * dx + dy * dy) > (distance * distance))
241  {
242  if (rotate_distance == 0.0)
243  {
244  stop();
245  return true;
246  }
247  else
248  {
249  turning_ = true;
250  command_.linear.x = 0.0;
251  }
252  }
253  else
254  {
255  command_.linear.x = -0.1;
256  }
257  }
258 
260  return false;
261 }
262 
263 bool BaseController::getCommand(geometry_msgs::Twist& command)
264 {
265  command = command_;
266  return true;
267 }
268 
270 {
271  command_ = geometry_msgs::Twist();
273 
274  // Reset the backup controller
275  ready_ = false;
276 
277  // Reset the approach controller
278  dist_ = 0.4;
279 }
bool approach(const geometry_msgs::PoseStamped &target)
Implements something loosely based on "A Smooth Control Law for Graceful Motion of Differential Wheel...
Definition: controller.cpp:44
void stop()
send stop command to robot base
Definition: controller.cpp:269
bool backup(double distance, double rotate_distance)
Back off dock, then rotate. Robot is first reversed by the prescribed distance, and then rotates with...
Definition: controller.cpp:173
#define ROS_WARN_STREAM_THROTTLE(period, args)
void transformPose(const std::string &target_frame, const geometry_msgs::PoseStamped &stamped_in, geometry_msgs::PoseStamped &stamped_out) const
#define ROS_ERROR_STREAM_NAMED(name, args)
static double getYaw(const Quaternion &bt_q)
double dist_
Definition: controller.h:74
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
ros::Publisher cmd_vel_pub_
Definition: controller.h:58
double max_angular_velocity_
Definition: controller.h:71
ros::Publisher path_pub_
Definition: controller.h:59
bool getCommand(geometry_msgs::Twist &command)
Get the last command sent.
Definition: controller.cpp:263
double beta_
Definition: controller.h:72
void publish(const boost::shared_ptr< M > &message) const
tf::TransformListener listener_
Definition: controller.h:61
static void quaternionMsgToTF(const geometry_msgs::Quaternion &msg, Quaternion &bt)
double lambda_
Definition: controller.h:73
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
def normalize_angle(angle)
geometry_msgs::PoseStamped start_
Definition: controller.h:79
BaseController(ros::NodeHandle &nh)
Definition: controller.cpp:28
static Time now()
double min_velocity_
Definition: controller.h:69
double max_velocity_
Definition: controller.h:70
geometry_msgs::Twist command_
Definition: controller.h:62


fetch_open_auto_dock
Author(s): Michael Ferguson, Griswald Brooks
autogenerated on Mon Feb 28 2022 22:21:37