Skip to content
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

638 feature rework entering overtake #659

Merged
merged 30 commits into from
Feb 6, 2025

Conversation

seefelke
Copy link
Collaborator

@seefelke seefelke commented Jan 26, 2025

Description

Overtake routine has been reworked to make use of intermediate layer.
Overtake obstacles are now detected based on the trajectory. If the nearest obstacle in x-direction is not moving,
a counter gets incremented. Once this exceeds a threshold, the Overtake behaviour starts.
Executing the overtake is now only allowed if left lane is free. Also based on a counter.

While in Approach the car gets slowed down and checks for a free lane. If it's free before we reach the
obstacle, wait gets skipped and the Overtake execution begins.
Wait handles standing in front of the obstacle and wating for a free lane.

A marker for visualizing the nearest entity on the trajectory in Overtake Ahead has been added as well.

Fixes # 638

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)

Does this PR introduce a breaking change?

No

Most important changes

Overtake Ahead, Approach and Wait were mainly changed by this.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works (might be obsolete with CI later on)
  • New and existing unit tests pass locally with my changes (might be obsolete with CI later on)

Summary by CodeRabbit

  • New Features

    • Enhanced overtaking behavior with improved decision-making logic.
    • Added new distance tracking and visualization for overtaking maneuvers.
    • Introduced more sophisticated obstacle detection and trajectory assessment.
    • Expanded conditions for resetting the stuck timer with additional behaviors.
    • New utility functions for behavior trees to enhance visualization and data retrieval.
  • Bug Fixes

    • Refined overtaking conditions to improve safety and effectiveness.
    • Updated motion planning logic to better handle overtaking scenarios.
    • Adjusted speed behavior for waiting during overtaking.
  • Chores

    • Updated constants related to motion planning.
    • Restructured behavior tree to improve clarity of overtaking logic.

@seefelke seefelke added this to the Sprint 05 2025-01-27 milestone Jan 26, 2025
@seefelke seefelke self-assigned this Jan 26, 2025
@seefelke seefelke linked an issue Jan 26, 2025 that may be closed by this pull request
Copy link
Contributor

coderabbitai bot commented Jan 26, 2025

Walkthrough

The pull request introduces comprehensive modifications to the overtaking behavior in the robotic system. The changes span multiple files in the planning and local planner modules, focusing on enhancing the decision-making process for overtaking maneuvers. The modifications include restructuring the behavior tree, refining overtaking logic, adding distance and marker publishers, and updating motion planning calculations to support more sophisticated overtaking strategies.

Changes

File Changes
code/planning/src/behavior_agent/behavior_tree.py Added new Selector node labeled "Overtaking" with a Sequence node for overtaking actions. Renamed existing Sequence node from "Overtaking" to "Overtake".
code/planning/src/behavior_agent/behaviors/overtake.py - Added publishers for overtaking distance and markers
- Overhauled overtaking condition logic
- Improved error handling and decision-making process
code/planning/src/local_planner/motion_planning.py - Added new subscriber for overtaking distance
- Updated trajectory calculation methods
- Modified overtaking state determination logic
code/planning/src/local_planner/utils.py - Updated TARGET_DISTANCE_TO_STOP constant
- Added TARGET_DISTANCE_TO_STOP_OVERTAKE constant
- Introduced convert_pose_to_array function
code/planning/src/behavior_agent/behaviors/behavior_speed.py Modified ot_wait_stopped behavior speed from calculated value to fixed value of 3.0.
code/planning/src/behavior_agent/behaviors/behavior_utils.py Introduced utility functions and constants for behavior trees, including get_hero_width and get_marker_arr_in_front.
code/planning/src/behavior_agent/behaviors/unstuck_routine.py Updated wait_behaviors list to include two new behaviors, expanding conditions for resetting the stuck timer.

Possibly related issues

Poem

🚗 Behold, a rabbit's overtaking tale,
Where bits and bytes now smoothly sail,
Through lanes of code with precision bright,
Our robotic friend takes flight just right! 🐰
Overtaking magic, now crystal clear! 🌟

Tip

🌐 Web search-backed reviews and chat
  • We have enabled web search-based reviews and chat for all users. This feature allows CodeRabbit to access the latest documentation and information on the web.
  • You can disable this feature by setting web_search: false in the knowledge_base settings.
  • Please share any feedback in the Discord discussion.
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@seefelke seefelke marked this pull request as draft January 26, 2025 21:00
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (18)
code/planning/src/behavior_agent/behaviors/leave_parking_space.py (3)

7-10: New import usage and commented-out import.

Good addition of the Map import; it is indeed used below to check lane availability. Since # from mapping import msg is now commented out, consider removing it if it's no longer needed to keep the codebase clean.

-# from mapping import msg

112-113: Clarifying comment about routine start.

This comment helps readers understand the sequential flow of the logic. Keep the docstring updated so it aligns well with these in-line details.


114-128: Safe lane-free check before unparking.

Invoking Map.from_ros_msg(map_data) and map.is_lane_free(...) is a good addition to ensure the left lane is actually free. Consider parameterizing lane_length=22.5 and lane_transform=-5.0 or referencing them in a config file to make adjustments easier.

code/planning/src/behavior_agent/behaviors/overtake.py (12)

6-6: Unused import math.

According to static analysis, math is never used. Consider removing it to keep the codebase lean.

-import math
🧰 Tools
🪛 Ruff (0.8.2)

6-6: math imported but unused

Remove unused import: math

(F401)


7-7: Unused import Rotation.

scipy.spatial.transform.Rotation is not used. Remove it to clean up.

-from scipy.spatial.transform import Rotation
🧰 Tools
🪛 Ruff (0.8.2)

7-7: scipy.spatial.transform.Rotation imported but unused

Remove unused import: scipy.spatial.transform.Rotation

(F401)


12-12: Unused import convert_to_ms.

This function is never called. Consider removing it.

-    convert_to_ms,
🧰 Tools
🪛 Ruff (0.8.2)

12-12: local_planner.utils.convert_to_ms imported but unused

Remove unused import: local_planner.utils.convert_to_ms

(F401)


16-16: Unused import Polygon.

Polygon is not used. Remove it to adhere to standard linting guidelines.

-from mapping_common.shape import Polygon
🧰 Tools
🪛 Ruff (0.8.2)

16-16: mapping_common.shape.Polygon imported but unused

Remove unused import: mapping_common.shape.Polygon

(F401)


100-105: Null-check for map data in Overtake.

