見出し画像

Pythonで駐車場シミュレーション

Pythonを使って駐車場シミュレーションを作ってみました。

駐車場といっても色々ありますが、今回は自走式の立体駐車場でフラット式といわれるタイプで、各階に向かう車路を簡易に再現したもので、駐車マスまでは再現してません。

よくショッピングモールなどで見かけるタイプです。下の階から順に駐車していく様子を再現します。ちなみにフラットタイプは駐車階が名前のとおり、平になっているタイプです。床が傾斜していて螺旋状の車路の両脇に駐車マスがあるのは、連続傾床式というもので、今回、対象としているものではありません。

<シミュレーション実行画面>
 シミュレーションの実行画面は以下のような感じです。レトロゲームエンジンを使っていることもあり、ちょっとレトロゲームみたいです。左下に入り口があって、右側の赤っぽい四角が各階の入り口(全7階)になります。四角部のブロックの大きさは8mです。いうまでもありませんが、薄紫の四角のものが車です。

画像2

実行環境

PythonとPythonで動作するレトロゲームエンジンのPyxel(ピクセルと読むみたいです。つづりからしてパイセルかと思ってました。^^;)を使用しています。下記を参照してインストールしてみてください。

Pyxelが動くのはPythonのバージョン3.6.8以上なので、私は3.8.0をインストールしました。3.6.8以上なら何でもいいと思います。

Python のインストール

Pyxel サイト

Pyxelはゲームエンジンなので、簡単に図を動かすことができるので、今回のようなシミュレーションを表現するのに、とても便利でした。開発者は日本人の方のようですが、感謝です。

シミュレーションモデルの内容

車両の発生

 交通量は時間交通量(1時間あたりに発生する交通量)で設定し、時間変動は与えていないです。ポアソン分布による発生分布で発生。
 一時間あたり500台発生する設定にしています。
 各階の駐車可能台数は80台としています。

車両の動作

制限速度は10/kmとしています。一般的な駐車場内の制限速度は8~10km/hくらいなので、一般的な設定です。車両の挙動は、前方車両との車間距離、相対速度に応じて、加速度を計算する追従モデルの一種でHellyモデルといわれるモデルを使用しています。

具体的な加速度の計算式は下記のとおりです。

自車の加速度 =  k1 * (車間距離 - DV) + k2*(先行車の速度 - 自車の速度)
DV = d + Tdes * 自車の速度
k1,d2,D,Tdes パラメータ
パラメータは以下のように設定しました。
k1 = 0.6
k2 = 0.2
Tdes = 0.8
d = 10.0

駐車階の選択行動

駐車場利用者が、どの駐車階を選択するかですが、一般的には下の階を優先して利用していくと考えられます。
これは入り口から近い階のほうが、短時間で駐車できるからです。
また、混雑してくれば止めにくくなるので、混雑状況も関係してくると考えられます。
入り口から各階への距離は以下のようになっています。
フロア 距離(m)
1F   232
2F   488
3F   744
4F   1000
5F   1258
6F   1512
7F   1760
入り口からの距離が長くなるにつれて、利用者にとって効用が減ると考えられることから、各階の駐車場を選択するための効用は、距離に応じて下記のようになると考えました。(満車になると選択率はゼロになる)
距離に応じた効用 = Exp(α*距離)
  α:パラメータ(今回はα = -0.003とした。ちなみにαを小さくすると距離が近い方に選択が集中する傾向になります。)
各階の満車率 = 各階の駐車台数 / 各階の駐車マス数
各階の距離・満車率を考慮した効用 = (1.0 - 満車率)*距離に応じた効用

上記から各階への選択率を下記の式で計算しました。
各階の選択率 = 
 各階の距離・満車率を考慮した効用/距離・満車率を考慮した効用の合計

なお、パラメータの設定は適当であり、根拠はありません。

プログラムの構造

