├── msg └── PointArray.msg ├── maps ├── r0_0.map.pgm ├── r0_1.map.pgm ├── r1_0.map.pgm ├── r1_1.map.pgm ├── rs_0.map.pgm ├── rs_1.map.pgm ├── r0_0.map.yaml ├── r0_1.map.yaml ├── r1_0.map.yaml ├── r1_1.map.yaml ├── rs_0.map.yaml └── rs_1.map.yaml ├── scripts ├── functions.pyc ├── functions_real_two_robots.pyc ├── getfrontier.py ├── recordDistance_two_robots.py ├── recordDistance.py ├── recordDistance_two_robots_real.py ├── frontier_opencv_detector.py ├── assigner.py ├── assigner_real_two_robots.py ├── filter.py ├── filter_real_two_robots.py ├── filter_backup.py ├── newassigner.py ├── functions.py └── functions_real_two_robots.py ├── LICENSE.md ├── package.xml ├── CMakeLists.txt ├── include ├── functions.h └── mtrand.h ├── launch ├── simple.launch ├── single.launch ├── three_tb3.launch ├── three_robots.launch ├── rrt_three_robots.launch ├── two_robots.launch └── two_robots_real.launch ├── src ├── mtrand.cpp ├── functions.cpp ├── global_rrt_detector.cpp ├── global_rrt_detector_real_two_robots.cpp ├── local_rrt_detector.cpp └── local_rrt_detector_real_two_robots.cpp └── readme.md /msg/PointArray.msg: -------------------------------------------------------------------------------- 1 | # An array of points 2 | 3 | geometry_msgs/Point[] points 4 | -------------------------------------------------------------------------------- /maps/r0_0.map.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GradyM2M/multi_rrt_exploration/HEAD/maps/r0_0.map.pgm -------------------------------------------------------------------------------- /maps/r0_1.map.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GradyM2M/multi_rrt_exploration/HEAD/maps/r0_1.map.pgm -------------------------------------------------------------------------------- /maps/r1_0.map.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GradyM2M/multi_rrt_exploration/HEAD/maps/r1_0.map.pgm -------------------------------------------------------------------------------- /maps/r1_1.map.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GradyM2M/multi_rrt_exploration/HEAD/maps/r1_1.map.pgm -------------------------------------------------------------------------------- /maps/rs_0.map.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GradyM2M/multi_rrt_exploration/HEAD/maps/rs_0.map.pgm -------------------------------------------------------------------------------- /maps/rs_1.map.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GradyM2M/multi_rrt_exploration/HEAD/maps/rs_1.map.pgm -------------------------------------------------------------------------------- /scripts/functions.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GradyM2M/multi_rrt_exploration/HEAD/scripts/functions.pyc -------------------------------------------------------------------------------- /scripts/functions_real_two_robots.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GradyM2M/multi_rrt_exploration/HEAD/scripts/functions_real_two_robots.pyc -------------------------------------------------------------------------------- /maps/r0_0.map.yaml: -------------------------------------------------------------------------------- 1 | image: r0_0.map.pgm 2 | resolution: 0.050000 3 | origin: [-10.000000, -10.000000, 0.000000] 4 | negate: 0 5 | occupied_thresh: 0.65 6 | free_thresh: 0.196 7 | 8 | -------------------------------------------------------------------------------- /maps/r0_1.map.yaml: -------------------------------------------------------------------------------- 1 | image: r0_1.map.pgm 2 | resolution: 0.050000 3 | origin: [-10.000000, -10.000000, 0.000000] 4 | negate: 0 5 | occupied_thresh: 0.65 6 | free_thresh: 0.196 7 | 8 | -------------------------------------------------------------------------------- /maps/r1_0.map.yaml: -------------------------------------------------------------------------------- 1 | image: r1_0.map.pgm 2 | resolution: 0.050000 3 | origin: [-10.000000, -21.200000, 0.000000] 4 | negate: 0 5 | occupied_thresh: 0.65 6 | free_thresh: 0.196 7 | 8 | -------------------------------------------------------------------------------- /maps/r1_1.map.yaml: -------------------------------------------------------------------------------- 1 | image: r1_1.map.pgm 2 | resolution: 0.050000 3 | origin: [-10.000000, -10.000000, 0.000000] 4 | negate: 0 5 | occupied_thresh: 0.65 6 | free_thresh: 0.196 7 | 8 | -------------------------------------------------------------------------------- /maps/rs_0.map.yaml: -------------------------------------------------------------------------------- 1 | image: rs_0.map.pgm 2 | resolution: 0.050000 3 | origin: [-12.000000, -18.800000, 0.000000] 4 | negate: 0 5 | occupied_thresh: 0.65 6 | free_thresh: 0.196 7 | 8 | -------------------------------------------------------------------------------- /maps/rs_1.map.yaml: -------------------------------------------------------------------------------- 1 | image: rs_1.map.pgm 2 | resolution: 0.050000 3 | origin: [-12.000000, -8.400000, 0.000000] 4 | negate: 0 5 | occupied_thresh: 0.65 6 | free_thresh: 0.196 7 | 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Hassan Umari 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | multi_rrt_exploration 4 | 1.0.0 5 | A ROS package that implements a multi-robot RRT-based map exploration algorithm. It also has the image-based frontier detection that uses image processing to extract frontier points 6 | 7 | Hassan Umari 8 | Hassan Umari 9 | 10 | MIT 11 | http://wiki.ros.org/rrt_exploration 12 | 13 | 14 | catkin 15 | geometry_msgs 16 | nav_msgs 17 | roscpp 18 | rospy 19 | std_msgs 20 | tf 21 | visualization_msgs 22 | message_generation 23 | geometry_msgs 24 | nav_msgs 25 | roscpp 26 | rospy 27 | std_msgs 28 | tf 29 | visualization_msgs 30 | message_runtime 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(multi_rrt_exploration) 3 | 4 | ## Find catkin macros and libraries 5 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) 6 | ## is used, also find other catkin packages 7 | find_package(catkin REQUIRED COMPONENTS 8 | geometry_msgs 9 | nav_msgs 10 | roscpp 11 | rospy 12 | std_msgs 13 | tf 14 | visualization_msgs 15 | message_generation 16 | ) 17 | 18 | 19 | add_message_files( 20 | FILES 21 | PointArray.msg 22 | ) 23 | 24 | generate_messages( 25 | DEPENDENCIES 26 | std_msgs 27 | geometry_msgs 28 | ) 29 | 30 | 31 | catkin_package( 32 | 33 | CATKIN_DEPENDS message_runtime 34 | ) 35 | 36 | 37 | ########### 38 | ## Build ## 39 | ########### 40 | # c++11 support required 41 | include(CheckCXXCompilerFlag) 42 | check_cxx_compiler_flag("-std=c++11" COMPILER_SUPPORTS_CXX11) 43 | if(COMPILER_SUPPORTS_CXX11) 44 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 45 | else() 46 | message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") 47 | endif() 48 | 49 | 50 | include_directories(include ${catkin_INCLUDE_DIRS}) 51 | 52 | add_executable(global_rrt_detector src/global_rrt_detector.cpp src/functions.cpp src/mtrand.cpp) 53 | target_link_libraries(global_rrt_detector ${catkin_LIBRARIES}) 54 | 55 | add_executable(local_rrt_detector src/local_rrt_detector.cpp src/functions.cpp src/mtrand.cpp) 56 | target_link_libraries(local_rrt_detector ${catkin_LIBRARIES}) 57 | -------------------------------------------------------------------------------- /include/functions.h: -------------------------------------------------------------------------------- 1 | #ifndef functions_H 2 | #define functions_H 3 | #include "ros/ros.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "nav_msgs/OccupancyGrid.h" 10 | #include "geometry_msgs/Point.h" 11 | #include "geometry_msgs/PointStamped.h" 12 | #include "visualization_msgs/Marker.h" 13 | 14 | // rdm class, for gentaring random flot numbers 15 | class rdm{ 16 | int i; 17 | public: 18 | rdm(); 19 | float randomize(); 20 | }; 21 | 22 | 23 | //Norm function prototype 24 | float Norm( std::vector , std::vector ); 25 | 26 | //sign function prototype 27 | float sign(float ); 28 | 29 | //Nearest function prototype 30 | std::vector Nearest( std::vector< std::vector > , std::vector ); 31 | 32 | //Steer function prototype 33 | std::vector Steer( std::vector, std::vector, float ); 34 | 35 | //gridValue function prototype 36 | int gridValue(nav_msgs::OccupancyGrid &,std::vector); 37 | 38 | //ObstacleFree function prototype 39 | char ObstacleFree(std::vector , std::vector & , nav_msgs::OccupancyGrid); 40 | 41 | bool getCompleteFrontier(geometry_msgs::Point &, geometry_msgs::PointStamped &, nav_msgs::OccupancyGrid&); 42 | 43 | std::vector nhood8(unsigned int, nav_msgs::OccupancyGrid&); 44 | 45 | 46 | bool isNewFrontierCell(unsigned, nav_msgs::OccupancyGrid&, const std::vector&); 47 | 48 | std::vector pointOfIndex(nav_msgs::OccupancyGrid&, unsigned int); 49 | 50 | #endif 51 | 52 | -------------------------------------------------------------------------------- /launch/simple.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /launch/single.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /scripts/getfrontier.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | #--------Include modules--------------- 5 | from copy import copy 6 | import rospy 7 | from nav_msgs.msg import OccupancyGrid 8 | 9 | import numpy as np 10 | import cv2 11 | 12 | #----------------------------------------------------- 13 | 14 | def getfrontier(mapData): 15 | data=mapData.data 16 | w=mapData.info.width 17 | h=mapData.info.height 18 | resolution=mapData.info.resolution 19 | Xstartx=mapData.info.origin.position.x 20 | Xstarty=mapData.info.origin.position.y 21 | 22 | img = np.zeros((h, w, 1), np.uint8) 23 | 24 | for i in range(0,h): 25 | for j in range(0,w): 26 | if data[i*w+j]==100: 27 | img[i,j]=0 28 | elif data[i*w+j]==0: 29 | img[i,j]=255 30 | elif data[i*w+j]==-1: 31 | img[i,j]=205 32 | 33 | 34 | o=cv2.inRange(img,0,1) 35 | edges = cv2.Canny(img,0,255) 36 | im2, contours, hierarchy = cv2.findContours(o,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) 37 | cv2.drawContours(o, contours, -1, (255,255,255), 5) 38 | o=cv2.bitwise_not(o) 39 | res = cv2.bitwise_and(o,edges) 40 | #------------------------------ 41 | 42 | frontier=copy(res) 43 | im2, contours, hierarchy = cv2.findContours(frontier,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) 44 | cv2.drawContours(frontier, contours, -1, (255,255,255), 2) 45 | 46 | im2, contours, hierarchy = cv2.findContours(frontier,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) 47 | all_pts=[] 48 | if len(contours)>0: 49 | upto=len(contours)-1 50 | i=0 51 | maxx=0 52 | maxind=0 53 | 54 | for i in range(0,len(contours)): 55 | cnt = contours[i] 56 | M = cv2.moments(cnt) 57 | cx = int(M['m10']/M['m00']) 58 | cy = int(M['m01']/M['m00']) 59 | xr=cx*resolution+Xstartx 60 | yr=cy*resolution+Xstarty 61 | pt=[np.array([xr,yr])] 62 | if len(all_pts)>0: 63 | all_pts=np.vstack([all_pts,pt]) 64 | else: 65 | 66 | all_pts=pt 67 | 68 | return all_pts 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/mtrand.cpp: -------------------------------------------------------------------------------- 1 | // mtrand.cpp, see include file mtrand.h for information 2 | 3 | #include "mtrand.h" 4 | // non-inline function definitions and static member definitions cannot 5 | // reside in header file because of the risk of multiple declarations 6 | 7 | // initialization of static private members 8 | unsigned long MTRand_int32::state[n] = {0x0UL}; 9 | int MTRand_int32::p = 0; 10 | bool MTRand_int32::init = false; 11 | 12 | void MTRand_int32::gen_state() { // generate new state vector 13 | for (int i = 0; i < (n - m); ++i) 14 | state[i] = state[i + m] ^ twiddle(state[i], state[i + 1]); 15 | for (int i = n - m; i < (n - 1); ++i) 16 | state[i] = state[i + m - n] ^ twiddle(state[i], state[i + 1]); 17 | state[n - 1] = state[m - 1] ^ twiddle(state[n - 1], state[0]); 18 | p = 0; // reset position 19 | } 20 | 21 | void MTRand_int32::seed(unsigned long s) { // init by 32 bit seed 22 | state[0] = s & 0xFFFFFFFFUL; // for > 32 bit machines 23 | for (int i = 1; i < n; ++i) { 24 | state[i] = 1812433253UL * (state[i - 1] ^ (state[i - 1] >> 30)) + i; 25 | // see Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier 26 | // in the previous versions, MSBs of the seed affect only MSBs of the array state 27 | // 2002/01/09 modified by Makoto Matsumoto 28 | state[i] &= 0xFFFFFFFFUL; // for > 32 bit machines 29 | } 30 | p = n; // force gen_state() to be called for next random number 31 | } 32 | 33 | void MTRand_int32::seed(const unsigned long* array, int size) { // init by array 34 | seed(19650218UL); 35 | int i = 1, j = 0; 36 | for (int k = ((n > size) ? n : size); k; --k) { 37 | state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> 30)) * 1664525UL)) 38 | + array[j] + j; // non linear 39 | state[i] &= 0xFFFFFFFFUL; // for > 32 bit machines 40 | ++j; j %= size; 41 | if ((++i) == n) { state[0] = state[n - 1]; i = 1; } 42 | } 43 | for (int k = n - 1; k; --k) { 44 | state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> 30)) * 1566083941UL)) - i; 45 | state[i] &= 0xFFFFFFFFUL; // for > 32 bit machines 46 | if ((++i) == n) { state[0] = state[n - 1]; i = 1; } 47 | } 48 | state[0] = 0x80000000UL; // MSB is 1; assuring non-zero initial array 49 | p = n; // force gen_state() to be called for next random number 50 | } 51 | -------------------------------------------------------------------------------- /scripts/recordDistance_two_robots.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import rospy 4 | import tf 5 | from geometry_msgs.msg import Point 6 | from math import sqrt 7 | from copy import copy 8 | 9 | class RecordDistance(): 10 | 11 | def __init__(self): 12 | 13 | # init and name the node 14 | rospy.init_node('recordDistanceNode', anonymous=False) 15 | 16 | self.listener = tf.TransformListener() 17 | 18 | rateHz = 1 19 | 20 | rate = rospy.Rate(rateHz) 21 | 22 | robots = ['robot_1', 'robot_2'] 23 | 24 | distances = [0, 0] 25 | last = [] 26 | p = Point() 27 | p.x = 0 28 | p.y = 0 29 | for i in range(len(distances)): 30 | last.append(copy(p)) 31 | 32 | for i in range(len(robots)): 33 | temp = 1 34 | while temp != 0: 35 | try: 36 | self.listener.waitForTransform('/'+ robots[i]+'/odom','/'+ robots[i]+'/base_link', rospy.Time(), rospy.Duration(5)) 37 | temp = 0 38 | except(tf.Exception): 39 | rospy.logerr("can't get the odom frame of robots" + str(i)) 40 | 41 | 42 | # rospy.signal_shutdown('tf Exception') 43 | 44 | while not rospy.is_shutdown(): 45 | dist = 0 46 | for i in range(len(robots)): 47 | position = self.get_odom(robots[i]) 48 | distances[i] += sqrt((position.x-last[i].x)**2 + (position.y-last[i].y)**2) 49 | dist += distances[i] 50 | last[i].x = position.x 51 | last[i].y = position.y 52 | 53 | rospy.logwarn('robots'+str(i)+' distance: ' + str(distances[i])) 54 | rospy.logwarn('Total distance: ' + str(dist)) 55 | rate.sleep() 56 | # rospy.sleep(5) 57 | 58 | def get_odom(self, robot): 59 | # get the current transform between the odom and base frame 60 | try: 61 | (trans, rot) = self.listener.lookupTransform('/'+ robot+'/odom', '/'+robot+'/base_link', rospy.Time(0)) 62 | except (tf.Exception): 63 | rospy.ERROR('tf Exception') 64 | return 65 | return Point(*trans) 66 | 67 | if __name__ == '__main__': 68 | try: 69 | RecordDistance() 70 | except (Exception): 71 | rospy.loginfo("Terminated.") 72 | -------------------------------------------------------------------------------- /scripts/recordDistance.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import rospy 4 | import tf 5 | from geometry_msgs.msg import Point 6 | from math import sqrt 7 | from copy import copy 8 | 9 | class RecordDistance(): 10 | 11 | def __init__(self): 12 | 13 | # init and name the node 14 | rospy.init_node('recordDistanceNode', anonymous=False) 15 | 16 | self.listener = tf.TransformListener() 17 | 18 | rateHz = 0.2 19 | 20 | rate = rospy.Rate(rateHz) 21 | 22 | robots = ['robot_1', 'robot_2', 'robot_3'] 23 | 24 | distances = [0, 0, 0] 25 | last = [] 26 | p = Point() 27 | p.x = 0 28 | p.y = 0 29 | for i in range(len(distances)): 30 | last.append(copy(p)) 31 | 32 | for i in range(len(robots)): 33 | temp = 1 34 | while temp != 0: 35 | try: 36 | self.listener.waitForTransform('/'+ robots[i]+'/odom','/'+ robots[i]+'/base_link', rospy.Time(), rospy.Duration(5)) 37 | temp = 0 38 | except(tf.Exception): 39 | rospy.logerr("can't get the odom frame of robots" + str(i)) 40 | 41 | 42 | # rospy.signal_shutdown('tf Exception') 43 | 44 | while not rospy.is_shutdown(): 45 | dist = 0 46 | for i in range(len(robots)): 47 | position = self.get_odom(robots[i]) 48 | distances[i] += sqrt((position.x-last[i].x)**2 + (position.y-last[i].y)**2) 49 | dist += distances[i] 50 | last[i].x = position.x 51 | last[i].y = position.y 52 | 53 | rospy.logwarn('robots'+str(i)+' distance: ' + str(distances[i])) 54 | rospy.logwarn('Total distance: ' + str(dist)) 55 | # rate.sleep() 56 | rospy.sleep(5) 57 | 58 | def get_odom(self, robot): 59 | # get the current transform between the odom and base frame 60 | try: 61 | (trans, rot) = self.listener.lookupTransform('/'+ robot+'/odom', '/'+robot+'/base_link', rospy.Time(0)) 62 | except (tf.Exception): 63 | rospy.ERROR('tf Exception') 64 | return 65 | return Point(*trans) 66 | 67 | if __name__ == '__main__': 68 | try: 69 | RecordDistance() 70 | except (Exception): 71 | rospy.loginfo("Terminated.") 72 | -------------------------------------------------------------------------------- /scripts/recordDistance_two_robots_real.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import rospy 4 | import tf 5 | from geometry_msgs.msg import Point 6 | from math import sqrt 7 | from copy import copy 8 | 9 | class RecordDistance(): 10 | 11 | def __init__(self): 12 | 13 | # init and name the node 14 | rospy.init_node('recordDistanceNode', anonymous=False) 15 | 16 | self.listener = tf.TransformListener() 17 | 18 | rateHz = 0.5 19 | 20 | rate = rospy.Rate(rateHz) 21 | 22 | robots = ['robot_0', 'robot_1'] 23 | 24 | distances = [0, 0] 25 | last = [] 26 | p = Point() 27 | p.x = 0 28 | p.y = 0 29 | for i in range(len(distances)): 30 | last.append(copy(p)) 31 | 32 | for i in range(len(robots)): 33 | temp = 1 34 | while temp != 0: 35 | try: 36 | self.listener.waitForTransform('/'+ robots[i]+'/odom','/'+ robots[i]+'/base_footprint', rospy.Time(), rospy.Duration(5)) 37 | temp = 0 38 | except(tf.Exception): 39 | rospy.logerr("can't get the odom frame of robots" + str(i)) 40 | 41 | 42 | # rospy.signal_shutdown('tf Exception') 43 | 44 | while not rospy.is_shutdown(): 45 | dist = 0 46 | for i in range(len(robots)): 47 | position = self.get_odom(robots[i]) 48 | distances[i] += sqrt((position.x-last[i].x)**2 + (position.y-last[i].y)**2) 49 | dist += distances[i] 50 | last[i].x = position.x 51 | last[i].y = position.y 52 | 53 | rospy.logwarn('robots'+str(i)+' distance: ' + str(distances[i])) 54 | rospy.logwarn('Total distance: ' + str(dist)) 55 | rate.sleep() 56 | # rospy.sleep(5) 57 | 58 | def get_odom(self, robot): 59 | # get the current transform between the odom and base frame 60 | try: 61 | (trans, rot) = self.listener.lookupTransform('/'+ robot+'/odom', '/'+robot+'/base_footprint', rospy.Time(0)) 62 | except (tf.Exception): 63 | rospy.ERROR('tf Exception') 64 | return 65 | return Point(*trans) 66 | 67 | if __name__ == '__main__': 68 | try: 69 | RecordDistance() 70 | except (Exception): 71 | rospy.loginfo("Terminated.") 72 | -------------------------------------------------------------------------------- /launch/three_tb3.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # multi_rrt_exploration 2 | 3 | This package is adapted from rrt_exploration. You could run the following command on the terminal: 4 | ```sh 5 | $ roslaunch multi_rrt_exploration rrt_three_robots.launch 6 | ``` 7 | 8 | #### 介绍 9 | 该功能包改编于GitHub上的开源项目[rrt_exploration](https://github.com/hasauino/rrt_exploration),笔者已在ROS melodic下跑通(采用三台turtlebot3机器人测试),整体来看,该项目中所使用的RRT算法扩展性与稳定性较好,在计算机算力允许的范围内,能够扩展至更多的机器人数量。 10 | 11 | #### 软件依赖 12 | 要在ROS melodic上成功运行该功能包并非一件容易的事,笔者花费了1周时间才将其从kinetic成功移植到melodic中,所需依赖如下: 13 | 1. 地图融合:[m-explore](https://github.com/hrnr/m-explore) 14 | 2. 探索插件:[frontier_exploration](https://github.com/paulbovbel/frontier_exploration) 15 | 3. 机器人模型:[turtlebot3](https://github.com/ROBOTIS-GIT/turtlebot3) 16 | 4. [turtlebot3_msgs](https://github.com/ROBOTIS-GIT/turtlebot3_msgs) 17 | 5. Python库:OpenCV、Numpy、Sklearn 18 | 19 | ```sh 20 | $ sudo apt-get install python-opencv 21 | $ sudo apt-get install python-numpy 22 | $ sudo apt-get install python-scikits-learn 23 | ``` 24 | 25 | #### 使用说明 26 | 1. 仿真: 27 | ```sh 28 | $ roslaunch multi_rrt_exploration rrt_three_robots.launch 29 | ``` 30 | 31 | 2. 实物(导航): 32 | ```sh 33 | master: roscore 34 | robot_0: export ROS_NAMESPACE=robot_0 35 | robot_0: roslaunch rikirobot lidar_slam_multi.launch tf_prefix:=robot_0 36 | robot_1: export ROS_NAMESPACE=robot_1 37 | robot_1: roslaunch robot_navigation robot_slam_laser_multi.launch tf_prefix:=robot_1 38 | master: roslaunch multirobot_test robots_merge.launch 39 | ``` 40 | 41 | 3. 实物(探索): 42 | ```sh 43 | master: roslaunch multirobot_test robots_merge.launch 44 | robot_: export ROS_NAMESPACE=robot_ 45 | robot_: roslaunch rikirobot lidar_slam_multi.launch tf_prefix:=robot_ 46 | master: roslaunch explore_lite explore.launch 47 | ``` 48 | 49 | ### 修改说明: 50 | 1. robot_: 配置 launch文件中加入 move_base explore_robot_.launch 配置使用公共 /map 话题 51 | 2. 修改 move_base global_costmap_params.yaml 、local_costmap_params.yaml中坐标系 52 | 53 | ## 部分实验结果: 54 | 55 | #### 多机器人协同探索实验 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /launch/three_robots.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /launch/rrt_three_robots.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /launch/two_robots.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /launch/two_robots_real.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /scripts/frontier_opencv_detector.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | #--------Include modules--------------- 5 | import rospy 6 | from visualization_msgs.msg import Marker 7 | from nav_msgs.msg import OccupancyGrid 8 | from geometry_msgs.msg import PointStamped 9 | from getfrontier import getfrontier 10 | 11 | #----------------------------------------------------- 12 | # Subscribers' callbacks------------------------------ 13 | mapData=OccupancyGrid() 14 | 15 | 16 | def mapCallBack(data): 17 | global mapData 18 | mapData=data 19 | 20 | 21 | 22 | 23 | # Node---------------------------------------------- 24 | def node(): 25 | global mapData 26 | exploration_goal=PointStamped() 27 | map_topic= rospy.get_param('~map_topic','robot_1/map') 28 | rospy.Subscriber(map_topic, OccupancyGrid, mapCallBack) 29 | targetspub = rospy.Publisher('/detected_points', PointStamped, queue_size=10) 30 | pub = rospy.Publisher('shapes', Marker, queue_size=10) 31 | rospy.init_node('detector', anonymous=False) 32 | # wait until map is received, when a map is received, mapData.header.seq will not be < 1 33 | while mapData.header.seq<1 or len(mapData.data)<1: 34 | pass 35 | 36 | rate = rospy.Rate(50) 37 | points=Marker() 38 | 39 | #Set the frame ID and timestamp. See the TF tutorials for information on these. 40 | points.header.frame_id=mapData.header.frame_id 41 | points.header.stamp=rospy.Time.now() 42 | 43 | points.ns= "markers" 44 | points.id = 0 45 | 46 | points.type = Marker.POINTS 47 | #Set the marker action. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 48 | points.action = Marker.ADD; 49 | 50 | points.pose.orientation.w = 1.0; 51 | points.scale.x=points.scale.y=0.3; 52 | points.color.r = 255.0/255.0 53 | points.color.g = 0.0/255.0 54 | points.color.b = 0.0/255.0 55 | points.color.a=1; 56 | points.lifetime == rospy.Duration(); 57 | 58 | #-------------------------------OpenCV frontier detection------------------------------------------ 59 | while not rospy.is_shutdown(): 60 | frontiers=getfrontier(mapData) 61 | for i in range(len(frontiers)): 62 | x=frontiers[i] 63 | exploration_goal.header.frame_id= mapData.header.frame_id 64 | exploration_goal.header.stamp=rospy.Time(0) 65 | exploration_goal.point.x=x[0] 66 | exploration_goal.point.y=x[1] 67 | exploration_goal.point.z=0 68 | 69 | 70 | targetspub.publish(exploration_goal) 71 | points.points=[exploration_goal.point] 72 | pub.publish(points) 73 | rate.sleep() 74 | 75 | 76 | 77 | #rate.sleep() 78 | 79 | 80 | 81 | #_____________________________________________________________________________ 82 | 83 | if __name__ == '__main__': 84 | try: 85 | node() 86 | except rospy.ROSInterruptException: 87 | pass 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/functions.cpp: -------------------------------------------------------------------------------- 1 | #include "functions.h" 2 | 3 | 4 | // rdm class, for gentaring random flot numbers 5 | rdm::rdm() {i=time(0);} 6 | float rdm::randomize() { i=i+1; srand (i); return float(rand())/float(RAND_MAX);} 7 | 8 | 9 | 10 | //Norm function 11 | float Norm(std::vector x1,std::vector x2) 12 | { 13 | return pow( (pow((x2[0]-x1[0]),2)+pow((x2[1]-x1[1]),2)) ,0.5); 14 | } 15 | 16 | 17 | //sign function 18 | float sign(float n) 19 | { 20 | if (n<0.0){return -1.0;} 21 | else{return 1.0;} 22 | } 23 | 24 | 25 | //Nearest function 26 | std::vector Nearest( std::vector< std::vector > V, std::vector x){ 27 | 28 | float min=Norm(V[0],x); 29 | int min_index; 30 | float temp; 31 | 32 | for (int j=0;j Steer( std::vector x_nearest , std::vector x_rand, float eta){ 48 | std::vector x_new; 49 | 50 | if (Norm(x_nearest,x_rand)<=eta){ 51 | x_new=x_rand; 52 | } 53 | else{ 54 | 55 | 56 | float m=(x_rand[1]-x_nearest[1])/(x_rand[0]-x_nearest[0]); 57 | 58 | x_new.push_back( (sign(x_rand[0]-x_nearest[0]))* ( sqrt( (pow(eta,2)) / ((pow(m,2))+1) ) )+x_nearest[0] ); 59 | x_new.push_back( m*(x_new[0]-x_nearest[0])+x_nearest[1] ); 60 | 61 | if(x_rand[0]==x_nearest[0]){ 62 | x_new[0]=x_nearest[0]; 63 | x_new[1]=x_nearest[1]+eta; 64 | } 65 | 66 | 67 | 68 | } 69 | return x_new; 70 | } 71 | 72 | 73 | 74 | 75 | 76 | //gridValue function 77 | int gridValue(nav_msgs::OccupancyGrid &mapData,std::vector Xp){ 78 | 79 | float resolution=mapData.info.resolution; 80 | float Xstartx=mapData.info.origin.position.x; 81 | float Xstarty=mapData.info.origin.position.y; 82 | 83 | float width=mapData.info.width; 84 | std::vector Data=mapData.data; 85 | 86 | //returns grid value at "Xp" location 87 | //map data: 100 occupied -1 unknown 0 free 88 | float indx=( floor((Xp[1]-Xstarty)/resolution)*width)+( floor((Xp[0]-Xstartx)/resolution) ); 89 | int out; 90 | out=Data[int(indx)]; 91 | return out; 92 | } 93 | 94 | 95 | 96 | 97 | // ObstacleFree function------------------------------------- 98 | 99 | char ObstacleFree(std::vector xnear, std::vector &xnew, nav_msgs::OccupancyGrid mapsub){ 100 | float rez=float(mapsub.info.resolution)*.2; 101 | float stepz=int(ceil(Norm(xnew,xnear))/rez); 102 | std::vector xi=xnear; 103 | char obs=0; char unk=0; 104 | 105 | geometry_msgs::Point p; 106 | for (int c=0;c out = nhood8(indx, mapsub); 140 | 141 | // center of the frontier 142 | float centerx = 0; 143 | float centery = 0; 144 | unsigned int size = 1; 145 | 146 | 147 | std::vector data = mapsub.data; 148 | 149 | if (data[indx] != -1) { 150 | ROS_WARN("The real map is not match with the gridmap!"); 151 | return false; 152 | } 153 | 154 | std::queue queue; 155 | std::vector visited(width*height, false); 156 | 157 | // push the initial cell 158 | queue.push(indx); 159 | visited[indx] = true; 160 | 161 | while (!queue.empty()) { 162 | unsigned int idx = queue.front(); 163 | queue.pop(); 164 | 165 | for (unsigned int nbr : nhood8(idx, mapsub)) { 166 | if (isNewFrontierCell(nbr, mapsub, visited)) { 167 | visited[nbr] = true; 168 | queue.push(nbr); 169 | 170 | std::vector point = pointOfIndex(mapsub, nbr); 171 | centerx += point[0]; 172 | centery += point[1]; 173 | size++; 174 | 175 | //std::cout<<"-----------------------\n" << point[0] << "," << point[1]<< std::endl; 176 | 177 | } 178 | } 179 | 180 | 181 | } 182 | 183 | exploration_goal.point.x = centerx/size; 184 | exploration_goal.point.y = centery/size; 185 | 186 | return true; 187 | } 188 | 189 | std::vector nhood8(unsigned int indx, nav_msgs::OccupancyGrid& mapData) { 190 | 191 | //returns grid value at "Xp" location 192 | //map data: 100 occupied -1 unknown 0 free 193 | unsigned int width = mapData.info.width; 194 | unsigned int height = mapData.info.height; 195 | //float indx=( floor((p.y-Xstarty)/resolution)*width)+( floor((p.x-Xstartx)/resolution) ); 196 | std::vector out; 197 | //std::vector Xp; 198 | //Xp.push_back(p.x); 199 | 200 | if (indx > width * height - 1) { 201 | ROS_WARN("Evaluating nhood for offmap point"); 202 | return out; 203 | } 204 | 205 | if (indx % width > 0) { 206 | out.push_back(indx-1); 207 | } 208 | 209 | if (indx % width < (width-1)) { 210 | out.push_back(indx+1); 211 | } 212 | 213 | if (indx >= width) { 214 | out.push_back(indx - width); 215 | } 216 | 217 | if (indx < width * (height-1)) { 218 | out.push_back(indx+width); 219 | } 220 | 221 | if (indx % width > 0 && indx >= width) { 222 | out.push_back(indx-1-width); 223 | } 224 | 225 | if (indx % width > 0 && indx < width*(height-1)){ 226 | out.push_back(indx-1+width); 227 | } 228 | 229 | if (indx % width < (width -1) && indx>=width) { 230 | out.push_back(indx+1-width); 231 | } 232 | 233 | if (indx % width < (width-1) && indx < width*(height-1)) { 234 | out.push_back(indx+1+width); 235 | } 236 | 237 | return out; 238 | } 239 | 240 | 241 | bool isNewFrontierCell(unsigned indx, nav_msgs::OccupancyGrid& mapData, const std::vector& visited) { 242 | std::vector data = mapData.data; 243 | 244 | //map data: 100 occupied -1 unknown 0 free 245 | if (data[indx] != -1 || visited[indx]) { 246 | return false; 247 | } 248 | 249 | for (unsigned int nbr : nhood8(indx, mapData)) { 250 | if (data[nbr] == 0) { 251 | return true; 252 | } 253 | } 254 | 255 | return false; 256 | } 257 | 258 | std::vector pointOfIndex(nav_msgs::OccupancyGrid& mapData, unsigned int indx){ 259 | std::vector point; 260 | unsigned int width = mapData.info.width; 261 | float Xstartx = mapData.info.origin.position.x; 262 | float Xstarty = mapData.info.origin.position.y; 263 | float resolution = mapData.info.resolution; 264 | 265 | float x = Xstartx + (indx % width)*resolution; 266 | float y = Xstarty + (indx / width)*resolution; 267 | 268 | point.push_back(x); 269 | point.push_back(y); 270 | 271 | return point; 272 | } 273 | -------------------------------------------------------------------------------- /scripts/assigner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | #--------Include modules--------------- 4 | from copy import copy 5 | import rospy 6 | from visualization_msgs.msg import Marker 7 | from geometry_msgs.msg import Point 8 | from nav_msgs.msg import OccupancyGrid 9 | import tf 10 | from multi_rrt_exploration.msg import PointArray 11 | from time import time 12 | from numpy import array 13 | from numpy import linalg as LA 14 | from numpy import all as All 15 | from numpy import inf 16 | from functions import robot,informationGain,discount 17 | from numpy.linalg import norm 18 | import time 19 | 20 | # Subscribers' callbacks------------------------------ 21 | mapData=OccupancyGrid() 22 | frontiers=[] 23 | global1=OccupancyGrid() 24 | global2=OccupancyGrid() 25 | global3=OccupancyGrid() 26 | globalmaps=[] 27 | def callBack(data): 28 | global frontiers 29 | frontiers=[] 30 | for point in data.points: 31 | frontiers.append(array([point.x,point.y])) 32 | 33 | def mapCallBack(data): 34 | global mapData 35 | mapData=data 36 | # Node---------------------------------------------- 37 | 38 | def node(): 39 | 40 | stop = 0 41 | 42 | global frontiers,mapData,global1,global2,global3,globalmaps 43 | rospy.init_node('assigner', anonymous=False) 44 | 45 | # fetching all parameters 46 | map_topic= rospy.get_param('~map_topic','/map_merge/map') 47 | info_radius= rospy.get_param('~info_radius',1.0) #this can be smaller than the laser scanner range, >> smaller >>less computation time>> too small is not good, info gain won't be accurate 48 | info_multiplier=rospy.get_param('~info_multiplier',3.0) 49 | hysteresis_radius=rospy.get_param('~hysteresis_radius',3.0) #at least as much as the laser scanner range 50 | hysteresis_gain=rospy.get_param('~hysteresis_gain',2.0) #bigger than 1 (biase robot to continue exploring current region 51 | frontiers_topic= rospy.get_param('~frontiers_topic','/filtered_points') 52 | n_robots = rospy.get_param('~n_robots',1) 53 | namespace = rospy.get_param('~namespace','') 54 | namespace_init_count = rospy.get_param('~namespace_init_count',1) 55 | delay_after_assignement=rospy.get_param('~delay_after_assignement',0.5) 56 | rateHz = rospy.get_param('~rate',2) 57 | 58 | rate = rospy.Rate(rateHz) 59 | #------------------------------------------- 60 | rospy.Subscriber(map_topic, OccupancyGrid, mapCallBack) 61 | rospy.Subscriber(frontiers_topic, PointArray, callBack) 62 | #--------------------------------------------------------------------------------------------------------------- 63 | # wait if no frontier is received yet 64 | while len(frontiers)<1: 65 | pass 66 | centroids=copy(frontiers) 67 | #wait if map is not received yet 68 | while (len(mapData.data)<1): 69 | pass 70 | 71 | robots=[] 72 | if len(namespace)>0: 73 | for i in range(0,n_robots): 74 | robots.append(robot(namespace+str(i+namespace_init_count))) 75 | elif len(namespace)==0: 76 | robots.append(robot(namespace)) 77 | for i in range(0,n_robots): 78 | robots[i].sendGoal(robots[i].getPosition()) 79 | 80 | 81 | start = time.time() 82 | #------------------------------------------------------------------------- 83 | #-------------------- Initial Rotation -------------------------- 84 | 85 | for i in range(0,n_robots): 86 | robots[i].sendGoal(robots[i].getPosition(), rotation=True) 87 | rospy.sleep(3); 88 | rospy.loginfo("initial finished.") 89 | 90 | 91 | #------------------------------------------------------------------------- 92 | #--------------------- Main Loop ------------------------------- 93 | #------------------------------------------------------------------------- 94 | while not rospy.is_shutdown(): 95 | centroids=copy(frontiers) 96 | #------------------------------------------------------------------------- 97 | #Get information gain for each frontier point 98 | infoGain=[] 99 | for ip in range(0,len(centroids)): 100 | infoGain.append(informationGain(mapData,[centroids[ip][0],centroids[ip][1]],info_radius)) 101 | #------------------------------------------------------------------------- 102 | #get number of available/busy robots 103 | na=[] #available robots 104 | nb=[] #busy robots 105 | for i in range(0,n_robots): 106 | if (robots[i].getState()==1): 107 | nb.append(i) 108 | else: 109 | na.append(i) 110 | rospy.loginfo("available robots: "+str(na)) 111 | #------------------------------------------------------------------------- 112 | #get dicount and update informationGain 113 | for i in nb+na: 114 | infoGain=discount(mapData,robots[i].assigned_point,centroids,infoGain,info_radius) 115 | #------------------------------------------------------------------------- 116 | revenue_record=[] 117 | centroid_record=[] 118 | id_record=[] 119 | 120 | for ir in na: 121 | for ip in range(0,len(centroids)): 122 | cost=norm(robots[ir].getPosition()-centroids[ip]) 123 | threshold=1 124 | information_gain=infoGain[ip] 125 | if (norm(robots[ir].getPosition()-centroids[ip])<=hysteresis_radius): 126 | 127 | information_gain*=hysteresis_gain 128 | revenue=information_gain*info_multiplier-cost 129 | revenue_record.append(revenue) 130 | centroid_record.append(centroids[ip]) 131 | id_record.append(ir) 132 | 133 | if len(na)<1: 134 | revenue_record=[] 135 | centroid_record=[] 136 | id_record=[] 137 | for ir in nb: 138 | for ip in range(0,len(centroids)): 139 | cost=norm(robots[ir].getPosition()-centroids[ip]) 140 | threshold=1 141 | information_gain=infoGain[ip] 142 | if (norm(robots[ir].getPosition()-centroids[ip])<=hysteresis_radius): 143 | information_gain*=hysteresis_gain 144 | 145 | if ((norm(centroids[ip]-robots[ir].assigned_point))0): 159 | stop = 0 160 | winner_id=revenue_record.index(max(revenue_record)) 161 | robots[id_record[winner_id]].sendGoal(centroid_record[winner_id]) 162 | rospy.loginfo(namespace+str(namespace_init_count+id_record[winner_id])+" assigned to "+str(centroid_record[winner_id])) 163 | rospy.sleep(delay_after_assignement) 164 | else: 165 | stop += 1 166 | 167 | if (stop == rateHz*5): 168 | for i in range(0, n_robots): 169 | robots[i].cancelGoal() 170 | 171 | rospy.loginfo("Exploration has been finished.") 172 | break 173 | #------------------------------------------------------------------------- 174 | rate.sleep() 175 | #------------------------------------------------------------------------- 176 | end = time.time() 177 | rospy.logwarn("total time: " + str(end-start)) 178 | 179 | 180 | if __name__ == '__main__': 181 | try: 182 | node() 183 | except rospy.ROSInterruptException: 184 | pass 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /scripts/assigner_real_two_robots.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | #--------Include modules--------------- 4 | from copy import copy 5 | import rospy 6 | from visualization_msgs.msg import Marker 7 | from geometry_msgs.msg import Point 8 | from nav_msgs.msg import OccupancyGrid 9 | import tf 10 | from rrt_exploration.msg import PointArray 11 | from time import time 12 | from numpy import array 13 | from numpy import linalg as LA 14 | from numpy import all as All 15 | from numpy import inf 16 | from functions_real_two_robots import robot,informationGain,discount 17 | from numpy.linalg import norm 18 | import time 19 | 20 | # Subscribers' callbacks------------------------------ 21 | mapData=OccupancyGrid() 22 | frontiers=[] 23 | global1=OccupancyGrid() 24 | global2=OccupancyGrid() 25 | global3=OccupancyGrid() 26 | globalmaps=[] 27 | def callBack(data): 28 | global frontiers 29 | frontiers=[] 30 | for point in data.points: 31 | frontiers.append(array([point.x,point.y])) 32 | 33 | def mapCallBack(data): 34 | global mapData 35 | mapData=data 36 | # Node---------------------------------------------- 37 | 38 | def node(): 39 | 40 | stop = 0 41 | 42 | global frontiers,mapData,global1,global2,global3,globalmaps 43 | rospy.init_node('assigner', anonymous=False) 44 | 45 | # fetching all parameters 46 | map_topic= rospy.get_param('~map_topic','/map') 47 | info_radius= rospy.get_param('~info_radius',1.0) #this can be smaller than the laser scanner range, >> smaller >>less computation time>> too small is not good, info gain won't be accurate 48 | info_multiplier=rospy.get_param('~info_multiplier',3.0) 49 | hysteresis_radius=rospy.get_param('~hysteresis_radius',3.0) #at least as much as the laser scanner range 50 | hysteresis_gain=rospy.get_param('~hysteresis_gain',2.0) #bigger than 1 (biase robot to continue exploring current region 51 | frontiers_topic= rospy.get_param('~frontiers_topic','/filtered_points') 52 | n_robots = rospy.get_param('~n_robots',1) 53 | namespace = rospy.get_param('~namespace','') 54 | namespace_init_count = rospy.get_param('~namespace_init_count',1) 55 | delay_after_assignement=rospy.get_param('~delay_after_assignement',0.5) 56 | rateHz = rospy.get_param('~rate',2) 57 | 58 | rate = rospy.Rate(rateHz) 59 | #------------------------------------------- 60 | rospy.Subscriber(map_topic, OccupancyGrid, mapCallBack) 61 | rospy.Subscriber(frontiers_topic, PointArray, callBack) 62 | #--------------------------------------------------------------------------------------------------------------- 63 | # wait if no frontier is received yet 64 | while len(frontiers)<1: 65 | pass 66 | centroids=copy(frontiers) 67 | #wait if map is not received yet 68 | while (len(mapData.data)<1): 69 | pass 70 | 71 | robots=[] 72 | if len(namespace)>0: 73 | for i in range(0,n_robots): 74 | robots.append(robot(namespace+str(i+namespace_init_count))) 75 | elif len(namespace)==0: 76 | robots.append(robot(namespace)) 77 | for i in range(0,n_robots): 78 | robots[i].sendGoal(robots[i].getPosition()) 79 | 80 | 81 | start = time.time() 82 | #------------------------------------------------------------------------- 83 | #-------------------- Initial Rotation -------------------------- 84 | 85 | for i in range(0,n_robots): 86 | robots[i].sendGoal(robots[i].getPosition(), rotation=True) 87 | rospy.sleep(3); 88 | rospy.loginfo("initial finished.") 89 | 90 | 91 | #------------------------------------------------------------------------- 92 | #--------------------- Main Loop ------------------------------- 93 | #------------------------------------------------------------------------- 94 | while not rospy.is_shutdown(): 95 | centroids=copy(frontiers) 96 | #------------------------------------------------------------------------- 97 | #Get information gain for each frontier point 98 | infoGain=[] 99 | for ip in range(0,len(centroids)): 100 | infoGain.append(informationGain(mapData,[centroids[ip][0],centroids[ip][1]],info_radius)) 101 | #------------------------------------------------------------------------- 102 | #get number of available/busy robots 103 | na=[] #available robots 104 | nb=[] #busy robots 105 | for i in range(0,n_robots): 106 | if (robots[i].getState()==1): 107 | nb.append(i) 108 | else: 109 | na.append(i) 110 | rospy.loginfo("available robots: "+str(na)) 111 | #------------------------------------------------------------------------- 112 | #get dicount and update informationGain 113 | for i in nb+na: 114 | infoGain=discount(mapData,robots[i].assigned_point,centroids,infoGain,info_radius) 115 | #------------------------------------------------------------------------- 116 | revenue_record=[] 117 | centroid_record=[] 118 | id_record=[] 119 | 120 | for ir in na: 121 | for ip in range(0,len(centroids)): 122 | cost=norm(robots[ir].getPosition()-centroids[ip]) 123 | threshold=1 124 | information_gain=infoGain[ip] 125 | if (norm(robots[ir].getPosition()-centroids[ip])<=hysteresis_radius): 126 | 127 | information_gain*=hysteresis_gain 128 | revenue=information_gain*info_multiplier-cost 129 | revenue_record.append(revenue) 130 | centroid_record.append(centroids[ip]) 131 | id_record.append(ir) 132 | 133 | if len(na)<1: 134 | revenue_record=[] 135 | centroid_record=[] 136 | id_record=[] 137 | for ir in nb: 138 | for ip in range(0,len(centroids)): 139 | cost=norm(robots[ir].getPosition()-centroids[ip]) 140 | threshold=1 141 | information_gain=infoGain[ip] 142 | if (norm(robots[ir].getPosition()-centroids[ip])<=hysteresis_radius): 143 | information_gain*=hysteresis_gain 144 | 145 | if ((norm(centroids[ip]-robots[ir].assigned_point))0): 159 | stop = 0 160 | winner_id=revenue_record.index(max(revenue_record)) 161 | robots[id_record[winner_id]].sendGoal(centroid_record[winner_id]) 162 | rospy.loginfo(namespace+str(namespace_init_count+id_record[winner_id])+" assigned to "+str(centroid_record[winner_id])) 163 | rospy.sleep(delay_after_assignement) 164 | else: 165 | stop += 1 166 | 167 | if (stop == rateHz*1000): 168 | for i in range(0, n_robots): 169 | robots[i].cancelGoal() 170 | 171 | rospy.loginfo("Exploration has been finished.") 172 | break 173 | #------------------------------------------------------------------------- 174 | rate.sleep() 175 | #------------------------------------------------------------------------- 176 | end = time.time() 177 | rospy.logwarn("total time: " + str(end-start)) 178 | 179 | 180 | if __name__ == '__main__': 181 | try: 182 | node() 183 | except rospy.ROSInterruptException: 184 | pass 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /src/global_rrt_detector.cpp: -------------------------------------------------------------------------------- 1 | #include "ros/ros.h" 2 | #include "std_msgs/String.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "stdint.h" 8 | #include "functions.h" 9 | #include "mtrand.h" 10 | 11 | 12 | #include "nav_msgs/OccupancyGrid.h" 13 | #include "geometry_msgs/PointStamped.h" 14 | #include "std_msgs/Header.h" 15 | #include "nav_msgs/MapMetaData.h" 16 | #include "geometry_msgs/Point.h" 17 | #include "visualization_msgs/Marker.h" 18 | #include 19 | 20 | 21 | 22 | // global variables 23 | nav_msgs::OccupancyGrid mapData; 24 | geometry_msgs::PointStamped clickedpoint; 25 | geometry_msgs::PointStamped exploration_goal; 26 | visualization_msgs::Marker points,line; 27 | float xdim,ydim,resolution,Xstartx,Xstarty,init_map_x,init_map_y; 28 | 29 | rdm r; // for genrating random numbers 30 | 31 | 32 | 33 | //Subscribers callback functions--------------------------------------- 34 | void mapCallBack(const nav_msgs::OccupancyGrid::ConstPtr& msg) 35 | { 36 | mapData=*msg; 37 | } 38 | 39 | 40 | 41 | void rvizCallBack(const geometry_msgs::PointStamped::ConstPtr& msg) 42 | { 43 | 44 | geometry_msgs::Point p; 45 | p.x=msg->point.x; 46 | p.y=msg->point.y; 47 | p.z=msg->point.z; 48 | 49 | points.points.push_back(p); 50 | 51 | } 52 | 53 | 54 | 55 | 56 | int main(int argc, char **argv) 57 | { 58 | 59 | unsigned long init[4] = {0x123, 0x234, 0x345, 0x456}, length = 7; 60 | MTRand_int32 irand(init, length); // 32-bit int generator 61 | // this is an example of initializing by an array 62 | // you may use MTRand(seed) with any 32bit integer 63 | // as a seed for a simpler initialization 64 | MTRand drand; // double in [0, 1) generator, already init 65 | 66 | // generate the same numbers as in the original C test program 67 | ros::init(argc, argv, "global_rrt_frontier_detector"); 68 | ros::NodeHandle nh; 69 | 70 | 71 | //debug enabled 72 | if (ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Info)) { 73 | ros::console::notifyLoggerLevelsChanged(); 74 | } 75 | 76 | // fetching all parameters 77 | float eta,init_map_x,init_map_y,range; 78 | std::string map_topic,base_frame_topic; 79 | 80 | std::string ns; 81 | ns=ros::this_node::getName(); 82 | 83 | ros::param::param(ns+"/eta", eta, 0.5); 84 | ros::param::param(ns+"/map_topic", map_topic, "robot_1/map"); 85 | //--------------------------------------------------------------- 86 | ros::Subscriber sub= nh.subscribe(map_topic, 100 ,mapCallBack); 87 | ros::Subscriber rviz_sub= nh.subscribe("/clicked_point", 100 ,rvizCallBack); 88 | 89 | ros::Publisher targetspub = nh.advertise("/detected_points", 10); 90 | ros::Publisher pub = nh.advertise(ns+"_shapes", 10); 91 | 92 | ros::Rate rate(100); 93 | 94 | 95 | // wait until map is received, when a map is received, mapData.header.seq will not be < 1 96 | while (mapData.header.seq<1 or mapData.data.size()<1) { ros::spinOnce(); ros::Duration(0.1).sleep();} 97 | 98 | 99 | 100 | //visualizations points and lines.. 101 | points.header.frame_id=mapData.header.frame_id; 102 | line.header.frame_id=mapData.header.frame_id; 103 | points.header.stamp=ros::Time(0); 104 | line.header.stamp=ros::Time(0); 105 | 106 | points.ns=line.ns = "markers"; 107 | points.id = 0; 108 | line.id =1; 109 | 110 | 111 | points.type = points.POINTS; 112 | line.type=line.LINE_LIST; 113 | 114 | //Set the marker action. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 115 | points.action =points.ADD; 116 | line.action = line.ADD; 117 | points.pose.orientation.w =1.0; 118 | line.pose.orientation.w = 1.0; 119 | line.scale.x = 0.03; 120 | line.scale.y= 0.03; 121 | points.scale.x=0.3; 122 | points.scale.y=0.3; 123 | 124 | line.color.r =9.0/255.0; 125 | line.color.g= 91.0/255.0; 126 | line.color.b =236.0/255.0; 127 | points.color.r = 255.0/255.0; 128 | points.color.g = 0.0/255.0; 129 | points.color.b = 0.0/255.0; 130 | points.color.a=1.0; 131 | line.color.a = 1.0; 132 | points.lifetime = ros::Duration(); 133 | line.lifetime = ros::Duration(); 134 | 135 | geometry_msgs::Point p; 136 | 137 | 138 | while(points.points.size()<5) 139 | { 140 | ros::spinOnce(); 141 | 142 | pub.publish(points) ; 143 | } 144 | 145 | 146 | 147 | 148 | std::vector temp1; 149 | temp1.push_back(points.points[0].x); 150 | temp1.push_back(points.points[0].y); 151 | 152 | std::vector temp2; 153 | temp2.push_back(points.points[2].x); 154 | temp2.push_back(points.points[0].y); 155 | 156 | 157 | init_map_x=Norm(temp1,temp2); 158 | temp1.clear(); temp2.clear(); 159 | 160 | temp1.push_back(points.points[0].x); 161 | temp1.push_back(points.points[0].y); 162 | 163 | temp2.push_back(points.points[0].x); 164 | temp2.push_back(points.points[2].y); 165 | 166 | init_map_y=Norm(temp1,temp2); 167 | temp1.clear(); temp2.clear(); 168 | 169 | Xstartx=(points.points[0].x+points.points[2].x)*.5; 170 | Xstarty=(points.points[0].y+points.points[2].y)*.5; 171 | 172 | 173 | 174 | 175 | 176 | geometry_msgs::Point trans; 177 | trans=points.points[4]; 178 | std::vector< std::vector > V; 179 | std::vector xnew; 180 | xnew.push_back( trans.x);xnew.push_back( trans.y); 181 | V.push_back(xnew); 182 | 183 | points.points.clear(); 184 | pub.publish(points) ; 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | std::vector frontiers; 193 | int i=0; 194 | float xr,yr; 195 | std::vector x_rand,x_nearest,x_new; 196 | 197 | 198 | // Main loop 199 | while (ros::ok()){ 200 | 201 | 202 | // Sample free 203 | x_rand.clear(); 204 | xr=(drand()*init_map_x)-(init_map_x*0.5)+Xstartx; 205 | yr=(drand()*init_map_y)-(init_map_y*0.5)+Xstarty; 206 | 207 | 208 | x_rand.push_back( xr ); x_rand.push_back( yr ); 209 | 210 | 211 | // Nearest 212 | x_nearest=Nearest(V,x_rand); 213 | 214 | // Steer 215 | 216 | x_new=Steer(x_nearest,x_rand,eta); 217 | 218 | 219 | // ObstacleFree 1:free -1:unkown (frontier region) 0:obstacle 220 | char checking=ObstacleFree(x_nearest,x_new,mapData); 221 | 222 | if (checking==-1){ 223 | //geometry_msgs::PointStamped exploration_point; 224 | 225 | // the first point of a frontier 226 | p.x=x_new[0]; 227 | p.y=x_new[1]; 228 | p.z=0.0; 229 | 230 | ROS_DEBUG("before(%f, %f)", p.x, p.y); 231 | bool existFrontier = getCompleteFrontier(p, exploration_goal, mapData); 232 | if (!existFrontier) { 233 | continue; 234 | } 235 | 236 | exploration_goal.header.stamp=ros::Time(0); 237 | exploration_goal.header.frame_id=mapData.header.frame_id; 238 | //exploration_goal.point.x=x_new[0]; 239 | //exploration_goal.point.y=x_new[1]; 240 | exploration_goal.point.z=0.0; 241 | 242 | //p.x = exploration_goal.point.x; 243 | //p.y = exploration_goal.point.y; 244 | points.points.push_back(p); 245 | 246 | ROS_DEBUG("after(%f, %f)", p.x, p.y); 247 | 248 | pub.publish(points) ; 249 | targetspub.publish(exploration_goal); 250 | points.points.clear(); 251 | 252 | } 253 | 254 | 255 | else if (checking==1){ 256 | V.push_back(x_new); 257 | 258 | p.x=x_new[0]; 259 | p.y=x_new[1]; 260 | p.z=0.0; 261 | line.points.push_back(p); 262 | p.x=x_nearest[0]; 263 | p.y=x_nearest[1]; 264 | p.z=0.0; 265 | line.points.push_back(p); 266 | 267 | } 268 | 269 | 270 | 271 | pub.publish(line); 272 | 273 | 274 | 275 | 276 | ros::spinOnce(); 277 | rate.sleep(); 278 | }return 0;} 279 | -------------------------------------------------------------------------------- /src/global_rrt_detector_real_two_robots.cpp: -------------------------------------------------------------------------------- 1 | #include "ros/ros.h" 2 | #include "std_msgs/String.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "stdint.h" 8 | #include "functions.h" 9 | #include "mtrand.h" 10 | 11 | 12 | #include "nav_msgs/OccupancyGrid.h" 13 | #include "geometry_msgs/PointStamped.h" 14 | #include "std_msgs/Header.h" 15 | #include "nav_msgs/MapMetaData.h" 16 | #include "geometry_msgs/Point.h" 17 | #include "visualization_msgs/Marker.h" 18 | #include 19 | 20 | 21 | 22 | // global variables 23 | nav_msgs::OccupancyGrid mapData; 24 | geometry_msgs::PointStamped clickedpoint; 25 | geometry_msgs::PointStamped exploration_goal; 26 | visualization_msgs::Marker points,line; 27 | float xdim,ydim,resolution,Xstartx,Xstarty,init_map_x,init_map_y; 28 | 29 | rdm r; // for genrating random numbers 30 | 31 | 32 | 33 | //Subscribers callback functions--------------------------------------- 34 | void mapCallBack(const nav_msgs::OccupancyGrid::ConstPtr& msg) 35 | { 36 | mapData=*msg; 37 | } 38 | 39 | 40 | 41 | void rvizCallBack(const geometry_msgs::PointStamped::ConstPtr& msg) 42 | { 43 | 44 | geometry_msgs::Point p; 45 | p.x=msg->point.x; 46 | p.y=msg->point.y; 47 | p.z=msg->point.z; 48 | 49 | points.points.push_back(p); 50 | 51 | } 52 | 53 | 54 | 55 | 56 | int main(int argc, char **argv) 57 | { 58 | 59 | unsigned long init[4] = {0x123, 0x234, 0x345, 0x456}, length = 7; 60 | MTRand_int32 irand(init, length); // 32-bit int generator 61 | // this is an example of initializing by an array 62 | // you may use MTRand(seed) with any 32bit integer 63 | // as a seed for a simpler initialization 64 | MTRand drand; // double in [0, 1) generator, already init 65 | 66 | // generate the same numbers as in the original C test program 67 | ros::init(argc, argv, "global_rrt_frontier_detector"); 68 | ros::NodeHandle nh; 69 | 70 | 71 | //debug enabled 72 | if (ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Info)) { 73 | ros::console::notifyLoggerLevelsChanged(); 74 | } 75 | 76 | // fetching all parameters 77 | float eta,init_map_x,init_map_y,range; 78 | std::string map_topic,base_frame_topic; 79 | 80 | std::string ns; 81 | ns=ros::this_node::getName(); 82 | 83 | ros::param::param(ns+"/eta", eta, 0.5); 84 | ros::param::param(ns+"/map_topic", map_topic, "/robot_1/map"); 85 | //--------------------------------------------------------------- 86 | ros::Subscriber sub= nh.subscribe(map_topic, 100 ,mapCallBack); 87 | ros::Subscriber rviz_sub= nh.subscribe("/clicked_point", 100 ,rvizCallBack); 88 | 89 | ros::Publisher targetspub = nh.advertise("/detected_points", 10); 90 | ros::Publisher pub = nh.advertise(ns+"_shapes", 10); 91 | 92 | ros::Rate rate(100); 93 | 94 | 95 | // wait until map is received, when a map is received, mapData.header.seq will not be < 1 96 | while (mapData.header.seq<1 or mapData.data.size()<1) { ros::spinOnce(); ros::Duration(0.1).sleep();} 97 | 98 | 99 | 100 | //visualizations points and lines.. 101 | points.header.frame_id=mapData.header.frame_id; 102 | line.header.frame_id=mapData.header.frame_id; 103 | points.header.stamp=ros::Time(0); 104 | line.header.stamp=ros::Time(0); 105 | 106 | points.ns=line.ns = "markers"; 107 | points.id = 0; 108 | line.id =1; 109 | 110 | 111 | points.type = points.POINTS; 112 | line.type=line.LINE_LIST; 113 | 114 | //Set the marker action. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 115 | points.action =points.ADD; 116 | line.action = line.ADD; 117 | points.pose.orientation.w =1.0; 118 | line.pose.orientation.w = 1.0; 119 | line.scale.x = 0.03; 120 | line.scale.y= 0.03; 121 | points.scale.x=0.3; 122 | points.scale.y=0.3; 123 | 124 | line.color.r =9.0/255.0; 125 | line.color.g= 91.0/255.0; 126 | line.color.b =236.0/255.0; 127 | points.color.r = 255.0/255.0; 128 | points.color.g = 0.0/255.0; 129 | points.color.b = 0.0/255.0; 130 | points.color.a=1.0; 131 | line.color.a = 1.0; 132 | points.lifetime = ros::Duration(); 133 | line.lifetime = ros::Duration(); 134 | 135 | geometry_msgs::Point p; 136 | 137 | 138 | while(points.points.size()<5) 139 | { 140 | ros::spinOnce(); 141 | 142 | pub.publish(points) ; 143 | } 144 | 145 | 146 | 147 | 148 | std::vector temp1; 149 | temp1.push_back(points.points[0].x); 150 | temp1.push_back(points.points[0].y); 151 | 152 | std::vector temp2; 153 | temp2.push_back(points.points[2].x); 154 | temp2.push_back(points.points[0].y); 155 | 156 | 157 | init_map_x=Norm(temp1,temp2); 158 | temp1.clear(); temp2.clear(); 159 | 160 | temp1.push_back(points.points[0].x); 161 | temp1.push_back(points.points[0].y); 162 | 163 | temp2.push_back(points.points[0].x); 164 | temp2.push_back(points.points[2].y); 165 | 166 | init_map_y=Norm(temp1,temp2); 167 | temp1.clear(); temp2.clear(); 168 | 169 | Xstartx=(points.points[0].x+points.points[2].x)*.5; 170 | Xstarty=(points.points[0].y+points.points[2].y)*.5; 171 | 172 | 173 | 174 | 175 | 176 | geometry_msgs::Point trans; 177 | trans=points.points[4]; 178 | std::vector< std::vector > V; 179 | std::vector xnew; 180 | xnew.push_back( trans.x);xnew.push_back( trans.y); 181 | V.push_back(xnew); 182 | 183 | points.points.clear(); 184 | pub.publish(points) ; 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | std::vector frontiers; 193 | int i=0; 194 | float xr,yr; 195 | std::vector x_rand,x_nearest,x_new; 196 | 197 | 198 | // Main loop 199 | while (ros::ok()){ 200 | 201 | 202 | // Sample free 203 | x_rand.clear(); 204 | xr=(drand()*init_map_x)-(init_map_x*0.5)+Xstartx; 205 | yr=(drand()*init_map_y)-(init_map_y*0.5)+Xstarty; 206 | 207 | 208 | x_rand.push_back( xr ); x_rand.push_back( yr ); 209 | 210 | 211 | // Nearest 212 | x_nearest=Nearest(V,x_rand); 213 | 214 | // Steer 215 | 216 | x_new=Steer(x_nearest,x_rand,eta); 217 | 218 | 219 | // ObstacleFree 1:free -1:unkown (frontier region) 0:obstacle 220 | char checking=ObstacleFree(x_nearest,x_new,mapData); 221 | 222 | if (checking==-1){ 223 | //geometry_msgs::PointStamped exploration_point; 224 | 225 | // the first point of a frontier 226 | p.x=x_new[0]; 227 | p.y=x_new[1]; 228 | p.z=0.0; 229 | 230 | ROS_DEBUG("before(%f, %f)", p.x, p.y); 231 | bool existFrontier = getCompleteFrontier(p, exploration_goal, mapData); 232 | if (!existFrontier) { 233 | continue; 234 | } 235 | 236 | exploration_goal.header.stamp=ros::Time(0); 237 | exploration_goal.header.frame_id=mapData.header.frame_id; 238 | //exploration_goal.point.x=x_new[0]; 239 | //exploration_goal.point.y=x_new[1]; 240 | exploration_goal.point.z=0.0; 241 | 242 | //p.x = exploration_goal.point.x; 243 | //p.y = exploration_goal.point.y; 244 | points.points.push_back(p); 245 | 246 | ROS_DEBUG("after(%f, %f)", p.x, p.y); 247 | 248 | pub.publish(points) ; 249 | targetspub.publish(exploration_goal); 250 | points.points.clear(); 251 | 252 | } 253 | 254 | 255 | else if (checking==1){ 256 | V.push_back(x_new); 257 | 258 | p.x=x_new[0]; 259 | p.y=x_new[1]; 260 | p.z=0.0; 261 | line.points.push_back(p); 262 | p.x=x_nearest[0]; 263 | p.y=x_nearest[1]; 264 | p.z=0.0; 265 | line.points.push_back(p); 266 | 267 | } 268 | 269 | 270 | 271 | pub.publish(line); 272 | 273 | 274 | 275 | 276 | ros::spinOnce(); 277 | rate.sleep(); 278 | }return 0;} 279 | -------------------------------------------------------------------------------- /include/mtrand.h: -------------------------------------------------------------------------------- 1 | // mtrand.h 2 | // C++ include file for MT19937, with initialization improved 2002/1/26. 3 | // Coded by Takuji Nishimura and Makoto Matsumoto. 4 | // Ported to C++ by Jasper Bedaux 2003/1/1 (see http://www.bedaux.net/mtrand/). 5 | // The generators returning floating point numbers are based on 6 | // a version by Isaku Wada, 2002/01/09 7 | // 8 | // Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, 9 | // All rights reserved. 10 | // 11 | // Redistribution and use in source and binary forms, with or without 12 | // modification, are permitted provided that the following conditions 13 | // are met: 14 | // 15 | // 1. Redistributions of source code must retain the above copyright 16 | // notice, this list of conditions and the following disclaimer. 17 | // 18 | // 2. Redistributions in binary form must reproduce the above copyright 19 | // notice, this list of conditions and the following disclaimer in the 20 | // documentation and/or other materials provided with the distribution. 21 | // 22 | // 3. The names of its contributors may not be used to endorse or promote 23 | // products derived from this software without specific prior written 24 | // permission. 25 | // 26 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 30 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 31 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 32 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 33 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 34 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 35 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 36 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | // 38 | // Any feedback is very welcome. 39 | // http://www.math.keio.ac.jp/matumoto/emt.html 40 | // email: matumoto@math.keio.ac.jp 41 | // 42 | // Feedback about the C++ port should be sent to Jasper Bedaux, 43 | // see http://www.bedaux.net/mtrand/ for e-mail address and info. 44 | 45 | #ifndef MTRAND_H 46 | #define MTRAND_H 47 | 48 | class MTRand_int32 { // Mersenne Twister random number generator 49 | public: 50 | // default constructor: uses default seed only if this is the first instance 51 | MTRand_int32() { if (!init) seed(5489UL); init = true; } 52 | // constructor with 32 bit int as seed 53 | MTRand_int32(unsigned long s) { seed(s); init = true; } 54 | // constructor with array of size 32 bit ints as seed 55 | MTRand_int32(const unsigned long* array, int size) { seed(array, size); init = true; } 56 | // the two seed functions 57 | void seed(unsigned long); // seed with 32 bit integer 58 | void seed(const unsigned long*, int size); // seed with array 59 | // overload operator() to make this a generator (functor) 60 | unsigned long operator()() { return rand_int32(); } 61 | // 2007-02-11: made the destructor virtual; thanks "double more" for pointing this out 62 | virtual ~MTRand_int32() {} // destructor 63 | protected: // used by derived classes, otherwise not accessible; use the ()-operator 64 | unsigned long rand_int32(); // generate 32 bit random integer 65 | private: 66 | static const int n = 624, m = 397; // compile time constants 67 | // the variables below are static (no duplicates can exist) 68 | static unsigned long state[n]; // state vector array 69 | static int p; // position in state array 70 | static bool init; // true if init function is called 71 | // private functions used to generate the pseudo random numbers 72 | unsigned long twiddle(unsigned long, unsigned long); // used by gen_state() 73 | void gen_state(); // generate new state 74 | // make copy constructor and assignment operator unavailable, they don't make sense 75 | MTRand_int32(const MTRand_int32&); // copy constructor not defined 76 | void operator=(const MTRand_int32&); // assignment operator not defined 77 | }; 78 | 79 | // inline for speed, must therefore reside in header file 80 | inline unsigned long MTRand_int32::twiddle(unsigned long u, unsigned long v) { 81 | return (((u & 0x80000000UL) | (v & 0x7FFFFFFFUL)) >> 1) 82 | ^ ((v & 1UL) * 0x9908B0DFUL); 83 | // 2013-07-22: line above modified for performance according to http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/Ierymenko.html 84 | // thanks Vitaliy FEOKTISTOV for pointing this out 85 | } 86 | 87 | inline unsigned long MTRand_int32::rand_int32() { // generate 32 bit random int 88 | if (p == n) gen_state(); // new state vector needed 89 | // gen_state() is split off to be non-inline, because it is only called once 90 | // in every 624 calls and otherwise irand() would become too big to get inlined 91 | unsigned long x = state[p++]; 92 | x ^= (x >> 11); 93 | x ^= (x << 7) & 0x9D2C5680UL; 94 | x ^= (x << 15) & 0xEFC60000UL; 95 | return x ^ (x >> 18); 96 | } 97 | 98 | // generates double floating point numbers in the half-open interval [0, 1) 99 | class MTRand : public MTRand_int32 { 100 | public: 101 | MTRand() : MTRand_int32() {} 102 | MTRand(unsigned long seed) : MTRand_int32(seed) {} 103 | MTRand(const unsigned long* seed, int size) : MTRand_int32(seed, size) {} 104 | ~MTRand() {} 105 | double operator()() { 106 | return static_cast(rand_int32()) * (1. / 4294967296.); } // divided by 2^32 107 | private: 108 | MTRand(const MTRand&); // copy constructor not defined 109 | void operator=(const MTRand&); // assignment operator not defined 110 | }; 111 | 112 | // generates double floating point numbers in the closed interval [0, 1] 113 | class MTRand_closed : public MTRand_int32 { 114 | public: 115 | MTRand_closed() : MTRand_int32() {} 116 | MTRand_closed(unsigned long seed) : MTRand_int32(seed) {} 117 | MTRand_closed(const unsigned long* seed, int size) : MTRand_int32(seed, size) {} 118 | ~MTRand_closed() {} 119 | double operator()() { 120 | return static_cast(rand_int32()) * (1. / 4294967295.); } // divided by 2^32 - 1 121 | private: 122 | MTRand_closed(const MTRand_closed&); // copy constructor not defined 123 | void operator=(const MTRand_closed&); // assignment operator not defined 124 | }; 125 | 126 | // generates double floating point numbers in the open interval (0, 1) 127 | class MTRand_open : public MTRand_int32 { 128 | public: 129 | MTRand_open() : MTRand_int32() {} 130 | MTRand_open(unsigned long seed) : MTRand_int32(seed) {} 131 | MTRand_open(const unsigned long* seed, int size) : MTRand_int32(seed, size) {} 132 | ~MTRand_open() {} 133 | double operator()() { 134 | return (static_cast(rand_int32()) + .5) * (1. / 4294967296.); } // divided by 2^32 135 | private: 136 | MTRand_open(const MTRand_open&); // copy constructor not defined 137 | void operator=(const MTRand_open&); // assignment operator not defined 138 | }; 139 | 140 | // generates 53 bit resolution doubles in the half-open interval [0, 1) 141 | class MTRand53 : public MTRand_int32 { 142 | public: 143 | MTRand53() : MTRand_int32() {} 144 | MTRand53(unsigned long seed) : MTRand_int32(seed) {} 145 | MTRand53(const unsigned long* seed, int size) : MTRand_int32(seed, size) {} 146 | ~MTRand53() {} 147 | double operator()() { 148 | return (static_cast(rand_int32() >> 5) * 67108864. + 149 | static_cast(rand_int32() >> 6)) * (1. / 9007199254740992.); } 150 | private: 151 | MTRand53(const MTRand53&); // copy constructor not defined 152 | void operator=(const MTRand53&); // assignment operator not defined 153 | }; 154 | 155 | #endif // MTRAND_H 156 | -------------------------------------------------------------------------------- /src/local_rrt_detector.cpp: -------------------------------------------------------------------------------- 1 | #include "ros/ros.h" 2 | #include "std_msgs/String.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "stdint.h" 8 | #include "functions.h" 9 | #include "mtrand.h" 10 | 11 | 12 | #include "nav_msgs/OccupancyGrid.h" 13 | #include "geometry_msgs/PointStamped.h" 14 | #include "std_msgs/Header.h" 15 | #include "nav_msgs/MapMetaData.h" 16 | #include "geometry_msgs/Point.h" 17 | #include "visualization_msgs/Marker.h" 18 | #include 19 | 20 | 21 | 22 | // global variables 23 | nav_msgs::OccupancyGrid mapData; 24 | geometry_msgs::PointStamped clickedpoint; 25 | geometry_msgs::PointStamped exploration_goal; 26 | visualization_msgs::Marker points,line; 27 | float xdim,ydim,resolution,Xstartx,Xstarty,init_map_x,init_map_y; 28 | 29 | rdm r; // for genrating random numbers 30 | 31 | 32 | 33 | //Subscribers callback functions--------------------------------------- 34 | void mapCallBack(const nav_msgs::OccupancyGrid::ConstPtr& msg) 35 | { 36 | mapData=*msg; 37 | } 38 | 39 | 40 | 41 | void rvizCallBack(const geometry_msgs::PointStamped::ConstPtr& msg) 42 | { 43 | 44 | geometry_msgs::Point p; 45 | p.x=msg->point.x; 46 | p.y=msg->point.y; 47 | p.z=msg->point.z; 48 | 49 | points.points.push_back(p); 50 | 51 | } 52 | 53 | 54 | 55 | 56 | int main(int argc, char **argv) 57 | { 58 | 59 | unsigned long init[4] = {0x123, 0x234, 0x345, 0x456}, length = 7; 60 | MTRand_int32 irand(init, length); // 32-bit int generator 61 | // this is an example of initializing by an array 62 | // you may use MTRand(seed) with any 32bit integer 63 | // as a seed for a simpler initialization 64 | MTRand drand; // double in [0, 1) generator, already init 65 | 66 | // generate the same numbers as in the original C test program 67 | ros::init(argc, argv, "local_rrt_frontier_detector"); 68 | ros::NodeHandle nh; 69 | 70 | // fetching all parameters 71 | float eta,init_map_x,init_map_y,range; 72 | std::string map_topic,base_frame_topic; 73 | 74 | std::string ns; 75 | ns=ros::this_node::getName(); 76 | 77 | ros::param::param(ns+"/eta", eta, 0.5); 78 | ros::param::param(ns+"/map_topic", map_topic, "robot_1/map"); 79 | ros::param::param(ns+"/robot_frame", base_frame_topic, "/robot_1/base_footprint"); 80 | //--------------------------------------------------------------- 81 | ros::Subscriber sub= nh.subscribe(map_topic, 100 ,mapCallBack); 82 | ros::Subscriber rviz_sub= nh.subscribe("/clicked_point", 100 ,rvizCallBack); 83 | 84 | ros::Publisher targetspub = nh.advertise("/detected_points", 10); 85 | ros::Publisher pub = nh.advertise(ns+"_shapes", 10); 86 | 87 | ros::Rate rate(100); 88 | 89 | 90 | // wait until map is received, when a map is received, mapData.header.seq will not be < 1 91 | while (mapData.header.seq<1 or mapData.data.size()<1) { ros::spinOnce(); ros::Duration(0.1).sleep();} 92 | 93 | 94 | 95 | //visualizations points and lines.. 96 | points.header.frame_id=mapData.header.frame_id; 97 | line.header.frame_id=mapData.header.frame_id; 98 | points.header.stamp=ros::Time(0); 99 | line.header.stamp=ros::Time(0); 100 | 101 | points.ns=line.ns = "markers"; 102 | points.id = 0; 103 | line.id =1; 104 | 105 | 106 | points.type = points.POINTS; 107 | line.type=line.LINE_LIST; 108 | 109 | //Set the marker action. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 110 | points.action =points.ADD; 111 | line.action = line.ADD; 112 | points.pose.orientation.w =1.0; 113 | line.pose.orientation.w = 1.0; 114 | line.scale.x = 0.03; 115 | line.scale.y= 0.03; 116 | points.scale.x=0.3; 117 | points.scale.y=0.3; 118 | 119 | line.color.r =255.0/255.0; 120 | line.color.g= 0.0/255.0; 121 | line.color.b =0.0/255.0; 122 | points.color.r = 255.0/255.0; 123 | points.color.g = 0.0/255.0; 124 | points.color.b = 0.0/255.0; 125 | points.color.a=0.3; 126 | line.color.a = 1.0; 127 | points.lifetime = ros::Duration(); 128 | line.lifetime = ros::Duration(); 129 | 130 | geometry_msgs::Point p; 131 | 132 | 133 | while(points.points.size()<5) 134 | { 135 | ros::spinOnce(); 136 | 137 | pub.publish(points) ; 138 | } 139 | 140 | 141 | 142 | 143 | std::vector temp1; 144 | temp1.push_back(points.points[0].x); 145 | temp1.push_back(points.points[0].y); 146 | 147 | std::vector temp2; 148 | temp2.push_back(points.points[2].x); 149 | temp2.push_back(points.points[0].y); 150 | 151 | 152 | init_map_x=Norm(temp1,temp2); 153 | temp1.clear(); temp2.clear(); 154 | 155 | temp1.push_back(points.points[0].x); 156 | temp1.push_back(points.points[0].y); 157 | 158 | temp2.push_back(points.points[0].x); 159 | temp2.push_back(points.points[2].y); 160 | 161 | init_map_y=Norm(temp1,temp2); 162 | temp1.clear(); temp2.clear(); 163 | 164 | Xstartx=(points.points[0].x+points.points[2].x)*.5; 165 | Xstarty=(points.points[0].y+points.points[2].y)*.5; 166 | 167 | 168 | 169 | 170 | 171 | geometry_msgs::Point trans; 172 | trans=points.points[4]; 173 | std::vector< std::vector > V; 174 | std::vector xnew; 175 | xnew.push_back( trans.x);xnew.push_back( trans.y); 176 | V.push_back(xnew); 177 | 178 | points.points.clear(); 179 | pub.publish(points) ; 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | std::vector frontiers; 188 | int i=0; 189 | float xr,yr; 190 | std::vector x_rand,x_nearest,x_new; 191 | 192 | tf::TransformListener listener; 193 | // Main loop 194 | while (ros::ok()){ 195 | 196 | 197 | // Sample free 198 | x_rand.clear(); 199 | xr=(drand()*init_map_x)-(init_map_x*0.5)+Xstartx; 200 | yr=(drand()*init_map_y)-(init_map_y*0.5)+Xstarty; 201 | 202 | 203 | x_rand.push_back( xr ); x_rand.push_back( yr ); 204 | 205 | 206 | // Nearest 207 | x_nearest=Nearest(V,x_rand); 208 | 209 | // Steer 210 | 211 | x_new=Steer(x_nearest,x_rand,eta); 212 | 213 | 214 | // ObstacleFree 1:free -1:unkown (frontier region) 0:obstacle 215 | char checking=ObstacleFree(x_nearest,x_new,mapData); 216 | 217 | if (checking==-1){ 218 | 219 | p.x=x_new[0]; 220 | p.y=x_new[1]; 221 | p.z=0.0; 222 | 223 | bool existFrontier = getCompleteFrontier(p, exploration_goal, mapData); 224 | if (!existFrontier) { 225 | continue; 226 | } 227 | exploration_goal.header.stamp=ros::Time(0); 228 | exploration_goal.header.frame_id=mapData.header.frame_id; 229 | 230 | //exploration_goal.point.x=x_new[0]; 231 | //exploration_goal.point.y=x_new[1]; 232 | exploration_goal.point.z=0.0; 233 | 234 | //p.x = exploration_goal.point.x; 235 | //p.y = exploration_goal.point.y; 236 | 237 | points.points.push_back(p); 238 | pub.publish(points); 239 | targetspub.publish(exploration_goal); 240 | points.points.clear(); 241 | V.clear(); 242 | 243 | 244 | tf::StampedTransform transform; 245 | int temp=0; 246 | while (temp==0){ 247 | try{ 248 | temp=1; 249 | listener.lookupTransform(map_topic, base_frame_topic , ros::Time(0), transform); 250 | } 251 | catch (tf::TransformException ex){ 252 | temp=0; 253 | ros::Duration(0.1).sleep(); 254 | }} 255 | 256 | x_new[0]=transform.getOrigin().x(); 257 | x_new[1]=transform.getOrigin().y(); 258 | V.push_back(x_new); 259 | line.points.clear(); 260 | } 261 | 262 | 263 | else if (checking==1){ 264 | V.push_back(x_new); 265 | 266 | p.x=x_new[0]; 267 | p.y=x_new[1]; 268 | p.z=0.0; 269 | line.points.push_back(p); 270 | p.x=x_nearest[0]; 271 | p.y=x_nearest[1]; 272 | p.z=0.0; 273 | line.points.push_back(p); 274 | 275 | } 276 | 277 | 278 | 279 | pub.publish(line); 280 | 281 | 282 | 283 | 284 | ros::spinOnce(); 285 | rate.sleep(); 286 | }return 0;} 287 | -------------------------------------------------------------------------------- /src/local_rrt_detector_real_two_robots.cpp: -------------------------------------------------------------------------------- 1 | #include "ros/ros.h" 2 | #include "std_msgs/String.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "stdint.h" 8 | #include "functions.h" 9 | #include "mtrand.h" 10 | 11 | 12 | #include "nav_msgs/OccupancyGrid.h" 13 | #include "geometry_msgs/PointStamped.h" 14 | #include "std_msgs/Header.h" 15 | #include "nav_msgs/MapMetaData.h" 16 | #include "geometry_msgs/Point.h" 17 | #include "visualization_msgs/Marker.h" 18 | #include 19 | 20 | 21 | 22 | // global variables 23 | nav_msgs::OccupancyGrid mapData; 24 | geometry_msgs::PointStamped clickedpoint; 25 | geometry_msgs::PointStamped exploration_goal; 26 | visualization_msgs::Marker points,line; 27 | float xdim,ydim,resolution,Xstartx,Xstarty,init_map_x,init_map_y; 28 | 29 | rdm r; // for genrating random numbers 30 | 31 | 32 | 33 | //Subscribers callback functions--------------------------------------- 34 | void mapCallBack(const nav_msgs::OccupancyGrid::ConstPtr& msg) 35 | { 36 | mapData=*msg; 37 | } 38 | 39 | 40 | 41 | void rvizCallBack(const geometry_msgs::PointStamped::ConstPtr& msg) 42 | { 43 | 44 | geometry_msgs::Point p; 45 | p.x=msg->point.x; 46 | p.y=msg->point.y; 47 | p.z=msg->point.z; 48 | 49 | points.points.push_back(p); 50 | 51 | } 52 | 53 | 54 | 55 | 56 | int main(int argc, char **argv) 57 | { 58 | 59 | unsigned long init[4] = {0x123, 0x234, 0x345, 0x456}, length = 7; 60 | MTRand_int32 irand(init, length); // 32-bit int generator 61 | // this is an example of initializing by an array 62 | // you may use MTRand(seed) with any 32bit integer 63 | // as a seed for a simpler initialization 64 | MTRand drand; // double in [0, 1) generator, already init 65 | 66 | // generate the same numbers as in the original C test program 67 | ros::init(argc, argv, "local_rrt_frontier_detector"); 68 | ros::NodeHandle nh; 69 | 70 | // fetching all parameters 71 | float eta,init_map_x,init_map_y,range; 72 | std::string map_topic,base_frame_topic; 73 | 74 | std::string ns; 75 | ns=ros::this_node::getName(); 76 | 77 | ros::param::param(ns+"/eta", eta, 0.5); 78 | ros::param::param(ns+"/map_topic", map_topic, "/robot_1/map"); 79 | ros::param::param(ns+"/robot_frame", base_frame_topic, "/robot_1/base_link"); 80 | //--------------------------------------------------------------- 81 | ros::Subscriber sub= nh.subscribe(map_topic, 100 ,mapCallBack); 82 | ros::Subscriber rviz_sub= nh.subscribe("/clicked_point", 100 ,rvizCallBack); 83 | 84 | ros::Publisher targetspub = nh.advertise("/detected_points", 10); 85 | ros::Publisher pub = nh.advertise(ns+"_shapes", 10); 86 | 87 | ros::Rate rate(100); 88 | 89 | 90 | // wait until map is received, when a map is received, mapData.header.seq will not be < 1 91 | while (mapData.header.seq<1 or mapData.data.size()<1) { ros::spinOnce(); ros::Duration(0.1).sleep();} 92 | 93 | 94 | 95 | //visualizations points and lines.. 96 | points.header.frame_id=mapData.header.frame_id; 97 | line.header.frame_id=mapData.header.frame_id; 98 | points.header.stamp=ros::Time(0); 99 | line.header.stamp=ros::Time(0); 100 | 101 | points.ns=line.ns = "markers"; 102 | points.id = 0; 103 | line.id =1; 104 | 105 | 106 | points.type = points.POINTS; 107 | line.type=line.LINE_LIST; 108 | 109 | //Set the marker action. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 110 | points.action =points.ADD; 111 | line.action = line.ADD; 112 | points.pose.orientation.w =1.0; 113 | line.pose.orientation.w = 1.0; 114 | line.scale.x = 0.03; 115 | line.scale.y= 0.03; 116 | points.scale.x=0.3; 117 | points.scale.y=0.3; 118 | 119 | line.color.r =255.0/255.0; 120 | line.color.g= 0.0/255.0; 121 | line.color.b =0.0/255.0; 122 | points.color.r = 255.0/255.0; 123 | points.color.g = 0.0/255.0; 124 | points.color.b = 0.0/255.0; 125 | points.color.a=0.3; 126 | line.color.a = 1.0; 127 | points.lifetime = ros::Duration(); 128 | line.lifetime = ros::Duration(); 129 | 130 | geometry_msgs::Point p; 131 | 132 | 133 | while(points.points.size()<5) 134 | { 135 | ros::spinOnce(); 136 | 137 | pub.publish(points) ; 138 | } 139 | 140 | 141 | 142 | 143 | std::vector temp1; 144 | temp1.push_back(points.points[0].x); 145 | temp1.push_back(points.points[0].y); 146 | 147 | std::vector temp2; 148 | temp2.push_back(points.points[2].x); 149 | temp2.push_back(points.points[0].y); 150 | 151 | 152 | init_map_x=Norm(temp1,temp2); 153 | temp1.clear(); temp2.clear(); 154 | 155 | temp1.push_back(points.points[0].x); 156 | temp1.push_back(points.points[0].y); 157 | 158 | temp2.push_back(points.points[0].x); 159 | temp2.push_back(points.points[2].y); 160 | 161 | init_map_y=Norm(temp1,temp2); 162 | temp1.clear(); temp2.clear(); 163 | 164 | Xstartx=(points.points[0].x+points.points[2].x)*.5; 165 | Xstarty=(points.points[0].y+points.points[2].y)*.5; 166 | 167 | 168 | 169 | 170 | 171 | geometry_msgs::Point trans; 172 | trans=points.points[4]; 173 | std::vector< std::vector > V; 174 | std::vector xnew; 175 | xnew.push_back( trans.x);xnew.push_back( trans.y); 176 | V.push_back(xnew); 177 | 178 | points.points.clear(); 179 | pub.publish(points) ; 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | std::vector frontiers; 188 | int i=0; 189 | float xr,yr; 190 | std::vector x_rand,x_nearest,x_new; 191 | 192 | tf::TransformListener listener; 193 | // Main loop 194 | while (ros::ok()){ 195 | 196 | 197 | // Sample free 198 | x_rand.clear(); 199 | xr=(drand()*init_map_x)-(init_map_x*0.5)+Xstartx; 200 | yr=(drand()*init_map_y)-(init_map_y*0.5)+Xstarty; 201 | 202 | 203 | x_rand.push_back( xr ); x_rand.push_back( yr ); 204 | 205 | 206 | // Nearest 207 | x_nearest=Nearest(V,x_rand); 208 | 209 | // Steer 210 | 211 | x_new=Steer(x_nearest,x_rand,eta); 212 | 213 | 214 | // ObstacleFree 1:free -1:unkown (frontier region) 0:obstacle 215 | char checking=ObstacleFree(x_nearest,x_new,mapData); 216 | 217 | if (checking==-1){ 218 | 219 | p.x=x_new[0]; 220 | p.y=x_new[1]; 221 | p.z=0.0; 222 | 223 | bool existFrontier = getCompleteFrontier(p, exploration_goal, mapData); 224 | if (!existFrontier) { 225 | continue; 226 | } 227 | exploration_goal.header.stamp=ros::Time(0); 228 | exploration_goal.header.frame_id=mapData.header.frame_id; 229 | 230 | //exploration_goal.point.x=x_new[0]; 231 | //exploration_goal.point.y=x_new[1]; 232 | exploration_goal.point.z=0.0; 233 | 234 | //p.x = exploration_goal.point.x; 235 | //p.y = exploration_goal.point.y; 236 | 237 | points.points.push_back(p); 238 | pub.publish(points); 239 | targetspub.publish(exploration_goal); 240 | points.points.clear(); 241 | V.clear(); 242 | 243 | 244 | tf::StampedTransform transform; 245 | int temp=0; 246 | while (temp==0){ 247 | try{ 248 | temp=1; 249 | listener.lookupTransform(map_topic, base_frame_topic , ros::Time(0), transform); 250 | } 251 | catch (tf::TransformException ex){ 252 | temp=0; 253 | ros::Duration(0.1).sleep(); 254 | }} 255 | 256 | x_new[0]=transform.getOrigin().x(); 257 | x_new[1]=transform.getOrigin().y(); 258 | V.push_back(x_new); 259 | line.points.clear(); 260 | } 261 | 262 | 263 | else if (checking==1){ 264 | V.push_back(x_new); 265 | 266 | p.x=x_new[0]; 267 | p.y=x_new[1]; 268 | p.z=0.0; 269 | line.points.push_back(p); 270 | p.x=x_nearest[0]; 271 | p.y=x_nearest[1]; 272 | p.z=0.0; 273 | line.points.push_back(p); 274 | 275 | } 276 | 277 | 278 | 279 | pub.publish(line); 280 | 281 | 282 | 283 | 284 | ros::spinOnce(); 285 | rate.sleep(); 286 | }return 0;} 287 | -------------------------------------------------------------------------------- /scripts/filter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | #--------Include modules--------------- 4 | from copy import copy 5 | import rospy 6 | from visualization_msgs.msg import Marker 7 | from geometry_msgs.msg import Point 8 | from nav_msgs.msg import OccupancyGrid 9 | from geometry_msgs.msg import PointStamped 10 | import tf 11 | from numpy import array,vstack,delete 12 | from functions import gridValue,informationGain 13 | from sklearn.cluster import MeanShift 14 | from multi_rrt_exploration.msg import PointArray 15 | 16 | # Subscribers' callbacks------------------------------ 17 | mapData=OccupancyGrid() 18 | frontiers=[] 19 | globalmaps=[] 20 | def callBack(data,args): 21 | global frontiers,min_distance 22 | transformedPoint=args[0].transformPoint(args[1],data) 23 | x=[array([transformedPoint.point.x,transformedPoint.point.y])] 24 | if len(frontiers)>0: 25 | frontiers=vstack((frontiers,x)) 26 | else: 27 | frontiers=x 28 | 29 | 30 | def mapCallBack(data): 31 | global mapData 32 | mapData=data 33 | 34 | def globalMap(data): 35 | global global1,globalmaps,litraIndx,namespace_init_count,n_robots 36 | global1=data 37 | if n_robots>1: 38 | indx=int(data._connection_header['topic'][litraIndx])-namespace_init_count 39 | elif n_robots==1: 40 | indx=0 41 | globalmaps[indx]=data 42 | 43 | # Node---------------------------------------------- 44 | def node(): 45 | global frontiers,mapData,global1,global2,global3,globalmaps,litraIndx,n_robots,namespace_init_count 46 | rospy.init_node('filter', anonymous=False) 47 | 48 | # fetching all parameters 49 | map_topic= rospy.get_param('~map_topic','/map_merge/map') 50 | threshold= rospy.get_param('~costmap_clearing_threshold',70) 51 | info_radius= rospy.get_param('~info_radius',1.0) #this can be smaller than the laser scanner range, >> smaller >>less computation time>> too small is not good, info gain won't be accurate 52 | goals_topic= rospy.get_param('~goals_topic','/detected_points') 53 | n_robots = rospy.get_param('~n_robots',1) 54 | namespace = rospy.get_param('~namespace','') 55 | namespace_init_count = rospy.get_param('~namespace_init_count',1) 56 | rateHz = rospy.get_param('~rate',2) 57 | litraIndx=len(namespace) 58 | rate = rospy.Rate(rateHz) 59 | #------------------------------------------- 60 | rospy.Subscriber(map_topic, OccupancyGrid, mapCallBack) 61 | 62 | 63 | #--------------------------------------------------------------------------------------------------------------- 64 | 65 | 66 | for i in range(0,n_robots): 67 | globalmaps.append(OccupancyGrid()) 68 | 69 | if len(namespace) > 0: 70 | for i in range(0,n_robots): 71 | rospy.Subscriber(namespace+str(i+namespace_init_count)+'/move_base/global_costmap/costmap', OccupancyGrid, globalMap) 72 | elif len(namespace)==0: 73 | rospy.Subscriber('/move_base/global_costmap/costmap', OccupancyGrid, globalMap) 74 | #wait if map is not received yet 75 | while (len(mapData.data)<1): 76 | pass 77 | #wait if any of robots' global costmap map is not received yet 78 | for i in range(0,n_robots): 79 | while (len(globalmaps[i].data)<1): 80 | pass 81 | 82 | global_frame="/"+mapData.header.frame_id 83 | 84 | 85 | tfLisn=tf.TransformListener() 86 | if len(namespace) > 0: 87 | for i in range(0,n_robots): 88 | tfLisn.waitForTransform(global_frame[1:], namespace+str(i+namespace_init_count)+'/base_footprint', rospy.Time(0),rospy.Duration(10.0)) 89 | elif len(namespace)==0: 90 | tfLisn.waitForTransform(global_frame[1:], '/base_footprint', rospy.Time(0),rospy.Duration(10.0)) 91 | 92 | rospy.Subscriber(goals_topic, PointStamped, callback=callBack,callback_args=[tfLisn,global_frame[1:]]) 93 | pub = rospy.Publisher('frontiers', Marker, queue_size=10) 94 | pub2 = rospy.Publisher('centroids', Marker, queue_size=10) 95 | filterpub = rospy.Publisher('filtered_points', PointArray, queue_size=10) 96 | 97 | rospy.loginfo("the map and global costmaps are received") 98 | 99 | 100 | # wait if no frontier is received yet 101 | while len(frontiers)<1: 102 | pass 103 | 104 | 105 | points=Marker() 106 | points_clust=Marker() 107 | #Set the frame ID and timestamp. See the TF tutorials for information on these. 108 | points.header.frame_id= mapData.header.frame_id 109 | points.header.stamp= rospy.Time.now() 110 | 111 | points.ns= "markers2" 112 | points.id = 0 113 | 114 | points.type = Marker.POINTS 115 | 116 | #Set the marker action for latched frontiers. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 117 | points.action = Marker.ADD; 118 | 119 | points.pose.orientation.w = 1.0 120 | 121 | points.scale.x=0.2 122 | points.scale.y=0.2 123 | 124 | points.color.r = 255.0/255.0 125 | points.color.g = 255.0/255.0 126 | points.color.b = 0.0/255.0 127 | 128 | points.color.a=1; 129 | points.lifetime = rospy.Duration(); 130 | 131 | p=Point() 132 | 133 | p.z = 0; 134 | 135 | pp=[] 136 | pl=[] 137 | 138 | points_clust.header.frame_id= mapData.header.frame_id 139 | points_clust.header.stamp= rospy.Time.now() 140 | 141 | points_clust.ns= "markers3" 142 | points_clust.id = 4 143 | 144 | points_clust.type = Marker.POINTS 145 | 146 | #Set the marker action for centroids. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 147 | points_clust.action = Marker.ADD; 148 | 149 | points_clust.pose.orientation.w = 1.0; 150 | 151 | points_clust.scale.x=0.2; 152 | points_clust.scale.y=0.2; 153 | points_clust.color.r = 0.0/255.0 154 | points_clust.color.g = 255.0/255.0 155 | points_clust.color.b = 0.0/255.0 156 | 157 | points_clust.color.a=1; 158 | points_clust.lifetime = rospy.Duration(); 159 | 160 | 161 | temppoint=PointStamped() 162 | temppoint.header.frame_id= mapData.header.frame_id 163 | temppoint.header.stamp=rospy.Time(0) 164 | temppoint.point.z=0.0 165 | 166 | arraypoints=PointArray() 167 | tempPoint=Point() 168 | tempPoint.z=0.0 169 | #------------------------------------------------------------------------- 170 | #--------------------- Main Loop ------------------------------- 171 | #------------------------------------------------------------------------- 172 | while not rospy.is_shutdown(): 173 | #------------------------------------------------------------------------- 174 | #Clustering frontier points 175 | centroids=[] 176 | front=copy(frontiers) 177 | if len(front)>1: 178 | ms = MeanShift(bandwidth=0.3) 179 | ms.fit(front) 180 | centroids= ms.cluster_centers_ #centroids array is the centers of each cluster 181 | 182 | #if there is only one frontier no need for clustering, i.e. centroids=frontiers 183 | if len(front)==1: 184 | centroids=front 185 | frontiers=copy(centroids) 186 | #------------------------------------------------------------------------- 187 | #clearing old frontiers 188 | 189 | z=0 190 | while zthreshold) or cond 201 | if (cond or (informationGain(mapData,[centroids[z][0],centroids[z][1]],info_radius*0.5))<0.2): 202 | centroids=delete(centroids, (z), axis=0) 203 | z=z-1 204 | z+=1 205 | #------------------------------------------------------------------------- 206 | #publishing 207 | arraypoints.points=[] 208 | for i in centroids: 209 | tempPoint.x=i[0] 210 | tempPoint.y=i[1] 211 | arraypoints.points.append(copy(tempPoint)) 212 | filterpub.publish(arraypoints) 213 | pp=[] 214 | for q in range(0,len(frontiers)): 215 | p.x=frontiers[q][0] 216 | p.y=frontiers[q][1] 217 | pp.append(copy(p)) 218 | points.points=pp 219 | 220 | pp=[] 221 | for q in range(0,len(centroids)): 222 | p.x=centroids[q][0] 223 | p.y=centroids[q][1] 224 | pp.append(copy(p)) 225 | points_clust.points=pp 226 | pub.publish(points) 227 | pub2.publish(points_clust) 228 | rate.sleep() 229 | #------------------------------------------------------------------------- 230 | 231 | if __name__ == '__main__': 232 | try: 233 | node() 234 | except rospy.ROSInterruptException: 235 | pass 236 | 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /scripts/filter_real_two_robots.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | #--------Include modules--------------- 4 | from copy import copy 5 | import rospy 6 | from visualization_msgs.msg import Marker 7 | from geometry_msgs.msg import Point 8 | from nav_msgs.msg import OccupancyGrid 9 | from geometry_msgs.msg import PointStamped 10 | import tf 11 | from numpy import array,vstack,delete 12 | from functions_real_two_robots import gridValue,informationGain 13 | from sklearn.cluster import MeanShift 14 | from rrt_exploration.msg import PointArray 15 | 16 | # Subscribers' callbacks------------------------------ 17 | mapData=OccupancyGrid() 18 | frontiers=[] 19 | globalmaps=[] 20 | def callBack(data,args): 21 | global frontiers,min_distance 22 | transformedPoint=args[0].transformPoint(args[1],data) 23 | x=[array([transformedPoint.point.x,transformedPoint.point.y])] 24 | if len(frontiers)>0: 25 | frontiers=vstack((frontiers,x)) 26 | else: 27 | frontiers=x 28 | 29 | 30 | def mapCallBack(data): 31 | global mapData 32 | mapData=data 33 | 34 | def globalMap(data): 35 | global global1,globalmaps,litraIndx,namespace_init_count,n_robots 36 | global1=data 37 | if n_robots>1: 38 | indx=int(data._connection_header['topic'][litraIndx])-namespace_init_count 39 | elif n_robots==1: 40 | indx=0 41 | globalmaps[indx]=data 42 | 43 | # Node---------------------------------------------- 44 | def node(): 45 | global frontiers,mapData,global1,global2,global3,globalmaps,litraIndx,n_robots,namespace_init_count 46 | rospy.init_node('filter', anonymous=False) 47 | 48 | # fetching all parameters 49 | map_topic= rospy.get_param('~map_topic','/map') 50 | threshold= rospy.get_param('~costmap_clearing_threshold',70) 51 | info_radius= rospy.get_param('~info_radius',1.0) #this can be smaller than the laser scanner range, >> smaller >>less computation time>> too small is not good, info gain won't be accurate 52 | goals_topic= rospy.get_param('~goals_topic','/detected_points') 53 | n_robots = rospy.get_param('~n_robots',1) 54 | namespace = rospy.get_param('~namespace','') 55 | namespace_init_count = rospy.get_param('~namespace_init_count',1) 56 | rateHz = rospy.get_param('~rate',2) 57 | litraIndx=len(namespace) 58 | rate = rospy.Rate(rateHz) 59 | #------------------------------------------- 60 | rospy.Subscriber(map_topic, OccupancyGrid, mapCallBack) 61 | 62 | 63 | #--------------------------------------------------------------------------------------------------------------- 64 | 65 | 66 | for i in range(0,n_robots): 67 | globalmaps.append(OccupancyGrid()) 68 | 69 | if len(namespace) > 0: 70 | for i in range(0,n_robots): 71 | rospy.Subscriber(namespace+str(i+namespace_init_count)+'/move_base_node/global_costmap/costmap', OccupancyGrid, globalMap) 72 | elif len(namespace)==0: 73 | rospy.Subscriber('/move_base_node/global_costmap/costmap', OccupancyGrid, globalMap) 74 | #wait if map is not received yet 75 | while (len(mapData.data)<1): 76 | pass 77 | #wait if any of robots' global costmap map is not received yet 78 | for i in range(0,n_robots): 79 | while (len(globalmaps[i].data)<1): 80 | pass 81 | 82 | global_frame="/"+mapData.header.frame_id 83 | 84 | 85 | tfLisn=tf.TransformListener() 86 | if len(namespace) > 0: 87 | for i in range(0,n_robots): 88 | tfLisn.waitForTransform(global_frame[1:], namespace+str(i+namespace_init_count)+'/base_footprint', rospy.Time(0),rospy.Duration(10.0)) 89 | elif len(namespace)==0: 90 | tfLisn.waitForTransform(global_frame[1:], '/base_footprint', rospy.Time(0),rospy.Duration(10.0)) 91 | 92 | rospy.Subscriber(goals_topic, PointStamped, callback=callBack,callback_args=[tfLisn,global_frame[1:]]) 93 | pub = rospy.Publisher('frontiers', Marker, queue_size=10) 94 | pub2 = rospy.Publisher('centroids', Marker, queue_size=10) 95 | filterpub = rospy.Publisher('filtered_points', PointArray, queue_size=10) 96 | 97 | rospy.loginfo("the map and global costmaps are received") 98 | 99 | 100 | # wait if no frontier is received yet 101 | while len(frontiers)<1: 102 | pass 103 | 104 | 105 | points=Marker() 106 | points_clust=Marker() 107 | #Set the frame ID and timestamp. See the TF tutorials for information on these. 108 | points.header.frame_id= mapData.header.frame_id 109 | points.header.stamp= rospy.Time.now() 110 | 111 | points.ns= "markers2" 112 | points.id = 0 113 | 114 | points.type = Marker.POINTS 115 | 116 | #Set the marker action for latched frontiers. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 117 | points.action = Marker.ADD; 118 | 119 | points.pose.orientation.w = 1.0 120 | 121 | points.scale.x=0.2 122 | points.scale.y=0.2 123 | 124 | points.color.r = 255.0/255.0 125 | points.color.g = 255.0/255.0 126 | points.color.b = 0.0/255.0 127 | 128 | points.color.a=1; 129 | points.lifetime = rospy.Duration(); 130 | 131 | p=Point() 132 | 133 | p.z = 0; 134 | 135 | pp=[] 136 | pl=[] 137 | 138 | points_clust.header.frame_id= mapData.header.frame_id 139 | points_clust.header.stamp= rospy.Time.now() 140 | 141 | points_clust.ns= "markers3" 142 | points_clust.id = 4 143 | 144 | points_clust.type = Marker.POINTS 145 | 146 | #Set the marker action for centroids. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 147 | points_clust.action = Marker.ADD; 148 | 149 | points_clust.pose.orientation.w = 1.0; 150 | 151 | points_clust.scale.x=0.2; 152 | points_clust.scale.y=0.2; 153 | points_clust.color.r = 0.0/255.0 154 | points_clust.color.g = 255.0/255.0 155 | points_clust.color.b = 0.0/255.0 156 | 157 | points_clust.color.a=1; 158 | points_clust.lifetime = rospy.Duration(); 159 | 160 | 161 | temppoint=PointStamped() 162 | temppoint.header.frame_id= mapData.header.frame_id 163 | temppoint.header.stamp=rospy.Time(0) 164 | temppoint.point.z=0.0 165 | 166 | arraypoints=PointArray() 167 | tempPoint=Point() 168 | tempPoint.z=0.0 169 | #------------------------------------------------------------------------- 170 | #--------------------- Main Loop ------------------------------- 171 | #------------------------------------------------------------------------- 172 | while not rospy.is_shutdown(): 173 | #------------------------------------------------------------------------- 174 | #Clustering frontier points 175 | centroids=[] 176 | front=copy(frontiers) 177 | if len(front)>1: 178 | ms = MeanShift(bandwidth=0.3) 179 | ms.fit(front) 180 | centroids= ms.cluster_centers_ #centroids array is the centers of each cluster 181 | 182 | #if there is only one frontier no need for clustering, i.e. centroids=frontiers 183 | if len(front)==1: 184 | centroids=front 185 | frontiers=copy(centroids) 186 | #------------------------------------------------------------------------- 187 | #clearing old frontiers 188 | 189 | z=0 190 | while zthreshold) or cond 201 | if (cond or (informationGain(mapData,[centroids[z][0],centroids[z][1]],info_radius*0.5))<0.2): 202 | centroids=delete(centroids, (z), axis=0) 203 | z=z-1 204 | z+=1 205 | #------------------------------------------------------------------------- 206 | #publishing 207 | arraypoints.points=[] 208 | for i in centroids: 209 | tempPoint.x=i[0] 210 | tempPoint.y=i[1] 211 | arraypoints.points.append(copy(tempPoint)) 212 | filterpub.publish(arraypoints) 213 | pp=[] 214 | for q in range(0,len(frontiers)): 215 | p.x=frontiers[q][0] 216 | p.y=frontiers[q][1] 217 | pp.append(copy(p)) 218 | points.points=pp 219 | 220 | pp=[] 221 | for q in range(0,len(centroids)): 222 | p.x=centroids[q][0] 223 | p.y=centroids[q][1] 224 | pp.append(copy(p)) 225 | points_clust.points=pp 226 | pub.publish(points) 227 | pub2.publish(points_clust) 228 | rate.sleep() 229 | #------------------------------------------------------------------------- 230 | 231 | if __name__ == '__main__': 232 | try: 233 | node() 234 | except rospy.ROSInterruptException: 235 | pass 236 | 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /scripts/filter_backup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | #--------Include modules--------------- 4 | from copy import copy 5 | import rospy 6 | from visualization_msgs.msg import Marker 7 | from geometry_msgs.msg import Point 8 | from nav_msgs.msg import OccupancyGrid 9 | from geometry_msgs.msg import PointStamped 10 | import tf 11 | from numpy import array,vstack,delete 12 | from functions import gridValue,informationGain2,isOldFrontier 13 | from sklearn.cluster import MeanShift 14 | from rrt_exploration.msg import PointArray 15 | 16 | # Subscribers' callbacks------------------------------ 17 | mapData=OccupancyGrid() 18 | frontiers=set() 19 | globalmaps=[] 20 | def callBack(data,args): 21 | global frontiers,min_distance 22 | 23 | # transform from local map point to global map 24 | transformedPoint=args[0].transformPoint(args[1],data) 25 | x=(transformedPoint.point.x,transformedPoint.point.y) 26 | frontiers.add(copy(x)) 27 | #if len(frontiers)>0: 28 | # frontiers=vstack((frontiers,x)) 29 | #else: 30 | # frontiers=x 31 | 32 | 33 | def mapCallBack(data): 34 | global mapData 35 | mapData=data 36 | 37 | def globalMap(data): 38 | global global1,globalmaps,litraIndx,namespace_init_count,n_robots 39 | global1=data 40 | if n_robots>1: 41 | indx=int(data._connection_header['topic'][litraIndx])-namespace_init_count 42 | elif n_robots==1: 43 | indx=0 44 | globalmaps[indx]=data 45 | 46 | # Node---------------------------------------------- 47 | def node(): 48 | global frontiers,mapData,global1,global2,global3,globalmaps,litraIndx,n_robots,namespace_init_count 49 | rospy.init_node('filter', anonymous=False) 50 | 51 | # fetching all parameters 52 | map_topic= rospy.get_param('~map_topic','/map') 53 | threshold= rospy.get_param('~costmap_clearing_threshold',70) 54 | info_radius= rospy.get_param('~info_radius',1.0) #this can be smaller than the laser scanner range, >> smaller >>less computation time>> too small is not good, info gain won't be accurate 55 | goals_topic= rospy.get_param('~goals_topic','/detected_points') 56 | n_robots = rospy.get_param('~n_robots',1) 57 | namespace = rospy.get_param('~namespace','') 58 | namespace_init_count = rospy.get_param('namespace_init_count',1) 59 | rateHz = rospy.get_param('~rate',1) 60 | litraIndx=len(namespace) 61 | rate = rospy.Rate(rateHz) 62 | #--------------------------------------- 63 | rospy.Subscriber(map_topic, OccupancyGrid, mapCallBack) 64 | 65 | 66 | #--------------------------------------------------------------------------------------------------------------- 67 | for i in range(0,n_robots): 68 | globalmaps.append(OccupancyGrid()) 69 | 70 | if len(namespace) > 0: 71 | for i in range(0,n_robots): 72 | rospy.Subscriber(namespace+str(i+namespace_init_count)+'/move_base/global_costmap/costmap', OccupancyGrid, globalMap) 73 | elif len(namespace)==0: 74 | rospy.Subscriber('/move_base/global_costmap/costmap', OccupancyGrid, globalMap) 75 | #wait if map is not received yet 76 | while (len(mapData.data)<1): 77 | pass 78 | #wait if any of robots' global costmap map is not received yet 79 | for i in range(0,n_robots): 80 | while (len(globalmaps[i].data)<1): 81 | pass 82 | 83 | global_frame="/"+mapData.header.frame_id 84 | 85 | 86 | tfLisn=tf.TransformListener() 87 | if len(namespace) > 0: 88 | for i in range(0,n_robots): 89 | tfLisn.waitForTransform(global_frame[1:], namespace+str(i+namespace_init_count)+'/base_link', rospy.Time(0),rospy.Duration(10.0)) 90 | elif len(namespace)==0: 91 | tfLisn.waitForTransform(global_frame[1:], '/base_link', rospy.Time(0),rospy.Duration(10.0)) 92 | 93 | rospy.Subscriber(goals_topic, PointStamped, callback=callBack,callback_args=[tfLisn,global_frame[1:]]) 94 | #pub = rospy.Publisher('frontiers', Marker, queue_size=10) 95 | pub2 = rospy.Publisher('frontiers', Marker, queue_size=10) 96 | filterpub = rospy.Publisher('filtered_points', PointArray, queue_size=10) 97 | 98 | rospy.loginfo("the map and global costmaps are received") 99 | 100 | 101 | # wait if no frontier is received yet 102 | while len(frontiers)<1: 103 | pass 104 | 105 | 106 | points=Marker() 107 | points_clust=Marker() 108 | 109 | #Set frame ID and timestamp. See the TF tutorials for information on these. 110 | points.header.frame_id= mapData.header.frame_id 111 | points.header.stamp= rospy.Time.now() 112 | 113 | points.ns= "markers2" 114 | points.id = 0 115 | 116 | points.type = Marker.POINTS 117 | 118 | #Set marker action for latched frontiers. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 119 | points.action = Marker.ADD; 120 | 121 | points.pose.orientation.w = 1.0 122 | 123 | points.scale.x=0.2 124 | points.scale.y=0.2 125 | 126 | points.color.r = 255.0/255.0 127 | points.color.g = 255.0/255.0 128 | points.color.b = 0.0/255.0 129 | 130 | points.color.a=1; 131 | points.lifetime = rospy.Duration(); 132 | 133 | p=Point() 134 | 135 | p.z = 0; 136 | 137 | pp=[] 138 | pl=[] 139 | 140 | points_clust.header.frame_id= mapData.header.frame_id 141 | points_clust.header.stamp= rospy.Time.now() 142 | 143 | points_clust.ns= "markers3" 144 | points_clust.id = 4 145 | 146 | points_clust.type = Marker.POINTS 147 | 148 | #Set marker action for centroids. Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL) 149 | points_clust.action = Marker.ADD; 150 | 151 | points_clust.pose.orientation.w = 1.0; 152 | 153 | points_clust.scale.x=0.2; 154 | points_clust.scale.y=0.2; 155 | points_clust.color.r = 0.0/255.0 156 | points_clust.color.g = 255.0/255.0 157 | points_clust.color.b = 0.0/255.0 158 | 159 | points_clust.color.a=1; 160 | points_clust.lifetime = rospy.Duration(); 161 | 162 | 163 | temppoint=PointStamped() 164 | temppoint.header.frame_id= mapData.header.frame_id 165 | temppoint.header.stamp=rospy.Time(0) 166 | temppoint.point.z=0.0 167 | 168 | arraypoints=PointArray() 169 | tempPoint=Point() 170 | tempPoint.z=0.0 171 | #--------------------------------------------------------------------- 172 | #----------------- Main Loop ------------------------------- 173 | #--------------------------------------------------------------------- 174 | while not rospy.is_shutdown(): 175 | #------------------------------------------------------------------------- 176 | #Clustering frontier points 177 | ''' 178 | #centroids=[] 179 | #front=copy(frontiers) 180 | #if len(front)>1: 181 | # ms = MeanShift(bandwidth=0.3) 182 | # ms.fit(front) 183 | # centroids= ms.cluster_centers_ #centroids array is the centers of each cluster 184 | 185 | #if there is only one frontier no need for clustering, i.e. centroids=frontiers 186 | #if len(front)==1: 187 | # centroids=front 188 | #frontiers=copy(centroids) 189 | ''' 190 | #--------------------------------------------------------------------- 191 | #clearing old frontiers 192 | 193 | for frontier in list(frontiers): 194 | cond = False 195 | temppoint.point.x=frontier[0] 196 | temppoint.point.y=frontier[1] 197 | 198 | for i in range(0,n_robots): 199 | 200 | transformedPoint=tfLisn.transformPoint(globalmaps[i].header.frame_id,temppoint) 201 | x=(transformedPoint.point.x,transformedPoint.point.y) 202 | cond=(gridValue(globalmaps[i],x)>threshold) or cond 203 | if (informationGain(mapData, [frontier[0], frontier[1]],info_radius*0.5) < 0.2): 204 | if (frontier[0], frontier[1]) in frontiers: 205 | frontiers.remove((frontier[0], frontier[1])) 206 | 207 | ''' 208 | if (cond or (informationGain(mapData,[frontier[0],frontier[1]],info_radius*0.5))<0.2): 209 | if (frontier[0], frontier[1]) in frontiers: 210 | frontiers.remove((frontier[0], frontier[1])) 211 | ''' 212 | 213 | #--------------------------------------------------------------------- 214 | #publishing 215 | arraypoints.points=[] 216 | 217 | for i in list(frontiers): 218 | tempPoint.x=i[0] 219 | tempPoint.y=i[1] 220 | arraypoints.points.append(copy(tempPoint)) 221 | filterpub.publish(arraypoints) 222 | 223 | ''' 224 | pp=[] 225 | for q in range(0,len(frontiers)): 226 | p.x=frontiers[q][0] 227 | p.y=frontiers[q][1] 228 | pp.append(copy(p)) 229 | points.points=pp 230 | ''' 231 | ''' 232 | for q in list(frontiers): 233 | p.x= q[0] 234 | p.y= q[1] 235 | pp.append(copy(p)) 236 | ''' 237 | points_clust.points= copy(arraypoints.points) 238 | 239 | # pub.publish(points) 240 | pub2.publish(points_clust) 241 | rate.sleep() 242 | #------------------------------------------------------------------------- 243 | 244 | if __name__ == '__main__': 245 | try: 246 | node() 247 | except rospy.ROSInterruptException: 248 | pass 249 | -------------------------------------------------------------------------------- /scripts/newassigner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | #--------Include modules--------------- 4 | from copy import copy 5 | import rospy 6 | from visualization_msgs.msg import Marker 7 | from geometry_msgs.msg import Point 8 | from nav_msgs.msg import OccupancyGrid 9 | import tf 10 | from rrt_exploration.msg import PointArray 11 | from time import time 12 | from numpy import array, delete 13 | from numpy import linalg as LA 14 | from numpy import all as All 15 | from numpy import inf 16 | from functions import robot,informationGain,discount 17 | from numpy.linalg import norm 18 | import time 19 | 20 | # Subscribers' callbacks------------------------------ 21 | mapData=OccupancyGrid() 22 | frontiers=[] 23 | global1=OccupancyGrid() 24 | global2=OccupancyGrid() 25 | global3=OccupancyGrid() 26 | globalmaps=[] 27 | def callBack(data): 28 | global frontiers 29 | frontiers=[] 30 | for point in data.points: 31 | frontiers.append(array([point.x,point.y])) 32 | 33 | def mapCallBack(data): 34 | global mapData 35 | mapData=data 36 | # Node---------------------------------------------- 37 | 38 | def node(): 39 | 40 | stop = 0 41 | 42 | global frontiers,mapData,global1,global2,global3,globalmaps 43 | rospy.init_node('assigner', anonymous=False) 44 | 45 | # fetching all parameters 46 | map_topic= rospy.get_param('~map_topic','/map_merge/map') 47 | info_radius= rospy.get_param('~info_radius',1.0) #this can be smaller than the laser scanner range, >> smaller >>less computation time>> too small is not good, info gain won't be accurate 48 | info_multiplier=rospy.get_param('~info_multiplier',3.0) 49 | hysteresis_radius=rospy.get_param('~hysteresis_radius',1.0) #at least as much as the laser scanner range 50 | hysteresis_gain=rospy.get_param('~hysteresis_gain',2.0) #bigger than 1 (biase robot to continue exploring current region 51 | frontiers_topic= rospy.get_param('~frontiers_topic','/filtered_points') 52 | n_robots = rospy.get_param('~n_robots',1) 53 | namespace = rospy.get_param('~namespace','') 54 | namespace_init_count = rospy.get_param('namespace_init_count',1) 55 | delay_after_assignement=rospy.get_param('~delay_after_assignement',0.5) 56 | rateHz = rospy.get_param('~rate',2) 57 | 58 | rate = rospy.Rate(rateHz) 59 | #------------------------------------------- 60 | rospy.Subscriber(map_topic, OccupancyGrid, mapCallBack) 61 | rospy.Subscriber(frontiers_topic, PointArray, callBack) 62 | #--------------------------------------------------------------------------------------------------------------- 63 | 64 | # wait if no frontier is received yet 65 | while len(frontiers)<1: 66 | pass 67 | centroids=copy(frontiers) 68 | #wait if map is not received yet 69 | while (len(mapData.data)<1): 70 | pass 71 | 72 | robots=[] 73 | if len(namespace)>0: 74 | for i in range(0,n_robots): 75 | robots.append(robot(namespace+str(i+namespace_init_count))) 76 | elif len(namespace)==0: 77 | robots.append(robot(namespace)) 78 | for i in range(0,n_robots): 79 | robots[i].sendGoal(robots[i].getPosition()) 80 | 81 | 82 | start = time.time() 83 | #------------------------------------------------------------------------- 84 | #-------------------- Initial Rotation -------------------------- 85 | 86 | if n_robots == 1: 87 | for i in range(0,n_robots): 88 | robots[i].sendGoal(robots[i].getPosition(), rotation=True) 89 | else: 90 | for i in range(0,n_robots): 91 | robots[i].sendGoal(robots[i].getPosition()) 92 | 93 | rospy.sleep(3); 94 | rospy.loginfo("initial finished.") 95 | 96 | 97 | #------------------------------------------------------------------------- 98 | #--------------------- Main Loop ------------------------------- 99 | #------------------------------------------------------------------------- 100 | while not rospy.is_shutdown(): 101 | centroids=copy(frontiers) 102 | #------------------------------------------------------------------------- 103 | #Get information gain for each assigned point 104 | for ip in range(n_robots): 105 | if (robots[ip].getState() == 1): 106 | infoGain = informationGain(mapData,[robots[ip].assigned_point[0],robots[ip].assigned_point[1]], 0.3) 107 | if infoGain < 0.1: 108 | rospy.logwarn("area has been finished, cancel robot_" +str(ip) + "'s goal.") 109 | robots[ip].cancelGoal() 110 | rospy.sleep(delay_after_assignement) 111 | 112 | #get number of available/busy robots 113 | na=[] #available robots 114 | nb=[] #busy robots 115 | for i in range(0,n_robots): 116 | if (robots[i].getState()==1): 117 | nb.append(i) 118 | else: 119 | na.append(i) 120 | if len(na) > 0: 121 | rospy.loginfo("available robots: "+str(na)) 122 | #------------------------------------------------------------------------- 123 | 124 | ''' 125 | 126 | #get dicount and update informationGain 127 | for i in nb+na: 128 | infoGain=discount(mapData,robots[i].assigned_point,centroids,infoGain,info_radius) 129 | #------------------------------------------------------------------------- 130 | ''' 131 | 132 | ip = 0 133 | while ip < len(centroids): 134 | for ir in nb: 135 | if (norm(robots[ir].assigned_point-centroids[ip])<=hysteresis_radius): 136 | centroids=delete(centroids, (ip), axis=0) 137 | ip -= 1 138 | break 139 | ip += 1 140 | 141 | 142 | revenue_record=[] 143 | centroid_record=[] 144 | id_record=[] 145 | 146 | 147 | for ir in na: 148 | for ip in range(0,len(centroids)): 149 | cost=norm(robots[ir].getPosition()-centroids[ip]) 150 | # information_gain=infoGain[ip] 151 | # if (norm(robots[ir].getPosition()-centroids[ip])<=hysteresis_radius): 152 | 153 | # information_gain*=hysteresis_gain 154 | #revenue=information_gain*info_multiplier-cost 155 | revenue=-cost 156 | 157 | revenue_record.append(revenue) 158 | centroid_record.append(centroids[ip]) 159 | id_record.append(ir) 160 | 161 | rospy.loginfo("revenue record: "+str(revenue_record)) 162 | rospy.loginfo("centroid record: "+str(centroid_record)) 163 | rospy.loginfo("robot IDs record: "+str(id_record)) 164 | ''' 165 | if len(na)<1: 166 | revenue_record=[] 167 | centroid_record=[] 168 | id_record=[] 169 | for ir in nb: 170 | for ip in range(0,len(centroids)): 171 | cost=norm(robots[ir].getPosition()-centroids[ip]) 172 | threshold=1 173 | information_gain=infoGain[ip] 174 | if (norm(robots[ir].getPosition()-centroids[ip])<=hysteresis_radius): 175 | information_gain*=hysteresis_gain 176 | 177 | if ((norm(centroids[ip]-robots[ir].assigned_point))0): 189 | winner_id=revenue_record.index(max(revenue_record)) 190 | robots[id_record[winner_id]].sendGoal(centroid_record[winner_id]) 191 | rospy.loginfo(namespace+str(namespace_init_count+id_record[winner_id])+" assigned to "+str(centroid_record[winner_id])) 192 | rospy.sleep(delay_after_assignement) 193 | 194 | if (len(frontiers) > 0): 195 | sub = list(range(n_robots)) 196 | for ir in na: 197 | cost = 1e10 198 | for ri in nb: 199 | if (norm(robots[ir].getPosition()-robots[ri].assigned_point) < norm(robots[ri].getPosition()-robots[ri].assigned_point)) and (cost > norm(robots[ri].getPosition()-robots[ri].assigned_point)): 200 | sub[ri] = ir 201 | cost = norm(robots[ri].getPosition()-robots[ri].assigned_point) 202 | for i in range(len(sub)): 203 | if sub[i] != i: 204 | rospy.loginfo(namespace+str(namespace_init_count+sub[i])+" sub " + str(i) + " assigned to "+str(robots[i].assigned_point)) 205 | 206 | robots[sub[i]].sendGoal(robots[i].assigned_point) 207 | robots[i].cancelGoal() 208 | rospy.sleep(delay_after_assignement) 209 | 210 | 211 | if (len(frontiers) != 0): 212 | stop = 0 213 | else: 214 | stop += 1 215 | 216 | if (stop >= rateHz*2): 217 | end = time.time() 218 | rospy.loginfo("total time: " + str(end-start)) 219 | stop = 0 220 | #------------------------------------------------------------------------- 221 | rate.sleep() 222 | #------------------------------------------------------------------------- 223 | 224 | 225 | if __name__ == '__main__': 226 | try: 227 | node() 228 | except rospy.ROSInterruptException: 229 | end = time.time() 230 | rospy.loginfo("total time: " + str(end-start)) 231 | rospy.loginfo("Exploration has been finished.") 232 | 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /scripts/functions.py: -------------------------------------------------------------------------------- 1 | import rospy 2 | import tf 3 | from numpy import array 4 | import actionlib 5 | from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal 6 | from nav_msgs.srv import GetPlan 7 | from geometry_msgs.msg import PoseStamped, Quaternion 8 | from numpy import floor 9 | from numpy.linalg import norm 10 | from numpy import inf 11 | from tf.transformations import quaternion_from_euler 12 | #________________________________________________________________________________ 13 | class robot: 14 | goal = MoveBaseGoal() 15 | start = PoseStamped() 16 | end = PoseStamped() 17 | 18 | def __init__(self,name): 19 | self.assigned_point=[] 20 | self.name=name 21 | self.global_frame=rospy.get_param('~global_frame','map') 22 | self.listener=tf.TransformListener() 23 | self.listener.waitForTransform(self.global_frame, name+'/base_footprint', rospy.Time(0),rospy.Duration(10.0)) 24 | cond=0; 25 | while cond==0: 26 | try: 27 | (trans,rot) = self.listener.lookupTransform(self.global_frame, self.name+'/base_footprint', rospy.Time(0)) 28 | cond=1 29 | except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException): 30 | cond==0 31 | self.position=array([trans[0],trans[1]]) 32 | self.assigned_point=self.position 33 | self.client=actionlib.SimpleActionClient(self.name+'/move_base', MoveBaseAction) 34 | self.client.wait_for_server() 35 | robot.goal.target_pose.header.frame_id=self.global_frame 36 | robot.goal.target_pose.header.stamp=rospy.Time.now() 37 | 38 | rospy.wait_for_service(self.name+'/move_base/NavfnROS/make_plan') 39 | self.make_plan = rospy.ServiceProxy(self.name+'/move_base/NavfnROS/make_plan', GetPlan) 40 | robot.start.header.frame_id=self.global_frame 41 | robot.end.header.frame_id=self.global_frame 42 | 43 | def getPosition(self): 44 | cond=0; 45 | while cond==0: 46 | try: 47 | (trans,rot) = self.listener.lookupTransform(self.global_frame, self.name+'/base_link', rospy.Time(0)) 48 | cond=1 49 | except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException): 50 | cond==0 51 | self.position=array([trans[0],trans[1]]) 52 | return self.position 53 | 54 | def sendGoal(self,point, rotation=False): 55 | robot.goal.target_pose.pose.position.x=point[0] 56 | robot.goal.target_pose.pose.position.y=point[1] 57 | if rotation: 58 | # robot.goal.target_pose.pose.orientation = Quaternion(*quaternion_from_euler(0, 0, 3.1415926)) 59 | robot.goal.target_pose.pose.orientation.z = 1.0 60 | robot.goal.target_pose.pose.orientation.w = 0.0 61 | else: 62 | robot.goal.target_pose.pose.orientation.w = 1.0 63 | robot.goal.target_pose.pose.orientation.z = 0.0 64 | self.client.send_goal(robot.goal) 65 | self.assigned_point=array(point) 66 | 67 | def cancelGoal(self): 68 | self.client.cancel_goal() 69 | self.assigned_point=self.getPosition() 70 | 71 | def getState(self): 72 | return self.client.get_state() 73 | 74 | def makePlan(self,start,end): 75 | robot.start.pose.position.x=start[0] 76 | robot.start.pose.position.y=start[1] 77 | robot.end.pose.position.x=end[0] 78 | robot.end.pose.position.y=end[1] 79 | start=self.listener.transformPose(self.name+'/map', robot.start) 80 | end=self.listener.transformPose(self.name+'/map', robot.end) 81 | plan=self.make_plan(start = start, goal = end, tolerance = 0.0) 82 | return plan.plan.poses 83 | #________________________________________________________________________________ 84 | 85 | def index_of_point(mapData,Xp): 86 | resolution=mapData.info.resolution 87 | Xstartx=mapData.info.origin.position.x 88 | Xstarty=mapData.info.origin.position.y 89 | width=mapData.info.width 90 | Data=mapData.data 91 | index=int( ( floor((Xp[1]-Xstarty)/resolution)*width)+( floor((Xp[0]-Xstartx)/resolution) )) 92 | return index 93 | 94 | def point_of_index(mapData,i): 95 | y=mapData.info.origin.position.y+(i/mapData.info.width)*mapData.info.resolution 96 | x=mapData.info.origin.position.x+(i-(i/mapData.info.width)*(mapData.info.width))*mapData.info.resolution 97 | return array([x,y]) 98 | #________________________________________________________________________________ 99 | 100 | def informationGain(mapData,point,r): 101 | infoGain=0; 102 | index=index_of_point(mapData,point) 103 | r_region=int(r/mapData.info.resolution) 104 | init_index=index-r_region*(mapData.info.width+1) 105 | for n in range(0,2*r_region+1): 106 | start=n*mapData.info.width+init_index 107 | end=start+2*r_region 108 | limit=((start/mapData.info.width)+2)*mapData.info.width 109 | for i in range(start,end+1): 110 | if (i>=0 and i=0 and i0): 135 | i=len(path)/2 136 | p1=array([path[i-1].pose.position.x,path[i-1].pose.position.y]) 137 | p2=array([path[i].pose.position.x,path[i].pose.position.y]) 138 | return norm(p1-p2)*(len(path)-1) 139 | else: 140 | return inf 141 | #________________________________________________________________________________ 142 | 143 | def unvalid(mapData,pt): 144 | index=index_of_point(mapData,pt) 145 | r_region=5 146 | init_index=index-r_region*(mapData.info.width+1) 147 | for n in range(0,2*r_region+1): 148 | start=n*mapData.info.width+init_index 149 | end=start+2*r_region 150 | limit=((start/mapData.info.width)+2)*mapData.info.width 151 | for i in range(start,end+1): 152 | if (i>=0 and i width*height-1): 203 | rospy.logwarn("Evaluating nhood for offmap point") 204 | return out 205 | 206 | if (index % width > 0): 207 | out.append(index-1) 208 | 209 | if (index % width < (width-1)): 210 | out.append(index+1) 211 | 212 | if (index >= width): 213 | out.append(index-width) 214 | 215 | if (index < width*(height-1)): 216 | out.append(index+width) 217 | 218 | if (index % width > 0 and index >= width): 219 | out.append(index-1-width) 220 | 221 | if (index % width > 0 and index < width*(height-1)): 222 | out.append(index-1+width) 223 | 224 | if (index % width < (width-1) and index >= width): 225 | out.append(index+1-width) 226 | 227 | if (index % width < (width-1) and index < width*(height-1)): 228 | out.append(index+1+width) 229 | 230 | return out 231 | 232 | def gridValue(mapData,Xp): 233 | resolution=mapData.info.resolution 234 | Xstartx=mapData.info.origin.position.x 235 | Xstarty=mapData.info.origin.position.y 236 | 237 | width=mapData.info.width 238 | Data=mapData.data 239 | # returns grid value at "Xp" location 240 | #map data: 100 occupied -1 unknown 0 free 241 | index=( floor((Xp[1]-Xstarty)/resolution)*width)+( floor((Xp[0]-Xstartx)/resolution) ) 242 | 243 | if int(index) < len(Data): 244 | return Data[int(index)] 245 | else: 246 | return 100 247 | -------------------------------------------------------------------------------- /scripts/functions_real_two_robots.py: -------------------------------------------------------------------------------- 1 | import rospy 2 | import tf 3 | from numpy import array 4 | import actionlib 5 | from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal 6 | from nav_msgs.srv import GetPlan 7 | from geometry_msgs.msg import PoseStamped, Quaternion 8 | from numpy import floor 9 | from numpy.linalg import norm 10 | from numpy import inf 11 | from tf.transformations import quaternion_from_euler 12 | #________________________________________________________________________________ 13 | class robot: 14 | goal = MoveBaseGoal() 15 | start = PoseStamped() 16 | end = PoseStamped() 17 | 18 | def __init__(self,name): 19 | self.assigned_point=[] 20 | self.name=name 21 | self.global_frame=rospy.get_param('~global_frame','/map') 22 | self.listener=tf.TransformListener() 23 | self.listener.waitForTransform(self.global_frame, name+'/base_footprint', rospy.Time(0),rospy.Duration(10.0)) 24 | cond=0; 25 | while cond==0: 26 | try: 27 | (trans,rot) = self.listener.lookupTransform(self.global_frame, self.name+'/base_footprint', rospy.Time(0)) 28 | cond=1 29 | except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException): 30 | cond==0 31 | self.position=array([trans[0],trans[1]]) 32 | self.assigned_point=self.position 33 | self.client=actionlib.SimpleActionClient(self.name+'/move_base', MoveBaseAction) 34 | self.client.wait_for_server() 35 | robot.goal.target_pose.header.frame_id=self.global_frame 36 | robot.goal.target_pose.header.stamp=rospy.Time.now() 37 | 38 | rospy.wait_for_service(self.name+'/move_base/NavfnROS/make_plan') 39 | self.make_plan = rospy.ServiceProxy(self.name+'/move_base/NavfnROS/make_plan', GetPlan) 40 | robot.start.header.frame_id=self.global_frame 41 | robot.end.header.frame_id=self.global_frame 42 | 43 | def getPosition(self): 44 | cond=0; 45 | while cond==0: 46 | try: 47 | (trans,rot) = self.listener.lookupTransform(self.global_frame, self.name+'/base_footprint', rospy.Time(0)) 48 | cond=1 49 | except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException): 50 | cond==0 51 | self.position=array([trans[0],trans[1]]) 52 | return self.position 53 | 54 | def sendGoal(self,point, rotation=False): 55 | robot.goal.target_pose.pose.position.x=point[0] 56 | robot.goal.target_pose.pose.position.y=point[1] 57 | if rotation: 58 | # robot.goal.target_pose.pose.orientation = Quaternion(*quaternion_from_euler(0, 0, 3.1415926)) 59 | robot.goal.target_pose.pose.orientation.z = 1.0 60 | robot.goal.target_pose.pose.orientation.w = 0.0 61 | else: 62 | robot.goal.target_pose.pose.orientation.w = 1.0 63 | robot.goal.target_pose.pose.orientation.z = 0.0 64 | self.client.send_goal(robot.goal) 65 | self.assigned_point=array(point) 66 | 67 | def cancelGoal(self): 68 | self.client.cancel_goal() 69 | self.assigned_point=self.getPosition() 70 | 71 | def getState(self): 72 | return self.client.get_state() 73 | 74 | def makePlan(self,start,end): 75 | robot.start.pose.position.x=start[0] 76 | robot.start.pose.position.y=start[1] 77 | robot.end.pose.position.x=end[0] 78 | robot.end.pose.position.y=end[1] 79 | start=self.listener.transformPose(self.name+'/map', robot.start) 80 | end=self.listener.transformPose(self.name+'/map', robot.end) 81 | plan=self.make_plan(start = start, goal = end, tolerance = 0.0) 82 | return plan.plan.poses 83 | #________________________________________________________________________________ 84 | 85 | def index_of_point(mapData,Xp): 86 | resolution=mapData.info.resolution 87 | Xstartx=mapData.info.origin.position.x 88 | Xstarty=mapData.info.origin.position.y 89 | width=mapData.info.width 90 | Data=mapData.data 91 | index=int( ( floor((Xp[1]-Xstarty)/resolution)*width)+( floor((Xp[0]-Xstartx)/resolution) )) 92 | return index 93 | 94 | def point_of_index(mapData,i): 95 | y=mapData.info.origin.position.y+(i/mapData.info.width)*mapData.info.resolution 96 | x=mapData.info.origin.position.x+(i-(i/mapData.info.width)*(mapData.info.width))*mapData.info.resolution 97 | return array([x,y]) 98 | #________________________________________________________________________________ 99 | 100 | def informationGain(mapData,point,r): 101 | infoGain=0; 102 | index=index_of_point(mapData,point) 103 | r_region=int(r/mapData.info.resolution) 104 | init_index=index-r_region*(mapData.info.width+1) 105 | for n in range(0,2*r_region+1): 106 | start=n*mapData.info.width+init_index 107 | end=start+2*r_region 108 | limit=((start/mapData.info.width)+2)*mapData.info.width 109 | for i in range(start,end+1): 110 | if (i>=0 and i=0 and i0): 135 | i=len(path)/2 136 | p1=array([path[i-1].pose.position.x,path[i-1].pose.position.y]) 137 | p2=array([path[i].pose.position.x,path[i].pose.position.y]) 138 | return norm(p1-p2)*(len(path)-1) 139 | else: 140 | return inf 141 | #________________________________________________________________________________ 142 | 143 | def unvalid(mapData,pt): 144 | index=index_of_point(mapData,pt) 145 | r_region=5 146 | init_index=index-r_region*(mapData.info.width+1) 147 | for n in range(0,2*r_region+1): 148 | start=n*mapData.info.width+init_index 149 | end=start+2*r_region 150 | limit=((start/mapData.info.width)+2)*mapData.info.width 151 | for i in range(start,end+1): 152 | if (i>=0 and i width*height-1): 203 | rospy.logwarn("Evaluating nhood for offmap point") 204 | return out 205 | 206 | if (index % width > 0): 207 | out.append(index-1) 208 | 209 | if (index % width < (width-1)): 210 | out.append(index+1) 211 | 212 | if (index >= width): 213 | out.append(index-width) 214 | 215 | if (index < width*(height-1)): 216 | out.append(index+width) 217 | 218 | if (index % width > 0 and index >= width): 219 | out.append(index-1-width) 220 | 221 | if (index % width > 0 and index < width*(height-1)): 222 | out.append(index-1+width) 223 | 224 | if (index % width < (width-1) and index >= width): 225 | out.append(index+1-width) 226 | 227 | if (index % width < (width-1) and index < width*(height-1)): 228 | out.append(index+1+width) 229 | 230 | return out 231 | 232 | def gridValue(mapData,Xp): 233 | resolution=mapData.info.resolution 234 | Xstartx=mapData.info.origin.position.x 235 | Xstarty=mapData.info.origin.position.y 236 | 237 | width=mapData.info.width 238 | Data=mapData.data 239 | # returns grid value at "Xp" location 240 | #map data: 100 occupied -1 unknown 0 free 241 | index=( floor((Xp[1]-Xstarty)/resolution)*width)+( floor((Xp[0]-Xstartx)/resolution) ) 242 | 243 | if int(index) < len(Data): 244 | return Data[int(index)] 245 | else: 246 | return 100 247 | --------------------------------------------------------------------------------