Failing immediately on missing map data has clear semantics. Though this ensures safety, consider fallback strategies if your system can operate in partial functionality without a full map.


145-149: Filtering out false positives.

Relying on lateral position thresholds (-2.5, 2.5) to disregard irrelevant objects can be effective, but might lead to missing certain edge cases. Confirm these boundary values align with your lane width specifications or real-world constraints.


152-161: Marker publishing with short lifetime.

The 0.3-second lifetime is good for ephemeral markers. If debugging or visualizing real-time data at lower framerates, consider whether a slightly longer lifetime might be more user-friendly.


162-170: Counter-based approach for positive overtake detection.

Adding a tolerance (counter > 4) helps filter out transient obstacles or noise. Consider documenting these magic numbers or making them configurable to accommodate different traffic conditions.


241-242: Additional state tracking for approach.

self.ot_counter and self.clear_distance contribute to the logic. Be sure to provide a clear definition or docstring about how these thresholds are derived.


265-269: Early failure on missing map data.

As with the Ahead class, a graceful fallback approach or an alternative behavior might be worth considering if partial operation is acceptable.


307-326: Oncoming lane check before overtaking.

Good logic to slow down if is_lane_free(False, ...) returns false. If you see repeated patterns for obtaining map status in multiple behaviors, consider extracting them into a shared helper.


392-394: New publisher for overtake distance in Wait.

Another instance of the same pattern. Reusing or centralizing these publisher setups across the three classes might reduce duplication.

I can help refactor this into a shared utility if you’d like. Let me know if you want a new issue created.

code/mapping/ext_modules/mapping_common/map.py (2)

109-172: Consider making lane dimensions configurable.

The method is well-implemented with proper input validation and clear documentation. However, the lane width (1.5m) and offset (2.2m) are hardcoded, which might not be suitable for all vehicle types or road configurations.

Consider making these dimensions configurable:

     def is_lane_free(
         self,
         right_lane: bool = False,
         lane_length: float = 20.0,
         lane_transform: float = 0.0,
+        lane_width: float = 1.5,
+        lane_offset: float = 2.2,
     ) -> bool:
         # ...
         lane_box_shape = Rectangle(
             length=lane_length,
-            width=1.5,
+            width=lane_width,
             offset=Transform2D.new_translation(
-                Vector2.new(lane_transform, lane_pos * 2.2)
+                Vector2.new(lane_transform, lane_pos * lane_offset)
             ),
         )
🧰 Tools
🪛 Ruff (0.8.2)

170-172: Return the condition bool(not lane_box_intersection_entities) directly

Replace with return bool(not lane_box_intersection_entities)

(SIM103)


Line range hint 175-186: Enhance return type documentation.

While the parameter documentation has been improved, the return type documentation could be more detailed.

Consider updating the return type documentation:

         Returns:
-        - Polygon: A Shapely Polygon representing the plane.
+        - shapely.geometry.Polygon: A rectangular polygon representing the projected
+          plane, with coordinates relative to the hero car's position.
🧰 Tools
🪛 Ruff (0.8.2)

170-172: Return the condition bool(not lane_box_intersection_entities) directly

Replace with return bool(not lane_box_intersection_entities)

(SIM103)

code/planning/src/local_planner/motion_planning.py (1)

530-533: Add validation in the setter method.

The setter should validate the input data before assignment:

 def __set_ot_distance(self, data: Float32):
     if data is not None:
-        self.__ot_distance = data.data
+        if data.data >= 0:
+            self.__ot_distance = data.data
+        else:
+            self.logwarn(f"Invalid overtake distance: {data.data}")
+            self.__ot_distance = float('inf')
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f173849 and a302b18.

