simple-autonomous-car 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. simple_autonomous_car/__init__.py +96 -0
  2. simple_autonomous_car/alerts/__init__.py +5 -0
  3. simple_autonomous_car/alerts/track_bounds_alert.py +276 -0
  4. simple_autonomous_car/car/__init__.py +5 -0
  5. simple_autonomous_car/car/car.py +234 -0
  6. simple_autonomous_car/constants.py +112 -0
  7. simple_autonomous_car/control/__init__.py +7 -0
  8. simple_autonomous_car/control/base_controller.py +152 -0
  9. simple_autonomous_car/control/controller_viz.py +282 -0
  10. simple_autonomous_car/control/pid_controller.py +153 -0
  11. simple_autonomous_car/control/pure_pursuit_controller.py +578 -0
  12. simple_autonomous_car/costmap/__init__.py +12 -0
  13. simple_autonomous_car/costmap/base_costmap.py +187 -0
  14. simple_autonomous_car/costmap/grid_costmap.py +507 -0
  15. simple_autonomous_car/costmap/inflation.py +126 -0
  16. simple_autonomous_car/detection/__init__.py +5 -0
  17. simple_autonomous_car/detection/error_detector.py +165 -0
  18. simple_autonomous_car/filters/__init__.py +7 -0
  19. simple_autonomous_car/filters/base_filter.py +119 -0
  20. simple_autonomous_car/filters/kalman_filter.py +131 -0
  21. simple_autonomous_car/filters/particle_filter.py +162 -0
  22. simple_autonomous_car/footprint/__init__.py +7 -0
  23. simple_autonomous_car/footprint/base_footprint.py +128 -0
  24. simple_autonomous_car/footprint/circular_footprint.py +73 -0
  25. simple_autonomous_car/footprint/rectangular_footprint.py +123 -0
  26. simple_autonomous_car/frames/__init__.py +21 -0
  27. simple_autonomous_car/frames/frenet.py +267 -0
  28. simple_autonomous_car/maps/__init__.py +9 -0
  29. simple_autonomous_car/maps/frenet_map.py +97 -0
  30. simple_autonomous_car/maps/grid_ground_truth_map.py +83 -0
  31. simple_autonomous_car/maps/grid_map.py +361 -0
  32. simple_autonomous_car/maps/ground_truth_map.py +64 -0
  33. simple_autonomous_car/maps/perceived_map.py +169 -0
  34. simple_autonomous_car/perception/__init__.py +5 -0
  35. simple_autonomous_car/perception/perception.py +107 -0
  36. simple_autonomous_car/planning/__init__.py +7 -0
  37. simple_autonomous_car/planning/base_planner.py +184 -0
  38. simple_autonomous_car/planning/goal_planner.py +261 -0
  39. simple_autonomous_car/planning/track_planner.py +199 -0
  40. simple_autonomous_car/sensors/__init__.py +6 -0
  41. simple_autonomous_car/sensors/base_sensor.py +105 -0
  42. simple_autonomous_car/sensors/lidar_sensor.py +145 -0
  43. simple_autonomous_car/track/__init__.py +5 -0
  44. simple_autonomous_car/track/track.py +463 -0
  45. simple_autonomous_car/visualization/__init__.py +25 -0
  46. simple_autonomous_car/visualization/alert_viz.py +316 -0
  47. simple_autonomous_car/visualization/utils.py +169 -0
  48. simple_autonomous_car-0.1.2.dist-info/METADATA +324 -0
  49. simple_autonomous_car-0.1.2.dist-info/RECORD +50 -0
  50. simple_autonomous_car-0.1.2.dist-info/WHEEL +4 -0
