Module scenariogeneration.xodr.lane_def

scenariogeneration https://github.com/pyoscx/scenariogeneration

This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

Copyright (c) 2022 The scenariogeneration Authors.

This is a collection methods and classes not related to OpenDRIVE, but relates to automation of lane creations

Functions

def create_lanes_merge_split(right_lane_def,
left_lane_def,
road_length,
center_road_mark,
lane_width,
lane_width_end)
Expand source code
def create_lanes_merge_split(
    right_lane_def,
    left_lane_def,
    road_length,
    center_road_mark,
    lane_width,
    lane_width_end,
):
    """create_lanes_merge_split is a generator that will create the Lanes of a road road that can contain one or more lane merges/splits
    This is a simple implementation and has some constraints:
     - left and right merges has to be at the same place (or one per lane), TODO: will be fixed with the singleSide attribute later on.
     - the change will be a 3 degree polynomial with the derivative 0 on both start and end.

    Please note that the merges/splits are defined in the road direction, NOT the driving direction.

    Parameters
    ----------
        right_lane_def (list of LaneDef, or an int): a list of the splits/merges that are wanted on the right side of the road, if int constant number of lanes

        left_lane_def (list of LaneDef, or an int): a list of the splits/merges that are wanted on the left side of the road, if int constant number of lanes.

        road_length (float): the full length of the road

        center_road_mark (RoadMark): roadmark for the center line

        lane_width (float): the width of the lanes

        lane_width_end (float): the end width of the lanes

    Return
    ------
        road (Lanes): the lanes of a road
    """

    lanesections = []
    # expand the lane list
    right_lane, left_lane = _create_lane_lists(
        right_lane_def, left_lane_def, road_length, lane_width
    )

    # create the lanesections needed
    for ls in range(len(left_lane)):
        lc = Lane(a=0)
        lc.add_roadmark(copy.deepcopy(center_road_mark))
        lsec = LaneSection(left_lane[ls].s_start, lc)
        # do the right lanes
        for i in range(max(right_lane[ls].n_lanes_start, right_lane[ls].n_lanes_end)):
            # add broken roadmarks for all lanes, except for the outer lane where a solid line is added
            if i == max(right_lane[ls].n_lanes_start, right_lane[ls].n_lanes_end) - 1:
                rm = std_roadmark_solid()
            else:
                rm = std_roadmark_broken()

            # check if the number of lanes should change or not
            if (
                right_lane[ls].n_lanes_start > right_lane[ls].n_lanes_end
                and i == np.abs(right_lane[ls].sub_lane) - 1
            ):
                # lane merge
                coeff = get_coeffs_for_poly3(
                    right_lane[ls].s_end - right_lane[ls].s_start,
                    right_lane[ls].lane_start_widths[i],
                    False,
                    right_lane[ls].lane_end_widths[i],
                )
                rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
                rightlane.add_roadmark(rm)
            elif (
                right_lane[ls].n_lanes_start < right_lane[ls].n_lanes_end
                and i == np.abs(right_lane[ls].sub_lane) - 1
            ):
                # lane split
                coeff = get_coeffs_for_poly3(
                    right_lane[ls].s_end - right_lane[ls].s_start,
                    right_lane[ls].lane_start_widths[i],
                    True,
                    right_lane[ls].lane_end_widths[i],
                )
                rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
                rightlane.add_roadmark(rm)
            elif (lane_width_end is not None) and (lane_width != lane_width_end):
                coeff = get_coeffs_for_poly3(
                    right_lane[ls].s_end - right_lane[ls].s_start,
                    lane_width,
                    False,
                    lane_width_end=lane_width_end,
                )
                rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
                rightlane.add_roadmark(rm)
            elif right_lane[ls].lane_start_widths:
                coeff = get_coeffs_for_poly3(
                    right_lane[ls].s_end - right_lane[ls].s_start,
                    right_lane[ls].lane_start_widths[i],
                    False,
                    lane_width_end=right_lane[ls].lane_end_widths[i],
                )
                rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
                rightlane.add_roadmark(rm)
            else:
                rightlane = Lane(lane_width)
                rightlane.add_roadmark(rm)
            lsec.add_right_lane(rightlane)

        # do the left lanes
        for i in range(max(left_lane[ls].n_lanes_start, left_lane[ls].n_lanes_end)):
            # add broken roadmarks for all lanes, except for the outer lane where a solid line is added
            if i == max(left_lane[ls].n_lanes_start, left_lane[ls].n_lanes_end) - 1:
                rm = std_roadmark_solid()
            else:
                rm = std_roadmark_broken()

            # check if the number of lanes should change or not
            if (
                left_lane[ls].n_lanes_start < left_lane[ls].n_lanes_end
                and i == left_lane[ls].sub_lane - 1
            ):
                # lane split
                coeff = get_coeffs_for_poly3(
                    left_lane[ls].s_end - left_lane[ls].s_start,
                    left_lane[ls].lane_start_widths[i],
                    True,
                    left_lane[ls].lane_end_widths[i],
                )
                leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
                leftlane.add_roadmark(rm)
            elif (
                left_lane[ls].n_lanes_start > left_lane[ls].n_lanes_end
                and i == left_lane[ls].sub_lane - 1
            ):
                # lane merge
                coeff = get_coeffs_for_poly3(
                    left_lane[ls].s_end - left_lane[ls].s_start,
                    left_lane[ls].lane_start_widths[i],
                    False,
                    left_lane[ls].lane_end_widths[i],
                )
                leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
                leftlane.add_roadmark(rm)
            elif (lane_width_end is not None) and (lane_width != lane_width_end):
                coeff = get_coeffs_for_poly3(
                    left_lane[ls].s_end - left_lane[ls].s_start,
                    lane_width,
                    False,
                    lane_width_end=lane_width_end,
                )
                leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
                leftlane.add_roadmark(rm)
            elif left_lane[ls].lane_start_widths:
                coeff = get_coeffs_for_poly3(
                    left_lane[ls].s_end - left_lane[ls].s_start,
                    left_lane[ls].lane_start_widths[i],
                    False,
                    lane_width_end=left_lane[ls].lane_end_widths[i],
                )
                leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
                leftlane.add_roadmark(rm)
            else:
                leftlane = Lane(lane_width)
                leftlane.add_roadmark(rm)
            lsec.add_left_lane(leftlane)

        lanesections.append(lsec)

    # create the lane linker to link the lanes correctly
    lanelinker = LaneLinker()
    for i in range(1, len(right_lane)):
        if right_lane[i].n_lanes_end > right_lane[i].n_lanes_start:
            # lane split
            for j in range(0, right_lane[i - 1].n_lanes_end + 1):
                # adjust for the new lane
                if right_lane[i].sub_lane < -(j + 1):
                    lanelinker.add_link(
                        lanesections[i - 1].rightlanes[j], lanesections[i].rightlanes[j]
                    )
                elif right_lane[i].sub_lane > -(j + 1):
                    lanelinker.add_link(
                        lanesections[i - 1].rightlanes[j - 1],
                        lanesections[i].rightlanes[j],
                    )
        elif right_lane[i - 1].n_lanes_end < right_lane[i - 1].n_lanes_start:
            # lane merge
            for j in range(0, right_lane[i - 1].n_lanes_end + 1):
                # adjust for the lost lane
                if right_lane[i - 1].sub_lane < -(j + 1):
                    lanelinker.add_link(
                        lanesections[i - 1].rightlanes[j], lanesections[i].rightlanes[j]
                    )
                elif right_lane[i - 1].sub_lane > -(j + 1):
                    lanelinker.add_link(
                        lanesections[i - 1].rightlanes[j],
                        lanesections[i].rightlanes[j - 1],
                    )

        else:
            # same number of lanes, just add the links
            for j in range(right_lane[i - 1].n_lanes_end):
                lanelinker.add_link(
                    lanesections[i - 1].rightlanes[j], lanesections[i].rightlanes[j]
                )

    for i in range(1, len(left_lane)):
        if left_lane[i].n_lanes_end > left_lane[i].n_lanes_start:
            # lane split
            for j in range(0, left_lane[i - 1].n_lanes_end + 1):
                # adjust for the new lane
                if left_lane[i].sub_lane < (j + 1):
                    lanelinker.add_link(
                        lanesections[i - 1].leftlanes[j - 1],
                        lanesections[i].leftlanes[j],
                    )
                elif left_lane[i].sub_lane > (j + 1):
                    lanelinker.add_link(
                        lanesections[i - 1].leftlanes[j], lanesections[i].leftlanes[j]
                    )
        elif left_lane[i - 1].n_lanes_end < left_lane[i - 1].n_lanes_start:
            # lane merge
            for j in range(0, left_lane[i - 1].n_lanes_end + 1):
                # adjust for the lost lane
                if left_lane[i - 1].sub_lane < (j + 1):
                    lanelinker.add_link(
                        lanesections[i - 1].leftlanes[j],
                        lanesections[i].leftlanes[j - 1],
                    )
                elif left_lane[i - 1].sub_lane > (j + 1):
                    lanelinker.add_link(
                        lanesections[i - 1].leftlanes[j], lanesections[i].leftlanes[j]
                    )

        else:
            # same number of lanes, just add the links
            for j in range(left_lane[i - 1].n_lanes_end):
                lanelinker.add_link(
                    lanesections[i - 1].leftlanes[j], lanesections[i].leftlanes[j]
                )

    # Add the lanesections to the lanes struct together the lanelinker
    lanes = Lanes()
    for ls in lanesections:
        lanes.add_lanesection(ls, lanelinker)
    return lanes

