Skip to content

Commit 0f19d83

Browse files
committed
feat: Add support for retrieving paths to multiple destinations in path planning algorithms
feat: Add support for retrieving paths to multiple destinations in path planning algorithms fix: Change destination_entity_ids type from list to set in path planning algorithms fix: Add type annotations for queue, distance, previous, and path in DijkstraPathPlanning fix: Add type annotation for path variable in DijkstraPathPlanning
1 parent 897bddf commit 0f19d83

File tree

5 files changed

+205
-9
lines changed

5 files changed

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