
writer:lIcht email:lIcht.gzl@gmail.com date: 2020.6.2
因为项目需要,对官方gps_goal这一功能包进行修改,实现对多个目标点的连续导航。订阅的主题1: /fix 提供实时的gps经纬度信息 消息格式为:geometry_msgs::NavSatFix ;订阅节点2: /position 提供多个gps目标点序列 消息格式为:std_msgs::String。同时加入状态机,显示目前的导航状态,返回每个点导航的结果。并在导航过程中或者导航完成后追加目标点序列。
源码分享:
xxxxxxxxxx#!/usr/bin/python#-*-coding: UTF-8 -*-import rospyimport mathimport actionlibimport tfimport threadingfrom geographiclib.geodesic import Geodesicfrom actionlib_msgs.msg import GoalStatusfrom geometry_msgs.msg import PoseStampedfrom move_base_msgs.msg import MoveBaseAction, MoveBaseGoalfrom sensor_msgs.msg import NavSatFixfrom tank_msgs.msg import GpsPointsfrom std_msgs.msg import Stringfrom smach import State,StateMachine,Concurrence#创建GPS点列表(全局)gps_list = []current_id = 0goal_count = 0def DMS_to_decimal_format(lat, long): # 转化度分秒格式转化为十进制 if ',' in lat: degrees, minutes, seconds = lat.split(',') degrees, minutes, seconds = float(degrees), float(minutes), float(seconds) if lat[0] == '-': # 负号情况 minutes = -minutes seconds = -seconds lat = degrees + minutes/60 + seconds/3600 if ',' in long: degrees, minutes, seconds = long.split(',') degrees, minutes, seconds = float(degrees), float(minutes), float(seconds) if long[0] == '-': # 负号情况 minutes = -minutes seconds = -seconds long = degrees + minutes/60 + seconds/3600 lat = float(lat) long = float(long) rospy.loginfo('Given GPS goal: lat %s, long %s.' % (lat, long)) return lat, longdef get_origin_lat_long(): # Get the lat long coordinates of our map frame's origin which must be publshed on topic /local_xy_origin. We use this to calculate our goal within the map frame. rospy.loginfo("Waiting for a message to initialize the origin GPS location...") # origin_pose = rospy.wait_for_message('local_xy_origin', PoseStamped) # if origin_pose is None: rospy.loginfo("Waiting for fix message to initialize the origin GPS location...") origin_pose = rospy.wait_for_message('/fix', NavSatFix) origin_lat = origin_pose.latitude origin_long = origin_pose.longitude # else: # origin_lat = origin_pose.pose.position.y # origin_long = origin_pose.pose.position.x rospy.loginfo('Received origin: lat %s, long %s.' % (origin_lat, origin_long)) return origin_lat, origin_longdef calc_goal(origin_lat, origin_long, goal_lat, goal_long): # Calculate distance and azimuth between GPS points geod = Geodesic.WGS84 # define the WGS84 ellipsoid g = geod.Inverse(origin_lat, origin_long, goal_lat, goal_long) # Compute several geodesic calculations between two GPS points hypotenuse = distance = g['s12'] # access distance rospy.loginfo("The distance from the origin to the goal is {:.3f} m.".format(distance)) azimuth = g['azi1'] rospy.loginfo("The azimuth from the origin to the goal is {:.3f} degrees.".format(azimuth)) # Convert polar (distance and azimuth) to x,y translation in meters (needed for ROS) by finding side lenghs of a right-angle triangle # Convert azimuth to radians azimuth = math.radians(azimuth) x = adjacent = math.cos(azimuth) * hypotenuse y = opposite = math.sin(azimuth) * hypotenuse rospy.loginfo("The translation from the origin to the goal is (x,y) {:.3f}, {:.3f} m.".format(x, y)) return x, yclass GpsGoal(): def __init__(self): rospy.init_node('multi_gps_goal') self.point_id = 0 self.waypoints = [] self.last_data = "" rospy.Subscriber('position', String, self.gps_points_callback) # 监听gps目标点 # Get the lat long coordinates of our map frame's origin which must be publshed on topic /local_xy_origin. We use this to calculate our goal within the map frame. self.origin_lat, self.origin_long = get_origin_lat_long() def point_to_goal(self, id, goal_lat, goal_long, ori_lat, ori_long, z=0, yaw=0, roll=0, pitch=0): # Calculate goal x and y in the frame_id given the frame's origin GPS and a goal GPS location x, y = calc_goal(ori_lat, ori_long, goal_lat, goal_long) # Create move_base goal quaternion = tf.transformations.quaternion_from_euler(roll, pitch, yaw) return [str(id), [x, y], [quaternion[0], quaternion[1], quaternion[2], quaternion[3]]] # self.creat_goal(x=x, y=y, z=z, yaw=yaw, roll=roll, pitch=pitch) def string_to_list(self, data): res = [] list1 = data.split(";") for i in list1: if i: rospy.loginfo('%s' % i) list2 = i.split(",") # rospy.loginfo('add %d point lat: %s, lon: %s' % (self.point_id,list2[0],list2[1])) temp_list = (self.point_id, float(list2[0]), float(list2[1])) res.append(tuple(temp_list)) self.point_id += 1 rospy.loginfo('add point %d lat:%s, lon:%s' % (temp_list[0],temp_list[1],temp_list[2])) self.point_id = 0 return res def gps_points_callback(self, data): global gps_list if data.data == self.last_data: return self.last_data = data.data gps_list = self.string_to_list(data.data) if gps_list: rospy.loginfo("get %d points !" % len(gps_list)) waypoints = self.get_waypoint() # statemachine(waypoints) add_thread = threading.Thread(target = statemachine, args=(waypoints,)) add_thread.start() gps_list=[] self.waypoints = [] return return def get_waypoint(self): if not gps_list: rospy.loginfo("no point in the point_list") return temp_goal = [] # 由获得的经纬度目标点序列转化为姿态序列 for i in range(0,len(gps_list)): if i == 0: temp_goal = self.point_to_goal(gps_list[i][0], gps_list[i][1], gps_list[i][2], self.origin_lat, self.origin_long) self.waypoints.append(tuple(temp_goal)) rospy.loginfo("add the goal %d: lat=%f, lon=%f" % (gps_list[i][0], gps_list[i][1], gps_list[i][2])) else: temp_goal = self.point_to_goal(gps_list[i][0], gps_list[i][1], gps_list[i][2], gps_list[i-1][1], gps_list[i-1][2]) self.waypoints.append(tuple(temp_goal)) rospy.loginfo("add the goal %d: lat=%f, lon=%f" % (gps_list[i][0], gps_list[i][1], gps_list[i][2])) self.point_id = 0 return self.waypoints# 状态机---------------------------------------------------------def statemachine(waypoints): global current_id rospy.loginfo('start with state %d ' % current_id) goal_count = len(waypoints) rospy.loginfo('waypoint count: %d' % goal_count) if waypoints: patrol = StateMachine(outcomes=['finished','failed']) with patrol: for i,w in enumerate(waypoints): if i+current_id < len(waypoints)+current_id-1: next_status = str(i+current_id+1) elif i+current_id == len(waypoints)+current_id-1: next_status = 'finished' StateMachine.add(str(i+current_id), Waypoint(w[1], w[2]), transitions={'success':next_status, 'fail':'failed'}) patrol.execute() waypoints = [] print('done!!!!!!!!!!') returnclass Waypoint(State): def __init__(self, position, orientation): State.__init__(self, outcomes=['success','fail']) # Get an action client self.client = actionlib.SimpleActionClient('move_base', MoveBaseAction) self.client.wait_for_server() # Define the goal self.goal = MoveBaseGoal() self.goal.target_pose.header.frame_id = 'odom' self.goal.target_pose.pose.position.x = position[0] self.goal.target_pose.pose.position.y = position[1] self.goal.target_pose.pose.position.z = 0.0 self.goal.target_pose.pose.orientation.x = orientation[0] self.goal.target_pose.pose.orientation.y = orientation[1] self.goal.target_pose.pose.orientation.z = orientation[2] self.goal.target_pose.pose.orientation.w = orientation[3] def execute(self, userdata): global gps_list global current_id global goal_count self.client.send_goal(self.goal) current_id += 1 rospy.loginfo('Inital goal status: %s' % GoalStatus.to_string(self.client.get_state())) status = self.client.get_goal_status_text() if status: rospy.loginfo(status) self.client.wait_for_result() res = GoalStatus.to_string(self.client.get_state()) rospy.loginfo('Final goal status: %s' % res) status = self.client.get_goal_status_text() if status: rospy.loginfo(status) if res == 'SUCCEEDED': return 'success' else: return 'fail'def ros_main(): # while True: gpsgoal = GpsGoal() rospy.loginfo("Waiting for goal points") rospy.wait_for_message('/position', String) rospy.sleep(2) rospy.loginfo('*********************************************') rospy.spin()if __name__ == '__main__': ros_main()