auto_dock.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2015, Fetch Robotics, Inc.
3  * Author: Michael Ferguson, Griswald Brooks
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 // STL Includes.
22 #include <math.h>
23 #include <algorithm>
24 
25 // ROS Includes.
26 #include <angles/angles.h>
27 
29  dock_(nh_, "dock", boost::bind(&AutoDocking::dockCallback, this, _1), false),
30  undock_(nh_, "undock", boost::bind(&AutoDocking::undockCallback, this, _1), false),
31  charge_lockout_("charge_lockout", true),
32  controller_(nh_),
33  perception_(nh_),
34  aborting_(false),
35  num_of_retries_(NUM_OF_RETRIES_),
36  cancel_docking_(true),
37  charging_timeout_set_(false)
38 {
39  // Load ros parameters
40  ros::NodeHandle pnh("~");
41  pnh.param("abort_distance", abort_distance_, 0.40);
42  pnh.param("abort_threshold", abort_threshold_, 0.025);
43  pnh.param("abort_angle", abort_angle_, 5.0*(M_PI/180.0)),
44  pnh.param("num_of_retries", NUM_OF_RETRIES_, 5);
45  pnh.param("dock_connector_clearance_distance", DOCK_CONNECTOR_CLEARANCE_DISTANCE_, 0.2);
46  pnh.param("docked_distance_threshold", DOCKED_DISTANCE_THRESHOLD_, 0.34);
47 
48  // Subscribe to robot state
49  state_ = nh_.subscribe<fetch_driver_msgs::RobotState>("robot_state",
50  1,
51  boost::bind(&AutoDocking::stateCallback, this, _1));
52 
53  // Start action server thread
54  dock_.start();
55  undock_.start();
56 }
57 
59 {
60 }
61 
62 void AutoDocking::stateCallback(const fetch_driver_msgs::RobotStateConstPtr& state)
63 {
64  // Check the voltage to see if we were connected to the dock.
65  // Previously, this check was done by checking the supply_breaker.current
66  // but this isn't the best check since there are conditions where the charger hasn't
67  // yet enabled charging or is in some error state.
68  // The thing that really needs to be checked is if we have made sufficient physical
69  // contact with the dock connect, which the voltage should tell us.
70  // This should also help extend the life of the tires, as previously the robot would
71  // continue to drive until the supply_breaker.current passed a threshold, which could
72  // take several seconds.
73  charging_ = (state->charger.supply_voltage > 30.0);
74 }
75 
76 void AutoDocking::dockCallback(const fetch_auto_dock_msgs::DockGoalConstPtr& goal)
77 {
78  fetch_auto_dock_msgs::DockFeedback feedback;
79  fetch_auto_dock_msgs::DockResult result;
80 
81  // Reset flags.
82  result.docked = false;
83  aborting_ = false;
84  charging_timeout_set_ = false;
85  cancel_docking_ = false;
86 
87  // Not currently supporting move_base option
88  if (goal->use_move_base)
89  {
90  dock_.setAborted(result);
91  ROS_ERROR("Docking failure: use_move_base not yet supported");
92  return;
93  }
94 
95  // Object for controlling loop rate.
96  ros::Rate r(50.0);
97 
98  // Start perception
99  perception_.start(goal->dock_pose);
100 
101  // For timeout calculation
102  initDockTimeout();
103 
104  // Get initial dock pose.
105  geometry_msgs::PoseStamped dock_pose_base_link;
106  while (!perception_.getPose(dock_pose_base_link, "base_link"))
107  {
108  // Wait for perception to get its first pose estimate.
109  if (!continueDocking(result))
110  {
111  ROS_DEBUG_NAMED("autodock_dock_callback",
112  "Docking failed: Initial dock not found.");
113  break;
114  }
115  }
116 
117  // Preorient the robot.
118  double dock_yaw = angles::normalize_angle(tf::getYaw(dock_pose_base_link.pose.orientation));
119  if (!std::isfinite(dock_yaw))
120  {
121  ROS_ERROR_STREAM_NAMED("auto_dock", "Dock yaw is invalid.");
122  cancel_docking_ = true;
123  }
124  else if (ros::ok() && continueDocking(result))
125  {
126  // Set backup limit to be the initial dock range.
127  backup_limit_ = sqrt(pow(dock_pose_base_link.pose.position.x, 2) + pow(dock_pose_base_link.pose.position.y, 2));
128  // Shorten up the range a bit.
129  backup_limit_ *= 0.9;
130 
131  // Preorient the robot towards the dock.
132  while (!controller_.backup(0.0, -dock_yaw) &&
133  continueDocking(result) &&
134  ros::ok()
135  )
136  {
137  r.sleep(); // Sleep the rate control object.
138  }
139  }
140 
141  // Make sure controller is ready
142  controller_.stop();
143 
144 
145  // Control
146  while (ros::ok() && continueDocking(result))
147  {
148  // Update perception
149  if (perception_.getPose(feedback.dock_pose))
150  {
151  if (aborting_)
152  {
153  // Backup.
155 
156  // Reset abort flag.
157  aborting_ = false;
158 
159  // Decrement the number of retries.
160  num_of_retries_--;
161  }
162  else
163  {
164  // Check to see if we are on target and compute yaw correction angle.
166  {
167  // Not on target, abort, abort, abort!
168  controller_.stop();
169  aborting_ = true;
170  }
171  else
172  {
173  // Update control
174  controller_.approach(feedback.dock_pose);
175  // Are we on the dock? Check charging timeout.
177  }
178  }
179 
180  // Feedback is mainly for our debugging
181  controller_.getCommand(feedback.command);
182  dock_.publishFeedback(feedback);
183  }
184 
185  // Sleep the rate control object.
186  r.sleep();
187  }
188 
189  // Make sure we stop things before we are done.
190  controller_.stop();
191  perception_.stop();
192 }
193 
199 bool AutoDocking::continueDocking(fetch_auto_dock_msgs::DockResult& result)
200 {
201  // If charging, stop and return success.
202  if (charging_)
203  {
204  result.docked = true;
205  dock_.setSucceeded(result);
206  ROS_DEBUG_NAMED("autodock_dock_callback",
207  "Docking success: Robot has docked");
208  return false;
209  }
210  // Timeout on time or retries.
211  else if (isDockingTimedOut() || cancel_docking_)
212  {
213  dock_.setAborted(result);
214  ROS_WARN("Docking failed: timed out");
215  return false;
216  }
217  // Something is stopping us.
218  else if (dock_.isPreemptRequested())
219  {
220  dock_.setPreempted(result);
221  ROS_WARN("Docking failure: preempted");
222  return false;
223  }
224 
225  return true;
226 }
227 
234 {
235  // Grab the dock pose in the base_link so we can evaluate it wrt the robot.
236  geometry_msgs::PoseStamped dock_pose_base_link;
237  perception_.getPose(dock_pose_base_link, "base_link");
238 
239  // Are we close enough to be docked?
240  if (dock_pose_base_link.pose.position.x < DOCKED_DISTANCE_THRESHOLD_)
241  {
243  {
245  charging_timeout_set_ = true;
246  }
248  {
249  // Reset charging timeout flag.
250  charging_timeout_set_ = false;
251  // Reset correction angle since it was probably fine.
252  correction_angle_ = 0.0;
253  // Reset since we don't seem to be charging.
254  aborting_ = true;
255  }
256  }
257  else
258  {
259  // If we back up enough, reset the charging timeout flag.
260  charging_timeout_set_ = false;
261  }
262 }
263 
268 {
271 }
272 
278 {
279  // Have we exceeded our deadline or tries?
281  {
282  return true;
283  }
284  return false;
285 }
286 
295 {
296  // Disable charging for a second.
297  lockoutCharger(1);
298 
299  // Get off of the dock. Try to straighten out.
301  {
302  if (isDockingTimedOut())
303  {
304  return;
305  }
306  r.sleep(); // Sleep the rate control object.
307  }
308  // Move to recovery pose.
309  while (!controller_.backup(backupDistance(), 0.0))
310  {
311  if (isDockingTimedOut())
312  {
313  return;
314  }
315  r.sleep();
316  }
317 }
318 
326 {
327  // Initialized to 1.0 meter as our basic backup amount.
328  double distance = 1.0;
329 
330  // Distance should be proportional to the amount of yaw correction.
331  // The constants are purely arbitrary because they seemed good at the time.
332  // distance *= 3.5*fabs(correction_angle_);
333  distance *= 1.5*fabs(correction_angle_);
334  // We should backup more the more times we try. This function should range from 1 to 2.
335  // num_of_retries is initially equal to NUM_OF_RETRIES and decrements as the robot retries.
336  double retry_constant = 2 - static_cast<float>(num_of_retries_)/NUM_OF_RETRIES_;
337  retry_constant = std::max(1.0, std::min(2.0, retry_constant));
338  distance *= retry_constant;
339 
340  // Cap the backup limit to 1 meter, just in case.
341  backup_limit_ = std::min(1.0, backup_limit_);
342  // Threshold distance.
343  distance = std::max(0.2, std::min(backup_limit_, distance));
344 
345  return distance;
346 }
347 
356 bool AutoDocking::isApproachBad(double & dock_yaw)
357 {
358  // Grab the dock pose in the base_link so we can evaluate it wrt the robot.
359  geometry_msgs::PoseStamped dock_pose_base_link;
360  perception_.getPose(dock_pose_base_link, "base_link");
361 
362  dock_yaw = angles::normalize_angle(tf::getYaw(dock_pose_base_link.pose.orientation));
363 
364  // If we are close to the dock but not quite docked, check other approach parameters.
365  if (dock_pose_base_link.pose.position.x < abort_distance_ &&
366  dock_pose_base_link.pose.position.x > DOCKED_DISTANCE_THRESHOLD_
367  )
368  {
369  // Check to see if we are too far side-to-side or at a bad angle.
370  if (fabs(dock_pose_base_link.pose.position.y) > abort_threshold_ ||
371  fabs(dock_yaw) > abort_angle_
372  )
373  {
374  // Things are bad, abort.
375  return true;
376  }
377  }
378  // Everything is ok.
379  return false;
380 }
381 
382 bool AutoDocking::lockoutCharger(unsigned seconds)
383 {
384  // Check if parameter is valid.
385  if (seconds > 255)
386  {
387  return false;
388  }
389 
390  fetch_driver_msgs::DisableChargingGoal lockout_goal;
391 
392  // Attempt to connect to the charger lockout server and lockout the charger.
394  {
395  lockout_goal.disable_duration = ros::Duration(seconds);
396  charge_lockout_.sendGoal(lockout_goal);
397 
399  {
400  ROS_WARN_STREAM_NAMED("autodock_undock_callback", "Unable to lockout charger before undocking.");
401  return false;
402  }
403  }
404  else
405  {
406  ROS_WARN_STREAM_NAMED("autodock_undock_callback", "Charge lockout server could not be contacted.");
407  return false;
408  }
409 
410  return true;
411 }
412 
413 void AutoDocking::undockCallback(const fetch_auto_dock_msgs::UndockGoalConstPtr& goal)
414 {
415 
416 
417  // Disable the charger for just a little bit longer than the undock procedure might take.
418  lockoutCharger(6);
419 
420  fetch_auto_dock_msgs::UndockFeedback feedback;
421  fetch_auto_dock_msgs::UndockResult result;
422  result.undocked = false;
423 
424  // Distances to backup/turn
425  double backup = DOCK_CONNECTOR_CLEARANCE_DISTANCE_;
426  double turn = goal->rotate_in_place ? 3.1 : 0.0;
427 
428  // Make sure controller is ready
429  controller_.stop();
430 
431  // Timeout for undocking
432  ros::Time timeout = ros::Time::now() + ros::Duration(5.0);
433 
434  // Control
435  ros::Rate r(50.0);
436  while (ros::ok())
437  {
439  {
440  controller_.stop();
441  undock_.setPreempted(result);
442  ROS_WARN_NAMED("autodock_undock_callback", "Undocking preempted");
443  return;
444  }
445 
446  // Update control
447  if (controller_.backup(backup, turn))
448  {
449  // Odom says we have undocked
450  result.undocked = true;
451  controller_.stop();
452  undock_.setSucceeded(result);
453  // Command the charger to turn back on.
454  lockoutCharger(0);
455  ROS_DEBUG_NAMED("autodock_undock_callback", "Robot has undocked");
456  return;
457  }
458 
459  // Feedback is mainly for our debugging
460  undock_.publishFeedback(feedback);
461 
462  // Timeout
463  if (ros::Time::now() > timeout)
464  {
465  controller_.stop();
466  undock_.setAborted(result);
467  // Command the charger to turn back on.
468  lockoutCharger(0);
469  ROS_WARN_NAMED("autodock_undock_callback", "Undocking failed: timed out");
470  return;
471  }
472 
473  r.sleep();
474  }
475 
476  controller_.stop();
477  undock_.setAborted(result);
478  // Command the charger to turn back on.
479  lockoutCharger(0);
480  ROS_WARN_NAMED("autodock_undock_callback", "Undocking failed: ROS no longer OK");
481 }
482 
483 int main(int argc, char** argv)
484 {
485  ros::init(argc, argv, "auto_dock");
486  AutoDocking auto_dock;
487  ros::spin();
488  return 0;
489 }
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
AutoDocking()
Autodocking constructor. Object builds action servers for docking and undocking.
Definition: auto_dock.cpp:28
void stop()
send stop command to robot base
Definition: controller.cpp:269
void publishFeedback(const FeedbackConstPtr &feedback)
int num_of_retries_
Definition: auto_dock.h:160
dock_server_t dock_
Definition: auto_dock.h:140
double abort_distance_
Definition: auto_dock.h:153
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
void undockCallback(const fetch_auto_dock_msgs::UndockGoalConstPtr &goal)
Method to execute the undocking behavior.
Definition: auto_dock.cpp:413
#define ROS_ERROR_STREAM_NAMED(name, args)
bool cancel_docking_
Definition: auto_dock.h:162
#define ROS_WARN_NAMED(name,...)
bool lockoutCharger(unsigned seconds)
Method to disable the charger for a finite amount of time.
Definition: auto_dock.cpp:382
double correction_angle_
Definition: auto_dock.h:156
double backup_limit_
Definition: auto_dock.h:157
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void checkDockChargingConditions()
Method to see if the robot seems to be docked but not charging. If the robot does seem to be docked a...
Definition: auto_dock.cpp:233
bool waitForResult(const ros::Duration &timeout=ros::Duration(0, 0))
static double getYaw(const Quaternion &bt_q)
void executeBackupSequence(ros::Rate &r)
Method to back the robot up under the abort conditon. Once complete, the method resets the abort flag...
Definition: auto_dock.cpp:294
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
bool isDockingTimedOut()
Method checks to see if we have run out of time or retries.
Definition: auto_dock.cpp:277
BaseController controller_
Definition: auto_dock.h:145
int main(int argc, char **argv)
Definition: auto_dock.cpp:483
#define ROS_WARN(...)
bool getPose(geometry_msgs::PoseStamped &pose, std::string frame="")
Get the pose of the dock.
Definition: perception.cpp:120
void setSucceeded(const Result &result=Result(), const std::string &text=std::string(""))
double DOCK_CONNECTOR_CLEARANCE_DISTANCE_
Definition: auto_dock.h:134
double abort_threshold_
Definition: auto_dock.h:154
bool isApproachBad(double &dock_yaw)
Method to check approach abort conditions. If we are close to the dock but the robot is too far off s...
Definition: auto_dock.cpp:356
void dockCallback(const fetch_auto_dock_msgs::DockGoalConstPtr &goal)
Method to execute the docking behavior.
Definition: auto_dock.cpp:76
bool waitForServer(const ros::Duration &timeout=ros::Duration(0, 0)) const
#define ROS_DEBUG_NAMED(name,...)
bool getCommand(geometry_msgs::Twist &command)
Get the last command sent.
Definition: controller.cpp:263
double DOCKED_DISTANCE_THRESHOLD_
Definition: auto_dock.h:136
ros::Subscriber state_
Definition: auto_dock.h:149
bool param(const std::string &param_name, T &param_val, const T &default_val) const
bool aborting_
Definition: auto_dock.h:159
void setPreempted(const Result &result=Result(), const std::string &text=std::string(""))
bool continueDocking(fetch_auto_dock_msgs::DockResult &result)
Method that checks success or failure of docking.
Definition: auto_dock.cpp:199
bool charging_timeout_set_
Definition: auto_dock.h:165
void stateCallback(const fetch_driver_msgs::RobotStateConstPtr &state)
Method to update the robot charge state.
Definition: auto_dock.cpp:62
void initDockTimeout()
Method sets the docking deadline and number of retries.
Definition: auto_dock.cpp:267
ROSCPP_DECL bool ok()
charge_lockout_client_t charge_lockout_
Definition: auto_dock.h:142
bool charging_
Definition: auto_dock.h:150
ros::Time deadline_not_charging_
Definition: auto_dock.h:164
DockPerception perception_
Definition: auto_dock.h:146
double backupDistance()
Method to compute the distance the robot should backup when attemping a docking correction. Method uses a number of state variables in the class to compute distance. TODO(enhancement): Should these be parameterized instead?
Definition: auto_dock.cpp:325
ROSCPP_DECL void spin()
def normalize_angle(angle)
ros::Time deadline_docking_
Definition: auto_dock.h:163
double abort_angle_
Definition: auto_dock.h:155
bool sleep()
void setAborted(const Result &result=Result(), const std::string &text=std::string(""))
void sendGoal(const Goal &goal, SimpleDoneCallback done_cb=SimpleDoneCallback(), SimpleActiveCallback active_cb=SimpleActiveCallback(), SimpleFeedbackCallback feedback_cb=SimpleFeedbackCallback())
bool start(const geometry_msgs::PoseStamped &pose)
Start dock detection.
Definition: perception.cpp:105
static Time now()
undock_server_t undock_
Definition: auto_dock.h:141
ros::NodeHandle nh_
Definition: auto_dock.h:139
bool stop()
Stop tracking the dock.
Definition: perception.cpp:114
#define ROS_ERROR(...)
#define ROS_WARN_STREAM_NAMED(name, args)
int NUM_OF_RETRIES_
Definition: auto_dock.h:132


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