OpenROAD to Lanelet2 Conversion error

I have a couple of problems when trying to use crdesigner that I was hoping to get your guidance on.

I’m having issues with converting maps from OpenDRIVE to Lanelet2.
I have installed crdesigner and commoroad_dc using pip.

When loading crdesigner in my conda environment that is python 3.10 based, I get this warning:

/home/sofya/.local/lib/python3.10/site-packages/crdesigner/map_conversion/sumo_map/cr2sumo/converter.py:34: UserWarning: Unable to import commonroad_dc.pycrccosy, converting static scenario into interactive is not supported!
  warnings.warn(

I believe this might be a sumo error, but I have also seen that I need commonroad_dc to resolve this warning. I have followed the tutorial and installed both packages. The warning still persists. When I convert OpenDRIVE map to CommonRoad, I get this message:

QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
/home/sofya/.local/lib/python3.10/site-packages/shapely/linear.py:90: RuntimeWarning: invalid value encountered in line_locate_point
  return lib.line_locate_point(line, other)
22-Jun-23 10:37:36 - INFO - update_max_step
22-Jun-23 10:37:36 - INFO - update_max_step
Conversion Ended
22-Jun-23 10:37:37 - INFO - update_max_step
22-Jun-23 10:37:37 - INFO - update_max_step

When converting from CommonRoad to Lanelet2, I get no messages.

The map seemed to have been transferred correctly into gui, but I’m suspecting that the conversion from CommonRoad to Lanelet2 is what is not working quite right.

Furthermore, when I do convert using crdesigner, I use TIER IV Account to check the map. I get error that map exists over multiple grid zones which is not true. That is not the case with regular Lanelet2 maps that were not converted.

I also created a python code from provided samples in CommonRoad.

# General Imports
import os
from lxml import etree

from commonroad.scenario.scenario import Tag
from commonroad.common.file_writer import CommonRoadFileWriter, OverwriteExistingFile
from commonroad.planning.planning_problem import PlanningProblemSet
from commonroad.common.file_reader import CommonRoadFileReader

#Chekcing dir
import os
print (os.getcwd())




#All code copied from https://commonroad.in.tum.de/tutorials/map-conversion


#FOR THE OpenDRIVE to Lanelet2
#input_path = "/home/sofya/Documents/OpenDrivetoCommonConverter"  # replace empty string
name = "urban"
input_path = name+".xodr"  #maybe sees from the terminal's dir?
output_path = name + ".osm"

#General API (OpenDRIVE to Common Road)

from crdesigner.map_conversion.map_conversion_interface import opendrive_to_commonroad

# load OpenDRIVE file, parse it, and convert it to a CommonRoad scenario
scenario = opendrive_to_commonroad(input_path)

# store converted file as CommonRoad scenario
writer = CommonRoadFileWriter(
    scenario=scenario,
    planning_problem_set=PlanningProblemSet(),
    author="Sebastian Maierhofer",
    affiliation="Technical University of Munich",
    source="CommonRoad Scenario Designer",
    tags={Tag.URBAN},
)
writer.write_to_file(os.path.dirname(os.path.realpath(__file__)) + "/" + "ZAM_OpenDRIVETest-1_1-T1.xml",
                     OverwriteExistingFile.ALWAYS)



#CommonRoad to Lanelet

input_path = "ZAM_OpenDRIVETest-1_1-T1.xml"  # replace empty string
proj = "proj=utm +zone=32 +ellps=WGS84"  # replace empty string



from crdesigner.map_conversion.map_conversion_interface import commonroad_to_lanelet

# load CommonRoad file and convert it to lanelet format
commonroad_to_lanelet(input_path, output_path, proj)

When I run it, I get this error

/home/sofya/Documents/hdmap_conversions
/home/sofya/.local/lib/python3.10/site-packages/crdesigner/map_conversion/opendrive/opendrive_parser/elements/roadLanes.py:607: UserWarning: Parser could not find value for road_mark.SOffset, 0 is used per default.
  warnings.warn("Parser could not find value for road_mark.SOffset, 0 is used per default.")
Replace file /home/sofya/Documents/hdmap_conversions/ZAM_OpenDRIVETest-1_1-T1.xml
Traceback (most recent call last):
  File "/home/sofya/Documents/hdmap_conversions/opendrive_to_lanelet.py", line 57, in <module>
    commonroad_to_lanelet(input_path, output_path, proj)
  File "/home/sofya/.local/lib/python3.10/site-packages/crdesigner/map_conversion/map_conversion_interface.py", line 77, in commonroad_to_lanelet
    osm = l2osm(scenario)
  File "/home/sofya/.local/lib/python3.10/site-packages/crdesigner/map_conversion/lanelet2/cr2lanelet.py", line 133, in __call__
    self._convert_lanelet(lanelet)
  File "/home/sofya/.local/lib/python3.10/site-packages/crdesigner/map_conversion/lanelet2/cr2lanelet.py", line 398, in _convert_lanelet
    left_nodes, right_nodes = self._create_nodes(lanelet, left_way_id, right_way_id)
  File "/home/sofya/.local/lib/python3.10/site-packages/crdesigner/map_conversion/lanelet2/cr2lanelet.py", line 467, in _create_nodes
    left_nodes = self._create_nodes_from_vertices(lanelet.left_vertices[start_index:end_index])
  File "/home/sofya/.local/lib/python3.10/site-packages/crdesigner/map_conversion/lanelet2/cr2lanelet.py", line 513, in _create_nodes_from_vertices
    if self._config.use_local_coordinates:
AttributeError: 'str' object has no attribute 'use_local_coordinates'

I would prefer to use python script for the conversions, however, I can’t get either of the methods to work. Would someone be able to help me with this?

Furthermore, what tools do you guys use to check whether the map conversion is correct?

Also, my original OpenDRIVE map is an xodr export from RoadNetwork_Urban from dSPACE.

Hi Sofya,

which version of the scenario-designer do you use?

Regarding the checking of the map.
We have a logic-based map verification tool.
However, the tool is currently not integrated in the scenario-designer.

Best,
Sebastian

1 Like

Hey Sebastian,

It’s v0.7.1

With v0.7.1 the APIs have changed slightly, but the tutorials on the website are not updated yet.
We will do this as soon as possible.

However, updated tutorials can be found here.

Sorry for the inconvenience!

1 Like

Hey Sebastian!

Thank you for giving me the tutorial!
I have implemented it, and I still get these warnings.

/home/sofya/.local/lib/python3.8/site-packages/crdesigner/map_conversion/sumo_map/cr2sumo/converter.py:34: UserWarning: Unable to import commonroad_dc.pycrccosy, converting static scenario into interactive is not supported!
  warnings.warn(
27-Jun-23 08:09:32 - WARNING - Cannot import SUMO, simulation will not be offered in Scenario Designer
/home/sofya/.local/lib/python3.8/site-packages/crdesigner/map_conversion/opendrive/opendrive_parser/elements/roadLanes.py:607: UserWarning: Parser could not find value for road_mark.SOffset, 0 is used per default.
  warnings.warn("Parser could not find value for road_mark.SOffset, 0 is used per default.")

When I import the converted lanelet2 map into autoware, I’m getting this error multiple times.

Could not find valid vertex for ear clipping triangulation. Triangulation result might be invalid

Furthermore, Tier4 (TIER IV Account) still tells me that it’s over multiple grid-zones

When looking up this issue, it seems to be due to the fundamental difference between OpenDRIVE and Lanelet2. They define the geometry for merging roads differently. From your paper, https://www.researchgate.net/publication/328458337_Automatic_Conversion_of_Road_Networks_from_OpenDRIVE_to_Lanelets, I thought that the software should be able to convert this geometry.

This is the python code I used.


#Chekcing dir
import os
print (os.getcwd())


name = "urabntry1"
input_path = name+".xodr"  #maybe sees from the terminal's dir?
cr_path = "ZAM_OpenDRIVETest-1_1-T1.xml"
output_name = name + ".osm"


#All code copied from https://gitlab.lrz.de/tum-cps/commonroad-scenario-designer/-/blob/main/tutorials/conversion_examples/


#FOR THE OpenDRIVE to CommonRoad

#input_path = "/home/sofya/Documents/OpenDrivetoCommonConverter"  # replace empty string


import os
from commonroad.scenario.scenario import Tag
from commonroad.common.file_writer import CommonRoadFileWriter, OverwriteExistingFile
from commonroad.planning.planning_problem import PlanningProblemSet
from crdesigner.map_conversion.opendrive.opendrive_parser.parser import parse_opendrive
from crdesigner.map_conversion.opendrive.opendrive_conversion.network import Network
from crdesigner.map_conversion.map_conversion_interface import opendrive_to_commonroad
from crdesigner.config.config import OpenDRIVEConversionParams


config = OpenDRIVEConversionParams()
config.lanelet_types_backwards_compatible = False

# ----------------------------------------------- Option 1: General API ------------------------------------------------
# load OpenDRIVE file, parse it, and convert it to a CommonRoad scenario

scenario = opendrive_to_commonroad(input_path)




#FOR THE COMMONROAD TO LANELET2
from lxml import etree
from commonroad.common.file_reader import CommonRoadFileReader
from crdesigner.map_conversion.lanelet2.cr2lanelet import CR2LaneletConverter
from crdesigner.map_conversion.map_conversion_interface import commonroad_to_lanelet
from crdesigner.config.config import Lanelet2ConversionParams

config = Lanelet2ConversionParams()
config.autoware = False

# ----------------------------------------------- Option 1: General API ------------------------------------------------
# load CommonRoad file and convert it to lanelet format
commonroad_to_lanelet(cr_path, output_name, config)

# ---------------------------------------- Option 2: Lanelet conversion APIs -------------------------------------------
try:
    commonroad_reader = CommonRoadFileReader(cr_path)
    scenario, _ = commonroad_reader.open()
except etree.XMLSyntaxError as xml_error:
    print(f"SyntaxError: {xml_error}")
    print(
        "There was an error during the loading of the selected CommonRoad file.\n"
    )
    scenario = None

if scenario:
    l2osm = CR2LaneletConverter(config)
    osm = l2osm(scenario)
    with open(f"{output_name}", "wb") as file_out:
        file_out.write(
            etree.tostring(
                osm, xml_declaration=True, encoding="UTF-8", pretty_print=True
            )
        )



I couldn’t attach the xodr file I’m trying to convert, but I can email it to you if you would like. It’s an example of urban road from dSPACE, so I’m expecting the formating of it to be up to date.

Could you let me know if there is something wrong with the code that I used, or whether the software can’t convert the merging correctly. Thank you for your help! :slight_smile:

In general, the code looks correct.
If you want to use the lanelet2 map with autoware, I would recommend to activate the corresponding flag since autoware requires an elevation:

config.autoware = True

Regarding the “multiple grid-zones”: I’m not sure whether this error indicates that the map covers several UTM zones. As far as I know, autoware does not support several zones.

However, it could also be a conversion bug.
Can you send me the map via e-mail?

1 Like

I tried to load your map and some of our converted maps into the Tier4 map builder.
For some of our maps it was possible (e.g., this map).

Additionally, I visualized one of our converted real-world maps on top of an aerial image, and it looked fine.

Therefore, the general lanelet2 conversion should be correct.
I think it is a special requirement from Tier4.

Could you ask Tier4 what the concrete problem is?
Then, we could update our converter so that this problem does not occur anymore.

1 Like

Hey Sebastian,

Thank you for looking into this. This is very odd as I also have errors when I load it into autoware ( Could not find valid vertex for ear clipping triangulation. Triangulation result might be invalid), which made me think that it’s a map issue. I have loaded the map you have attached and it has worked for me in autoware as well. Would it be possible for you to send me the conversion of the map I sent you to see if it’s something wrong with my set-up?

I’m just using the following code:

from crdesigner.map_conversion.map_conversion_interface import commonroad_to_lanelet
from crdesigner.config.config import Lanelet2ConversionParams


input_path = "/path/to/map/urban-1_lanelets_utm.xml"
output_name = "/output/path/urban-1_lanelets_utm_from_cr.osm"
config = Lanelet2ConversionParams()
config.autoware = True

commonroad_to_lanelet(input_path, output_name, config)

For some maps, I also have the problem you described.

Hello Sebastian,

Sorry for miscommunication. When you load your map into Tier4, it also tells you that it “exists over multiple grid-zones”?

Just want to make sure I understand this problem before I reach out to Tier4.

yes, I receive the same error message.

1 Like

I have noticed that this is something that might be due to the fact that when you go from CommonRoad to Lanelet2, the long and lat of CommonRoad file does not get translated to Lanelet2. Because it seems that Lanelet2 is based on origin, it is crossing a boundary.

CommonRoad:

<location>
<geoNameId>54902</geoNameId>
<gpsLatitude>43.78786</gpsLatitude>
<gpsLongitude>-88.86428</gpsLongitude>

Lanelet2:

<?xml version='1.0' encoding='UTF-8'?>
<osm version="0.6" upload="true" generator="commonroad-scenario-designer">
  <node id="1" action="modify" visible="true" version="1" lat="-0.00010779873240956308" lon="0.0010240785255809703"/>
  <node id="2" action="modify" visible="true" version="1" lat="-0.00010779873240956308" lon="0.0010240785255809703"/>
  <node id="3" action="modify" visible="true" version="1" lat="-0.00010779873240956308" lon="0.0010240785255809703"/>
  <node id="4" action="modify" visible="true" version="1" lat="-0.00010354610785454851" lon="0.001023948269864773"/>
  <node id="5" action="modify" visible="true" version="1" lat="-9.928180520083983e-05" lon="0.0010235619942926017"/>
  <node id="6" action="modify" visible="true" version="1" lat="-9.500492613315294e-05" lon="0.0010229286820172972"/>

I have OSM maps with proper lon/lat coordinates where I also get FileFormatError: Map exists over multiple grid zones. I can load the map in JOSM and activate an aerial image and everything looks good.

I have made a python parser that adds the lat and long offset to the existing file (lat = 1 and long = 1). That has solved that problem, so that now the map gets displayed. However, then I got this error:

Next Lanelet is Empty.

I reached out to the Tier4 developers about this, asking what in the Lanelet2 text would cause this. This was their response:

The reason is that the linestrings of the lanelet is not connected to the next lanelet. 

You need to select the linestring and change the end point ID to the same number as the start point ID of the next linestring. (e.g. 1-2 2-3 3-4)

I looked into my file and I found that it’s true. Those points should all be one point. All of the connected linestrings are ending at about the same location but all have different ID.

  <node id="7108" action="modify" visible="true" version="1" lat="1.0000359326113648" lon="1.0011318772579907"/>
  <node id="1019" action="modify" visible="true" version="1" lat="1.0000359326113648" lon="1.0011318772579907"/>
<node id="213" action="modify" visible="true" version="1" lat="1.0000359326113648" lon="1.0011318772579907"/>


This is the python based code that I used to fix the points. After running this code on the osm files after the CommonRoad conversions, it worked in Tier4 and in the simulation. This has solved my problem.

#Edit path and name here
name = 'your_openDRIVE_file.osm'
#The location of the original file
file_read = '/home/sofya/tum_commonroad/commonroad-scenario-designer/maps/osm/'+name
#The location of where you want the new file written
file_write = '/home/sofya/tum_commonroad/commonroad-scenario-designer/maps/shifted-osm/'+name
#Note: the name that is used to read and write is the same, so if you select the same directory, it will overwrite the original file
#To  fix it, change the name in file_write to something else


#Your lat and long offset. You can keep it a 0 if you don't want to shift
long_offset = 1
lat_offset = 1

#some variables
Node_Dict = {} #   Dict =  lat long (abbriviated) : [ lat long (full), Note IDs]
is_nd = False
prev_line = ''

#Opening the reading file
with open(file_read) as file_r:
    #Iterating through each line
    for line in file_r:
        new_line = ''
        current_line = line.split() #splitting line into words
        

        #Checking to see whether it is a new node on the line
        if current_line[0] == "<node":

            #Appending or making a new dictionary entry            
            dict_value = current_line[5] + " "+ current_line[6] #lat="xxxx" long="xxxx"
            dict_key= str(round(float(current_line[5][5:-1]), 6)) + " "+ str(round(float(current_line[6][5:-3]),6)) #getting just the numbers or the xxxx from above

            #Check if abriviated lat and long is in the dictionary
            if dict_key in Node_Dict.keys(): #If yes, append new ID with same long and lat into the Dictionary
                Node_Dict[dict_key].append(current_line[1][4:-1])
                continue
            else:
                Node_Dict[dict_key] =  [dict_value, current_line[1][4:-1]] #If not, make a new entry
        

        #Iterating through each word in a line
        for word in line.split():

            #substituing for the new id
            if is_nd:
                is_nd = False
                id_num = current_line[1][5:-3] #get current id's number
                which_id_to_use = id_num #which ID to overwrite it with. If the next if doesn't get triggered, the ID stays the same
                for other_value in Node_Dict:
                    if id_num in Node_Dict[other_value]:
                        which_id_to_use = Node_Dict[other_value][1] #Use the first value that has the same location
                        word = 'ref="'+str(which_id_to_use)+'"/>'
                        #print(word)
                        break
                    
                
        
            #Founding out how many spaces needed before each line for formating
            if word[0] == '<':
                if word == "<node" or word == "<way"or word == "</way>" or word == "<relation" or word == "</relation>" :
                    word = '  '+word
                elif word == "<nd" or word == "<member" or word == "<tag":
                    if word == "<nd":
                        is_nd = True
                    word = '  '+'  '+word
            #If none of the starting words, then it's just space before the next word  
            else:
                new_line =new_line+' '

            #Picking appart the lat and long incase it needs to be shifted
            if word[0] == 'l' and word[1] == 'a' and word[2] == 't':
                word = 'lat="'+ str(float(word[5:-1]) + lat_offset) +'"'
            elif word[0] == 'l' and word[1] == 'o' and word[2] == 'n':
                word = 'lon="'+ str(float(word[5:-3]) + long_offset)+'"/>'

            #Adding the edited word to the new line
            new_line = new_line + word

        #if the line is the same as previous, we can skip it. Mostly used to not write reduntant points with same lat and long. 
        if prev_line==new_line:
            continue
        #Writing to the new file
        with open(file_write, 'a') as file_w:
            file_w.write(new_line +'\n')

        #updating the previous line
        prev_line = new_line




Thank you for the script!

I found the reason of the “multiple grid-zones” problem (at least for our maps).
It was related to a bug in the stop line projection.
This is fixed with the latest release (v0.7.2) of the scenario-designer.

Regarding the problem with the points.
We have a tool which automatically repairs maps.
We are currently working on the integration into the scenario-designer.
This should be finished latest end of September.

1 Like

Could you please share the tool which automatically repairs maps you mentioned before?

I met the same problem when I conversing Opendrive data to lanelet2.

Origin Map: Carla Town10HD.xodr
crlanelet2 command: crdesigner --input-file Town10.xml --output-file Town10.osm crlanelet2 --proj “+proj=utm +zone=31 +datum=WGS84 +ellps=WGS84” --autoware