Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 33 additions & 23 deletions docs/source/hands-on/clustering.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## クラスタリングモジュールの目的

複数のエージェントを動かす場合は、それらのエージェントにどのように協調させるかが重要になります。RRSでは多くのチームが、エージェントに各々の担当地域を持たせ役割分担をおこなう協調を取り入れています(他の手段による協調も取り入れています)。担当地域を割り振るためには、地図上のオブジェクトをいくつかのグループに分ける必要があります。このようなグループ分けをしてそれらを管理する場合には、クラスタリングモジュールと呼ばれるモジュールを用います。
複数のエージェントを動かす場合は、それらのエージェントにどのように協調させるかが重要になります。RRS では多くのチームが、エージェントに各々の担当地域を持たせ役割分担をおこなう協調を取り入れています(他の手段による協調も取り入れています)。担当地域を割り振るためには、地図上のオブジェクトをいくつかのグループに分ける必要があります。このようなグループ分けをしてそれらを管理する場合には、クラスタリングモジュールと呼ばれるモジュールを用います。

本資料では、多くの世界大会参加チームが使用しているアルゴリズムを用いたクラスタリングモジュールの実装をおこないます。

Expand All @@ -11,7 +11,7 @@
本資料で開発するモジュールは下の画像のように、

1. k-means++アルゴリズムによって地図上のオブジェクトをエージェント数分の区画に分けます。
1. Hungarianアルゴリズムによってそれらの区画とエージェントを (間の距離の総和が最も小さくなるように)1対1で結びつけます
1. Hungarian アルゴリズムによってそれらの区画とエージェントを (間の距離の総和が最も小さくなるように)1 対 1 で結びつけます

![クラスタリングの画像](./../images/clustering_image.jpg)

Expand Down Expand Up @@ -39,17 +39,19 @@ from adf_core_python.core.agent.info.world_info import WorldInfo
from adf_core_python.core.agent.module.module_manager import ModuleManager
from adf_core_python.core.component.module.algorithm.clustering import Clustering
from adf_core_python.core.logger.logger import get_logger
from rcrs_core.connection.URN import Entity as EntityURN
from rcrs_core.entities.ambulanceCenter import AmbulanceCentre
from rcrs_core.entities.building import Building
from rcrs_core.entities.entity import Entity
from rcrs_core.entities.fireStation import FireStation
from rcrs_core.entities.gasStation import GasStation
from rcrs_core.entities.hydrant import Hydrant
from rcrs_core.entities.policeOffice import PoliceOffice
from rcrs_core.entities.refuge import Refuge
from rcrs_core.entities.road import Road
from rcrs_core.worldmodel.entityID import EntityID
from rcrscore.entities import (
AmbulanceCenter,
Building,
Entity,
EntityID,
FireStation,
GasStation,
Hydrant,
PoliceOffice,
Refuge,
Road,
)
from rcrscore.urn import EntityURN
from scipy.optimize import linear_sum_assignment
from sklearn.cluster import KMeans

Expand All @@ -74,7 +76,13 @@ class KMeansPPClustering(Clustering):

# クラスター数の設定
self._cluster_number: int = 1
match agent_info.get_myself().get_urn():

me = agent_info.get_myself()
if not me:
self._logger.error("Myself entity is None.")
return

