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

688 feature add lane free check to lanechange behavior #731

Open
wants to merge 84 commits into
base: main
Choose a base branch
from

Conversation

Johannes1098
Copy link
Collaborator

@Johannes1098 Johannes1098 commented Mar 1, 2025

Description

Before changing lanes in the behavior lane change, a lane-free check should be performed.
The work started harmlessly -
BUT ended up in a complete rework of this behavior:

  • including deleting unused sub behaviors
  • changed logic of behavior in general
  • added debug messages and markers
  • detection of lane change to left or right and correct behavior due to this
  • added lane free check, added stopmarker
  • added pre lanechange before official lane change including new trajectory planning
  • including deleting overtake stop markers for not getting stuck
  • including check and handle if already on left lane cause of overtake

Fixes #688

Type of change

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

Does this PR introduce a breaking change?

Publisher "lane_change_distance" is now only "lane_change".
But changed the name everywhere (not used outside behavior tree and lane change)

Most important changes

code/perception/src/global_plan_distance_publisher.py
code/planning/src/behavior_agent/behavior_tree.py
code/planning/src/behavior_agent/behaviors/behavior_speed.py
code/planning/src/behavior_agent/behaviors/lane_change.py
code/planning/src/behavior_agent/behaviors/topics2blackboard.py
code/planning/src/local_planner/utils.py
code/perception/CMakeLists.txt
code/perception/msg/LaneChange.msg

build/agent_service.yaml

doc/general/architecture_current.md
doc/general/architecture_planned.md
doc/planning/motion_planning.md

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

    • Added a new enumeration for lane directions, enhancing lane management capabilities.
    • Introduced a new behavior for lane change scenarios, providing an additional speed state.
    • Added a new field to the LaneChange message for position data.
  • Refactor

    • Consolidated lane change actions into a single execution action for improved clarity.
    • Enhanced lane change logic and overtaking checks for increased robustness and maintainability.
    • Updated distance thresholds for stopping and lane-changing maneuvers to improve precision.
    • Updated topic names for lane change to simplify naming conventions.
  • Documentation

    • Updated system diagrams and planning guides to reflect the revised lane change topic nomenclature across various documents.

Ralf524 and others added 30 commits February 6, 2025 12:19
…ot trigger because target_velocity was not published
@Johannes1098 Johannes1098 marked this pull request as ready for review March 3, 2025 18:08
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 (6)
code/planning/src/behavior_agent/behavior_tree.py (1)

127-127: Behavior tree structure improved

Replacing metarules with rules in the root Parallel node simplifies the execution flow by directly using the rules defined earlier in the function rather than wrapping them in a meta sequence.

Note that while this change is positive, it's important to verify that this doesn't impact any functionality that might have been dependent on the meta sequence structure.

code/control/src/velocity_controller.py (1)

114-116: Moved logging message outside the condition block

The logging message for reverse driving ("VelocityController: reverse driving") appears to be outside of the FIXED_SPEED_OVERRIDE condition but should be part of the else block. This might lead to confusing logs when fixed speed mode is active.

Apply this diff to fix the logging placement:

                if self.FIXED_SPEED_OVERRIDE:
                    reverse = True
                    throttle = 1
                    brake = 0
+                    rospy.loginfo("VelocityController: reverse driving with fixed speed")
                else:
                    reverse = True
                    throttle = (-1) * self.pid_t(abs(target_velocity))
                    brake = 0
                    rospy.loginfo("VelocityController: reverse driving")
code/planning/src/behavior_agent/behaviors/unstuck_routine.py (2)

5-9: Remove unused imports to keep code clean.
According to static analysis, Tuple, Union, and ShapelyEntity are not used, so it’s best to remove them for clarity.

- from typing import Optional, Tuple, Union
+ from typing import Optional

- from mapping_common.entity import ShapelyEntity, Entity
+ from mapping_common.entity import Entity
🧰 Tools
🪛 Ruff (0.8.2)

5-5: typing.Tuple imported but unused

Remove unused import

(F401)


5-5: typing.Union imported but unused

Remove unused import

(F401)


9-9: mapping_common.entity.ShapelyEntity imported but unused

Remove unused import: mapping_common.entity.ShapelyEntity

(F401)


344-349: Combine the two branches to reduce repetition.
Since both conditions call add_speed_override(-0.05), you can merge them:

- if (
-     get_distance(self.init_pos, self.current_pos) < UNSTUCK_CLEAR_DISTANCE
- ) and not collision_detected:
-     add_speed_override(-0.05)
- elif get_distance(self.init_pos, self.current_pos) < 0.5:
-     add_speed_override(-0.05)
+ if (
+     (get_distance(self.init_pos, self.current_pos) < UNSTUCK_CLEAR_DISTANCE and not collision_detected)
+     or get_distance(self.init_pos, self.current_pos) < 0.5
+ ):
+     add_speed_override(-0.05)
+ else:
+     add_speed_override(0.001)
🧰 Tools
🪛 Ruff (0.8.2)

344-349: Combine if branches using logical or operator

Combine if branches

(SIM114)

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

69-70: Avoid using a global variable for lane-change state in multi-threaded code.
Using LANECHANGE_FREE as a global variable can cause concurrency issues if multiple agents/threads access it simultaneously. Storing lane-change states in the behavior’s blackboard or a thread-safe data structure would improve maintainability and avoid data races.


556-567: Reconsider returning Status.FAILURE on a successfully finished lane change.
When the lane change completes (line 565), the code returns Status.FAILURE. Typically, a completed lane change aligns with Status.SUCCESS. Unless there is a design reason for a “finished” lane change to signal failure, consider using:

- return debug_status(
-     self.name, Status.FAILURE, "Lane Change Change: Finished"
- )
+ return debug_status(
+     self.name, Status.SUCCESS, "Lane Change Change: Finished"
+ )
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e949a6d and 0513e83.

📒 Files selected for processing (19)
  • build/agent_service.yaml (0 hunks)
  • code/acting/src/acting/passthrough.py (2 hunks)
  • code/control/src/vehicle_controller.py (1 hunks)
  • code/control/src/velocity_controller.py (1 hunks)
  • code/mapping/ext_modules/mapping_common/map.py (1 hunks)
  • code/perception/CMakeLists.txt (2 hunks)
  • code/perception/msg/LaneChange.msg (1 hunks)
  • code/perception/src/global_plan_distance_publisher.py (4 hunks)
  • code/planning/src/behavior_agent/behavior_tree.py (2 hunks)
  • code/planning/src/behavior_agent/behaviors/behavior_speed.py (1 hunks)
  • code/planning/src/behavior_agent/behaviors/lane_change.py (5 hunks)
  • code/planning/src/behavior_agent/behaviors/overtake.py (3 hunks)
  • code/planning/src/behavior_agent/behaviors/overtake_service_utils.py (1 hunks)
  • code/planning/src/behavior_agent/behaviors/topics2blackboard.py (1 hunks)
  • code/planning/src/behavior_agent/behaviors/unstuck_routine.py (5 hunks)
  • code/planning/src/local_planner/utils.py (1 hunks)
  • doc/general/architecture_current.md (3 hunks)
  • doc/general/architecture_planned.md (1 hunks)
  • doc/planning/motion_planning.md (1 hunks)
💤 Files with no reviewable changes (1)
  • build/agent_service.yaml
✅ Files skipped from review due to trivial changes (2)
  • code/planning/src/behavior_agent/behaviors/overtake_service_utils.py
  • doc/general/architecture_planned.md
🧰 Additional context used
🪛 Ruff (0.8.2)
code/planning/src/behavior_agent/behaviors/unstuck_routine.py

5-5: typing.Tuple imported but unused

Remove unused import

(F401)


5-5: typing.Union imported but unused

Remove unused import

(F401)


9-9: mapping_common.entity.ShapelyEntity imported but unused

Remove unused import: mapping_common.entity.ShapelyEntity

(F401)


11-11: shapely imported but unused

Remove unused import: shapely

(F401)


344-349: Combine if branches using logical or operator

Combine if branches

(SIM114)

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

437-437: SyntaxError: Expected ',', found int

🪛 LanguageTool
doc/planning/motion_planning.md

[uncategorized] ~38-~38: Loose punctuation mark.
Context: ...rent waypoint. - /paf/hero/lane_change: Subscribes to the Carla Waypoint to che...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (30)
code/perception/msg/LaneChange.msg (1)

4-4: Good addition of spatial information to enhance lane change messages.

The addition of the position field using geometry_msgs/Point type enhances the message by providing explicit spatial coordinates for the lane change point. This is a logical extension that complements the existing distance field, enabling more precise navigation during lane changes.

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

36-39:

✅ Verification successful

Well-structured enum for lane change direction.

The newly added LaneFreeDirection enum provides clear semantics for lane change directions with properly named constants (LEFT and RIGHT). This improves code readability and maintains consistency throughout lane change operations.

One thing to verify: ensure that the boolean mapping (LEFT=False, RIGHT=True) is used consistently throughout the codebase to prevent potential confusion.


🏁 Script executed:

#!/bin/bash
# Search for usages of LaneFreeDirection to ensure consistent usage
rg "LaneFreeDirection\.(LEFT|RIGHT)" -A 2 -B 2

Length of output: 3175


Enum Mapping for LaneFreeDirection Verified and Consistent

The LaneFreeDirection enum defined as:

class LaneFreeDirection(Enum):
    LEFT = False
    RIGHT = True

provides clear semantics and aligns with the intended boolean mapping. A repository-wide search confirmed that the enum is used consistently in the lane change logic (e.g., in code/planning/src/behavior_agent/behaviors/lane_change.py).

Approved.

doc/planning/motion_planning.md (1)

38-38: Topic name updated correctly in documentation.

The topic name has been updated from /paf/hero/lane_change_distance to /paf/hero/lane_change to reflect the changes made throughout the codebase. This maintains consistency between the implementation and documentation.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~38-~38: Loose punctuation mark.
Context: ...rent waypoint. - /paf/hero/lane_change: Subscribes to the Carla Waypoint to che...

(UNLIKELY_OPENING_PUNCTUATION)

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

60-60:

✅ Verification successful

Appropriate addition of new lane change behavior state.

The new lc_wait_free behavior with a non-zero speed (30 km/h) complements the existing lc_wait behavior (0 speed). This appears to support the implementation of lane-free checks, allowing the vehicle to proceed at a moderate speed when the target lane is free rather than coming to a complete stop.

Note that according to the file header comments, the speed values in this file are deprecated and "completely unused in favor of the ACC" since PR #725. Verify that the behavior name itself is being properly utilized in the lane change implementation.


🏁 Script executed:

#!/bin/bash
# Search for usages of the new behavior name to ensure it's being used appropriately
rg "lc_wait_free" --type py

Length of output: 276


Verification complete: "lc_wait_free" behavior is correctly integrated.

We confirmed that the new behavior is actively used within the lane change implementation (refer to the publish call in code/planning/src/behavior_agent/behaviors/lane_change.py). The utilization aligns with the intended design despite the deprecation of speed values per the header comments.

