Skip to content

Commit 28f4259

Browse files
authored
Merge pull request #7 from SubhashiniPerumal/scenario
Implementation of Traffic Scenario 01
2 parents 4825f32 + 3fb621d commit 28f4259

File tree

5 files changed

+217
-2
lines changed

5 files changed

+217
-2
lines changed

Docs/list_of_scenarios.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,8 @@ through a junction without signal.
6363
The ego vehicle is passing through a junction without traffic lights
6464
And encounters another vehicle passing across the junction. The ego vehicle has
6565
to avoid collision and navigate accross the junction to succeed.
66+
67+
### ControlLoss
68+
In this scenario control loss of a vehicle is tested due to bad road conditions, etc
69+
and it checks whether the vehicle is regained its control and corrected its course.
70+

ScenarioManager/atomic_scenario_behavior.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,3 +635,33 @@ def terminate(self, new_status):
635635
self._control.brake = 0.0
636636
self._vehicle.apply_control(self._control)
637637
super(SyncArrival, self).terminate(new_status)
638+
639+
class SteerVehicle(AtomicBehavior):
640+
641+
"""
642+
This class contains an atomic steer behavior.
643+
To set the steer value of the vehicle.
644+
"""
645+
646+
def __init__(self, vehicle, steer_value, name="Steering"):
647+
"""
648+
Setup vehicle and maximum steer value
649+
"""
650+
super(SteerVehicle, self).__init__(name)
651+
self.logger.debug("%s.__init__()" % (self.__class__.__name__))
652+
self._control = carla.VehicleControl()
653+
self._vehicle = vehicle
654+
self._steer_value = steer_value
655+
656+
def update(self):
657+
"""
658+
Set steer to steer_value until reaching full stop
659+
"""
660+
self._control.steer = self._steer_value
661+
new_status = py_trees.common.Status.SUCCESS
662+
663+
self.logger.debug("%s.update()[%s->%s]" %
664+
(self.__class__.__name__, self.status, new_status))
665+
self._vehicle.apply_control(self._control)
666+
667+
return new_status

ScenarioManager/atomic_scenario_criteria.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,3 +354,52 @@ def _count_lane_invasion(weak_self, event):
354354
if not self:
355355
return
356356
self.actual_value += 1
357+
358+
359+
class ReachedRegionTest(Criterion):
360+
361+
"""
362+
This class contains the reached region test
363+
The test is a success if the vehicle reaches a specified region
364+
"""
365+
366+
def __init__(self, vehicle, min_x, max_x, min_y,
367+
max_y, name="ReachedRegionTest"):
368+
"""
369+
Setup trigger region (rectangle provided by
370+
[min_x,min_y] and [max_x,max_y]
371+
"""
372+
super(ReachedRegionTest, self).__init__(name, vehicle, 0)
373+
self.logger.debug("%s.__init__()" % (self.__class__.__name__))
374+
self._vehicle = vehicle
375+
self._min_x = min_x
376+
self._max_x = max_x
377+
self._min_y = min_y
378+
self._max_y = max_y
379+
380+
def update(self):
381+
"""
382+
Check if the vehicle location is within trigger region
383+
"""
384+
new_status = py_trees.common.Status.RUNNING
385+
386+
location = CarlaDataProvider.get_location(self._vehicle)
387+
if location is None:
388+
return new_status
389+
390+
in_region = False
391+
if self.test_status != "SUCCESS":
392+
in_region = (location.x > self._min_x and location.x < self._max_x) and (
393+
location.y > self._min_y and location.y < self._max_y)
394+
if in_region:
395+
self.test_status = "SUCCESS"
396+
else:
397+
self.test_status = "RUNNING"
398+
399+
if self.test_status == "SUCCESS":
400+
new_status = py_trees.common.Status.SUCCESS
401+
402+
self.logger.debug("%s.update()[%s->%s]" %
403+
(self.__class__.__name__, self.status, new_status))
404+
405+
return new_status

