Skip to content

Commit

Permalink
New version of test generator. Complete rework. Replaced lists with d…
Browse files Browse the repository at this point in the history
…icts and added more extendability.
  • Loading branch information
masskro0 committed Apr 17, 2020
1 parent 50ed7b6 commit c66234d
Show file tree
Hide file tree
Showing 10 changed files with 443 additions and 256 deletions.
255 changes: 161 additions & 94 deletions test_generator.py

Large diffs are not rendered by default.

62 changes: 36 additions & 26 deletions utils/dbc_xml_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,67 +68,77 @@ def ai_freq(self, frequency="6"):
aifreq = ElementTree.SubElement(self.root, "aiFrequency")
aifreq.text = str(frequency)

def add_car(self, init_state, waypoints, participant_id="ego", model="ETK800"):
def add_car(self, participant):
"""Adds a car to this test case. At least one car (the ego car) should be added.
:param init_state: Array with initial states. Contains: x-coordinate (int), y-coordinate (int),
:param participant: Dict which contains init_state, waypoints, participant_id and model. See the lines below
for more information:
init_state: Array with initial states. Contains: x-coordinate (int), y-coordinate (int),
orientation (int), movementMode (MANUAL, _BEAMNG, AUTONOMOUS, TRAINING),
speed (int)
:param waypoints: Array with waypoints. One waypoint contains: x-coordinate (int),
waypoints: Array with waypoints. One waypoint contains: x-coordinate (int),
y-coordinate (int), tolerance (int), movementMode (see above),
speedLimit (int) (optional)
:param participant_id: unique ID of this participant as String.
:param model: BeamNG model car as String. See beamngpy documentation for more models.
participant_id: unique ID of this participant as String.
model: BeamNG model car as String. See beamngpy documentation for more models.
:return: Void
"""
participant_id = participant.get("id")
init_state = participant.get("init_state")
waypoints = participant.get("waypoints")
model = participant.get("model")
participant = ElementTree.SubElement(self.participants, "participant")
participant.set("id", participant_id)
participant.set("model", model)
ElementTree.SubElement(participant, 'initialState x="{}" y="{}"'
' orientation="{}" movementMode="{}"'
' speed="{}"'
.format(str(init_state[0]), str(init_state[1]), str(init_state[2]),
init_state[3], str(init_state[4])))
.format(str(init_state.get("x")), str(init_state.get("y")),
str(init_state.get("orientation")), init_state.get("movementMode"),
str(init_state.get("speed"))))

ai = ElementTree.SubElement(participant, "ai")
# ElementTree.SubElement(ai, 'roadCenterDistance id="{}"'.format("egoLaneDist"))
# ElementTree.SubElement(ai, 'camera width="{}" height="{}" fov="{}" direction="{}" id="{}"'
# .format(str(320), str(160), str(120), "FRONT", "egoFrontCamera"))
ElementTree.SubElement(ai, 'roadCenterDistance id="{}"'.format("egoLaneDist"))
ElementTree.SubElement(ai, 'camera width="{}" height="{}" fov="{}" direction="{}" id="{}"'
.format(str(600), str(400), str(120), "FRONT", "egoFrontCamera"))

movement = ElementTree.SubElement(participant, "movement")
for waypoint in waypoints:
waypoint_tag = ElementTree.SubElement(movement, 'waypoint x="{}" y="{}" tolerance="{}"'
' movementMode="{}"'
.format(str(waypoint[0]), str(waypoint[1]),
str(waypoint[2]), waypoint[3]))
if len(waypoint) == 5:
waypoint_tag.set("speedLimit", str(waypoint[4]))
.format(str(waypoint.get("x")), str(waypoint.get("y")),
str(waypoint.get("tolerance")),
waypoint.get("movementMode")))
if waypoint.get("speedLimit"):
waypoint_tag.set("speedLimit", str(waypoint.get("speedLimit")))