create_lanes_merge_split is a generator that will create the Lanes of a road road that can contain one or more lane merges/splits This is a simple implementation and has some constraints: - left and right merges has to be at the same place (or one per lane), TODO: will be fixed with the singleSide attribute later on. - the change will be a 3 degree polynomial with the derivative 0 on both start and end.

Please note that the merges/splits are defined in the road direction, NOT the driving direction.

Parameters

right_lane_def (list of LaneDef, or an int): a list of the splits/merges that are wanted on the right side of the road, if int constant number of lanes

left_lane_def (list of LaneDef, or an int): a list of the splits/merges that are wanted on the left side of the road, if int constant number of lanes.

road_length (float): the full length of the road

center_road_mark (RoadMark): roadmark for the center line

lane_width (float): the width of the lanes

lane_width_end (float): the end width of the lanes

Return

road (Lanes): the lanes of a road
def std_roadmark_broken()
Expand source code
def std_roadmark_broken():
    roadmark = RoadMark(RoadMarkType.broken, 0.2)
    roadmark.add_specific_road_line(RoadLine(0.15, 3, 9, 0, 0))
    return roadmark
def std_roadmark_broken_broken()
Expand source code
def std_roadmark_broken_broken():
    roadmark = RoadMark(RoadMarkType.broken_broken)
    roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, 0.2, 0))
    roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, -0.2, 0))
    return roadmark
