パットシミュレーター

stimpmeter 9 ft — 俯瞰図(真上から)

t = 0.00 s
ボール(直径 42.67 mm)
カップ(直径 108 mm)
各秒の位置
時刻 (s)位置 (m)

計算方法(Python)

import math

# ボールがカップで止まるような速度で転がり始めた場合の、n秒後のボールの位置を計算する。
# 空気抵抗は考慮しない。
#
# stimpmeter_feet: ゴルフ場に書いてあるスティンプメーター値
# distance_to_cup_meter: カップまでの距離
#
# 戻り値は
# ([1秒ごとのボールの位置(m)]、ボールが止まるまでの秒数)
def ball_at(stimpmeter_feet: float, distance_to_cup_meter: float, slope_percent: float = 0.0):
    g = 9.8

    # フィート -> メートルに変換
    stimpmeter_meter = stimpmeter_feet * 0.3048

    # スティンプメーターから転がした球の初速
    stimpmeter_initial_v = math.sqrt(2 * g * 0.1715) # 0.1715はスティンプメーターの高さ (m)

    # スティンプメーターの計測値を使った転がり抵抗係数の計算
    rolling_resistance_factor = (stimpmeter_initial_v ** 2) / (2 * g * stimpmeter_meter)

    # 平坦なグリーン上のボール減速度 (m/s^2)
    a_flat = rolling_resistance_factor * g

    # 傾斜による加速度の補正。斜面に沿った重力 g*sin(theta) の近似。
    # 上り(正)なら減速方向、下り(負)なら加速方向に働くので、
    # 「減速度」に対しては符号そのまま加算すればよい:
    #   上り: 重力が逆向きに働く -> 減速が強まる -> a_slope は正
    #   下り: 重力が進行方向に働く -> 減速が弱まる -> a_slope は負
    a_slope = g * (slope_percent / 100.0)

    # 傾斜を考慮した実効減速度
    a = a_flat + a_slope

    if a <= 0:
        # 下りが急すぎて転がり抵抗だけでは減速しきれず、止まらないケース
        raise ValueError(
            f"傾斜が急すぎてボールが止まりません "
            f"(stimp={stimpmeter_feet}ft, slope={slope_percent}%, a_eff={a:.4f})"
        )


    # カップまでの距離をDとすると、
    # カップで止まらなければいけないので、Dm地点での速度が0になるから、
    # v(T) = v0 - a * T = 0
    # -> T = v0 / a
    # また、D = v0 * T + 1/2 * a * T^2 なので、
    # D = v0 * (v0 / a) + 1/2 * a * (v0 / a)^2
    #   = (v0^2 / a) + (v0^2 / 2a)
    #   = v0^2 / 2a
    # 求めたいのはv0なので、
    # v0 = sqrt(2a * D)
    v0 = math.sqrt(2 * a * distance_to_cup_meter)

    def x(t):
        # T秒後のボールの位置は
        # D = v0 * T + 1/2 * a * T^2
        # ただし減速なので
        xT = (v0 * t) - ((1/2) * a * (t ** 2))
        return xT

    xts = []
    T_stop = v0 / a
    for T in range(0, int(T_stop)):
        # ボールが1秒以内に止まるケースを考慮する
        # ボールが止まるまでの時間は
        # v(T) = v0 - a * T
        # このときの速度が0なので
        # v0 - a * T_stop = 0
        xT = x(T+1)
        xts.append(xT)
    return (xts, T_stop)

stimpmeter_feet = [8.5, 9.0, 9.5, 10.0, 10.5]
meters = [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 10.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5, 16.0, 16.5, 17.0, 17.5, 18.0, 18.5, 19.0, 19.5, 20.0]
# 正が上り、負が下り
slope_percents = [-2.0, -1.0, 0.0, 1.0, 2.0]

for f in stimpmeter_feet:
    print(f"{f}フィートグリーンの場合:")
    for s in slope_percents:
        print(f"  傾斜{s}%の場合:")
        for m in meters:
            ball_positions_m, total_s = ball_at(f, m, s)
            print(f"    カップまで{m}m: 止まるまでの時間: {total_s:.2f}秒")
            print("\n".join([f"      {i+1}秒後のボール位置: {pos:.5f}m\t" for i, pos in enumerate(ball_positions_m)]))