def add_precond_partic_sc_speed(self, vc_pos, sc_speed):
"""Adds a precondition for a position, which must be satisfied in order to continue the test.
This method requires a lower speed bound, which must be reached.
:param vc_pos: Position of the precondition. Array contains: participant id (string),
:param vc_pos: Position of the precondition. Dict contains: participant id (string),
xPos (int), yPos (int), tolerance (int) defines a circle which must be entered.
:param sc_speed: Lower speed bound. Array contains: participant id (string), limit (int).
:param sc_speed: Lower speed bound as integer.
:return: Void
"""
vc_position = ElementTree.SubElement(self.preconditions, 'vcPosition')
vc_position.set("participant", vc_pos[0])
vc_position.set("x", str(vc_pos[1]))
vc_position.set("y", str(vc_pos[2]))
vc_position.set("tolerance", str(vc_pos[3]))
vc_position.set("participant", vc_pos.get("id"))
vc_position.set("x", str(vc_pos.get("x")))
vc_position.set("y", str(vc_pos.get("y")))
vc_position.set("tolerance", str(vc_pos.get("tolerance")))

not_tag = ElementTree.SubElement(vc_position, "not")
ElementTree.SubElement(not_tag, 'scSpeed participant="{}" limit="{}"'
.format(vc_pos[0], str(sc_speed[1])))
.format(vc_pos.get("id"), str(sc_speed)))

def add_success_point(self, sc_pos):
"""Defines when a test was successfully finished.
:param sc_pos: Array of success states. Array contains: participant id (string), xPos (int),
yPos (int), tolerance (int) which defines a circle.
def add_success_point(self, participant_id, success_point):
"""Point when reached a test was successfully finished.
:param: participant_id: ID of the participant as a string.
:param success_point: Dict of success states. Contains: x (int), y (int), tolerance (int) which defines a
circle.
:return: Void
"""
ElementTree.SubElement(self.success, 'scPosition participant="{}" x="{}" y="{}" tolerance="{}"'
.format(sc_pos[0], str(sc_pos[1]), str(sc_pos[2]), str(sc_pos[3])))
.format(participant_id, str(success_point.get("x")), str(success_point.get("y")),
str(success_point.get("tolerance"))))

def add_failure_damage(self, participant_id):
"""Adds damage observation as a test failure condition.
Expand Down
39 changes: 30 additions & 9 deletions utils/dbc_xml_examplebuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,41 @@
dbc.steps_per_second(60)
dbc.ai_freq(6)

init_state = [6, 6, 0, "MANUAL", 50]
waypoint1 = [0, 4, 4, "_BEAMNG", 40]
waypoint2 = [61, 4, 5, "AUTONOMOUS"]
init_state = {"x": 0,
"y": 4,
"orientation": 0,
"movementMode": "MANUAL",
"speed": 50}
waypoint1 = {"x": 15,
"y": 4,
"tolerance": 4,
"movementMode": "_BEAMNG",
"speed": 40}
waypoint2 = {"x": 61,
"y": 4,
"tolerance": 5,
"movementMode": "AUTONOMOUS"}
waypoints = [waypoint1, waypoint2]
participant_id = "ego"
model = "ETK800"
dbc.add_car(init_state, waypoints, participant_id, model)

vc_pos = [participant_id, -4, 4, 4]
sc_speed = [participant_id, 15]
participant = {"init_state": init_state,
"waypoints": waypoints,
"model": model,
"id": participant_id}
dbc.add_car(participant=participant)

vc_pos = {"id": participant_id,
"x": waypoint1.get("x"),
"y": waypoint1.get("y"),
"tolerance": waypoint1.get("tolerance")}
sc_speed = 15
dbc.add_precond_partic_sc_speed(vc_pos, sc_speed)

success_point = [participant_id, 61, 4, 5]
dbc.add_success_point(success_point)
success_point = {"id": participant_id,
"x": waypoint2.get("x"),
"y": waypoint2.get("y"),
"tolerance": waypoint2.get("tolerance")}
dbc.add_success_point(participant_id=participant_id, success_point=success_point)

dbc.add_failure_conditions(participant_id, "offroad")

