#!/usr/bin/env python # coding: utf-8 # Open In Colab # # Rocket guidance # # # # $$ # \newcommand{\eg}{{\it e.g.}} # \newcommand{\ie}{{\it i.e.}} # \newcommand{\argmin}{\operatornamewithlimits{argmin}} # \newcommand{\mc}{\mathcal} # \newcommand{\mb}{\mathbb} # \newcommand{\mf}{\mathbf} # \newcommand{\minimize}{{\text{minimize}}} # \newcommand{\diag}{{\text{diag}}} # \newcommand{\cond}{{\text{cond}}} # \newcommand{\rank}{{\text{rank }}} # \newcommand{\range}{{\mathcal{R}}} # \newcommand{\null}{{\mathcal{N}}} # \newcommand{\tr}{{\text{trace}}} # \newcommand{\dom}{{\text{dom}}} # \newcommand{\dist}{{\text{dist}}} # \newcommand{\R}{\mathbf{R}} # \newcommand{\SM}{\mathbf{S}} # \newcommand{\ball}{\mathcal{B}} # \newcommand{\bmat}[1]{\begin{bmatrix}#1\end{bmatrix}} # \newcommand{\loss}{\ell} # \newcommand{\eloss}{\mc{L}} # \newcommand{\abs}[1]{| #1 |} # \newcommand{\norm}[1]{\| #1 \|} # \newcommand{\tp}{T} # $$ # __
ASE3001: Computational Experiments for Aerospace Engineering, Inha University.
__ # _
Jong-Han Kim (jonghank@inha.ac.kr)
_ # #
# # ___ # #
# # Proportional Navigation (PN) is a widely used guidance law in missile and rocket systems designed to intercept moving targets efficiently. The fundamental principle of PN is to adjust the missile’s acceleration in proportion to the rate of change of the Line of Sight (LOS) angle to the target. This method is favored for its simplicity and effectiveness in homing guidance systems. #
# # ## Intercept geometry # # Consider a missile and a target moving in a two-dimensional plane. The missile's objective is to intercept the target by minimizing the miss distance at the closest point of approach. PN achieves this by ensuring that the LOS rate is driven to zero, effectively guiding the missile to collide with the target if no perturbations occur. # # In[1]: import numpy as np import matplotlib.pyplot as plt # Positions r_m = np.array([0, 0]) # Missile position r_t = np.array([5000, 1000]) # Target position # LOS vector delta_r = r_t - r_m lambda_angle = np.arctan2(delta_r[1], delta_r[0]) # Unit vectors u_los = delta_r / np.linalg.norm(delta_r) n_normal = np.array([-u_los[1], u_los[0]]) # Normal to LOS # Missile and Target velocities # Missile velocity vector v_magnitude = 400 # Missile speed (m/s) v_m = v_magnitude * np.array([np.cos(0), np.sin(0)]) # Target velocity vector v_t_magnitude = 300 # Target speed (m/s) v_t = v_t_magnitude * np.array([np.cos(np.pi), np.sin(np.pi)]) # Plot settings plt.figure(figsize=(10, 6), dpi=100) # Plot missile and target positions plt.plot(*r_m, 'o', color=plt.cm.tab20(0), label='Missile ($r_m$)') plt.plot(*r_t, 'o', color=plt.cm.tab20(2), label='Target ($r_t$)') # Plot LOS plt.plot([r_m[0], r_t[0]], [r_m[1], r_t[1]], 'k--', \ label='Line of Sight (LOS)') # Annotate LOS angle arc_radius = 800 theta = np.linspace(0, lambda_angle, 100) x_arc = arc_radius * np.cos(theta) y_arc = arc_radius * np.sin(theta) plt.plot(x_arc, y_arc, 'g-', linewidth=1) plt.text(arc_radius * np.cos(lambda_angle / 2)+80, arc_radius * np.sin(lambda_angle / 2)-50, '$\lambda$', fontsize=12) # Plot missile velocity vector plt.arrow(*r_m, *v_m, head_width=100, width=50, color=plt.cm.tab20(0), \ ec='none', label='Missile Velocity ($v_m$)') # Plot target velocity vector plt.arrow(*r_t, *v_t, head_width=100, width=50, color=plt.cm.tab20(2), \ ec='none', label='Target Velocity ($v_t$)') # Coordinate axes plt.axhline(0, color='gray', linewidth=0.5) plt.axvline(0, color='gray', linewidth=0.5) # Labels and title plt.xlabel('X Position (m)') plt.ylabel('Y Position (m)') plt.title('Geometry of Proportional Navigation') plt.legend() plt.grid(True) plt.axis('equal') plt.xlim(-1000, 6000) plt.ylim(-1000, 3000) plt.show() # The diagram illustrates a two-dimensional plane where both the missile and the target are moving. The missile is at position ${r}_m$ with velocity ${v}_m$, and the target is at position ${r}_t$ with velocity ${v}_t$ . The Line of Sight (LOS) connects the missile and the target, forming an angle $\lambda$ with a reference axis (typically the horizontal axis). The LOS angle $\lambda$ changes as both the missile and the target move. #
# # ___ # #
# # ## Formulation # # Let’s define: # # - ${r}_m = (x_m, y_m)$ : Position vector of the missile. # - ${r}_t = (x_t, y_t)$ : Position vector of the target. # - ${v}_m = (V_{m,x}, V_{m,y})$ : Velocity vector of the missile. # - ${v}_t = (V_{t,x}, V_{t,y})$ : Velocity vector of the target. # - $\lambda$ : Line of Sight (LOS) angle between the missile and the target. # - $\dot{\lambda}$ : Rate of change of the LOS angle. # #
# # The LOS angle $\lambda$ is calculated as: # # $$\lambda = \tan^{-1}\left( \frac{y_t - y_m}{x_t - x_m} \right) # = \angle \left(r_t-r_m\right)$$ # # and the rate of change of the LOS angle $\dot{\lambda}$ is: # # $$\dot{\lambda} = \frac{(v_{t,y} - v_{m,y})(x_t - x_m) - (v_{t,x} - v_{m,x})(y_t - y_m)}{(x_t - x_m)^2 + (y_t - y_m)^2} # = \frac{(r_t-r_m)\times(v_t-v_m)}{\|r_t-r_m\|^2}$$ # # # #
# # ## Proportional navigation guidance # # The acceleration command ${a}_m$ for the missile is given by: # # $${a}_m = N v_c \dot{\lambda} {n}$$ # # where: # # - $N$ : Navigation constant (typically between 3 and 5). # - $v_c$ : Closing velocity between the missile and the target. # - ${n}$ : Unit vector normal to the LOS direction. # #
# # The closing velocity $v_c$ is the relative velocity component of the target along the line of sight direction: # # $$ # v_c = (v_t - v_m) ^T u # $$ # # where ${u}$ is the unit vector along the LOS: # # $$ # {u} = \frac{{r}_t - {r}_m}{\|{r}_t - {r}_m\|} # $$ # # and ${n}$ is the unit vector normal to ${u}$ : # # $$ # {n} = \bmat{ -u_y \\ u_x } # $$ #
# # ___ # #
# # ## Numerical experiments # # We will simulate a missile intercepting a target using Proportional Navigation in Python. # # Assumptions # # - Two-dimensional motion. # - Constant speeds for both missile and target. # - The missile moves faster than the target. # - The target moves with a constant velocity (non-maneuvering). # In[2]: import numpy as np import matplotlib.pyplot as plt # Simulation parameters dt = 0.01 # Time step (seconds) t_end = 20 # End time (seconds) time = np.arange(0, t_end, dt) # Missile initial conditions r_m = np.array([0.0, 0.0]) # Initial position (meters) v_magnitude = 400.0 # Missile speed (m/s) v_m = v_magnitude * np.array([np.cos(0), np.sin(0)]) # to right # Target initial conditions r_t = np.array([5000.0, 1000.0]) # Initial position (meters) v_t = 300.0 * np.array([np.cos(np.pi), np.sin(np.pi)]) # to left # Navigation constant N = 3.0 # Lists to store trajectory data missile_path = [] target_path = [] maneuver_acc = [] for t in time: # Relative position and velocity delta_r = r_t - r_m delta_v = v_t - v_m range_sq = np.dot(delta_r, delta_r) # LOS angle and its rate LOS_angle = np.arctan2(delta_r[1], delta_r[0]) #lambda_dot = (delta_r[0]*delta_v[1]-delta_r[1]*delta_v[0])/range_sq lambda_dot = np.cross(delta_r, delta_v) / range_sq # Closing velocity v_c = -np.dot(delta_v, delta_r) / np.linalg.norm(delta_r) # Acceleration command a_magnitude = N * v_c * lambda_dot u = np.array([-np.sin(LOS_angle), np.cos(LOS_angle)]) # LOS normal a_m = a_magnitude * u # maneuver acceleration vector # Update missile velocity and position v_m += a_m * dt r_m += v_m * dt # Update target position r_t += v_t * dt # Store positions missile_path.append(r_m.copy()) target_path.append(r_t.copy()) maneuver_acc.append(a_m.copy()) # Check for interception (within 10 meters) if np.linalg.norm(delta_r) < 10.0: print(f"Target intercepted at time {t:.2f} seconds") break # Convert paths to arrays for plotting missile_path = np.array(missile_path) target_path = np.array(target_path) # Plot trajectories plt.figure(figsize=(10, 6), dpi=100) plt.plot(missile_path[:, 0], missile_path[:, 1], \ label='Missile Trajectory', color=plt.cm.tab20(0)) plt.plot(target_path[:, 0], target_path[:, 1], \ label='Target Trajectory', color=plt.cm.tab20(2)) for i in range(0,len(maneuver_acc),20): plt.arrow(*missile_path[i], *maneuver_acc[i]*5, head_width=40, width=15, color=plt.cm.tab20(4), ec='none') plt.scatter(r_m[0], r_m[1], color=plt.cm.tab20(6), \ label='Interception Point') plt.title('Proportional Navigation Trajectories') plt.xlabel('X Position (m)') plt.ylabel('Y Position (m)') plt.legend() plt.grid(True) plt.axis('equal') plt.xlim(-1000, 6000) plt.ylim(-1000, 3000) plt.show()