コード(3次元でのショット軌道シミュレーション)
def search_shot(shot_info, shot_angle, contact_height, net_height, shot_distance):
ball_radius = 0.033 # ボールの半径 (メートル)
A = np.pi * (ball_radius**2) # ボールの断面積 (m^2)
m = 0.0577 # ボールの質量 (kg)
sv, spin, spin_axis = shot_info
shot_v = sv / 3.6
# サーブの斜め回転に対応するため、回転を物理モデルに組み込む
w = (spin * 2 * np.pi) / 60
w_axis = spin_axis # 回転軸
net_length = shot_distance[0]
net_height += ball_radius
bound_length = shot_distance[1]
after_bound_length = shot_distance[2]
# 物理定数
g = 9.81 # 重力加速度 (m/s^2)
rho = 1.205 # 空気密度 (kg/m^3)
e = 0.75 # 反発係数
myu = 0.72 # 摩擦係数
micron = 1.822 * 10 ** (-5) #粘度
cos0 = (23.77/2 + 6.4) / bound_length
sin0 = np.sqrt(1 - cos0 ** 2)
v0 = np.zeros(6)
v0[0] = -0.3
v0[2] = contact_height
v0[3] = shot_v * math.cos(math.radians(shot_angle)) * sin0
v0[4] = shot_v * math.cos(math.radians(shot_angle)) * cos0
v0[5] = shot_v * math.sin(math.radians(shot_angle))
t_min = 0
t_max = 3
dt = 0.001
t = np.arange(t_min, t_max, dt)
t_span = (t_min, t_max)
# ショット到達時間
shot_time = 0
reach_time = []
hitting_position = []
try:
solved_data = solve_ivp(f, t_span, v0, t_eval=t, args=(ball_radius, w, w_axis, rho, m, g, A, micron))
flag = True
not_net_pass = "not checked"
for i in range(len(solved_data.t)):
x_point = solved_data.y[0, i]
y_point = solved_data.y[1, i]
z_point = solved_data.y[2, i]
x_velocity = solved_data.y[3, i]
y_velocity = solved_data.y[4, i]
z_velocity = solved_data.y[5, i]
xy_length = np.sqrt(x_point ** 2 + y_point ** 2)
xy_velocity = np.sqrt(x_velocity ** 2 + y_velocity ** 2)
if (not_net_pass != "checked") and (y_point >= 23.77/2):
if (solved_data.y[2, i-1] <= net_height):
return [np.nan] * 4
else:
not_net_pass = "checked"
if z_point <= ball_radius:
if xy_length <= net_length:
return [np.nan] * 4
elif y_point > 23.77/2 + 6.4:
return [np.nan] * 4
elif x_point < 0 or x_point >8.23/2:
return [np.nan] * 4
elif (x_point < (8.23/2 - 1)) or (y_point < (23.77/2 + 6.4 - 2)):
return [np.nan] * 4
elif (x_point >= (8.23/2 - 1)) or (y_point >= (23.77/2 + 6.4 - 2)):
shot_time = solved_data.t[i]
y2 = np.zeros(6)
y2[0] = x_point
y2[1] = y_point
y2[2] = z_point
y2[3] = x_velocity + myu * (x_velocity / xy_velocity) * (e + 1) * z_velocity
y2[4] = y_velocity + myu * (y_velocity / xy_velocity) * (e + 1) * z_velocity
y2[5] = - e * z_velocity
if xy_velocity - w > 0:
w = w - (3 * myu * ball_radius * (1 + e) * z_velocity) / (2 * ball_radius ** 2)
elif xy_velocity - w < 0:
w = w + (3 * myu * ball_radius * (1 + e) * z_velocity) / (2 * ball_radius ** 2)
w_axis = 90
break
else:
return [np.nan] * 4
# print(f"bound: {sv}, {spin_axis}, {spin}, {shot_angle}")
solved_data = solve_ivp(f, t_span, y2, t_eval=t, args=(ball_radius, w, w_axis, rho, m, g, A, micron))
check_yline = 23.77
for i in range(len(solved_data.t)):
if len(solved_data.t) == i - 1:
return [np.nan] * 4
if solved_data.y[1, i] >= check_yline:
reach_time.append(shot_time + solved_data.t[i])
hitting_position.append(solved_data.y[0, i])
hitting_position.append(solved_data.y[1, i])
return [shot_time] + reach_time + hitting_position
except Exception as e:
tb = sys.exc_info()[2]
print(e.with_traceback(tb))
return [np.nan] * 4
def f(t, y, ball_radius, w, w_axis, rho, m, g, A, micron):
u, udot = y[:3], y[3:]
v = np.sqrt(udot[0]**2 + udot[1]**2 + udot[2]**2)
v_xy = np.sqrt(udot[0] ** 2 + udot[1] ** 2)
v_yz = np.sqrt(udot[1] ** 2 + udot[2] ** 2)
cosXY = udot[1] / v_xy
sinXY = udot[0] / v_xy
# レイノルズ数
Re = (rho * (2 * ball_radius) * v) / micron
# 抗力係数
Cd = 24 / Re
if Re > 3 * 10 ** 5:
Cd = 0.1
elif Re > 10 ** 3:
Cd = 0.44
# 抗力
Fd = (1 / 2) * Cd * A * rho * v * udot
# 揚力
Cl = 1 / (2.02 + 0.981 * (v / (ball_radius * w)))
w_vec = [
w * math.sin(math.radians(w_axis)) * sinXY,
w * math.sin(math.radians(w_axis)) * cosXY,
w * math.cos(math.radians(w_axis))
]
omega = [
[0, w_vec[2], -w_vec[1]],
[-w_vec[2], 0, w_vec[0]],
[w_vec[1], -w_vec[0], 0]
]
Fl = (1 / 2) * Cl * A * rho * (v ** 2) * (np.dot(omega, udot)) / np.linalg.norm(np.dot(omega, udot))
udotdot_x = (1 / m) * (- Fd[0] + Fl[0])
udotdot_y = (1 / m) * (- Fd[1] + Fl[1])
udotdot_z = (1 / m) * (- Fd[2] + Fl[2]) - g
dydt = np.hstack([udot, udotdot_x, udotdot_y, udotdot_z])
return dydt
columns = ["shot velocity", "spin rate", "spin angle", "shot angle"]
df = pd.DataFrame(columns=columns)
v = [160, 170]
s = [x for x in range(2000, 4001, 50)]
a = [x for x in range(0, 71, 5)]
b = [round(x, 1) for x in np.arange(-5.5, -3.9, 0.1)]
for i, comb in enumerate(itertools.product(v, s, a, b)):
df.loc[i, columns] = list(comb)
#セットアップ
tqdm.pandas()
contact_height = 2.5
l = 23.77/2+6.4
result_df = pd.DataFrame()
for shot_width in np.arange(0, 1.6, 0.1):
shot_width = round(shot_width, 1)
print(f"shot width: {shot_width}")
bound_length = np.sqrt(l ** 2 + (8.23/2 - shot_width) ** 2)
shot_length = bound_length + 5
net_length = bound_length * (((23.77/2)) / l)
net_height = min(1.07, 0.914 + (0.156 * (abs(((np.sqrt((net_length ** 2 - ((23.77 / 2)) ** 2))) - (8.23 / 2 - shot_width))) / 5.029)))
result_columns = ["shot time", "reach time", "x", "y"]
df["shot width"] = shot_width
df[result_columns] = df[columns].progress_apply(
lambda x: search_shot([x[0], x[1], x[2]], x[3], contact_height, net_height, [net_length, bound_length, (shot_length - bound_length)]),
axis=1,
result_type="expand"
)
result_df = pd.concat([result_df, df.dropna()])
この記事が気に入ったらサポートをしてみませんか?