def std_roadmark_broken_long_line()
Expand source code
def std_roadmark_broken_long_line():
    roadmark = RoadMark(RoadMarkType.broken, 0.2)
    roadmark.add_specific_road_line(RoadLine(0.15, 9, 3, 0, 0))
    return roadmark
def std_roadmark_broken_solid()
Expand source code
def std_roadmark_broken_solid():
    roadmark = RoadMark(RoadMarkType.broken_solid)
    roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, -0.2, 0))
    roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, 0.2, 0))
    return roadmark
def std_roadmark_broken_tight()
Expand source code
def std_roadmark_broken_tight():
    roadmark = RoadMark(RoadMarkType.broken, 0.2)
    roadmark.add_specific_road_line(RoadLine(0.15, 3, 3, 0, 0))
    return roadmark
def std_roadmark_solid()
Expand source code
def std_roadmark_solid():
    return RoadMark(RoadMarkType.solid, 0.2)
def std_roadmark_solid_broken()
Expand source code
def std_roadmark_solid_broken():
    roadmark = RoadMark(RoadMarkType.solid_broken)
    roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, 0.2, 0))
    roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, -0.2, 0))
    return roadmark
def std_roadmark_solid_solid()
Expand source code
def std_roadmark_solid_solid():
    roadmark = RoadMark(RoadMarkType.solid_solid)
    roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, 0.2, 0))
    roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, -0.2, 0))
    return roadmark

