cb_base_navigation
local_planner_interface.cpp
Go to the documentation of this file.
2 
4 
5 #include <ed_msgs/SimpleQuery.h>
6 
7 #include <nav_core/base_local_planner.h>
8 
9 #include <tf2/utils.h>
10 
12 #include <tue/profiling/profiler.h>
14 
15 #include <limits>
16 
17 using namespace cb_base_navigation_msgs;
18 
19 namespace cb_local_planner {
20 
21 LocalPlannerInterface::~LocalPlannerInterface()
22 {
23  // Clean things up
24  local_planner_.reset();
25 
26  ros::shutdown();
27  controller_thread_->join();
28 }
29 
30 LocalPlannerInterface::LocalPlannerInterface(costmap_2d::Costmap2DROS* costmap, tf2_ros::Buffer* tf) :
31  controller_thread_(nullptr),
32  action_server_(nullptr),
33  lp_loader_("nav_core", "nav_core::BaseLocalPlanner"),
34  tf_(tf),
35  costmap_(costmap)
36 {
37  ros::NodeHandle gh;
38  ros::NodeHandle nh("~");
39 
40  // Parameter setup
41  std::string local_planner;
42  nh.param("local_planner", local_planner, std::string("dwa_local_planner/DWAPlannerROS"));
43  nh.param("robot_base_frame", robot_base_frame_, std::string("base_link"));;
44  nh.param("global_frame", global_frame_, std::string("map"));
45  nh.param("controller_frequency", controller_frequency_, 20.0);
46 
47  // ROS Publishers
48  vel_pub_ = gh.advertise<geometry_msgs::Twist>("cmd_vel", 1);
49 
50  // Initialize the local planner
51  try {
52  if(!lp_loader_.isClassAvailable(local_planner)){
53  std::vector<std::string> classes = lp_loader_.getDeclaredClasses();
54  for(unsigned int i = 0; i < classes.size(); ++i){
55  if(local_planner == lp_loader_.getName(classes[i])){
56  local_planner = classes[i]; break;
57  }
58  }
59  }
60  local_planner_ = lp_loader_.createInstance(local_planner);
61  local_planner_->initialize(lp_loader_.getName(local_planner), tf_, costmap_);
62  } catch (const pluginlib::PluginlibException& ex)
63  {
64  ROS_FATAL("Failed to create the %s planner, are you sure it is properly registered and that the containing library is built? Exception: %s", local_planner.c_str(), ex.what());
65  exit(0);
66  }
67 
68  // Construct action server
69  action_server_ = std::unique_ptr<actionlib::SimpleActionServer<LocalPlannerAction> >(new actionlib::SimpleActionServer<LocalPlannerAction>(nh, "action_server", false));
70  action_server_->registerGoalCallback(boost::bind(&LocalPlannerInterface::actionServerSetPlan, this));
71  action_server_->registerPreemptCallback(boost::bind(&LocalPlannerInterface::actionServerPreempt, this));
72  action_server_->start();
73 
74  ROS_INFO_STREAM("Local planner of type: '" << local_planner << "' initialized.");
75 
76  // Start the controller thread
78 
79  ed_client_ = gh.serviceClient<ed_msgs::SimpleQuery>("ed/simple_query");
80 }
81 
82 void LocalPlannerInterface::topicGoalCallback(const LocalPlannerActionGoalConstPtr& goal)
83 {
84  boost::unique_lock<boost::mutex> lock(goal_mtx_); // goal is altered
85 
86  ROS_DEBUG("Incoming topic plan.");
87 
88  goal_ = goal->goal;
89 
90  if (isGoalSet()) {
92  local_planner_->setPlan(goal_.plan);
93  } else {
94  ROS_ERROR("Received a plan of length 0, is something wrong?");
95  }
96 }
97 
99 {
100  boost::unique_lock<boost::mutex> lock(goal_mtx_); // goal is altered
101 
102  ROS_DEBUG("Incoming actionlib plan.");
103 
104  goal_ = *action_server_->acceptNewGoal();
105 
106  if (isGoalSet()) {
108  local_planner_->setPlan(goal_.plan);
109  } else {
110  ROS_ERROR("Received a plan of length 0, is something wrong?");
111  }
112 }
113 
115 {
116  boost::unique_lock<boost::mutex> lock(goal_mtx_); // goal is altered
117 
118  action_server_->setPreempted();
119  goal_.plan.clear();
120 }
121 
123 {
124  tue::ProfilePublisher profile_pub;
126  profile_pub.initialize(profiler);
127 
128  ros::Rate r(controller_frequency_);
129 
130  ROS_INFO_STREAM("LPI: Started local planner thread @ " << controller_frequency_ << " hz!");
131 
132  if (costmap_->getMapUpdateFrequency() > 0)
133  {
134  ROS_ERROR("LPI: Local costmap map update frequency should be 0, it is now: %2f Hz. This is not allowed by the Local Planner Interface!", costmap_->getMapUpdateFrequency());
135  ROS_ERROR("LPI: [[ Shutting down ... ]] ");
136  exit(1);
137  }
138 
139  while (ros::ok())
140  {
141  tue::ScopedTimer timer(profiler, "total");
142 
143  {
144  tue::ScopedTimer timer(profiler, "updateMap()");
145  costmap_->updateMap();
146  }
147 
148  {
149  tue::ScopedTimer timer(profiler, "publishCostmap()");
151  }
152 
153  {
154  tue::ScopedTimer timer(profiler, "doSomeMotionPlanning()");
156  }
157 
158  {
159  tue::ScopedTimer timer(profiler, "rateCheck()");
160  static ros::Time t_last_rate_met = ros::Time::now();
161 
162  if (r.cycleTime() < r.expectedCycleTime())
163  t_last_rate_met = ros::Time::now();
164 
165  if ((ros::Time::now() - t_last_rate_met).toSec() > 1.0) // When we do not meet the rate, print it every 1 second
166  {
167  ROS_WARN_STREAM("LPI: Local planner rate of " << 1.0 / r.expectedCycleTime().toSec() << " hz not met. " << profiler);
168  t_last_rate_met = ros::Time::now();
169  }
170  }
171 
172  {
173  tue::ScopedTimer timer(profiler, "sleep()");
174  r.sleep();
175  }
176 
177  profile_pub.publish();
178  }
179 }
180 
181 void prunePlan(const geometry_msgs::PoseStamped& global_pose, std::vector<geometry_msgs::PoseStamped>& plan){
183  while(it != plan.end()){
184  const geometry_msgs::PoseStamped& w = *it;
185  // Fixed error bound of 2 meters for now. Can reduce to a portion of the map size or based on the resolution
186  double x_diff = global_pose.pose.position.x - w.pose.position.x;
187  double y_diff = global_pose.pose.position.y - w.pose.position.y;
188  double distance_sq = x_diff * x_diff + y_diff * y_diff;
189  if(distance_sq < .5){
190  it = plan.erase(it);
191  ROS_DEBUG("Nearest waypoint to <%f, %f> is <%f, %f>\n", global_pose.pose.position.x, global_pose.pose.position.y, w.pose.position.x, w.pose.position.y);
192  break;
193  }
194  it = plan.erase(it);
195  }
196  }
197 
199 {
200  double distance = 0;
201  if (plan.size() > 1)
202  {
203  for (unsigned int i = 1; i < plan.size(); ++i)
204  distance+=hypot(plan[i].pose.position.x-plan[i-1].pose.position.x, plan[i].pose.position.y-plan[i-1].pose.position.y);
205  }
206  return distance;
207 }
208 
209 bool getBlockedPoint(const std::vector<geometry_msgs::PoseStamped>& plan, costmap_2d::Costmap2D* costmap, geometry_msgs::Point& p)
210 {
211  for (std::vector<geometry_msgs::PoseStamped>::const_iterator it = plan.begin(); it != plan.end(); ++it)
212  {
213  unsigned int mx, my;
214  if (costmap->worldToMap(it->pose.position.x, it->pose.position.y, mx, my))
215  {
216  if (costmap->getCost(mx, my) >= costmap_2d::INSCRIBED_INFLATED_OBSTACLE)
217  {
218  p = it->pose.position;
219  return true;
220  }
221  }
222  }
223  return false;
224 }
225 
227 {
228  boost::unique_lock<boost::mutex> lock(goal_mtx_);
229 
230  geometry_msgs::PoseStamped global_pose;
231 
232  if (!isGoalSet() || !costmap_->getRobotPose(global_pose)) return;
233 
234  // 1) Update the end goal orientation due to the orientation constraint, if updated, set new plan
236  local_planner_->setPlan(goal_.plan);
237 
238  // 2) Check if we are already there
239  if (local_planner_->isGoalReached()) {
240  geometry_msgs::Twist tw;
241  vel_pub_.publish(tw);
242  ROS_INFO("LPI: Local planner arrived at the goal position/orientation; clearing plan...");
243  action_server_->setSucceeded();
244  goal_.plan.clear();
245  return;
246  }
247 
248  // 3) Compute and publish velocity commands to base, check if not blocked
249  geometry_msgs::Twist tw;
250  feedback_.blocked = !local_planner_->computeVelocityCommands(tw);
251  vel_pub_.publish(tw);
252 
253  // 4) Get the pruned plan
255  prunePlan(global_pose, pruned_plan);
256 
257  // 4) Publish some feedback to via the action_server
258  feedback_.dtg = getDistance(pruned_plan);
259  if (feedback_.dtg < 1)
260  feedback_.dtg = base_local_planner::getGoalPositionDistance(global_pose, pruned_plan.end()->pose.position.x, pruned_plan.end()->pose.position.y);
261  if (feedback_.blocked)
262  feedback_.blocked = getBlockedPoint(pruned_plan, costmap_->getCostmap(), feedback_.point_blocked);
263  action_server_->publishFeedback(feedback_);
264 }
265 
267 {
268  if (!isGoalSet()) return false;
269 
270  tf2::Transform constraint_to_world_tf;
271 
272  if (goal_.orientation_constraint.frame == "/map" || goal_.orientation_constraint.frame == "map")
273  {
274  constraint_to_world_tf = tf2::Transform::getIdentity();
275  }
276  else
277  {
278  ed_msgs::SimpleQuery ed_query;
279  ed_query.request.id = goal_.orientation_constraint.frame;
280  ed_query.request.radius = std::numeric_limits<float>::infinity();;
281 
282  if (!ed_client_.call(ed_query))
283  {
284  ROS_ERROR("LPI: Failed to get robot orientation constraint frame: ED could not be queried.");
285  return false;
286  }
287 
288  if (ed_query.response.entities.empty())
289  {
290  ROS_ERROR_STREAM("LPI: Failed to get robot orientation constraint frame: ED returns 'no such entity'. ("
291  << goal_.orientation_constraint.frame << ").");
292  return false;
293  }
294 
295  const ed_msgs::EntityInfo& e_info = ed_query.response.entities.front();
296 
297  tf2::Transform world_to_constraint_tf;
298  tf2::fromMsg(e_info.pose, world_to_constraint_tf);
299  constraint_to_world_tf = world_to_constraint_tf;
300  }
301 
302 // // Request the desired transform from constraint frame to map
303 // tf::StampedTransform constraint_to_world_tf;
304 // try {
305 // tf_->lookupTransform(costmap_->getGlobalFrameID(), goal_.orientation_constraint.frame, ros::Time(0), constraint_to_world_tf);
306 // feedback_.frame_exists = true;
307 // } catch(tf::TransformException& ex) {
308 // ROS_ERROR("LPI: Failed to get robot orientation constraint frame");
309 // feedback_.frame_exists = false;
310 // return false;
311 // }
312 
313  // Get end and look at point
314  tf2::Vector3 look_at, end_point;
315  tf2::fromMsg(goal_.orientation_constraint.look_at, look_at);
316  tf2::fromMsg(goal_.plan.back().pose.position, end_point);
317 
318  // Get difference
319  tf2::Vector3 diff = constraint_to_world_tf*look_at - end_point;
320 
321  // Calculate current and new orientation
322  geometry_msgs::Quaternion& q = goal_.plan.back().pose.orientation;
323  tf2::Quaternion new_quat;
324  new_quat.setRPY(0, 0, atan2(diff.y(),diff.x()) + goal_.orientation_constraint.angle_offset);
325 
326  // Check if the end goal orientation is already set
327  bool changed = (q.x == 0 && q.y == 0 && q.z == 0 && q.w == 0);
328 
329  if (!changed)
330  {
331  tf2::Quaternion current_quat;
332  tf2::fromMsg(goal_.plan.back().pose.orientation, current_quat);
333  changed = abs(current_quat.dot(new_quat)) < 1-1e-6;
334  }
335 
336  // Check if the orientation has changed
337  if (changed)
338  {
339  // Set new orientation
340  q = tf2::toMsg(new_quat);
341 
342  // Visualize the end pose
343  vis.publishGoalPoseMarker(goal_.plan.back());
344 
345  return true;
346  }
347 
348  return false;
349 }
350 
351 }
std::bind
T bind(T... args)
std::string
cb_local_planner::LocalPlannerInterface::controller_frequency_
double controller_frequency_
Definition: local_planner_interface.h:81
cb_local_planner::LocalPlannerInterface::local_planner_
boost::shared_ptr< nav_core::BaseLocalPlanner > local_planner_
Planners + loaders.
Definition: local_planner_interface.h:79
costmap_2d::Costmap2DROS::getMapUpdateFrequency
double getMapUpdateFrequency()
costmap_2d::Costmap2D::worldToMap
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
std::vector
std::vector::size
T size(T... args)
costmap_2d::INSCRIBED_INFLATED_OBSTACLE
static const unsigned char INSCRIBED_INFLATED_OBSTACLE
cb_local_planner::LocalPlannerInterface::ed_client_
ros::ServiceClient ed_client_
World model client.
Definition: local_planner_interface.h:70
cb_local_planner::LocalPlannerInterface::goal_mtx_
boost::mutex goal_mtx_
Definition: local_planner_interface.h:53
goal_functions.h
cb_local_planner
Definition: local_planner_interface.h:41
cb_local_planner::LocalPlannerInterface::robot_base_frame_
std::string robot_base_frame_
Frame names + Tranforms.
Definition: local_planner_interface.h:84
cb_local_planner::LocalPlannerInterface::doSomeMotionPlanning
void doSomeMotionPlanning()
Definition: local_planner_interface.cpp:226
cb_local_planner::getDistance
double getDistance(const std::vector< geometry_msgs::PoseStamped > &plan)
Definition: local_planner_interface.cpp:198
cb_local_planner::LocalPlannerInterface::vel_pub_
ros::Publisher vel_pub_
ROS Communication.
Definition: local_planner_interface.h:62
costmap_2d::Costmap2D
cb_local_planner::prunePlan
void prunePlan(const geometry_msgs::PoseStamped &global_pose, std::vector< geometry_msgs::PoseStamped > &plan)
Definition: local_planner_interface.cpp:181
base_local_planner::getGoalPositionDistance
double getGoalPositionDistance(const geometry_msgs::PoseStamped &global_pose, double goal_x, double goal_y)
costmap_2d::Costmap2DPublisher::publishCostmap
void publishCostmap()
costmap_2d::Costmap2DROS::getPublisher
Costmap2DPublisher * getPublisher()
scoped_timer.h
cb_local_planner::LocalPlannerInterface::isGoalSet
bool isGoalSet()
Definition: local_planner_interface.h:59
cb_local_planner::LocalPlannerInterface::global_frame_
std::string global_frame_
Definition: local_planner_interface.h:84
cb_local_planner::LocalPlannerInterface::actionServerPreempt
void actionServerPreempt()
Definition: local_planner_interface.cpp:114
std::thread
std::numeric_limits::infinity
T infinity(T... args)
std::string::c_str
T c_str(T... args)
profiler
tue::Profiler profiler
std::vector::erase
T erase(T... args)
cb_local_planner::LocalPlannerInterface::goal_
cb_base_navigation_msgs::LocalPlannerGoal goal_
Definition: local_planner_interface.h:75
cb_local_planner::LocalPlannerInterface::tf_
tf2_ros::Buffer * tf_
Definition: local_planner_interface.h:85
tue::ProfilePublisher::publish
void publish() const
cb_local_planner::LocalPlannerInterface::updateEndGoalOrientation
bool updateEndGoalOrientation()
Helper functions.
Definition: local_planner_interface.cpp:266
cb_local_planner::LocalPlannerInterface::controllerThread
void controllerThread()
Definition: local_planner_interface.cpp:122
cb_local_planner::getBlockedPoint
bool getBlockedPoint(const std::vector< geometry_msgs::PoseStamped > &plan, costmap_2d::Costmap2D *costmap, geometry_msgs::Point &p)
Definition: local_planner_interface.cpp:209
tue::Profiler
cb_local_planner::LocalPlannerInterface::costmap_
costmap_2d::Costmap2DROS * costmap_
Costmaps.
Definition: local_planner_interface.h:88
cb_local_planner::LocalPlannerInterface::lp_loader_
pluginlib::ClassLoader< nav_core::BaseLocalPlanner > lp_loader_
Definition: local_planner_interface.h:80
cb_local_planner::LocalPlannerInterface::feedback_
cb_base_navigation_msgs::LocalPlannerFeedback feedback_
Definition: local_planner_interface.h:76
cb_local_planner::LocalPlannerInterface::actionServerSetPlan
void actionServerSetPlan()
Action Server stuff.
Definition: local_planner_interface.cpp:98
limits
costmap_2d::Costmap2DROS::getRobotPose
bool getRobotPose(geometry_msgs::PoseStamped &global_pose) const
std::vector::begin
T begin(T... args)
local_planner_interface.h
cb_local_planner::LocalPlannerInterface::vis
Visualization vis
Visualization.
Definition: local_planner_interface.h:94
std::vector::end
T end(T... args)
tue::ProfilePublisher
cb_local_planner::LocalPlannerInterface::action_server_
std::unique_ptr< actionlib::SimpleActionServer< cb_base_navigation_msgs::LocalPlannerAction > > action_server_
Definition: local_planner_interface.h:64
costmap_2d::Costmap2DROS::getCostmap
Costmap2D * getCostmap() const
costmap_2d::Costmap2DROS::updateMap
void updateMap()
profiler.h
profile_publisher.h
tue::ProfilePublisher::initialize
void initialize(const Profiler &profiler)
tf_
tf2_ros::Buffer * tf_
std::unique_ptr
costmap_2d::Costmap2D::getCost
unsigned char getCost(unsigned int mx, unsigned int my) const
cb_local_planner::Visualization::publishGoalPoseMarker
void publishGoalPoseMarker(const geometry_msgs::PoseStamped &goal, const std::string &frame="map")
Publishes goal pose marker.
Definition: local_planner/visualization.cpp:11
costmap_2d::Costmap2DROS
cb_local_planner::LocalPlannerInterface::topicGoalCallback
void topicGoalCallback(const cb_base_navigation_msgs::LocalPlannerActionGoalConstPtr &goal)
topic goal cb
Definition: local_planner_interface.cpp:82
tue::ScopedTimer
cb_local_planner::LocalPlannerInterface::controller_thread_
std::unique_ptr< std::thread > controller_thread_
Definition: local_planner_interface.h:54