Pyxelでは データの更新、描画は下記のサブルーチンで行うようです。
def update(self): 指定したフレームレート(FPS)で 更新
def draw(self):    描画を行う 

これを踏まえ、下記の構成でプログラムを作成しました。
データの初期値の設定                        def __init__(self):
各フレームレートでのデータの更新  def update(self):
描画                                                     def draw(self):

フレームレートは、10FSP(つまり0.1秒ごとに1フレーム)に設定し、シミュレーションは1フレーム1秒で演算されます。

演算結果

演算結果はプログラムファイルと同じフォルダに「simdata.txt」というファイル名で保存されます。算出結果の抜粋を下記に示します。

カンマ区切りで、左から車両番号、駐車階数、入り口に入った時間(秒)、駐車階に入った時間(秒)、所要時間(秒)を示します。

vid,ParkingNo,StartTime,EndTime,TravelTime
101,1,11.0,86.0,75.0
102,1,26.0,101.0,75.0
103,1,41.0,116.0,75.0
104,1,44.0,119.0,75.0
105,1,79.0,154.0,75.0
106,1,87.0,157.0,70.0
107,1,93.0,161.0,68.0
108,1,99.0,165.0,66.0
109,1,101.0,169.0,68.0

下図は、この演算結果を元に10分間ごとにどの駐車階に入ったかをグラフ化したものです。(エクセルで集計)

下記に示すように1階から上の階に順に駐車している様子がわかります。

画像2

駐車階ごとの平均所要時間を算出すると下記のとおりです。

駐車階 平均所要時間(分)
1階   1.2
2階   2.6
3階   4.1
4階   5.6
5階   7.0
6階   -
7階   -

プログラムコード

プログラムコードを下記に示します。Pythonはあまりなれていないので、おかしな点があるかもしれませんが、ご容赦ください。

下記、プログラムは、ご自由に活用していただいて構いませんが、無断転載はお断りします。

import pyxel
import math
import random
import numpy as np
import copy
class Vehicle:
	def __init__(self,vid,length,LinkID,pos,Speed,Route,Endflg,Stime,Etime,ParkingNo):
		self.vid = vid # 車両ID
		self.length = length # 車長(m)
		self.LinkID = LinkID # 走行中のリンクID
		self.pos = pos       # 走行中のリンク上の位置(m)
		self.Speed = Speed   # 車両走行速度(km/h)
		self.Route = Route   # 車両の走行速度
		self.Endflg = Endflg # 最後まで走行したかを示すフラグ
		self.ParkingNo = ParkingNo # 入庫した駐車階
		self.Stime = Stime # スタートした時間(秒)
		self.Etime = Etime # ゴールした時間(秒)
class Node:
	def __init__(self,nid,x,y,ntype,flg):
		self.nid = nid # ノードID
		self.x  = x # X座標
		self.y  = y # Y座標
		self.ntype = ntype #  1 通路 2:入口 3 分岐部との接続 4 駐車場 5:分岐部
		self.flg = flg # フラグ(リンクの自動生成のために使用)
class Link:
	def __init__(self,lid,n1,n2,leng):
		self.lid = lid # リンクID
		self.n1  = n1 # 上流側ノードID
		self.n2  = n2 # 下流側ノードID
		self.leng  = leng # リンクの長さ
