Skip to content

Commit 6af1ea5

Browse files
committed
feat: Add support for retrieving paths to multiple destinations in path planning algorithms
1 parent 897bddf commit 6af1ea5

File tree

5 files changed

+206
-9
lines changed

5 files changed

+206
-9
lines changed

adf_core_python/core/component/module/algorithm/path_planning.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ def get_path(
3636
) -> list[EntityID]:
3737
pass
3838

39+
@abstractmethod
40+
def get_path_to_multiple_destinations(
41+
self, from_entity_id: EntityID, destination_entity_ids: list[EntityID]
42+
) -> list[EntityID]:
43+
pass
44+
3945
@abstractmethod
4046
def get_distance(self, from_entity_id: EntityID, to_entity_id: EntityID) -> float:
4147
pass

adf_core_python/core/gateway/component/module/algorithm/gateway_path_planning.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
from adf_core_python.core.agent.communication.message_manager import MessageManager
1515
from adf_core_python.core.agent.develop.develop_data import DevelopData
1616
from adf_core_python.core.agent.info.agent_info import AgentInfo
17-
from adf_core_python.core.agent.info.world_info import WorldInfo
1817
from adf_core_python.core.agent.info.scenario_info import ScenarioInfo
18+
from adf_core_python.core.agent.info.world_info import WorldInfo
1919
from adf_core_python.core.agent.module.module_manager import ModuleManager
2020
from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData
2121
from adf_core_python.core.gateway.gateway_module import GatewayModule
@@ -77,6 +77,25 @@ def get_path(
7777
entity_ids.append(EntityID(entity_id))
7878
return entity_ids
7979

80+
def get_path_to_multiple_destinations(
81+
self, from_entity_id: EntityID, destination_entity_ids: list[EntityID]
82+
) -> list[EntityID]:
83+
arguments: dict[str, str] = {
84+
"From": str(from_entity_id.get_value()),
85+
"Destinations": json.dumps(
86+
[entity_id.get_value() for entity_id in destination_entity_ids]
87+
),
88+
}
89+
result = self._gateway_module.execute(
90+
"getResult(EntityID, List[EntityID])", arguments
91+
)
92+
json_str = result.get_value_or_default("Result", "[]")
93+
raw_entity_ids: list[int] = json.loads(json_str)
94+
entity_ids: list[EntityID] = []
95+
for entity_id in raw_entity_ids:
96+
entity_ids.append(EntityID(entity_id))
97+
return entity_ids
98+
8099
def get_distance(self, from_entity_id: EntityID, to_entity_id: EntityID) -> float:
81100
arguments: dict[str, str] = {
82101
"From": str(from_entity_id.get_value()),

adf_core_python/implement/module/algorithm/a_star_path_planning.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ def get_path(
6969

7070
return []
7171

72+
def get_path_to_multiple_destinations(
73+
self, from_entity_id: EntityID, destination_entity_ids: list[EntityID]
74+
) -> list[EntityID]:
75+
raise NotImplementedError
76+
7277
def heuristic(self, from_entity_id: EntityID, to_entity_id: EntityID) -> float:
7378
# Implement a heuristic function, for example, Euclidean distance
7479
return self._world_info.get_distance(from_entity_id, to_entity_id)
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
from __future__ import annotations
2+
3+
import heapq
4+
from typing import Dict, List, Tuple
5+
6+
from rcrs_core.entities.area import Area
7+
from rcrs_core.entities.entity import Entity
8+
from rcrs_core.worldmodel.entityID import EntityID
9+
10+
from adf_core_python.core.agent.develop.develop_data import DevelopData
11+
from adf_core_python.core.agent.info.agent_info import AgentInfo
12+
from adf_core_python.core.agent.info.scenario_info import ScenarioInfo
13+
from adf_core_python.core.agent.info.world_info import WorldInfo
14+
from adf_core_python.core.agent.module.module_manager import ModuleManager
15+
from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning
16+
17+
18+
class DijkstraPathPlanning(PathPlanning):
19+
def __init__(
20+
self,
21+
agent_info: AgentInfo,
22+
world_info: WorldInfo,
23+
scenario_info: ScenarioInfo,
24+
module_manager: ModuleManager,
25+
develop_data: DevelopData,
26+
) -> None:
27+
super().__init__(
28+
agent_info, world_info, scenario_info, module_manager, develop_data
29+
)
30+
self.graph: Dict[EntityID, List[Tuple[EntityID, float]]] = {}
31+
# グラフの構築
32+
for area in self._world_info.get_entities_of_types([Area]):
33+
if not isinstance(area, Area):
34+
continue
35+
if (neighbors := area.get_neighbours()) is None:
36+
continue
37+
area_id = area.get_id()
38+
self.graph[area_id] = [
39+
(
40+
neighbor,
41+
self._world_info.get_distance(area_id, entity_id2=neighbor),
42+
)
43+
for neighbor in neighbors
44+
if neighbor.get_value() != 0
45+
]
46+
47+
def calculate(self) -> PathPlanning:
48+
return self
49+
50+
def get_path(
51+
self, from_entity_id: EntityID, to_entity_id: EntityID
52+
) -> List[EntityID]:
53+
# ダイクストラ法で最短経路を計算
54+
queue = []
55+
heapq.heappush(queue, (0, from_entity_id))
56+
distance = {from_entity_id: 0}
57+
previous = {from_entity_id: None}
58+
59+
while queue:
60+
current_distance, current_node = heapq.heappop(queue)
61+
if current_node == to_entity_id:
62+
break
63+
64+
self._logger.info(
65+
f"current_node: {current_node}, current_entity: {self._world_info.get_entity(current_node)}"
66+
)
67+
68+
for neighbor, weight in self.graph[current_node]:
69+
new_distance = current_distance + weight
70+
if neighbor not in distance or new_distance < distance[neighbor]:
71+
distance[neighbor] = new_distance
72+
heapq.heappush(queue, (new_distance, neighbor))
73+
previous[neighbor] = current_node
74+
75+
path = []
76+
current_node = to_entity_id
77+
while current_node is not None:
78+
path.append(current_node)
79+
current_node = previous[current_node]
80+
81+
return path[::-1]
82+
83+
def get_path_to_multiple_destinations(
84+
self, from_entity_id: EntityID, destination_entity_ids: List[EntityID]
85+
) -> List[EntityID]:
86+
open_list = [from_entity_id]
87+
ancestors = {from_entity_id: from_entity_id}
88+
found = False
89+
next_node = None
90+
91+
while open_list and not found:
92+
next_node = open_list.pop(0)
93+
if self.is_goal(next_node, destination_entity_ids):
94+
found = True
95+
break
96+
97+
neighbors = self.graph.get(next_node, [])
98+
if not neighbors:
99+
continue
100+
101+
for neighbor, _ in neighbors:
102+
if self.is_goal(neighbor, destination_entity_ids):
103+
ancestors[neighbor] = next_node
104+
next_node = neighbor
105+
found = True
106+
break
107+
elif neighbor not in ancestors:
108+
open_list.append(neighbor)
109+
ancestors[neighbor] = next_node
110+
111+
if not found:
112+
return []
113+
114+
path = []
115+
current = next_node
116+
while current != from_entity_id:
117+
if current is None:
118+
raise RuntimeError(
119+
"Found a node with no ancestor! Something is broken."
120+
)
121+
path.insert(0, current)
122+
current = ancestors.get(current)
123+
path.insert(0, from_entity_id)
124+
125+
return path
126+
127+
def is_goal(self, entity_id: EntityID, target_ids: List[EntityID]) -> bool:
128+
return entity_id in target_ids
129+
130+
def get_distance(self, from_entity_id: EntityID, to_entity_id: EntityID) -> float:
131+
path = self.get_path(from_entity_id, to_entity_id)
132+
distance = 0.0
133+
for i in range(len(path) - 1):
134+
distance += self._world_info.get_distance(path[i], path[i + 1])
135+
return distance

java/lib/src/main/java/adf_core_python/gateway/mapper/module/algorithm/PathPlanningMapper.java

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
package adf_core_python.gateway.mapper.module.algorithm;
22

3+
import java.util.Collection;
4+
import java.util.List;
5+
6+
import com.fasterxml.jackson.core.JsonProcessingException;
7+
import com.fasterxml.jackson.core.type.TypeReference;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
310
import adf.core.agent.communication.MessageManager;
411
import adf_core_python.agent.precompute.PrecomputeData;
512
import adf_core_python.component.module.algorithm.PathPlanning;
613
import adf_core_python.gateway.mapper.module.AbstractModuleMapper;
7-
import com.fasterxml.jackson.core.JsonProcessingException;
8-
import com.fasterxml.jackson.core.type.TypeReference;
9-
import com.fasterxml.jackson.databind.ObjectMapper;
1014
import rescuecore2.config.Config;
1115
import rescuecore2.worldmodel.EntityID;
1216

13-
import java.util.Collection;
14-
import java.util.List;
15-
1617
public class PathPlanningMapper extends AbstractModuleMapper {
1718
public PathPlanningMapper(PathPlanning pathPlanning, PrecomputeData precomputeData, MessageManager messageManager) {
1819
super(pathPlanning, precomputeData, messageManager);
@@ -42,10 +43,24 @@ public Config execMethod(String methodName, Config arguments) {
4243
result = execGetDistance();
4344
}
4445
if (methodName.equals("getDistance(EntityID, EntityID)")) {
45-
result = execGetDistance(new EntityID(arguments.getIntValue("From")), new EntityID(arguments.getIntValue("Dest")));
46+
result = execGetDistance(new EntityID(arguments.getIntValue("From")),
47+
new EntityID(arguments.getIntValue("Dest")));
4648
}
4749
if (methodName.equals("getResult(EntityID, EntityID)")) {
48-
result = execGetResult(new EntityID(arguments.getIntValue("From")), new EntityID(arguments.getIntValue("Dest")));
50+
result = execGetResult(new EntityID(arguments.getIntValue("From")),
51+
new EntityID(arguments.getIntValue("Dest")));
52+
}
53+
if (methodName.equals("getResult(EntityID, List[EntityID])")) {
54+
ObjectMapper objectMapper = new ObjectMapper();
55+
Collection<EntityID> destinations;
56+
try {
57+
destinations = objectMapper.readValue(arguments.getValue("Destinations"),
58+
new TypeReference<List<EntityID>>() {
59+
});
60+
} catch (JsonProcessingException e) {
61+
throw new RuntimeException(e);
62+
}
63+
result = execGetResult(new EntityID(arguments.getIntValue("From")), destinations);
4964
}
5065
return result;
5166
}
@@ -105,4 +120,21 @@ private Config execGetResult(EntityID from, EntityID dest) {
105120
result.setValue("Result", String.valueOf(jsonStr));
106121
return result;
107122
}
123+
124+
private Config execGetResult(EntityID from, Collection<EntityID> destinations) {
125+
PathPlanning pathPlanning = (PathPlanning) abstractModule;
126+
pathPlanning.setFrom(from);
127+
pathPlanning.setDestination(destinations);
128+
List<EntityID> entityIDs = pathPlanning.getResult();
129+
Config result = new Config();
130+
ObjectMapper objectMapper = new ObjectMapper();
131+
String jsonStr;
132+
try {
133+
jsonStr = objectMapper.writeValueAsString(entityIDs.stream().map(EntityID::getValue).toArray());
134+
} catch (JsonProcessingException e) {
135+
throw new RuntimeException(e);
136+
}
137+
result.setValue("Result", String.valueOf(jsonStr));
138+
return result;
139+
}
108140
}

0 commit comments

Comments
 (0)