-
Raphael Memmesheimer authoredRaphael Memmesheimer authored
homer_navigation_node.cpp 35.93 KiB
#include <vector>
#include <iostream>
#include <sstream>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ros/package.h>
#include "homer_navigation/homer_navigation_node.h"
//messages
#include <std_msgs/Empty.h>
#include <homer_mapnav_msgs/TargetUnreachable.h>
#include <homer_mapnav_msgs/GetPointsOfInterest.h>
#include <homer_mapnav_msgs/ModifyMap.h>
#include <nav_msgs/Path.h>
#include <std_msgs/Int8.h>
#include <tools/tools.h>
#include <tools/loadRosConfig.h>
//nav_libs
#include "Explorer/Explorer.h"
#include "SpeedControl/SpeedControl.h"
using namespace std;
HomerNavigationNode::HomerNavigationNode()
{
ros::NodeHandle nh;
srand(time(NULL));// Initialize random numbers
//subscribers
map_sub_ = nh.subscribe<nav_msgs::OccupancyGrid>("/map", 1, &HomerNavigationNode::mapCallback, this);
pose_sub_ = nh.subscribe<geometry_msgs::PoseStamped>("/pose", 1, &HomerNavigationNode::poseCallback, this);
laser_data_sub_ = nh.subscribe<sensor_msgs::LaserScan>("/scan", 1, &HomerNavigationNode::laserDataCallback, this);
laser_back_data_sub_ = nh.subscribe<sensor_msgs::LaserScan>("/back_scan", 1, &HomerNavigationNode::backLaserCallback, this);
start_navigation_sub_ = nh.subscribe<homer_mapnav_msgs::StartNavigation>("/homer_navigation/start_navigation", 1, &HomerNavigationNode::startNavigationCallback, this);
stop_navigation_sub_ = nh.subscribe<homer_mapnav_msgs::StopNavigation>("/homer_navigation/stop_navigation", 1, &HomerNavigationNode::stopNavigationCallback, this);
navigate_to_poi_sub_ = nh.subscribe<homer_mapnav_msgs::NavigateToPOI>("/homer_navigation/navigate_to_POI", 1, &HomerNavigationNode::navigateToPOICallback, this);
unknown_threshold_sub_ = nh.subscribe<std_msgs::Int8>("/homer_navigation/unknown_threshold", 1, &HomerNavigationNode::unknownThresholdCallback, this);
refresh_param_sub_ = nh.subscribe<std_msgs::Empty>("/homer_navigation/refresh_params", 1, &HomerNavigationNode::refreshParamsCallback, this);
cmd_vel_pub_ = nh.advertise<geometry_msgs::Twist>("/robot_platform/cmd_vel", 1);
target_reached_pub_ = nh.advertise<std_msgs::Empty>("/homer_navigation/target_reached", 1);
target_unreachable_pub_ = nh.advertise<homer_mapnav_msgs::TargetUnreachable>("/homer_navigation/target_unreachable", 1);
path_pub_ = nh.advertise<nav_msgs::Path>("/homer_navigation/path", 1);
get_POIs_client_ = nh.serviceClient<homer_mapnav_msgs::GetPointsOfInterest>("/map_manager/get_pois");
m_move_base_simple_goal_sub_ = nh.subscribe<geometry_msgs::PoseStamped>("/move_base_simple/goal", 1, &HomerNavigationNode::moveBaseSimpleGoalCallback, this); // for RVIZ usage
m_MapTypeMachine.setName( "HomerNavigation Node" );
ADD_MACHINE_STATE( m_MapTypeMachine, SLAM_MAP );
ADD_MACHINE_STATE( m_MapTypeMachine, NAVIGATION_MAP );
m_MapTypeMachine.setState( SLAM_MAP );
m_MainMachine.setName( "HomerNavigation Main" );
ADD_MACHINE_STATE( m_MainMachine, IDLE );
ADD_MACHINE_STATE( m_MainMachine, AWAITING_EXPLORATION_MAP );
ADD_MACHINE_STATE( m_MainMachine, AWAITING_PATHPLANNING_MAP );
ADD_MACHINE_STATE( m_MainMachine, FOLLOWING_PATH );
ADD_MACHINE_STATE( m_MainMachine, AVOIDING_COLLISION );
ADD_MACHINE_STATE( m_MainMachine, FINAL_TURN );
ADD_MACHINE_STATE( m_MainMachine, TARGET_REACHED );
m_avoided_collision = false;
init();
m_act_speed = 0;
m_act_angle = 0;
}
void HomerNavigationNode::loadParameters()
{
SpeedControl::loadDimensions();
//Explorer constructor
loadConfigValue("/homer_navigation/safe_path_weight", m_SafePathWeight);
loadConfigValue("/homer_mapping/resolution", resolution_);
loadConfigValue("/homer_navigation/allowed_obstacle_distance/min", m_AllowedObstacleDistance.first);
loadConfigValue("/homer_navigation/allowed_obstacle_distance/max", m_AllowedObstacleDistance.second);
m_AllowedObstacleDistance.first /= resolution_;
m_AllowedObstacleDistance.second/= resolution_;
loadConfigValue("/homer_navigation/safe_obstacle_distance/min", m_SafeObstacleDistance.first);
loadConfigValue("/homer_navigation/safe_obstacle_distance/max", m_SafeObstacleDistance.second);
m_SafeObstacleDistance.first /= resolution_;
m_SafeObstacleDistance.second /= resolution_;
loadConfigValue("/homer_navigation/frontier_safeness_factor", m_FrontierSafenessFactor);
//Explorer
loadConfigValue("/homer_navigation/waypoint_sampling_threshold", waypoint_sampling_threshold_);
//check path
loadConfigValue("/homer_navigation/check_path", check_path_);
loadConfigValue("/homer_navigation/check_path_max_errors", check_path_max_errors_);
loadConfigValue("/homer_navigation/check_path_max_distance", check_path_max_distance_);
//collision
loadConfigValue("/homer_navigation/collision_distance", collision_distance_);
loadConfigValue("/homer_navigation/collision_distance_near_target", collision_distance_near_target_);
//moveRobot config values
loadConfigValue("/homer_navigation/backward_distance", backward_distance_);
loadConfigValue("/homer_navigation/max_trans_vel", m_MaxTransVel);
loadConfigValue("/homer_navigation/max_rot_vel", m_MaxRotVel);
loadConfigValue("/homer_navigation/speed_control/last_speedfactor_count", m_SpeedFactorMeanFilterSize);
loadConfigValue("/homer_navigation/speed_control/min_move_speedfactor", m_MinMoveSpeedFactor);
loadConfigValue("/homer_navigation/speed_control/min_turn_speedfactor_moving", m_MinTurnSpeedFactorMoving);
loadConfigValue("/homer_navigation/speed_control/min_turn_speedfactor_standing", m_MinTurnSpeedFactorStanding);
loadConfigValue("/homer_navigation/turn_threshold_angle", turn_threshold_angle_);
//cmd_vel config values
loadConfigValue("/homer_navigation/use_cmd_vel", m_use_cmd_vel_);
loadConfigValue("/homer_navigation/min_turn_angle", m_min_turn_angle);
loadConfigValue("/homer_navigation/max_turn_speed", m_max_turn_speed);
loadConfigValue("/homer_navigation/max_move_speed", m_max_move_speed);
loadConfigValue("/homer_navigation/max_drive_angle", m_max_drive_angle);
}
void HomerNavigationNode::init()
{
last_map_timestamp_ = ros::Time(0);
loadParameters();
robot_pose_.position.x = 0.0;
robot_pose_.position.y = 0.0;
robot_pose_.position.z = 0.0;
robot_pose_.orientation = tf::createQuaternionMsgFromYaw(0.0);
last_laser_time_ = ros::Time::now();
explorer_ = new Explorer ( m_AllowedObstacleDistance.first, m_AllowedObstacleDistance.second,
m_SafeObstacleDistance.first, m_SafeObstacleDistance.second,
m_SafePathWeight, m_FrontierSafenessFactor );
m_MainMachine.setState ( IDLE );
}
HomerNavigationNode::~HomerNavigationNode()
{
if ( explorer_ )
{
delete explorer_;
}
if(last_map_data_)
{
delete last_map_data_;
}
}
void HomerNavigationNode::sendStopRobot()
{
m_act_speed = 0;
m_act_angle = 0;
geometry_msgs::Twist cmd_vel_msg;
cmd_vel_msg.linear.x = 0;
cmd_vel_msg.linear.y = 0;
cmd_vel_msg.linear.z = 0;
cmd_vel_msg.angular.x = 0;
cmd_vel_msg.angular.y = 0;
cmd_vel_msg.angular.z = 0;
cmd_vel_pub_.publish(cmd_vel_msg);
}
void HomerNavigationNode::idleProcess()
{
if ( m_MainMachine.state() == FOLLOWING_PATH )
{
if ( (ros::Time::now() - last_laser_time_) > ros::Duration(2.0) )
{
ROS_ERROR_STREAM( "Laser data timeout!\n");
sendTargetUnreachableMsg(homer_mapnav_msgs::TargetUnreachable::LASER_TIMEOUT);
}
}
}
void HomerNavigationNode::calculatePath()
{
float desired_distance_2 = desired_distance_ - 0.05;
if ( desired_distance_2 < resolution_ )
{
desired_distance_2 = resolution_;
}
explorer_->setStart ( map_tools::toMapCoords( robot_pose_.position, origin_, resolution_) );
if ( map_tools::distance( map_tools::fromMapCoords( target_approx_, origin_, resolution_ ), target_point_ ) <= desired_distance_ )
{
ROS_INFO_STREAM( "There is a way to the target circle. Planning direct path." );
explorer_->setTarget ( map_tools::toMapCoords( target_point_, origin_, resolution_ ), desired_distance_2 / resolution_ );
}
else
{
ROS_INFO_STREAM( "The target area is not reachable. Trying to go as close as possible." );
explorer_->setTarget ( target_approx_, 0 );
}
bool success;
pixel_path_ = explorer_->getPath( success );
if ( !success )
{
ROS_WARN_STREAM("No path found for navigation, reporting as unreachable." );
sendTargetUnreachableMsg(homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND);
}
else
{
ROS_INFO_STREAM("homer_navigation::calculatePath - Path Size: " << pixel_path_.size());
std::vector<Eigen::Vector2i> waypoint_pixels = explorer_->sampleWaypointsFromPath ( pixel_path_, waypoint_sampling_threshold_ );
waypoints_.clear();
for(std::vector<Eigen::Vector2i>::iterator it = waypoint_pixels.begin(); it != waypoint_pixels.end(); ++it)
{
geometry_msgs::PoseStamped poseStamped;
poseStamped.pose.position = map_tools::fromMapCoords(*it, origin_, resolution_);
poseStamped.pose.orientation.x = 0.0;
poseStamped.pose.orientation.y = 0.0;
poseStamped.pose.orientation.z = 0.0;
poseStamped.pose.orientation.w = 1.0;
waypoints_.push_back(poseStamped);
}
ROS_INFO_STREAM("homer_navigation::calculatePath - Path Size: " << waypoints_.size());
sendPathData();
//make sure no timeout occurs after long calculation time
last_laser_time_ = ros::Time::now();
}
}
void HomerNavigationNode::startExploration()
{
ROS_INFO_STREAM( "Starting exploration." );
/*
m_Explorer->resetExploration();
m_Explorer->setOccupancyMap ( gridSize, gridSize, mapData, m_ExploredRegion );
m_Explorer->setFrontierSafenessFactor( m_FrontierSafenessFactor );
m_Explorer->setStart ( m_CoordinateConverter->worldToMap ( m_RobotPose ) );
bool success;
m_PixelPath= m_Explorer->getExplorationTransformPath( success );
if ( success ) {
m_Target = m_CoordinateConverter->mapToWorld( m_PixelPath.back() );
m_TargetApprox = m_PixelPath.back();
}
vector<Pixel> waypointPixels = m_Explorer->sampleWaypointsFromPath ( m_PixelPath, m_WaypointSamplingThreshold );
m_Waypoints = m_CoordinateConverter->mapToWorld ( waypointPixels );
*/
if ( pixel_path_.size() == 0 )
{
ROS_WARN_STREAM("No path found for exploration, sending NoTargetM." );
//sendMessage ( new NoTargetM() );
m_MainMachine.setState( IDLE );
}
else
{
m_MainMachine.setState ( FOLLOWING_PATH );
sendPathData();
}
//make sure no timeout occurs after long calculation time
last_laser_time_ = ros::Time::now();
}
void HomerNavigationNode::startNavigation()
{
ROS_INFO_STREAM("Starting navigation to " << target_point_.x << "," << target_point_.y);
if ( distanceTo(target_point_) < desired_distance_ )
{
ROS_INFO_STREAM( "Will not (re-)plan path: Target position already reached." );
targetPositionReached();
return;
}
ROS_INFO_STREAM( "Distance to target still too large (" << distanceTo( target_point_ ) << "m; requested: " << desired_distance_ << "m)" );
ROS_DEBUG_STREAM("homer_navigation_node::startNavigation: target point " << target_point_.x << " " << target_point_.y);
Eigen::Vector2i new_target = map_tools::toMapCoords(target_point_, origin_, resolution_);
ROS_DEBUG_STREAM("homer_navigation_node::startNavigation: target cell " << new_target);
Eigen::Vector2i new_target_approx;
switch ( m_MapTypeMachine.state() )
{
case SLAM_MAP:
{
ROS_INFO_STREAM( "Resetting occupancy map." );
if(fast_path_planning_)
{
maskMap();
}
explorer_->setOccupancyMap(width_, width_, origin_, &(*last_map_data_)[0]);
explorer_->setStart(map_tools::toMapCoords(robot_pose_.position, origin_, resolution_));
new_target_approx = explorer_->getNearestAccessibleTarget(new_target);
break;
}
case NAVIGATION_MAP:
ROS_INFO_STREAM( "Updating obstacles in externally assigned navigation map." );
explorer_->updateObstacles( width_, width_, origin_, &(*last_map_data_)[0] );
explorer_->setStart(map_tools::toMapCoords(robot_pose_.position, origin_, resolution_));
new_target_approx = new_target;
break;
}
geometry_msgs::Point new_target_approx_world = map_tools::fromMapCoords(new_target_approx, origin_, resolution_);
ROS_INFO_STREAM("start Navigation: Approx target: " << new_target_approx_world);
bool new_approx_is_better = ( map_tools::distance( robot_pose_.position, target_point_ ) - map_tools::distance( new_target_approx_world, target_point_ ) ) > 0.2;
bool new_approx_reaches_target = ( map_tools::distance(new_target_approx_world, target_point_ ) < desired_distance_ );
m_path_reaches_target = new_approx_reaches_target;
if ( !new_approx_is_better && !new_approx_reaches_target )
{
ROS_WARN_STREAM( "No better way to target found, turning and then reporting as unreachable."
<< endl << "Distance to target: " << distanceTo( target_point_ ) << "m; requested: " << desired_distance_ << "m" );
m_MainMachine.setState( FINAL_TURN );
}
else
{
target_approx_ = new_target_approx;
m_MainMachine.setState ( FOLLOWING_PATH );
calculatePath();
}
}
void HomerNavigationNode::sendPathData()
{
geometry_msgs::PoseStamped pose_stamped;
pose_stamped.pose = robot_pose_;
pose_stamped.header.frame_id = "/map";
pose_stamped.header.stamp = ros::Time::now();
nav_msgs::Path msg;
msg.poses = waypoints_;
if(waypoints_.size() > 0)
msg.poses.insert(msg.poses.begin(), pose_stamped);
path_pub_.publish(msg);
}
void HomerNavigationNode::sendTargetReachedMsg() {
sendStopRobot();
m_MainMachine.setState( IDLE );
std_msgs::Empty reached_msg;
target_reached_pub_.publish(reached_msg);
waypoints_.clear();
nav_msgs::Path empty_path_msg;
empty_path_msg.poses = waypoints_;
path_pub_.publish(empty_path_msg);
ROS_INFO_STREAM("TargetReachedMsg");
}
void HomerNavigationNode::sendTargetUnreachableMsg( int8_t reason ) {
sendStopRobot();
m_MainMachine.setState( IDLE );
homer_mapnav_msgs::TargetUnreachable unreachable_msg;
unreachable_msg.reason = reason;
target_unreachable_pub_.publish(unreachable_msg);
waypoints_.clear();
nav_msgs::Path empty_path_msg;
empty_path_msg.poses = waypoints_;
path_pub_.publish(empty_path_msg);
ROS_INFO_STREAM("TargetUnreachableMsg");
}
void HomerNavigationNode::targetPositionReached()
{
//we're as close s we can get, target reached
ROS_INFO_STREAM( "Target position reached. Distance to target: " << distanceTo( target_point_ ) << "m. Desired distance:" << desired_distance_ << "m" );
sendStopRobot();
// usleep( 30000 );
waypoints_.clear();
sendPathData();
m_MainMachine.setState( FINAL_TURN );
ROS_INFO_STREAM("Turning to look-at point");
}
void HomerNavigationNode::checkPath()
{
invalid_path_count_=0;
for ( unsigned i=0; i<pixel_path_.size(); i++ )
{
geometry_msgs::Point p = map_tools::fromMapCoords(pixel_path_.at(i), origin_, resolution_);
if ( distanceTo(p) > check_path_max_distance_ )
{
continue;
}
if (map_tools::findValue( last_map_data_, width_, height_, pixel_path_[i].x(), pixel_path_[i].y(), 90, m_AllowedObstacleDistance.first ) )
{
invalid_path_count_++;
ROS_WARN_STREAM("Obstacle detected in current path " << invalid_path_count_ << " of " << check_path_max_errors_ << " times.");
if ( invalid_path_count_ >= check_path_max_errors_ )
{
ROS_WARN_STREAM( "Replanning path." );
currentPathFinished();
}
return;
}
}
}
void HomerNavigationNode::handleCollision ()
{
if ( m_MainMachine.state()== FOLLOWING_PATH )
{
sendStopRobot();
if( distanceTo( target_point_) < collision_distance_near_target_ )
{
ROS_INFO_STREAM("Collision detected near target. Switch to final turn.");
targetPositionReached();
}
else
{
m_MainMachine.setState( AVOIDING_COLLISION );
ROS_WARN_STREAM( "Collision detected while following path!" );
}
}
}
float HomerNavigationNode::calcSpeedFactor()
{
float speedFactor = SpeedControl::getSpeedFactor(laser_points_, m_MinMoveSpeedFactor, 1.0 );
m_LastSpeedFactors.push_back( speedFactor );
if ( m_LastSpeedFactors.size() > m_SpeedFactorMeanFilterSize ) { m_LastSpeedFactors.pop_front(); }
float speedFactorMean = mean( m_LastSpeedFactors );
return speedFactorMean;
}
void HomerNavigationNode::performNextMove()
{
float maxMoveDistance = SpeedControl::getMaxMoveDistance ( laser_points_ );
float speedFactor = calcSpeedFactor();
switch ( m_MainMachine.state() )
{
case FOLLOWING_PATH:
{
if ( distanceTo( target_point_ ) < desired_distance_ )
{
ROS_INFO_STREAM( "Desired distance to target was reached." );
targetPositionReached();
return;
}
float waypointRadiusLaser = 0.125 * maxMoveDistance;
Eigen::Vector2i waypointPixel = map_tools::toMapCoords(waypoints_[0].pose.position, origin_, resolution_);
float obstacleDistanceMap = explorer_->getObstacleTransform()->getValue( waypointPixel.x(), waypointPixel.y() );
float waypointRadiusMap = 0.125 * obstacleDistanceMap * resolution_;
float waypointRadius = waypointRadiusLaser + waypointRadiusMap;
if ( ( waypointRadius < resolution_ ) || ( waypoints_.size() == 1 ) )
{
waypointRadius = resolution_;
}
//if we have accidentaly skipped waypoints, recalculate path
float minDistance=FLT_MAX;
unsigned nearestWaypoint=0;
for ( unsigned i=0; i<waypoints_.size(); i++ )
{
if ( distanceTo( waypoints_[i].pose.position ) < minDistance ) {
nearestWaypoint = i;
minDistance = distanceTo( waypoints_[i].pose.position );
}
}
if ( nearestWaypoint != 0 )
{
ROS_WARN_STREAM("Waypoints skipped. Recalculating path!");
calculatePath();
if ( m_MainMachine.state() != FOLLOWING_PATH ) { break; }
}
ROS_DEBUG_STREAM("NextMove DEBUG VALUES: maxMoveDistance="<< maxMoveDistance <<"\n"
<< "\tWaypointRadiusLaser=" << waypointRadiusLaser << "\n\t"
<< "obstacleDistanceMap=" << obstacleDistanceMap << "\n\t"
<< "");
//check if current waypoint has been reached
if ( (waypoints_.size() != 0) && ( distanceTo( waypoints_[0].pose.position ) < waypointRadius ) )
{
waypoints_.erase ( waypoints_.begin() );
ROS_DEBUG_STREAM("homer_navigation::performNextMove(): Current waypoint has been reached! Still " << waypoints_.size() << " to reach!");
}
sendPathData();
ROS_ERROR_STREAM("waypointRadius: " << waypointRadius);
//last wayoint reached
if ( waypoints_.size() == 0 )
{
ROS_INFO_STREAM("Last waypoint reached");
currentPathFinished();
return;
}
geometry_msgs::Point currentWaypoint = waypoints_[0].pose.position;
double distanceToWaypoint = distanceTo ( currentWaypoint );
double angleToWaypoint = angleToPointDeg ( currentWaypoint );
ROS_DEBUG_STREAM("homer_navigation::performNextMove(): Distance to waypoint: "<<distanceToWaypoint<<" Angle to waypoint: "<< (angleToWaypoint) << " Waypoint: " << currentWaypoint << "Robot Pose: " << robot_pose_.position << " Robot orientation " << tf::getYaw(robot_pose_.orientation)) ;
ostringstream stream;
stream.precision(2);
if (!m_use_cmd_vel_)
{
ROS_ERROR_STREAM("Not supported");
}
//move to next waypoint if heading in right direction
else // else use cmd_vel
{
if (angleToWaypoint < -180)
{
angleToWaypoint += 360;
}
//linear speed calculation
double speed = distanceToWaypoint;
if (speed < 0 )
{
speed = max(speed,-m_max_move_speed);
}
else
{
speed = min(speed,m_max_move_speed);
}
if(m_avoided_collision)
{
if( std::abs(angleToWaypoint) < 10)
{
m_avoided_collision = false;
}
else
{
speed = 0;
}
}
//linear speed calculation end
//angular speed calculation
double angle = angleToWaypoint*3.14/180.0;
if (abs(angle) < m_min_turn_angle)
{
angle = 0.0;
}
else
{
if (abs(angle) > m_max_drive_angle)
{
speed = 0.0;
}
if (angle < 0 )
{
angle = max(angle,-m_max_turn_speed);
}
else
{
angle = min(angle,m_max_turn_speed);
}
if (distanceToWaypoint < 1.0)
{
angle *= distanceToWaypoint;
}
// min speed for angles because under 0.35 the machine is really really slow
if (speed < 0.07)
{
if ( angle < 0 )
{
angle = min(angle,-0.45);
}
else
{
angle = max(angle,0.45);
}
}
}
//angular speed calculation end
m_act_speed = speed;
m_act_angle = angle;
geometry_msgs::Twist cmd_vel_msg;
cmd_vel_msg.linear.x = speed;
cmd_vel_msg.angular.z = angle;
cmd_vel_pub_.publish(cmd_vel_msg);
stream << "Driving & turning" << endl;
stream << "linear: " << speed << " angular: " << angle << endl;
stream << "distanceToWaypoint:" << distanceToWaypoint << "angleToWaypoint: " << angleToWaypoint << endl;
}
ROS_INFO_STREAM( stream.str() );
break;
}
case AVOIDING_COLLISION:
{
if( distanceTo( target_point_) < collision_distance_near_target_ )
{
ROS_INFO_STREAM("Collision detected near target. Switch to final turn.");
targetPositionReached();
}
else if ( maxMoveDistance <= collision_distance_ )
{
ostringstream stream;
stream << "Maximum driving distance too short (" << maxMoveDistance << "m)! Moving back.";
ROS_WARN_STREAM( stream.str() );
if (!m_use_cmd_vel_)
{
ROS_ERROR_STREAM("Not supported");
}
else // cmd_vel
{
geometry_msgs::Twist cmd_vel_msg;
if(HomerNavigationNode::obstacleBackwardDistance() > 0.4)
{
cmd_vel_msg.linear.x = -0.3;
}
else
{
cmd_vel_msg.angular.z = -0.45;
}
cmd_vel_pub_.publish(cmd_vel_msg);
}
}
else
{
m_avoided_collision = true;
ROS_WARN_STREAM( "Collision avoided. Updating path." );
currentPathFinished();
}
break;
}
case FINAL_TURN:
{
if ( skip_final_turn_ )
{
ROS_INFO_STREAM("Final turn skipped. Target reached.");
if( m_path_reaches_target )
{
sendTargetReachedMsg();
}
else
{
sendTargetUnreachableMsg(homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND);
}
break;
}
double turnAngle = minTurnAngle( tf::getYaw(robot_pose_.orientation), target_orientation_ );
ROS_INFO_STREAM("homer_navigation::PerformNextMove:: Final Turn. Robot orientation: " << rad2Deg(tf::getYaw(robot_pose_.orientation)) << ". Target orientation: " << rad2Deg(target_orientation_) );
ROS_DEBUG_STREAM ( "homer_navigation::PerformNextMove:: turnAngle: " << rad2Deg(turnAngle));
if (m_use_cmd_vel_ ) // final turn with cmd_vel
{
if (turnAngle< 0 )
{
turnAngle= max(turnAngle,-m_max_turn_speed);
}
else
{
turnAngle = min(turnAngle,m_max_turn_speed);
}
if (abs(turnAngle) < m_min_turn_angle)
{
ROS_INFO_STREAM(":::::::TARGET REACHED BECAUSE lower "<<m_min_turn_angle);
if( m_path_reaches_target )
{
sendTargetReachedMsg();
}
else
{
sendTargetUnreachableMsg(homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND);
}
}
else
{
geometry_msgs::Twist cmd_vel_msg;
cmd_vel_msg.angular.z = turnAngle;
cmd_vel_pub_.publish(cmd_vel_msg);
}
}
break;
}
case AWAITING_EXPLORATION_MAP:
case AWAITING_PATHPLANNING_MAP:
{
//make sure that the robot doesn't move
ROS_INFO_STREAM("Awaiting pathplanning map");
sendStopRobot();
break;
}
case IDLE:
break;
}
}
void HomerNavigationNode::currentPathFinished() // also used for replanning
{
ROS_INFO_STREAM( "Current path was finished, initiating recalculation.");
waypoints_.clear();
sendStopRobot();
m_MainMachine.setState( AWAITING_PATHPLANNING_MAP );
}
// returns angle to target point in degrees(!)
int HomerNavigationNode::angleToPointDeg ( geometry_msgs::Point target )
{
double cx = robot_pose_.position.x;
double cy = robot_pose_.position.y;
int targetAngle = rad2Deg( atan2 ( target.y - cy, target.x - cx ) );
int currentAngle = rad2Deg( tf::getYaw(robot_pose_.orientation) );
int angleDiff = targetAngle - currentAngle;
angleDiff = (angleDiff + 180) % 360 - 180;
return angleDiff;
}
double HomerNavigationNode::distanceTo ( geometry_msgs::Point target )
{
double cx = robot_pose_.position.x;
double cy = robot_pose_.position.y;
double distance_to_target_2 = ( cx - target.x ) * ( cx - target.x ) + ( cy - target.y ) * ( cy - target.y );
return sqrt ( distance_to_target_2 );
}
float HomerNavigationNode::obstacleBackwardDistance()
{
float min_y = -0.27;
float max_y = 0.27;
float back_distance = 4;
for(float depth = -0.3 ; depth > -1.0; depth -= 0.01)
{
for(float y = min_y ; y <= max_y ; y += 0.1)
{
geometry_msgs::Point base_link_point;
base_link_point.x = depth;
base_link_point.y = y;
geometry_msgs::Point map_point = map_tools::transformPoint(base_link_point, transform_listener_ ,"/base_link", "/map");
int i = map_tools::map_index(map_point, origin_ ,width_,resolution_);
if(last_map_data_->at(i) > 90)
{
if(back_distance > HomerNavigationNode::distanceTo(map_point))
{
back_distance = HomerNavigationNode::distanceTo(map_point);
}
}
}
if(back_distance != 4)
{
break;
}
}
ROS_ERROR_STREAM("Back Distance: "<< back_distance);
return back_distance;
}
void HomerNavigationNode::actualizeStatusInfo()
{
ostringstream stream;
stream << m_MapTypeMachine.stateString() << '\n'
<< m_MainMachine.stateString();
ROS_DEBUG_STREAM( stream.str() );
}
void HomerNavigationNode::maskMap()
{
//generate bounding box
ROS_INFO_STREAM("Calculating Bounding box for fast planning");
Eigen::Vector2i pose_pixel = map_tools::toMapCoords( robot_pose_.position, origin_, resolution_);
Eigen::Vector2i target_pixel = map_tools::toMapCoords( target_point_, origin_, resolution_);
Eigen::Vector2i safe_pixel_distance(m_AllowedObstacleDistance.first * 4,
m_AllowedObstacleDistance.first * 4);
Eigen::AlignedBox2i planning_box;
planning_box.extend(pose_pixel);
planning_box.extend(target_pixel);
ROS_INFO_STREAM("Bounding Box: (" << planning_box.min() << " " << planning_box.max());
Eigen::AlignedBox2i safe_planning_box(planning_box.min() - safe_pixel_distance, planning_box.max() + safe_pixel_distance);
ROS_INFO_STREAM("safe Bounding Box: (" << safe_planning_box.min() << " " << safe_planning_box.max());
ROS_INFO_STREAM("min in m: " <<map_tools::fromMapCoords(safe_planning_box.min(), origin_, resolution_));
ROS_INFO_STREAM("max in m: "<<map_tools::fromMapCoords(safe_planning_box.max(), origin_, resolution_));
for(size_t x = 0; x < width_; x++)
{
for(size_t y = 0; y < width_; y++)
{
if(!safe_planning_box.contains(Eigen::Vector2i(x, y)))
{
last_map_data_->at(y * width_ + x) = -1;
}
}
}
}
//convenience math functions
float HomerNavigationNode::minTurnAngle( float angle1, float angle2 )
{
angle1 *= 180.0/M_PI;
angle2 *= 180.0/M_PI;
int diff= angle2 - angle1;
diff = (diff + 180) % 360 - 180;
float ret = static_cast<double>(diff) * M_PI/180.0;
return ret;
}
template<class ContainerT>
double HomerNavigationNode::mean ( const ContainerT& values )
{
typename ContainerT::const_iterator it;
it = values.begin();
double sum = 0;
while ( it != values.end() )
{
sum += *it;
it++;
}
return sum / double ( values.size() );
}
void HomerNavigationNode::refreshParamsCallback(const std_msgs::Empty::ConstPtr& msg)
{
ROS_INFO_STREAM("Refreshing Parameters");
loadParameters();
}
void HomerNavigationNode::mapCallback(const nav_msgs::OccupancyGrid::ConstPtr& msg)
{
if(msg->info.height != msg->info.width)
{
ROS_ERROR_STREAM("Incoming Map not quadratic. No map update!");
return;
}
if(msg->header.stamp != last_map_timestamp_)
{
last_map_timestamp_ = msg->header.stamp;
last_map_data_ = new std::vector<int8_t>(msg->data);
origin_ = msg->info.origin;
width_ = msg->info.width;
height_ = msg->info.height;
resolution_ = msg->info.resolution;
Eigen::Vector2i map_point;
if(m_laser_points_map.size() > 0)
{
for(int i = 0; i < m_laser_points_map.size(); i++)
{
geometry_msgs::Point& point = m_laser_points_map[i];
map_point = map_tools::toMapCoords(point, origin_, resolution_);
int k = map_point.y() * width_ + map_point.x();
if(k < 0 || k > width_*height_)
{
continue;
}
last_map_data_->at(k) = homer_mapnav_msgs::ModifyMap::BLOCKED;
}
}
if(m_back_laser.size() > 0 )
{
for(int i = 0; i < m_back_laser.size(); i++)
{
geometry_msgs::Point& point = m_back_laser[i];
map_point = map_tools::toMapCoords(point, origin_, resolution_);
int k = map_point.y() * width_ + map_point.x();
if(k < 0 || k > width_*height_)
{
continue;
}
last_map_data_->at(k) = homer_mapnav_msgs::ModifyMap::BLOCKED;
}
}
switch ( m_MainMachine.state() )
{
case AWAITING_EXPLORATION_MAP:
if ( m_MapTypeMachine.state() == SLAM_MAP )
{
startExploration();
}
break;
case AWAITING_PATHPLANNING_MAP:
startNavigation();
break;
case FOLLOWING_PATH:
{
if ( check_path_)
{
checkPath();
}
break;
}
default:
break;
}
}
}
void HomerNavigationNode::poseCallback(const geometry_msgs::PoseStamped::ConstPtr& msg)
{
robot_pose_ = msg->pose;
performNextMove();
}
void HomerNavigationNode::laserDataCallback(const sensor_msgs::LaserScan::ConstPtr& msg)
{
float frontal_obstacle_distance;
bool collision_detected;
last_laser_time_ = ros::Time::now();
laser_points_ = map_tools::laser_ranges_to_points(msg->ranges, msg->angle_min, msg->angle_increment, msg->range_min, msg->range_max, transform_listener_, msg->header.frame_id, "/base_link");
m_laser_points_map = map_tools::laser_ranges_to_points( msg->ranges, msg->angle_min, msg->angle_increment, msg->range_min, msg->range_max, transform_listener_, msg->header.frame_id, "/map");
if(m_act_speed == 0)
{
return;
}
frontal_obstacle_distance = SpeedControl::getMaxMoveDistance ( laser_points_ );
collision_detected = frontal_obstacle_distance <= collision_distance_;
if(collision_detected)
{
handleCollision();
}
}
void HomerNavigationNode::backLaserCallback(const sensor_msgs::LaserScan::ConstPtr& msg)
{
m_back_laser = map_tools::laser_ranges_to_points( msg->ranges, msg->angle_min, msg->angle_increment, msg->range_min, msg->range_max, transform_listener_, msg->header.frame_id, "/map");
}
void HomerNavigationNode::startNavigationCallback(const homer_mapnav_msgs::StartNavigation::ConstPtr& msg)
{
ROS_INFO_STREAM("Start navigating to (" << msg->goal.position.x << ", " << msg->goal.position.y << ")");
if (m_MainMachine.state() != IDLE) {
ROS_WARN_STREAM( "Aborting current operation and starting navigation.\n");
}
m_MapTypeMachine.setState(SLAM_MAP);
target_point_ = msg->goal.position;
target_orientation_ = tf::getYaw(msg->goal.orientation);
desired_distance_ = msg->distance_to_target < 0.1 ? 0.1 : msg->distance_to_target;
skip_final_turn_ = msg->skip_final_turn;
fast_path_planning_ = msg->fast_planning;
m_LastSpeedFactors.clear();
ROS_INFO_STREAM("Navigating to target " << target_point_.x << ", " << target_point_.y
<< "\nTarget orientation: " << target_orientation_
<< "Desired distance to target: " << desired_distance_);
m_MainMachine.setState( AWAITING_PATHPLANNING_MAP );
}
void HomerNavigationNode::moveBaseSimpleGoalCallback(const geometry_msgs::PoseStamped::ConstPtr& msg)
{
m_avoided_collision = false;
target_point_ = msg->pose.position;
target_orientation_ = tf::getYaw(msg->pose.orientation);
desired_distance_ = 0.1;// msg->distance_to_target < 0.1 ? 0.1 : msg->distance_to_target;
skip_final_turn_ = false;
fast_path_planning_ = false;
ROS_INFO_STREAM("Navigating to target via Move Base Simple x: " << target_point_.x << ", y: " << target_point_.y
<< "\nTarget orientation: " << target_orientation_
<< "Desired distance to target: " << desired_distance_ );
m_MainMachine.setState( AWAITING_PATHPLANNING_MAP );
}
void HomerNavigationNode::navigateToPOICallback(const homer_mapnav_msgs::NavigateToPOI::ConstPtr &msg)
{
m_avoided_collision = false;
std::string name = msg->poi_name;
homer_mapnav_msgs::GetPointsOfInterest srv;
get_POIs_client_.call(srv);
std::vector<homer_mapnav_msgs::PointOfInterest>::iterator it;
for(it = srv.response.poi_list.pois.begin(); it != srv.response.poi_list.pois.end(); ++it)
{
if(it->name == name)
{
ROS_INFO_STREAM("Start navigating to (" << it->pose.position.x << ", " << it->pose.position.y << ")");
if (m_MainMachine.state() != IDLE) {
ROS_WARN_STREAM( "Aborting current operation and starting navigation.\n");
}
m_MapTypeMachine.setState(SLAM_MAP);
sendStopRobot();
target_point_ = it->pose.position;
target_orientation_ = tf::getYaw(it->pose.orientation);
desired_distance_ = msg->distance_to_target < 0.1 ? 0.1 : msg->distance_to_target;
skip_final_turn_ = msg->skip_final_turn;
m_LastSpeedFactors.clear();
ROS_INFO_STREAM("Navigating to target " << target_point_.x << ", " << target_point_.y
<< "\nTarget orientation: " << target_orientation_
<< "Desired distance to target: " << desired_distance_);
m_MainMachine.setState( AWAITING_PATHPLANNING_MAP );
return;
}
}
ROS_ERROR_STREAM("No point of interest with name '" << msg->poi_name << "' found in current poi list");
}
void HomerNavigationNode::stopNavigationCallback(const homer_mapnav_msgs::StopNavigation::ConstPtr& msg)
{
ROS_INFO_STREAM("Stopping navigation." );
// stop exploring
m_MainMachine.setState( IDLE );
m_avoided_collision = false;
sendStopRobot();
waypoints_.clear();
nav_msgs::Path empty_path_msg;
empty_path_msg.poses = waypoints_;
path_pub_.publish(empty_path_msg);
}
void HomerNavigationNode::unknownThresholdCallback(const std_msgs::Int8::ConstPtr &msg)
{
explorer_->setUnknownThreshold(static_cast<int>(msg->data));
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "homer_navigation");
HomerNavigationNode node;
ros::Rate rate(12);
while(ros::ok())
{
ros::spinOnce();
node.idleProcess();
rate.sleep();
}
return 0;
}