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

631 feature execution check function for planned local trajectory #654

Conversation

THDThieleT
Copy link
Collaborator

@THDThieleT THDThieleT commented Jan 26, 2025

Description

Added mapping functions with the following functionality:

Fixes # (issue)

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Does this PR introduce a breaking change?

No

Most important changes

Only map.py is affected by this change.
All other files are changed temporarly for testing purpose only

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

Release Notes: Mapping System Update

  • New Features

    • Enhanced map management with advanced entity detection and trajectory analysis.
    • Added lane marking detection and trajectory checking capabilities.
    • Introduced a new test path generation node for trajectory simulation.
  • Improvements

    • Expanded mapping data integration with lane marking data and trajectory checks.
    • Improved trajectory validation and obstacle detection mechanisms.
  • Testing

    • Added a new path testing node to validate mapping and trajectory generation.

These updates significantly enhance the mapping system's ability to detect and analyze spatial relationships, improving overall navigation and path planning capabilities.

THDThieleT and others added 22 commits January 16, 2025 16:57
…of-lane-markings' into 631-feature-execution-check-function-for-planned-local-trajectory
…sed; [WIP] get entities in specified diameter around the car
…-check-function-for-planned-local-trajectory
@THDThieleT THDThieleT linked an issue Jan 26, 2025 that may be closed by this pull request
Copy link
Contributor

coderabbitai bot commented Jan 26, 2025

Walkthrough

This pull request introduces enhancements to the mapping system's functionality, focusing on trajectory management, lane marking detection, and spatial entity analysis. The Map class in map.py gains new methods for detecting entities in an area, identifying lane markings, and checking trajectories. The MappingDataIntegrationNode is updated to incorporate lane marking data and trajectory checks. A new TestPath node is created to generate and test trajectories, and the launch configuration is updated to include this new node.

Changes

File Change Summary
code/mapping/ext_modules/mapping_common/map.py - Added methods: entities_in_area(), get_obstacle_on_trajectory(), lane_marking_left(), lane_marking_right(), check_trajectory()
- Added constants: VEHICLE_WIDTH_BUFFER, LANE_CHECK_BUFFER, OBSTACLE_DETECTION_RADIUS
code/mapping/src/mapping_data_integration.py - Added lanemarkings attribute
- Added lanemarkings_callback() and callback() methods
- Modified publish_new_map() method
code/mapping/src/path_test.py - New file with TestPath class
- Added methods: __init__(), generate_trajectory(), map_callback()
code/mapping/launch/mapping.launch - Added new node Local_Trajectory_Node for path_test.py (commented out)

Suggested reviewers

  • vinzenzm
  • Zelberor

Poem

🐰 In the mapping world, we hop and play,
New paths and lanes light up the way.
Entities gather, trajectories flow,
With each little change, our knowledge will grow.
Hooray for the code, let innovation bloom! 🚗✨

✨ 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.

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: 6

🔭 Outside diff range comments (2)
code/perception/src/lidar_distance.py (2)

Line range hint 4-11: Add error handling for external dependencies.

The code imports several external libraries but lacks proper error handling for import failures. This could lead to cryptic errors if dependencies are missing.

Add proper error handling:

-from joblib import Parallel, delayed
-import rospy
-import ros_numpy
-import numpy as np
-from lidar_filter_utility import bounding_box, remove_field_name
-from sensor_msgs.msg import PointCloud2, Image as ImageMsg
-from sklearn.cluster import DBSCAN
-from cv_bridge import CvBridge
+try:
+    from joblib import Parallel, delayed
+    import rospy
+    import ros_numpy
+    import numpy as np
+    from lidar_filter_utility import bounding_box, remove_field_name
+    from sensor_msgs.msg import PointCloud2, Image as ImageMsg
+    from sklearn.cluster import DBSCAN
+    from cv_bridge import CvBridge
+except ImportError as e:
+    print(f"Failed to import required packages: {e}")
+    sys.exit(1)

Missing test coverage for critical LiDAR functionality confirmed

The perception module lacks any test coverage for the LiDAR processing components. Critical untested functionality includes:

  • Point cloud processing and filtering
  • Image reconstruction from LiDAR data
  • DBSCAN clustering operations
  • Error handling for edge cases

While other modules like mapping demonstrate proper test organization, the perception module needs similar test infrastructure. Consider following the established testing patterns from the mapping module's test structure at code/mapping/tests/.

🔗 Analysis chain

Line range hint 1-700: Add comprehensive test coverage.

The file lacks unit tests for critical functionality:

  1. Point cloud processing
  2. Image reconstruction
  3. Clustering operations
  4. Error handling

Let's check the test coverage:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Analyze test coverage for LiDAR processing
# Look for test files related to LiDAR processing

echo "Searching for test files..."
fd -e py test_lidar -x cat {}

echo "Checking test coverage..."
rg -i "test.*lidar" --type python

Length of output: 226


Script:

#!/bin/bash
# Search for Python test files and test-related content

echo "=== Searching for Python test files ==="
fd -e py "test_.*\.py$"

echo -e "\n=== Searching for test configuration files ==="
fd "pytest.ini|tox.ini|setup.cfg|conftest.py"

echo -e "\n=== Searching for LiDAR-related test content ==="
rg -l "class.*Test.*Lidar|def test_.*lidar|from.*lidar.*import"

echo -e "\n=== Checking for test framework imports in Python files ==="
rg "^import pytest|^from pytest|^import unittest|^from unittest" --type py

Length of output: 1024


Script:

#!/bin/bash
# Analyze perception module structure and test files

echo "=== Perception Module Structure ==="
fd . code/perception -t d

echo -e "\n=== Files in Perception Module ==="
fd . code/perception -t f

echo -e "\n=== Searching for any test-related files in perception ==="
rg -l "test|assert|mock" code/perception/

echo -e "\n=== Looking for alternative test locations ==="
fd -e py "(test|tests|testing)" --exclude "test_route.py"

Length of output: 4123

🧹 Nitpick comments (25)
code/perception/src/lidar_distance.py (1)

Line range hint 297-361: Consider optimizing the image reconstruction process.

The reconstruct_img_from_lidar method could benefit from performance improvements:

  1. Pre-allocate arrays for better memory efficiency
  2. Vectorize the coordinate transformations
  3. Use NumPy's masked arrays instead of boolean indexing

Consider this optimization:

 def reconstruct_img_from_lidar(self, coordinates_xyz, focus):
-    img = np.zeros(shape=(720, 1280), dtype=np.float32)
-    dist_array = np.zeros(shape=(720, 1280, 3), dtype=np.float32)
+    # Pre-allocate arrays with masked arrays
+    dist_array = np.ma.zeros(shape=(720, 1280, 3), dtype=np.float32)
+    dist_array.mask = True  # All points are initially masked
 
     # Prepare points based on focus
     if focus in ["Center", "Back"]:
-        points = np.column_stack((
-            coordinates_xyz[:, 1],
-            coordinates_xyz[:, 2],
-            coordinates_xyz[:, 0],
-            np.ones(coordinates_xyz.shape[0]),
-        ))
+        # Vectorized transformation
+        points = np.column_stack([
+            coordinates_xyz[:, [1, 2, 0]],
+            np.ones(len(coordinates_xyz))
+        ])
code/perception/src/Lanedetection_node.py (4)

30-33: Consider graceful device fallback for non-CUDA environments.
Currently, the code assumes CUDA availability by default. Systems without CUDA support will raise an exception. As a best practice, implement a fallback to CPU if CUDA is not available, and log a warning.

 self.model = torch.hub.load("hustvl/yolop", "yolop", pretrained=True)
-self.device = torch.device("cuda")
+if torch.cuda.is_available():
+    self.device = torch.device("cuda")
+else:
+    self.device = torch.device("cpu")
 self.model.to(self.device)

39-40: Parameterize camera angles if needed.
The code only subscribes to "Center" camera images (setup_camera_subscriptions("Center")), but you have a parameter for role name. If you eventually plan to extend to additional camera angles (e.g., left/right), expose “side” as a parameter or handle it dynamically.


103-104: Avoid unnecessary frequent CUDA clear calls
Calling torch.cuda.empty_cache() in every image callback might degrade performance. Invoke it strategically (e.g., only when memory usage is high or on a timed interval).


47-47: Remove redundant pass statement
The pass statement in line 47 is unnecessary since spin() blocks until shutdown.

 self.spin()
-pass
code/mapping/ext_modules/mapping_common/map.py (3)

77-105: Docstring mismatch in entities_in_area
The docstring states it returns entities “without the hero car,” but also discusses “only checks if the first entity is hero.” The function indeed filters out the hero entity. Recommend clarifying the docstring and ensuring consistent behavior.


173-226: Simplify nested if conditions for lane checks
Static analysis hints (SIM102) suggest combining nested conditions with a single and. For example, lines 211–214 and 220–223 can be combined as one if condition.

        if left_lane is not None:
-            if shapely.intersection(
-                left_lane.to_shapely().poly, local_trajectory_buffer
-            ):
-                return 2
+            if shapely.intersection(left_lane.to_shapely().poly, local_trajectory_buffer):
+                return 2

        if right_lane is not None:
-            if shapely.intersection(
-                right_lane.to_shapely().poly, local_trajectory_buffer
-            ):
-                return 3
+            if shapely.intersection(right_lane.to_shapely().poly, local_trajectory_buffer):
+                return 3
🧰 Tools
🪛 Ruff (0.8.2)

211-214: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)


220-223: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)


196-197: Consider lane buffer size
Using local_trajectory.buffer(0.5, 3) might cause false positives or negatives, depending on vehicle size and lane width. Validate these parameters in real-world tests or add prompts for dynamic parameterization.

code/perception/src/lane_position.py (4)

433-433: Address unused loop control variable
Static analysis flagged that label is unused in this loop. Rename _label to avoid confusion or to reflect usage if needed.

-for label, cluster in clusters.items():
+for _label, cluster in clusters.items():
    y = cluster[:, 1]
    ...
🧰 Tools
🪛 Ruff (0.8.2)

433-433: Loop control variable label not used within loop body

Rename unused label to _label

(B007)


445-452: Combine inline if-else for clarity
As static analysis suggests (SIM108), use a ternary operator for conciseness instead of a multi-line if-else.

-if np.ndim(res) == 2:
-    std_dev = np.sqrt(np.diag(res))
-else:
-    std_dev = np.sqrt(res)
+std_dev = np.sqrt(np.diag(res)) if np.ndim(res) == 2 else np.sqrt(res)
🧰 Tools
🪛 Ruff (0.8.2)

445-452: Use ternary operator std_dev = np.sqrt(np.diag(res)) if np.ndim(res) == 2 else np.sqrt(res) instead of if-else-block

(SIM108)


628-628: Remove or utilize the unused unique_labels variable
unique_labels = np.unique(clustered_mask) is assigned but never used. Consider removing it if not needed.

-unique_labels = np.unique(clustered_mask)
🧰 Tools
🪛 Ruff (0.8.2)

628-628: Local variable unique_labels is assigned to but never used

Remove assignment to unused variable unique_labels

(F841)


345-366: Cross-check confidence thresholds
The confidence calculation is purely heuristic. If the model or environment changes, these thresholds might become too restrictive or lenient. Provide documentation or adjustable parameters for consistent performance.

code/mapping/msg/TypeLanemarking.msg (1)

2-3: Add documentation for new message fields.

Please add comments to document:

  • The meaning and valid range of position_index
  • The semantic meaning of predicted and when it's set to true/false
code/mapping/launch/mapping.launch (2)

3-3: Consider making debug port configurable.

The debug port (53000) is hardcoded. Consider making it configurable via a launch parameter for flexibility and to avoid potential port conflicts.

-  <node pkg="mapping" type="debug_wrapper.py" name="mapping_data_integration" output="screen" args="--debug_node=mapping_data_integration.py --debug_port=53000">
+  <arg name="debug_port" default="53000"/>
+  <node pkg="mapping" type="debug_wrapper.py" name="mapping_data_integration" output="screen" args="--debug_node=mapping_data_integration.py --debug_port=$(arg debug_port)">