code/control/src/vehicle_controller.py (1)

156-159:

❓ Verification inconclusive

Improved steering logic for unstuck and stop behaviors

The change inverts the steering direction during unstuck and stop behaviors, which should help the vehicle maneuver more effectively in recovery situations. This is a sensible approach as it allows the vehicle to steer in the opposite direction to get out of stuck positions.

To ensure this change doesn't cause unexpected behavior, verify this in simulation with these specific scenarios:

  1. Vehicle attempting to get unstuck from various positions
  2. Vehicle executing stop behaviors
  3. Testing that the transition between normal and unstuck/stop steering is smooth

🏁 Script executed:

#!/bin/bash
# Search for any existing tests for unstuck or stop behaviors
rg -A 3 -B 3 "us_unstuck|us_stop" --type py

Length of output: 3640


Simulation Verification Required: Confirm Unstuck & Stop Behavior Transitions

The modified steering logic (multiplying _p_steer by -1 for us_unstuck and us_stop) is implemented as intended in code/control/src/vehicle_controller.py (lines 156–159). References in related modules (e.g., in planning and behavior routines) confirm these behaviors are triggered as expected. Please ensure that simulation tests cover the following:

  • Vehicle unstuck scenarios: Validate steering inversion when the vehicle is trying to recover from being stuck.
  • Stop behavior: Confirm that the vehicle executes a controlled stopping behavior.
  • Transition smoothness: Verify that switching between normal and recovery (unstuck/stop) steering is seamless.
doc/general/architecture_current.md (2)

126-126: Topic name update reflects the changes in code

The topic rename from lane_change_distance to lane_change is consistent with other changes in the codebase. This documentation update ensures that the architecture documentation stays accurate.


262-262: Topic name update in local planning section

The topic reference has been properly updated here as well, maintaining consistency across the documentation.

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

95-95:

❓ Verification inconclusive

Topic subscription updated to match the new topic name

This change aligns the topic subscription with the renamed topic (lane_change instead of lane_change_distance), consistent with the architectural changes throughout the codebase.

To ensure the topic rename is consistent across the entire codebase:


🏁 Script executed:

#!/bin/bash
# Check if there are any remaining references to the old topic name
rg "lane_change_distance" --type py

Length of output: 35


Action: Manually Verify Topic Name Consistency

Automated searches across Python files show no lingering references to the old topic name “lane_change_distance.” However, since the initial script produced no output, please manually verify that:

  • The file code/planning/src/behavior_agent/behaviors/topics2blackboard.py (line 95) correctly uses "name": f"/paf/{role_name}/lane_change" without any residual references to “lane_change_distance.”
  • There are no occurrences of “lane_change_distance” in non-Python files (e.g., configuration, documentation, tests) that might affect the rename.

Once confirmed, you can safely assume the topic update is fully consistent.

code/perception/CMakeLists.txt (2)

17-17: Added geometry_msgs dependency to support LaneChange message enhancement

The addition of geometry_msgs as a dependency is necessary to support the new position field in the LaneChange message, which allows for spatial information to be included in lane change data.


79-79:

✅ Verification successful

Added geometry_msgs to message generation dependencies

This correctly adds geometry_msgs to the message generation dependencies, ensuring that types from this package can be used in the LaneChange message. This change complements the addition to the find_package section above.

Verify that the LaneChange.msg file has been updated to include a position field of type geometry_msgs/Point:


🏁 Script executed:

#!/bin/bash
# Check the LaneChange.msg file for the position field
cat code/perception/msg/LaneChange.msg

Length of output: 120


Review Confirmed: Geometry_msgs Dependency and Message Field Verification

The changes correctly add the geometry_msgs dependency, and the LaneChange.msg file now includes the position field of type geometry_msgs/Point as expected.

  • Verified in code/perception/msg/LaneChange.msg that the line geometry_msgs/Point position exists.
  • This update complements the changes made in CMakeLists.txt and the find_package additions.
code/planning/src/behavior_agent/behavior_tree.py (1)

83-83:

✅ Verification successful

Lane change behavior has been simplified

The change from separate Enter/Leave actions to a single Change action simplifies the lane change process. This consolidation aligns with the PR objective to rework the lane change behavior.


🏁 Script executed:

#!/bin/bash
# Description: Check for implementation of lane_change.Change class and its usage

# Find the implementation of the Change class in lane_change.py
rg -A 10 "class Change" code/planning/src/behavior_agent/behaviors/lane_change.py

Length of output: 395


Validated: Lane Change Behavior Simplification

The usage of lane_change.Change("Execute Change") in the behavior tree now aligns with the intended simplified lane change process. The implementation in code/planning/src/behavior_agent/behaviors/lane_change.py confirms that the Change class is correctly defined, inheriting from py_trees.behaviour.Behaviour, which meets the PR objectives.

  • File Verified: code/planning/src/behavior_agent/behaviors/lane_change.py contains the proper Change class implementation.
  • Usage Confirmed: The instantiation lane_change.Change("Execute Change") in code/planning/src/behavior_agent/behavior_tree.py is correct and reflects the consolidation of lane change actions.
code/planning/src/local_planner/utils.py (2)

15-16: Improved precision with floating-point values

Converting integer values to floating-point improves precision in distance calculations, which is beneficial for vehicle control systems.


17-18:

❓ Verification inconclusive

New lane change distance parameters added

These new constants provide more nuanced control for lane change behaviors:

  • TARGET_DISTANCE_TO_STOP_LANECHANGE: Defines when to stop during a lane change
  • TARGET_DISTANCE_TO_TRIGGER_LANECHANGE: Controls when to trigger lane changes