@@ -0,0 +1,153 @@
1
+ """PID controller for path following."""
2
+
3
+ from typing import TYPE_CHECKING, Optional
4
+
5
+ import numpy as np
6
+
7
+ from simple_autonomous_car.car.car import CarState
8
+ from simple_autonomous_car.constants import DEFAULT_GOAL_TOLERANCE
9
+ from simple_autonomous_car.control.base_controller import BaseController
10
+ from simple_autonomous_car.perception.perception import PerceptionPoints
11
+
12
+ if TYPE_CHECKING:
13
+ from simple_autonomous_car.costmap.base_costmap import BaseCostmap
14
+
15
+
16
+ class PIDController(BaseController):
17
+ """
18
+ PID (Proportional-Integral-Derivative) controller.
19
+
20
+ A simple PID controller for following a path or maintaining a target state.
21
+
22
+ Parameters
23
+ ----------
24
+ kp : float, default=1.0
25
+ Proportional gain.
26
+ ki : float, default=0.0
27
+ Integral gain.
28
+ kd : float, default=0.1
29
+ Derivative gain.
30
+ target_velocity : float, default=10.0
31
+ Target velocity in m/s.
32
+ name : str, default="pid"
33
+ Controller name.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ kp: float = 1.0,
39
+ ki: float = 0.0,
40
+ kd: float = 0.1,
41
+ target_velocity: float = 10.0,
42
+ name: str = "pid",
43
+ ):
44
+ super().__init__(name=name)
45
+ self.kp = kp
46
+ self.ki = ki
47
+ self.kd = kd
48
+ self.target_velocity = target_velocity
49
+ self.integral_error = 0.0
50
+ self.last_error = 0.0
51
+
52
+ def compute_control(
53
+ self,
54
+ car_state: CarState,
55
+ perception_data: dict[str, PerceptionPoints] | None = None,
56
+ costmap: Optional["BaseCostmap"] = None,
57
+ plan: np.ndarray | None = None,
58
+ goal: np.ndarray | None = None,
59
+ goal_tolerance: float | None = None,
60
+ dt: float = 0.1,
61
+ ) -> dict[str, float]:
62
+ """
63
+ Compute control using PID algorithm.
64
+
65
+ Parameters
66
+ ----------
67
+ car_state : CarState
68
+ Current car state.
69
+ perception_data : dict, optional
70
+ Not used by basic PID.
71
+ plan : np.ndarray, optional
72
+ Planned path. If provided, computes error to nearest point.
73
+ dt : float, default=0.1
74
+ Time step in seconds.
75
+
76
+ Returns
77
+ -------
78
+ Dict[str, float]
79
+ Control commands with "acceleration" and "steering_rate".
80
+ """
81
+ if not self.enabled:
82
+ return {"acceleration": 0.0, "steering_rate": 0.0}
83
+
84
+ # Simple heading control if plan provided
85
+ steering_rate = 0.0
86
+ if plan is not None and len(plan) > 0:
87
+ car_pos = car_state.position()
88
+ distances = np.linalg.norm(plan - car_pos, axis=1)
89
+ closest_idx = np.argmin(distances)
90
+ target_point = plan[closest_idx]
91
+
92
+ target_vector = target_point - car_pos
93
+ target_angle = np.arctan2(target_vector[1], target_vector[0])
94
+ angle_error = target_angle - car_state.heading
95
+ angle_error = np.arctan2(np.sin(angle_error), np.cos(angle_error))
96
+
97
+ # PID control
98
+ self.integral_error += angle_error * dt
99
+ derivative_error = (angle_error - self.last_error) / dt
100
+ self.last_error = angle_error
101
+
102
+ steering_rate = (
103
+ self.kp * angle_error + self.ki * self.integral_error + self.kd * derivative_error
104
+ )
105
+ steering_rate = np.clip(steering_rate, -1.0, 1.0)
106
+
107
+ # Velocity control (adapt based on goal distance)
108
+ target_velocity = self.target_velocity
109
+
110
+ # Reduce velocity if approaching goal (smooth deceleration)
111
+ if goal is not None:
112
+ distance_to_goal = np.linalg.norm(car_state.position() - goal)
113
+ tolerance = goal_tolerance if goal_tolerance is not None else DEFAULT_GOAL_TOLERANCE
114
+
115
+ # If within tolerance, stop completely
116
+ if distance_to_goal <= tolerance:
117
+ target_velocity = 0.0
118
+ else:
119
+ # Start slowing down when within 10 meters of goal
120
+ # Use a smaller slow_down_distance to ensure smooth deceleration even when close
121
+ slow_down_distance = max(10.0, tolerance * 5.0) # At least 5x tolerance, or 10m
122
+ if distance_to_goal < slow_down_distance:
123
+ # Smooth velocity reduction: linear from full speed to zero
124
+ # At distance=tolerance, velocity=0; at distance=slow_down_distance, velocity=target_velocity
125
+ # Map distance from [tolerance, slow_down_distance] to velocity [0, target_velocity]
126
+ if slow_down_distance > tolerance:
127
+ velocity_factor = (distance_to_goal - tolerance) / (
128
+ slow_down_distance - tolerance
129
+ )
130
+ velocity_factor = max( # type: ignore[arg-type]
131
+ 0.0, min(1.0, velocity_factor) # type: ignore[arg-type]
132
+ ) # Clamp to [0, 1] # type: ignore[assignment]
133
+ target_velocity = self.target_velocity * velocity_factor # type: ignore[assignment]
134
+ else:
135
+ target_velocity = 0.0
136
+ # Ensure we always slow down when very close (even if above tolerance)
137
+ if distance_to_goal < tolerance * 2.0:
138
+ # Extra safety: cap velocity when very close
139
+ max_velocity_near_goal = float(
140
+ self.target_velocity * (distance_to_goal / (tolerance * 2.0))
141
+ )
142
+ target_velocity = float(min(target_velocity, max_velocity_near_goal))
143
+
144
+ velocity_error = target_velocity - car_state.velocity
145
+ acceleration = 0.5 * velocity_error
146
+ acceleration = np.clip(acceleration, -2.0, 2.0)
147
+
148
+ return {"acceleration": acceleration, "steering_rate": steering_rate}
149
+
150
+ def reset(self) -> None:
151
+ """Reset PID controller state."""
152
+ self.integral_error = 0.0
153
+ self.last_error = 0.0