Guest User

Untitled

a guest
Nov 20th, 2017
390
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.35 KB | None | 0 0
  1. #!/usr/bin/env python
  2. #ROS Node to convert a GPS waypoint published on the topic "waypoint" into a 2D Navigation Goal in SLAM to achieve autonomous navigation to a GPS Waypoint
  3. #Converts Decimal GPS Coordinates of waypoint to ROS Position Vector relative to the current gps position of the robot
  4. #Accounts for curvature of the earth using haversine formula
  5.  
  6. #Depends rospy, std_msgs, geographic_msgs, sensor_msgs, numpy
  7. #Written by Alex McClung, 2015, alex.mcclung@hotmail.com, To be Released Open Source under Creative Commons Attribution Share-Alike Licence
  8.  
  9. import roslib
  10. import rospy
  11. from math import radians, cos, sin, asin, sqrt, pow, pi, atan2
  12. import numpy as np
  13. from std_msgs.msg import String
  14. from sensor_msgs.msg import NavSatFix
  15. from geometry_msgs.msg import PoseStamped
  16. from geometry_msgs.msg import PoseWithCovarianceStamped
  17. from geographic_msgs.msg import WayPoint
  18.  
  19. debug = False
  20.  
  21. latCur = 0.0
  22. lonCur = 0.0
  23. latWP = 0.0
  24. lonWP = 0.0
  25. altWP = 0.0
  26.  
  27. earthRadius = 6371000.0 #Metres
  28. currPosX = 0.0
  29. currPosY = 0.0
  30. currPosZ = 0.0
  31.  
  32. WPUpdateState = False #True if there has been an update in the waypoint position
  33.  
  34. lastValidFixTime = 0.0
  35.  
  36. gpsValidityTimeout = 10.0 #Seconds
  37.  
  38. def haversineDistance(latCur, lonCur, latWP, lonWP): #Returns distance to waypoint in Metres
  39. latWP, lonWP, latCur, lonCur = map(radians, [latWP, lonWP, latCur, lonCur]) #Convert into Radians to perform math
  40. a = pow(sin((latWP - latCur)/2),2) + cos(latCur) * cos(latWP) * pow(sin((lonWP - lonCur)/2),2)
  41. return earthRadius * 2.0 * asin(sqrt(a)) #Return calculated distance to waypoint in Metres
  42.  
  43. def bearing(latCur, lonCur, latWP, lonWP): #Bearing to waypoint (degrees)
  44. latWP, lonWP, latCur, lonCur = map(radians, [latWP, lonWP, latCur, lonCur]) #Convert into Radians to perform math
  45. dLon = lonWP - lonCur
  46. return atan2(sin(dLon) * cos(latWP), cos(latCur) * sin(latWP) - (sin(latCur) * cos(latWP) * cos(dLon)))
  47.  
  48. def gpsSubscriber(gpsMsg): #GPS Coordinate recieved from ROS topic, run this function
  49. if gpsMsg.status.status > -1: #If there is a GPS fix (Either Augmented or Unaugmented)
  50. global latCur
  51. global lonCur
  52. global lastValidFixTime
  53.  
  54. lastValidFixTime = rospy.get_time()
  55. latCur = gpsMsg.latitude
  56. lonCur = gpsMsg.longitude
  57. if debug == True:
  58. rospy.loginfo("GPS Fix Available, Latitude: %f, Longitude: %f", latCur, lonCur)
  59.  
  60. def gpsFixIsValid(): #Check to see if there has been a GPS fix within the last <gpsValidityTimeout> seconds
  61. global gpsValidityTimeout
  62.  
  63. if (rospy.get_time()- lastValidFixTime) < gpsValidityTimeout:
  64. return True
  65. else:
  66. rospy.loginfo("GPS Fix Invalid! Last valid update was: %f seconds ago", rospy.get_time()- lastValidFixTime)
  67. return False
  68.  
  69. def robotPoseSubscriber(poseMsg): #Odometry update recieved from ROS topic, run this function
  70. global currPosX
  71. global currPosY
  72. global currPosZ
  73.  
  74. currPosX = poseMsg.pose.pose.position.x
  75. currPosY = poseMsg.pose.pose.position.y
  76. currPosZ = poseMsg.pose.pose.position.z
  77.  
  78. def waypointSubscriber(WPMsg): #Waypoint Command recieved from ROS topic, run this function
  79. global waypointUpdateState
  80. global latWP
  81. global lonWP
  82. global altWP
  83.  
  84. WPUpdateState = True
  85. latWP = WPMsg.position.latitude
  86. lonWP = WPMsg.position.longitude
  87. altWP = WPMsg.position.altitude
  88.  
  89. rospy.loginfo("Recieved Waypoint Command, Latitude: %f, Longitude: %f", latWP, lonWP)
  90.  
  91. if gpsFixIsValid() == True: #If there is a valid GPS fix, publish nav goal to ROS
  92. posePublisher()
  93.  
  94. def posePublisher(): #Convert absolute waypoint to vector relative to robot, then publish navigation goal to ROS
  95. desiredPose = PoseStamped()
  96. desiredPose.header.frame_id = "/gps_link"
  97. desiredPose.header.stamp = rospy.Time.now()
  98.  
  99. global currPosX
  100. global currPosY
  101. global currPosZ
  102. global debug
  103.  
  104. if debug:
  105. rospy.loginfo("LatWP: %f, LonWP: %f, LatCur: %f, LonCur: %f", latWP, lonWP, latCur, lonCur)
  106. distToWP = haversineDistance(latCur, lonCur, latWP, lonWP)
  107. bearingToWP = bearing(latCur, lonCur, latWP, lonWP)
  108.  
  109. desiredPose.pose.position.x = currPosX + (distToWP * cos(bearingToWP)) #Convert distance and angle to waypoint from Polar to Cartesian co-ordinates then add current position of robot odometry
  110. desiredPose.pose.position.y = currPosY + (distToWP * sin(bearingToWP))
  111. desiredPose.pose.position.z = altWP - currPosZ #Assuming CurrPosZ is abslolute (eg barometer or GPS)
  112. desiredPose.pose.orientation.x = 0
  113. desiredPose.pose.orientation.y = 0
  114. desiredPose.pose.orientation.z = 0
  115. desiredPose.pose.orientation.w = 1
  116.  
  117. navGoalPub = rospy.Publisher('move_base_simple/goal', PoseStamped, queue_size=10) #Publish Nav Goal to ROS topic
  118. navGoalPub.publish(desiredPose)
  119.  
  120. rospy.loginfo("GPS Fix is Valid! Setting Navigation Goal to: %f, %f, %f", desiredPose.pose.position.x, desiredPose.pose.position.y, desiredPose.pose.position.z)
  121. rospy.loginfo("Robot is heading %f metres at a bearing of %f degrees", distToWP, (bearingToWP * 180/pi + 360) % 360)
  122.  
  123. def main():
  124. rospy.init_node('gps_2d_nav_goal', anonymous=True)
  125. rospy.loginfo("Initiating GPS 2D Nav Goal Node.")
  126.  
  127. while not rospy.is_shutdown(): #While ros comms are running smoothly
  128. rospy.Subscriber("waypoint", WayPoint, waypointSubscriber) #Subscribe to "pose", "fix" and "waypoint" ROS topics
  129. rospy.Subscriber("fix", NavSatFix, gpsSubscriber)
  130. rospy.Subscriber("odom_combined", PoseWithCovarianceStamped, robotPoseSubscriber)
  131. rospy.spin()
  132.  
  133. if __name__ == '__main__':
  134. try:
  135. main()
  136. except rospy.ROSInterruptException:
  137. pass
Add Comment
Please, Sign In to add comment