-
Notifications
You must be signed in to change notification settings - Fork 372
/
Copy pathscenario_helper.py
832 lines (686 loc) · 29.8 KB
/
scenario_helper.py
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
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
#!/usr/bin/env python
# Copyright (c) 2019 Intel Corporation
#
# This work is licensed under the terms of the MIT license.
# For a copy, see <https://opensource.org/licenses/MIT>.
"""
Summary of useful helper functions for scenarios
"""
import math
import shapely.geometry
import shapely.affinity
import numpy as np
import carla
from agents.tools.misc import vector
from agents.navigation.local_planner import RoadOption
from srunner.scenariomanager.carla_data_provider import CarlaDataProvider
def get_distance_along_route(route, target_location):
"""
Calculate the distance of the given location along the route
Note: If the location is not along the route, the route length will be returned
"""
wmap = CarlaDataProvider.get_map()
covered_distance = 0
prev_position = None
found = False
# Don't use the input location, use the corresponding wp as location
target_location_from_wp = wmap.get_waypoint(target_location).transform.location
for position, _ in route:
location = target_location_from_wp
# Don't perform any calculations for the first route point
if not prev_position:
prev_position = position
continue
# Calculate distance between previous and current route point
interval_length_squared = ((prev_position.x - position.x) ** 2) + ((prev_position.y - position.y) ** 2)
distance_squared = ((location.x - prev_position.x) ** 2) + ((location.y - prev_position.y) ** 2)
# Close to the current position? Stop calculation
if distance_squared < 0.01:
break
if distance_squared < 400 and not distance_squared < interval_length_squared:
# Check if a neighbor lane is closer to the route
# Do this only in a close distance to correct route interval, otherwise the computation load is too high
starting_wp = wmap.get_waypoint(location)
wp = starting_wp.get_left_lane()
while wp is not None:
new_location = wp.transform.location
new_distance_squared = ((new_location.x - prev_position.x) ** 2) + (
(new_location.y - prev_position.y) ** 2)
if np.sign(starting_wp.lane_id) != np.sign(wp.lane_id):
break
if new_distance_squared < distance_squared:
distance_squared = new_distance_squared
location = new_location
else:
break
wp = wp.get_left_lane()
wp = starting_wp.get_right_lane()
while wp is not None:
new_location = wp.transform.location
new_distance_squared = ((new_location.x - prev_position.x) ** 2) + (
(new_location.y - prev_position.y) ** 2)
if np.sign(starting_wp.lane_id) != np.sign(wp.lane_id):
break
if new_distance_squared < distance_squared:
distance_squared = new_distance_squared
location = new_location
else:
break
wp = wp.get_right_lane()
if distance_squared < interval_length_squared:
# The location could be inside the current route interval, if route/lane ids match
# Note: This assumes a sufficiently small route interval
# An alternative is to compare orientations, however, this also does not work for
# long route intervals
curr_wp = wmap.get_waypoint(position)
prev_wp = wmap.get_waypoint(prev_position)
wp = wmap.get_waypoint(location)
if prev_wp and curr_wp and wp:
if wp.road_id in (prev_wp.road_id, curr_wp.road_id):
# Roads match, now compare the sign of the lane ids
if (np.sign(wp.lane_id) == np.sign(prev_wp.lane_id) or
np.sign(wp.lane_id) == np.sign(curr_wp.lane_id)):
# The location is within the current route interval
covered_distance += math.sqrt(distance_squared)
found = True
break
covered_distance += math.sqrt(interval_length_squared)
prev_position = position
return covered_distance, found
def get_crossing_point(actor):
"""
Get the next crossing point location in front of the ego vehicle
@return point of crossing
"""
wp_cross = CarlaDataProvider.get_map().get_waypoint(actor.get_location())
while not wp_cross.is_junction:
wp_cross = wp_cross.next(2)[0]
crossing = carla.Location(x=wp_cross.transform.location.x,
y=wp_cross.transform.location.y, z=wp_cross.transform.location.z)
return crossing
def get_geometric_linear_intersection(ego_location, other_location, move_to_junction=False):
"""
Obtain a intersection point between two actor's location by using their waypoints (wp)
@return point of intersection of the two vehicles
"""
wp_ego_1 = CarlaDataProvider.get_map().get_waypoint(ego_location)
wp_ego_2 = wp_ego_1.next(1)[0]
if move_to_junction:
while True:
next_wp = wp_ego_2.next(1)[0]
if next_wp.is_junction:
break
else:
wp_ego_1 = wp_ego_2
wp_ego_2 = next_wp
ego_1_loc = wp_ego_1.transform.location
ego_2_loc = wp_ego_2.transform.location
wp_other_1 = CarlaDataProvider.get_world().get_map().get_waypoint(other_location)
wp_other_2 = wp_other_1.next(1)[0]
if move_to_junction:
while True:
next_wp = wp_other_2.next(1)[0]
if next_wp.is_junction:
break
else:
wp_other_1 = wp_other_2
wp_other_2 = next_wp
other_1_loc = wp_other_1.transform.location
other_2_loc = wp_other_2.transform.location
s = np.vstack([
(ego_1_loc.x, ego_1_loc.y),
(ego_2_loc.x, ego_2_loc.y),
(other_1_loc.x, other_1_loc.y),
(other_2_loc.x, other_2_loc.y)
])
h = np.hstack((s, np.ones((4, 1))))
line1 = np.cross(h[0], h[1])
line2 = np.cross(h[2], h[3])
x, y, z = np.cross(line1, line2)
if z == 0:
return None
return carla.Location(x=x/z, y=y/z, z=0)
def get_location_in_distance(actor, distance):
"""
Obtain a location in a given distance from the current actor's location.
Note: Search is stopped on first intersection.
@return obtained location and the traveled distance
"""
waypoint = CarlaDataProvider.get_map().get_waypoint(actor.get_location())
traveled_distance = 0
while not waypoint.is_junction and traveled_distance < distance:
waypoint_new = waypoint.next(1.0)[-1]
traveled_distance += waypoint_new.transform.location.distance(waypoint.transform.location)
waypoint = waypoint_new
return waypoint.transform.location, traveled_distance
def get_location_in_distance_from_wp(waypoint, distance, stop_at_junction=True):
"""
Obtain a location in a given distance from the current actor's location.
Note: Search is stopped on first intersection.
@return obtained location and the traveled distance
"""
traveled_distance = 0
while not (waypoint.is_junction and stop_at_junction) and traveled_distance < distance:
wp_next = waypoint.next(1.0)
if wp_next:
waypoint_new = wp_next[-1]
traveled_distance += waypoint_new.transform.location.distance(waypoint.transform.location)
waypoint = waypoint_new
else:
break
return waypoint.transform.location, traveled_distance
def get_waypoint_in_distance(waypoint, distance, stop_at_junction=True):
"""
Obtain a waypoint in a given distance from the current actor's location.
Note: Search is stopped on first intersection.
@return obtained waypoint and the traveled distance
"""
traveled_distance = 0
while not (waypoint.is_junction and stop_at_junction) and traveled_distance < distance:
wp_next = waypoint.next(1.0)
if wp_next:
waypoint_new = wp_next[-1]
traveled_distance += waypoint_new.transform.location.distance(waypoint.transform.location)
waypoint = waypoint_new
else:
break
return waypoint, traveled_distance
def generate_target_waypoint_list(waypoint, turn=0):
"""
This method follow waypoints to a junction and choose path based on turn input.
Turn input: LEFT -> -1, RIGHT -> 1, STRAIGHT -> 0
@returns a waypoint list from the starting point to the end point according to turn input
"""
reached_junction = False
threshold = math.radians(0.1)
plan = []
while True:
wp_choice = waypoint.next(2)
if len(wp_choice) > 1:
reached_junction = True
waypoint = choose_at_junction(waypoint, wp_choice, turn)
else:
waypoint = wp_choice[0]
plan.append((waypoint, RoadOption.LANEFOLLOW))
# End condition for the behavior
if turn != 0 and reached_junction and len(plan) >= 3:
v_1 = vector(
plan[-2][0].transform.location,
plan[-1][0].transform.location)
v_2 = vector(
plan[-3][0].transform.location,
plan[-2][0].transform.location)
angle_wp = math.acos(
np.dot(v_1, v_2) / abs((np.linalg.norm(v_1) * np.linalg.norm(v_2))))
if angle_wp < threshold:
break
elif reached_junction and not plan[-1][0].is_junction:
break
return plan, plan[-1][0]
def generate_target_waypoint_list_multilane(waypoint, change='left', # pylint: disable=too-many-return-statements
distance_same_lane=10, distance_other_lane=25,
total_lane_change_distance=25, check=True,
lane_changes=1, step_distance=2):
"""
This methods generates a waypoint list which leads the vehicle to a parallel lane.
The change input must be 'left' or 'right', depending on which lane you want to change.
The default step distance between waypoints on the same lane is 2m.
The default step distance between the lane change is set to 25m.
@returns a waypoint list from the starting point to the end point on a right or left parallel lane.
The function might break before reaching the end point, if the asked behavior is impossible.
"""
plan = []
plan.append((waypoint, RoadOption.LANEFOLLOW)) # start position
option = RoadOption.LANEFOLLOW
# Same lane
distance = 0
while distance < distance_same_lane:
next_wps = plan[-1][0].next(step_distance)
if not next_wps:
return None, None
next_wp = next_wps[0]
distance += next_wp.transform.location.distance(plan[-1][0].transform.location)
plan.append((next_wp, RoadOption.LANEFOLLOW))
if change == 'left':
option = RoadOption.CHANGELANELEFT
elif change == 'right':
option = RoadOption.CHANGELANERIGHT
else:
# ERROR, input value for change must be 'left' or 'right'
return None, None
lane_changes_done = 0
lane_change_distance = total_lane_change_distance / lane_changes
# Lane change
while lane_changes_done < lane_changes:
# Move forward
next_wps = plan[-1][0].next(lane_change_distance)
if not next_wps:
return None, None
next_wp = next_wps[0]
# Get the side lane
if change == 'left':
if check and str(next_wp.lane_change) not in ['Left', 'Both']:
return None, None
side_wp = next_wp.get_left_lane()
else:
if check and str(next_wp.lane_change) not in ['Right', 'Both']:
return None, None
side_wp = next_wp.get_right_lane()
if not side_wp or side_wp.lane_type != carla.LaneType.Driving:
return None, None
# Update the plan
plan.append((side_wp, option))
lane_changes_done += 1
# Other lane
distance = 0
while distance < distance_other_lane:
next_wps = plan[-1][0].next(step_distance)
if not next_wps:
return None, None
next_wp = next_wps[0]
distance += next_wp.transform.location.distance(plan[-1][0].transform.location)
plan.append((next_wp, RoadOption.LANEFOLLOW))
target_lane_id = plan[-1][0].lane_id
return plan, target_lane_id
def generate_target_waypoint(waypoint, turn=0):
"""
This method follow waypoints to a junction and choose path based on turn input.
Turn input: LEFT -> -1, RIGHT -> 1, STRAIGHT -> 0
@returns a waypoint list according to turn input
"""
sampling_radius = 1
reached_junction = False
wp_list = []
while True:
wp_choice = waypoint.next(sampling_radius)
# Choose path at intersection
if not reached_junction and (len(wp_choice) > 1 or wp_choice[0].is_junction):
reached_junction = True
waypoint = choose_at_junction(waypoint, wp_choice, turn)
else:
waypoint = wp_choice[0]
wp_list.append(waypoint)
# End condition for the behavior
if reached_junction and not wp_list[-1].is_junction:
break
return wp_list[-1]
def generate_target_waypoint_in_route(waypoint, route):
"""
This method follow waypoints to a junction
@returns a waypoint list according to turn input
"""
target_waypoint = None
wmap = CarlaDataProvider.get_map()
reached_junction = False
# Get the route location
shortest_distance = float('inf')
for index, route_pos in enumerate(route):
route_location = route_pos[0].location
trigger_location = waypoint.transform.location
dist_to_route = trigger_location.distance(route_location)
if dist_to_route <= shortest_distance:
closest_index = index
shortest_distance = dist_to_route
route_location = route[closest_index][0].location
index = closest_index
for i in range(index, len(route)):
route_location = route[i][0].location
road_option = route[i][1]
# Enter the junction
if not reached_junction and (road_option in (RoadOption.LEFT, RoadOption.RIGHT, RoadOption.STRAIGHT)):
reached_junction = True
# End condition for the behavior, at the end of the junction
if reached_junction and (road_option not in (RoadOption.LEFT, RoadOption.RIGHT, RoadOption.STRAIGHT)):
target_waypoint = route_location
break
return wmap.get_waypoint(target_waypoint)
def choose_at_junction(current_waypoint, next_choices, direction=0):
"""
This function chooses the appropriate waypoint from next_choices based on direction
"""
current_transform = current_waypoint.transform
current_location = current_transform.location
projected_location = current_location + \
carla.Location(
x=math.cos(math.radians(current_transform.rotation.yaw)),
y=math.sin(math.radians(current_transform.rotation.yaw)))
current_vector = vector(current_location, projected_location)
cross_list = []
cross_to_waypoint = {}
for waypoint in next_choices:
waypoint = waypoint.next(10)[0]
select_vector = vector(current_location, waypoint.transform.location)
cross = np.cross(current_vector, select_vector)[2]
cross_list.append(cross)
cross_to_waypoint[cross] = waypoint
select_cross = None
if direction > 0:
select_cross = max(cross_list)
elif direction < 0:
select_cross = min(cross_list)
else:
select_cross = min(cross_list, key=abs)
return cross_to_waypoint[select_cross]
def get_intersection(ego_actor, other_actor):
"""
Obtain a intersection point between two actor's location
@return the intersection location
"""
waypoint = CarlaDataProvider.get_map().get_waypoint(ego_actor.get_location())
waypoint_other = CarlaDataProvider.get_map().get_waypoint(other_actor.get_location())
max_dist = float("inf")
distance = float("inf")
while distance <= max_dist:
max_dist = distance
current_location = waypoint.transform.location
waypoint_choice = waypoint.next(1)
# Select the straighter path at intersection
if len(waypoint_choice) > 1:
max_dot = -1 * float('inf')
loc_projection = current_location + carla.Location(
x=math.cos(math.radians(waypoint.transform.rotation.yaw)),
y=math.sin(math.radians(waypoint.transform.rotation.yaw)))
v_current = vector(current_location, loc_projection)
for wp_select in waypoint_choice:
v_select = vector(current_location, wp_select.transform.location)
dot_select = np.dot(v_current, v_select)
if dot_select > max_dot:
max_dot = dot_select
waypoint = wp_select
else:
waypoint = waypoint_choice[0]
distance = current_location.distance(waypoint_other.transform.location)
return current_location
def detect_lane_obstacle(actor, extension_factor=3, margin=1.02):
"""
This function identifies if an obstacle is present in front of the reference actor
"""
world_actors = CarlaDataProvider.get_all_actors().filter('vehicle.*')
actor_bbox = actor.bounding_box
actor_transform = actor.get_transform()
actor_location = actor_transform.location
actor_vector = actor_transform.rotation.get_forward_vector()
actor_vector = np.array([actor_vector.x, actor_vector.y])
actor_vector = actor_vector / np.linalg.norm(actor_vector)
actor_vector = actor_vector * (extension_factor - 1) * actor_bbox.extent.x
actor_location = actor_location + carla.Location(actor_vector[0], actor_vector[1])
actor_yaw = actor_transform.rotation.yaw
is_hazard = False
for adversary in world_actors:
if adversary.id != actor.id and \
actor_transform.location.distance(adversary.get_location()) < 50:
adversary_bbox = adversary.bounding_box
adversary_transform = adversary.get_transform()
adversary_loc = adversary_transform.location
adversary_yaw = adversary_transform.rotation.yaw
overlap_adversary = RotatedRectangle(
adversary_loc.x, adversary_loc.y,
2 * margin * adversary_bbox.extent.x, 2 * margin * adversary_bbox.extent.y, adversary_yaw)
overlap_actor = RotatedRectangle(
actor_location.x, actor_location.y,
2 * margin * actor_bbox.extent.x * extension_factor, 2 * margin * actor_bbox.extent.y, actor_yaw)
overlap_area = overlap_adversary.intersection(overlap_actor).area
if overlap_area > 0:
is_hazard = True
break
return is_hazard
def get_junction_topology(junction):
"""
Given a junction, returns a two list of waypoints corresponding to the entry
and exit lanes of the junction
"""
def get_lane_key(waypoint):
return str(waypoint.road_id) + '*' + str(waypoint.lane_id)
def get_junction_entry_wp(entry_wp):
while entry_wp.is_junction:
entry_wps = entry_wp.previous(0.2)
if len(entry_wps) == 0:
return None
entry_wp = entry_wps[0]
return entry_wp
def get_junction_exit_wp(exit_wp):
while exit_wp.is_junction:
exit_wps = exit_wp.next(0.2)
if len(exit_wps) == 0:
return None
exit_wp = exit_wps[0]
return exit_wp
used_entry_lanes = []
used_exit_lanes = []
entry_wps = []
exit_wps = []
for entry_wp, exit_wp in junction.get_waypoints(carla.LaneType.Driving):
entry_wp = get_junction_entry_wp(entry_wp)
if not entry_wp:
continue
if get_lane_key(entry_wp) not in used_entry_lanes:
used_entry_lanes.append(get_lane_key(entry_wp))
entry_wps.append(entry_wp)
exit_wp = get_junction_exit_wp(exit_wp)
if not exit_wp:
continue
if get_lane_key(exit_wp) not in used_exit_lanes:
used_exit_lanes.append(get_lane_key(exit_wp))
exit_wps.append(exit_wp)
return entry_wps, exit_wps
def filter_junction_wp_direction(reference_wp, wp_list, direction='opposite'):
"""
Given a list of entry / exit wps of a junction, filters them according to a specific direction,
returning all waypoint part of lanes that are at 'direction' with respect to the reference.
This might fail for complex junctions, as only the wp yaws is checked, not their relative positions
"""
filtered_wps = []
reference_yaw = reference_wp.transform.rotation.yaw
for wp in wp_list:
diff = (wp.transform.rotation.yaw - reference_yaw) % 360
if diff > 330.0:
wp_direction = 'ref'
elif diff > 225.0:
wp_direction = 'right'
elif diff > 135.0:
wp_direction = 'opposite'
elif diff > 30.0:
wp_direction = 'left'
else:
wp_direction = 'ref'
if wp_direction == direction:
filtered_wps.append(wp)
return filtered_wps
def get_closest_traffic_light(waypoint, traffic_lights=None):
"""
Returns the traffic light closest to the waypoint. The distance is computed between the
waypoint and the traffic light's bounding box.
Checks all traffic lights part of 'traffic_lights', or all the town ones, if None are passed.
"""
if not traffic_lights:
traffic_lights = CarlaDataProvider.get_all_actors().filter('*traffic_light*')
closest_dist = float('inf')
closest_tl = None
wp_location = waypoint.transform.location
for tl in traffic_lights:
tl_waypoints = tl.get_stop_waypoints()
for tl_waypoint in tl_waypoints:
distance = wp_location.distance(tl_waypoint.transform.location)
if distance < closest_dist:
closest_dist = distance
closest_tl = tl
return closest_tl
def get_offset_transform(transform, offset):
"""
This function adjusts the give transform by offset and returns the new transform.
"""
if offset != 0:
forward_vector = transform.rotation.get_forward_vector()
orthogonal_vector = carla.Vector3D(x=-forward_vector.y, y=forward_vector.x, z=forward_vector.z)
transform.location.x = transform.location.x + offset * orthogonal_vector.x
transform.location.y = transform.location.y + offset * orthogonal_vector.y
return transform
def get_troad_from_transform(actor_transform):
"""
This function finds the lateral road position (t) from actor_transform
"""
actor_loc = actor_transform.location
c_wp = CarlaDataProvider.get_map().get_waypoint(actor_loc)
left_lanes, right_lanes = [], []
# opendrive standard: (left ==> +ve lane_id) and (right ==> -ve lane_id)
ref_lane = CarlaDataProvider.get_map().get_waypoint_xodr(c_wp.road_id, 0, c_wp.s)
for i in range(-50, 50):
_wp = CarlaDataProvider.get_map().get_waypoint_xodr(c_wp.road_id, i, c_wp.s)
if _wp:
if i < 0:
left_lanes.append(_wp)
elif i > 0:
right_lanes.append(_wp)
if left_lanes:
left_lane_ids = [ln.lane_id for ln in left_lanes]
lm_id = min(left_lane_ids)
lm_lane = left_lanes[left_lane_ids.index(lm_id)]
lm_lane_offset = lm_lane.lane_width / 2
else:
lm_lane, lm_lane_offset = ref_lane, 0
lm_tr = get_offset_transform(carla.Transform(lm_lane.transform.location, lm_lane.transform.rotation),
lm_lane_offset)
distance_from_lm_lane_edge = lm_tr.location.distance(actor_loc)
distance_from_lm_lane_ref_lane = lm_tr.location.distance(ref_lane.transform.location)
if right_lanes:
right_lane_ids = [ln.lane_id for ln in right_lanes]
rm_id = max(right_lane_ids)
rm_lane = right_lanes[right_lane_ids.index(rm_id)]
rm_lane_offset = -rm_lane.lane_width / 2
else:
rm_lane, rm_lane_offset = ref_lane, -distance_from_lm_lane_ref_lane
distance_from_rm_lane_edge = get_offset_transform(carla.Transform(rm_lane.transform.location,
rm_lane.transform.rotation),
rm_lane_offset).location.distance(actor_loc)
t_road = ref_lane.transform.location.distance(actor_loc)
if not right_lanes or not left_lanes:
closest_road_edge = min(distance_from_lm_lane_edge, distance_from_rm_lane_edge)
if closest_road_edge == distance_from_lm_lane_edge:
t_road = -1*t_road
else:
if c_wp.lane_id < 0:
t_road = -1*t_road
return t_road
def get_distance_between_actors(current, target, distance_type="euclidianDistance", freespace=False,
global_planner=None):
"""
This function finds the distance between actors for different use cases described by distance_type and freespace
attributes
"""
target_transform = CarlaDataProvider.get_transform(target)
current_transform = CarlaDataProvider.get_transform(current)
target_wp = CarlaDataProvider.get_map().get_waypoint(target_transform.location)
current_wp = CarlaDataProvider.get_map().get_waypoint(current_transform.location)
extent_sum_x, extent_sum_y = 0, 0
if freespace:
if isinstance(target, (carla.Vehicle, carla.Walker)):
extent_sum_x = target.bounding_box.extent.x + current.bounding_box.extent.x
extent_sum_y = target.bounding_box.extent.y + current.bounding_box.extent.y
if distance_type == "longitudinal":
if not current_wp.road_id == target_wp.road_id:
distance = 0
# Get the route
route = global_planner.trace_route(current_transform.location, target_transform.location)
# Get the distance of the route
for i in range(1, len(route)):
curr_loc = route[i][0].transform.location
prev_loc = route[i - 1][0].transform.location
distance += curr_loc.distance(prev_loc)
else:
distance = abs(current_wp.s - target_wp.s)
if freespace:
distance = distance - extent_sum_x
elif distance_type == "lateral":
target_t = get_troad_from_transform(target_transform)
current_t = get_troad_from_transform(current_transform)
distance = abs(target_t - current_t)
if freespace:
distance = distance - extent_sum_y
elif distance_type in ["cartesianDistance", "euclidianDistance"]:
distance = target_transform.location.distance(current_transform.location)
if freespace:
distance = distance - extent_sum_x
else:
raise TypeError("unknown distance_type: {}".format(distance_type))
# distance will be negative for feeespace when there is overlap condition
# truncate to 0.0 when this happens
distance = 0.0 if distance < 0.0 else distance
return distance
def get_same_dir_lanes(waypoint):
"""
Gets all the lanes with the same direction of the road of a wp.
Ordered from the edge lane to the center one (from outwards to inwards)
"""
same_dir_wps = [waypoint]
# Check roads on the right
right_wp = waypoint
while True:
possible_right_wp = right_wp.get_right_lane()
if possible_right_wp is None or possible_right_wp.lane_type != carla.LaneType.Driving:
break
right_wp = possible_right_wp
same_dir_wps.append(right_wp)
# Check roads on the left
left_wp = waypoint
while True:
possible_left_wp = left_wp.get_left_lane()
if possible_left_wp is None or possible_left_wp.lane_type != carla.LaneType.Driving:
break
if possible_left_wp.lane_id * left_wp.lane_id < 0:
break
left_wp = possible_left_wp
same_dir_wps.insert(0, left_wp)
return same_dir_wps
def get_opposite_dir_lanes(waypoint):
"""
Gets all the lanes with opposite direction of the road of a wp
Ordered from the center lane to the edge one (from inwards to outwards)
"""
other_dir_wps = []
other_dir_wp = None
# Get the first lane of the opposite direction
left_wp = waypoint
while True:
possible_left_wp = left_wp.get_left_lane()
if possible_left_wp is None:
break
if possible_left_wp.lane_id * left_wp.lane_id < 0:
other_dir_wp = possible_left_wp
break
left_wp = possible_left_wp
if not other_dir_wp:
return other_dir_wps
# Check roads on the right
right_wp = other_dir_wp
while True:
if right_wp.lane_type == carla.LaneType.Driving:
other_dir_wps.append(right_wp)
possible_right_wp = right_wp.get_right_lane()
if possible_right_wp is None:
break
right_wp = possible_right_wp
return other_dir_wps
class RotatedRectangle(object):
"""
This class contains method to draw rectangle and find intersection point.
"""
def __init__(self, c_x, c_y, width, height, angle):
self.c_x = c_x
self.c_y = c_y
self.w = width # pylint: disable=invalid-name
self.h = height # pylint: disable=invalid-name
self.angle = angle
def get_contour(self):
"""
create contour
"""
w = self.w
h = self.h
c = shapely.geometry.box(-w / 2.0, -h / 2.0, w / 2.0, h / 2.0)
rc = shapely.affinity.rotate(c, self.angle)
return shapely.affinity.translate(rc, self.c_x, self.c_y)
def intersection(self, other):
"""
Obtain a intersection point between two contour.
"""
return self.get_contour().intersection(other.get_contour())