Expand Down
79 changes: 50 additions & 29 deletions utils/dbe_xml_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,48 +47,69 @@ def indent(self, elem, level=0):

def add_obstacles(self, obstacle_list):
"""Adds obstacles to the XML files.
:param obstacle_list: Array of obstacles. First value is the shape/name of the obstacles,
the other parameters are defining the obstacle.
:param obstacle_list: List of obstacles. Each obstacle is a dict and must contain x and y position. Check
generator.py in simnode in DriveBuild to see which object needs which properties.
:return: Void.
"""
obstacles = ElementTree.SubElement(self.root, "obstacles")
for obstacle in obstacle_list:
if obstacle[0] == "cube":
ElementTree.SubElement(obstacles, 'cube x="{}" y="{}" width="{}" length="{}"'
' height="{}"'
.format(obstacle[1], obstacle[2], obstacle[3], obstacle[4],
obstacle[5]))
elif obstacle[0] == "cylinder":
ElementTree.SubElement(obstacles, 'cylinder x="{}" y="{}" radius="{}" height="{}"'
.format(obstacle[1], obstacle[2], obstacle[3], obstacle[4]))
elif obstacle[0] == "cone":
ElementTree.SubElement(obstacles, 'cone x="{}" y="{}" height="{}" baseRadius="{}"'
.format(obstacle[1], obstacle[2], obstacle[3], obstacle[4]))
elif obstacle[0] == "bump":
ElementTree.SubElement(obstacles, 'bump x="{}" y="{}" width="{}" length="{}" height="{}"'
' upperLength="{}" upperWidth="{}"'
.format(obstacle[1], obstacle[2], obstacle[3], obstacle[4],
obstacle[5], obstacle[6], obstacle[7]))

def add_lane(self, segments, markings=True, left_lanes=0, right_lanes=0):
name = obstacle.get("name")
x = obstacle.get("x")
y = obstacle.get("y")
z = obstacle.get("z")
xRot = obstacle.get("xRot")
yRot = obstacle.get("yRot")
zRot = obstacle.get("zRot")
width = obstacle.get("width")
length = obstacle.get("length")
height = obstacle.get("height")
radius = obstacle.get("radius")
baseRadius = obstacle.get("baseRadius")
upperWidth = obstacle.get("upperWidth")
upperLength = obstacle.get("upperLength")
full_string = '' + name + ' x="' + str(x) + '" y="' + str(y) + '"'
if z:
full_string += ' z="' + str(z) + '"'
if xRot:
full_string += ' xRot="' + str(xRot) + '"'
if yRot:
full_string += ' yRot="' + str(yRot) + '"'
if zRot:
full_string += ' zRot="' + str(zRot) + '"'
if width:
full_string += ' width="' + str(width) + '"'
if length:
full_string += ' length="' + str(length) + '"'
if height:
full_string += ' height="' + str(height) + '"'
if radius:
full_string += ' radius="' + str(radius) + '"'
if baseRadius:
full_string += ' baseRadius="' + str(baseRadius) + '"'
if upperWidth:
full_string += ' upperWidth="' + str(upperWidth) + '"'
if upperLength:
full_string += ' upperLength="' + str(upperLength) + '"'
ElementTree.SubElement(obstacles, full_string)

def add_lane(self, segments, markings: bool = True, left_lanes: int = 0, right_lanes: int = 0):
"""Adds a lane and road segments.
:param segments: Array of tuples containing nodes to generate road segments. Segments must have
x-coordinate, y-coordinate and width.
:param segments: List of dicts containing x-coordinate, y-coordinate and width.
:param markings: {@code True} Enables road markings, {@code False} makes them invisible.
:param left_lanes: number of left lanes (int)
:param right_lanes: number of right lanes (int)
:param left_lanes: number of left lanes
:param right_lanes: number of right lanes
:return: Void
"""
lane = ElementTree.SubElement(self.lanes, "lane")
if markings:
lane.set("markings", "true")
if left_lanes != 0:
lane.set("leftLanes", left_lanes)
if right_lanes != 0:
lane.set("rightLanes", right_lanes)
if left_lanes != 0 and left_lanes is not None:
lane.set("leftLanes", str(left_lanes))
if right_lanes != 0 and right_lanes is not None:
lane.set("rightLanes", str(right_lanes))
for segment in segments:
ElementTree.SubElement(lane, 'laneSegment x="{}" y="{}" width="{}"'
.format(segment[0], segment[1], segment[2]))
.format(segment.get("x"), segment.get("y"), segment.get("width")))