14-15: Consider adding configuration parameters for path_test_node.

The path_test node might benefit from configurable parameters such as:

  • Update rate (currently hardcoded to 0.5s in the implementation)
  • Frame ID (currently hardcoded to "hero")
  • Path length (currently hardcoded to 10 points)
code/mapping/src/path_test.py (2)

7-10: Remove unused imports.

The following imports are not used in the code:

  • mapping.msg.Map
  • mapping_common.map.Map
  • shapely
🧰 Tools
🪛 Ruff (0.8.2)

7-7: mapping.msg.Map imported but unused

Remove unused import: mapping.msg.Map

(F401)


8-8: mapping_common.map.Map imported but unused

Remove unused import: mapping_common.map.Map

(F401)


10-10: shapely imported but unused

Remove unused import: shapely

(F401)


32-33: Make update rate configurable via ROS parameter.

The trajectory update rate is hardcoded to 0.5 seconds. Consider making it configurable via a ROS parameter.

-        self.rate = 0.5
-        self.new_timer(0.5, self.generate_trajectory)
+        self.rate = self.get_parameter('update_rate', 0.5)
+        self.new_timer(self.rate, self.generate_trajectory)
code/requirements.txt (1)

20-31: Consider grouping related dependencies.

The new dependencies could be organized better by grouping them based on their purpose (e.g., visualization, ML processing, utilities).

Consider reorganizing like this:

 numpy==1.23.5
 ultralytics==8.3.32
 scikit-learn>=0.18
 pandas==2.0.3
 debugpy==1.8.7

-tqdm
-yacs
-
-matplotlib>=3.2.2
-Pillow
-PyYAML>=5.3
-tensorboardX
-seaborn
-prefetch_generator
-imageio
+# ML Processing
+tqdm
+yacs
+tensorboardX
+prefetch_generator
+
+# Visualization
+matplotlib>=3.2.2
+seaborn
+
+# Image Processing
+Pillow
+imageio
+
+# Configuration
+PyYAML>=5.3
doc/perception/Lanedetection_node.md (3)

3-4: Fix typos and improve language.

There are several issues in these lines:

  • "briev" should be "brief"
  • Inconsistent capitalization of "Lane markings" vs "driveable Area"
-This Document gives a briev overview over the Lanedetection Node.
-The Lanedetection Node is responsible for detecting the Lane markings and driveable Area in a given Image and publish the results.
+This document gives a brief overview of the Lanedetection Node.
+The Lanedetection Node is responsible for detecting the lane markings and driveable area in a given image and publishing the results.

63-64: Fix markdown formatting.

The bullet point formatting is incorrect in the MarkerArray description.

-    - - **Description**: Contains all Lidar points that collide with the lane mask.
+    - **Description**: Contains all Lidar points that collide with the lane mask.

29-41: Add more technical details to the Image Handler section.

The Image Handler section could benefit from more technical details:

  • What preprocessing steps are performed?
  • What are the input image requirements (size, format)?
  • What is the expected performance of the YOLOP model?

Consider expanding this section with these technical details to help future maintainers.

code/mapping/src/mapping_data_integration.py (3)

101-109: Consider parameterizing the trajectory topic name.

The topic name "test_trajectory" is hardcoded. Consider using the get_param method for better configurability, similar to other topic subscriptions in the class.

-            topic="test_trajectory",
+            topic=self.get_param("~trajectory_topic", "/test_trajectory"),

114-115: Add error handling and documentation to the callback method.

The callback method should:

  • Include docstring explaining the purpose
  • Add type checking for the input data
  • Handle potential exceptions
 def callback(self, data: Path):
+    """Store the received trajectory for later use in lane marking predictions.
+    
+    Args:
+        data (Path): The trajectory message to store
+    """
+    if not isinstance(data, Path):
+        self.logwarn(f"Received invalid trajectory data type: {type(data)}")
+        return
     self.local_trajectory = data

309-311: Consider parameterizing the area filter value.

The area filter value (10) is hardcoded. Consider making it configurable through ROS parameters for better flexibility.

-        map.entities = map.entities_in_area(10)
+        area_size = self.get_param("~map_area_size", 10)
+        map.entities = map.entities_in_area(area_size)

Also applies to: 331-339

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

452-473: Consider using constants for visualization parameters.

The marker visualization code uses magic numbers for colors and transparency. Consider extracting these as class constants for better maintainability.

 class Lanemarking(Entity):
+    # Visualization constants
+    MARKER_ALPHA = 0.5
+    MARKER_HEIGHT = 0.1
+    MARKER_COLOR_MAX = 255
+    
     def to_marker(self) -> Marker:
         m = self.shape.to_marker(self.transform)
 
         if self.predicted:
-            m.color.a = 0.5
-            m.color.r = int(255 * self.confidence)
+            m.color.a = self.MARKER_ALPHA
+            m.color.r = int(self.MARKER_COLOR_MAX * self.confidence)
             m.color.g = 0
             m.color.b = 0
         else:
-            m.color.a = 0.5
+            m.color.a = self.MARKER_ALPHA
             m.color.r = 0
             m.color.g = 0
-            m.color.b = int(255 * self.confidence)
+            m.color.b = int(self.MARKER_COLOR_MAX * self.confidence)
 
-        m.scale.z = 0.1
+        m.scale.z = self.MARKER_HEIGHT
         m.pose.position.z = m.scale.z / 2.0
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3603937 and 8cb86c3.

