速さの錯覚
図1のような2つの車輪の回転速度は、物理的には同じ回転速度であっても、主観的にはスポーク(輻)の数が多い方が速く感じる。このことを見るための調整法の実験プログラムをPyGameを用いて、リスト1のように作成した。リスト1のプログラムファイルは、圧縮ファイルspeedillusionfile.zipとしたので、ダウンロード展開すれば自由に利用できる。
図1
リスト1のプログラムを実行すると、図1のように2つの回転している車輪が表示される。左の車輪はスポークの数も回転速度も一定であるが、右側の車輪のスポークの数は左より多い場合と少ない場合があり、回転速度も図1の画面の下方に説明されているようにキーUを押すと速くなり、キーDを押すと遅くなる。右側の車輪の見かけの回転速度が左側の車輪の見かけの回転速度に等しくなるように調整する。物理的に同じ速さではなく、主観的回転速度が同じになるようにする。同じになったと思えば、キーOを押す。キーOを押すと、次の試行に移る。試行数は画面の上に表示されている。スラッシュ/の右側の数値は、総試行数である。
総試行数の試行が終わると、図1の画面は閉じられて、左の車輪の回転速度と主観的に同じであると判断された右側の車輪の回転速度のヒストグラムが表示される(図2)。
図2
横軸は、回転速度、すなわち1回の更新における回転角度の値である。データは、右側の車輪におけるスポークが5本のものと19本のものに分けて集計されている。本数が5本の場合に比べて、19本の場合は、更新角度が小さい傾向にある。すなわち、19本の場合は、5本の場合より少ない回転角度で左の車輪の回転と同じであると判断されている。
import numpy
as np
import pygame
as pg
from pygame
import font
import matplotlib.pyplot
as plt
pg.init()
screen = pg.display.set_mode(size=(1200,800)) # 画面サイズ
screen.fill((255,255,255))
# 白画面
pg.display.set_caption('Speed illusion')
pg.display.flip()
c_L_x = 300
# 左の車輪の中心のX座標
c_L_y = 300
# 左の車輪の中心のY座標
r_main = 200
# 車輪の外径(半径)
r_sub = 50
# 車輪の内径(軸の半径)
n_L = 9
# 左の車輪のスポーク数
c_R_x = 900
# 右の車輪の中心のX座標
c_R_y = 300
# 右の車輪の中心のY座標
# 右の車輪のスポーク数
n_conds = [19, 5, 19, 5, 19, 5, 19, 5]
# 右の車輪の回転速度の初期値
s_conds = [0.01, 0.04, 0.04, 0.01, 0.04,
0.01, 0.01, 0.04]
#
# キー操作説明メッセージ
#
msg = 'U: speed up, D: speed down, O: OK!'
my_font = pg.font.SysFont(None,
48)
img_msg = my_font.render(msg, True, (0, 0, 0), (255, 255, 255))
rect_msg = img_msg.get_rect()
#
# 試行数表示用フォント
#
my_t_font = pg.font.SysFont(None,
38)
res_steps = []
# 観察者設定値の保存用リスト
ck_step = 0
# 観察者押下キーの
for i_trial
in range(8):
n_R
= n_conds[i_trial]
step_L
= 0.02
step_R
= s_conds[i_trial]
#
# スポーク(輻)の向き
#
degree_L
= np.array([i * 2 * np.pi / n_L for i in range(n_L)])
degree_R
= np.array([i * 2 * np.pi / n_R for i in range(n_R)])
ck
= True
while ck:
#
#
スクリーンを白で塗りつぶす。
# pg.display.flip()の実行までは、画面に反映されない。
#
screen.fill((255,255,255))
#
#
左の車輪の描画
#
pg.draw.circle(screen, (0, 0, 0), (c_L_x,
c_L_y), r_main, width=3)
pg.draw.circle(screen, (0,0,0), (c_L_x,
c_L_y), r_sub, width=3)
for i in range(n_L):
pg.draw.line(screen, (0,0,0),
(c_L_x + (r_sub) * np.cos(degree_L[i]),
c_L_y + (r_sub) * np.sin(degree_L[i])),
(c_L_x + (r_main) * np.cos(degree_L[i]),
c_L_y + (r_main) * np.sin(degree_L[i])),
width = 5)
#
#
右の車輪の描画
#
pg.draw.circle(screen, (0,0,0), (c_R_x,
c_R_y), r_main, width=3)
pg.draw.circle(screen, (0,0,0), (c_R_x,
c_R_y), r_sub, width=3)
for i in range(n_R):
pg.draw.line(screen, (0,0,0),
(c_R_x + (r_sub) * np.cos(degree_R[i]),
c_R_y + (r_sub) * np.sin(degree_R[i])),
(c_R_x + (r_main) * np.cos(degree_R[i]),
c_R_y + (r_main) * np.sin(degree_R[i])),
width = 5)
#
#
メッセージの表示
#
screen.blit(img_msg, (600 - rect_msg.width//2,
800 - rect_msg.height*3))
trial_msg = my_t_font.render(f'Trial {i_trial+1}/8', True,
(0,0,0),
(255,255,255))
rect_trial = trial_msg.get_rect()
screen.blit(trial_msg, (600 - rect_trial.width//2,
rect_trial.height))
pg.display.flip()
# スクリーンの画面への表示
degree_L = (degree_L + step_L) % (2*np.pi)
degree_R = (degree_R + step_R) % (2*np.pi)
for
e in pg.event.get():
# イベントのチェック
if e.type == pg.KEYUP: # キーが離されたか?
ck_step = 0
elif e.type == pg.KEYDOWN:
# キーが押されたか?
if e.key == pg.K_o: # 「キーJ」が押されたか?
ck = False
# While文の終了
elif e.key == pg.K_u:
# 「キーU」が押されたか?
ck_step = 1
# 更新値を増やす
elif e.key == pg.K_d:
# 「キーD」が押されたか?
ck_step = -1
# 更新値を減らす
step_R *= 1.02 ** ck_step # 更新値の増減
pg.time.wait(20)
# ウェイト20ms
print('step_R
=', step_R, ' n_R
=', n_R)
res_steps.append(step_R)
# 更新値step_Rをリストres_stepsに登録
pg.quit()
# PyGameの終了
steps_M = [] # 19本のスポークでの結果
steps_F = [] # 5本のスポークでの結果
for i in
range(8):
if n_conds[i] > 10:
steps_M.append(res_steps[i])
else:
steps_F.append(res_steps[i])
plt.hist(steps_M,
label='19 spokes', alpha=0.7)
plt.hist(steps_F,
label=' 5 spokes', alpha=0.7)
plt.xlabel('Speed(step size)')
plt.ylabel('Frequency')
plt.legend()
plt.show()