The trigger distance of 75.0 meters seems quite large. Consider whether this distance is appropriate based on the comments in the PR about premature lane changes.


🏁 Script executed:

#!/bin/bash
# Description: Check where and how the new lane change constants are used

# Search for usage of TARGET_DISTANCE_TO_TRIGGER_LANECHANGE
rg "TARGET_DISTANCE_TO_TRIGGER_LANECHANGE" -A 5 code/planning/src/behavior_agent/behaviors/

# Search for usage of TARGET_DISTANCE_TO_STOP_LANECHANGE
rg "TARGET_DISTANCE_TO_STOP_LANECHANGE" -A 5 code/planning/src/behavior_agent/behaviors/

Length of output: 2630


Action: Revisit and confirm lane change distance parameters

The new constants are correctly integrated in the lane change behavior code (see code/planning/src/behavior_agent/behaviors/lane_change.py). Specifically:

  • TARGET_DISTANCE_TO_STOP_LANECHANGE (5.0 meters) is used to determine when a lane change should stop.
  • TARGET_DISTANCE_TO_TRIGGER_LANECHANGE (75.0 meters) is applied to trigger the lane change maneuver.

Based on the verification output, these constants are consistently referenced. However, the trigger distance of 75.0 meters appears relatively high compared to the stopping distance, which might be a contributing factor for premature lane changes. Please reassess this value against simulation results or defined behavioral requirements to ensure it meets the intended control strategy.

code/acting/src/acting/passthrough.py (2)

61-67: Updated mapped_topics list

The mapped_topics list is properly updated to include the new acc_to_target_velocity mapping, ensuring it's processed by the Passthrough class.


38-42:

✅ Verification successful

New topic mapping for acceleration velocity

This new TopicMapping creates a pass-through from /paf/{role_name}/acc_velocity to /paf/hero/target_velocity, enabling direct influence on target velocity from acceleration commands. This enhances the communication between components.


🏁 Script executed:

#!/bin/bash
# Description: Verify topic naming consistency across the codebase

# Check for other usages of the target_velocity topic
rg "/paf/hero/target_velocity" --glob "!code/acting/src/acting/passthrough.py"

# Check for other usages of the acc_velocity topic
rg "/paf/hero/acc_velocity" --glob "!code/acting/src/acting/passthrough.py"

Length of output: 1825


Validated Topic Mapping for Acceleration Velocity

The new TopicMapping in code/acting/src/acting/passthrough.py (lines 38–42) correctly creates a pass-through from /paf/{role_name}/acc_velocity to /paf/hero/target_velocity. Verification shows that:

  • /paf/hero/target_velocity is used consistently across launch files, behavior routines, and documentation.
  • /paf/hero/acc_velocity is referenced in planning behaviors (e.g., in leave_parking_space.py) and related documents.

This confirms that the naming and implementation are consistent with the rest of the codebase. No further changes are needed.

code/control/src/velocity_controller.py (1)

111-124: Improved handling of negative target velocities

The changes to reverse driving logic are beneficial:

  1. Simplified logic by handling all negative velocities as signals for reverse driving
  2. Added flexibility through PID-based throttle control when not in fixed speed mode
  3. Removed hard-coded checks for specific velocity values (-3)

These changes make the code more maintainable and adaptable to different scenarios, which is helpful for the lane change behavior enhancements.

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

369-370: Resetting counter only if lane is blocked looks consistent.
This aligns with the idea that the counter should only reset if the oncoming lane is definitively blocked.


507-508: Identical reset logic in Wait class.
Mirroring the same counter reset condition when the lane is blocked helps maintain consistency between behaviors.

code/planning/src/behavior_agent/behaviors/unstuck_routine.py (4)

13-14: Additional imports look correct.
The newly imported add_speed_override, debug_status, and debug_marker appear to be used properly below.


16-19: Stop mark service imports are used appropriately.
These look useful for adjusting stop marks during the unstuck routine.


22-24: Configuration changes for stuck durations are fine.
Increasing the thresholds for stuck detection and wait seems intentional.


26-27: Marker color and lookup distance adjustments are acceptable.
No obvious issues with the new constants.

code/perception/src/global_plan_distance_publisher.py (6)

7-7: Using RoadOption for clarity is a good step.
Replacing magic numbers with enumerations improves readability.


62-62: Topic name change aligns with the new naming convention.
Switching to "/lane_change" is consistent with the broader rename.


91-98: Enumerated checks for normal driving options.
Relying on RoadOption.VOID, LEFT, RIGHT, STRAIGHT fosters explicitness.


103-108: Extended LaneChange message with position is logical.
Publishing the current route position can provide richer context.


112-115: Conditional lane change checks.
Ensuring we only set lane_change=True if we detect left/right lane changes is clearer.


131-135: Fallback to LANEFOLLOW for repeated lane changes.
Setting the second upcoming road option to LANEFOLLOW prevents unwanted double lane changes.

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

110-111: Question the conditional usage of RoadOption.CHANGELANERIGHT.
Here, the condition if RoadOption.CHANGELANERIGHT: sets lane_pos = 1 without comparing it to self.change_option. This might always evaluate to true if RoadOption.CHANGELANERIGHT is a non-zero constant, leading to unintended logic. Consider confirming if the check should instead be:

- if RoadOption.CHANGELANERIGHT:
+ if self.change_option == RoadOption.CHANGELANERIGHT:

285-288: Ensure early-right lane changes are disallowed if no lane exists.
The logic at line 285 only checks lane-free conditions when changing to the left. This aligns with the comment about not changing to the right too early. However, make sure you also detect if a right lane is non-existent and handle it safely to prevent abrupt lane shifts onto walkways.

