-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathUavBrain.m
638 lines (599 loc) · 28.2 KB
/
UavBrain.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
classdef UavBrain < handle
% UAV behavioural constants
properties (Constant)
AvoidanceRadius = 135;
DangerRadius = 65;
RandomWalkMaxDistance = 600.0;
FirstTarget = [0 100];
PheremoneDetectionRange = 800.0;
PheremoneAvoidanceRange = 300.0;
MaxPheremoneCount = 100;
CalibrationTime = 30.0;
end
% UAV physical state
% Objective physical properties of the UAV and the world
properties (SetAccess = public, GetAccess = public)
uavBody;
nextTurnRate;
nextVelocity;
mapRect;
end
% UAV estimated state
% Updated automatically during each timestep
properties (SetAccess = public, GetAccess = public)
% UAV physical state
posEstimate;
lastPosEstimate;
bearingEstimate;
% Search data
conc;
lastConc;
% Avoidance data
closestUav;
closestSqrDist;
closeUavCount;
end
% UAV AI state
% Used to control the actions taken by the UAVs AI
properties (SetAccess = public, GetAccess = public)
id
currentState;
lastState;
% Tracking data
localPheremones;
localPheremoneCount;
nextPheremone;
% State data
targetPos;
calibrationTimer;
calibrated;
searchTimeout;
home;
end
methods
function uavBrain = UavBrain(uavBody, id, mapRect)
% UAV physical state
uavBrain.uavBody = uavBody;
uavBrain.mapRect = mapRect;
% UAV estimated state
uavBrain.posEstimate = uavBody.getGpsPos();
uavBrain.bearingEstimate = 0;
uavBrain.conc = 0;
uavBrain.closeUavCount = 0;
% UAV AI state
uavBrain.id = id;
uavBrain.currentState = UavState.Start;
uavBrain.localPheremones = Pheremone([0 0], PheremoneType.None);
for i = 1:UavBrain.MaxPheremoneCount
uavBrain.localPheremones(i) = Pheremone([0 0], PheremoneType.None);
end
uavBrain.localPheremoneCount = 0;
uavBrain.targetPos = [0,0];
uavBrain.calibrated = false;
uavBrain.nextPheremone = PheremoneType.None;
uavBrain.home = [0 0];
end
%% Environment Queries
% Returns true if the UAV is coming close to leaving the map
function leaving = leavingMap(uavBrain)
moved = uavBrain.posEstimate - uavBrain.lastPosEstimate;
if uavBrain.posEstimate(1) < uavBrain.mapRect(1,1) && moved(1) < 0
leaving = true;
elseif uavBrain.posEstimate(1) > uavBrain.mapRect(2,1) && moved(1) > 0
leaving = true;
elseif uavBrain.posEstimate(2) < uavBrain.mapRect(1,2) && moved(2) < 0
leaving = true;
elseif uavBrain.posEstimate(2) > uavBrain.mapRect(2,2) && moved(2) > 0
leaving = true;
else
leaving = false;
end
end
% Returns true if this UAV has priority over another UAV with the
% given state and ID
function hasPriority = uavPriority(uavBrain, otherID, otherState)
statePriority = UavState.priority(uavBrain.currentState, otherState);
if statePriority > 0
hasPriority = true;
elseif statePriority < 0
hasPriority = false;
else
if uavBrain.id > otherID
hasPriority = true;
else
hasPriority = false;
end
end
end
% Returns the closest UAV according to the given messages, along
% with is squared distance from this UAV and the total number of
% other UAVs in avoidance range
function [closestUav, closestSqrDist, closeUavCount] = findClosestUav(uavBrain, messages)
closestSqrDist = inf;
closestUav = 0;
closeUavCount = 0;
messageCount = length(messages);
for i = 1:messageCount
if messages(i).id == uavBrain.id || messages(i).state == UavState.Inactive
continue
end
differenceVector = uavBrain.posEstimate - messages(i).pos;
squaredDistance = sqrLen(differenceVector);
if squaredDistance <= UavBrain.AvoidanceRadius^2
closeUavCount = closeUavCount + 1;
end
if squaredDistance < closestSqrDist
closestSqrDist = squaredDistance;
closestUav = i;
end
end
end
%% Output Functions
% Returns the message constructed from this UAV's current state
function message = getMessage(uavBrain)
message = Message();
if uavBrain.uavBody.operational
message.pos = uavBrain.posEstimate;
message.state = uavBrain.currentState;
message.bearing = uavBrain.bearingEstimate;
message.pheremoneType = uavBrain.nextPheremone;
message.id = uint8(uavBrain.id);
end
end
% Plots this UAV's AI data; setting extra=true enables display of
% pheremones, which is highly perfomance intensive
function draw(uavBrain, extra)
if uavBrain.uavBody.operational
if uavBrain.currentState == UavState.Search
plot(uavBrain.targetPos(1),uavBrain.targetPos(2),'x');
end
if extra
for i = 1:length(uavBrain.localPheremones)
if uavBrain.localPheremones(i).type == PheremoneType.Searched
pos = uavBrain.localPheremones(i).pos;
plot(pos(1),pos(2),'+b');
end
end
end
end
end
%% Pathfinding
% Sets the UAVs velocity and turnrate to the ideal values for
% reaching the current target point
function flyToTarget(uavBrain, dt)
targetDiff = uavBrain.targetPos - uavBrain.posEstimate;
targetAng = atan2(targetDiff(1), targetDiff(2));
angDiff = wrapToPi(targetAng - uavBrain.bearingEstimate);
% If not facing towards the target point (within 5 degrees),
% rotate until we are
if abs(angDiff) > pi/60
uavBrain.nextVelocity = UavBody.MinVelocity;
requiredTurnRate = angDiff / (uavBrain.nextVelocity * dt);
% Clamp the turn rate to the correct value
uavBrain.nextTurnRate = max(min(requiredTurnRate,UavBody.MaxTurnRate),-UavBody.MaxTurnRate);
% Otherwise, full speed ahead
else
uavBrain.nextTurnRate = 0;
uavBrain.nextVelocity = UavBody.MaxVelocity;
end
end
function [vel, turn] = avoidanceTrajectory(uavBrain, pos, bearing, dt)
avoidVec = pos - uavBrain.posEstimate;
avoidAng = atan2(avoidVec(1),avoidVec(2));
angDiff = wrapToPi(avoidAng - uavBrain.bearingEstimate);
right = angDiff > 0;
front = abs(angDiff) < pi/2;
if right
turn = -UavBody.MaxTurnRate/2;
else
turn = UavBody.MaxTurnRate/2;
end
if front
turn = turn * 1.5;
vel = UavBody.MinVelocity;
else
vel = UavBody.MaxVelocity;
end
end
% Finds a random target location within the given radius that is
% suitably distant from any "Searched" pheremones
function target = getRandomTarget(uavBrain, radius)
valid = false;
currentRadius = radius;
sqrPheremoneRadius = UavBrain.PheremoneAvoidanceRange^2;
attemptCount = 0;
while valid == false
ang = (rand * 2 * pi) - pi;
dist = (rand * (currentRadius - UavBrain.PheremoneAvoidanceRange)) ...
+ UavBrain.PheremoneAvoidanceRange;
[flippedTargetVec(1),flippedTargetVec(2)] = pol2cart(ang,dist);
target = uavBrain.posEstimate + fliplr(flippedTargetVec);
valid = ( ...
target(1) >= uavBrain.mapRect(1,1) & ...
target(1) <= uavBrain.mapRect(2,1) & ...
target(2) >= uavBrain.mapRect(1,2) & ...
target(2) <= uavBrain.mapRect(2,2));
i = 1;
% To prevent near-infinite loops
if attemptCount < 20
while valid == true && i <= length(uavBrain.localPheremones)
if uavBrain.localPheremones(i).type == PheremoneType.Searched
pheremoneVec = uavBrain.localPheremones(i).pos - target;
sqrPheremoneDist = sqrLen(pheremoneVec);
if sqrPheremoneDist <= sqrPheremoneRadius
valid = false;
end
end
i = i + 1;
end
end
if currentRadius < (uavBrain.mapRect(2,1) - uavBrain.mapRect(1,1))/2
currentRadius = currentRadius + 100.0;
end
attemptCount = attemptCount + 1;
end
end
%% Controlling AI state
% Extract all pheremone data from messages sent within the
% Pheremone Detection Range, and store in the local pheremone array
function getPheremoneData(uavBrain, messages)
for i = 1:length(messages)
if messages(i).pheremoneType ~= PheremoneType.None ...
&& uavBrain.localPheremoneCount < UavBrain.MaxPheremoneCount
uavBrain.localPheremoneCount = uavBrain.localPheremoneCount + 1;
pheremoneVec = messages(i).pos - uavBrain.posEstimate;
sqrPheremoneDist = pheremoneVec(1)^2 + pheremoneVec(2)^2;
if sqrPheremoneDist <= UavBrain.PheremoneDetectionRange^2
pheremone = Pheremone(messages(i).pos, messages(i).pheremoneType);
uavBrain.localPheremones(uavBrain.localPheremoneCount) = pheremone;
end
end
end
end
% Decrements the remaining duration of all stored pheremones, and
% removes pheremones that have expired
function updatePheremoneData(uavBrain, dt)
removePheremoneCount = 0;
for i = 1:uavBrain.localPheremoneCount
uavBrain.localPheremones(i).duration = ...
uavBrain.localPheremones(i).duration - dt;
if uavBrain.localPheremones(i).duration <= 0
removePheremoneCount = removePheremoneCount + 1;
end
end
if removePheremoneCount > 0
uavBrain.localPheremoneCount = uavBrain.localPheremoneCount - removePheremoneCount;
for i = 1:uavBrain.localPheremoneCount
uavBrain.localPheremones(i) = uavBrain.localPheremones(i+removePheremoneCount);
end
end
end
% Update all state values and make decisions moving from time
% t -> t + dt
function decisionStep(uavBrain, cloud, t, dt, messages)
% Do nothing if the UAV is no longer active
if uavBrain.uavBody.operational == false
return;
end
%% Update UAV data
uavBrain.lastPosEstimate = uavBrain.posEstimate;
uavBrain.lastConc = uavBrain.conc;
uavBrain.posEstimate = uavBrain.uavBody.getGpsPos();
uavBrain.conc = uavBrain.uavBody.sensorReading(cloud, t);
%[uavBrain.closeUavs, uavBrain.closeSqrDists] = ...
% uavBrain.findCloseUavs(messages);
[uavBrain.closestUav, uavBrain.closestSqrDist, uavBrain.closeUavCount] ...
= uavBrain.findClosestUav(messages);
uavBrain.getPheremoneData(messages);
uavBrain.nextPheremone = PheremoneType.None;
%% Perform state actions
switch uavBrain.currentState
case UavState.Start
nextState = start(uavBrain, dt, messages);
case UavState.Calibration
nextState = calibration(uavBrain, dt, messages);
case UavState.Search
nextState = search(uavBrain, dt, messages);
case UavState.Approach
nextState = approach(uavBrain, dt, messages);
case UavState.Track
nextState = track(uavBrain, dt, messages);
case UavState.Land
nextState = land(uavBrain, dt, messages);
otherwise
error('UAV brain has invalid state.');
end
%% Update AI state and send commands
uavBrain.lastState = uavBrain.currentState;
% Allow 2 minutes to return to the landing position
if uavBrain.uavBody.batteryLife < 120
uavBrain.currentState = UavState.Land;
else
uavBrain.currentState = nextState;
end
uavBrain.uavBody.setVelocity(uavBrain.nextVelocity);
uavBrain.uavBody.setTurnRate(uavBrain.nextTurnRate);
uavBrain.bearingEstimate = wrapToPi(uavBrain.bearingEstimate + ...
(uavBrain.nextTurnRate * uavBrain.nextVelocity * dt));
uavBrain.updatePheremoneData(dt);
end
%% State functions
% Start state
% This state simply moves the UAV forward and immediately
% transitions; it is used to initialise UAV state at the start of
% the UAV's flight time.
function nextState = start(uavBrain, dt, messages)
uavBrain.nextVelocity = UavBody.MinVelocity;
uavBrain.nextTurnRate = 0;
uavBrain.home = uavBrain.posEstimate;
nextState = UavState.Calibration;
end
% Calibration state
% UAV will spend a fixed amount of time moving forward in an
% attempt to calibrate its bearing.
% This state is only entered once, at the start of the UAV's flight
% time, and should not be entered again as vital collision
% avoidance is not used (due to the close proximity of UAVs at
% launch).
function nextState = calibration(uavBrain, dt, messages)
%% State Actions
if uavBrain.lastState ~= UavState.Calibration
uavBrain.calibrationTimer = UavBrain.CalibrationTime;
end
uavBrain.calibrationTimer = uavBrain.calibrationTimer - dt;
% Update bearing estimate
estimatedMove = uavBrain.posEstimate - uavBrain.lastPosEstimate;
calculatedMove = [sin(uavBrain.bearingEstimate), ...
cos(uavBrain.bearingEstimate)];
combinedMove = estimatedMove + calculatedMove;
uavBrain.bearingEstimate = atan2(combinedMove(1), combinedMove(2));
% Set movement values
uavBrain.nextVelocity = UavBody.MinVelocity;
uavBrain.nextTurnRate = 0;
%% State Transition
% Transition state if enough total time has elapsed
if (uavBrain.calibrationTimer <= 0)
uavBrain.calibrated = true;
nextState = UavState.Search;
else
nextState = UavState.Calibration;
end
end
% Search state
% UAV will continually travel on a random walk, preferring to
% travel short distances.
% Each time a step of the walk is completed, a pheremone will be
% issued, preventing the area from being searched again by any UAV
% for a while.
% Avoidance behaviour is integrated into the search state by
% finding a new random walk whenever the current path would lead to
% a collision.
function nextState = search(uavBrain, dt, messages)
%% State Actions
if uavBrain.lastState ~= UavState.Search
uavBrain.targetPos = getRandomTarget(uavBrain, UavBrain.RandomWalkMaxDistance);
uavBrain.nextPheremone = PheremoneType.Searched;
end
targetDiff = uavBrain.targetPos - uavBrain.posEstimate;
% If the target location has been reached or the target point
% is invalid, find a new target location
while sqrLen(targetDiff) <= (UavBody.MaxVelocity * dt)^2
uavBrain.targetPos = getRandomTarget(uavBrain, UavBrain.RandomWalkMaxDistance);
targetDiff = uavBrain.targetPos - uavBrain.posEstimate;
uavBrain.nextPheremone = PheremoneType.Searched;
end
% Avoidance behaviour
if uavBrain.closeUavCount > 0
closestUavPos = messages(uavBrain.closestUav).pos;
closestUavBearing = messages(uavBrain.closestUav).bearing;
if uavBrain.uavPriority(messages(uavBrain.closestUav).id, messages(uavBrain.closestUav).state) ...
&& uavBrain.closestSqrDist > UavBrain.DangerRadius^2
% Fly as usual, with minimum speed
uavBrain.flyToTarget(dt);
uavBrain.nextVelocity = UavBody.MinVelocity;
else
% Find the avoidance trajectory and use
[uavBrain.nextVelocity, uavBrain.nextTurnRate] = ...
uavBrain.avoidanceTrajectory(closestUavPos, closestUavBearing, dt);
end
else
uavBrain.flyToTarget(dt);
end
%% State Transition
if uavBrain.conc > 0.05
nextState = UavState.Approach;
else
nextState = UavState.Search;
end
end
% Approach state
% UAV will circle into the cloud until it reaches the 1ppm contour
% While approaching, the UAV will attempt to avoid other
% approaching and orbiting UAVs.
% Avoidance behaviour is similar to the orbiting behaviour - UAVs
% will accelerate or decelerate depending on the relative positions
% and trajectories of them and the approaching UAV.
function nextState = approach(uavBrain, dt, messages)
%% State Actions
concDiff = uavBrain.conc - uavBrain.lastConc;
% Avoidance behaviour
if uavBrain.closeUavCount > 0
closestUavPos = messages(uavBrain.closestUav).pos;
closestUavBearing = messages(uavBrain.closestUav).bearing;
% UAV has priority, act as normal but with minimum velocity
if uavBrain.uavPriority(messages(uavBrain.closestUav).id, messages(uavBrain.closestUav).state) ...
&& uavBrain.closestSqrDist > UavBrain.DangerRadius^2
uavBrain.nextVelocity = UavBody.MinVelocity;
% If we are flying into the cloud and are within the map
% bounds, go straight ahead
if concDiff > 0 && ~uavBrain.leavingMap
uavBrain.nextTurnRate = 0;
% Otherwise, steer right
else
uavBrain.nextTurnRate = UavBody.MaxTurnRate * 0.5;
end
else
% Find the avoidance trajectory and use
[uavBrain.nextVelocity, uavBrain.nextTurnRate] = ...
uavBrain.avoidanceTrajectory(closestUavPos, closestUavBearing, dt);
uavBrain.nextVelocity = UavBody.MaxVelocity;
% If the other UAV is tracking and travelling the same
% direction, use the Track avoidance method instead
if messages(uavBrain.closestUav).state == UavState.Track && ...
uavBrain.closestSqrDist > (UavBrain.DangerRadius+30)^2
angDiff = wrapToPi(closestUavBearing - uavBrain.bearingEstimate);
if abs(angDiff) < pi/4
avoidVec = closestUavPos - uavBrain.posEstimate;
avoidAng = atan2(avoidVec(1),avoidVec(2));
angDiff = wrapToPi(avoidAng - uavBrain.bearingEstimate);
front = abs(angDiff) < pi/2;
if front
uavBrain.nextVelocity = UavBody.MinVelocity;
else
uavBrain.nextVelocity = UavBody.MaxVelocity;
end
% If we are flying into the cloud, go straight ahead
if concDiff > 0
uavBrain.nextTurnRate = 0;
% Otherwise, steer right
else
uavBrain.nextVelocity = UavBody.MinVelocity;
uavBrain.nextTurnRate = UavBody.MaxTurnRate * 0.5;
end
end
end
end
else
% If we are flying into the cloud and are within the map
% bounds, go straight ahead
if concDiff > 0 && ~uavBrain.leavingMap
uavBrain.nextVelocity = UavBody.MaxVelocity;
uavBrain.nextTurnRate = 0;
% Otherwise, steer right
else
uavBrain.nextVelocity = UavBody.MinVelocity;
uavBrain.nextTurnRate = UavBody.MaxTurnRate * 0.5;
end
end
%% State Transition
% Transition state back to searching if concentration gets too
% low, or on to tracking if it crosses the threshold
if uavBrain.conc < 0.025;
nextState = UavState.Search;
elseif uavBrain.conc > 0.9
nextState = UavState.Track;
else
nextState = UavState.Approach;
end
end
% Track state
% UAV will continually circle around the 1ppm contour line.
% The UAV will generally not actually avoid other UAVs, but instead
% speed up or slow down to maintain distance from other circling
% UAVs. If necessary however an avoidance trajectory will be used.
function nextState = track(uavBrain, dt, messages)
%% State Actions
concDiff = uavBrain.conc - uavBrain.lastConc;
% Used to calculate the turning rate/direction
contourDistance = 1.0 - uavBrain.conc;
if sign(concDiff) == sign(contourDistance)
uavBrain.nextTurnRate = 0;
else
desiredTurnRate = contourDistance * 4 * UavBody.MaxTurnRate;
uavBrain.nextTurnRate = max(min(desiredTurnRate,UavBody.MaxTurnRate/3),-UavBody.MaxTurnRate/4);
end
% Avoidance behaviour
if uavBrain.closeUavCount > 0
% If other UAVs are close, leave pheremones to prevent
% other UAVs from moving to the cloud
if uavBrain.closeUavCount > 1
if rand < dt*(1/30)
uavBrain.nextPheremone = PheremoneType.Searched;
end
end
closestUavPos = messages(uavBrain.closestUav).pos;
closestUavBearing = messages(uavBrain.closestUav).bearing;
if uavBrain.uavPriority(messages(uavBrain.closestUav).id, messages(uavBrain.closestUav).state) ...
&& uavBrain.closestSqrDist > UavBrain.DangerRadius^2
avoidVec = closestUavPos - uavBrain.posEstimate;
avoidAng = atan2(avoidVec(1),avoidVec(2));
angDiff = wrapToPi(avoidAng - uavBrain.bearingEstimate);
front = abs(angDiff) < pi/2;
if front
uavBrain.nextVelocity = UavBody.MinVelocity;
else
uavBrain.nextVelocity = UavBody.MinVelocity + (UavBody.MaxVelocity - UavBody.MinVelocity)*2/3;
end
else
angDiff = wrapToPi(closestUavBearing - uavBrain.bearingEstimate);
% If we are at serious risk of collision, take evasive maneuvers
if uavBrain.closestSqrDist < UavBrain.DangerRadius^2 ...
&& abs(angDiff) > pi/2
if uavBrain.id < messages(uavBrain.closestUav).id
[uavBrain.nextVelocity, uavBrain.nextTurnRate] = ...
uavBrain.avoidanceTrajectory(closestUavPos, closestUavBearing, dt);
uavBrain.nextVelocity = UavBody.MaxVelocity;
else
uavBrain.nextVelocity = UavBody.MinVelocity;
end
% Otherwise, adjust speed to increase distance and carry on
else
avoidVec = closestUavPos - uavBrain.posEstimate;
avoidAng = atan2(avoidVec(1),avoidVec(2));
angDiff = wrapToPi(avoidAng - uavBrain.bearingEstimate);
front = abs(angDiff) < pi/2;
if front
uavBrain.nextVelocity = UavBody.MinVelocity;
else
uavBrain.nextVelocity = UavBody.MaxVelocity;
end
end
end
else
% If the situation is neutral, progress at a moderate pace
uavBrain.nextVelocity = UavBody.MinVelocity + (UavBody.MaxVelocity - UavBody.MinVelocity)/3;
end
%% State Transition
if uavBrain.conc < 0.3;
nextState = UavState.Approach;
else
nextState = UavState.Track;
end
end
% Land state
% UAV is low on power and will attempt to land back at its start
% position before its battery fails.
% This state is high priority, hence other UAVs will make way for
% this UAV (even tracking UAVs) and allow easy access to the
% landing point.
function nextState = land(uavBrain, dt, messages)
%% State Actions
uavBrain.targetPos = uavBrain.home;
% Avoidance behaviour
if uavBrain.closeUavCount > 0
closestUavPos = messages(uavBrain.closestUav).pos;
closestUavBearing = messages(uavBrain.closestUav).bearing;
if uavBrain.uavPriority(messages(uavBrain.closestUav).id, messages(uavBrain.closestUav).state) ...
&& uavBrain.closestSqrDist > UavBrain.DangerRadius^2
% Fly to home carefully
uavBrain.flyToTarget(dt);
uavBrain.nextVelocity = UavBody.MinVelocity;
else
% Find the avoidance trajectory and use
[uavBrain.nextVelocity, uavBrain.nextTurnRate] = ...
uavBrain.avoidanceTrajectory(closestUavPos, closestUavBearing, dt);
end
else
% If no avoidance necessary, fly straight to home
uavBrain.flyToTarget(dt);
end
targetDiff = uavBrain.targetPos - uavBrain.posEstimate;
% If the target location has been reached, land the UAV
if sqrLen(targetDiff) <= (UavBody.MaxVelocity * dt)^2
uavBrain.uavBody.disable;
end
%% State Transition
nextState = UavState.Land;
end
end
end