class App:
	def __init__(self):
		def PoissonDistribution(R,AVG,SD): # R:発生回数 AVG:発生間隔 SD:ランダムシード
			random.seed(SD)
			Val = []
			TA = 0
			DT = 1
			n = 0
			while n < R:
				rn = random.randint(0,99)*0.01
				if rn != 0:
					TA = int(-AVG * math.log(rn)+0.999)
					Val.append(TA)
					n = n + 1
			return Val
		self.f = open('simdata.txt', 'w')
		self.PI = 3.14159265359 # 円周率
		self.psize = 8  # リンクの距離
		self.Fps   = 10 # FPS(フレーム/秒)
		pyxel.init(248,216,fps = self.Fps)
		self.Time = 0
		self.Simtime    = 3600 # 車両が発生する時間(秒)
		self.SimtimeALL = 4200 # シミュレーション実行時間(秒)
		self.TimeStep = 1.0 # シミュレーションタイムステップ(秒)
		self.Quantity = 400 # 交通量(台/時)
		self.ParkingCapcity = 80 # 1フロアごとの駐車台数(台)
		self.k1 = 0.6        # 追従モデルパラメータ k1
		self.k2 = 0.2        # 追従モデルパラメータ k2
		self.Tdes = 0.8      # 追従モデルパラメータ Tdes
		self.d = 10.0         # 追従モデルパラメータ d
		self.alpha = -0.003  # 効用関数パラメータ
		self.Speed = 10.0    # 制限速度(km/h)
		self.wsize = 31      # マップサイズ横
		self.hsize = 27      # マップサイズ縦
		self.MaxHeight = self.hsize*self.psize
		self.nodelist = []   # ノードリスト
		self.linklist = []   # リンクリスト
		self.PDis = []       # 車両発生フラグ
		self.VID = 100       # 車両IDの初期値
		self.VehicleQueue = [] # 待ち行列の配列
		self.MovingVehicle = [] # 走行している車両の配列
		self.CountCar = []      # 駐車台数
		self.ParkingPos = []    # 駐車場位置
		self.ParkingLinkID = [] # 各駐車階入口リンク
		self.BLinkID = [] # 分岐部のリンク
		self.PRate = [] # 満車率
		self.TC    = [] # 距離による効用
		self.ATC   = [] # 効用関数
		self.TRate = [] # 選択率
		self.BRate = [] # 分岐率
		PD = []
		PDS = []
		PT = 0
		SD = 3278 # ランダムシード
		T = self.Simtime / 3600
		R = self.Quantity * T  # シミュレーション時間内に発生する車両台数
		AVG = self.Simtime / R # 平均車両発生間隔
		PD = PoissonDistribution(R,AVG,SD) # 車両発生間隔の分布(何秒間隔で車両が発生するか)
		for i in range(len(PD)):
			if PD[i] != 0:
				PT = PT + PD[i]
				PDS.append(PT)
		for i in range(int(self.Simtime)): # PDの発生分布を1秒ごとの車両の発生フラグを作成
			self.PDis.append(0)
			for j in range(len(PDS)):
				if PDS[j] == i:
					self.PDis[i] = 1 # 
		nid = 0
		nx  = 0
		ny  = 0
		lid = 0
		hl = int(pyxel.height/self.psize)
		wl = int(pyxel.width/self.psize)