Also applies to: 295-298

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 (2)
code/planning/src/behavior_agent/behaviors/lane_change.py (2)

17-17: Remove unused import
The math.isclose import is unused throughout the file, as flagged by static analysis. Consider removing it to keep the code clean.

- from math import isclose
🧰 Tools
🪛 Ruff (0.8.2)

17-17: math.isclose imported but unused

Remove unused import: math.isclose

(F401)


69-70: Question use of global state
The repeated assignment to the global variable LANECHANGE_FREE here and elsewhere can complicate code clarity and maintainability. Globally shared mutable state can lead to subtle bugs if multiple behaviors alter it in conflicting ways.

You might consider encapsulating the lane-free state logic within a class or the blackboard to avoid introducing potential side effects across behaviors.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0513e83 and dedd7f3.

📒 Files selected for processing (1)
  • code/planning/src/behavior_agent/behaviors/lane_change.py (5 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
code/planning/src/behavior_agent/behaviors/lane_change.py

17-17: math.isclose imported but unused

Remove unused import: math.isclose

(F401)

🔇 Additional comments (1)
code/planning/src/behavior_agent/behaviors/lane_change.py (1)

283-349: Verify lane change approach for right lane
The code explicitly handles early lane changes only when self.change_direction is LaneFreeDirection.LEFT. Lane changes to the right do not undergo the same lane-free check in this approach block, which can be intentional to prevent unintended right-lane maneuvers. However, if you plan to allow early right-lane changes under certain conditions, you might need a symmetrical check.

Would you like to:

  1. Keep the existing logic that prevents early right-lane changes?
  2. Add a similar lane-free approach check for LaneFreeDirection.RIGHT?

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

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

107-110: ⚠️ Potential issue

Fix the lane position conditional logic.

The conditional check if RoadOption.CHANGELANERIGHT: is not comparing against any value, so it will always evaluate to True as long as RoadOption.CHANGELANERIGHT has a non-zero/non-False value. This is likely not the intended behavior.

Apply this diff to fix the conditional check:

- if RoadOption.CHANGELANERIGHT:
+ if self.change_option == RoadOption.CHANGELANERIGHT:
    lane_pos = 1
🧹 Nitpick comments (1)
code/planning/src/behavior_agent/behaviors/lane_change.py (1)

42-42: Replace global variable with a better state management approach.

Using a global variable like LANECHANGE_FREE makes the code harder to test, debug, and reason about. Consider using a blackboard parameter or class attribute instead for better encapsulation.

- LANECHANGE_FREE = False

Add to the appropriate class:

def __init__(self, name):
    super().__init__(name)
    # Add to blackboard during initialization
    self.blackboard = py_trees.blackboard.Blackboard()
    self.blackboard.set("lanechange_free", False)

# Then replace all references to the global variable:
# global LANECHANGE_FREE -> self.blackboard.get("lanechange_free")
# LANECHANGE_FREE = X -> self.blackboard.set("lanechange_free", X)
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between dedd7f3 and 5c4d5ae.

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

116-123: Good approach to remove potential blocking stop marks.

Removing the overtake stop marks before executing a lane change is a good practice to prevent conflicts between different behaviors.


125-145: Clever handling of overtaking scenarios with lane changes.

This is an elegant solution to handle the case where the vehicle is already in an overtaking maneuver when a lane change to the left is requested. By adapting the overtake end position rather than executing a separate lane change, you avoid unnecessary maneuvers.


232-238: Good optimization to skip unnecessary states.

Skipping the approach phase when the lane change is already determined to be free is an effective optimization that streamlines the behavior flow.


284-284: Potential issue with left-only early lane change logic.

The condition self.change_direction is LaneFreeDirection.LEFT means early lane changes are only attempted when changing to the left lane. While this makes sense based on the PR objective comment about avoiding driving on walkways, this could limit functionality.

Consider whether this is the intended behavior and if there are scenarios where early lane changes to the right would be beneficial and safe. If you want to be more selective, consider checking for specific road conditions rather than disabling all right lane changes.


295-326: Counter-based reliability pattern for lane-free checks.

Using a counter to require multiple consecutive positive lane-free checks is an excellent approach to handle inconsistencies in sensor data or perception. This helps prevent premature lane changes based on false positive checks.


4-5: Good addition of Point for positional awareness.

Adding the Point import from geometry_msgs.msg to incorporate positional data in lane change decisions is a valuable enhancement that provides more context for decision-making.


157-158:

❓ Verification inconclusive

Validate coordinate transformation.

Make sure that the position values from the lane_change message are consistent with the coordinate system expected by the local_pos transformation. It would be safer to add a comment documenting the coordinate system expectations or add validation for the position values.


🏁 Script executed:

#!/bin/bash
# Checking for coordinate system documentation or validation

# Look for coordinate system documentation
echo "Looking for coordinate system documentation..."
rg -A 5 -B 5 "coordinate system" --glob "*.py" --glob "*.md"

# Check the LaneChange message definition to understand position field
echo "Checking LaneChange message definition..."
rg -A 10 "LaneChange.msg" --type md,txt

# Check for other transformations of position fields for comparison
echo "Looking for similar position transformations..."
rg -A 3 -B 3 "inverse\(\) \* Point2\.new\(" --glob "*.py"

Length of output: 17663


Action: Add inline documentation/validation for coordinate transformation consistency.

Please ensure that the position values from the lane_change message (i.e. self.change_position.x and self.change_position.y) are in the coordinate system required by the local position transformation. Consider the following:

  • Add an inline comment or reference to the design documentation (e.g., see doc/research/paf24/intermediate_layer/DesignDocument_TopDownMap.md) that clearly states the expected coordinate system.
  • Optionally, implement a validation or assertion to confirm that the lane_change message values are properly formatted before applying the transformation.
  • Compare this transformation with similar operations (e.g., in local_planner or radar_node) to ensure consistency across the codebase.

540-546:

❓ Verification inconclusive

Verify vector addition safety for position calculation.

The code adds a forward vector to the transformed position, but without clear documentation of the coordinate system. Ensure that this addition is correctly applying a 5-meter offset in the direction of travel.


🏁 Script executed:

#!/bin/bash
# Look for similar Vector2.forward() usage

echo "Searching for Vector2.forward() usage..."
rg -A 3 -B 3 "Vector2\.forward\(\)" --glob "*.py"

echo "Checking Vector2 class definition..."
rg -A 20 "class Vector2" --glob "*.py"

Length of output: 6358


Clarify Coordinate System for 5-Meter Offset

The calculation in lane change (lines 540–546) applies a 5-meter offset by adding Vector2.forward() * 5.0 to the transformed position. We observed that similar patterns appear in other parts of the codebase (e.g., in the pure pursuit controller, ACC, and mapping modules), which suggests that Vector2.forward() is consistently used to generate a forward offset. However, the underlying coordinate system—specifically, whether Vector2.forward() reliably returns a unit vector in the proper travel direction—is not clearly documented here.

Please verify that:

  • Vector2.forward() returns a unit vector aligned with the intended forward direction.
  • The transformation applied via hero_transform.inverse() correctly positions the offset relative to the vehicle's coordinate frame.
  • Any assumptions about the coordinate system are either documented or referenced elsewhere in the codebase.

434-439:

❓ Verification inconclusive

Inconsistent check method between Approach and Wait.

In Approach.update(), the lane free check uses check_method="fallback" (line 289) while in Wait.update(), it uses check_method="lanemarking" (line 438). This inconsistency could lead to different behavior between the two states.

If the intentional difference is to have stricter checks during waiting vs. approaching, consider adding a comment explaining this design decision.


🏁 Script executed:

#!/bin/bash
# Check for documentation of check_method parameter

# Look for definitions or usage of check_method parameter
echo "Looking for check_method documentation or usage..."
rg -A 5 -B 5 "check_method" --glob "*.py"

# Look for definition of is_lane_free method
echo "Looking for is_lane_free method definition..."
rg -A 10 "def is_lane_free" --glob "*.py"

Length of output: 15400


Clarification Needed: Document Intentional Use of Different check_method Values

The repository intentionally uses both "fallback" and "lanemarking" as valid options for the check_method parameter (see documentation in code/mapping/ext_modules/mapping_common/map.py). For example, in lane_change.py one branch uses "fallback" (when self.change_detected is true and the direction is left) while another branch uses "lanemarking". A similar pattern is observed in other behaviors (e.g., in Approach vs. Wait states).

To avoid confusion, please verify that this deliberate difference in lane free-checks is intended. If so, consider adding an inline comment explaining why one state (or branch) uses "fallback" while another uses "lanemarking", clarifying the design rationale behind these choices.

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 (2)
code/planning/src/behavior_agent/behaviors/lane_change.py (2)

41-43: Consider avoiding global mutable state for LANECHANGE_FREE.
Using a global variable (LANECHANGE_FREE) in multiple classes and methods can introduce hidden dependencies and make the control flow harder to reason about, potentially leading to race conditions in more complex or multithreaded contexts. Storing this state in the blackboard, or encapsulating it in a dedicated class or data structure, would improve maintainability and testability.


403-409: Potential race condition using LANECHANGE_FREE within Wait.
The Wait behavior terminates immediately if LANECHANGE_FREE is set, but this global state could be updated asynchronously by Ahead or Approach. If multiple updates happen at once, the final state might become inconsistent. Ensuring that Wait verifies the lane status from the blackboard or another deterministic source would reduce the risk of synchronization issues.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7d6e938 and 5bf0a81.

📒 Files selected for processing (1)
  • code/planning/src/behavior_agent/behaviors/lane_change.py (5 hunks)
🧰 Additional context used
🧠 Learnings (1)
code/planning/src/behavior_agent/behaviors/lane_change.py (1)
Learnt from: Johannes1098
PR: una-auxme/paf#731
File: code/planning/src/behavior_agent/behaviors/lane_change.py:567-568
Timestamp: 2025-03-03T22:19:36.933Z
Learning: In the behavior tree for lane change in `code/planning/src/behavior_agent/behaviors/lane_change.py`, the last sub-behavior (`Change`) intentionally returns `Status.FAILURE` when completed to signal termination of the subtree and exit the lane change behavior sequence.
⏰ 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/lane_change.py (4)

68-69: Re-initializing LANECHANGE_FREE on each Ahead initialization may cause logical confusion.
Every time the Ahead behavior starts, LANECHANGE_FREE is set to False. If other behaviors rely on this global flag’s prior state, the reset here could lead to unexpected transitions. Verify whether this repeated re-initialization accurately reflects the intended design.


128-140: Confirm overtake end logic consistency.
When an overtake is already in progress and a left-lane change is requested, the code adapts the overtake end position rather than performing a separate lane change. Double-check that the newly computed end_transition_length and end_pos properly facilitate a safe, completed maneuver in all edge cases (e.g., slow-moving traffic).


157-177: Validate stop mark usage during lane change.
The code creates a 20m-long stop mark slightly offset from the hero vehicle’s position. While this helps block the lane for oncoming or conflicting traffic, ensure that this offset correctly accounts for different road widths and does not inadvertently place the stop mark where it can cause collisions with adjacent lanes or sidewalks.


557-568: Returning Status.FAILURE upon successful lane change is intentional.
This return value can appear counterintuitive; however, based on prior conversations (), you are using FAILURE to terminate the lane change subtree. As long as the parent behavior is designed to interpret this as a completed lane change, no further changes are required.

Comment on lines +283 to +326
# (as there could be no road till change point!)
if self.change_detected and self.change_direction is LaneFreeDirection.LEFT:
lc_free, lc_mask = tree.is_lane_free(
right_lane=self.change_direction.value,
lane_length=22.5,
lane_transform=-5.0,
check_method="fallback",
)
if isinstance(lc_mask, shapely.Polygon):
add_debug_marker(debug_marker(lc_mask, color=LANECHANGE_MARKER_COLOR))
add_debug_entry(self.name, f"Lane change: Is lane free? {lc_free.name}")
if lc_free is LaneFreeState.FREE:
self.counter_lanefree += 1
# using a counter to account for inconsistencies
if self.counter_lanefree > 1:
# bool to skip Wait since oncoming is free
LANECHANGE_FREE = True
if self.change_direction is LaneFreeDirection.RIGHT:
lanechange_offset = -2.5
else:
lanechange_offset = 2.5
end_transition_length = min(15.0, self.change_distance + 9.0)
request_start_overtake(
proxy=self.start_overtake_proxy,
offset=lanechange_offset,
local_end_pos=Point2.new(
self.change_distance + 10.0, lanechange_offset
),
end_transition_length=end_transition_length,
)
update_stop_marks(
self.stop_proxy,
id=LANECHANGE_STOPMARK_ID,
reason="lane not blocked",
is_global=False,
marks=[],
)
self.curr_behavior_pub.publish(bs.lc_app_free.name)
return debug_status(
self.name,
Status.SUCCESS,
"Lane Change: Lane free, changing directly",
)
else:
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Review the decision to skip lane-free checks for right-lane changes.
Currently, the lane-free check is only applied if self.change_direction is LaneFreeDirection.LEFT. This avoids early right-lane changes, but if the map triggers a right-lane change prematurely, the vehicle might still drift onto a walkway if no lane is available. Consider either adding a similar is_lane_free check for the right-lane scenario or ensuring the logic defers any right-lane maneuver until the lane definitely exists and is safe.

@Johannes1098 Johannes1098 requested a review from Zelberor March 3, 2025 23:18
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/lane_change.py (3)

41-42: Document the purpose of the LANECHANGE_FREE global variable.

The newly introduced global variable LANECHANGE_FREE is used to track lane change state across multiple behaviors, but lacks documentation explaining its purpose and expected usage patterns.

Add a comment explaining that this global variable is used to coordinate state across the Ahead, Approach, Wait, and Change behaviors to indicate when a lane is free for changing.


161-176: Consider adding a safety margin to the stop mark shape.

The stop mark for a lane change is currently set to exactly match the needed space. Consider adding a small safety margin to the dimensions to account for imperfect localization or control.

- stop_mark_shape = Rectangle(
-     length=20.0,
-     width=2.5,
-     offset=Transform2D.new_translation(
-         Vector2.new(
-             local_pos.x() + 10.0, local_pos.y() + lane_pos * 2.5
-         )
-     ),
- )
+ # Add safety margins to stop mark to account for control inaccuracies
+ stop_mark_shape = Rectangle(
+     length=22.0,  # +2.0 safety margin
+     width=3.0,    # +0.5 safety margin
+     offset=Transform2D.new_translation(
+         Vector2.new(
+             local_pos.x() + 11.0, local_pos.y() + lane_pos * 2.5
+         )
+     ),
+ )

444-467: Consider documenting the counter approach for lane state detection.

The counter-based approach for confirming lane-free state is a good way to mitigate sensor noise and intermittent lane detection issues. However, it's not immediately obvious why this approach was chosen.

Add a comment explaining that the counter approach helps filter out transient false positives in lane-free detection due to sensor noise or detection inconsistencies, requiring multiple consecutive positive detections before taking action.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5bf0a81 and 2a44169.

📒 Files selected for processing (1)
  • code/planning/src/behavior_agent/behaviors/lane_change.py (5 hunks)
🧰 Additional context used
🧠 Learnings (1)
code/planning/src/behavior_agent/behaviors/lane_change.py (1)
Learnt from: Johannes1098
PR: una-auxme/paf#731
File: code/planning/src/behavior_agent/behaviors/lane_change.py:567-568
Timestamp: 2025-03-03T22:19:36.933Z
Learning: In the behavior tree for lane change in `code/planning/src/behavior_agent/behaviors/lane_change.py`, the last sub-behavior (`Change`) intentionally returns `Status.FAILURE` when completed to signal termination of the subtree and exit the lane change behavior sequence.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image
🔇 Additional comments (7)
code/planning/src/behavior_agent/behaviors/lane_change.py (7)

110-111: LGTM! Issue fixed.

The comparison logic now correctly checks if the lane change option equals RoadOption.CHANGELANERIGHT.


127-147: Well-handled special case for lane changes during overtaking.

This intelligent handling of lane changes to the left when already overtaking is a good solution. It avoids redundant maneuvers by adapting the existing overtake parameters rather than executing a separate lane change.


562-563: LGTM! Intentional FAILURE return for behavior tree control flow.

The Status.FAILURE return when the lane change is finished is intentional to signal termination of the subtree and exit the lane change behavior sequence, as confirmed in the learned feedback.


535-541:

❓ Verification inconclusive

Verify trajectory calculation logic during lane change.

The calculation to determine if the lane change is complete involves getting a point 5 meters behind the global change point. Ensure this logic works correctly for various lane geometries and vehicle approaches.


🏁 Script executed:

#!/bin/bash
# Check for any existing tests or simulation scenarios involving lane changes

# Look for test files related to lane changes
fd "test.*lane.*change" --type f

# Look for simulation scenarios that might test lane changes
fd "lane.*change.*scenario" --type f
fd "scenario.*lane.*change" --type f

Length of output: 112


Action Required: Manually Verify Lane Change Trajectory Calculation
Our search for automated tests related to lane change scenarios returned no output. This suggests that the trajectory calculation logic hasn't been validated via tests yet. Please ensure that the transformation applied using _get_global_hero_transform() combined with the addition of Vector2.forward() * 5.0 truly yields the intended 5-meter shift in various lane geometries and vehicle approach scenarios.

  • Confirm that _get_global_hero_transform() provides correct global coordinates.
  • Verify that the transformation inversion and addition of the forward vector correctly computes the new local position.
  • Consider adding or updating simulation tests to cover diverse lane change geometries.

93-94:

❓ Verification inconclusive

Validate the usage of lane_change.position.

This code now extracts the position field from the lane_change message, which was newly added to the LaneChange message type. Ensure that all publishers of this message include valid position information.


🏁 Script executed:

#!/bin/bash
# Find all locations that publish to lane_change topics and check if they populate the position field

# Search for lane_change publishers in the codebase
echo "Looking for lane_change publishers..."
rg -A 5 -B 5 "publish.*lane_change" --type py

Length of output: 132


ACTION REQUIRED: Verify that all lane_change publishers are setting the new position field properly.

The current change extracts lane_change.position from the message, but our automated search did not locate any code publishing to the lane_change topic that explicitly sets the position field. Please perform a manual review of the publisher implementations for the LaneChange message to confirm that they correctly populate this field.


282-326:

❓ Verification inconclusive

Consider a different approach for right-lane change safety.

The current implementation intentionally skips lane-free checks for right-lane changes to avoid premature lane changes that could lead to driving on walkways. While this addresses the immediate concern, it might not detect obstacles on the target lane early enough.

Consider implementing a progressive checking approach for right-lane changes that still performs the lane-free check but at a progressively decreasing distance as the vehicle approaches the lane change point. This would allow early detection of obstacles while avoiding premature lane changes.


🏁 Script executed:

#!/bin/bash
# Checking if lane-free check is implemented for other methods that could be adapted

# Check if there are adaptive or progressive checking methods in the mapping code
rg -A 5 "is_lane_free" --type py

Length of output: 6612


Action Required: Enhance Right-Lane Change Safety Checks via a Progressive Approach

The current implementation deliberately omits early lane-free checks for right-lane changes to prevent initiating premature maneuvers (and potential incursions onto walkways). While this prevents some risks, it also delays obstacle detection on the target lane when approaching the change point. In contrast, similar functions (as seen in the mapping module and overtaking behavior) use checks with various methods (like "fallback" and "lanemarking") but do not yet incorporate a dynamic, progressive distance approach.

Suggestions:

  • Evaluate updating the right-lane change logic (in code/planning/src/behavior_agent/behaviors/lane_change.py, lines 282–326) to include a progressive lane-free check. This could involve starting the check at a longer distance and then tightening the parameters (e.g., adjusting lane_length or lane_transform) as the vehicle nears the lane change point.
  • Review and consider adapting techniques similar to those in code/mapping/ext_modules/mapping_common/map.py where parameters like reduce_lane are leveraged to dynamically adjust the lane-free evaluation.

Please verify that this change will not introduce premature lane changes in other scenarios while still ensuring obstacles are detected early enough.


434-439:

❓ Verification inconclusive

Inconsistent lane-free check methods between Approach and Wait.

The Approach class uses "fallback" method while the Wait class uses "lanemarking" method for the lane-free check. These inconsistencies could lead to different behaviors in different phases.


🏁 Script executed:

#!/bin/bash
# Check the implementation differences between lane-free check methods

# Find the implementation of is_lane_free
rg -A 20 "def is_lane_free" --type py

# Look for documentation on the different check methods
rg "check_method" --type py

# Find usage patterns of the different methods
rg "check_method=\"fallback\"" --type py
rg "check_method=\"lanemarking\"" --type py

Length of output: 7730


Review Note: Verify Consistency of Lane-Free Check Methods

The usage of the is_lane_free method appears to vary across behavior modules. For example, in some parts of the code (including within lane_change.py and overtake.py), the "fallback" method is used, while in other parts (such as the snippet shown in lane_change.py and in leave_parking_space.py), the "lanemarking" method is applied. The original comment mentioned that the Approach behavior uses "fallback" whereas the Wait behavior relies on "lanemarking." Given that the underlying function supports several check methods ("rectangle", "lanemarking", and "fallback"), please verify that these differences reflect an intentional design choice rather than an oversight.

Action Items:

  • Confirm whether the distinct methods (“fallback” vs. “lanemarking”) are deliberately chosen for different phases (e.g., Approach vs. Wait) or if they should be unified.
  • Review the configuration across the relevant behavior classes (including Approach, Wait, lane change, and overtake) to ensure consistent lane-checking criteria and predictable behavior.
  • If the divergence is unintended, refactor the code to use a consistent check method or clearly document the rationale for the differences.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: 🏗 In progress
Development

Successfully merging this pull request may close these issues.

[Feature]: Add lane free check to lanechange behavior
3 participants