見出し画像

ショット到達時間とフットワークの時間からエースの取り方を考える

こんにちは、トモヒトです。

前回まで、ショットスピードやポジションを変えたときの、ショット到達時間を見てきました。

今回は、これらの結果にフットワークの時間を合わせて、エースを取るにはどうすればいいのかを考えてみます。


前提条件

まずは、今回の前提条件について定義します。

フットワークの時間

フットワークの時間は、次の2局面に分けて考えます。

  1. 反応時間(ショットのコースを確認してスタートを切るまでの時間)=0.3秒を想定

  2. スプリント時間(スタートを切ってヒッティングポジションまで移動する時間)
    =3mで0.546秒、3.5mで0.625秒

2.のスプリント時間に関しては、以下の論文を参考に次のように算出したものです。

3m: (1.25〔m〕 /  4〔m/s〕) + (1.75〔m) / 7.5〔m/s〕) ≒ 0.546
3.5m: (1.5〔m〕 /  4〔m/s〕) + (2〔m) / 8〔m/s〕) = 0.625

また、4mに関しては0.7秒を想定スプリント時間として用います。

加えて、返球可能範囲は、移動距離+リーチの長さとして、ここではリーチの長さを1mと仮定します。

つまり、今回想定している相手は、0.85秒で4m、0.93秒で4.5m、1秒で5mの範囲をカバーできることになります。

ショット到達時間

ショット到達時間は、前回まで使用したコードを使用しますが、今回は、一部条件を変更します。

  • 打点の高さ=0.6m

  • ショットの落下点=想定よりも0.5m以上短い位置でバウンドした場合はカウントしない

  • ネットの上0.5m以上を通過したものをカウントする

使用ソースは以下の通りです。

# ショット到達時間を算出する関数
def search_shot(sv, swing_angle, spin, contact_height, net_height, shot_distance):
    ball_radius = 0.033  # ボールの半径 (メートル)
    A = np.pi * (ball_radius**2)  # ボールの断面積 (m^2)
    m = 0.0577  # ボールの質量 (kg)

    shot_v = sv / 3.6

    w = spin * (np.pi / 30)

    net_length = shot_distance[0] + 0.5
    net_height += ball_radius

    shot_length = shot_distance[1]
    bound_length = shot_distance[2]
    hitting_point_distance = shot_length + bound_length

    # 物理定数
    g = 9.81  # 重力加速度 (m/s^2)
    rho = 1.21  # 空気密度 (kg/m^3)
    e = 0.75  # 反発係数
    myu = 0.72  # 摩擦係数

    v0 = np.zeros(4)
    v0[1] = contact_height
    v0[2] = shot_v * math.cos(math.radians(swing_angle))
    v0[3] = shot_v * math.sin(math.radians(swing_angle))

    t_min = 0
    t_max = 5
    dt = 0.001
    t = np.arange(t_min, t_max, dt)
    t_span = (t_min,t_max)

    solved_data = solve_ivp(f, t_span, v0, t_eval=t, args=(ball_radius, w, rho, m, g, A))

    flag = True

    for i in range(len(solved_data.t)):
        if solved_data.y[0, i] >= net_length - 0.02 and solved_data.y[0, i] <= net_length + 0.02 and solved_data.y[1, i] <= net_height:
            return np.nan, np.nan, np.nan
        if solved_data.y[1, i] <= ball_radius:
            if solved_data.y[0, i] <= net_length:
                return np.nan, np.nan, np.nan
            elif solved_data.y[0, i] < shot_length - 0.5:
                return np.nan, np.nan, np.nan
            elif solved_data.y[0, i] <= shot_length:
                shot_time = solved_data.t[i]
                reach_time = solved_data.t[i]
                y2 = np.zeros(4)
                y2[0] = solved_data.y[0, i]
                y2[1] = solved_data.y[1, i]
                y2[2] = solved_data.y[2, i] * e - myu * g
                y2[3] = - solved_data.y[3, i] * e
                if solved_data.y[2, i] - w > 0:
                    w = w - myu * g
                elif solved_data.y[2, i] - w < 0:
                    w = w + myu * g
                break
            else:
                return np.nan, np.nan, np.nan

    solved_data = solve_ivp(f, t_span, y2, t_eval=t, args=(ball_radius, w, rho, m, g, A))

    flag = False
    for i in range(len(solved_data.t)):
        if len(solved_data.t) == i - 1:
            break
        if solved_data.y[1, i+1] - solved_data.y[1, i]:
            flag = True
        if solved_data.y[0, i] >= hitting_point_distance or (flag and solved_data.y[1, i] <= ball_radius):
            bound_time = solved_data.t[i] + 0.004
            reach_time += solved_data.t[i] + 0.004
            return shot_time, bound_time, reach_time


def f(t, y, ball_radius, w, rho, m, g, A):
    u, udot = y[:2], y[2:]
    v = np.sqrt(udot[0]**2 + udot[1]**2)

    # 抗力係数
    Cd = 0.508 + (1 / ((22.503 + 4.196 * (v / (ball_radius * w)) ** 2.5) ** 0.4) )
    # 揚力係数
    Cl = 1 / (2.02 + 0.981 * (v / (ball_radius * w)))

    udotdot_x = ((A * rho * v) / (2 * m)) * (- Cd * udot[0] + Cl * udot[1])
    udotdot_y =  ((A * rho * v) / (2 * m)) * (- Cd * udot[1] - Cl * udot[0]) - g
    dydt = np.hstack([udot, udotdot_x, udotdot_y])
    retur#n dydt
