Created
November 18, 2015 23:51
-
-
Save peace098beat/f88cd7d2639a70cc9b37 to your computer and use it in GitHub Desktop.
[機械学習] 車のレース. GUI付き。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #! coding:utf-8 | |
| """ | |
| CircleRace main.py | |
| サークルレースゲーム | |
| (2015/11/19) 4:47 ver0.1作成 | |
| Created by 0160929 on 2015/11/19 7:28 | |
| """ | |
| __version__ = '0.1' | |
| import sys | |
| import numpy as np | |
| # PySide系モジュール | |
| from PySide.QtGui import * | |
| from PySide.QtCore import * | |
| # logを保存 | |
| # -------------------- | |
| from datetime import datetime | |
| import csv | |
| def debuglog(s=None, data=None): | |
| d = datetime.now().isoformat() | |
| with open('log.csv', 'a') as f: | |
| writer = csv.writer(f, lineterminator='\n') | |
| if not s is None: | |
| writer.writerow([d, s]) | |
| print d, s | |
| if not data is None: | |
| writer.writerow([d, data]) | |
| print d, data | |
| class Car(object): | |
| """ | |
| 車オブジェクト | |
| 自車の速度・位置の情報を持つ | |
| """ | |
| SENCER_LENGTH = 10. | |
| def __init__(self, iniPos=[0, 0], iniVelocity=[1, 1], sencer_angle=30): | |
| # -- 自車情報 ---------------------- | |
| self.position = np.array([0, 0]) | |
| # velocityはnorm=1ではない | |
| self.velocity = np.array([0, 0]) | |
| self.velocity_n = np.array([0, 0]) | |
| self.speed = 1 | |
| # -- 初期値代入 --------------------- | |
| if not iniPos is None: | |
| _iniPos = np.asarray(iniPos) | |
| self.position = _iniPos | |
| if not iniVelocity is None: | |
| _iniVelocity = np.asarray(iniVelocity) | |
| self.velocity_n = self.vectorNormalize(_iniVelocity) | |
| self.velocity = self.velocity_n * self.speed | |
| # -- センサの情報------------------- | |
| self.sencerL_angle_deg = sencer_angle | |
| self.sencerR_angle_deg = -1. * self.sencerL_angle_deg | |
| self.sencer_length = self.SENCER_LENGTH | |
| self.sencerF = np.array([0, 0]) | |
| self.sencerL = np.array([0, 0]) | |
| self.sencerR = np.array([0, 0]) | |
| self.sencerF_val = 0.0 | |
| self.sencerL_val = 0.0 | |
| self.sencerR_val = 0.0 | |
| # -- センサの更新 ------------------ | |
| self.calc_sencer() | |
| # -- ゲームの情報------------------- | |
| self.reward = 0 | |
| # -- その他 ------------------------ | |
| self.deltaT = 1 | |
| def update(self, deg=0, speed=1): | |
| """ | |
| 車の次の位置を予想し更新 | |
| (※ 等速運動仮定) | |
| :param theta: 回転角度 [deg] | |
| """ | |
| self.speed = speed | |
| # -- 位置速度計算 -------------------------------------- | |
| # (ノルム計算がややこしい. 速度ノルムは常に1を確保する必要あり) | |
| new_velocity_n = np.dot(self.rotate(deg), self.velocity_n.T) | |
| self.velocity = new_velocity_n * self.speed | |
| self.velocity_n = new_velocity_n.copy() | |
| self.position = self.position + self.velocity * self.deltaT | |
| # -- debug.log ----------------------------------------- | |
| s = 'pos:[%0.1f, %0.1f], vel:[%0.1f, %0.1f], vel:%d' % ( | |
| self.position[0], self.position[1], self.velocity[0], self.velocity[1], self.speed) | |
| debuglog(s) | |
| # -- センサ情報の計算 ---------------------------------- | |
| self.calc_sencer() | |
| def calc_sencer(self): | |
| """ | |
| センサのヴェクトルを更新 | |
| フロントセンサは速度ベクトルと同じ向き | |
| 大きさは, SENCER_LENGTH倍 | |
| :return: | |
| """ | |
| self.sencerF = self.velocity_n.copy() * self.sencer_length | |
| self.sencerL = np.dot(self.rotate(self.sencerL_angle_deg), self.sencerF.T) | |
| self.sencerR = np.dot(self.rotate(self.sencerR_angle_deg), self.sencerF.T) | |
| # -- debug.log ----------------------------------------- | |
| s = 'sencerF:[%0.1f, %0.1f], sencerL:[%0.1f, %0.1f], sencerR:[%0.1f, %0.1f]' % ( | |
| self.sencerF[0], self.sencerF[1], self.sencerL[0], self.sencerL[1], self.sencerR[0], self.sencerR[1],) | |
| debuglog(s) | |
| def rotate(self, deg): | |
| """ | |
| 回転行列 | |
| :param theta: 回転角度 [deg] | |
| """ | |
| rad = np.deg2rad(deg) | |
| return np.array([[np.cos(rad), -np.sin(rad)], | |
| [np.sin(rad), np.cos(rad)]]) | |
| def set_pos(self, pos): | |
| """ | |
| 車の位置を再度セット | |
| """ | |
| self.position = pos.copy() | |
| # -- debug.log ----------------------------------------- | |
| s = 'reset>> pos:[%0.1f, %0.1f], vel:[%0.1f, %0.1f], vel:%d' % ( | |
| self.position[0], self.position[1], self.velocity[0], self.velocity[1], self.speed) | |
| debuglog(s) | |
| def vectorNormalize(self,_vec): | |
| vec = np.asarray(_vec) | |
| return vec / np.linalg.norm(vec) | |
| class CircleRace(object): | |
| """ | |
| サークルレースゲームオブジェクト | |
| 車の状態更新、フィールド情報、得点等をコントロール | |
| """ | |
| STEER_ANGLES = [10, 20, 30, 40, 90] | |
| REWARD_CRASH = -1.0 | |
| SENCER_RESOLUTION = 10 # センサ解像度(何分割するか) | |
| def __init__(self, area_width, area_height): | |
| # -- 領域情報 --- ----------------------------- | |
| self.area_width = area_width | |
| self.area_height = area_height | |
| self.xlim = [0, self.area_width] | |
| self.ylim = [0, self.area_height] | |
| # -- オブジェクト ----------------------------- | |
| initial_pos = [self.area_width / 2., self.area_height / 2.] | |
| self.car = Car(iniPos=initial_pos, iniVelocity=[1, 1], sencer_angle=10) | |
| def update_game(self, steer='forward', steer_level=0, speed=1): | |
| """ | |
| ゲーム更新 | |
| :param steer_angle: 車の回転角度 [deg] | |
| :param steer_level: 車の回転角度のレベル | |
| """ | |
| # -- t時刻の位置・速度を保管 ------------ | |
| old_pos = self.car.position.copy() | |
| old_vel = self.car.velocity.copy() | |
| # -- 角度の計算(levelからdegreeに変更) -- | |
| if steer is 'left': | |
| steer_angle = self.STEER_ANGLES[steer_level] | |
| elif steer is 'right': | |
| steer_angle = (-1.) * self.STEER_ANGLES[steer_level] | |
| else: | |
| steer_angle = 0 | |
| # -- 車の状態更新 ------------------------ | |
| self.car.update(deg=steer_angle, speed=speed) | |
| new_pos = self.car.position.copy() | |
| new_vel = self.car.velocity.copy() | |
| # -- 衝突判定 ---------------------------- | |
| if self.isCrash(pos=new_pos): | |
| # -- 位置のみ前時刻に戻す -------- | |
| self.car.set_pos(old_pos.copy()) | |
| # -- はねかえす ------------------ | |
| self.car.set_pos(old_pos - (1. / 2.) * old_vel * self.car.deltaT) | |
| # -- 報酬の支払い ---------------- | |
| self.car.reward = self.REWARD_CRASH | |
| debuglog(s='reward:%0.1f' % self.car.reward) | |
| # -- センサ値の更新 ---------------------- | |
| self.car.sencerF_val = self.calc_sencer_value(self.car.sencerF) | |
| self.car.sencerR_val = self.calc_sencer_value(self.car.sencerR) | |
| self.car.sencerL_val = self.calc_sencer_value(self.car.sencerL) | |
| def calc_sencer_value(self, sencer_vec): | |
| """ | |
| センサ値の計算 | |
| :param sencer: (ndarry) センサの方向ベクトル | |
| :return: センサ値 {0~1}={1:reso}/reso | |
| """ | |
| sencer_value = 0 | |
| # -- センサ値の衝突判定 ------------------ | |
| for i in range(self.SENCER_RESOLUTION, -1, -1): | |
| ratio = i / self.SENCER_RESOLUTION | |
| pos = sencer_vec * ratio | |
| if not self.isCrash(pos): | |
| sencer_value = ratio | |
| break | |
| return sencer_value | |
| def isCrash(self, pos): | |
| """ | |
| 車が壁に衝突したか判定 | |
| :param pos: (ndarry) 位置 | |
| :return: (bool) 衝突:true, 以外:false | |
| """ | |
| x, y = [0, 1] | |
| xlim_min, xlim_max = self.xlim | |
| ylim_min, ylim_max = self.ylim | |
| if pos[x] < xlim_min or xlim_max < pos[x]: | |
| return True | |
| if pos[y] < ylim_min or ylim_max < pos[y]: | |
| return True | |
| return False | |
| # 描画用PySideクラス | |
| class GameWindow(QWidget): | |
| # -- 定数(画面サイズ) ----------------- | |
| SCREEN_HEIGHT = 500 | |
| SCREEN_WIDTH = 700 | |
| MARGIN_HEIGHT = 50 | |
| MARGIN_WIDTH = 50 | |
| STAGE_HEIGHT = SCREEN_HEIGHT - 2 * MARGIN_HEIGHT | |
| STAGE_WIDTH = SCREEN_WIDTH - 2 * MARGIN_WIDTH | |
| # -- 定数(タイマー) ------------------- | |
| INTERVAL_TIME = 0.01 | |
| # -- 定数(その他) --------------------- | |
| SIZE_CAR = 50 | |
| def __init__(self, parent=None): | |
| QWidget.__init__(self, parent) | |
| self.resize(self.SCREEN_WIDTH, self.SCREEN_HEIGHT) | |
| # -- 画面バッファ -------------------- | |
| self.pixmap = QPixmap(self.size()) | |
| # -- 解析用オブジェクト -------------- | |
| self.game = CircleRace(area_width=self.STAGE_WIDTH, area_height=self.STAGE_HEIGHT) | |
| # -- 操作用変数 ---------------------- | |
| self.car_steer = 'forward' | |
| self.car_speed =1 | |
| self.car_steer_level = 1 | |
| # -- 初期画面の準備 ------------------ | |
| # 画面バッファの初期化 | |
| self.refreshPixmap() | |
| # グリッドの表示 | |
| painter = QPainter(self.pixmap) | |
| self.drawGrid(painter) | |
| # メインループの準備と開始 | |
| # ------------------------- | |
| if True: | |
| self.timer = QTimer() | |
| self.timer.timeout.connect(self.mainloop) | |
| self.timer.start(self.INTERVAL_TIME) | |
| self.mainloop() | |
| # ************************************************************* # | |
| # メインループ | |
| # ************************************************************* # | |
| def mainloop(self): | |
| """ | |
| アニメーションのメインループ | |
| アルゴリズムの時間更新等はここで行う | |
| """ | |
| # -- アルゴリズム処理----------- | |
| self.game.update_game(steer=self.car_steer, steer_level=1, speed=self.car_speed) | |
| self.car_steer=None | |
| # -- 描画準備 ------------------ | |
| painter = QPainter(self.pixmap) | |
| self.drawGrid(painter) | |
| self.drawGeoPoints(painter) | |
| # 画面更新 | |
| self.update() | |
| def paintEvent(self, *args, **kwargs): | |
| print 'paintEvent' | |
| # QPainterを生成 | |
| painter = QStylePainter(self) | |
| # QPainterでバッファに準備したデータを描画 | |
| painter.drawPixmap(0, 0, self.pixmap) | |
| # -- 車両位置をプロット | |
| x, y = 0, 1 | |
| _pos = self.game.car.position.copy() | |
| pos = self.locateXY(_pos) | |
| _vel = self.game.car.velocity.copy() | |
| vel = self.locateVec(_vel) | |
| vel_n = vel / np.linalg.norm(vel) | |
| print 'paintEvent: ', pos | |
| painter.drawPoint(pos[x], pos[y]) | |
| painter.drawEllipse(pos[x] - self.SIZE_CAR / 2, pos[y] - self.SIZE_CAR / 2, self.SIZE_CAR, self.SIZE_CAR) | |
| painter.drawLine(pos[x], pos[y], (pos[x] + vel[x]), (pos[y] + vel[y])) | |
| # ************************************************************* # | |
| # 描画系補助関数 | |
| # ************************************************************* # | |
| def drawDebugLog(self, painter): | |
| pass | |
| def drawGeoPoints(self, painter): | |
| pass | |
| def drawGrid(self, painter): | |
| """ マップを表示する関数 | |
| """ | |
| for x in range(self.STAGE_WIDTH): | |
| for y in range(self.STAGE_HEIGHT): | |
| # painter.drawPoint(x,y) | |
| pass | |
| painter.drawRect(self.MARGIN_WIDTH, self.MARGIN_HEIGHT, self.STAGE_WIDTH, self.STAGE_HEIGHT) | |
| pass | |
| def locateXY(self, pos): | |
| """ | |
| オブジェクトのローカル座標を、スクリーン上の座標へ変換 | |
| """ | |
| local_x, local_y = pos.copy() | |
| srn_x = local_x + self.MARGIN_WIDTH | |
| srn_y = (self.SCREEN_HEIGHT - local_y - self.MARGIN_HEIGHT) | |
| return np.asarray([srn_x, srn_y]) | |
| def locateVec(self, vel): | |
| """ | |
| オブジェクトのベクトルを、スクリーン上の座標空間へ変換 | |
| (※yを反転させるだけ) | |
| """ | |
| srn_vec = np.array([vel[0], -1 * vel[1]]) | |
| return srn_vec | |
| # ************************************************************* # | |
| # その他Qt関連補助関数 | |
| # ************************************************************* # | |
| def refreshPixmap(self): | |
| """ | |
| 画面バッファの初期化関数 | |
| """ | |
| # 画面バッファの初期化 | |
| self.pixmap = QPixmap(self.size()) | |
| # 画面を塗りつぶし (おまじない) | |
| self.pixmap.fill(self, 0, 0) | |
| self.pixmap.fill(Qt.white) | |
| # ぺインターの生成 (おまじない) | |
| painter = QPainter(self.pixmap) | |
| # ぺインターによる初期化 (おまじない) | |
| painter.initFrom(self) | |
| pass | |
| def sizeHint(self): | |
| return QSize(self.SCREEN_WIDTH, self.SCREEN_HEIGHT) | |
| def keyPressEvent(self, event): | |
| e = event.key() | |
| if e == Qt.Key_Up: | |
| self.car_speed += 1 | |
| pass | |
| elif e == Qt.Key_Down: | |
| self.car_speed -= 1 | |
| if self.car_speed < 0: | |
| self.car_speed = 0 | |
| elif e == Qt.Key_Left: | |
| self.car_steer = 'left' | |
| pass | |
| elif e == Qt.Key_Right: | |
| self.car_steer = 'right' | |
| pass | |
| elif e == Qt.Key_Plus: | |
| pass | |
| elif e == Qt.Key_Minus: | |
| pass | |
| elif e == Qt.Key_Q: | |
| self.close() | |
| else: | |
| pass | |
| print 'Presskey', e | |
| self.update() | |
| # ******************************************************* | |
| # main関数 | |
| # ******************************************************* | |
| def mainAlgorizm(): | |
| game = CircleRace() | |
| for i in range(50): | |
| game.update_game(steer='left', steer_level=1) | |
| pass | |
| def mainGUI(): | |
| app = QApplication(sys.argv) | |
| win = GameWindow() | |
| win.show() | |
| sys.exit(app.exec_()) | |
| if __name__ == "__main__": | |
| # mainAlgorizm() | |
| mainGUI() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment