move_basic.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-21, Ubiquity Robotics
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  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  *
26  * The views and conclusions contained in the software and documentation are
27  * those of the authors and should not be interpreted as representing official
28  * policies, either expressed or implied, of the FreeBSD Project.
29  *
30  */
31 
32 #include <ros/ros.h>
33 #include <tf/transform_datatypes.h>
35 #include <tf2_ros/buffer.h>
38 #include <geometry_msgs/PoseStamped.h>
39 #include <geometry_msgs/Twist.h>
40 #include <geometry_msgs/Vector3.h>
41 #include <nav_msgs/Path.h>
42 #include <std_msgs/Float32.h>
43 #include <std_msgs/Bool.h>
44 #include <move_base_msgs/MoveBaseAction.h>
45 
47 #include <dynamic_reconfigure/server.h>
50 #include <move_basic/MovebasicConfig.h>
51 
52 #include <string>
53 
55 
56 class MoveBasic {
57  private:
60 
66 
67  std::unique_ptr<MoveBaseActionServer> actionServer;
68  std::unique_ptr<CollisionChecker> collision_checker;
69  std::unique_ptr<ObstaclePoints> obstacle_points;
70 
73 
79 
84 
85  double lateralKp;
86  double lateralKi;
87  double lateralKd;
88 
91  double minSideDist;
94  bool stop;
95 
99  tf2::Vector3 forwardLeft;
100  tf2::Vector3 forwardRight;
101 
106  std::string baseFrame;
107 
108  dynamic_reconfigure::Server<move_basic::MovebasicConfig> dr_srv;
109 
110  void dynamicReconfigCallback(move_basic::MovebasicConfig& config, uint32_t);
111  void stopCallback(const std_msgs::Bool::ConstPtr& msg);
112  void goalCallback(const geometry_msgs::PoseStamped::ConstPtr &msg);
113  void executeAction(const move_base_msgs::MoveBaseGoalConstPtr& goal);
114  void drawLine(double x0, double y0, double x1, double y1);
115  void sendCmd(double angular, double linear);
116  void abortGoal(const std::string msg);
117 
118  bool getTransform(const std::string& from, const std::string& to,
119  tf2::Transform& tf);
120  bool transformPose(const std::string& from, const std::string& to,
121  const tf2::Transform& in, tf2::Transform& out);
122 
123  public:
124  MoveBasic();
125 
126  void run();
127 
128  bool moveLinear(tf2::Transform& goalInDriving,
129  const std::string& drivingFrame,
130  tf2::Transform& goalInPlanning,
131  const std::string& planningFrame);
132  bool rotate(double requestedYaw,
133  const std::string& drivingFrame);
134 };
135 
136 
137 // Radians to degrees
138 
139 static double rad2deg(double rad)
140 {
141  return rad * 180.0 / M_PI;
142 }
143 
144 // Get the sign of a number
145 
146 static int sign(double n)
147 {
148  return (n <0 ? -1 : 1);
149 }
150 
151 // Adjust angle to be between -PI and PI
152 
153 static void normalizeAngle(double& angle)
154 {
155  if (angle < -M_PI) {
156  angle += 2 * M_PI;
157  }
158  if (angle > M_PI) {
159  angle -= 2 * M_PI;
160  }
161 }
162 
163 
164 // retreive the 3 DOF we are interested in from a Transform
165 
166 static void getPose(const tf2::Transform& tf, double& x, double& y, double& yaw)
167 {
168  tf2::Vector3 trans = tf.getOrigin();
169  x = trans.x();
170  y = trans.y();
171 
172  double roll, pitch;
173  tf.getBasis().getRPY(roll, pitch, yaw);
174 }
175 
176 
177 // Constructor
178 
179 MoveBasic::MoveBasic(): tfBuffer(ros::Duration(3.0)),
181 {
182  ros::NodeHandle nh("~");
183 
184  // Velocity parameters
185  nh.param<double>("min_turning_velocity", minTurningVelocity, 0.18);
186  nh.param<double>("max_turning_velocity", maxTurningVelocity, 1.0);
187  nh.param<double>("max_lateral_velocity", maxLateralVelocity, 0.5);
188  nh.param<double>("max_linear_velocity", maxLinearVelocity, 0.5);
189  nh.param<double>("min_linear_velocity", minLinearVelocity, 0.1);
190  nh.param<double>("linear_acceleration", linearAcceleration, 0.1);
191  nh.param<double>("turning_acceleration", turningAcceleration, 0.2);
192 
193  // Within tolerance, goal is successfully reached
194  nh.param<double>("angular_tolerance", angularTolerance, 0.05);
195  nh.param<double>("linear_tolerance", linearTolerance, 0.05);
196 
197  // PID parameters for lateral control
198  nh.param<double>("lateral_kp", lateralKp, 0.0);
199  nh.param<double>("lateral_ki", lateralKi, 0.0);
200  nh.param<double>("lateral_kd", lateralKd, 3.0);
201 
202  // how long to wait after moving to be sure localization is accurate
203  nh.param<double>("localization_latency", localizationLatency, 0.5);
204 
205  // how long robot can be driving away from the goal
206  nh.param<double>("runaway_timeout", runawayTimeoutSecs, 1.0);
207 
208  // how long to wait for an obstacle to disappear
209  nh.param<double>("obstacle_wait_threshold", obstacleWaitThreshold, 60.0);
210 
211  // Minimum distance to maintain in front
212  nh.param<double>("forward_obstacle_threshold", forwardObstacleThreshold, 0.5);
213 
214  // Minimum distance to maintain at each side
215  nh.param<double>("min_side_dist", minSideDist, 0.3);
216 
217  nh.param<std::string>("preferred_planning_frame",
219  nh.param<std::string>("alternate_planning_frame",
220  alternatePlanningFrame, "odom");
221  nh.param<std::string>("preferred_driving_frame",
222  preferredDrivingFrame, "map");
223  nh.param<std::string>("alternate_driving_frame",
224  alternateDrivingFrame, "odom");
225  nh.param<std::string>("base_frame", baseFrame, "base_footprint");
226 
227  stop = false;
228 
229  dynamic_reconfigure::Server<move_basic::MovebasicConfig>::CallbackType f;
230  f = boost::bind(&MoveBasic::dynamicReconfigCallback, this, _1, _2);
231  dr_srv.setCallback(f);
232 
233  cmdPub = ros::Publisher(nh.advertise<geometry_msgs::Twist>("/cmd_vel", 1));
234  pathPub = ros::Publisher(nh.advertise<nav_msgs::Path>("/plan", 1));
235 
237  ros::Publisher(nh.advertise<geometry_msgs::Vector3>("/obstacle_distance", 1));
238  errorPub =
239  ros::Publisher(nh.advertise<geometry_msgs::Vector3>("/lateral_error", 1));
240 
241  goalSub = nh.subscribe("/move_base_simple/goal", 1,
242  &MoveBasic::goalCallback, this);
243 
244  stopSub = nh.subscribe("/move_base/stop", 1,
245  &MoveBasic::stopCallback, this);
246 
247  ros::NodeHandle actionNh("");
248 
249  actionServer.reset(new MoveBaseActionServer(actionNh, "move_base",
250  boost::bind(&MoveBasic::executeAction, this, _1)));
251 
252  actionServer->start();
253  goalPub = actionNh.advertise<move_base_msgs::MoveBaseActionGoal>(
254  "/move_base/goal", 1);
255 
256  obstacle_points.reset(new ObstaclePoints(nh, tfBuffer));
258 
259  ROS_INFO("Move Basic ready");
260 }
261 
262 
263 // Lookup the specified transform, returns true on success
264 
265 bool MoveBasic::getTransform(const std::string& from, const std::string& to,
267 {
268  try {
269  geometry_msgs::TransformStamped tfs =
270  tfBuffer.lookupTransform(to, from, ros::Time(0));
271  tf2::fromMsg(tfs.transform, tf);
272  return true;
273  }
274  catch (tf2::TransformException &ex) {
275  return false;
276  }
277 }
278 
279 // Transform a pose from one frame to another
280 
281 bool MoveBasic::transformPose(const std::string& from, const std::string& to,
282  const tf2::Transform& in, tf2::Transform& out)
283 {
285  if (!getTransform(from, to, tf)) {
286  return false;
287  }
288  out = tf * in;
289  return true;
290 }
291 
292 // Dynamic reconfigure
293 
294 void MoveBasic::dynamicReconfigCallback(move_basic::MovebasicConfig& config, uint32_t){
295  minTurningVelocity = config.min_turning_velocity;
296  maxTurningVelocity = config.max_turning_velocity;
297  maxLateralVelocity = config.max_lateral_velocity;
298  turningAcceleration = config.turning_acceleration;
299  maxLinearVelocity = config.max_linear_velocity;
300  minLinearVelocity = config.min_linear_velocity;
301  linearAcceleration = config.linear_acceleration;
302 
303  angularTolerance = config.angular_tolerance;
304  linearTolerance = config.linear_tolerance;
305 
306  lateralKp = config.lateral_kp;
307  lateralKi = config.lateral_ki;
308  lateralKd = config.lateral_kd;
309 
310  localizationLatency = config.localization_latency;
311  runawayTimeoutSecs = config.runaway_timeout;
312 
313  minSideDist = config.min_side_dist;
314  obstacleWaitThreshold = config.obstacle_wait_threshold;
315  forwardObstacleThreshold = config.forward_obstacle_threshold;
316 
317  ROS_WARN("Parameter change detected");
318 }
319 
320 // Stop robot in place and save last state
321 
322 void MoveBasic::stopCallback(const std_msgs::Bool::ConstPtr& msg)
323 {
324  stop = msg->data;
325  if (stop) ROS_WARN("MoveBasic: Robot is forced to stop!");
326 }
327 
328 // Called when a simple goal message is received
329 
330 void MoveBasic::goalCallback(const geometry_msgs::PoseStamped::ConstPtr &msg)
331 {
332  ROS_INFO("MoveBasic: Received simple goal");
333 
334  move_base_msgs::MoveBaseActionGoal actionGoal;
335  actionGoal.header.stamp = ros::Time::now();
336  actionGoal.goal.target_pose = *msg;
337 
338  // Send the goal to the action server
339  goalPub.publish(actionGoal);
340 }
341 
342 
343 // Abort goal and print message
344 
345 void MoveBasic::abortGoal(const std::string msg)
346 {
347  actionServer->setAborted(move_base_msgs::MoveBaseResult(), msg);
348  ROS_ERROR("%s", msg.c_str());
349 }
350 
351 
352 // Called when an action goal is received
353 
354 void MoveBasic::executeAction(const move_base_msgs::MoveBaseGoalConstPtr& msg)
355 {
356  /*
357  Plan a path that involves rotating to face the goal, going straight towards it,
358  and then rotating for the final orientation.
359 
360  It is assumed that we are dealing with imperfect localization data:
361  map->base_link is accurate but may be delayed and is at a slow rate
362  odom->base_link is frequent, but drifts, particularly after rotating
363  To counter these issues, we plan in the map frame, and wait localizationLatency
364  after each step, and execute in the odom frame.
365  */
366 
367  tf2::Transform goal;
368  tf2::fromMsg(msg->target_pose.pose, goal);
369  std::string frameId = msg->target_pose.header.frame_id;
370 
371  // Needed for RobotCommander
372  if (frameId[0] == '/')
373  frameId = frameId.substr(1);
374 
375  double x, y, yaw;
376  getPose(goal, x, y, yaw);
377 
378  ROS_INFO("MoveBasic: Received goal %f %f %f %s", x, y, rad2deg(yaw), frameId.c_str());
379 
380  bool do_final_rotation = true;
381  if (std::isnan(yaw)) {
382  ROS_WARN("MoveBasic: Received a goal with invalid orientation, will go to it but not do final turn");
383  do_final_rotation = false;
384  }
385 
386  // The pose of the robot planning frame MUST be known initially, and may or may not
387  // be known after that (e.g. fiducial marker).
388  // The pose of the robot in the driving frame MUST be known at all times.
389  // An empty planning frame means to use what ever frame the goal is specified in.
390  double goalYaw;
391  std::string planningFrame;
392  tf2::Transform goalInPlanning;
393  if (preferredPlanningFrame == "") {
394  planningFrame = frameId;
395  goalInPlanning = goal;
396  ROS_INFO("Planning in goal frame: %s\n", planningFrame.c_str());
397  }
398  else if (!transformPose(frameId, preferredPlanningFrame, goal, goalInPlanning)) {
399  ROS_WARN("MoveBasic: Will attempt to plan in %s frame", alternatePlanningFrame.c_str());
400  if (!transformPose(frameId, alternatePlanningFrame, goal, goalInPlanning)) {
401  abortGoal("MoveBasic: No localization available for planning");
402  return;
403  }
404  planningFrame = alternatePlanningFrame;
405  goalYaw = yaw;
406  }
407  else {
408  planningFrame = preferredPlanningFrame;
409  }
410 
411  getPose(goalInPlanning, x, y, goalYaw);
412  ROS_INFO("MoveBasic: Goal in %s %f %f %f", planningFrame.c_str(),
413  x, y, rad2deg(goalYaw));
414 
415  // Publish our planned path
416  nav_msgs::Path path;
417  geometry_msgs::PoseStamped p0, p1;
418  path.header.frame_id = frameId;
419  p0.pose.position.x = x;
420  p0.pose.position.y = y;
421  p0.header.frame_id = frameId;
422  path.poses.push_back(p0);
423 
424  tf2::Transform poseFrameId;
425  if (!getTransform(baseFrame, frameId, poseFrameId)) {
426  abortGoal("MoveBasic: Cannot determine robot pose in goal frame");
427  return;
428  }
429  getPose(poseFrameId, x, y, yaw);
430  p1.pose.position.x = x;
431  p1.pose.position.y = y;
432  p1.header.frame_id = frameId;
433  path.poses.push_back(p1);
434 
435  pathPub.publish(path);
436 
437  std::string drivingFrame;
438  tf2::Transform goalInDriving;
439  tf2::Transform currentDrivingBase;
440  // Should be at time of goal message
441  if (!getTransform(preferredDrivingFrame, baseFrame, currentDrivingBase)) {
442  ROS_WARN("MoveBasic: %s not available, attempting to drive using %s frame",
444  if (!getTransform(alternateDrivingFrame, baseFrame, currentDrivingBase)) {
445  abortGoal("MoveBasic: Cannot determine robot pose in driving frame");
446  return;
447  }
448  drivingFrame = alternateDrivingFrame;
449  }
450  else {
451  drivingFrame = preferredDrivingFrame;
452  }
453 
454  if (!transformPose(frameId, drivingFrame, goal, goalInDriving)) {
455  abortGoal("MoveBasic: Cannot determine goal pose in driving frame");
456  return;
457  }
458 
459  tf2::Transform goalInBase = currentDrivingBase * goalInDriving;
460  {
461  double x, y, yaw;
462  getPose(goalInBase, x, y, yaw);
463  ROS_INFO("MoveBasic: Goal in %s %f %f %f", baseFrame.c_str(),
464  x, y, rad2deg(yaw));
465  }
466 
467  tf2::Vector3 linear = goalInBase.getOrigin();
468  linear.setZ(0);
469  double dist = linear.length();
470 
471  if (!transformPose(frameId, baseFrame, goal, goalInBase)) {
472  ROS_WARN("MoveBasic: Cannot determine robot pose for rotation");
473  return;
474  }
475 
476  if (dist > linearTolerance) {
477 
478  // Initial rotation to face the goal
479  double requestedYaw = atan2(linear.y(), linear.x());
480  if (std::abs(requestedYaw) > angularTolerance) {
481  if (!rotate(requestedYaw, drivingFrame)) {
482  return;
483  }
484  }
485  sleep(localizationLatency);
486 
487  // Do linear portion of the goal
488  if (!moveLinear(goalInDriving, drivingFrame, goalInPlanning, planningFrame)) {
489  return;
490  }
491  sleep(localizationLatency);
492  }
493 
494  // Final rotation as specified in goal
495  if (do_final_rotation) {
496  double requestedYaw = atan2(linear.y(), linear.x());
497  double finalYaw = goalYaw - (yaw + requestedYaw);
498  if (std::abs(finalYaw) > angularTolerance) {
499  if (!rotate(finalYaw, drivingFrame)) {
500  return;
501  }
502  }
503 
504  sleep(localizationLatency);
505  }
506 
507  ROS_INFO("Goal reached!");
508  actionServer->setSucceeded();
509 }
510 
511 
512 // Send a motion command
513 
514 void MoveBasic::sendCmd(double angular, double linear)
515 {
516  if (stop) {
517  angular = 0;
518  linear = 0;
519  }
520  geometry_msgs::Twist msg;
521  msg.angular.z = angular;
522  msg.linear.x = linear;
523 
524  cmdPub.publish(msg);
525 }
526 
527 
528 // Main loop
529 
531 {
532  ros::Rate r(20);
533 
534  while (ros::ok()) {
535  ros::spinOnce();
536  collision_checker->min_side_dist = minSideDist;
537  forwardObstacleDist = collision_checker->obstacle_dist(true,
540  forwardLeft,
541  forwardRight);
542  geometry_msgs::Vector3 msg;
543  msg.x = forwardObstacleDist;
544  msg.y = leftObstacleDist;
545  msg.z = rightObstacleDist;
547  r.sleep();
548  }
549 }
550 
551 
552 // Rotate relative to current orientation
553 
554 bool MoveBasic::rotate(double yaw, const std::string& drivingFrame)
555 {
556  tf2::Transform poseDriving;
557  if (!getTransform(baseFrame, drivingFrame, poseDriving)) {
558  abortGoal("MoveBasic: Cannot determine robot pose for rotation");
559  return false;
560  }
561 
562  double x, y, currentYaw;
563  getPose(poseDriving, x, y, currentYaw);
564  double requestedYaw = currentYaw + yaw;
565  normalizeAngle(requestedYaw);
566  ROS_INFO("MoveBasic: Requested rotation %f", rad2deg(requestedYaw));
567 
568  int oscillations = 0;
569  double prevAngleRemaining = 0;
570 
571  bool done = false;
572  ros::Rate r(50);
573 
574  while (!done && ros::ok()) {
575  ros::spinOnce();
576  r.sleep();
577 
578  double x, y, currentYaw;
579  tf2::Transform poseDriving;
580  if (!getTransform(baseFrame, drivingFrame, poseDriving)) {
581  abortGoal("MoveBasic: Cannot determine robot pose for rotation");
582  return false;
583  }
584  getPose(poseDriving, x, y, currentYaw);
585 
586  double angleRemaining = requestedYaw - currentYaw;
587  normalizeAngle(angleRemaining);
588 
589  double obstacle = collision_checker->obstacle_angle(angleRemaining > 0);
590  double remaining = std::min(std::abs(angleRemaining), std::abs(obstacle));
591  double velocity = std::max(minTurningVelocity,
592  std::min(remaining, std::min(maxTurningVelocity,
593  std::sqrt(2.0 * turningAcceleration *remaining))));
594 
595  if (sign(prevAngleRemaining) != sign(angleRemaining)) {
596  oscillations++;
597  }
598  prevAngleRemaining = angleRemaining;
599 
600  if (actionServer->isPreemptRequested()) {
601  ROS_INFO("MoveBasic: Stopping rotation due to preempt");
602  sendCmd(0, 0);
603  actionServer->setPreempted();
604  return false;
605  }
606 
607  if (std::abs(angleRemaining) < angularTolerance || oscillations > 2) {
608  ROS_INFO("MoveBasic: Done rotation, error %f degrees", rad2deg(angleRemaining));
609  velocity = 0;
610  done = true;
611  }
612 
613  bool counterwise = (angleRemaining < 0.0);
614  if (counterwise) {
615  velocity = -velocity;
616  }
617 
618  sendCmd(velocity, 0);
619  ROS_DEBUG("Angle remaining: %f, Angular velocity: %f", rad2deg(angleRemaining), velocity);
620  }
621  return done;
622 }
623 
624 // Move forward specified distance
625 
627  const std::string& drivingFrame, // Odom or map
628  tf2::Transform& goalInPlanning,
629  const std::string& planningFrame) // Fiducial
630 {
631  tf2::Transform poseDriving;
632  if (!getTransform(drivingFrame, baseFrame, poseDriving)) {
633  abortGoal("MoveBasic: Cannot determine robot pose for linear");
634  return false;
635  }
636  tf2::Transform goalInBase = poseDriving * goalInDriving;
637  tf2::Vector3 remaining = goalInBase.getOrigin();
638 
639  remaining.setZ(0);
640  double requestedDistance = remaining.length();
641 
642  bool pausingForObstacle = false;
643  ros::Time obstacleTime;
644  ros::Duration runawayTimeout(runawayTimeoutSecs);
645  ros::Time last = ros::Time::now();
646 
647  // For lateral control
648  double lateralIntegral = 0.0;
649  double lateralError = 0.0;
650  double prevLateralError = 0.0;
651  double lateralDiff = 0.0;
652 
653  bool done = false;
654  ros::Rate r(50);
655 
656  while (!done && ros::ok()) {
657  ros::spinOnce();
658  r.sleep();
659 
660  // TODO: TEST
661  // Try to update the goal in the driving frame according to planning frame.
662  // This might not work, for example if it was based on a fiducial
663  // which was no longer visible. However, if the goal was based up a fiducial,
664  // then it is likely that the estimate of our position relative to it
665  // will improve as we get closer.
666  tf2::Transform T_planning_driving;
667  if (getTransform(planningFrame, drivingFrame, T_planning_driving)) {
668  goalInDriving = T_planning_driving * goalInPlanning;
669  double gx, gy, gyaw;
670  getPose(goalInDriving, gx, gy, gyaw);
671  ROS_INFO("Updated goal %f %f %f\n", gx, gy, gyaw);
672  }
673  else {
674  ROS_INFO("Could not update goal\n");
675  }
676  // TODO: TEST
677 
678  if (!getTransform(drivingFrame, baseFrame, poseDriving)) {
679  ROS_WARN("MoveBasic: Cannot determine robot pose for linear");
680  return false;
681  }
682  goalInBase = poseDriving * goalInDriving;
683  remaining = goalInBase.getOrigin();
684  double distRemaining = sqrt(remaining.x() * remaining.x() + remaining.y() * remaining.y());
685 
686  // PID lateral control to keep robot on path
687  double rotation = 0.0;
688  lateralError = remaining.y();
689  lateralDiff = lateralError - prevLateralError;
690  prevLateralError = lateralError;
691  lateralIntegral += lateralError;
692  rotation = (lateralKp * lateralError) + (lateralKi * lateralIntegral) +
693  (lateralKd * lateralDiff);
694  // Clamp rotation
695  rotation = std::max(-maxLateralVelocity, std::min(maxLateralVelocity,
696  rotation));
697  ROS_DEBUG("MoveBasic: %f L %f, R %f %f %f %f %f \n",
699  remaining.x(), remaining.y(), lateralError,
700  rotation);
701 
702  // Publish messages for PID tuning
703  geometry_msgs::Vector3 pid_debug;
704  pid_debug.x = remaining.x();
705  pid_debug.y = lateralError;
706  pid_debug.z = rotation;
707  errorPub.publish(pid_debug);
708 
709  // Collision Avoidance
710  double obstacleDist = forwardObstacleDist;
711  if (requestedDistance < 0.0) {
712  obstacleDist = collision_checker->obstacle_dist(false,
715  forwardLeft,
716  forwardRight);
717  }
718 
719  double velocity = std::max(minLinearVelocity,
720  std::min(std::min(std::abs(obstacleDist), std::abs(distRemaining)),
721  std::min(maxLinearVelocity, std::sqrt(2.0 * linearAcceleration *
722  std::min(std::abs(obstacleDist), std::abs(distRemaining))))));
723 
724  bool obstacleDetected = (obstacleDist < forwardObstacleThreshold);
725  if (obstacleDetected) {
726  velocity = 0;
727  if (!pausingForObstacle) {
728  ROS_INFO("MoveBasic: PAUSING for OBSTACLE");
729  obstacleTime = ros::Time::now();
730  pausingForObstacle = true;
731  }
732  else {
733  ROS_INFO("MoveBasic: Still waiting for obstacle at %f meters!", obstacleDist);
734  ros::Duration waitTime = ros::Time::now() - obstacleTime;
735  if (waitTime.toSec() > obstacleWaitThreshold) {
736  abortGoal("MoveBasic: Aborting due to obstacle");
737  return false;
738  }
739  }
740  }
741 
742  if (!obstacleDetected && pausingForObstacle) {
743  ROS_INFO("MoveBasic: Resuming after obstacle has gone");
744  pausingForObstacle = false;
745  }
746 
747  // Abort Checks
748  if (actionServer->isPreemptRequested()) {
749  ROS_INFO("MoveBasic: Stopping move due to preempt");
750  actionServer->setPreempted();
751  sendCmd(0, 0);
752  return false;
753  }
754 
755  /* Since we are dealing with imperfect localization we should make
756  * sure we are at least runawayTimeout driving away from the goal*/
757  double angleRemaining = std::atan2(remaining.y(), remaining.x());
758  if (std::cos(angleRemaining) < 0) {
759  if (ros::Time::now() - last > runawayTimeout) {
760  abortGoal("MoveBasic: Moving away from goal");
761  sendCmd(0, 0);
762  return false;
763  }
764  }
765  else {
766  // Only update time when moving towards the goal
767  last = ros::Time::now();
768  }
769 
770  /* Finish Check */
771 
772  if (distRemaining < linearTolerance) {
773  ROS_INFO("MoveBasic: Done linear, error: x: %f meters, y: %f meters", remaining.x(), remaining.y());
774  velocity = 0;
775  done = true;
776  }
777 
778  sendCmd(rotation, velocity);
779  ROS_DEBUG("Distance remaining: %f, Linear velocity: %f", distRemaining, velocity);
780  }
781 
782  return done;
783 }
784 
785 
786 int main(int argc, char ** argv) {
787  ros::init(argc, argv, "move_basic");
788  MoveBasic mb_node;
789  mb_node.run();
790 
791  return 0;
792 }
int main(int argc, char **argv)
Definition: move_basic.cpp:786
std::unique_ptr< CollisionChecker > collision_checker
Definition: move_basic.cpp:68
void executeAction(const move_base_msgs::MoveBaseGoalConstPtr &goal)
Definition: move_basic.cpp:354
std::unique_ptr< ObstaclePoints > obstacle_points
Definition: move_basic.cpp:69
double linearAcceleration
Definition: move_basic.cpp:82
TF2SIMD_FORCE_INLINE Matrix3x3 & getBasis()
bool moveLinear(tf2::Transform &goalInDriving, const std::string &drivingFrame, tf2::Transform &goalInPlanning, const std::string &planningFrame)
Definition: move_basic.cpp:626
f
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
ros::Publisher errorPub
Definition: move_basic.cpp:65
double maxTurningVelocity
Definition: move_basic.cpp:75
double minSideDist
Definition: move_basic.cpp:91
std::string preferredPlanningFrame
Definition: move_basic.cpp:102
ros::Publisher pathPub
Definition: move_basic.cpp:63
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
actionlib::QueuedActionServer< move_base_msgs::MoveBaseAction > MoveBaseActionServer
Definition: move_basic.cpp:54
float leftObstacleDist
Definition: move_basic.cpp:97
double lateralKd
Definition: move_basic.cpp:87
std::string * frameId(M &m)
std::unique_ptr< MoveBaseActionServer > actionServer
Definition: move_basic.cpp:67
static int sign(double n)
Definition: move_basic.cpp:146
ros::Subscriber stopSub
Definition: move_basic.cpp:59
ros::Publisher goalPub
Definition: move_basic.cpp:61
#define ROS_WARN(...)
tf2::Vector3 forwardLeft
Definition: move_basic.cpp:99
double obstacleWaitThreshold
Definition: move_basic.cpp:89
ros::Publisher cmdPub
Definition: move_basic.cpp:62
void run()
Definition: move_basic.cpp:530
void dynamicReconfigCallback(move_basic::MovebasicConfig &config, uint32_t)
Definition: move_basic.cpp:294
double lateralKp
Definition: move_basic.cpp:85
double angularTolerance
Definition: move_basic.cpp:77
bool param(const std::string &param_name, T &param_val, const T &default_val) const
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
void publish(const boost::shared_ptr< M > &message) const
tf2_ros::Buffer tfBuffer
Definition: move_basic.cpp:71
double minTurningVelocity
Definition: move_basic.cpp:74
TF2SIMD_FORCE_INLINE Vector3 & getOrigin()
double maxLinearVelocity
Definition: move_basic.cpp:80
#define ROS_INFO(...)
double turningAcceleration
Definition: move_basic.cpp:76
ros::Publisher obstacleDistPub
Definition: move_basic.cpp:64
bool getTransform(const std::string &from, const std::string &to, tf2::Transform &tf)
Definition: move_basic.cpp:265
static void normalizeAngle(double &angle)
Definition: move_basic.cpp:153
float forwardObstacleDist
Definition: move_basic.cpp:96
void drawLine(double x0, double y0, double x1, double y1)
void fromMsg(const A &, B &b)
ros::Subscriber goalSub
Definition: move_basic.cpp:58
double linearTolerance
Definition: move_basic.cpp:83
ROSCPP_DECL bool ok()
std::string preferredDrivingFrame
Definition: move_basic.cpp:104
std::string baseFrame
Definition: move_basic.cpp:106
void goalCallback(const geometry_msgs::PoseStamped::ConstPtr &msg)
Definition: move_basic.cpp:330
float rightObstacleDist
Definition: move_basic.cpp:98
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
std::string alternatePlanningFrame
Definition: move_basic.cpp:103
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
tf2::Vector3 forwardRight
Definition: move_basic.cpp:100
double maxLateralVelocity
Definition: move_basic.cpp:78
void abortGoal(const std::string msg)
Definition: move_basic.cpp:345
dynamic_reconfigure::Server< move_basic::MovebasicConfig > dr_srv
Definition: move_basic.cpp:108
static double rad2deg(double rad)
Definition: move_basic.cpp:139
bool sleep()
double lateralKi
Definition: move_basic.cpp:86
static void getPose(const tf2::Transform &tf, double &x, double &y, double &yaw)
Definition: move_basic.cpp:166
double localizationLatency
Definition: move_basic.cpp:92
std::string alternateDrivingFrame
Definition: move_basic.cpp:105
void getRPY(tf2Scalar &roll, tf2Scalar &pitch, tf2Scalar &yaw, unsigned int solution_number=1) const
void sendCmd(double angular, double linear)
Definition: move_basic.cpp:514
bool rotate(double requestedYaw, const std::string &drivingFrame)
Definition: move_basic.cpp:554
INLINE Rall1d< T, V, S > atan2(const Rall1d< T, V, S > &y, const Rall1d< T, V, S > &x)
static Time now()
tf2_ros::TransformListener listener
Definition: move_basic.cpp:72
void stopCallback(const std_msgs::Bool::ConstPtr &msg)
Definition: move_basic.cpp:322
double minLinearVelocity
Definition: move_basic.cpp:81
double runawayTimeoutSecs
Definition: move_basic.cpp:93
double forwardObstacleThreshold
Definition: move_basic.cpp:90
ROSCPP_DECL void spinOnce()
#define ROS_ERROR(...)
bool transformPose(const std::string &from, const std::string &to, const tf2::Transform &in, tf2::Transform &out)
Definition: move_basic.cpp:281
#define ROS_DEBUG(...)


move_basic
Author(s): Jim Vaughan
autogenerated on Mon Feb 28 2022 22:49:55