⛔ Files ignored due to path filters (1)
  • doc/assets/perception/Overview_lanedetection_node.jpg is excluded by !**/*.jpg
📒 Files selected for processing (15)
  • code/agent/config/rviz_config.rviz (5 hunks)
  • code/mapping/ext_modules/.debug_enabled (1 hunks)
  • code/mapping/ext_modules/mapping_common/entity.py (1 hunks)
  • code/mapping/ext_modules/mapping_common/map.py (4 hunks)
  • code/mapping/launch/mapping.launch (2 hunks)
  • code/mapping/msg/TypeLanemarking.msg (1 hunks)
  • code/mapping/src/mapping_data_integration.py (6 hunks)
  • code/mapping/src/path_test.py (1 hunks)
  • code/mapping/tests/mapping_common/test_entity.py (1 hunks)
  • code/perception/launch/perception.launch (2 hunks)
  • code/perception/src/Lanedetection_node.py (1 hunks)
  • code/perception/src/lane_position.py (1 hunks)
  • code/perception/src/lidar_distance.py (1 hunks)
  • code/requirements.txt (1 hunks)
  • doc/perception/Lanedetection_node.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • code/mapping/ext_modules/.debug_enabled
🧰 Additional context used
🪛 Ruff (0.8.2)
code/mapping/src/path_test.py

7-7: mapping.msg.Map imported but unused

Remove unused import: mapping.msg.Map

(F401)


8-8: mapping_common.map.Map imported but unused

Remove unused import: mapping_common.map.Map

(F401)


10-10: shapely imported but unused

Remove unused import: shapely

(F401)

code/mapping/ext_modules/mapping_common/map.py

211-214: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)


220-223: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)

code/perception/src/lane_position.py

433-433: Loop control variable label not used within loop body

Rename unused label to _label

(B007)


445-452: Use ternary operator std_dev = np.sqrt(np.diag(res)) if np.ndim(res) == 2 else np.sqrt(res) instead of if-else-block

(SIM108)


628-628: Local variable unique_labels is assigned to but never used

Remove assignment to unused variable unique_labels

(F841)

🔇 Additional comments (15)
code/perception/src/lidar_distance.py (2)

213-213: Validate alignment with lane detection requirements.

The removal of the z-axis constraint (min_z: -np.inf) aligns with the PR's lane detection objectives by potentially including more road surface points. However, ensure this doesn't introduce noise that could affect lane detection accuracy.

Let's validate the impact on lane detection:


213-213: Verify the impact of extending the z-axis range.

The change from min_z: -1.6 to min_z: -np.inf for the "Center" direction removes the lower bound constraint on the z-axis. This could significantly impact the point cloud processing by:

  1. Including ground points and underground structures
  2. Potentially increasing processing time due to more points
  3. Affecting the quality of distance images

Let's analyze the impact:

✅ Verification successful

Extending z-axis range for center direction is intentionally designed

The change from -1.6 to -np.inf for the center direction's min_z is a deliberate design choice that:

  • Only affects forward-facing point cloud processing
  • Maintains ground filtering (-1.6) for back, left, and right directions
  • Allows detection of below-ground features in front of the vehicle while keeping safe filtering elsewhere
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Analyze the impact of removing z-axis constraint
# 1. Check if there are any critical dependencies on the z-axis filtering
# 2. Look for potential performance implications

# Check for dependencies on z-axis filtering
echo "Checking for z-axis dependencies..."
rg -A 5 "min_z" --type python

# Look for performance-related comments or issues
echo "Checking for performance considerations..."
rg -i "performance|processing time|optimization" --type python

# Check for test coverage
echo "Checking for related tests..."
fd -e py test_lidar -x cat {}

Length of output: 435


Script:

#!/bin/bash
# Search for z-axis dependencies and point cloud processing
echo "Checking z-axis dependencies..."
rg "min_z" -A 5

echo "Checking point cloud processing..."
rg "point_cloud|PointCloud" -A 5

echo "Checking performance considerations..."
rg -i "performance|processing time|optimization"

echo "Finding and checking test files..."
fd "test.*lidar.*\.py$" -x cat {}

Length of output: 36082

code/perception/src/Lanedetection_node.py (2)

45-47: Check missing parameter defaults
Parameters like "epsilon_clustering", "min_samples_clustering", etc., assume a valid value in the launch/config file. Ensure that sensible defaults are set if they are not provided, or warn the user.


121-123: Validate image encoding
When converting the ImageMsg (line 123), consider adding defensive checks (e.g., catch exceptions or verify the encoding). This helps prevent runtime errors if an unexpected image format arrives.

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

161-171: Check lane marking existence
Both lane_marking_left() and lane_marking_right() return None if no matching lane marking is found. Ensure callers handle None properly to avoid AttributeError.

code/perception/src/lane_position.py (1)

386-391: Handle empty or malformed distance arrays
When no valid distance data arrives, mask_lidarpoints returns an empty list. Confirm calling methods gracefully handle empty data to prevent index errors or unexpected behaviors down the pipeline.

code/mapping/src/path_test.py (1)

35-59: 🛠️ Refactor suggestion

Improve trajectory generation implementation.

Several concerns with the current implementation:

  1. Random trajectory generation might not be suitable for testing deterministic behaviors
  2. Hardcoded values (10 points, curve factors) should be configurable
  3. No validation of generated trajectory for feasibility
  4. Missing documentation for the trajectory generation algorithm

Consider:

  • Making the number of points configurable
  • Adding different trajectory patterns (straight, circle, figure-8)
  • Validating generated paths for maximum curvature
  • Documenting the trajectory generation approach

Also, the orientation (quaternion) is set to identity (w=1) without considering the path tangent, which might not be realistic for vehicle motion.

code/mapping/src/mapping_data_integration.py (2)

Line range hint 309-339: Verify map publishing conditions.

The map publishing conditions have changed to include lane markings. Ensure that the map is still published correctly when lane markings are not available.

✅ Verification successful

Map publishing conditions are properly handled

The code includes comprehensive null checks for all required components, including lane markings. The map publishing will safely return early if lane markings or any other required component is not available, preventing any potential issues.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if map publishing works without lane markings

# Test: Search for error handling in map publishing
rg -A 5 "publish_new_map" | rg "return|None"

# Test: Search for map publishing with null checks
rg -A 10 "map_publisher.publish"

Length of output: 1659


Script:

#!/bin/bash
# Find variable initialization and the complete publish_new_map method
ast-grep --pattern 'def publish_new_map($$$) { $$$ }'

# Search for variable initialization
rg "self\.lidar_data =|self\.lanemarkings =|self\.radar_marker_data ="

# Search for any error handling patterns
rg -B 2 -A 2 "if.*None.*return"

Length of output: 576


Script:

#!/bin/bash
# Get the complete publish_new_map method implementation
rg -U "def publish_new_map.*?(?=def|\z)" -A 50 --multiline

# Look for map publishing patterns
rg -B 5 -A 5 "map_publisher"

Length of output: 3562


22-26: LGTM! The new imports and subscriptions are correctly set up.

The changes properly integrate the new lane markings functionality with appropriate ROS message types and subscriptions.

Also applies to: 53-61

code/requirements.txt (2)

21-30: Verify version constraints for critical dependencies.

Several new ML and visualization dependencies are added without version constraints. This could lead to compatibility issues or breaking changes in the future.

Let's check for known compatibility issues and latest versions:

✅ Verification successful

Add version constraints for critical dependencies

Based on the analysis of package versions and stability:

  • Add minimum version constraints for Pillow (>=11.0.0) and tensorboardX (>=2.6.0) as they handle critical image processing and visualization tasks
  • Current packages are stable and actively maintained, except yacs which is stable but unmaintained
  • No immediate breaking changes or critical deprecations found in recent versions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check latest versions and compatibility
for pkg in tqdm yacs Pillow tensorboardX seaborn prefetch_generator imageio; do
    echo "Checking $pkg..."
    curl -s "https://pypi.org/pypi/$pkg/json" | jq -r '. | "Latest version: \(.info.version)\nRelease date: \(.releases[.info.version][0].upload_time)\n"'
done

# Check for known vulnerabilities
pip-audit tqdm yacs Pillow tensorboardX seaborn prefetch_generator imageio

Length of output: 2575


Script:

#!/bin/bash
# Check for known vulnerabilities using safety
pip install safety
safety check tqdm yacs Pillow tensorboardX seaborn prefetch_generator imageio

# Check for deprecation notices in latest releases
for pkg in tqdm yacs Pillow tensorboardX seaborn prefetch_generator imageio; do
    echo "=== Checking $pkg changelog ==="
    curl -s "https://pypi.org/pypi/$pkg/json" | jq -r '.info.description' | grep -i -A 5 "deprecat\|breaking" || true
done

Length of output: 7766


24-26: Verify compatibility between matplotlib and PyYAML versions.

The version constraints matplotlib>=3.2.2 and PyYAML>=5.3 need verification against the existing dependencies to ensure no conflicts arise.

✅ Verification successful

Dependencies are compatible

The dry-run installation test confirms that matplotlib>=3.2.2 and PyYAML>=5.3 are compatible with each other and with other major dependencies in the project. No version conflicts were detected.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check compatibility with existing dependencies
pip install matplotlib==3.2.2 PyYAML==5.3 torch==2.1.2 numpy==1.23.5 --dry-run

Length of output: 6151

doc/perception/Lanedetection_node.md (1)

10-11: Verify the system diagram accessibility.

The system diagram is crucial for understanding the node's architecture. Please ensure:

  1. The image file exists at the specified path
  2. The image is accessible to all team members
  3. The diagram is up-to-date with the implementation
✅ Verification successful

System diagram is properly accessible and up-to-date

The diagram file exists with appropriate read permissions for all team members. The recent modification date (Jan 26) and the inclusion of an editable Lucid chart link ensures the team has access to both the current diagram and the ability to collaborate on updates.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the system diagram exists and is accessible
test -f doc/assets/perception/Overview_lanedetection_node.jpg && echo "Image exists" || echo "Image missing"

# Get image metadata to verify last modification date
stat doc/assets/perception/Overview_lanedetection_node.jpg

Length of output: 254


Script:

#!/bin/bash
# Get detailed file information including permissions and timestamps
ls -la doc/assets/perception/Overview_lanedetection_node.jpg

# Check file type
file doc/assets/perception/Overview_lanedetection_node.jpg

# Get file size in human-readable format
du -h doc/assets/perception/Overview_lanedetection_node.jpg

Length of output: 444

code/agent/config/rviz_config.rviz (1)

3-9: LGTM! The RViz configuration changes look good.

The changes enhance the visualization setup by:

  • Adjusting display dimensions for better visibility
  • Adding a new Path entry for test trajectory visualization
  • Optimizing window layout

Also applies to: 69-69, 212-235, 289-305

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

420-421: LGTM! The new Lanemarking attributes are well-defined.

The addition of position_index and predicted attributes with proper initialization looks good.

Also applies to: 427-433


437-440: LGTM! The serialization methods are properly updated.

The _extract_kwargs and to_ros_msg methods correctly handle the new attributes.

Also applies to: 445-450

Comment on lines 55 to 56
position_index=1,
predicted=False,
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

Enhance test coverage for new Lanemarking parameters.

The current test only verifies basic conversion with fixed values. Consider adding test cases for:

  • Negative position indices
  • Edge cases for position_index (-128, 127)
  • Both true/false cases for predicted flag
  • Invalid position_index values

Example additional test cases:

def test_lanemark_conversion_edge_cases():
    # Test negative index
    e1 = entity.Lanemarking(
        confidence=1.0, priority=1.0, shape=get_shape(),
        transform=get_transform(), style=entity.Lanemarking.Style.SOLID,
        position_index=-1, predicted=True
    )
    msg1 = e1.to_ros_msg()
    e1_conv = entity.Entity.from_ros_msg(msg1)
    assert e1 == e1_conv

    # Test maximum index
    e2 = entity.Lanemarking(
        confidence=1.0, priority=1.0, shape=get_shape(),
        transform=get_transform(), style=entity.Lanemarking.Style.SOLID,
        position_index=127, predicted=False
    )
    msg2 = e2.to_ros_msg()
    e2_conv = entity.Entity.from_ros_msg(msg2)
    assert e2 == e2_conv

Comment on lines 92 to 94
<node pkg="perception" type="Lanedetection_node.py" name="Lanedetection" output="screen">
<param name="role_name" value="$(arg role_name)" />
</node>
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

Add camera configuration parameters to the Lanedetection node.

The node appears to be vision-based but lacks essential camera configuration parameters. Consider adding similar camera parameters as the lane_position node for consistency and configurability.

Comment on lines 107 to 128
<node pkg="perception" type="lane_position.py" name="lane_position" output="screen">
<param name="camera_x" value="0.0"/>
<param name="camera_y" value="0.0"/>
<param name="camera_z" value="1.7"/>
<param name="camera_roll" value="0.0"/>
<param name="camera_pitch" value="0.0"/>
<param name="camera_yaw" value="0.0"/>
<param name="camera_width" value="1280"/>
<param name="camera_height" value="720"/>
<param name="camera_fov" value="100"/>
<param name="epsilon_clustering" value="1.8"/>
<param name="min_samples_clustering" value="4"/>
<param name="angle_weight" value="0.3"/>
<param name="size_weight" value="0.3"/>
<param name="std_dev_weight" value="0.4"/>
<param name="angle_normalization" value="25"/>
<param name="size_normalization" value="15"/>
<param name="std_dev_normalization" value="0.1"/>
<param name="angle_prediction_threshold" value="5"/>
<param name="confidence_threshold" value="0.6"/>
<param name="y_tolerance" value="1.0"/>
</node>
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

Document parameters and avoid hardcoded values.

The node configuration has several issues:

  1. Camera parameters are hardcoded without explanation
  2. Clustering parameters use magic numbers
  3. No documentation for parameter meanings and valid ranges

Consider:

  1. Moving parameters to a separate config file
  2. Adding documentation for each parameter
  3. Using meaningful variable names for magic numbers

Example config structure:

camera:
  position:
    x: 0.0  # Distance from vehicle center in meters
    y: 0.0  # Lateral offset in meters
    z: 1.7  # Height from ground in meters
  orientation:
    roll: 0.0   # Roll angle in degrees
    pitch: 0.0  # Pitch angle in degrees
    yaw: 0.0    # Yaw angle in degrees
  properties:
    width: 1280  # Image width in pixels
    height: 720  # Image height in pixels
    fov: 100    # Field of view in degrees

clustering:
  epsilon: 1.8   # Maximum distance between points in cluster
  min_samples: 4 # Minimum points to form a cluster
  weights:
    angle: 0.3    # Weight for angle-based clustering
    size: 0.3     # Weight for size-based clustering
    std_dev: 0.4  # Weight for standard deviation

Comment on lines 104 to 108
topic="test_trajectory",
msg_type=Path,
callback=self.callback,
qos_profile=1,
)
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

Add error handling for Path messages.

The subscription to test_trajectory lacks error handling for invalid messages. Consider adding try-catch blocks and message validation.

 def callback(self, data: Path):
+    try:
+        if not data.poses:
+            self.logwarn("Received empty path message")
+            return
         self.local_trajectory = data
+    except AttributeError as e:
+        self.logerr(f"Invalid path message: {e}")

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 139 to 151
self.loginfo(
"Trajectory check: " + str(map.check_trajectory(self.local_trajectory))
)
if map.check_trajectory(self.local_trajectory) == 2:
for lane in self.lanemarkings:
if lane.position_index == 1:
lane.predicted = True

if map.check_trajectory(self.local_trajectory) == 3:
for lane in self.lanemarkings:
if lane.position_index == -1:
lane.predicted = True

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

Improve trajectory check logic.

The code has several issues:

  1. Magic numbers (2, 3) in trajectory check comparisons
  2. No error handling for invalid trajectory checks
  3. Redundant trajectory check calls
+    # Define trajectory check results as constants
+    LEFT_TRAJECTORY = 2
+    RIGHT_TRAJECTORY = 3
+
+    try:
+        check_result = map.check_trajectory(self.local_trajectory)
         self.loginfo(
-            "Trajectory check: " + str(map.check_trajectory(self.local_trajectory))
+            f"Trajectory check: {check_result}"
         )
-        if map.check_trajectory(self.local_trajectory) == 2:
+        if check_result == LEFT_TRAJECTORY:
             for lane in self.lanemarkings:
                 if lane.position_index == 1:
                     lane.predicted = True
 
-        if map.check_trajectory(self.local_trajectory) == 3:
+        elif check_result == RIGHT_TRAJECTORY:
             for lane in self.lanemarkings:
                 if lane.position_index == -1:
                     lane.predicted = True
+    except Exception as e:
+        self.logerr(f"Error in trajectory check: {e}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self.loginfo(
"Trajectory check: " + str(map.check_trajectory(self.local_trajectory))
)
if map.check_trajectory(self.local_trajectory) == 2:
for lane in self.lanemarkings:
if lane.position_index == 1:
lane.predicted = True
if map.check_trajectory(self.local_trajectory) == 3:
for lane in self.lanemarkings:
if lane.position_index == -1:
lane.predicted = True
# Define trajectory check results as constants
LEFT_TRAJECTORY = 2
RIGHT_TRAJECTORY = 3
try:
check_result = map.check_trajectory(self.local_trajectory)
self.loginfo(
f"Trajectory check: {check_result}"
)
if check_result == LEFT_TRAJECTORY:
for lane in self.lanemarkings:
if lane.position_index == 1:
lane.predicted = True
elif check_result == RIGHT_TRAJECTORY:
for lane in self.lanemarkings:
if lane.position_index == -1:
lane.predicted = True
except Exception as e:
self.logerr(f"Error in trajectory check: {e}")

Comment on lines 135 to 151
def lanemarkings_callback(self, data: MapMsg):
map = Map.from_ros_msg(data)
self.lanemarkings = map.entities_without_hero()

self.loginfo(
"Trajectory check: " + str(map.check_trajectory(self.local_trajectory))
)
if map.check_trajectory(self.local_trajectory) == 2:
for lane in self.lanemarkings:
if lane.position_index == 1:
lane.predicted = True

if map.check_trajectory(self.local_trajectory) == 3:
for lane in self.lanemarkings:
if lane.position_index == -1:
lane.predicted = True

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

Improve trajectory checking logic and error handling.

The lane marking callback needs:

  • Error handling for invalid trajectory data
  • Documentation for the trajectory check return values (what do 2 and 3 mean?)
  • Consider extracting the prediction logic into a separate method
 def lanemarkings_callback(self, data: MapMsg):
+    """Process lane markings and update their predicted status based on trajectory.
+    
+    Args:
+        data (MapMsg): The map message containing lane markings
+    """
     map = Map.from_ros_msg(data)
     self.lanemarkings = map.entities_without_hero()
 
+    if not hasattr(self, 'local_trajectory') or not self.local_trajectory:
+        self.logwarn("No trajectory data available for checking")
+        return
+
     self.loginfo(
         "Trajectory check: " + str(map.check_trajectory(self.local_trajectory))
     )
+    
+    # Extract prediction logic to a separate method for better readability
+    self._update_lane_predictions(map.check_trajectory(self.local_trajectory))
+
+def _update_lane_predictions(self, check_result: int) -> None:
+    """Update lane marking predictions based on trajectory check result.
+    
+    Args:
+        check_result (int): The result from trajectory checking
+            2: Right lane deviation detected
+            3: Left lane deviation detected
+    """
     if map.check_trajectory(self.local_trajectory) == 2:
         for lane in self.lanemarkings:
             if lane.position_index == 1:
                 lane.predicted = True
 
     if map.check_trajectory(self.local_trajectory) == 3:
         for lane in self.lanemarkings:
             if lane.position_index == -1:
                 lane.predicted = True

Committable suggestion skipped: line range outside the PR's diff.

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 (4)
code/mapping/ext_modules/mapping_common/map.py (4)

181-214: Unify front/back filtering & fix docstring typo.

  1. The “entities_in_area” logic is duplicated for front/back. You could unify both loops by conditionally checking entity.transform.translation().x() > 0 and distance in the same loop, reducing duplication.
  2. There’s a small spelling issue in the docstring at line 185 (“returend” should be “returned”).
-            behind the car, are returend too.
+            behind the car, are returned too.

# (Optional) Example unification snippet
 def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
     entities = self.entities_without_hero()
     entities_in_area = []
     for e in entities:
         distance = np.sqrt(e.transform.translation().x()**2 + e.transform.translation().y()**2)
-        if only_in_front:
-            if e.transform.translation().x() > 0 and distance < radius:
-                entities_in_area.append(e)
-        else:
-            if distance < radius:
-                entities_in_area.append(e)
+        # Combine logic for front/back
+        if distance < radius and (not only_in_front or e.transform.translation().x() > 0):
+            entities_in_area.append(e)
     return entities_in_area

268-273: Return type mismatch.

Although this method signature suggests returning an Entity, you also return None. Consider updating the return type to Optional[Entity] or ensuring the docstring explains that None may be returned.

-    def lane_marking_left(self) -> Entity:
+    def lane_marking_left(self) -> Optional[Entity]:
🧰 Tools
🪛 Ruff (0.8.2)

269-269: Import entity from line 12 shadowed by loop variable

(F402)


274-279: Match return type with possible None.

Same as above: update signature and/or docstring to clarify that None can be returned.

-    def lane_marking_right(self) -> Entity:
+    def lane_marking_right(self) -> Optional[Entity]:
🧰 Tools
🪛 Ruff (0.8.2)

275-275: Import entity from line 12 shadowed by loop variable

(F402)


280-336: Consolidate nested conditionals & clarify intersection checks.

  1. Combine nested checks for left/right lane into a single if statement each (e.g., if lane is not None and shapely.intersection(...):). This addresses the static analysis hint (SIM102).
  2. Be explicit about geometry intersection checks by testing intersection_result.is_empty to clarify logic.
--- a/map.py
+++ b/map.py
@@ -317,10 +317,9 @@
     left_lane = self.lane_marking_left()
     if left_lane is not None:
-        if shapely.intersection(
-            left_lane.to_shapely().poly, local_trajectory_buffer
-        ):
-            return 2
+        intersection_result = shapely.intersection(left_lane.to_shapely().poly, local_trajectory_buffer)
+        if intersection_result and not intersection_result.is_empty:
+            return 2

     # Check right lane
     right_lane = self.lane_marking_right()
@@ -327,10 +326,9 @@
     if right_lane is not None:
-        if shapely.intersection(
-            right_lane.to_shapely().poly, local_trajectory_buffer
-        ):
-            return 3
+        intersection_result = shapely.intersection(right_lane.to_shapely().poly, local_trajectory_buffer)
+        if intersection_result and not intersection_result.is_empty:
+            return 3
🧰 Tools
🪛 Ruff (0.8.2)

307-307: Import entity from line 12 shadowed by loop variable

(F402)


318-321: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)


327-330: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8cb86c3 and 4a63321.

📒 Files selected for processing (2)
  • code/mapping/ext_modules/mapping_common/map.py (4 hunks)
  • code/mapping/src/mapping_data_integration.py (4 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
code/mapping/src/mapping_data_integration.py

58-58: SyntaxError: Expected a statement


58-58: SyntaxError: Expected a statement


58-58: SyntaxError: Expected a statement


58-58: SyntaxError: Expected a statement


58-58: SyntaxError: Expected a statement


58-58: SyntaxError: Simple statements must be separated by newlines or semicolons


58-58: SyntaxError: Simple statements must be separated by newlines or semicolons


58-58: SyntaxError: Simple statements must be separated by newlines or semicolons


111-111: SyntaxError: unindent does not match any outer indentation level


154-154: SyntaxError: Expected a statement


154-154: SyntaxError: Expected a statement


154-154: SyntaxError: Expected a statement


154-154: SyntaxError: Expected a statement


154-154: SyntaxError: Expected a statement


154-154: SyntaxError: Simple statements must be separated by newlines or semicolons


154-154: SyntaxError: Simple statements must be separated by newlines or semicolons


154-154: SyntaxError: Simple statements must be separated by newlines or semicolons

code/mapping/ext_modules/mapping_common/map.py

269-269: Import entity from line 12 shadowed by loop variable

(F402)


275-275: Import entity from line 12 shadowed by loop variable

(F402)


307-307: Import entity from line 12 shadowed by loop variable

(F402)


318-321: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)


327-330: 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 (4)
code/mapping/src/mapping_data_integration.py (2)

111-113: Add error handling for Path messages.

This addition sets self.local_trajectory but doesn’t account for invalid or empty Path messages. Consider validating data.poses or catching attribute errors here.

def callback(self, data: Path):
+    if not data.poses:
+        self.logwarn("Received empty or invalid Path message.")
+        return
    self.local_trajectory = data
🧰 Tools
🪛 Ruff (0.8.2)

111-111: SyntaxError: unindent does not match any outer indentation level


345-345: Looks good.

No functional changes on this line; the invocation of self.map_publisher.publish(msg) is clear and consistent with the rest of the method.

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

6-6: Import usage appears valid.

This import for shapely.plotting may be used for any advanced debugging or visualization; no issue flagged by static analysis.


19-19: Nav_msgs import is appropriate.

The import of Path from nav_msgs.msg is valid and used in the new check_trajectory method.

Comment on lines 57 to 58
self.lidar_data = None
>>>>>>>>> Temporary merge branch 2
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Resolve merge conflict.

There’s a merge conflict marker at line 58 (>>>>>>>>> Temporary merge branch 2) that breaks Python syntax and needs removal or resolution.

 57     self.lidar_data = None
-58     >>>>>>>>> Temporary merge branch 2
+58     # (Resolve or remove the conflict marker)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self.lidar_data = None
>>>>>>>>> Temporary merge branch 2
self.lidar_data = None
# (Resolve or remove the conflict marker)
🧰 Tools
🪛 Ruff (0.8.2)

58-58: SyntaxError: Expected a statement


58-58: SyntaxError: Expected a statement


58-58: SyntaxError: Expected a statement


58-58: SyntaxError: Expected a statement


58-58: SyntaxError: Expected a statement


58-58: SyntaxError: Simple statements must be separated by newlines or semicolons


58-58: SyntaxError: Simple statements must be separated by newlines or semicolons


58-58: SyntaxError: Simple statements must be separated by newlines or semicolons

@@ -146,6 +151,7 @@
def lidar_callback(self, data: PointCloud2):
self.lidar_data = data

<<<<<<<<< Temporary merge branch 1
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Resolve merge conflict.

Conflicting marker (<<<<<<<<< Temporary merge branch 1) at line 154 breaks Python syntax. Remove or resolve it.

-154 <<<<<<<<< Temporary merge branch 1
+# (Resolve or remove the conflict marker)

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.8.2)

154-154: SyntaxError: Expected a statement


154-154: SyntaxError: Expected a statement


154-154: SyntaxError: Expected a statement


154-154: SyntaxError: Expected a statement


154-154: SyntaxError: Expected a statement


154-154: SyntaxError: Simple statements must be separated by newlines or semicolons


154-154: SyntaxError: Simple statements must be separated by newlines or semicolons


154-154: SyntaxError: Simple statements must be separated by newlines or semicolons

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 (2)
code/mapping/src/mapping_data_integration.py (2)

110-112: ⚠️ Potential issue

Add error handling for Path messages.

The callback lacks error handling for invalid messages. Consider adding try-catch blocks and message validation.

 def callback(self, data: Path):
+    try:
+        if not data.poses:
+            self.logwarn("Received empty path message")
+            return
         self.local_trajectory = data
+    except AttributeError as e:
+        self.logerr(f"Invalid path message: {e}")

153-153: ⚠️ Potential issue

Resolve merge conflict.

There's a merge conflict marker (<<<<<<<<< Temporary merge branch 1) that breaks Python syntax.

-<<<<<<<<< Temporary merge branch 1
+# (Resolve or remove the conflict marker)
🧰 Tools
🪛 Ruff (0.8.2)

153-153: SyntaxError: Expected a statement


153-153: SyntaxError: Expected a statement


153-153: SyntaxError: Expected a statement


153-153: SyntaxError: Expected a statement


153-153: SyntaxError: Expected a statement


153-153: SyntaxError: Simple statements must be separated by newlines or semicolons


153-153: SyntaxError: Simple statements must be separated by newlines or semicolons


153-153: SyntaxError: Simple statements must be separated by newlines or semicolons

🧹 Nitpick comments (2)
code/mapping/src/mapping_data_integration.py (2)

57-57: Add type hints for consistency.

The lanemarkings attribute should have type hints to maintain consistency with other class attributes.

-    self.lanemarkings = None
+    lanemarkings: Optional[List[Entity]] = None

344-344: Add documentation and validation for lane markings.

The map publishing logic should include:

  1. Documentation about the expected format and structure of lane markings
  2. Validation of lane marking data before publishing
         if self.lanemarkings is not None and self.get_param("~enable_lane_marker"):
+            # Validate lane markings before publishing
+            valid_markings = [
+                marking for marking in self.lanemarkings 
+                if hasattr(marking, 'position_index')
+            ]
+            if len(valid_markings) != len(self.lanemarkings):
+                self.logwarn("Some lane markings have invalid format")
             entities.extend(self.lanemarkings)

Consider adding docstring:

def publish_new_map(self, timer_event=None):
    """Publish a new map with hero car, sensor data, and lane markings.
    
    Lane markings are expected to have:
    - position_index: int (-1 for left, 1 for right)
    - predicted: bool (True if predicted by trajectory)
    """
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4a63321 and 6db0192.

📒 Files selected for processing (2)
  • code/mapping/src/mapping_data_integration.py (4 hunks)
  • code/mapping/src/path_test.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/mapping/src/path_test.py
🧰 Additional context used
🪛 Ruff (0.8.2)
code/mapping/src/mapping_data_integration.py

153-153: SyntaxError: Expected a statement


153-153: SyntaxError: Expected a statement


153-153: SyntaxError: Expected a statement


153-153: SyntaxError: Expected a statement


153-153: SyntaxError: Expected a statement


153-153: SyntaxError: Simple statements must be separated by newlines or semicolons


153-153: SyntaxError: Simple statements must be separated by newlines or semicolons


153-153: SyntaxError: Simple statements must be separated by newlines or semicolons

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image

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: 4

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6db0192 and 7b03578.

📒 Files selected for processing (2)
  • code/mapping/ext_modules/mapping_common/map.py (4 hunks)
  • code/mapping/src/mapping_data_integration.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/mapping/src/mapping_data_integration.py
🧰 Additional context used
🪛 Ruff (0.8.2)
code/mapping/ext_modules/mapping_common/map.py

318-321: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)


327-330: 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

Comment on lines 268 to 278
def lane_marking_left(self) -> Entity:
for entry in self.entities:
if entry.flags._is_lanemark is True and entry.position_index == 1:
return entry
return None

def lane_marking_right(self) -> Entity:
for entry in self.entities:
if entry.flags._is_lanemark is True and entry.position_index == -1:
return entry
return None
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

Fix return type annotation and add documentation.

The methods have incorrect return type annotations and lack proper documentation. Consider consolidating the duplicate logic into a helper method.

-    def lane_marking_left(self) -> Entity:
+    def lane_marking_left(self) -> Optional[Entity]:
+        """Returns the left lane marking entity if it exists.
+
+        Returns:
+            Optional[Entity]: The left lane marking entity or None if not found
+        """
+        return self._get_lane_marking(position_index=1)
+
-        for entry in self.entities:
-            if entry.flags._is_lanemark is True and entry.position_index == 1:
-                return entry
-        return None
+    def lane_marking_right(self) -> Optional[Entity]:
+        """Returns the right lane marking entity if it exists.
+
+        Returns:
+            Optional[Entity]: The right lane marking entity or None if not found
+        """
+        return self._get_lane_marking(position_index=-1)
+
+    def _get_lane_marking(self, position_index: int) -> Optional[Entity]:
+        """Helper method to get lane marking by position index.
+
+        Args:
+            position_index (int): 1 for left, -1 for right
-    def lane_marking_right(self) -> Entity:
+        Returns:
+            Optional[Entity]: The lane marking entity or None if not found
+        """
         for entry in self.entities:
-            if entry.flags._is_lanemark is True and entry.position_index == -1:
+            if entry.flags._is_lanemark and entry.position_index == position_index:
                 return entry
         return None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def lane_marking_left(self) -> Entity:
for entry in self.entities:
if entry.flags._is_lanemark is True and entry.position_index == 1:
return entry
return None
def lane_marking_right(self) -> Entity:
for entry in self.entities:
if entry.flags._is_lanemark is True and entry.position_index == -1:
return entry
return None
def lane_marking_left(self) -> Optional[Entity]:
"""Returns the left lane marking entity if it exists.
Returns:
Optional[Entity]: The left lane marking entity or None if not found
"""
return self._get_lane_marking(position_index=1)
def lane_marking_right(self) -> Optional[Entity]:
"""Returns the right lane marking entity if it exists.
Returns:
Optional[Entity]: The right lane marking entity or None if not found
"""
return self._get_lane_marking(position_index=-1)
def _get_lane_marking(self, position_index: int) -> Optional[Entity]:
"""Helper method to get lane marking by position index.
Args:
position_index (int): 1 for left, -1 for right
Returns:
Optional[Entity]: The lane marking entity or None if not found
"""
for entry in self.entities:
if entry.flags._is_lanemark and entry.position_index == position_index:
return entry
return None

Comment on lines 181 to 213
def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
""" Returns the entities without the hero car
in a given radius around the hero car.
Flag only_in_front decides if entities
behind the car, are returend too.
Only checks if the first entity is_hero

Returns:
List[Entity]: Entities in front and/or back of hero
without the hero car in given radius
"""
entities = self.entities_without_hero()
entities_in_area = []

if only_in_front:
for entry in entities:
distance = np.sqrt(
np.power(entry.transform.translation().x(), 2)
+ np.power(entry.transform.translation().y(), 2)
)
if entry.transform.translation().x() > 0 and distance < radius:
entities_in_area.append(entry)
return entities_in_area
else:
for entry in entities:
distance = np.sqrt(
np.power(entry.transform.translation().x(), 2)
+ np.power(entry.transform.translation().y(), 2)
)
if distance < radius:
entities_in_area.append(entry)

return entities_in_area
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

Refactor to improve code quality and performance.

  1. Add type hints and validation for the radius parameter
  2. Remove code duplication in distance calculation
  3. Consider using numpy vectorization for better performance
-    def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
+    def entities_in_area(self, radius: float, only_in_front: bool = True) -> List[Entity]:
+        """Returns entities within a given radius around the hero car.
+
+        Args:
+            radius (float): The radius to search for entities (must be positive)
+            only_in_front (bool): If True, only return entities in front of the car
+
+        Returns:
+            List[Entity]: Filtered list of entities within the radius
+
+        Raises:
+            ValueError: If radius is negative
+        """
+        if radius < 0:
+            raise ValueError("Radius must be positive")
+
         entities = self.entities_without_hero()
-        entities_in_area = []
-
-        if only_in_front:
-            for entry in entities:
-                distance = np.sqrt(
-                    np.power(entry.transform.translation().x(), 2)
-                    + np.power(entry.transform.translation().y(), 2)
-                )
-                if entry.transform.translation().x() > 0 and distance < radius:
-                    entities_in_area.append(entry)
-            return entities_in_area
-        else:
-            for entry in entities:
-                distance = np.sqrt(
-                    np.power(entry.transform.translation().x(), 2)
-                    + np.power(entry.transform.translation().y(), 2)
-                )
-                if distance < radius:
-                    entities_in_area.append(entry)
-
-        return entities_in_area
+        # Vectorized distance calculation
+        positions = np.array([[e.transform.translation().x(), e.transform.translation().y()] 
+                            for e in entities])
+        distances = np.sqrt(np.sum(positions**2, axis=1))
+        
+        if only_in_front:
+            mask = (positions[:, 0] > 0) & (distances < radius)
+        else:
+            mask = distances < radius
+            
+        return [e for i, e in enumerate(entities) if mask[i]]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
""" Returns the entities without the hero car
in a given radius around the hero car.
Flag only_in_front decides if entities
behind the car, are returend too.
Only checks if the first entity is_hero
Returns:
List[Entity]: Entities in front and/or back of hero
without the hero car in given radius
"""
entities = self.entities_without_hero()
entities_in_area = []
if only_in_front:
for entry in entities:
distance = np.sqrt(
np.power(entry.transform.translation().x(), 2)
+ np.power(entry.transform.translation().y(), 2)
)
if entry.transform.translation().x() > 0 and distance < radius:
entities_in_area.append(entry)
return entities_in_area
else:
for entry in entities:
distance = np.sqrt(
np.power(entry.transform.translation().x(), 2)
+ np.power(entry.transform.translation().y(), 2)
)
if distance < radius:
entities_in_area.append(entry)
return entities_in_area
def entities_in_area(self, radius: float, only_in_front: bool = True) -> List[Entity]:
"""Returns entities within a given radius around the hero car.
Args:
radius (float): The radius to search for entities (must be positive)
only_in_front (bool): If True, only return entities in front of the car
Returns:
List[Entity]: Filtered list of entities within the radius
Raises:
ValueError: If radius is negative
"""
if radius < 0:
raise ValueError("Radius must be positive")
entities = self.entities_without_hero()
# Vectorized distance calculation
positions = np.array([[e.transform.translation().x(), e.transform.translation().y()]
for e in entities])
distances = np.sqrt(np.sum(positions**2, axis=1))
if only_in_front:
mask = (positions[:, 0] > 0) & (distances < radius)
else:
mask = distances < radius
return [e for i, e in enumerate(entities) if mask[i]]

Comment on lines 280 to 335
def check_trajectory(self, local_path: Path) -> int:
"""
Checks the calculated local path on multiple conditions
return -1: local_path is None
return 0: Path has no obstacles / infringments
return 1: Path is planned through a gap,
which is too small for the car (without lanes)
return 2: Path cuts the left lane marking
return 3: Path cuts the right lane marking
"""

# check if there is a local path
if local_path.poses is None:
return -1

# transform local path to shapely object
trajectory_shapely_points = []
for pos in local_path.poses:
trajectory_shapely_points.append(
shapely.Point(pos.pose.position.x, pos.pose.position.y)
)
local_trajectory = shapely.LineString(trajectory_shapely_points)
# widen the Linestring to an area representing the cars width
local_trajectory_buffer = local_trajectory.buffer(1, 3)

# Check path collision with other entities
obstacles = self.entities_in_area(10)
for entry in obstacles:
if (
shapely.intersection(entry.to_shapely().poly, local_trajectory_buffer)
and entry.flags._is_lanemark is False
):
return 1

# decrease buffer size for lane checkking
local_trajectory_buffer = local_trajectory.buffer(0.5, 3)
# Check left lane
left_lane = self.lane_marking_left()
if left_lane is not None:
if shapely.intersection(
left_lane.to_shapely().poly, local_trajectory_buffer
):
# given path crosses lane marking on the left
return 2

# Check right lane
right_lane = self.lane_marking_right()
if right_lane is not None:
if shapely.intersection(
right_lane.to_shapely().poly, local_trajectory_buffer
):
# given path crosses lane marking on the right
return 3

# given path is okay and is not colliding
return 0
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

Improve robustness and maintainability of trajectory checking.

  1. Extract magic numbers into named constants
  2. Add validation for empty poses list
  3. Simplify nested if statements
  4. Consider making the obstacle detection radius configurable
+    # Class-level constants
+    VEHICLE_WIDTH_BUFFER = 1.0  # meters
+    LANE_CHECK_BUFFER = 0.5     # meters
+    OBSTACLE_DETECTION_RADIUS = 10.0  # meters
+
     def check_trajectory(self, local_path: Path) -> int:
         """
         Checks the calculated local path on multiple conditions
+
+        Args:
+            local_path (Path): The path to check
+
+        Returns:
+            int: Status code indicating the path's validity:
+                -1: local_path is None or empty
+                 0: Path has no obstacles/infringements
+                 1: Path planned through gap too small for car
+                 2: Path cuts left lane marking
+                 3: Path cuts right lane marking
+
+        Raises:
+            ValueError: If local_path.poses is empty
         """
-        if local_path.poses is None:
+        if local_path.poses is None or not local_path.poses:
             return -1

         # transform local path to shapely object
         trajectory_shapely_points = []
         for pos in local_path.poses:
             trajectory_shapely_points.append(
                 shapely.Point(pos.pose.position.x, pos.pose.position.y)
             )
         local_trajectory = shapely.LineString(trajectory_shapely_points)
         # widen the Linestring to an area representing the cars width
-        local_trajectory_buffer = local_trajectory.buffer(1, 3)
+        local_trajectory_buffer = local_trajectory.buffer(self.VEHICLE_WIDTH_BUFFER, 3)

         # Check path collision with other entities
-        obstacles = self.entities_in_area(10)
+        obstacles = self.entities_in_area(self.OBSTACLE_DETECTION_RADIUS)
         for entry in obstacles:
-            if (
-                shapely.intersection(entry.to_shapely().poly, local_trajectory_buffer)
-                and entry.flags._is_lanemark is False
-            ):
+            if (not entry.flags._is_lanemark and 
+                shapely.intersection(entry.to_shapely().poly, local_trajectory_buffer)):
                 return 1

         # decrease buffer size for lane checking
-        local_trajectory_buffer = local_trajectory.buffer(0.5, 3)
+        local_trajectory_buffer = local_trajectory.buffer(self.LANE_CHECK_BUFFER, 3)
         
         # Check left lane
         left_lane = self.lane_marking_left()
-        if left_lane is not None:
-            if shapely.intersection(
-                left_lane.to_shapely().poly, local_trajectory_buffer
-            ):
-                return 2
+        if left_lane is not None and shapely.intersection(
+            left_lane.to_shapely().poly, local_trajectory_buffer):
+            return 2

         # Check right lane
         right_lane = self.lane_marking_right()
-        if right_lane is not None:
-            if shapely.intersection(
-                right_lane.to_shapely().poly, local_trajectory_buffer
-            ):
-                return 3
+        if right_lane is not None and shapely.intersection(
+            right_lane.to_shapely().poly, local_trajectory_buffer):
+            return 3

         return 0
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def check_trajectory(self, local_path: Path) -> int:
"""
Checks the calculated local path on multiple conditions
return -1: local_path is None
return 0: Path has no obstacles / infringments
return 1: Path is planned through a gap,
which is too small for the car (without lanes)
return 2: Path cuts the left lane marking
return 3: Path cuts the right lane marking
"""
# check if there is a local path
if local_path.poses is None:
return -1
# transform local path to shapely object
trajectory_shapely_points = []
for pos in local_path.poses:
trajectory_shapely_points.append(
shapely.Point(pos.pose.position.x, pos.pose.position.y)
)
local_trajectory = shapely.LineString(trajectory_shapely_points)
# widen the Linestring to an area representing the cars width
local_trajectory_buffer = local_trajectory.buffer(1, 3)
# Check path collision with other entities
obstacles = self.entities_in_area(10)
for entry in obstacles:
if (
shapely.intersection(entry.to_shapely().poly, local_trajectory_buffer)
and entry.flags._is_lanemark is False
):
return 1
# decrease buffer size for lane checkking
local_trajectory_buffer = local_trajectory.buffer(0.5, 3)
# Check left lane
left_lane = self.lane_marking_left()
if left_lane is not None:
if shapely.intersection(
left_lane.to_shapely().poly, local_trajectory_buffer
):
# given path crosses lane marking on the left
return 2
# Check right lane
right_lane = self.lane_marking_right()
if right_lane is not None:
if shapely.intersection(
right_lane.to_shapely().poly, local_trajectory_buffer
):
# given path crosses lane marking on the right
return 3
# given path is okay and is not colliding
return 0
# Class-level constants
VEHICLE_WIDTH_BUFFER = 1.0 # meters
LANE_CHECK_BUFFER = 0.5 # meters
OBSTACLE_DETECTION_RADIUS = 10.0 # meters
def check_trajectory(self, local_path: Path) -> int:
"""
Checks the calculated local path on multiple conditions
Args:
local_path (Path): The path to check
Returns:
int: Status code indicating the path's validity:
-1: local_path is None or empty
0: Path has no obstacles/infringements
1: Path planned through gap too small for car
2: Path cuts left lane marking
3: Path cuts right lane marking
Raises:
ValueError: If local_path.poses is empty
"""
if local_path.poses is None or not local_path.poses:
return -1
# transform local path to shapely object
trajectory_shapely_points = []
for pos in local_path.poses:
trajectory_shapely_points.append(
shapely.Point(pos.pose.position.x, pos.pose.position.y)
)
local_trajectory = shapely.LineString(trajectory_shapely_points)
# widen the Linestring to an area representing the cars width
local_trajectory_buffer = local_trajectory.buffer(self.VEHICLE_WIDTH_BUFFER, 3)
# Check path collision with other entities
obstacles = self.entities_in_area(self.OBSTACLE_DETECTION_RADIUS)
for entry in obstacles:
if (not entry.flags._is_lanemark and
shapely.intersection(entry.to_shapely().poly, local_trajectory_buffer)):
return 1
# decrease buffer size for lane checking
local_trajectory_buffer = local_trajectory.buffer(self.LANE_CHECK_BUFFER, 3)
# Check left lane
left_lane = self.lane_marking_left()
if left_lane is not None and shapely.intersection(
left_lane.to_shapely().poly, local_trajectory_buffer):
return 2
# Check right lane
right_lane = self.lane_marking_right()
if right_lane is not None and shapely.intersection(
right_lane.to_shapely().poly, local_trajectory_buffer):
return 3
return 0
🧰 Tools
🪛 Ruff (0.8.2)

318-321: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)


327-330: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)


💡 Codebase verification

Unit tests needed for trajectory validation functionality

The codebase lacks test coverage for the new trajectory validation features. New tests should be added to code/mapping/tests/mapping_common/test_general.py to verify:

  • check_trajectory return values (-1, 0, 1, 2, 3)
  • entities_in_area with various scenarios
  • Lane marking detection logic
  • Edge cases with invalid inputs

Follow the existing test patterns in the codebase, particularly the structure in test_entity.py for consistency.

🔗 Analysis chain

Add unit tests for the new functionality.

The new methods need comprehensive unit tests to verify:

  1. Edge cases in entities_in_area (empty list, negative radius)
  2. Lane marking detection for various scenarios
  3. All possible return cases of check_trajectory

Let me help you verify the test coverage:

Would you like me to help generate comprehensive unit tests for these new methods?

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing test files
echo "Searching for test files..."
fd "test.*\.py$"

# Look for test cases related to the new functionality
echo -e "\nSearching for related test cases..."
rg -l "test.*(?:entities_in_area|lane_marking|check_trajectory)" --type py

Length of output: 601


Script:

#!/bin/bash
# Check content of test files in mapping_common for related tests
echo "=== Checking test_entity.py ==="
cat code/mapping/tests/mapping_common/test_entity.py

echo -e "\n=== Checking test_general.py ==="
cat code/mapping/tests/mapping_common/test_general.py

# Search for any trajectory or path related tests
echo -e "\n=== Searching for trajectory/path related tests ==="
rg "def test.*(?:trajectory|path)" --type py -A 5

Length of output: 3711

🧰 Tools
🪛 Ruff (0.8.2)

318-321: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)


327-330: Use a single if statement instead of nested if statements

Combine if statements using and

(SIM102)

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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7b03578 and 4182c57.

📒 Files selected for processing (1)
  • code/mapping/ext_modules/mapping_common/map.py (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image
🔇 Additional comments (4)
code/mapping/ext_modules/mapping_common/map.py (4)

288-291: LGTM! Well-documented constants with clear units.

The constants are well-named and documented with their respective units.


293-349: Add comprehensive test coverage.

The check_trajectory method lacks test coverage for its various return cases (-1, 0, 1, 2, 3). This is critical functionality that should be thoroughly tested.

Would you like me to help generate comprehensive unit tests for this method? The tests should cover:

  1. Invalid input (None/empty path)
  2. Valid path with no obstacles
  3. Path with vehicle width conflicts
  4. Path crossing left/right lane markings

268-286: 🛠️ Refactor suggestion

Consolidate duplicate logic and improve documentation.

The lane marking methods have duplicate logic that can be consolidated into a helper method.

-    def lane_marking_left(self) -> Optional[Entity]:
+    def lane_marking_left(self) -> Optional[Entity]:
+        """Returns the left lane marking entity if it exists.
+
+        Returns:
+            Optional[Entity]: The left lane marking entity or None if not found
+        """
+        return self._get_lane_marking(position_index=1)
+
+    def lane_marking_right(self) -> Optional[Entity]:
+        """Returns the right lane marking entity if it exists.
+
+        Returns:
+            Optional[Entity]: The right lane marking entity or None if not found
+        """
+        return self._get_lane_marking(position_index=-1)
+
+    def _get_lane_marking(self, position_index: int) -> Optional[Entity]:
+        """Helper method to get lane marking by position index.
+
+        Args:
+            position_index (int): 1 for left, -1 for right
+
+        Returns:
+            Optional[Entity]: The lane marking entity or None if not found
+        """
         for entry in self.entities:
-            if entry.flags._is_lanemark is True and entry.position_index == 1:
-                return entry
-        return None
-
-    def lane_marking_right(self) -> Optional[Entity]:
-        for entry in self.entities:
-            if entry.flags._is_lanemark is True and entry.position_index == -1:
+            if entry.flags._is_lanemark and entry.position_index == position_index:
                 return entry
         return None

Likely invalid or redundant comment.


181-213: 🛠️ Refactor suggestion

Optimize performance and improve robustness.

The method has several areas for improvement:

  1. Add validation for negative radius
  2. Remove code duplication in distance calculation
  3. Use numpy vectorization for better performance
  4. Improve documentation
-    def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
+    def entities_in_area(self, radius: float, only_in_front: bool = True) -> List[Entity]:
+        """Returns entities within a given radius around the hero car.
+
+        Args:
+            radius (float): The radius to search for entities (must be positive)
+            only_in_front (bool): If True, only return entities in front of the car
+
+        Returns:
+            List[Entity]: Filtered list of entities within the radius
+
+        Raises:
+            ValueError: If radius is negative
+        """
+        if radius < 0:
+            raise ValueError("Radius must be positive")
+
         entities = self.entities_without_hero()
-        entities_in_area = []
-
-        if only_in_front:
-            for entry in entities:
-                distance = np.sqrt(
-                    np.power(entry.transform.translation().x(), 2)
-                    + np.power(entry.transform.translation().y(), 2)
-                )
-                if entry.transform.translation().x() > 0 and distance < radius:
-                    entities_in_area.append(entry)
-            return entities_in_area
-        else:
-            for entry in entities:
-                distance = np.sqrt(
-                    np.power(entry.transform.translation().x(), 2)
-                    + np.power(entry.transform.translation().y(), 2)
-                )
-                if distance < radius:
-                    entities_in_area.append(entry)
-
-        return entities_in_area
+        # Vectorized distance calculation
+        positions = np.array([[e.transform.translation().x(), e.transform.translation().y()] 
+                            for e in entities])
+        distances = np.sqrt(np.sum(positions**2, axis=1))
+        
+        if only_in_front:
+            mask = (positions[:, 0] > 0) & (distances < radius)
+        else:
+            mask = distances < radius
+            
+        return [e for i, e in enumerate(entities) if mask[i]]

Likely invalid or redundant comment.

Comment on lines +293 to +349
def check_trajectory(self, local_path: Path) -> int:
"""
Checks the calculated local path on multiple conditions

Args:
local_path (Path): The path to check

Returns:
int: Status code indicating the path's validity:
-1: local_path is None or empty
0: Path has no obstacles/infringements
1: Path planned through gap too small for car
2: Path cuts left lane marking
3: Path cuts right lane marking

Raises:
ValueError: If local_path.poses is empty
"""
if local_path.poses is None or not local_path.poses:
return -1

# transform local path to shapely object
trajectory_shapely_points = []
for pos in local_path.poses:
trajectory_shapely_points.append(
shapely.Point(pos.pose.position.x, pos.pose.position.y)
)
local_trajectory = shapely.LineString(trajectory_shapely_points)
# widen the Linestring to an area representing the cars width
local_trajectory_buffer = local_trajectory.buffer(self.VEHICLE_WIDTH_BUFFER, 3)

# Check path collision with other entities
obstacles = self.entities_in_area(self.OBSTACLE_DETECTION_RADIUS)
for entry in obstacles:
if not entry.flags._is_lanemark and shapely.intersection(
entry.to_shapely().poly, local_trajectory_buffer
):
return 1

# decrease buffer size for lane checking
local_trajectory_buffer = local_trajectory.buffer(self.LANE_CHECK_BUFFER, 3)

# Check left lane
left_lane = self.lane_marking_left()
if left_lane is not None and shapely.intersection(
left_lane.to_shapely().poly, local_trajectory_buffer
):
return 2

# Check right lane
right_lane = self.lane_marking_right()
if right_lane is not None and shapely.intersection(
right_lane.to_shapely().poly, local_trajectory_buffer
):
return 3

return 0
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

Verification confirms missing test coverage for critical trajectory validation.

The codebase lacks unit tests for the check_trajectory method, which is responsible for crucial safety checks including:

  • Path validation
  • Obstacle collision detection
  • Lane marking violation checks

The magic number '3' in buffer calls is also confirmed to be hardcoded without explanation.

🔗 Analysis chain

Improve robustness and maintainability of trajectory checking.

The method has several areas for improvement:

  1. Extract magic number '3' in buffer calls into a constant
  2. Simplify nested conditions
  3. Add comprehensive unit tests
+    # Additional class constant
+    BUFFER_QUADRANT_SEGMENTS = 3  # Number of segments per quadrant for buffer operations
+
     def check_trajectory(self, local_path: Path) -> int:
         # ... existing docstring ...
         local_trajectory = shapely.LineString(trajectory_shapely_points)
-        local_trajectory_buffer = local_trajectory.buffer(self.VEHICLE_WIDTH_BUFFER, 3)
+        local_trajectory_buffer = local_trajectory.buffer(
+            self.VEHICLE_WIDTH_BUFFER, 
+            self.BUFFER_QUADRANT_SEGMENTS
+        )
         
         # ... rest of the method ...
-        local_trajectory_buffer = local_trajectory.buffer(self.LANE_CHECK_BUFFER, 3)
+        local_trajectory_buffer = local_trajectory.buffer(
+            self.LANE_CHECK_BUFFER, 
+            self.BUFFER_QUADRANT_SEGMENTS
+        )

Let me verify the test coverage:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing test cases
echo "=== Searching for trajectory tests ==="
rg "def test.*trajectory" --type py

# Check content of test files
echo -e "\n=== Checking test files ==="
fd "test_.*\.py$" -x cat {}

Length of output: 18773

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: 3

🧹 Nitpick comments (3)
code/mapping/src/path_test.py (3)

14-20: Update class documentation to match implementation.

The current documentation focuses on frame translation, but the class primarily handles trajectory generation and checking. Consider updating the documentation to accurately reflect the class's main responsibilities.


31-36: Consider making the map topic more configurable.

The hardcoded default topic path /paf/hero/mapping/init_data might limit reusability. Consider moving this to a configuration file or making it more generic.


74-78: Maintain consistency in node naming.

The node name in __main__ ("path_test_node") differs from the class name ("TestPath"). Consider maintaining consistency to avoid confusion.

 if __name__ == "__main__":
-    name = "path_test_node"
+    name = "test_path_node"  # Align with class name TestPath
     roscomp.init(name)
     node = TestPath()
     node.spin()
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4182c57 and 2b028c9.

📒 Files selected for processing (2)
  • code/mapping/launch/mapping.launch (1 hunks)
  • code/mapping/src/path_test.py (1 hunks)
🔇 Additional comments (1)
code/mapping/src/path_test.py (1)

67-67: Add input validation for map data.

Consider adding validation for incoming map data to ensure robustness:

  1. Check for null/empty map data
  2. Validate map boundaries and coordinates
  3. Verify essential map features are present

Comment on lines 39 to 41
<node pkg="mapping" type="path_test.py" name="Local_Trajectory_Node" output="screen" />
<param name="map_publish_rate" value="$(arg map_publish_rate)" />

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix parameter indentation within node tags.

The map_publish_rate parameter should be nested within the node tags to properly associate it with the Local_Trajectory_Node.

Apply this diff to fix the indentation:

  <node pkg="mapping" type="path_test.py" name="Local_Trajectory_Node" output="screen" />
-    <param name="map_publish_rate" value="$(arg map_publish_rate)" />
+  <node pkg="mapping" type="path_test.py" name="Local_Trajectory_Node" output="screen">
+    <param name="map_publish_rate" value="$(arg map_publish_rate)" />
+  </node>

Committable suggestion skipped: line range outside the PR's diff.

@una-auxme una-auxme deleted a comment from coderabbitai bot Jan 29, 2025
@una-auxme una-auxme deleted a comment from coderabbitai bot Jan 29, 2025
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

🔭 Outside diff range comments (1)
code/mapping/ext_modules/mapping_common/map.py (1)

New methods require test coverage

The codebase lacks dedicated test coverage for the recently added methods. While there are existing test files and patterns, they don't cover:

  • entities_in_area() method (edge cases, radius checks)
  • lane_marking_left() and lane_marking_right() methods
  • check_trajectory() method's various return cases

Recommend adding tests in code/mapping/tests/mapping_common/test_general.py following existing patterns.

🔗 Analysis chain

Line range hint 289-457: Add unit tests for new functionality.

The new methods need comprehensive unit tests to verify:

  1. Edge cases in entities_in_area (empty list, negative radius)
  2. Lane marking detection for various scenarios
  3. All possible return cases of check_trajectory

Let me help verify the test coverage:

Would you like me to help generate comprehensive unit tests for these new methods?

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing test files
echo "Searching for test files..."
fd "test.*\.py$" code/mapping/tests/

# Look for test cases related to the new functionality
echo -e "\nSearching for related test cases..."
rg -l "test.*(?:entities_in_area|lane_marking|check_trajectory)" --type py

Length of output: 516


Script:

#!/bin/bash
# Check content of test files for any related tests or patterns
echo "=== Checking test_entity.py ==="
cat code/mapping/tests/mapping_common/test_entity.py

echo -e "\n=== Checking test_general.py ==="
cat code/mapping/tests/mapping_common/test_general.py

# Search for Map class tests specifically
echo -e "\n=== Searching for Map class tests ==="
rg "class.*Test.*Map" --type py

# Look for any usage of the new methods in tests
echo -e "\n=== Searching for usage of new methods ==="
rg "entities_in_area|lane_marking_left|lane_marking_right|check_trajectory" --type py

Length of output: 4491

🧹 Nitpick comments (2)
code/mapping/ext_modules/mapping_common/map.py (2)

6-6: Remove unused import.

The shapely.plotting import is not used in the code.

-import shapely.plotting

401-457: Improve robustness and maintainability of trajectory checking.

The method has several areas for improvement:

  1. Extract magic number 3 in buffer calls
  2. Add validation for buffer parameters
  3. Consider breaking down into helper methods

Apply this diff to improve the implementation:

+    # Additional class constant
+    BUFFER_QUADRANT_SEGMENTS = 3  # Number of segments per quadrant for buffer operations
+
+    def _check_obstacle_collision(self, trajectory_buffer: Polygon) -> bool:
+        """Check if trajectory collides with any obstacles.
+
+        Args:
+            trajectory_buffer (Polygon): The buffered trajectory
+
+        Returns:
+            bool: True if collision detected, False otherwise
+        """
+        obstacles = self.entities_in_area(self.OBSTACLE_DETECTION_RADIUS)
+        return any(
+            not entry.flags._is_lanemark and shapely.intersection(
+                entry.to_shapely().poly, trajectory_buffer
+            )
+            for entry in obstacles
+        )
+
+    def _check_lane_violation(self, trajectory_buffer: Polygon) -> Optional[int]:
+        """Check if trajectory violates lane markings.
+
+        Args:
+            trajectory_buffer (Polygon): The buffered trajectory
+
+        Returns:
+            Optional[int]: 2 for left lane violation, 3 for right lane violation,
+                          None if no violation
+        """
+        left_lane = self.lane_marking_left()
+        if left_lane is not None and shapely.intersection(
+            left_lane.to_shapely().poly, trajectory_buffer
+        ):
+            return 2
+
+        right_lane = self.lane_marking_right()
+        if right_lane is not None and shapely.intersection(
+            right_lane.to_shapely().poly, trajectory_buffer
+        ):
+            return 3
+        return None
+
     def check_trajectory(self, local_path: Path) -> int:
         if local_path.poses is None or not local_path.poses:
             return -1

         trajectory_shapely_points = []
         for pos in local_path.poses:
             trajectory_shapely_points.append(
                 shapely.Point(pos.pose.position.x, pos.pose.position.y)
             )
         local_trajectory = shapely.LineString(trajectory_shapely_points)
-        local_trajectory_buffer = local_trajectory.buffer(self.VEHICLE_WIDTH_BUFFER, 3)
+        local_trajectory_buffer = local_trajectory.buffer(
+            self.VEHICLE_WIDTH_BUFFER, 
+            self.BUFFER_QUADRANT_SEGMENTS
+        )

-        # Check path collision with other entities
-        obstacles = self.entities_in_area(self.OBSTACLE_DETECTION_RADIUS)
-        for entry in obstacles:
-            if not entry.flags._is_lanemark and shapely.intersection(
-                entry.to_shapely().poly, local_trajectory_buffer
-            ):
-                return 1
+        # Check for obstacle collisions
+        if self._check_obstacle_collision(local_trajectory_buffer):
+            return 1

         # decrease buffer size for lane checking
-        local_trajectory_buffer = local_trajectory.buffer(self.LANE_CHECK_BUFFER, 3)
+        local_trajectory_buffer = local_trajectory.buffer(
+            self.LANE_CHECK_BUFFER,
+            self.BUFFER_QUADRANT_SEGMENTS
+        )

-        # Check left lane
-        left_lane = self.lane_marking_left()
-        if left_lane is not None and shapely.intersection(
-            left_lane.to_shapely().poly, local_trajectory_buffer
-        ):
-            return 2
-
-        # Check right lane
-        right_lane = self.lane_marking_right()
-        if right_lane is not None and shapely.intersection(
-            right_lane.to_shapely().poly, local_trajectory_buffer
-        ):
-            return 3
+        # Check for lane violations
+        lane_violation = self._check_lane_violation(local_trajectory_buffer)
+        if lane_violation is not None:
+            return lane_violation

         return 0
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2b028c9 and 9c6207c.

📒 Files selected for processing (4)
  • code/mapping/ext_modules/mapping_common/map.py (4 hunks)
  • code/mapping/launch/mapping.launch (1 hunks)
  • code/mapping/src/mapping_data_integration.py (2 hunks)
  • code/mapping/src/path_test.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • code/mapping/launch/mapping.launch
  • code/mapping/src/mapping_data_integration.py
  • code/mapping/src/path_test.py
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Build and push image
🔇 Additional comments (3)
code/mapping/ext_modules/mapping_common/map.py (3)

396-399: LGTM! Well-defined constants with clear documentation.

The constants are appropriately named and have reasonable values for their intended use.


289-321: 🛠️ Refactor suggestion

Optimize performance and improve code quality.

The method has several areas for improvement:

  1. Code duplication in distance calculation
  2. Inefficient iteration and calculation
  3. Missing type hints
  4. Docstring formatting issues

Apply this diff to improve the implementation:

-    def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
+    def entities_in_area(self, radius: float, only_in_front: bool = True) -> List[Entity]:
+        """Returns entities within a given radius around the hero car.
+
+        Args:
+            radius (float): The radius to search for entities
+            only_in_front (bool): If True, only return entities in front of the car
+
+        Returns:
+            List[Entity]: Filtered list of entities within the radius
+        """
         entities = self.entities_without_hero()
-        entities_in_area = []
-
-        if only_in_front:
-            for entry in entities:
-                distance = np.sqrt(
-                    np.power(entry.transform.translation().x(), 2)
-                    + np.power(entry.transform.translation().y(), 2)
-                )
-                if entry.transform.translation().x() > 0 and distance < radius:
-                    entities_in_area.append(entry)
-            return entities_in_area
-        else:
-            for entry in entities:
-                distance = np.sqrt(
-                    np.power(entry.transform.translation().x(), 2)
-                    + np.power(entry.transform.translation().y(), 2)
-                )
-                if distance < radius:
-                    entities_in_area.append(entry)
-
-        return entities_in_area
+        # Vectorized calculation using numpy
+        positions = np.array([[e.transform.translation().x(), e.transform.translation().y()] 
+                            for e in entities])
+        distances = np.sqrt(np.sum(positions**2, axis=1))
+        
+        if only_in_front:
+            mask = (positions[:, 0] > 0) & (distances < radius)
+        else:
+            mask = distances < radius
+            
+        return [e for i, e in enumerate(entities) if mask[i]]

Likely invalid or redundant comment.


376-394: 🛠️ Refactor suggestion

Consolidate duplicate logic and improve documentation.

The lane marking methods have duplicate logic and incomplete documentation.

Apply this diff to improve the implementation:

+    def _get_lane_marking(self, position_index: int) -> Optional[Entity]:
+        """Helper method to get lane marking by position index.
+
+        Args:
+            position_index (int): 1 for left, -1 for right
+
+        Returns:
+            Optional[Entity]: The lane marking entity or None if not found
+        """
+        for entry in self.entities:
+            if entry.flags._is_lanemark and entry.position_index == position_index:
+                return entry
+        return None
+
     def lane_marking_left(self) -> Optional[Entity]:
         """Returns the left lane marking entity if it exists.
+
+        Returns:
+            Optional[Entity]: The left lane marking entity or None if not found
+        """
-        for entry in self.entities:
-            if entry.flags._is_lanemark is True and entry.position_index == 1:
-                return entry
-        return None
+        return self._get_lane_marking(position_index=1)

     def lane_marking_right(self) -> Optional[Entity]:
         """Returns the right lane marking entity if it exists.
+
+        Returns:
+            Optional[Entity]: The right lane marking entity or None if not found
+        """
-        for entry in self.entities:
-            if entry.flags._is_lanemark is True and entry.position_index == -1:
-                return entry
-        return None
+        return self._get_lane_marking(position_index=-1)

Likely invalid or redundant comment.

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/mapping/ext_modules/mapping_common/map.py (1)

6-6: Remove unused import.

The shapely.plotting import is not used in the code.

-import shapely.plotting

Also applies to: 18-18

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ac8038b and ccac3ef.

📒 Files selected for processing (3)
  • code/mapping/ext_modules/mapping_common/map.py (4 hunks)
  • code/mapping/launch/mapping.launch (1 hunks)
  • code/mapping/src/mapping_data_integration.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • code/mapping/launch/mapping.launch
  • code/mapping/src/mapping_data_integration.py
🧰 Additional context used
🪛 Ruff (0.8.2)
code/mapping/ext_modules/mapping_common/map.py

123-123: Undefined name points

(F821)


125-125: Undefined name width

(F821)


129-129: Undefined name points

(F821)


132-132: Undefined name width

(F821)


150-150: Undefined name direction

(F821)


152-152: Undefined name direction

(F821)


154-154: Undefined name direction

(F821)


155-155: Local variable y_axis_line is assigned to but never used

Remove assignment to unused variable y_axis_line

(F841)


157-157: Undefined name entities

(F821)


162-162: Undefined name radius

(F821)


163-163: Undefined name entities_in_area

(F821)


165-165: Undefined name entities_in_area

(F821)


167-167: Redefinition of unused entities_in_area from line 113

(F811)

🔇 Additional comments (2)
code/mapping/ext_modules/mapping_common/map.py (2)

254-272: Refactor lane marking methods to reduce duplication.

The lane marking methods share similar logic and can be consolidated into a helper method.

     def lane_marking_left(self) -> Optional[Entity]:
         """Returns the left lane marking entity if it exists.
 
         Returns:
             Optional[Entity]: The left lane marking entity or None if not found
         """
-        for entry in self.entities:
-            if entry.flags._is_lanemark is True and entry.position_index == 1:
-                return entry
-        return None
+        return self._get_lane_marking(position_index=1)
 
     def lane_marking_right(self) -> Optional[Entity]:
         """Returns the right lane marking entity if it exists.
 
         Returns:
             Optional[Entity]: The right lane marking entity or None if not found
         """
-        for entry in self.entities:
-            if entry.flags._is_lanemark is True and entry.position_index == -1:
-                return entry
-        return None
+        return self._get_lane_marking(position_index=-1)
+
+    def _get_lane_marking(self, position_index: int) -> Optional[Entity]:
+        """Helper method to get lane marking by position index.
+
+        Args:
+            position_index (int): 1 for left, -1 for right
+
+        Returns:
+            Optional[Entity]: The lane marking entity or None if not found
+        """
+        for entry in self.entities:
+            if entry.flags._is_lanemark and entry.position_index == position_index:
+                return entry
+        return None

274-335: Extract magic number into a constant.

The magic number '3' in buffer calls should be extracted into a constant for better maintainability.

     # Class-level constants
     VEHICLE_WIDTH_BUFFER = 1.0  # meters
     LANE_CHECK_BUFFER = 0.5  # meters
     OBSTACLE_DETECTION_RADIUS = 10.0  # meters
+    BUFFER_QUADRANT_SEGMENTS = 3  # Number of segments per quadrant for buffer operations

     def check_trajectory(self, local_path: Path) -> int:
         # ... existing docstring ...
         local_trajectory = shapely.LineString(trajectory_shapely_points)
-        local_trajectory_buffer = local_trajectory.buffer(self.VEHICLE_WIDTH_BUFFER, 3)
+        local_trajectory_buffer = local_trajectory.buffer(
+            self.VEHICLE_WIDTH_BUFFER, 
+            self.BUFFER_QUADRANT_SEGMENTS
+        )
         
         # ... rest of the method ...
-        local_trajectory_buffer = local_trajectory.buffer(self.LANE_CHECK_BUFFER, 3)
+        local_trajectory_buffer = local_trajectory.buffer(
+            self.LANE_CHECK_BUFFER, 
+            self.BUFFER_QUADRANT_SEGMENTS
+        )

Comment on lines +113 to +135
def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
"""Returns the entities without the hero car
in a given radius around the hero car.
Flag only_in_front decides if entities
behind the car, are returend too.
Only checks if the first entity is_hero

Returns:
- Polygon: A Shapely Polygon representing the widened curve."""

if len(points) < 2:
raise ValueError("At least two points are required to define a curve.")
if width <= 0:
raise ValueError("Width must be a positive value.")

# Create a LineString from the given points
curve = LineString(points)

# Create a buffer around the curve to form a polygon with the given width
polygon = curve.buffer(width / 2, cap_style="round", join_style="round")

return polygon

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove incorrect method definition.

This appears to be an incorrect code fragment with undefined variables (points, width) and an incorrect docstring. The correct implementation is defined below at line 167.

-    def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
-        """Returns the entities without the hero car
-            in a given radius around the hero car.
-            Flag only_in_front decides if entities
-            behind the car, are returend too.
-        Only checks if the first entity is_hero
-
-        Returns:
-        - Polygon: A Shapely Polygon representing the widened curve."""
-
-        if len(points) < 2:
-            raise ValueError("At least two points are required to define a curve.")
-        if width <= 0:
-            raise ValueError("Width must be a positive value.")
-
-        # Create a LineString from the given points
-        curve = LineString(points)
-
-        # Create a buffer around the curve to form a polygon with the given width
-        polygon = curve.buffer(width / 2, cap_style="round", join_style="round")
-
-        return polygon
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
"""Returns the entities without the hero car
in a given radius around the hero car.
Flag only_in_front decides if entities
behind the car, are returend too.
Only checks if the first entity is_hero
Returns:
- Polygon: A Shapely Polygon representing the widened curve."""
if len(points) < 2:
raise ValueError("At least two points are required to define a curve.")
if width <= 0:
raise ValueError("Width must be a positive value.")
# Create a LineString from the given points
curve = LineString(points)
# Create a buffer around the curve to form a polygon with the given width
polygon = curve.buffer(width / 2, cap_style="round", join_style="round")
return polygon
# (The incorrect definition of `entities_in_area` has been removed.)
🧰 Tools
🪛 Ruff (0.8.2)

123-123: Undefined name points

(F821)


125-125: Undefined name width

(F821)


129-129: Undefined name points

(F821)


132-132: Undefined name width

(F821)

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.

Since the restructuring in the map module, most functions should be moved into the MapTree.

Some of the functions also seem a bit outdated and should be reworked (or alternatively removed if unused).

Comment on lines +274 to +277
# Class-level constants
VEHICLE_WIDTH_BUFFER = 1.0 # meters
LANE_CHECK_BUFFER = 0.5 # meters
OBSTACLE_DETECTION_RADIUS = 10.0 # meters
Copy link
Collaborator

Choose a reason for hiding this comment

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

These should be input parameters to the function

Comment on lines +167 to +199
def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
"""Returns the entities without the hero car
in a given radius around the hero car.
Flag only_in_front decides if entities
behind the car, are returend too.
Only checks if the first entity is_hero

Returns:
List[Entity]: Entities in front and/or back of hero
without the hero car in given radius
"""
entities = self.entities_without_hero()
entities_in_area = []

if only_in_front:
for entry in entities:
distance = np.sqrt(
np.power(entry.transform.translation().x(), 2)
+ np.power(entry.transform.translation().y(), 2)
)
if entry.transform.translation().x() > 0 and distance < radius:
entities_in_area.append(entry)
return entities_in_area
else:
for entry in entities:
distance = np.sqrt(
np.power(entry.transform.translation().x(), 2)
+ np.power(entry.transform.translation().y(), 2)
)
if distance < radius:
entities_in_area.append(entry)

return entities_in_area
Copy link
Collaborator

Choose a reason for hiding this comment

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

Several remarks:

  • Should be moved into the MapTree class
  • This function seems to not take into account the shapes of the entities. It is also a duplicate of the same name function above. It should use the Circle polygon generated there and then filter these entities with MapTree.get_overlapping_entities()

Comment on lines +113 to +134
def entities_in_area(self, radius, only_in_front=True) -> List[Entity]:
"""Returns the entities without the hero car
in a given radius around the hero car.
Flag only_in_front decides if entities
behind the car, are returend too.
Only checks if the first entity is_hero

Returns:
- Polygon: A Shapely Polygon representing the widened curve."""

if len(points) < 2:
raise ValueError("At least two points are required to define a curve.")
if width <= 0:
raise ValueError("Width must be a positive value.")

# Create a LineString from the given points
curve = LineString(points)

# Create a buffer around the curve to form a polygon with the given width
polygon = curve.buffer(width / 2, cap_style="round", join_style="round")

return polygon
Copy link
Collaborator

Choose a reason for hiding this comment

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

Duplicate (namewise) of a function below. I commented there for further steps

Comment on lines +136 to +165
def get_obstacle_on_trajectory(
self, trajectory, hero_pos, hero_heading, width=1.0
) -> Optional[Entity]:
"""Calculates the closest entity on the given trajectory. Transforms
trajectory world coordinates into map coordinates based on hero position.

Args:
direction (str): lanemarks on "left", "right" or "both" will be checked.
Other inputs will be ignored

Returns:
dict{uuid, coordinate}: dictionary with uuid of lanemark as keys
and coordinates as according entries
"""
if direction == "left":
y_axis_line = LineString([[0, 0], [0, 8]])
elif direction == "right":
y_axis_line = LineString([[0, 0], [0, -8]])
elif direction == "both":
y_axis_line = LineString([[0, -8], [0, 8]])
else:
for entry in entities:
distance = np.sqrt(
np.power(entry.transform.translation().x(), 2)
+ np.power(entry.transform.translation().y(), 2)
)
if distance < radius:
entities_in_area.append(entry)

return entities_in_area
Copy link
Collaborator

Choose a reason for hiding this comment

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

This function does not seem to use the trajectory and hero data put into it?

Checking for obstacles is already done in the ACC and overtake Behaviors and they use different functions.

Comment on lines +254 to +272
def lane_marking_left(self) -> Optional[Entity]:
"""
Returns:
+ Optional[Entity]: The left lane marking entity or None if not found
"""
for entry in self.entities:
if entry.flags._is_lanemark is True and entry.position_index == 1:
return entry
return None

def lane_marking_right(self) -> Optional[Entity]:
"""
Returns:
+ Optional[Entity]: The right lane marking entity or None if not found
"""
for entry in self.entities:
if entry.flags._is_lanemark is True and entry.position_index == -1:
return entry
return None
Copy link
Collaborator

Choose a reason for hiding this comment

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

Combining those functions into one with a flag for left/right would be nicer

Copy link
Collaborator

Choose a reason for hiding this comment

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

Since this is not a "main" node of the car, I would recommend putting this in a test subfolder inside the src directory

Copy link
Collaborator

Choose a reason for hiding this comment

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

The changes in this file seem unnecessary. (lidar_data is already set to None as part of the class definition)

@Johannes1098
Copy link
Collaborator

Too much work to do for a feature that doesn't get used in our project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature]: execution check function for planned local trajectory
5 participants