Skip to content
This repository was archived by the owner on Jan 2, 2024. It is now read-only.

Commit f96ecf7

Browse files
Zephkotylerlum
andauthored
Add boats to path (#62)
* Add code to handle cases with invalid start states or not enough runtime * Improve code readability, use inexact solution path if needed, show non-blocking pathfinding problem plot, and extend obstacles based on their distance to the boat * Fix typo * Improve log error message * Fix bug, make the shrinkFactor a float * Fix tests for extendObstaclesArray * Save work for new working implementation * Improve obstacleOnPath detection to use the boat's position and index * Add unit tests for obstacleOnPath * Fix visualizer to have equal aspect ratio and add optional plotting in createLocalPathSS * Fix small bug * Clean utilities.py code * Fix typo * Improve code readability * Write tests for sailingUpwindOrDownwind and clean up code for that * Make plot close-able * modified MOCK_AIS to add and delete boats, addBoat node to accept addBoat msg, new msg type addBoat * new class Ship, added roslaunch that includes visualizer, adding and deleting boats working * accept speed parameters from ship part of msg * added README, changed topic name, code cleanup * fixed obstacles not showing when speed = 0 Co-authored-by: Tyler Lum <tylergwlum@gmail.com>
1 parent d8fe191 commit f96ecf7

File tree

7 files changed

+130
-9
lines changed

7 files changed

+130
-9
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ add_message_files(
5757
latlon.msg
5858
path.msg
5959
windSensor.msg
60+
addBoat.msg
6061
)
6162

6263
## Generate services in the 'srv' folder

launch/all.launch

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<launch>
2+
<node pkg="local_pathfinding" type="MOCK_wind_sensor.py" name="MOCK_wind_sensor" />
3+
<node pkg="local_pathfinding" type="MOCK_controller_and_boat.py" name="MOCK_controller_and_boat" />
4+
<node pkg="local_pathfinding" type="MOCK_AIS.py" name="MOCK_AIS" />
5+
<node pkg="local_pathfinding" type="MOCK_global_planner.py" name="MOCK_global_planner" />
6+
<node pkg="local_pathfinding" type="MOCK_UDP_bridge.py" name="MOCK_UDP_bridge" />
7+
<node pkg="local_pathfinding" type="addBoat.py" name="addBoat" />
8+
<node pkg="local_pathfinding" type="main_loop.py" name="main_loop" />
9+
<node pkg="local_pathfinding" type="local_path_visualizer.py" name="local_path_visualizer" />
10+
</launch>

msg/addBoat.msg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
string addType
2+
AISShip ship
3+
int32 waypointIndex

python/MOCK_AIS.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
import random
55
from geopy.distance import distance
66
from utilities import headingToBearingDegrees, PORT_RENFREW_LATLON
7+
from std_msgs.msg import Int32
78

8-
import local_pathfinding.msg as msg
9+
from local_pathfinding.msg import AISShip, AISMsg
910

10-
class Ship:
11+
class RandomShip:
1112
def __init__(self, id, sailbot_lat, sailbot_lon, publishPeriodSeconds):
1213
self.id = id
1314
self.headingDegrees = random.randint(0, 360)
@@ -28,42 +29,71 @@ def move(self):
2829
self.lat = boatLatlon.latitude
2930

3031
def make_ros_message(self):
31-
return msg.AISShip(
32+
return AISShip(
3233
self.id,
3334
self.lat,
3435
self.lon,
3536
self.headingDegrees,
3637
self.speedKmph)
3738

39+
class Ship:
40+
def __init__(self, id, boat_lat, boat_lon, heading, speed):
41+
self.id = id
42+
self.lat = boat_lat
43+
self.lon = boat_lon
44+
self.headingDegrees = heading
45+
self.speedKmph = speed
46+
47+
def make_ros_message(self):
48+
return AISShip(
49+
self.id,
50+
self.lat,
51+
self.lon,
52+
self.headingDegrees,
53+
self.speedKmph)
3854

55+
3956
class MOCK_AISEnvironment:
4057
# Just a class to keep track of the ships surrounding the sailbot
4158
def __init__(self, lat, lon):
4259
self.publishPeriodSeconds = 1.0
4360
self.numShips = 10
4461
self.ships = []
4562
for i in range(self.numShips):
46-
self.ships.append(Ship(i, lat, lon, self.publishPeriodSeconds))
63+
self.ships.append(RandomShip(i, lat, lon, self.publishPeriodSeconds))
64+
4765

4866
rospy.init_node('MOCK_AIS', anonymous=True)
49-
self.publisher = rospy.Publisher("AIS", msg.AISMsg, queue_size=4)
67+
self.publisher = rospy.Publisher("AIS", AISMsg, queue_size=4)
68+
rospy.Subscriber('/new_boats', AISShip, self.new_boat_callback)
69+
rospy.Subscriber('/delete_boats', Int32, self.remove_boat_callback)
5070

5171
def move_ships(self):
5272
for i in range(self.numShips):
53-
self.ships[i].move()
54-
73+
if isinstance(self.ships[i], RandomShip):
74+
self.ships[i].move()
75+
5576
def make_ros_message(self):
77+
rospy.loginfo([ship.id for ship in self.ships])
5678
ship_list = []
5779
for i in range(self.numShips):
5880
ship_list.append(self.ships[i].make_ros_message())
59-
return msg.AISMsg(ship_list)
81+
return AISMsg(ship_list)
82+
83+
def new_boat_callback(self, msg):
84+
self.ships.append(Ship(msg.ID, msg.lat, msg.lon, msg.headingDegrees, msg.speedKmph))
85+
self.numShips += 1
86+
87+
def remove_boat_callback(self, msg):
88+
self.ships[:] = [ship for ship in self.ships if not ship.id == msg.data]
89+
self.numShips = len(self.ships)
6090

6191
if __name__ == '__main__':
6292
ais_env = MOCK_AISEnvironment(PORT_RENFREW_LATLON.lat, PORT_RENFREW_LATLON.lon)
6393
r = rospy.Rate(1.0 / ais_env.publishPeriodSeconds) #hz
6494

6595
while not rospy.is_shutdown():
66-
ais_env.move_ships()
6796
data = ais_env.make_ros_message()
97+
ais_env.move_ships()
6898
ais_env.publisher.publish(data)
6999
r.sleep()

python/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# How to add and delete boats
2+
3+
Ensure all nodes are launched. Use `roslaunch local_pathfinding all.launch`
4+
5+
## Add Boats
6+
7+
Type `rostopic pub /new_boats ...` and press Tab twice to generate a message template and fill in the parameters for the ship you would like to add.
8+
9+
## Delete Boats
10+
11+
Type `rostopic pub /delete_boats` and press Tab twice. Fill in the ID of the boats you want to remove.
12+
13+
## Add boats using other methods
14+
15+
Type `rostopic pub /boat_on_path ...` and press Tab twice.
16+
17+
specify the addType as one of the following options:
18+
19+
20+
1. `addType = latlon` will just publish a boat with the specified parameters in addBoat.ship
21+
22+
2. `addType = nextWaypoint` will publish a boat at the next localWaypoint with the specified ID, heading, and speed in addBoat.ship (lat lon params are ignored)
23+
24+
3. `addType = onBoat` will publish a boat at the current location of the Sailbot with the specified ID, heading, and speed in addBoat.ship (lat lon params are ignored)
25+
26+
4. `addType = index` will publish a boat at localPath[addBoat.waypointIndex] with the specified ID, heading, and speed in addBoat.ship (lat lon params are ignored)
27+
28+
29+

python/addBoat.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env python
2+
import rospy
3+
from std_msgs.msg import Int32
4+
from local_pathfinding.msg import AISMsg, AISShip, latlon, addBoat, GPS, path
5+
6+
localWaypoint = latlon()
7+
gps = latlon()
8+
localPath = []
9+
10+
def command_callback(msg):
11+
if msg.addType == "latlon":
12+
add_pub.publish(msg.ship)
13+
if msg.addType == "nextWaypoint":
14+
add_pub.publish(AISShip(msg.ship.ID, localWaypoint.lat, localWaypoint.lon, msg.ship.headingDegrees, msg.ship.speedKmph))
15+
if msg.addType == "onBoat":
16+
add_pub.publish(AISShip(msg.ship.ID, gps.lat, gps.lon, msg.ship.headingDegrees, msg.ship.speedKmph))
17+
if msg.addType == "index":
18+
if msg.waypointIndex < len(localPath) and msg.waypointIndex >=0:
19+
add_pub.publish(AISShip(msg.ship.ID, localPath[msg.waypointIndex].lat, localPath[msg.waypointIndex].lon, msg.ship.headingDegrees, msg.ship.speedKmph))
20+
rospy.loginfo("new ship generated at index {} of local path".format(msg.waypointIndex))
21+
else:
22+
rospy.loginfo("invalid index passed")
23+
24+
def lwp_callback(coordinates):
25+
global localWaypoint
26+
localWaypoint = coordinates
27+
28+
def gps_callback(msg):
29+
global gps
30+
gps = latlon(msg.lat, msg.lon)
31+
32+
def localPath_callback(msg):
33+
global localPath
34+
localPath = msg.waypoints
35+
36+
if __name__ == "__main__":
37+
rospy.init_node('addBoatsToPath', anonymous=True)
38+
rospy.Subscriber('/boat_on_path', addBoat, command_callback)
39+
rospy.Subscriber('/nextLocalWaypoint', latlon, lwp_callback)
40+
rospy.Subscriber('/GPS', GPS, gps_callback)
41+
rospy.Subscriber('/localPath', path, localPath_callback)
42+
add_pub = rospy.Publisher('/new_boats', AISShip, queue_size=4)
43+
rospy.Rate(1)
44+
rospy.spin()

python/utilities.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,10 @@ def extendObstaclesArray(aisArray, sailbotPosition, sailbotSpeedKmph, referenceL
295295
timeToLocHours = distanceToBoatKm / sailbotSpeedKmph
296296
extendBoatLengthKm = aisData.speedKmph * timeToLocHours
297297

298+
if extendBoatLengthKm == 0:
299+
obstacles.append(Obstacle(aisX, aisY, AIS_BOAT_RADIUS_KM))
300+
301+
298302
if aisData.headingDegrees == 90 or aisData.headingDegrees == 270:
299303
if aisData.headingDegrees == 90:
300304
endY = aisY + extendBoatLengthKm

0 commit comments

Comments
 (0)