Classes

class LaneDef (s_start,
s_end,
n_lanes_start,
n_lanes_end,
sub_lane=None,
lane_start_widths=[],
lane_end_widths=[])
Expand source code
class LaneDef:
    """LaneDef is used to help create a lane merge or split. Can handle one lane merging or spliting.

    NOTE: This is not part of the OpenDRIVE standard, but a helper for the xodr module.

    Parameters
    ----------
        s_start (float): s coordinate of the start of the change

        s_end (float): s coordinate of the end of the change

        n_lanes_start (int): number of lanes at s_start

        n_lanes_end (int): number of lanes at s_end

        sub_lane (int): the lane that should be created (split) or removed (merge)

        lane_start_widths (list of float): widths of lanes at start, must be [] or same length as n_lanes_start
            Default: []

        lane_end_widths (list of float): widths of lanes at end, must be [] or same length as n_lanes_end
            Default: same as lane_start_widths

    Attributes
    ----------
        s_start (float): s coordinate of the start of the change

        s_end (float): s coordinate of the end of the change

        n_lanes_start (int): number of lanes at s_start

        n_lanes_end (int): number of lanes at s_end

        sub_lane (int): the lane that should be created (split) or removed (merge)

        lane_start_widths (list of float): widths of lanes at start, must be [] or same length as n_lanes_start

        lane_end_widths (list of float): widths of lanes at end, must be [] or same length as n_lanes_end
    """

    def __init__(
        self,
        s_start,
        s_end,
        n_lanes_start,
        n_lanes_end,
        sub_lane=None,
        lane_start_widths=[],
        lane_end_widths=[],
    ):
        self.s_start = s_start
        self.s_end = s_end
        self.n_lanes_start = n_lanes_start
        self.n_lanes_end = n_lanes_end
        self.sub_lane = sub_lane
        self.lane_start_widths = lane_start_widths
        if lane_end_widths == []:
            self.lane_end_widths = self.lane_start_widths.copy()
        else:
            self.lane_end_widths = lane_end_widths

    def _adjust_lane_widths(self):
        if self.sub_lane:
            if self.lane_end_widths and len(self.lane_end_widths) < self.n_lanes_start:
                # mergeo
                self.lane_end_widths.insert(abs(self.sub_lane) - 1, 0)
            elif (
                self.lane_start_widths
                and len(self.lane_start_widths) < self.n_lanes_end
            ):
                # split
                self.lane_start_widths.insert(abs(self.sub_lane) - 1, 0)
        # TODO: add some checks here?

LaneDef is used to help create a lane merge or split. Can handle one lane merging or spliting.

NOTE: This is not part of the OpenDRIVE standard, but a helper for the xodr module.

Parameters

s_start (float): s coordinate of the start of the change

s_end (float): s coordinate of the end of the change

n_lanes_start (int): number of lanes at s_start

n_lanes_end (int): number of lanes at s_end

sub_lane (int): the lane that should be created (split) or removed (merge)

lane_start_widths (list of float): widths of lanes at start, must be [] or same length as n_lanes_start
    Default: []

lane_end_widths (list of float): widths of lanes at end, must be [] or same length as n_lanes_end
    Default: same as lane_start_widths

Attributes

s_start (float): s coordinate of the start of the change

s_end (float): s coordinate of the end of the change

n_lanes_start (int): number of lanes at s_start

n_lanes_end (int): number of lanes at s_end

sub_lane (int): the lane that should be created (split) or removed (merge)

lane_start_widths (list of float): widths of lanes at start, must be [] or same length as n_lanes_start

lane_end_widths (list of float): widths of lanes at end, must be [] or same length as n_lanes_end