# ショット情報DataFrame
import itertools

columns = ["shot velocity", "shot angle", "spin rate"]
df = pd.DataFrame(columns=columns)
v = [x for x in range(90, 150, 5)]
a = [x for x in np.arange(1, 20, 0.5)]
s = [x for x in range(1200, 2800, 50)]

for i, comb in enumerate(itertools.product(v, a, s)):
    df.loc[i, columns] = list(comb)
# ショット到達時間算出
x = 0
y = 0
contact_height = 0.6

for i, l in enumerate([23.77/2+6.4, 23.77/2+6.4+((23.77/2-6.4)/2), 23.77]):
    print(l)
    # Near Side
    if x == 0:
        bound_l = l - y
        net_l = 23.77/2 - y
        shot_l = 23.77 - y + 2
        net_height = 1.042
    else:
        bound_l = np.sqrt((l - y) ** 2 + x ** 2)
        net_l = bound_l * (((23.77/2) - y) / (l - y))
        shot_l = bound_l * ((23.77 - y + 2) / (l - y))
        net_height = min(1.07, 0.914 + (0.156 * (((8.23 / 2) - x + (np.sqrt((net_l ** 2 - ((23.77/2) - y) ** 2)))) / 5.029)))

    result_columns = [f"shot time near{i}", f"bound time near{i}", f"reach time near{i}"]

    df[result_columns] = df[columns].apply(
        lambda x: search_shot(x[0], x[1], x[2], contact_height, net_height, [net_l, bound_l, (shot_l - bound_l)]),
        axis=1,
        result_type="expand"
    )

    # Far Side
    bound_l = np.sqrt((l - y) ** 2 + (8.23 - x) ** 2)
    net_l = bound_l * (((23.77/2) - y) / (l - y))
    shot_l = bound_l * ((23.77 - y + 2) / (l - y))
    net_height = min(1.07, 0.914 + (0.156 * (abs(((np.sqrt((net_l ** 2 - ((23.77 / 2) - y) ** 2) + x)) - (8.23 / 2))) / 5.029)))

    print(bound_l, net_l, shot_l, net_height)

    result_columns = [f"shot time far{i}", f"bound time far{i}", f"reach time far{i}"]

    df[result_columns] = df[columns].apply(
        lambda x: search_shot(x[0], x[1], x[2], contact_height, net_height, [net_l, bound_l, (shot_l - bound_l)]),
        axis=1,
        result_type="expand"
    )

今回の条件でショット到達時間を算出すると、前回までの結果とは大きく異なります。

コース別ショット到達時間

エースの条件

エースの条件は、相手がベースライン後方2mにポジショニングしていると仮定し、ベースライン後方2mのショットの到達時間がフットワークの時間を下回った場合とします。

結果

まずは、シングルスサイドラインとベースラインの交点からショットを打った場合を考えます。

コース別ショット到達時間(サイド・ベースライン上)


相手がコートの端で打つ場合、合理的待機位置はややクロス寄りになります。
そのため、ここでは5mカバーの1秒で考えてみます。

ショットまでの距離を5mと考えると、エースになるショットの速度は、コースごとに次のようになります。

  • ストレート: 115km/h以上

  • クロス(ショート): 95km/h以上

  • クロス(ミドル): 110km/h以上

  • クロス(ディープ): 120km/h以上

次に、先ほどのヒッティングポジションから0.5mネットに近づいた位置で考えてみます。
コート内0.5m地点でのコース別ショット到達時間は次のとおりです。

コース別ショット到達時間(サイド・ベースライン内)

ベースライン上のときと比較すると、全体的にショット到達時間が0.02~0.03秒短くなっています。

また、ショットまでの距離を5mと考えると、エースになるショットの速度は、コースごとに次のようになります。
これをみると、全体的にエースになる最低速度が低くなっていることがわかります。

  • ストレート: 110km/h以上

  • クロス(ショート): 90km/h以上

  • クロス(ミドル): 105km/h以上

  • クロス(ディープ): 120km/h以上

110km/hのダウンザラインへのショットを打てるプレーヤーがエースを取るには

今回の結果を踏まえて、仮に110km/hのダウンザラインへのショットを打てるプレーヤーが、エースを取るには次の3つの方法が考えられます。
ただし、対戦相手は今回の想定と同じフットワークを持つものとします。

  1. ダウンザラインのスピードを5km/h上げる

  2. コート内0.5mの地点でダウンザラインが打てるチャンスを作る

  3. 相手が合理的待機位置に戻れないような配球

3に関しては、相手がヒッティングポジションへ移動するまで1.02秒以上かかるポジションからスタートさせることができれば、計算上はエースを取ることができるという意味です。

これら以外にも、打点の高さや横のポジションの変化、ショットの落下点が関係する可能性はありますが、これに関しては今後見ていきたいと思います。

まとめ

今回は、ショット到達時間とフットワークの時間の兼ね合いから、エースが取れる条件について考えてきました。

エースを奪うことができる状況がどのようなものかを明確にできれば、次はその状況を作り出すにはどうするか?を考えることになります。
反対に、守りの面でも相手にどのような状況を作られてはいけないかが明確にしやすくなります。

最後までお読みいただきありがとうございました。
ご意見ご感想あれば、コメントにお願いします。

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