naohiro701 commited on
Commit
0535c91
·
verified ·
1 Parent(s): 712fff8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -328
app.py CHANGED
@@ -1,328 +0,0 @@
1
- # boids_streamlit_advanced.py
2
-
3
- import streamlit as st
4
- import numpy as np
5
- import plotly.graph_objects as go
6
- import time
7
-
8
- # Boidクラスの定義
9
- class Boid:
10
- def __init__(self, position, velocity):
11
- self.position = np.array(position, dtype='float64')
12
- self.velocity = np.array(velocity, dtype='float64')
13
- self.acceleration = np.zeros(2, dtype='float64')
14
- self.history = []
15
-
16
- def update(self, boids, width, height, params):
17
- self.flock(boids, params)
18
- self.velocity += self.acceleration
19
- speed = np.linalg.norm(self.velocity)
20
- if speed > params['max_speed']:
21
- self.velocity = (self.velocity / speed) * params['max_speed']
22
- self.position += self.velocity
23
- self.acceleration = np.zeros(2, dtype='float64')
24
-
25
- # 画面端でのラッピング
26
- self.position[0] = self.position[0] % width
27
- self.position[1] = self.position[1] % height
28
-
29
- # 履歴に現在の位置を追加
30
- if params['draw_trail']:
31
- self.history.append(self.position.copy())
32
- if len(self.history) > params['max_history']:
33
- self.history.pop(0)
34
-
35
- def flock(self, boids, params):
36
- self.acceleration = np.zeros(2, dtype='float64')
37
- self.fly_towards_center(boids, params)
38
- self.avoid_others(boids, params)
39
- self.match_velocity(boids, params)
40
- self.limit_speed(params)
41
- self.avoid_boundaries(width=params['width'], height=params['height'], params=params)
42
-
43
- def fly_towards_center(self, boids, params):
44
- centering_factor = params['centering_factor']
45
- center_x = 0.0
46
- center_y = 0.0
47
- num_neighbors = 0
48
-
49
- for other in boids:
50
- if other is not self and self.distance(other) < params['visual_range']:
51
- center_x += other.position[0]
52
- center_y += other.position[1]
53
- num_neighbors += 1
54
-
55
- if num_neighbors > 0:
56
- center_x /= num_neighbors
57
- center_y /= num_neighbors
58
- direction = np.array([center_x, center_y]) - self.position
59
- self.acceleration += centering_factor * direction
60
-
61
- def avoid_others(self, boids, params):
62
- min_distance = params['min_distance']
63
- avoid_factor = params['avoid_factor']
64
- move = np.zeros(2, dtype='float64')
65
-
66
- for other in boids:
67
- if other is not self and self.distance(other) < min_distance:
68
- move += self.position - other.position
69
-
70
- if np.linalg.norm(move) > 0:
71
- move = move / np.linalg.norm(move) * avoid_factor
72
- self.acceleration += move
73
-
74
- def match_velocity(self, boids, params):
75
- matching_factor = params['matching_factor']
76
- avg_velocity = np.zeros(2, dtype='float64')
77
- num_neighbors = 0
78
-
79
- for other in boids:
80
- if other is not self and self.distance(other) < params['visual_range']:
81
- avg_velocity += other.velocity
82
- num_neighbors += 1
83
-
84
- if num_neighbors > 0:
85
- avg_velocity /= num_neighbors
86
- self.acceleration += matching_factor * (avg_velocity - self.velocity)
87
-
88
- def limit_speed(self, params):
89
- speed = np.linalg.norm(self.velocity)
90
- if speed > params['max_speed']:
91
- self.velocity = (self.velocity / speed) * params['max_speed']
92
-
93
- def avoid_boundaries(self, width, height, params):
94
- margin = params['boundary_margin']
95
- turn_factor = params['boundary_turn_factor']
96
-
97
- if self.position[0] < margin:
98
- self.acceleration[0] += turn_factor
99
- elif self.position[0] > width - margin:
100
- self.acceleration[0] -= turn_factor
101
-
102
- if self.position[1] < margin:
103
- self.acceleration[1] += turn_factor
104
- elif self.position[1] > height - margin:
105
- self.acceleration[1] -= turn_factor
106
-
107
- def distance(self, other):
108
- return np.linalg.norm(self.position - other.position)
109
-
110
- # シミュレーションパラメータ
111
- params = {
112
- 'num_boids': 100,
113
- 'visual_range': 75.0,
114
- 'min_distance': 20.0,
115
- 'centering_factor': 0.005,
116
- 'avoid_factor': 0.05,
117
- 'matching_factor': 0.05,
118
- 'max_speed': 15.0,
119
- 'draw_trail': False,
120
- 'max_history': 50,
121
- 'width': 800,
122
- 'height': 600,
123
- 'boundary_margin': 100.0,
124
- 'boundary_turn_factor': 0.05
125
- }
126
-
127
- # Streamlitのサイドバーでパラメータを調整
128
- st.sidebar.title("Boids Simulation Parameters")
129
- params['num_boids'] = st.sidebar.slider("Number of Boids", 10, 300, 100)
130
- params['visual_range'] = st.sidebar.slider("Visual Range", 10.0, 200.0, 75.0)
131
- params['min_distance'] = st.sidebar.slider("Minimum Separation Distance", 5.0, 100.0, 20.0)
132
- params['centering_factor'] = st.sidebar.slider("Centering Factor", 0.001, 0.02, 0.005)
133
- params['avoid_factor'] = st.sidebar.slider("Avoidance Factor", 0.01, 0.1, 0.05)
134
- params['matching_factor'] = st.sidebar.slider("Matching Factor", 0.01, 0.1, 0.05)
135
- params['max_speed'] = st.sidebar.slider("Maximum Speed", 5.0, 30.0, 15.0)
136
- params['draw_trail'] = st.sidebar.checkbox("Draw Trails")
137
- if params['draw_trail']:
138
- params['max_history'] = st.sidebar.slider("Trail Length", 10, 100, 50)
139
- params['boundary_margin'] = st.sidebar.slider("Boundary Margin", 50.0, 300.0, 100.0)
140
- params['boundary_turn_factor'] = st.sidebar.slider("Boundary Turn Factor", 0.01, 0.2, 0.05)
141
-
142
- # シミュレーション画面サイズ
143
- width, height = 800, 600
144
- params['width'] = width
145
- params['height'] = height
146
-
147
- # Boidsの初期化
148
- boids = []
149
- for _ in range(params['num_boids']):
150
- position = [np.random.uniform(0, width), np.random.uniform(0, height)]
151
- angle = np.random.uniform(0, 2 * np.pi)
152
- velocity = [np.cos(angle), np.sin(angle)]
153
- boids.append(Boid(position, velocity))
154
-
155
- # Plotlyのグラフ設定
156
- fig = go.Figure(
157
- layout=go.Layout(
158
- xaxis=dict(range=[0, width], autorange=False, showgrid=False, zeroline=False),
159
- yaxis=dict(range=[0, height], autorange=False, showgrid=False, zeroline=False),
160
- width=width,
161
- height=height,
162
- margin=dict(l=0, r=0, t=0, b=0)
163
- )
164
- )
165
-
166
- # Boidsの初期位置をプロット
167
- scatter = go.Scatter(
168
- x=[boid.position[0] for boid in boids],
169
- y=[boid.position[1] for boid in boids],
170
- mode='markers',
171
- marker=dict(size=8, color='blue')
172
- )
173
-
174
- fig.add_trace(scatter)
175
-
176
- # トレイル用のトレース
177
- if params['draw_trail']:
178
- trail_scatter = go.Scatter(
179
- x=[],
180
- y=[],
181
- mode='lines',
182
- line=dict(color='rgba(0,0,255,0.2)', width=1),
183
- showlegend=False
184
- )
185
- fig.add_trace(trail_scatter)
186
-
187
- # Streamlitのタイトル
188
- st.title("Boids Simulation with Streamlit and Plotly")
189
-
190
- # 説明セクション
191
- st.header("群れアルゴリズムBoidsの数学的背景")
192
-
193
- st.markdown(r"""
194
- ### **Boidsアルゴリズムの概要**
195
-
196
- Boidsアルゴリズムは、Craig Reynoldsによって1986年に提案された群れ行動のシミュレーション手法です。各エージェント(Boid)は単純なルールに従うことで、複雑な群れ行動を再現します。基本的な3つのルールは以下の通りです:
197
-
198
- 1. **分離(Separation)**: 隣接するBoidから適切な距離を保ち、衝突を避ける。
199
- 2. **整列(Alignment)**: 隣接するBoidの平均速度に自身の速度を合わせる。
200
- 3. **結合(Cohesion)**: 隣接するBoidの中心に向かって移動する。
201
-
202
- ### **数式によるモデルの定義**
203
-
204
- Boidsの動作は、各エージェントの位置ベクトル \(\mathbf{p}_i(t)\) と速度ベクトル \(\mathbf{v}_i(t)\) で表されます。時間 \(t\) におけるエージェント \(i\) の位置と速度は、以下の微分方程式で記述されます:
205
-
206
- \[
207
- \frac{d\mathbf{p}_i(t)}{dt} = \mathbf{v}_i(t)
208
- \]
209
-
210
- \[
211
- \frac{d\mathbf{v}_i(t)}{dt} = \mathbf{a}_i(t)
212
- \]
213
-
214
- ここで、加速度 \(\mathbf{a}_i(t)\) は以下の3つの力の合成です:
215
-
216
- \[
217
- \mathbf{a}_i(t) = \mathbf{a}_{\text{separation}} + \mathbf{a}_{\text{alignment}} + \mathbf{a}_{\text{cohesion}}
218
- \]
219
-
220
- #### **1. 分離(Separation)**
221
-
222
- Boidが他のBoidと衝突しないようにするための力。エージェント \(i\) と近傍エージェント \(j\) との間の距離を \(d_{ij}\) とすると、分離力 \(\mathbf{a}_{\text{separation}}\) は以下のように定義されます:
223
-
224
- \[
225
- \mathbf{a}_{\text{separation}} = \sum_{j \in N(i)} \frac{\mathbf{p}_i - \mathbf{p}_j}{d_{ij}^2}
226
- \]
227
-
228
- ここで、\(N(i)\) はBoid \(i\) の近傍にいるBoidの集合です。
229
-
230
- #### **2. 整列(Alignment)**
231
-
232
- Boidが周囲のBoidと同じ方向に移動するようにするための力。近傍Boidの平均速度 \(\mathbf{v}_{\text{avg}}\) を計算し、Boid \(i\) の速度をそれに合わせるための力は以下の通りです:
233
-
234
- \[
235
- \mathbf{a}_{\text{alignment}} = \frac{\mathbf{v}_{\text{avg}} - \mathbf{v}_i}{\tau}
236
- \]
237
-
238
- ここで、\(\tau\) は調整パラメータです。
239
-
240
- #### **3. 結合(Cohesion)**
241
-
242
- Boidが周囲のBoidの中心に向かって移動するようにするための力。近傍Boidの中心 \(\mathbf{C}_{\text{avg}}\) を計算し、Boid \(i\) がそこに向かうための力は以下の通りです:
243
-
244
- \[
245
- \mathbf{a}_{\text{cohesion}} = \frac{\mathbf{C}_{\text{avg}} - \mathbf{p}_i}{\sigma}
246
- \]
247
-
248
- ここで、\(\sigma\) は調整パラメータです。
249
-
250
- ### **アルゴリズムの更新ルール**
251
-
252
- 各Boidの位置と速度は、離散的な時間ステップ \( \Delta t \) に基づいて更新されます。更新ルールは以下のようになります:
253
-
254
- \[
255
- \mathbf{v}_i(t + \Delta t) = \mathbf{v}_i(t) + \mathbf{a}_i(t) \Delta t
256
- \]
257
-
258
- \[
259
- \mathbf{p}_i(t + \Delta t) = \mathbf{p}_i(t) + \mathbf{v}_i(t + \Delta t) \Delta t
260
- \]
261
-
262
- これらの式に基づき、各Boidは分離、整列、結合の力を計算し、それに基づいて速度と位置を更新します。
263
-
264
- ### **追加機能の説明**
265
-
266
- 本シミュレーションでは、以下の追加機能を���装しています:
267
-
268
- 1. **境界回避(Boundary Avoidance)**: シミュレーション領域の端にBoidが近づくと、Boidが逆方向に加速度を受けて領域内に留まるようにします。これにより、Boidが画面外に出ることを防ぎます。
269
-
270
- 2. **トレイル描画(Trail Drawing)**: Boidの過去の位置をトレイルとして表示し、動きの軌跡を視覚化します。これにより、Boidの動きのパターンを追跡することが容易になります。
271
-
272
- ### **パラメータの追加とその役割**
273
-
274
- - **Boundary Margin (\(M\))**: シミュレーション領域の端からBoidが回避を開始する距離。
275
- - **Boundary Turn Factor (\(\gamma\))**: 境界回避力の強さを調整する係数。
276
-
277
- これらのパラメータにより、Boidが領域の端に近づいた際の挙動を細かく制御できます。
278
-
279
- """)
280
-
281
- # アニメーションの表示領域
282
- animation_placeholder = st.empty()
283
-
284
- # アニメーションの実行
285
- frame_rate = 30 # フレームレート (FPS)
286
- sleep_time = 1.0 / frame_rate
287
-
288
- # リセットボタンの追加
289
- if st.sidebar.button("Reset Simulation"):
290
- boids = []
291
- for _ in range(params['num_boids']):
292
- position = [np.random.uniform(0, width), np.random.uniform(0, height)]
293
- angle = np.random.uniform(0, 2 * np.pi)
294
- velocity = [np.cos(angle), np.sin(angle)]
295
- boids.append(Boid(position, velocity))
296
-
297
- # アニメーションの実行ループ
298
- while True:
299
- # Boidsの更新
300
- for boid in boids:
301
- boid.update(boids, width, height, params)
302
-
303
- # Boidsの位置データを更新
304
- scatter.x = [boid.position[0] for boid in boids]
305
- scatter.y = [boid.position[1] for boid in boids]
306
-
307
- # トレイルの更新
308
- if params['draw_trail']:
309
- trail_x = []
310
- trail_y = []
311
- for boid in boids:
312
- trail_x.extend([pos[0] for pos in boid.history])
313
- trail_y.extend([pos[1] for pos in boid.history])
314
- trail_scatter.x = trail_x
315
- trail_scatter.y = trail_y
316
-
317
- # グラフの更新
318
- fig.data[0].x = scatter.x
319
- fig.data[0].y = scatter.y
320
- if params['draw_trail']:
321
- fig.data[1].x = trail_scatter.x
322
- fig.data[1].y = trail_scatter.y
323
-
324
- # アニメーション表示
325
- animation_placeholder.plotly_chart(fig, use_container_width=True)
326
-
327
- # フレームレート調整
328
- time.sleep(sleep_time)