Scenarios/control_loss.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/env python
2+
3+
#
4+
# This work is licensed under the terms of the MIT license.
5+
# For a copy, see <https://opensource.org/licenses/MIT>.
6+
7+
"""
8+
Control Loss Vehicle scenario:
9+
10+
The scenario realizes that the vehicle looses control due to
11+
bad road conditions, etc. and checks to see if the vehicle
12+
regains control and corrects it's course.
13+
"""
14+
15+
import random
16+
17+
import py_trees
18+
import carla
19+
20+
from ScenarioManager.atomic_scenario_behavior import *
21+
from ScenarioManager.atomic_scenario_criteria import *
22+
from ScenarioManager.scenario_manager import Scenario
23+
from ScenarioManager.timer import TimeOut
24+
from Scenarios.basic_scenario import *
25+
26+
27+
class ControlLoss(BasicScenario):
28+
29+
"""
30+
Implementation of "Control Loss Vehicle" (Traffic Scenario 01)
31+
32+
Location : Town03
33+
"""
34+
35+
timeout = 60 # Timeout of scenario in seconds
36+
37+
# ego vehicle parameters
38+
_ego_vehicle_model = 'vehicle.carlamotors.carlacola'
39+
_ego_vehicle_start = carla.Transform(
40+
carla.Location(x=15, y=207.2, z=2.0), carla.Rotation(yaw=0))
41+
_no_of_jitter_actions = 20
42+
_noise_mean = 0 # Mean value of steering noise
43+
_noise_std = 0.02 # Std. deviation of steerning noise
44+
_dynamic_mean = 0.05
45+
46+
def __init__(self, world, debug_mode=False):
47+
"""
48+
Setup all relevant parameters and create scenario
49+
and instantiate scenario manager
50+
"""
51+
self.ego_vehicle = setup_vehicle(world,
52+
self._ego_vehicle_model,
53+
self._ego_vehicle_start,
54+
hero=True)
55+
56+
super(ControlLoss, self).__init__(
57+
name="ControlLoss",
58+
town="Town03",
59+
world=world,
60+
debug_mode=debug_mode)
61+
62+
def _create_behavior(self):
63+
"""
64+
The scenario defined after is a "control loss vehicle" scenario. After
65+
invoking this scenario, it will wait for the user controlled vehicle to
66+
enter the start region, then it performs a jitter action. Finally,
67+
the user-controlled vehicle has to reach a target region.
68+
If this does not happen within 60 seconds, a timeout stops the scenario
69+
"""
70+
71+
# start condition
72+
start_condition = InTriggerRegion(self.ego_vehicle, 43, 49, 190, 210)
73+
74+
# jitter sequence
75+
jitter_sequence = py_trees.composites.Sequence(
76+
"Jitter Sequence Behavior")
77+
jitter_timeout = TimeOut(timeout=0.2, name="Timeout for next jitter")
78+
79+
for i in range(self._no_of_jitter_actions):
80+
ego_vehicle_max_steer = random.gauss(
81+
self._noise_mean, self._noise_std)
82+
if ego_vehicle_max_steer > 0:
83+
ego_vehicle_max_steer += self._dynamic_mean
84+
elif ego_vehicle_max_steer < 0:
85+
ego_vehicle_max_steer -= self._dynamic_mean
86+
87+
# turn vehicle
88+
turn = SteerVehicle(
89+
self.ego_vehicle,
90+
ego_vehicle_max_steer,
91+
name='Steering ' + str(i))
92+
93+
jitter_action = py_trees.composites.Parallel(
94+
"Jitter Actions with Timeouts",
95+
policy=py_trees.common.
96+
ParallelPolicy.SUCCESS_ON_ALL)
97+
jitter_action.add_child(turn)
98+
jitter_action.add_child(jitter_timeout)
99+
jitter_sequence.add_child(jitter_action)
100+
101+
# endcondition
102+
end_condition = InTriggerRegion(self.ego_vehicle, 145, 150, 190, 210)
103+
104+
# Build behavior tree
105+
sequence = py_trees.composites.Sequence("Sequence Behavior")
106+
sequence.add_child(start_condition)
107+
sequence.add_child(jitter_sequence)
108+
sequence.add_child(end_condition)
109+
return sequence
110+
111+
def _create_test_criteria(self):
112+
"""
113+
A list of all test criteria will be created that is later used
114+
in parallel behavior tree.
115+
"""
116+
criteria = []
117+
118+
collision_criterion = CollisionTest(self.ego_vehicle)
119+
# Region check to verify if the vehicle reached correct lane
120+
reached_region_criterion = ReachedRegionTest(
121+
self.ego_vehicle,
122+
113, 119,
123+
204.2, 210.2)
124+
125+
criteria.append(collision_criterion)
126+
criteria.append(reached_region_criterion)
127+
128+
return criteria

scenario_runner.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from Scenarios.object_crash_vehicle import *
2525
from Scenarios.no_signal_junction_crossing import NoSignalJunctionCrossing
2626
from Scenarios.object_crash_intersection import *
27+
from Scenarios.control_loss import *
2728
from ScenarioManager.scenario_manager import ScenarioManager
2829

2930

@@ -40,7 +41,8 @@
4041
"OppositeVehicleRunningRedLight",
4142
"NoSignalJunctionCrossing",
4243
"VehicleTurningRight",
43-
"VehicleTurningLeft"
44+
"VehicleTurningLeft",
45+
"ControlLoss"
4446
}
4547

4648

@@ -100,7 +102,8 @@ def main(args):
100102
if args.junit is not None:
101103
junit_filename = args.junit.split(".")[0] + "_{}.xml".format(i)
102104

103-
if not manager.analyze_scenario(args.output, args.filename, junit_filename):
105+
if not manager.analyze_scenario(
106+
args.output, args.filename, junit_filename):
104107
print("Success!")
105108
else:
106109
print("Failure!")

0 commit comments

Comments
 (0)