carla icon indicating copy to clipboard operation
carla copied to clipboard

The topology generated from Map is not usable with networkx

Open RaviBeagle opened this issue 3 years ago • 1 comments

CARLA version: 0.9.12 Platform/OS: Ubunut 20.04

I the documentation: https://carla.readthedocs.io/en/latest/python_api/#methods_28 says that: get_topology(self) Returns a list of tuples describing a minimal graph of the topology of the OpenDRIVE file. The tuples contain pairs of waypoints located either at the point a road begins or ends. The first one is the origin and the second one represents another road end that can be reached. This graph can be loaded into NetworkX to work with. Output could look like this: [(w0, w1), (w0, w2), (w1, w3), (w2, w3), (w0, w4)].

Return: list(tuple([carla.Waypoint](https://carla.readthedocs.io/en/latest/python_api/#carla.Waypoint), [carla.Waypoint](https://carla.readthedocs.io/en/latest/python_api/#carla.Waypoint)))

But the following code is not working:

        print("Drawing topology over the map..")
        topology = tmp_map.get_topology()
        graph = nx.Graph()
        graph.add_edges_from(topology,object=id)
        print("Topology Pairs: ", len(topology))
        print("Node: ",graph.number_of_nodes(),graph.nodes)
        print("Edges: ",graph.number_of_edges(),graph.edges)

Topology Pairs: 503 Node: 1006 Edges: 503

This is because the eq and ne Dunder methods are not provided which will compare the "id" attribute of to see if the nodes are same.

RaviBeagle avatar Sep 21 '22 07:09 RaviBeagle

I have made some changes to get it to work with networkx.

class WaypointTransform:
    def __init__(self,id,transform: carla.Transform):
         self.id = id
         self.transform = transform
    def __eq__(self,other) -> bool:
        return self.id == other.id
    
    def __ne__(self,other) -> bool:
        return self.id != other.id

    def __hash__(self) -> int:
        return self.id

    def __str__(self) -> str:
        return str(self.id) #+ ":" + str(self.transform)
...
        for index in range(len(topology)):
            #print(index, topology[index])
            start_waypoint = topology[index][0]
            end_waypoint = topology[index][1]
            topology_connected.append((WaypointTransform(start_waypoint.id,start_waypoint.transform),WaypointTransform(end_waypoint.id,end_waypoint.transform)))

But there is no Eulerian Circuit in the Graph:

        graph = nx.Graph()
        graph.add_edges_from(topology_connected)
        print("Topology Pairs: ", len(topology_connected))
        print("Node: ",graph.number_of_nodes(), graph.nodes)
        print("Edges: ",graph.number_of_edges(),graph.edges)
        if nx.is_eulerian(graph):
            print([u for u, v in nx.eulerian_circuit(graph)])
        else:
            print(graph,"is NOT Eulerian?")
            nx.write_graphml_lxml(graph,"topology_connected.graphml")

RaviBeagle avatar Sep 21 '22 11:09 RaviBeagle

Hey @RaviBeagle, not sure if it helps, as I'm not really following what you mean about the eq and ne methods, but CARLA has a global route planner, which uses the topology to create a networkx graph here. I've been using it for a long time and it seems to be working fine for me

glopezdiest avatar Sep 22 '22 10:09 glopezdiest

Hi @glopezdiest,

Thanks. That was a useful script. My original need was to see if there was an Eulerian path or circuit in the topology that I could use such that I am able to create the PCD map of all the Towns Main need: PCD Map of all Towns with Intensity data.

        sampling_resolution = 3
        global_planner = GlobalRoutePlanner(tmp_map, sampling_resolution)

        start_pose = tmp_map.get_spawn_points()[SPAWN_POINTS[i_map - 1]]
        start_location = start_pose.location
        end_location = start_pose.location
        route = global_planner.trace_route(start_location, end_location) 
        print(route)
        print(f"Size of graph |n| = {len(global_planner._graph.nodes)}, |e| = {len(global_planner._graph.edges)}")
        if nx.is_eulerian(global_planner._graph):
            print(f"Graph {global_planner._wmap} has an eulerian circuit:")
            #nx.write_graphml_xml(global_planner._graph,f"{map_name}.graphml"
            print(global_planner._graph)
        else:
            print(f"Graph {global_planner._wmap} does not have an eulerian circuit")

RaviBeagle avatar Oct 11 '22 16:10 RaviBeagle

Hi @glopezdiest,

Thanks. That was a useful script. My original need was to see if there was an Eulerian path or circuit in the topology that I could use such that I am able to create the PCD map of all the Towns Main need: PCD Map of all Towns with Intensity data.

        sampling_resolution = 3
        global_planner = GlobalRoutePlanner(tmp_map, sampling_resolution)

        start_pose = tmp_map.get_spawn_points()[SPAWN_POINTS[i_map - 1]]
        start_location = start_pose.location
        end_location = start_pose.location
        route = global_planner.trace_route(start_location, end_location) 
        print(route)
        print(f"Size of graph |n| = {len(global_planner._graph.nodes)}, |e| = {len(global_planner._graph.edges)}")
        if nx.is_eulerian(global_planner._graph):
            print(f"Graph {global_planner._wmap} has an eulerian circuit:")
            #nx.write_graphml_xml(global_planner._graph,f"{map_name}.graphml"
            print(global_planner._graph)
        else:
            print(f"Graph {global_planner._wmap} does not have an eulerian circuit")

Hi, when I use an OpenDRIVE file as the map for Carla, the world.get_map().get_topology() in global planner will be empty. Do you know the reason?

MayDGT avatar Nov 24 '23 14:11 MayDGT