📒 Files selected for processing (8)
  • code/mapping/ext_modules/mapping_common/map.py (5 hunks)
  • code/mapping/src/mapping_data_integration.py (1 hunks)
  • code/planning/src/behavior_agent/behavior_tree.py (1 hunks)
  • code/planning/src/behavior_agent/behaviors/leave_parking_space.py (4 hunks)
  • code/planning/src/behavior_agent/behaviors/overtake.py (11 hunks)
  • code/planning/src/behavior_agent/behaviors/topics2blackboard.py (2 hunks)
  • code/planning/src/local_planner/motion_planning.py (9 hunks)
  • code/planning/src/local_planner/utils.py (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • code/mapping/src/mapping_data_integration.py
🧰 Additional context used
🪛 Ruff (0.8.2)
code/mapping/ext_modules/mapping_common/map.py

14-14: Redefinition of unused Transform2D from line 12

Remove definition: Transform2D

(F811)


170-172: Return the condition bool(not lane_box_intersection_entities) directly

Replace with return bool(not lane_box_intersection_entities)

(SIM103)

code/planning/src/behavior_agent/behaviors/overtake.py

6-6: math imported but unused

Remove unused import: math

(F401)


7-7: scipy.spatial.transform.Rotation imported but unused

Remove unused import: scipy.spatial.transform.Rotation

(F401)


12-12: local_planner.utils.convert_to_ms imported but unused

Remove unused import: local_planner.utils.convert_to_ms

(F401)


16-16: mapping_common.shape.Polygon imported but unused

Remove unused import: mapping_common.shape.Polygon

(F401)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image
🔇 Additional comments (21)
code/planning/src/behavior_agent/behaviors/leave_parking_space.py (6)

28-29: More expressive state management.

Replacing self.called with self.started and self.finished is a good approach to better reflect the behavior’s lifecycle and improve clarity.


92-93: Retrieving map data from blackboard.

Fetching map_data from the blackboard aligns with the existing architecture. Confirm that the publisher for "/paf/hero/mapping/init_data" is reliably providing data in all scenarios to prevent potential blocking conditions downstream.


94-94: Conditional check for the ‘finished’ state.

This additional guard prevents re-running the leaving-parking process after completion. Good defensive approach to avoid repeated actions.


100-100: Null-check for map data.

Validating map_data ensures correct usage of the mapping logic. This avoids potential NoneType errors. Good job on defensive coding.


129-136: Return status usage might be confusing.

Marking the behavior as FAILURE once the vehicle moves sufficiently seems counterintuitive if “finished leaving” would normally imply success. Ensure that using FAILURE here won’t cause undesired logic in parent nodes (e.g., fallback or parallel behaviors).


138-139: Continuing in RUNNING state.

This fallback to a RUNNING state is clear, ensuring the behavior remains active. Confirm that once the lane is blocked again or the flow changes, further re-checks are triggered as intended.

code/planning/src/behavior_agent/behaviors/overtake.py (7)

2-2: Useful imports for new messages.

The addition of Float32 helps in publishing overtaking distance. Keep the usage of all new imports in sync with your data flows.


30-30: Clarification of global variable usage.

OVERTAKE_EXECUTING is set to zero here as a placeholder. Ensure that usage across classes is thread-safe and consistent in a multithreaded environment, if applicable.


63-68: Publishing overtaking distance and markers.

Great idea to have separate publishers for distance and visualization. Validate that the topics are properly handled in your ROS environment, and consider logging any exceptions if these publishers fail to connect.


81-81: Old obstacle distance tracking.

self.old_obstacle_distance = 200 is used for referencing the previous distance. If you plan to handle incremental logic based on changes in distance, ensure there’s a plan to compare or reset it appropriately.


108-128: Trajectory-based obstacle detection.

Using map.get_obstacle_on_trajectory(...) significantly improves the decision-making process. Confirm that the friction model or environmental factors don’t require additional logic for edge cases (e.g., partial occlusion or static obstacles not on the exact path).


223-225: New publisher for overtaking distance.

Consistent pattern with the Ahead class. Keep an eye on iteration or duplication across classes that might indicate a shared utility for publishing overtake data.


408-409: Clean separation of counters and distances.

self.clear_distance and self.ot_counter keep the wait logic consistent. Consider verifying these thresholds match real-world driving constraints (lane widths, safe distances, etc.).

code/planning/src/behavior_agent/behaviors/topics2blackboard.py (1)

109-118: New topics for trajectory and current waypoint.

Adding these topics extends the blackboard with essential planning data. This integration is important for advanced maneuvers. Ensure test coverage includes validating that these topics correctly populate the blackboard and remain in sync with the map data usage.

code/planning/src/behavior_agent/behavior_tree.py (1)

Line range hint 79-92: LGTM! Well-structured behavior tree modification.

The new "Overtaking" Selector node follows the established pattern and maintains consistency with other behavior implementations like lane switching. This structure aligns well with the PR's objective of reworking the overtake routine.

code/planning/src/local_planner/utils.py (2)

16-16: Verify the shorter stopping distance for overtaking.

The overtaking stopping distance (3.5m) is significantly shorter than the regular stopping distance (5m). While this allows for more aggressive overtaking maneuvers, ensure this shorter distance has been validated for safety across different speeds and conditions.


246-260: LGTM! Well-structured utility function.

The implementation is clean and efficient:

  • Good use of numpy for array operations
  • Clear type hints and docstring
  • Proper error handling through numpy's built-in checks
code/planning/src/local_planner/motion_planning.py (4)

117-122: LGTM! Well-structured subscriber setup.

The new overtake distance subscriber follows the established pattern:

  • Consistent topic naming convention
  • Proper QoS profile
  • Type safety with Float32

512-516: Verify overtake status handling.

The code checks for infinite distance before trajectory change but doesn't validate other edge cases:

  • What happens if distance is negative?
  • What happens if distance is zero?

699-700: LGTM! Clear virtual overtake calculation.

The change from collision point to overtake distance improves the clarity of the code's intent.


Line range hint 283-298: Verify the trajectory generation parameters.

The function uses hardcoded values that might need adjustment:

  • normal_x_offset = 2 for regular overtaking
  • unstuck_x_offset = 3 for unstuck scenarios

Run the following script to check if these values are consistent with other trajectory calculations in the codebase:

✅ Verification successful

Trajectory generation parameters are consistently implemented

The offset values are used appropriately for trajectory generation:

  • Values are consistently applied throughout the motion planning system
  • Implementation properly handles both regular overtaking (2m) and unstuck scenarios (3m)
  • Comment about potential adjustment with better steering shows awareness of the parameter's impact
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other trajectory offset calculations
rg -A 3 'x_offset.*=.*[23]' --type py

Length of output: 540


Script:

#!/bin/bash
# Search for trajectory-related parameters and their usage
echo "=== Searching for trajectory and offset parameters ==="
rg -g '*.py' -g '*.yaml' -g '*.json' -i 'offset.*=|trajectory.*param|NUM_WAYPOINTS' -A 2

echo -e "\n=== Searching for configuration files ==="
fd -e yaml -e json

echo -e "\n=== Checking how these offsets are used ==="
ast-grep --pattern 'normal_x_offset|unstuck_x_offset' -l python

Length of output: 10839

code/mapping/ext_modules/mapping_common/map.py Outdated Show resolved Hide resolved
code/mapping/ext_modules/mapping_common/map.py Outdated Show resolved Hide resolved
@seefelke seefelke marked this pull request as ready for review January 28, 2025 17:50
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (9)
code/planning/src/local_planner/motion_planning.py (4)

Line range hint 283-297: Improve readability of trajectory selection logic.

The selection of trajectory points for overtaking could be made more readable by extracting the index calculations into named variables.

Consider refactoring like this:

-            selection = pose_list[
-                int(currentwp)
-                + min(2, int(distance / 2)) : int(currentwp)
-                + int(distance)
-                + NUM_WAYPOINTS
-                + 2
-            ]
+            start_idx = int(currentwp) + min(2, int(distance / 2))
+            end_idx = int(currentwp) + int(distance) + NUM_WAYPOINTS + 2
+            selection = pose_list[start_idx:end_idx]

513-517: Add error logging for invalid overtake distance.

When the overtake distance is infinite, it would be helpful to log this condition for debugging purposes.

Add error logging:

             if np.isinf(self.__ot_distance):
+                self.logwarn("Overtake distance is infinite, aborting trajectory change")
                 self.__overtake_status = -1
                 self.overtake_success_pub.publish(self.__overtake_status)
                 return

675-676: Document the speed calculation constants.

The magic numbers 9.0 and 1.4 in the speed calculation should be documented to explain their significance.

Add comments explaining the constants:

-        v_stop = min(convert_to_ms(9.0), convert_to_ms(stopline / 1.4))
+        # Maximum speed during overtake approach is 9.0 m/s
+        # Deceleration factor of 1.4 provides smooth braking
+        v_stop = min(convert_to_ms(9.0), convert_to_ms(stopline / 1.4))

700-701: Consider adding a buffer constant.

The magic number 2.5 used as a buffer distance should be defined as a constant at the module level.

Define the buffer as a constant:

+# Buffer distance in meters to maintain from the obstacle during overtake
+OVERTAKE_BUFFER_DISTANCE = 2.5

     def __calc_virtual_overtake(self) -> float:
         if (self.__ot_distance is not None) and self.__ot_distance != np.inf:
-            return self.__ot_distance - 2.5
+            return self.__ot_distance - OVERTAKE_BUFFER_DISTANCE
code/planning/src/behavior_agent/behaviors/overtake.py (5)

26-28: Consider replacing global variables with class-level state management.

Using global variables for state management can lead to thread-safety issues and make the code harder to test. Consider encapsulating these states within a dedicated class or using the behavior tree's blackboard for state management.

-# Variable to determine the distance to overtake the object
-OVERTAKE_EXECUTING = 0
-OVERTAKE_FREE = False
+class OvertakeState:
+    def __init__(self):
+        self.executing_distance = 0
+        self.is_free = False
+
+    @classmethod
+    def get_instance(cls):
+        if not hasattr(cls, '_instance'):
+            cls._instance = cls()
+        return cls._instance

167-168: Extract magic numbers into named constants.

The counter threshold of 4 is hardcoded. Consider extracting it into a named constant for better maintainability and documentation.

+# Number of consecutive detections required to confirm overtake situation
+OVERTAKE_CONFIRMATION_THRESHOLD = 4

-            if self.counter_overtake > 4:
+            if self.counter_overtake > OVERTAKE_CONFIRMATION_THRESHOLD:

246-246: Extract magic number into a named constant.

The clear_distance value of 40 is hardcoded. Consider extracting it into a named constant at the module level.

+# Distance to check for clear path during overtake approach
+CLEAR_PATH_CHECK_DISTANCE = 40

-        self.clear_distance = 40
+        self.clear_distance = CLEAR_PATH_CHECK_DISTANCE

429-430: Extract magic number into a named constant.

The clear_distance value of 35 is hardcoded. Consider extracting it into a named constant at the module level.

+# Distance to check for clear path during wait phase
+WAIT_CLEAR_PATH_CHECK_DISTANCE = 35

-        self.clear_distance = 35
+        self.clear_distance = WAIT_CLEAR_PATH_CHECK_DISTANCE

Line range hint 606-685: LGTM with a minor note.

The Leave class implementation is clean, but it still relies on the global OVERTAKE_EXECUTING variable. Consider refactoring this as suggested earlier to use class-level state management.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a302b18 and 31e5845.

📒 Files selected for processing (3)
  • code/planning/src/behavior_agent/behaviors/overtake.py (11 hunks)
  • code/planning/src/local_planner/motion_planning.py (9 hunks)
  • code/planning/src/local_planner/utils.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/planning/src/local_planner/utils.py
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image
🔇 Additional comments (2)
code/planning/src/local_planner/motion_planning.py (1)

22-22: LGTM! Import added for new constant.

The addition of TARGET_DISTANCE_TO_STOP_OVERTAKE is consistent with the PR's objective to enhance overtaking behavior.

code/planning/src/behavior_agent/behaviors/overtake.py (1)

Line range hint 531-605: LGTM!

The Enter class implementation is clean and well-structured.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
code/planning/src/behavior_agent/behaviors/overtake.py (3)

28-30: Consider encapsulating global state in a dedicated class.

Global variables OVERTAKE_EXECUTING and OVERTAKE_FREE could lead to thread safety issues and make state management difficult to track. Consider encapsulating these in a dedicated state management class.

-OVERTAKE_EXECUTING = 0
-OVERTAKE_FREE = False
+class OvertakeState:
+    def __init__(self):
+        self._executing = 0
+        self._free = False
+
+    @property
+    def executing(self):
+        return self._executing
+
+    @executing.setter
+    def executing(self, value):
+        self._executing = value
+
+    @property
+    def free(self):
+        return self._free
+
+    @free.setter
+    def free(self, value):
+        self._free = value
+
+overtake_state = OvertakeState()

324-341: Simplify lane clearance logic and extract counter mechanism.

The lane clearance logic with counter could be simplified and extracted into a helper method for better readability and reusability.

+    def _is_lane_consistently_free(self, map_instance, clear_distance=15):
+        """Check if the lane is consistently free over multiple checks."""
+        if map_instance.is_lane_free(False, self.clear_distance, clear_distance):
+            self.ot_counter += 1
+            return self.ot_counter > 3
+        self.ot_counter = 0
+        return False

-        if self.ot_distance < 15.0:
-            rospy.loginfo(f"Overtake Approach Distance: {self.ot_distance}")
-            ot_free = map.is_lane_free(False, self.clear_distance, 15)
-            rospy.loginfo(f"Overtake is free: {ot_free}")
-            if ot_free:
-                self.ot_counter += 1
-                # using a counter to account for inconsistencies
-                if self.ot_counter > 3:
-                    rospy.loginfo("Overtake is free not slowing down!")
-                    self.ot_distance_pub.publish(self.ot_distance)
-                    self.curr_behavior_pub.publish(bs.ot_app_free.name)
-                    # bool to skip Wait since oncoming is free
-                    OVERTAKE_FREE = True
-                    return py_trees.common.Status.SUCCESS
-                else:
-                    self.ot_distance_pub.publish(self.ot_distance)
-                    self.curr_behavior_pub.publish(bs.ot_app_blocked.name)
-                    return py_trees.common.Status.RUNNING
+        if self.ot_distance < 15.0:
+            rospy.loginfo(f"Overtake Approach Distance: {self.ot_distance}")
+            if self._is_lane_consistently_free(map):
+                rospy.loginfo("Overtake is free not slowing down!")
+                self.ot_distance_pub.publish(self.ot_distance)
+                self.curr_behavior_pub.publish(bs.ot_app_free.name)
+                OVERTAKE_FREE = True
+                return py_trees.common.Status.SUCCESS
+            self.ot_distance_pub.publish(self.ot_distance)
+            self.curr_behavior_pub.publish(bs.ot_app_blocked.name)
+            return py_trees.common.Status.RUNNING

Line range hint 31-627: Create a base behavior class for common functionality.

Multiple behavior classes share common functionality such as:

  • Map integration
  • Data preparation
  • Counter mechanisms
  • Publisher setup
  • Marker visualization

Consider creating a base behavior class to reduce code duplication and improve maintainability.

class BaseOvertakeBehavior(py_trees.behaviour.Behaviour):
    """Base class for overtaking behaviors."""
    
    def setup(self, timeout):
        """Common setup for all overtaking behaviors."""
        self.blackboard = py_trees.blackboard.Blackboard()
        self._setup_publishers()
        return True
    
    def _setup_publishers(self):
        """Setup common publishers."""
        self.curr_behavior_pub = rospy.Publisher(
            "/paf/hero/curr_behavior", String, queue_size=1
        )
        self.ot_distance_pub = rospy.Publisher(
            "/paf/hero/overtake_distance", Float32, queue_size=1
        )
        self.ot_marker_pub = rospy.Publisher(
            "/paf/hero/overtake_marker", Marker, queue_size=1
        )
    
    def _prepare_data(self):
        """Common data preparation logic."""
        trajectory = self.blackboard.get("/paf/hero/trajectory")
        current_wp = self.blackboard.get("/paf/hero/current_wp")
        hero_pos = self.blackboard.get("/paf/hero/current_pos")
        hero_heading = self.blackboard.get("/paf/hero/current_heading")
        
        if all(x is not None for x in [trajectory, current_wp, hero_pos, hero_heading]):
            return self._process_trajectory_data(trajectory, current_wp, hero_pos, hero_heading)
        return None, None, None, None
    
    def _process_trajectory_data(self, trajectory, current_wp, hero_pos, hero_heading):
        """Process trajectory data."""
        hero_heading = hero_heading.data
        current_wp = current_wp.data
        hero_pos = (hero_pos.pose.position.x, hero_pos.pose.position.y)
        return trajectory, current_wp, hero_pos, hero_heading
    
    def _publish_marker(self, entity):
        """Common marker visualization logic."""
        if entity is not None:
            ot_marker = entity.to_marker()
            ot_marker.color.r = 1.0
            ot_marker.color.g = 0
            ot_marker.color.b = 0
            ot_marker.header.frame_id = "hero"
            ot_marker.header.stamp = rospy.Time.now()
            ot_marker.lifetime = rospy.Duration(0.3)
            self.ot_marker_pub.publish(ot_marker)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31e5845 and 677bb7d.

📒 Files selected for processing (1)
  • code/planning/src/behavior_agent/behaviors/overtake.py (11 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image
🔇 Additional comments (2)
code/planning/src/behavior_agent/behaviors/overtake.py (2)

153-161: Add null check before accessing entity for visualization.

The code assumes entity is not None when generating the visualization marker. Add a null check to prevent potential crashes.


462-489: Extract duplicate data preparation logic into a helper function.

This data preparation code is duplicated across multiple classes. Consider extracting it into a helper function to improve maintainability.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
code/planning/src/local_planner/motion_planning.py (1)

117-122: ⚠️ Potential issue

Fix the topic name format.

The topic name is missing a leading forward slash which is inconsistent with other topic names in the file.

Apply this diff to fix the topic name format:

-            f"paf/{self.role_name}/overtake_distance",
+            f"/paf/{self.role_name}/overtake_distance",
🧹 Nitpick comments (10)
code/planning/src/local_planner/utils.py (2)

15-15: Consider using float for distance constants.

While functionally equivalent, using 5.0 instead of 5 would be more consistent with the floating-point nature of distance measurements.

-TARGET_DISTANCE_TO_STOP = 5
+TARGET_DISTANCE_TO_STOP = 5.0

246-260: Consider optimizing the pose conversion.

While the implementation is correct, it could be improved in terms of efficiency and robustness.

Consider these improvements:

  1. Add input validation
  2. Use numpy vectorization for better performance
 def convert_pose_to_array(poses: np.ndarray) -> np.ndarray:
     """Convert an array of PoseStamped objects to a numpy array of positions.
 
     Args:
         poses (np.ndarray): Array of PoseStamped objects.
 
     Returns:
         np.ndarray: Numpy array of shape (n, 2) containing the x and y positions.
     """
+    if not poses or len(poses) == 0:
+        return np.empty((0, 2))
+
-    result_array = np.empty((len(poses), 2))
-    for pose in range(len(poses)):
-        result_array[pose] = np.array(
-            [poses[pose].pose.position.x, poses[pose].pose.position.y]
-        )
-    return result_array
+    return np.array([[pose.pose.position.x, pose.pose.position.y] for pose in poses])
code/planning/src/behavior_agent/behaviors/behavior_speed.py (1)

71-71: Consider using the conversion function for consistency.

The speed value should use the convert_to_ms() function for consistency with other behaviors. Also, consider adding a comment explaining the rationale for choosing 3.0 m/s.

-ot_wait_stopped = Behavior("ot_wait_stopped", 3.0)
+# Maintain a slow speed while waiting to check for overtaking opportunities
+ot_wait_stopped = Behavior("ot_wait_stopped", convert_to_ms(10.8))  # 3.0 m/s = 10.8 km/h
code/planning/src/behavior_agent/behaviors/behavior_utils.py (1)

32-43: Complete the return type in docstring.

The docstring is missing the return type specification.

     Returns:
-        _type_: _description_
+        MarkerArray: Visualization marker array containing text and shape markers
code/planning/src/behavior_agent/behaviors/unstuck_routine.py (1)

157-163: Add a comment explaining the wait behaviors list expansion.

Consider adding a comment explaining why int_app_init and int_app_to_stop are considered wait behaviors, as this affects the stuck timer reset logic.

 wait_behaviors = [
     bs.int_wait.name,
     bs.lc_wait.name,
     bs.ot_wait_stopped,
+    # Include initialization and stopping behaviors as they represent intentional waiting states
     bs.int_app_init,
     bs.int_app_to_stop,
 ]
code/planning/src/behavior_agent/behaviors/overtake.py (4)

2-26: Consider organizing imports by category.

The imports could be better organized by grouping standard library, ROS, and local imports.

-import sys
-import os
-from std_msgs.msg import String, Float32
-
-import rospy
-import numpy as np
-
-from behaviors import behavior_speed as bs
-import mapping_common.map
-import mapping_common.mask
-import mapping_common.entity
-from mapping_common.map import Map
-import shapely
-from mapping_common.entity import FlagFilter
-from mapping_common.entity import Pedestrian
-from visualization_msgs.msg import Marker, MarkerArray
-import behaviors.behavior_utils
-
-sys.path.append(os.path.abspath(sys.path[0] + "/.."))
-# from behavior_utils import get_hero_width, get_marker_arr_in_front
-from local_planner.utils import (
-    NUM_WAYPOINTS,
-    TARGET_DISTANCE_TO_STOP_OVERTAKE,
-)  # type: ignore # noqa: E402
+# Standard library imports
+import os
+import sys
+
+# ROS imports
+import rospy
+import numpy as np
+from std_msgs.msg import String, Float32
+from visualization_msgs.msg import Marker, MarkerArray
+
+# Local imports
+from behaviors import behavior_speed as bs
+import behaviors.behavior_utils
+import mapping_common.map
+import mapping_common.mask
+import mapping_common.entity
+from mapping_common.map import Map
+import shapely
+from mapping_common.entity import FlagFilter, Pedestrian
+
+# Add local planner to path
+sys.path.append(os.path.abspath(sys.path[0] + "/.."))
+from local_planner.utils import (
+    NUM_WAYPOINTS,
+    TARGET_DISTANCE_TO_STOP_OVERTAKE,
+)  # type: ignore # noqa: E402

107-111: Combine nested if statements.

The nested if statements can be combined using and for better readability.

-        wp = self.blackboard.get("/paf/hero/waypoint_distance")
-        if wp is not None:
-            if wp.isStopLine and wp.distance < 20:
-                rospy.logerr("Not overtaking in intersection!")
-                return py_trees.common.Status.FAILURE
+        wp = self.blackboard.get("/paf/hero/waypoint_distance")
+        if wp is not None and wp.isStopLine and wp.distance < 20:
+            rospy.logerr("Not overtaking in intersection!")
+            return py_trees.common.Status.FAILURE
🧰 Tools
🪛 Ruff (0.8.2)

107-108: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)


206-214: Add a constant for the speed and counter thresholds.

The speed (2.5) and counter (4) thresholds are hardcoded. Consider extracting them as named constants.

+# Constants for overtake detection
+OBSTACLE_SPEED_THRESHOLD = 2.5  # m/s
+OBSTACLE_DISTANCE_THRESHOLD = 15.0  # meters
+OVERTAKE_COUNTER_THRESHOLD = 4  # number of consecutive detections
+
-        if obstacle_speed < 2.5 and obstacle_distance < 15:
+        if obstacle_speed < OBSTACLE_SPEED_THRESHOLD and obstacle_distance < OBSTACLE_DISTANCE_THRESHOLD:
             self.counter_overtake += 1
             rospy.loginfo(f"Obstacle distance: {obstacle_distance}")
             rospy.loginfo("Overtake counter: " + str(self.counter_overtake))
-            if self.counter_overtake > 4:
+            if self.counter_overtake > OVERTAKE_COUNTER_THRESHOLD:
                 self.ot_distance_pub.publish(obstacle_distance)
                 return py_trees.common.Status.SUCCESS
             self.old_obstacle_distance = obstacle_distance

619-623: Consider extracting lane check parameters to constants.

The lane check parameters (0.5, 15.0, -4.0) are hardcoded. Consider extracting them as named constants for better maintainability.

+# Constants for lane checks
+OBSTACLE_STATIONARY_SPEED = 0.5  # m/s
+LANE_CHECK_DISTANCE_STATIONARY = 15.0  # meters
+LANE_CHECK_DISTANCE_MOVING = -4.0  # meters
+
-        if obstacle_speed < 0.5:
-            ot_free = tree.is_lane_free(False, self.clear_distance, 15.0)
+        if obstacle_speed < OBSTACLE_STATIONARY_SPEED:
+            ot_free = tree.is_lane_free(False, self.clear_distance, LANE_CHECK_DISTANCE_STATIONARY)
         else:
-            ot_free = tree.is_lane_free(False, 15.0, -4.0)
+            ot_free = tree.is_lane_free(False, LANE_CHECK_DISTANCE_STATIONARY, LANE_CHECK_DISTANCE_MOVING)
code/planning/src/local_planner/motion_planning.py (1)

513-520: Improve error handling for overtake initialization.

The code checks for infinite distance but doesn't validate other edge cases.

Consider adding validation for:

  1. Negative distances
  2. Zero distances
  3. NaN values
     if data.data == bs.ot_enter_init.name:
-        if np.isinf(self.__ot_distance):
+        if np.isinf(self.__ot_distance) or np.isnan(self.__ot_distance) or self.__ot_distance <= 0:
             self.__overtake_status = -1
             self.overtake_success_pub.publish(self.__overtake_status)
             return
         self.change_trajectory(self.__ot_distance)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 677bb7d and 88eea2a.

📒 Files selected for processing (6)
  • code/planning/src/behavior_agent/behaviors/behavior_speed.py (1 hunks)
  • code/planning/src/behavior_agent/behaviors/behavior_utils.py (1 hunks)
  • code/planning/src/behavior_agent/behaviors/overtake.py (11 hunks)
  • code/planning/src/behavior_agent/behaviors/unstuck_routine.py (1 hunks)
  • code/planning/src/local_planner/motion_planning.py (9 hunks)
  • code/planning/src/local_planner/utils.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
code/planning/src/behavior_agent/behaviors/overtake.py

107-108: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image
🔇 Additional comments (7)
code/planning/src/local_planner/utils.py (2)

18-18: LGTM: Increased waypoints for smoother trajectory.

Increasing the number of waypoints from 7 to 9 should provide better trajectory control during overtaking maneuvers.


15-16: Verify the stopping distance difference for overtaking.

The overtaking stopping distance (7m) is larger than the regular stopping distance (5m). While this makes sense to give more room for maneuvering during overtaking, please verify that this difference is sufficient for safe overtaking at various speeds.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Search for speed-related calculations in overtaking code
# to verify if the stopping distances are appropriate.

# Look for speed calculations in overtaking-related code
rg -A 5 "speed.*overtake|overtake.*speed" --type py

Length of output: 3226


Verification of Stopping Distance Constants and Their Use in Overtaking Logic

  • The regular stopping distance is defined as 5 m and the overtaking stopping distance is defined as 7 m in code/planning/src/local_planner/utils.py.
  • In the overtaking speed calculation (__calc_speed_to_stop_overtake in code/planning/src/local_planner/motion_planning.py), the code checks if the computed stopline is less than TARGET_DISTANCE_TO_STOP_OVERTAKE (7 m) and, if so, sets the target speed to 2.5 m/s. This indicates that a larger buffer is enforced during overtaking maneuvers.
  • The logic shows a deliberate design choice to allow an extra 2 m of stopping distance for overtaking, which is intended to provide a safety margin for maneuvering at varying speeds.
  • While the static analysis confirms that the constants are correctly used, assessing whether a 7 m threshold is physically sufficient for safe overtaking at all speeds would require empirical validation (e.g., simulation or real-world testing).
code/planning/src/behavior_agent/behaviors/behavior_utils.py (1)

14-29: LGTM! Good error handling.

The function properly handles cases where hero or motion data is unavailable, and provides a sensible minimum width of 1.0.

code/planning/src/behavior_agent/behaviors/overtake.py (1)

195-203: Add null check before accessing entity for visualization.

The code assumes entity is not None when generating the visualization marker.

code/planning/src/local_planner/motion_planning.py (3)

22-22: LGTM! New constant for overtake stopping distance.

The addition of TARGET_DISTANCE_TO_STOP_OVERTAKE aligns with the PR objectives to rework the overtaking routine.


701-704: LGTM! Improved distance calculation for overtaking.

The changes to virtual overtake calculation:

  1. Use new __ot_distance instead of collision point
  2. Add a 2.5m buffer for safety

283-300: Verify the trajectory generation parameters.

The changes to waypoint selection in the overtake trajectory generation include:

  1. Dynamic offset based on distance/2
  2. Additional buffer points (+2)

These changes could affect the smoothness of the overtaking maneuver.

Run this script to analyze the trajectory generation parameters:

code/planning/src/behavior_agent/behaviors/overtake.py Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
code/planning/src/behavior_agent/behaviors/overtake.py (3)

34-34: Consider replacing global variable with class property.

Using global variables for state management can lead to maintainability issues and make the code harder to test. Consider encapsulating OVERTAKE_FREE within a class or using a shared state object.


197-198: Extract magic numbers into named constants.

The speed threshold (2.5) and distance threshold (15) should be defined as named constants at the module level for better maintainability.

+# Constants for overtake conditions
+OBSTACLE_SPEED_THRESHOLD = 2.5  # m/s
+OBSTACLE_DISTANCE_THRESHOLD = 15.0  # meters

-        if obstacle_speed < 2.5 and obstacle_distance < 15:
+        if obstacle_speed < OBSTACLE_SPEED_THRESHOLD and obstacle_distance < OBSTACLE_DISTANCE_THRESHOLD:

618-621: Document bicycle speed thresholds and extract magic numbers.

The bicycle speed check uses undocumented magic numbers. Add comments explaining the speed thresholds and extract them into named constants.

+# Constants for bicycle handling
+BICYCLE_MIN_SPEED = 1.7  # m/s
+BICYCLE_MAX_SPEED = 2.4  # m/s
+BICYCLE_CLEAR_DISTANCE = 15.0  # meters
+BICYCLE_CLEAR_WIDTH = -4.0  # meters

-        if obstacle_speed > 1.7 and obstacle_speed < 2.4:
-            ot_free = tree.is_lane_free(False, 15.0, -4.0)
+        # Special handling for bicycles based on typical speed range
+        if obstacle_speed > BICYCLE_MIN_SPEED and obstacle_speed < BICYCLE_MAX_SPEED:
+            ot_free = tree.is_lane_free(False, BICYCLE_CLEAR_DISTANCE, BICYCLE_CLEAR_WIDTH)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 88eea2a and 4486128.

📒 Files selected for processing (1)
  • code/planning/src/behavior_agent/behaviors/overtake.py (11 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image
🔇 Additional comments (4)
code/planning/src/behavior_agent/behaviors/overtake.py (4)

186-194: Add null check before accessing entity for visualization.

The code assumes entity is not None when generating the visualization marker.


387-395: Extract duplicate marker visualization logic into a helper function.


314-364: Extract duplicate data preparation logic into a helper function.


107-113: Great improvements in error handling and logging!

The addition of detailed error logging and proper handling of edge cases (missing map data, invalid trajectory masks) improves the system's robustness and debuggability.

Also applies to: 146-148, 345-347, 572-574

Copy link
Collaborator

@Zelberor Zelberor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good so far.
As spoken the duplicated code and marker publishing might be optimized in a future pr.
Requested changes are only a few nitpicks ;)

code/planning/src/behavior_agent/behaviors/overtake.py Outdated Show resolved Hide resolved
code/planning/src/behavior_agent/behaviors/overtake.py Outdated Show resolved Hide resolved
code/planning/src/local_planner/motion_planning.py Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
code/planning/src/behavior_agent/behaviors/overtake.py (1)

16-16: Remove unused import.

The Pedestrian import is not used in the code.

Apply this diff to remove the unused import:

-from mapping_common.entity import Pedestrian
🧰 Tools
🪛 Ruff (0.8.2)

16-16: mapping_common.entity.Pedestrian imported but unused

Remove unused import: mapping_common.entity.Pedestrian

(F401)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4486128 and b1343c3.

📒 Files selected for processing (2)
  • code/planning/src/behavior_agent/behaviors/overtake.py (11 hunks)
  • code/planning/src/local_planner/motion_planning.py (9 hunks)
🧰 Additional context used
📓 Learnings (1)
code/planning/src/behavior_agent/behaviors/overtake.py (1)
Learnt from: Zelberor
PR: una-auxme/paf#659
File: code/planning/src/behavior_agent/behaviors/overtake.py:171-174
Timestamp: 2025-02-05T17:35:21.384Z
Learning: In the overtake behavior, marker generation for detected entities should be placed outside motion data checks to ensure visualization of all entities, regardless of their motion data availability.
🪛 Ruff (0.8.2)
code/planning/src/behavior_agent/behaviors/overtake.py

16-16: mapping_common.entity.Pedestrian imported but unused

Remove unused import: mapping_common.entity.Pedestrian

(F401)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image
🔇 Additional comments (3)
code/planning/src/local_planner/motion_planning.py (1)

117-122: ⚠️ Potential issue

Initialize __ot_distance in constructor.

The __ot_distance attribute is used but not initialized in the constructor.

Add initialization in the constructor:

     def __init__(self):
         # ... existing code ...
         self.unstuck_distance = None
         self.unstuck_overtake_flag = False
         self.init_overtake_pos = None
+        self.__ot_distance = np.inf  # Initialize with infinity

Likely invalid or redundant comment.

code/planning/src/behavior_agent/behaviors/overtake.py (2)

184-192: Extract duplicate marker visualization logic into a helper function.

This marker visualization code is duplicated across multiple classes.

Extract the marker generation code into a helper function:

+def publish_obstacle_marker(entity, marker_pub):
+    """Publish visualization marker for detected obstacle."""
+    if entity is not None:
+        ot_marker = entity.to_marker()
+        ot_marker.color.r = 1.0
+        ot_marker.color.g = 0
+        ot_marker.color.b = 0
+        ot_marker.header.frame_id = "hero"
+        ot_marker.header.stamp = rospy.Time.now()
+        ot_marker.lifetime = rospy.Duration(0.3)
+        marker_pub.publish(ot_marker)

-        # generate visualization marker for obstacle
-        ot_marker = entity.to_marker()
-        ot_marker.color.r = 1.0
-        ot_marker.color.g = 0
-        ot_marker.color.b = 0
-        ot_marker.header.frame_id = "hero"
-        ot_marker.header.stamp = rospy.Time.now()
-        ot_marker.lifetime = rospy.Duration(0.3)
-        self.ot_marker_pub.publish(ot_marker)
+        publish_obstacle_marker(entity, self.ot_marker_pub)

616-619: Verify bicycle handling logic.

The code uses a different distance check for bicycles (speed between 1.7 and 2.4). This seems like a special case that needs verification.

Could you confirm if this speed range is appropriate for bicycles and whether they should be handled differently from other obstacles?

Comment on lines 675 to 680
def __calc_speed_to_stop_overtake(self) -> float:
stopline = self.__calc_virtual_overtake()
v_stop = max(convert_to_ms(10.0), convert_to_ms(stopline / 0.8))
if stopline < TARGET_DISTANCE_TO_STOP:
v_stop = 0.0
v_stop = min(convert_to_ms(9.0), convert_to_ms(stopline / 1.4))
if stopline < TARGET_DISTANCE_TO_STOP_OVERTAKE:
v_stop = 2.5

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Add test coverage for overtaking speed parameters.

The changes to speed calculation include:

  1. Modified speed scaling (1.4 instead of 0.8)
  2. Different minimum speed (2.5 instead of 0)
  3. New maximum speed limit (9.0)

These parameters are crucial for safe overtaking but lack test coverage.

Run this script to check for any test coverage of these parameters:


🏁 Script executed:

#!/bin/bash
# Description: Check test coverage for overtaking speed parameters

# Search for test files that verify overtaking speeds
rg -A 5 "calc_speed_to_stop_overtake|TARGET_DISTANCE_TO_STOP_OVERTAKE" --type python

Length of output: 117


🏁 Script executed:

#!/bin/bash
# Description: Check test coverage for overtaking speed parameters in Python files using the correct file type.
rg -A 5 "calc_speed_to_stop_overtake|TARGET_DISTANCE_TO_STOP_OVERTAKE" --type py

Length of output: 4167

Missing test coverage for overtaking speed parameters.

The search results indicate that while the constant TARGET_DISTANCE_TO_STOP_OVERTAKE and the __calc_speed_to_stop_overtake method are referenced across multiple production files (e.g., in motion_planning.py and overtake.py), there is no dedicated test case or test file providing validation for these parameters. It's important to add tests that:

  • Validate the speed scaling method (using a factor of 1.4 and ensuring the min speed of 2.5 is applied properly).
  • Check that the computed speed does not exceed the converted maximum speed limit (9.0 m/s).
  • Ensure that the condition involving TARGET_DISTANCE_TO_STOP_OVERTAKE behaves as expected.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
code/planning/src/behavior_agent/behaviors/overtake.py (1)

183-191: ⚠️ Potential issue

Add null check before accessing entity for visualization.

The code assumes entity is not None when generating the visualization marker. Add a null check to prevent potential crashes.

🧹 Nitpick comments (4)
code/planning/src/behavior_agent/behaviors/overtake.py (4)

33-33: Consider using a state manager instead of global variables.

Using global variables for state management can lead to maintainability issues and make the code harder to test. Consider implementing a state manager class to handle the overtaking state.

Example implementation:

class OvertakeStateManager:
    def __init__(self):
        self._is_free = False
        self._executing = 0

    @property
    def is_free(self):
        return self._is_free

    @is_free.setter
    def is_free(self, value):
        self._is_free = value

    @property
    def executing(self):
        return self._executing

    @executing.setter
    def executing(self, value):
        self._executing = value

194-202: Consider parameterizing the counter threshold.

The counter threshold for overtake initiation is hardcoded. Consider making it configurable through ROS parameters for easier tuning.

Example implementation:

-            if self.counter_overtake > 4:
+            self.counter_threshold = rospy.get_param('~overtake_counter_threshold', 4)
+            if self.counter_overtake > self.counter_threshold:

280-281: Parameterize distance thresholds.

The clear distance threshold is hardcoded. Consider making it configurable through ROS parameters.

Example implementation:

-        self.clear_distance = 35
+        self.clear_distance = rospy.get_param('~overtake_clear_distance', 35.0)

615-618: Parameterize bicycle speed thresholds.

The bicycle speed range (1.7-2.4) is hardcoded. Consider making these thresholds configurable through ROS parameters and adding comments explaining the chosen values.

Example implementation:

-        if obstacle_speed > 1.7 and obstacle_speed < 2.4:
+        min_bicycle_speed = rospy.get_param('~min_bicycle_speed', 1.7)
+        max_bicycle_speed = rospy.get_param('~max_bicycle_speed', 2.4)
+        # Speed range typical for bicycles in urban environments
+        if obstacle_speed > min_bicycle_speed and obstacle_speed < max_bicycle_speed:
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1343c3 and c7b3487.

📒 Files selected for processing (1)
  • code/planning/src/behavior_agent/behaviors/overtake.py (11 hunks)
🧰 Additional context used
📓 Learnings (1)
code/planning/src/behavior_agent/behaviors/overtake.py (1)
Learnt from: Zelberor
PR: una-auxme/paf#659
File: code/planning/src/behavior_agent/behaviors/overtake.py:171-174
Timestamp: 2025-02-05T17:35:21.384Z
Learning: In the overtake behavior, marker generation for detected entities should be placed outside motion data checks to ensure visualization of all entities, regardless of their motion data availability.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image
🔇 Additional comments (2)
code/planning/src/behavior_agent/behaviors/overtake.py (2)

65-73: LGTM! Publishers are well-organized.

The publishers for distance and markers are properly initialized with appropriate topics and queue sizes.


394-421: LGTM! Robust lane availability check.

The implementation uses a counter to handle inconsistencies in lane availability data, which is a good practice for dealing with sensor noise and intermittent detection issues.

Copy link
Collaborator

@Zelberor Zelberor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

Still observed some edge cases which need to be improved in the future (mostly related to the lane free check), but is much better than what is currently in main.

@seefelke seefelke merged commit 057b3e3 into main Feb 6, 2025
4 checks passed
@seefelke seefelke deleted the 638-feature-rework-entering-overtake branch February 6, 2025 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

[Feature]: Rework entering Overtake
2 participants