-
Notifications
You must be signed in to change notification settings - Fork 3
User/rnar17/new update collision zones #822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -92,10 +92,7 @@ def polar_to_cartesian(angle_rad: float, magnitude: float) -> XY: | |||||
| - x → east component | ||||||
| - y → north component | ||||||
| """ | ||||||
| return XY( | ||||||
| x=magnitude * math.sin(angle_rad), | ||||||
| y=magnitude * math.cos(angle_rad) | ||||||
| ) | ||||||
| return XY(x=magnitude * math.sin(angle_rad), y=magnitude * math.cos(angle_rad)) | ||||||
|
|
||||||
|
|
||||||
| def meters_to_km(meters: float) -> float: | ||||||
|
|
@@ -246,3 +243,25 @@ def _latlon_point_to_xy_point(latlon_point: tuple) -> Point: | |||||
| def latlon_list_to_xy_list(reference_latlon, lat_lon_list: List[ci.HelperLatLon]) -> List[XY]: | ||||||
| """Converts a list of lat/lon coordinates to x/y coordinates.""" | ||||||
| return [latlon_to_xy(reference=reference_latlon, latlon=pos) for pos in lat_lon_list] | ||||||
|
|
||||||
|
|
||||||
| def rot_to_rad_per_sec(rot: int) -> float: | ||||||
| """ | ||||||
| Convert AIS ROT (rate of turn) int8 value into radians per second. | ||||||
|
|
||||||
| Returns: | ||||||
| float: rate of turn in radians per second. | ||||||
| """ | ||||||
|
|
||||||
| if rot == -128: | ||||||
| return 0.0 | ||||||
|
|
||||||
| if abs(rot) == 127: | ||||||
| rot_dpm = 10.0 | ||||||
| else: | ||||||
| rot_dpm = (abs(rot) / 4.733) ** 2 | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: why not remove the
Suggested change
This now encodes direction directly. I am not sure if this would produce the correct result, please verify this change through testing. |
||||||
|
|
||||||
| if rot < 0: | ||||||
| rot_dpm *= -1 | ||||||
|
|
||||||
| return math.radians(rot_dpm / 60.0) | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,8 @@ | |
| import local_pathfinding.coord_systems as cs | ||
|
|
||
| # Constants | ||
| PREDICTION_HORIZON = 10.0 # seconds | ||
| DT = 0.5 | ||
| PROJ_DISTANCE_NO_COLLISION = 0.0 | ||
| BOAT_BUFFER = 0.25 # km | ||
| COLLISION_ZONE_STRETCH_FACTOR = 1.25 # This factor changes the width of the boat collision zone | ||
|
|
@@ -204,6 +206,7 @@ def __init__( | |
| self.ais_ship = ais_ship | ||
| self.width = cs.meters_to_km(self.ais_ship.width.dimension) | ||
| self.length = cs.meters_to_km(self.ais_ship.length.dimension) | ||
| self._raw_collision_zone = None | ||
| self.update_collision_zone() | ||
|
|
||
| def update_sailbot_data( | ||
|
|
@@ -233,33 +236,88 @@ def update_collision_zone(self, **kwargs) -> None: | |
| raise ValueError("Argument AIS Ship ID does not match this Boat instance's ID") | ||
| self.ais_ship = ais_ship | ||
|
|
||
| projected_distance = self._calculate_projected_distance() | ||
| # Calculate ROT in radians per second | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment not needed |
||
| rot = self.ais_ship.rot.rot | ||
| rot_rps = cs.rot_to_rad_per_sec(rot) | ||
|
|
||
| # TODO maybe incorporate ROT in this calculation at a later time | ||
| collision_zone_width = projected_distance * COLLISION_ZONE_STRETCH_FACTOR * self.width | ||
| # Calculate current and future heading and projected distances | ||
| dt = DT | ||
| speed_kmps = self.ais_ship.sog.speed / 3600.0 | ||
| cog_rad = math.radians(self.ais_ship.cog.heading) | ||
| x, y = cs.latlon_to_xy(self.reference, self.ais_ship.lat_lon) | ||
| projected_distance = self._calculate_projected_distance(cog_rad) | ||
| bow_y = self.length / 2 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what does |
||
|
|
||
| if projected_distance == PROJ_DISTANCE_NO_COLLISION: | ||
| # If no collision detected, create small collision zone around the boat | ||
| RADIUS_MULTIPLIER = 5 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How did you come up with 5? |
||
| radius = cs.km_to_meters(max(self.width, self.length)) * RADIUS_MULTIPLIER | ||
| boat_collision_zone = Point(-self.width / 2, -self.length / 2).buffer( | ||
| radius, resolution=16 | ||
| ) | ||
| raw = self._translate_collision_zone(boat_collision_zone) | ||
| self._raw_collision_zone = raw | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest assigning |
||
| self.collision_zone = raw.buffer(radius + BOAT_BUFFER, join_style=2) | ||
| prepared.prep(self.collision_zone) | ||
|
|
||
| # Points of the boat collision cone polygon before rotation and centred at the origin | ||
| boat_collision_zone = Polygon( | ||
| [ | ||
| [-self.width / 2, -self.length / 2], | ||
| [-collision_zone_width, self.length / 2 + projected_distance], | ||
| [collision_zone_width, self.length / 2 + projected_distance], | ||
| [self.width / 2, -self.length / 2], | ||
| elif abs(rot_rps) < 1e-4: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How did you come up with this threshold? + Magic number |
||
| # Straight line | ||
| collision_zone_width = projected_distance * COLLISION_ZONE_STRETCH_FACTOR * self.width | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code is same as earlier, right? |
||
| boat_collision_zone = Polygon( | ||
| [ | ||
| [-self.width / 2, -self.length / 2], | ||
| [-collision_zone_width, self.length / 2 + projected_distance], | ||
| [collision_zone_width, self.length / 2 + projected_distance], | ||
| [self.width / 2, -self.length / 2], | ||
| ] | ||
| ) | ||
| raw = self._translate_collision_zone(boat_collision_zone) | ||
| self._raw_collision_zone = raw | ||
| self.collision_zone = raw.buffer(BOAT_BUFFER, join_style=2) | ||
| prepared.prep(self.collision_zone) | ||
| else: | ||
| # Turning motion | ||
| R = speed_kmps / abs(rot_rps) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add the units of
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is how much the boat translates per radian right? |
||
| turn_angle = rot_rps * dt | ||
|
|
||
| # Center of rotation in world frame | ||
| cx = x - R * math.sin(cog_rad) * math.copysign(1, rot_rps) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't verified this but, could you please check if Please note the documentaiton might be wrong in places; i appologize for that. I am in the process of fixing it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will also point out that i had discovered that
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, can you please explain the math here. Especially why does |
||
| cy = y + R * math.cos(cog_rad) * math.copysign(1, rot_rps) | ||
|
|
||
| # Rotate the ship around the center | ||
| future_x = cx + R * math.sin(cog_rad + turn_angle) | ||
| future_y = cy + R * math.cos(cog_rad + turn_angle) | ||
|
|
||
| delta_heading = rot_rps * dt | ||
| future_cog_rad = cog_rad + delta_heading | ||
| future_projected_distance = self._calculate_projected_distance( | ||
| future_cog_rad, position_override=(future_x, future_y) | ||
| ) | ||
|
|
||
| A = [0.0, bow_y] | ||
| B = [0.0, bow_y + projected_distance] | ||
| C = [ | ||
| future_projected_distance * math.sin(delta_heading), | ||
| bow_y + future_projected_distance * math.cos(delta_heading), | ||
| ] | ||
| ) | ||
|
|
||
| boat_collision_zone = Polygon([A, B, C]) | ||
| raw = self._translate_collision_zone(boat_collision_zone) | ||
| self._raw_collision_zone = raw | ||
| self.collision_zone = raw.buffer(BOAT_BUFFER, join_style=2) | ||
| prepared.prep(self.collision_zone) | ||
|
|
||
| def _translate_collision_zone(self, boat_collision_zone): | ||
| # this code block translates and rotates the collision zone to the correct position | ||
| dx, dy = cs.latlon_to_xy(self.reference, self.ais_ship.lat_lon) | ||
| angle_rad = math.radians(-self.ais_ship.cog.heading) | ||
| sin_theta = math.sin(angle_rad) | ||
| cos_theta = math.cos(angle_rad) | ||
| transformation = np.array([cos_theta, -sin_theta, sin_theta, cos_theta, dx, dy]) | ||
| collision_zone = affine_transform(boat_collision_zone, transformation) | ||
| return collision_zone | ||
|
|
||
| self.collision_zone = collision_zone.buffer(BOAT_BUFFER, join_style=2) | ||
| prepared.prep(self.collision_zone) | ||
|
|
||
| def _calculate_projected_distance(self) -> float: | ||
| def _calculate_projected_distance(self, cog_rad, position_override=None) -> float: | ||
| """Calculates the distance the boat obstacle will travel before collision, if | ||
| Sailbot moves directly towards the soonest possible collision point at its current speed. | ||
| The system is modeled by two parametric lines extending from the positions of the boat | ||
|
|
@@ -276,8 +334,10 @@ def _calculate_projected_distance(self) -> float: | |
| float: Distance in km that the boat will travel before collision or the constant value | ||
| PROJ_DISTANCE_NO_COLLISION if a collision is not possible. | ||
| """ | ||
| position = cs.latlon_to_xy(self.reference, self.ais_ship.lat_lon) | ||
| cog_rad = math.radians(self.ais_ship.cog.heading) | ||
| if position_override is None: | ||
| position = cs.latlon_to_xy(self.reference, self.ais_ship.lat_lon) | ||
| else: | ||
| position = position_override | ||
| v1 = self.ais_ship.sog.speed * math.sin(cog_rad) | ||
| v2 = self.ais_ship.sog.speed * math.cos(cog_rad) | ||
| a, b = position | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of Magic number violations. You don't need to make all of them constants, but can you explain some of the calculations you are doing here? Wherever it makes sense to you, please repalce magic numbers with appropriately named constants.