-
Notifications
You must be signed in to change notification settings - Fork 0
/
CustomPurePursuit.m
144 lines (129 loc) · 6.7 KB
/
CustomPurePursuit.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
classdef CustomPurePursuit < handle
properties
LookaheadDistanceBase = 1.0; % Base lookahead distance, remains constant
LookaheadSpeedGain = 0.1; % Gain for speed-dependent lookahead adjustment
DesiredLinearVelocity = 1.0; % Desired linear velocity
MaxSteeringAngle = 1.0;
Waypoints = []; % Original waypoints provided by the user
InterpolatedWaypoints = []; % Waypoints after spline interpolation for a smoother path
Speed = 0; % Current speed of the vehicle
DynamicLookaheadDistance; % Calculated dynamic lookahead distance based on speed
WaypointIndex = 1;
end
methods
function obj = CustomPurePursuit(varargin)
% Constructor that accepts name-value pairs for setting properties
for i = 1:2:length(varargin)
if i + 1 <= length(varargin)
propName = varargin{i};
propValue = varargin{i + 1};
switch lower(propName)
case 'lookaheaddistance'
obj.LookaheadDistanceBase = propValue;
case 'desiredlinearvelocity'
obj.DesiredLinearVelocity = propValue;
% case 'waypoints'
% obj.Waypoints = propValue;
% disp("waypoints")
% obj = obj.interpolateWaypoints(); % Interpolate waypoints after setting
case 'maxsteeringangle'
obj.MaxSteeringAngle = propValue;
otherwise
warning('Unknown property name: %s', propName);
end
end
end
% Initialize DynamicLookaheadDistance with the base value
obj.DynamicLookaheadDistance = obj.LookaheadDistanceBase;
end
function obj = interpolateWaypoints(obj)
% Spline interpolation for smoother path generation
if ~isempty(obj.Waypoints) && size(obj.Waypoints, 1) > 1
x = obj.Waypoints(:, 1);
y = obj.Waypoints(:, 2);
t = 1:length(x);
tt = linspace(1, length(x), 10 * length(x)); % Increase points
xi = spline(t, x, tt);
yi = spline(t, y, tt);
obj.InterpolatedWaypoints = [xi', yi'];
else
obj.InterpolatedWaypoints = obj.Waypoints; % Not enough points for spline
end
obj.WaypointIndex = 1;
end
function [v, omega, obj] = control(obj, currentPose, currentSpeed)
% Calculate control commands based on the current pose and speed
obj.Speed = currentSpeed;
obj = obj.adjustLookaheadDistance(); % Adjusts and updates DynamicLookaheadDistance based on current speed
[lookAheadPoint, found, obj] = obj.findLookAheadPoint(currentPose);
% plot(lookAheadPoint(1), lookAheadPoint(2), '+', 'Color', 'g', 'MarkerSize', 20);
% plot(currentPose(1) + obj.DynamicLookaheadDistance * cos(linspace(0, 2*pi)), currentPose(2) + obj.DynamicLookaheadDistance * sin(linspace(0, 2*pi)));
% plot(obj.InterpolatedWaypoints(:,1), obj.InterpolatedWaypoints(:,2))
% disp("pt")
% disp(lookAheadPoint)
if ~found
% disp("help!")
v = 0;
omega = 0;
return;
end
angleToGoal = atan2(lookAheadPoint(2) - currentPose(2), lookAheadPoint(1) - currentPose(1));
steeringAngle = angleToGoal - currentPose(3);
steeringAngle = atan2(sin(steeringAngle), cos(steeringAngle));
steeringAngle = clip(steeringAngle, -obj.MaxSteeringAngle, obj.MaxSteeringAngle);
% disp(angleToGoal)
% disp(steeringAngle)
% omega = (2 * obj.DesiredLinearVelocity / obj.DynamicLookaheadDistance) * sin(steeringAngle);
% omega = max(min(omega, obj.MaxAngularVelocity), -obj.MaxAngularVelocity);
v = obj.DesiredLinearVelocity * (1 - 0.9 * abs(steeringAngle) / obj.MaxSteeringAngle);
omega = steeringAngle;
% [v omega obj.DesiredLinearVelocity obj.MaxSteeringAngle]
end
function obj = adjustLookaheadDistance(obj)
% Dynamically adjusts the lookahead distance based on the current speed
obj.DynamicLookaheadDistance = obj.LookaheadDistanceBase + obj.LookaheadSpeedGain * sqrt(obj.Speed);
end
function [lookAheadPoint, found, obj] = findLookAheadPoint(obj, currentPose)
% Find the lookahead point on the interpolated path using DynamicLookaheadDistance
% disp("func?")
found = false;
lookAheadPoint = [0, 0];
minDist = inf;
% disp(size(obj.InterpolatedWaypoints, 1))
% disp("pose")
% disp(currentPose(1:2))
for i = obj.WaypointIndex:min(size(obj.InterpolatedWaypoints, 1), obj.WaypointIndex+20)
point = obj.InterpolatedWaypoints(i, :);
% if size(obj.InterpolatedWaypoints, 1) ~= i
% disp(obj.InterpolatedWaypoints)
% disp(i)
% segmentEnd = obj.InterpolatedWaypoints(i + 1, :);
%
% [point, isOnSegment] = obj.projectLookAheadPoint(currentPose, segmentStart, segmentEnd);
% plot(point(1), point(2), 'x', 'Color', 'r', 'MarkerSize', 10);
% end
% if isOnSegment
% plot(point(1), point(2), 'x', 'Color', 'r', 'MarkerSize', 10);
dist = abs(norm(currentPose(1:2) - point) - obj.DynamicLookaheadDistance);
if dist < minDist % && dist <= obj.DynamicLookaheadDistance
minDist = dist;
lookAheadPoint = point;
found = true;
obj.WaypointIndex = i;
end
% end
end
% disp("dist")
% disp(minDist)
end
function [point, isOnSegment] = projectLookAheadPoint(~, currentPose, segmentStart, segmentEnd)
% Vector projection to find the closest point on the segment to the current position
a = currentPose(1:2) - segmentStart;
b = segmentEnd - segmentStart;
t = dot(a, b) / norm(b);
isOnSegment = t >= 0 && t <= norm(b);
% t = max(0, min(1, t)); % Clamp t to the segment
point = segmentStart + t * b;
end
end
end