# 横31 x 縦23 のマップ(図化のイメージとは上下逆になるので注意) 1 通路 2:入口 3 分岐部との接続 4 駐車場 5:分岐部
		self.maplist = [
					[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,3,1,1,1,1,1,1,1,1,1,1,4,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,3,1,1,1,1,1,1,1,1,1,1,4,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,3,1,1,1,1,1,1,1,1,1,1,4,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,3,1,1,1,1,1,1,1,1,1,1,4,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,3,1,1,1,1,1,1,1,1,1,1,4,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,3,1,1,1,1,1,1,1,1,1,1,4,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
					[0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,0],
					[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
					]
		for i in range(self.wsize):
			for j in range(self.hsize):
				if self.maplist[j][i] == 1:
					nid = nid + 1
					nx = self.psize*i + self.psize*0.5
					ny = self.psize*j + self.psize*0.5
					node = Node(nid,nx,ny,1,0)
					self.nodelist.append(node)
				if self.maplist[j][i] == 2:
					nid = nid + 1
					nx = self.psize*i + self.psize*0.5
					ny = self.psize*j + self.psize*0.5
					node = Node(nid,nx,ny,2,0)
					self.nodelist.append(node)
				if self.maplist[j][i] == 3:
					nid = nid + 1
					nx = self.psize*i + self.psize*0.5
					ny = self.psize*j + self.psize*0.5
					node = Node(nid,nx,ny,3,0)
					self.nodelist.append(node)
				if self.maplist[j][i] == 4:
					nid = nid + 1
					nx = self.psize*i + self.psize*0.5
					ny = self.psize*j + self.psize*0.5
					node = Node(nid,nx,ny,4,0)
					self.nodelist.append(node)
				if self.maplist[j][i] == 5:
					nid = nid + 1
					nx = self.psize*i + self.psize*0.5
					ny = self.psize*j + self.psize*0.5
					node = Node(nid,nx,ny,5,0)
					self.nodelist.append(node)
# 起点からスタートするリンクの生成
		self.Route = []
		le = 0
		for i in range(len(self.nodelist)):
			if self.nodelist[i].ntype == 2: # 入り口ノードの場合
				snode = self.nodelist[i] # 入り口ノード
				node = Node(self.nodelist[i].nid,self.nodelist[i].x,self.nodelist[i].y,self.nodelist[i].ntype,1)
				self.nodelist[i].flg = 1
		sn = snode
		a = 0
		while a == 0:
			a = 1
			for j in range(len(self.nodelist)):
				le = math.sqrt((sn.x - self.nodelist[j].x)**2 + (sn.y - self.nodelist[j].y)**2)
				if (le == self.psize) and (self.nodelist[j].flg == 0) and (self.nodelist[j].ntype != 3):# 距離がself.psizeで分岐を経由しないルート
					lid = lid + 1
					link = Link(lid,sn.nid,self.nodelist[j].nid,le)
					self.linklist.append(link)
					node = Node(self.nodelist[j].nid,self.nodelist[j].x,self.nodelist[j].y,self.nodelist[j].ntype,1)
					self.nodelist[j].flg = 1
					sn = node
					a = 0
					self.Route.append(copy.copy(link))	# 本線のルート(リンクの配列)
# 分岐点からスタートするリンクの生成
		le = 0
		self.bnode = [] # 分岐ルートのノード配列
		for i in range(len(self.nodelist)):
			if self.nodelist[i].ntype == 5: # 分岐点のノードだったら
				self.bnode.append(self.nodelist[i])
		self.BRoute = [] # 分岐ルートのリンク配列
		for i in range(len(self.bnode)):
			bn = self.bnode[i]
			br = []
			a = 0
			while a == 0:
				a = 1
				for j in range(len(self.nodelist)):
					le = math.sqrt((bn.x - self.nodelist[j].x)**2 + (bn.y - self.nodelist[j].y)**2)
					if (le == self.psize) and (self.nodelist[j].flg == 0):
						lid = lid + 1
						link = Link(lid,bn.nid,self.nodelist[j].nid,le)
						self.linklist.append(link)
						node = Node(self.nodelist[j].nid,self.nodelist[j].x,self.nodelist[j].y,self.nodelist[j].ntype,1)
						self.nodelist[j].flg = 1
						bn = node
						a = 0
						br.append(copy.copy(link))
			self.BRoute.append(br)	# 分岐先のルート
		bs = 0
		for i in range(len(self.Route)):
			bs = bs + self.Route[i].leng
		bl = 0
		self.blength = [] # 各階への距離
		for i in range(len(self.Route)):
			nf = 0
			for j in range(len(self.nodelist)):
				if self.Route[i].n2 == self.nodelist[j].nid:
					nf = self.nodelist[j].ntype
			bl = bl + self.Route[i].leng
			if nf == 5:
				self.blength.append(bl)
		for i in range(len(self.BRoute)):
			bl = 0
			for j in range(len(self.BRoute[i])):
				b = self.BRoute[i]
				bl = bl + b[j].leng
			self.blength[i] = self.blength[i] + bl
		self.blength.append(bs)
		for i in range(len(self.nodelist)):
			if self.nodelist[i].ntype == 5:
				Blid = 0
				for j in range(len(self.linklist)):
					if self.nodelist[i].nid == self.linklist[j].n2:
						Blid = self.linklist[j].lid
				self.BLinkID.append(Blid)
			if self.nodelist[i].ntype == 4:
				self.CountCar.append(0)
				self.PRate.append(0)
				self.TC.append(0)
				self.ATC.append(0)
				self.TRate.append(0)
				self.BRate.append(0)
				self.ParkingPos.append(copy.copy(self.nodelist[i])) # 駐車場位置
				Plid = 0
				for j in range(len(self.linklist)):
					if self.nodelist[i].nid == self.linklist[j].n2:
						Plid = self.linklist[j].lid
				self.ParkingLinkID.append(Plid)
		self.f.write('vid,ParkingNo,StartTime,EndTime,TravelTime\n') # シミュレーション結果出力のヘッダ
		pyxel.run(self.update,self.draw)
	def update(self):
		AT = 0.0
		for i in range(len(self.CountCar)):
			self.PRate[i] = self.CountCar[i]/self.ParkingCapcity # 満車率
			if self.PRate[i] > 1.0:
				self.PRate[i] = 1.0
			self.TC[i]  =  math.exp(self.alpha * self.blength[i]) # 距離に応じた効用(距離が短いほど高い)
			self.ATC[i]   = (1.0 - self.PRate[i]) * self.TC[i] # 満車率・距離を踏まえた効用(距離が近い・満車率が低いほど高い)
			AT = AT + self.ATC[i] # 効用の合計
		for i in range(len(self.CountCar)):
			if AT != 0:
				self.TRate[i] = self.ATC[i] / AT # 各駐車階の選択率
			else:
				self.TRate[i] = 0.0
		for i in range(len(self.CountCar)):
			if i == 0:
				self.BRate[i] = self.TRate[i]
			else:
				if self.TRate[i-1] != 0:
					self.BRate[i] = self.TRate[i]/self.TRate[i-1] # 分岐率
				else:
					self.BRate[i] = self.TRate[i]
		sx = 0
		sy = 0
		self.Time = pyxel.frame_count * self.TimeStep
		slid = self.linklist[0].lid # 起点部のリンク番号
		if pyxel.btnp(pyxel.KEY_Q):
			pyxel.quit()
		if self.Time > self.SimtimeALL:
			pyxel.quit()
		for i in range(len(self.PDis)): # 交通量の発生
			if (int(self.Time) == i) and (self.PDis[i] == 1):
				sx = self.nodelist[0].x
				sy = self.nodelist[0].y
				self.VID = self.VID + 1
				vehicle = Vehicle(self.VID,4.5,0,0,self.Speed,self.Route,0,-99,-99,-99)
				self.VehicleQueue.append(vehicle) # 車両が発生したら待ち行列の配列にいれる
		gflg = 0
		for i in range(len(self.MovingVehicle)): # 起点にリンク上に車両がいるかをチェック
			if slid == self.MovingVehicle[i].LinkID:
				gflg = 1
		if (gflg == 0) and (len(self.VehicleQueue) > 0): # 起点リンク上に車両がいない
			R = copy.copy(self.Route)
			Vin = Vehicle(self.VehicleQueue[0].vid,self.VehicleQueue[0].length,slid,0,self.VehicleQueue[0].Speed,R,0,self.Time,-99,-99)
			if len(Vin.Route) != 0:
				Vin.Route.pop(0)
			self.MovingVehicle.append(Vin)
			if len(self.VehicleQueue) != 0:
				self.VehicleQueue.pop(0) # 起点リンクに車両が流入したら待ち行列から削除する
		for i in range(len(self.MovingVehicle)):
			for k in range(len(self.BLinkID)): # 分岐処理
				B = self.BRate[k]*100 # 分岐率(%)
				rn = random.randint(0,99) # 0~100の乱数
				if B > rn: # 分岐率 > 乱数
					if len(self.MovingVehicle[i].Route) >= 2: # 駐車場への分岐
						if self.MovingVehicle[i].Route[1].lid == self.BLinkID[k]:
							rnum = len(self.MovingVehicle[i].Route)
							self.MovingVehicle[i].Route[2:rnum] = [] # 上層階のルートを削除
							for m in range(len(self.BRoute[k])): # 駐車場に向かうルートを追加
								self.MovingVehicle[i].Route.append(self.BRoute[k][m])
			dp = -99
			fv = self.Speed
			for j in range(len(self.MovingVehicle)):
				if self.MovingVehicle[i].vid != self.MovingVehicle[j].vid:# 先行車のサーチ
					vlen = self.MovingVehicle[j].length*0.5 -self.MovingVehicle[i].length*0.5
					dflg = 0
					if self.MovingVehicle[i].LinkID == self.MovingVehicle[j].LinkID:# 同じリンクに他車がいる
						if self.MovingVehicle[i].pos < self.MovingVehicle[j].pos: # 他車が前にいる
							dp = self.MovingVehicle[j].pos - self.MovingVehicle[i].pos - vlen # 車間距離
							dflg = 1
							fv = self.MovingVehicle[j].Speed # 先行車の速度
					if (dflg == 0) and (len(self.MovingVehicle[i].Route) >= 2):
						if self.MovingVehicle[i].Route[1].lid == self.MovingVehicle[j].LinkID:# 一つ先のリンクに他車がいる
							l1 = self.MovingVehicle[i].Route[1].leng
							dp = self.MovingVehicle[j].pos - self.MovingVehicle[i].pos + l1 - vlen # 車間距離
							dflg = 1
							fv = self.MovingVehicle[j].Speed # 先行車の速度
					if (dflg == 0) and len(self.MovingVehicle[i].Route) >= 3:
						if self.MovingVehicle[i].Route[2].lid == self.MovingVehicle[j].LinkID:# 2つ先のリンクに他車がいる
							l1 = self.MovingVehicle[i].Route[1].leng + self.MovingVehicle[i].Route[0].leng
							dp = self.MovingVehicle[j].pos - self.MovingVehicle[i].pos + l1 - vlen # 車間距離
							dflg = 1
							fv = self.MovingVehicle[j].Speed # 先行車の速度
					if (dflg == 0) and len(self.MovingVehicle[i].Route) >= 4:
						if self.MovingVehicle[i].Route[3].lid == self.MovingVehicle[j].LinkID:# 3つ先のリンクに他車がいる
							l1 = self.MovingVehicle[i].Route[2].leng + self.MovingVehicle[i].Route[1].leng + self.MovingVehicle[i].Route[0].leng
							dp = self.MovingVehicle[j].pos - self.MovingVehicle[i].pos + l1 - vlen # 車間距離
							dflg = 1
							fv = self.MovingVehicle[j].Speed # 先行車の速度
					if (dflg == 0) and len(self.MovingVehicle[i].Route) >= 5:
						if self.MovingVehicle[i].Route[4].lid == self.MovingVehicle[j].LinkID:# 4つ先のリンクに他車がいる
							l1 = self.MovingVehicle[i].Route[3].leng + self.MovingVehicle[i].Route[2].leng + self.MovingVehicle[i].Route[1].leng + self.MovingVehicle[i].Route[0].leng
							dp = self.MovingVehicle[j].pos - self.MovingVehicle[i].pos + l1 - vlen # 車間距離
							dflg = 1
							fv = self.MovingVehicle[j].Speed # 先行車の速度
			acc = 0
			if dp > 0:
				yd = (fv - self.MovingVehicle[i].Speed)/3.6
				dv = self.d + self.Tdes * self.MovingVehicle[i].Speed/3.6
				acc = self.k1 * (dp - dv) + self.k2 * (fv - self.MovingVehicle[i].Speed)/3.6
			else:
				acc = 1.0 # 先頭車両の場合
			if self.MovingVehicle[i].Speed <= self.Speed:
				self.MovingVehicle[i].Speed = self.MovingVehicle[i].Speed + acc * self.TimeStep
				if self.MovingVehicle[i].Speed < 0:
					self.MovingVehicle[i].Speed = 0.0
			else:
				self.MovingVehicle[i].Speed = self.Speed
			lp  = self.MovingVehicle[i].pos + (self.MovingVehicle[i].Speed / 3.6)
			lid = self.MovingVehicle[i].LinkID
			if len(self.MovingVehicle[i].Route) >= 1:
				nextlink1 = self.MovingVehicle[i].Route[0]
			if len(self.MovingVehicle[i].Route) >= 2:
				nextlink2 = self.MovingVehicle[i].Route[1]
			leng1 = 0.0
			leng2 = 0.0
			if len(self.MovingVehicle[i].Route) >= 2:
				leng1 = nextlink1.leng
				leng2 = nextlink2.leng
				if lp < leng1:
					self.MovingVehicle[i].pos = lp
				elif lp < (leng1 + leng2):
					self.MovingVehicle[i].pos = lp - leng1
					self.MovingVehicle[i].LinkID = nextlink2.lid
					self.MovingVehicle[i].Route.pop(0)
			else:
				self.MovingVehicle[i].Endflg = 1
		for i in range(len(self.MovingVehicle)):
			for j in range(len(self.ParkingLinkID)):
				if (self.MovingVehicle[i].LinkID == self.ParkingLinkID[j]) and (self.MovingVehicle[i].Endflg == 1):
					self.CountCar[j] = self.CountCar[j] + 1
					self.MovingVehicle[i].Etime = self.Time
					TraTim = self.MovingVehicle[i].Etime - self.MovingVehicle[i].Stime
					self.MovingVehicle[i].ParkingNo = j+1
					# シミュレーション結果出力
					self.f.write(str(self.MovingVehicle[i].vid) + ',' + str(self.MovingVehicle[i].ParkingNo) +',' + str(self.MovingVehicle[i].Stime) + ',' + str(self.MovingVehicle[i].Etime) + ',' + str(TraTim)+'\n')
		for i in reversed(range(len(self.MovingVehicle))):
			if self.MovingVehicle[i].Endflg == 1:
				del self.MovingVehicle[i]
	def draw(self): # 画面の描画
		class VPoint: # 車両の座標、角度の情報を格納する変数
			def __init__(self,x,y,ang):
				self.x = x # X座標
				self.y = y # Y座標
				self.ang = ang # 角度
		def VehiclePos(LinkID,pos): # LinkID、posを入力値として、車の座標、角度を求める
			px  = 0.0
			py  = 0.0
			n1x = 0.0
			n1y = 0.0
			n2x = 0.0
			n2y = 0.0
			ang = 0.0
			for i in range(len(self.linklist)):
				if LinkID == self.linklist[i].lid:
					np1 = self.linklist[i].n1
					np2 = self.linklist[i].n2
					for j in range(len(self.nodelist)):
						if np1 == self.nodelist[j].nid:
							n1x = self.nodelist[j].x
							n1y = self.nodelist[j].y
						if np2 == self.nodelist[j].nid:
							n2x = self.nodelist[j].x
							n2y = self.nodelist[j].y
					r = math.sqrt((n2x - n1x)** 2 + (n2y - n1y)**2)
					ang = math.atan2((n2y - n1y),(n2x - n1x))
					px = n1x + pos * math.cos(ang)
					py = n2y + pos * math.sin(ang)
			Vp = VPoint(px,py,ang)
			return Vp
		pyxel.cls(0) # 画面クリア
		pyxel.rect(0,0,pyxel.width,pyxel.height,13) # 背景描画
		hl = int(pyxel.height/self.psize)
		wl = int(pyxel.width/self.psize)
		for i in range(hl): # 横線の描画
			pyxel.line(0,self.psize*i,pyxel.width,self.psize*i,4)
		for i in range(wl): # 縦線の描画
			pyxel.line(self.psize*i,0,self.psize*i,pyxel.height,4)
		for i in range(self.wsize): # ノード属性を示すピクセルを描画
			for j in range(self.hsize):
				if self.maplist[j][i] == 1: # 通路
					pyxel.rect(self.psize*i,self.psize*(self.hsize - j -1),self.psize,self.psize,1)
				if self.maplist[j][i] == 2: # 入口
					pyxel.rect(self.psize*i,self.psize*(self.hsize - j -1),self.psize,self.psize,2)
				if self.maplist[j][i] == 3: # 分岐部との接続
					pyxel.rect(self.psize*i,self.psize*(self.hsize - j -1),self.psize,self.psize,3)
				if self.maplist[j][i] == 4: # 駐車場
					pyxel.rect(self.psize*i,self.psize*(self.hsize - j -1),self.psize,self.psize,4)
				if self.maplist[j][i] == 5: # 分岐部
					pyxel.rect(self.psize*i,self.psize*(self.hsize - j -1),self.psize,self.psize,5)
		for i in range(len(self.linklist)): # リンクの描画
			p1x = 0
			p1y = 0
			p2x = 0
			p2y = 0
			for j in range(len(self.nodelist)):
				if self.linklist[i].n1 == self.nodelist[j].nid:
					p1x = self.nodelist[j].x
					p1y = self.nodelist[j].y
				if self.linklist[i].n2 == self.nodelist[j].nid:
					p2x = self.nodelist[j].x
					p2y = self.nodelist[j].y
			pyxel.line(p1x,self.MaxHeight - p1y,p2x,self.MaxHeight - p2y,7)
		for i in range(len(self.MovingVehicle)): # 車の描画
			lid = self.MovingVehicle[i].LinkID
			P =   self.MovingVehicle[i].pos
			VL =  self.MovingVehicle[i].length
			V = VehiclePos(lid,P)
			X = V.x
			Y = V.y
			FX = X + 3 * math.cos(V.ang)
			FY = Y + 3 * math.sin(V.ang)
			BX = X - 3 * math.cos(V.ang)
			BY = Y - 3 * math.sin(V.ang)
			F1x = FX + VL*0.5 * math.cos(V.ang + self.PI*0.5)
			F1y = FY + 2 * math.sin(V.ang + self.PI*0.5)
			F2x = FX + VL*0.5 * math.cos(V.ang - self.PI*0.5)
			F2y = FY + 2 * math.sin(V.ang - self.PI*0.5)
			B1x = BX + VL*0.5 * math.cos(V.ang + self.PI*0.5)
			B1y = BY + 2 * math.sin(V.ang + self.PI*0.5)
			B2x = BX + VL*0.5 * math.cos(V.ang - self.PI*0.5)
			B2y = BY + 2 * math.sin(V.ang - self.PI*0.5)
			pyxel.tri(F1x,self.MaxHeight - F1y,F2x,self.MaxHeight - F2y,B2x,self.MaxHeight - B2y,6) # 三角形を二つで車両を表現
			pyxel.tri(B2x,self.MaxHeight - B2y,B1x,self.MaxHeight - B1y,F1x,self.MaxHeight - F1y,6)
		s = str(self.Time)
		pyxel.text(1,1,s,1)
		for i in range(len(self.CountCar)):
			s= str(self.CountCar[i])
			pyxel.text(self.ParkingPos[i].x,self.MaxHeight - self.ParkingPos[i].y,s,1) # 駐車台数を表示
App()

この記事が気に入ったらサポートをしてみませんか?