match me.get_urn():
# エージェントのクラスに応じてクラスター数を設定
case EntityURN.AMBULANCE_TEAM:
self._cluster_number = scenario_info.get_value(
Expand All @@ -95,7 +103,7 @@ class KMeansPPClustering(Clustering):
# 自分と同じクラスのエージェントのリストを取得
self._agents: list[Entity] = world_info.get_entities_of_types(
[
agent_info.get_myself().__class__,
me.__class__,
]
)

Expand All @@ -105,7 +113,7 @@ class KMeansPPClustering(Clustering):
# クラスタリング対象のエンティティのリストを取得
self._entities: list[Entity] = world_info.get_entities_of_types(
[
AmbulanceCentre,
AmbulanceCenter,
FireStation,
GasStation,
Hydrant,
Expand Down Expand Up @@ -180,7 +188,9 @@ class KMeansPPClustering(Clustering):
"""
if cluster_index >= len(self._cluster_entities):
return []
return [entity.get_id() for entity in self._cluster_entities[cluster_index]]
return [
entity.get_entity_id() for entity in self._cluster_entities[cluster_index]
]

def prepare(self) -> Clustering:
"""
Expand All @@ -205,17 +215,17 @@ class KMeansPPClustering(Clustering):

# エージェントとクラスターの対応付け結果を保持
self._agent_cluster_indices = {
entity.get_id(): cluster_index
entity.get_entity_id(): cluster_index
for entity, cluster_index in zip(self._agents, agent_cluster_indices)
}

# デバッグ用のログ出力
self._logger.info(
f"Clustered entities: {[[entity.get_id().get_value() for entity in cluster] for cluster in self._cluster_entities]}"
f"Clustered entities: {[[entity.get_entity_id().get_value() for entity in cluster] for cluster in self._cluster_entities]}"
)

self._logger.info(
f"Agent cluster indices: {[([self._world_info.get_entity(entity_id).get_x(), self._world_info.get_entity(entity_id).get_y()], int(cluster_index)) for entity_id, cluster_index in self._agent_cluster_indices.items()]}"
f"Agent cluster indices: {[([e.get_x(), e.get_y()] if (e is not None and e.get_x() is not None and e.get_y() is not None) else [None, None], int(cluster_index)) for entity_id, cluster_index in self._agent_cluster_indices.items() for e in (self._world_info.get_entity(entity_id),)]}"
)

return self
Expand Down Expand Up @@ -292,9 +302,9 @@ class KMeansPPClustering(Clustering):
return col_ind
```

k-means++の実装は、scikit-learnの`KMeans`クラスを使用しています。`KMeans`クラスは、`n_clusters`で指定したクラスター数によって地図上のオブジェクトをクラスタリングします。クラスタリング結果は、`labels_`属性に格納されます。また、`cluster_centers_`属性には各クラスターの中心座標が格納されます。
k-means++の実装は、scikit-learn の`KMeans`クラスを使用しています。`KMeans`クラスは、`n_clusters`で指定したクラスター数によって地図上のオブジェクトをクラスタリングします。クラスタリング結果は、`labels_`属性に格納されます。また、`cluster_centers_`属性には各クラスターの中心座標が格納されます。

hungarianアルゴリズムの実装は、scipyの`linear_sum_assignment`関数を使用しています。`linear_sum_assignment`関数は、コスト行列を引数として受け取り、最適な割り当てを行います。
hungarian アルゴリズムの実装は、scipy の`linear_sum_assignment`関数を使用しています。`linear_sum_assignment`関数は、コスト行列を引数として受け取り、最適な割り当てを行います。

次に、作成したモジュールを登録します。`config/module.yaml` を以下のように編集してください。

Expand All @@ -307,7 +317,7 @@ SampleHumanDetector:
Clustering: src.<your_team_name>.module.algorithm.k_means_pp_clustering.KMeansPPClustering
```

ターミナルを2つ起動します
ターミナルを 2 つ起動します

片方のターミナルを開き、シミュレーションサーバーを以下のコマンドで起動します:

Expand Down
32 changes: 14 additions & 18 deletions docs/source/hands-on/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ touch src/<your_team_name>/module/complex/k_means_pp_search.py
import random
from typing import Optional, cast

from rcrs_core.entities.building import Building
from rcrs_core.entities.entity import Entity
from rcrs_core.entities.refuge import Refuge
from rcrs_core.worldmodel.entityID import EntityID

from adf_core_python.core.agent.develop.develop_data import DevelopData
from adf_core_python.core.agent.info.agent_info import AgentInfo
from adf_core_python.core.agent.info.scenario_info import ScenarioInfo
Expand All @@ -36,6 +31,7 @@ from adf_core_python.core.agent.module.module_manager import ModuleManager
from adf_core_python.core.component.module.algorithm.clustering import Clustering
from adf_core_python.core.component.module.complex.search import Search
from adf_core_python.core.logger.logger import get_agent_logger
from rcrscore.entities import Building, Entity, Refuge, EntityID


class KMeansPPSearch(Search):
Expand Down Expand Up @@ -109,7 +105,7 @@ class KMeansPPSearch(Search):
agent_info, world_info, scenario_info, module_manager, develop_data
)
self._result: Optional[EntityID] = None

# ロガーの取得
self._logger = get_agent_logger(
f"{self.__class__.__module__}.{self.__class__.__qualname__}",
Expand Down Expand Up @@ -148,16 +144,16 @@ class KMeansPPSearch(Search):
# 乱数で選択
if cluster_entity_ids:
self._result = random.choice(cluster_entity_ids)

# ログ出力
self._logger.info(f"Target entity ID: {self._result}")

return self
```

以上で、`KMeansPPClustering` モジュールを用いた `KMeansPPSearch` モジュールの実装が完了しました。

ターミナルを2つ起動します
ターミナルを 2 つ起動します

片方のターミナルを開き、シミュレーションサーバーを以下のコマンドで起動します:

Expand Down Expand Up @@ -208,7 +204,7 @@ python main.py
一度選択した探索対象に到達するまで、探索対象を変更しないようにする
```

```{admonition} プログラム例
`````{admonition} プログラム例
:class: hint dropdown

````python
Expand Down Expand Up @@ -236,12 +232,12 @@ python main.py
# 探索対象が未選択の場合
if not self._result and cluster_entity_ids:
self._result = random.choice(cluster_entity_ids)

# ログ出力
self._logger.info(f"Target entity ID: {self._result}")

return self
```
`````

### すでに探索したエンティティを再度探索対象として選択してしまう問題

Expand All @@ -251,7 +247,7 @@ python main.py
すでに探索したエンティティを何かしらの方法で記録し、再度探索対象として選択しないようにする
```

```{admonition} プログラム例
`````{admonition} プログラム例
:class: hint dropdown

````python
Expand Down Expand Up @@ -319,12 +315,12 @@ python main.py
# 探索対象が未選択の場合(変更)
if not self._result and self._search_entity_ids:
self._result = random.choice(self._search_entity_ids)

# ログ出力
self._logger.info(f"Target entity ID: {self._result}")

return self
```
`````

### 近くに未探索のエンティティがあるのに、遠くのエンティティを探索対象として選択してしまう

Expand All @@ -334,7 +330,7 @@ python main.py
エンティティ間の距離を計算し、もっとも近いエンティティを探索対象として選択する
```

```{admonition} プログラム例
`````{admonition} プログラム例
:class: hint dropdown

````python
Expand Down Expand Up @@ -375,9 +371,9 @@ python main.py
nearest_entity_id = entity_id
nearest_distance = distance
self._result = nearest_entity_id

# ログ出力
self._logger.info(f"Target entity ID: {self._result}")

return self
```
`````
Loading
Loading