python
import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation from IPython.display import HTML import _ctypes a1 = -0.034298b1 = 3.348394c1 = 4.252840d1 = -0.003423 survival=dict() class Agent: def __init__(self, name = None, pos = None, vel = None, SIZE = 10, VEL = 1, RANGE_SEPARATE = 0.5,color = None): self.name = name if name else 'agent_02' self.pos = pos if pos else np.random.uniform(-SIZE, SIZE, 2) self.vel = vel if vel else np.random.uniform(-VEL, VEL, 2) self.SIZE = SIZE self.VEL = VEL self.RANGE_SEPARATE = RANGE_SEPARATE#分離範囲 self.vel_tmp = np.zeros(2)#移動 self.color = color if color else 'k' def distance(self, other):#ユークリッド距離 return np.linalg.norm(self.pos - other.pos) def ruleSeparate(self, others, ratio = 1): others = [other for other in others if self.distance(other) < self.RANGE_SEPARATE]#範囲内に入ったエージェントの選別 v = np.zeros(2) if not others: return 0 for other in others: d = self.pos - other.pos#dが-になる時ある? v += d / self.distance(other)#進行方向の単位ベクトル self.vel_tmp += v / len(others) * ratio#平均を出してそれをratio倍する return v / len(others) * ratio def calculate(self, others): self.ruleSeparate(others, 0.5) def update(self) -> bool:#返り値の型名 self.vel += self.vel_tmp#進行速度ベクトル更新 v = np.linalg.norm(self.vel) #壁に当たったら反転 if (abs(self.pos + self.vel)[0] > self.SIZE): self.vel[0] = -self.vel[0] if (abs(self.pos + self.vel)[1] > self.SIZE): self.vel[1] = -self.vel[1] self.pos += self.vel#位置更新 self.vel_tmp = np.zeros(2)##進行速度ベクトル初期化 return True class AgentEdited(Agent): def __init__(self, *args, **kwargs): super(AgentEdited, self).__init__(*args, **kwargs) #クラス(子クラス)で別のクラス(親クラス)を継承できます。継承することで、親クラスのメソッドを子クラスから呼び出せる #super().親クラスのメソッド # python3系での標準の書き方 #super(親クラスのオブジェクト, self).親クラスのメソッド # python2系での書き方 #不特定多数の引数を受け取ることができる仕組みを作るのが「*args」「**kwargs」 self.color = kwargs.get('color', 'r') self.name=kwargs.get('agent_01') def ruleSeparate(self, others, ratio = 1): others = [other for other in others if self.distance(other) < self.RANGE_SEPARATE] v = np.zeros(2) if not others: return 0 for other in others: d = self.pos - other.pos vel = self.vel - other.vel TTNP = d / (vel + 1e-5) # 0除算防止のため微小値を挿入 DINP = self.distance(other) v += 1/(1+np.exp(-(c1+d1*TTNP))) * 1/(1+np.exp(-(b1+a1*DINP))) self.vel_tmp += v / len(others) * ratio return v / len(others) * ratio def calculate(self, others): self.ruleSeparate(others) # only separate def calc_rad(pos2, pos1):#角計算 return np.arctan2(pos2[1] - pos1[1], pos2[0] - pos1[0]) #np.arctan(x)は引数が一つでarctan(x)をラジアンで返す。返り値は-pi / 2からpi / 2(-90度から90度)の間になる。#np.arctan2(y, x)は引数が二つで、arctan(y / x)をラジアンで返す。この角度は、極座標平面において原点から座標(x, y)へのベクトルがx軸の正の方向となす角度(偏角)であり、返り値は-piからpi(-180度から180度)の間になる#print(np.degrees(np.arctan2(-1, 1)))# -45.0#print(np.degrees(np.arctan2(1, -1)))# 135.0 def rotate_vec(vec, rad):#ベクトル更新 return np.dot(vec, np.array([[np.cos(rad), -np.sin(rad)], [np.sin(rad), np.cos(rad)]]).T) class AgentGoForWall(AgentEdited): def __init__(self, *args, **kwargs): super(AgentGoForWall, self).__init__(*args, **kwargs) survival[id(self)] = [self.pos] # カウント初期化 self.color = kwargs.get('color', 'b') self.name=kwargs.get('agent_03') max_distance = 0 self.goal = None # 角が一番遠い場所 for px in [-self.SIZE, self.SIZE]: for py in [-self.SIZE, self.SIZE]: dist = np.linalg.norm(self.pos - np.array([px, py])) if max_distance < dist: max_distance = dist#最大値の更新 self.goal = np.array([px, py]) self.vel = rotate_vec(np.array([self.VEL, 0]), calc_rad(self.goal, self.pos)) def update(self) -> bool:#"返り値の型名" self.vel += self.vel_tmp self.vel += rotate_vec(np.array([self.VEL, 0]), calc_rad(self.goal, self.pos)) v = np.linalg.norm(self.vel) self.vel =self.vel /v if abs(self.pos + self.vel)[0] > self.SIZE or abs(self.pos + self.vel)[1] > self.SIZE: return False # deactivate this Agent self.pos += self.vel survival[id(self)].append(self.pos) # カウントアップ self.vel_tmp = np.zeros(2) return True class Boids: def __init__(self, AGENT = 20):#Agent数 self.agents = [Agent() for _ in range(AGENT)] def calculate(self): for agent in self.agents: agent.calculate([other for other in self.agents if agent != other]) def update(self): self.agents = [agent for agent in self.agents if agent.update()] def simulation(self): self.calculate() self.update() def positions(self):#showimageはpos_arrayを渡してた x, y, c = list(), list(), list() for agent in self.agents: x.append(agent.pos[0]) y.append(agent.pos[1]) c.append(agent.color) return (np.array(x), np.array(y)), c np.random.seed( 0 )B = Boids(0)num_central=1for i in range(num_central): B.agents.append(AgentEdited())num=100for i in range(num): B.agents.append(AgentGoForWall()) fig, ax = plt.subplots(figsize = (6, 6))ax.set_xlim(-10, 10)ax.set_ylim(-10, 10)ax.grid(True)ims = []for t in range(100): B.simulation() plot_data,plot_color = B.positions() im=ax.scatter(*plot_data, marker="o", s=20, c=plot_color) ims.append([im]) ani = animation.ArtistAnimation(fig, ims, interval=400, repeat=False)HTML(ani.to_jshtml()) for ptr, log in survival.items(): agent = _ctypes.PyObj_FromPtr(ptr) # idから復元 print(agent.name + ": " + len(log))
0 コメント