def save_xml(self, name):
"""Creates and saves the XML file, and moves it to the scenario folder.
Expand Down
32 changes: 24 additions & 8 deletions utils/dbe_xml_examplebuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,32 @@

dbe = DBEBuilder()

segment1 = [0, 0, 8]
segment2 = [50, 0, 8]
segment3 = [80, 20, 8]
segment4 = [100, 20, 8]
segment1 = {"x": 0,
"y": 0,
"width": 12}
segment2 = {"x": 50,
"y": 0,
"width": 12}
segment3 = {"x": 80,
"y": 20,
"width": 12}
segment4 = {"x": 100,
"y": 20,
"width": 12}
segments = [segment1, segment2, segment3, segment4]

dbe.add_lane(segments)

cone = ["cone", 5, 5, 5, 5]
cylinder = ["cylinder", 10, 10, 2, 2]
dbe.add_lane(segments, left_lanes=1, right_lanes=2)

cone = {"name": "cone",
"x": 5,
"y": 5,
"baseRadius": 5,
"height": 5}
cylinder = {"name": "cylinder",
"x": 10,
"y": 10,
"radius": 2,
"height": 2}
obstacles = [cone, cylinder]

dbe.add_obstacles(obstacles)
Expand Down
19 changes: 11 additions & 8 deletions utils/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@

def plotter(control_points):
"""Plots every point and lines between them. Used to visualize a road.
:param control_points: List of points as tuples [(x, y),...]
:param control_points: List of points as dict type.
:return: Void.
"""
control_points = np.asarray(control_points)
x = control_points[:, 0]
y = control_points[:, 1]

plt.plot(x, y, '-og', markersize=10, linewidth=7)
point_list = []
for point in control_points:
point_list.append((point.get("x"), point.get("y")))
point_list = np.asarray(point_list)
x = point_list[:, 0]
y = point_list[:, 1]

plt.plot(x, y, '-og', markersize=10, linewidth=control_points[0].get("width"))
plt.xlim([min(x) - 0.3, max(x) + 0.3])
plt.ylim([min(y) - 0.3, max(y) + 0.3])

Expand All @@ -23,12 +26,12 @@ def plotter(control_points):

def plot_all(population):
"""Plots a whole population. Method starts a new figure for every individual.
:param population: Population in form of [[control_points, fitness_value],...]
:param population: Population with individuals in dict form containing another dict type called control_points.
:return: Void
"""
iterator = 0
while iterator < len(population):
plotter(population[iterator][0])
plotter(population[iterator].get("control_points"))
iterator += 1


Expand Down
16 changes: 14 additions & 2 deletions utils/utility_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

def convert_points_to_lines(control_points):
"""Turns a list of points into a list of LineStrings.
:param control_points: List of points
:return: List of LineStrings
:param control_points: List of dicts containing points.
:return: List of LineStrings.
"""
control_points_lines = []
iterator = 0
Expand All @@ -15,3 +15,15 @@ def convert_points_to_lines(control_points):
control_points_lines.append(line)
iterator += 1
return control_points_lines

"""def convert_points_to_lines(control_points):
control_points_lines = []
iterator = 0
while iterator < (len(control_points) - 1):
p1 = (control_points[iterator].get("x"), control_points[iterator].get("y"))
p2 = (control_points[iterator + 1].get("x"), control_points[iterator + 1].get("y"))
line = LineString([p1, p2])
control_points_lines.append(line)
iterator += 1
return control_points_lines
"""
Loading

0 comments on commit c66234d

Please sign in to comment.