File size: 5,366 Bytes
013216e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt


class TrajectoryFitter:
    def __init__(self, gravity=9.81):
        self.g = gravity

    def trajectory_model(self, t, v0, theta, h0):
        """
        Physics model for projectile motion

        Parameters:
        t: time points
        v0: initial velocity
        theta: launch angle (in degrees)
        h0: initial height

        Returns:
        (x, y) coordinates at each time t
        """
        # Convert angle to radians
        theta_rad = np.radians(theta)

        # Initial velocities in x and y directions
        v0x = v0 * np.cos(theta_rad)
        v0y = v0 * np.sin(theta_rad)

        # Position equations
        x = v0x * t
        y = h0 + v0y * t - 0.5 * self.g * t**2

        return np.column_stack((x, y))

    def fit_trajectory(self, points, times=None):
        """
        Fit trajectory to user-selected points

        Parameters:
        points: array of (x, y) coordinates
        times: optional array of timestamps for each point

        Returns:
        (v0, theta, h0): initial velocity, launch angle, initial height
        trajectory: predicted points along entire path
        """
        points = np.array(points)

        # If times not provided, estimate based on x positions
        if times is None:
            times = (points[:, 0] - points[0, 0]) / np.linalg.norm(
                points[1] - points[0]
            )

        # Initial guesses
        initial_height = points[0, 1]

        # Estimate initial angle and velocity from first two points
        if len(points) >= 2:
            dx = points[1, 0] - points[0, 0]
            dy = points[1, 1] - points[0, 1]
            initial_theta = np.degrees(np.arctan2(dy, dx))
            initial_v0 = np.sqrt(dx**2 + dy**2) / (times[1] - times[0])
        else:
            initial_theta = 45
            initial_v0 = 50

        # Fit physics model to points
        try:
            params, covariance = curve_fit(
                lambda t, v0, theta: self.trajectory_model(
                    t, v0, theta, initial_height
                ),
                times,
                points,
                p0=[initial_v0, initial_theta],
                bounds=([0, -90], [1000, 90]),  # Reasonable bounds for golf
            )

            v0_fit, theta_fit = params

            # Generate smooth trajectory for visualization
            t_smooth = np.linspace(min(times), max(times), 100)
            trajectory = self.trajectory_model(
                t_smooth, v0_fit, theta_fit, initial_height
            )

            return {
                "initial_velocity": v0_fit,
                "launch_angle": theta_fit,
                "initial_height": initial_height,
                "trajectory": trajectory,
                "times": t_smooth,
                "covariance": covariance,
            }

        except RuntimeError as e:
            print(f"Fitting failed: {e}")
            return None

    def calculate_metrics(self, fit_results):
        """Calculate additional metrics from the fit"""
        v0 = fit_results["initial_velocity"]
        theta = fit_results["launch_angle"]
        h0 = fit_results["initial_height"]

        # Maximum height
        theta_rad = np.radians(theta)
        v0y = v0 * np.sin(theta_rad)
        max_height = h0 + (v0y**2) / (2 * self.g)

        # Total distance (range)
        t_total = (v0y + np.sqrt(v0y**2 + 2 * self.g * h0)) / self.g
        total_distance = v0 * np.cos(theta_rad) * t_total

        # Flight time
        flight_time = t_total

        return {
            "max_height": max_height,
            "total_distance": total_distance,
            "flight_time": flight_time,
            "initial_velocity_mph": v0 * 2.237,  # Convert m/s to mph
        }

    def plot_trajectory(self, points, fit_results):
        """Visualize the fitted trajectory and original points"""
        plt.figure(figsize=(12, 6))

        # Plot original points
        points = np.array(points)
        plt.scatter(points[:, 0], points[:, 1], color="red", label="Selected Points")

        # Plot fitted trajectory
        trajectory = fit_results["trajectory"]
        plt.plot(trajectory[:, 0], trajectory[:, 1], "b-", label="Fitted Trajectory")

        plt.grid(True)
        plt.xlabel("Distance (m)")
        plt.ylabel("Height (m)")
        plt.title("Golf Ball Trajectory Fit")
        plt.legend()
        plt.axis("equal")
        plt.show()


# Example usage:
if __name__ == "__main__":
    # Sample points (x, y) in meters
    points = [
        (0, 0),  # Starting point
        (50, 20),  # Some point during flight
        (100, 0),  # Landing point
    ]

    fitter = TrajectoryFitter()
    results = fitter.fit_trajectory(points)

    if results:
        metrics = fitter.calculate_metrics(results)
        print("\nFitted Parameters:")
        print(
            f"Initial Velocity: {results['initial_velocity']:.1f} m/s ({metrics['initial_velocity_mph']:.1f} mph)"
        )
        print(f"Launch Angle: {results['launch_angle']:.1f} degrees")
        print(f"Max Height: {metrics['max_height']:.1f} m")
        print(f"Total Distance: {metrics['total_distance']:.1f} m")
        print(f"Flight Time: {metrics['flight_time']:.1f} s")

        fitter.plot_trajectory(points, results)