Following the trajectory dispersion simulation of the free-flight rocket discussed in the previous example, we will address the rocket guidance problem of sending the rocket to a desired target. Correcting the trajectory by generating maneuver acceleration to move the vehicle to the desired target point is called guidance, and the guidance algorithm plays the role of periodically calculating appropriate maneuvering acceleration to move the vehicle to the target point using available information provided from navigation systems and sensors. The required maneuvering acceleration can be generated through normal forces such as lift produced by the vehicle’s angle of attack and side-slip angle.
First, let's revisit the simulation of the free-flight rocket's dynamic model that incorporates the effects of wind.
The dynamics for the state variables that describe the direction of the rocket's velocity vector, namely the speed $V$, the vertical flight path angle $\gamma$, and the horizontal heading angle $\psi$, are given by,
$$ \begin{align*} m\dot{V} &= D_x - mg\sin\gamma, \\ mV\dot{\gamma} &= -D_z - mg\cos\gamma, \\ mV\dot{\psi} &= D_y, \end{align*} $$where the angles defined by,
Parameters and variables are given by,
The rocket's position relative to the launch origin is expressed as $p = (p_n, p_e, h) \in \R^3$, representing:
Note that the altitude direction (H) is opposite to the downward direction (D) where gravity acts. Also note that the gravitational acceleration varies with altitude as follows.
$$ g(h) = g_0 \left(\frac{R_e}{R_e + h}\right)^2 $$The rates of change of the position variables are given by:
$$ \begin{align*} \dot{p}_n &= v_n = V\cos\gamma\cos\psi, \\ \dot{p}_e &= v_e = V\cos\gamma\sin\psi, \\ \dot{h} &= -v_d = V\sin\gamma, \end{align*} $$where:
These equations express the NEH (North-East-Height) components of the velocity vector
The magnitude of the drag force is proportional to the dynamic pressure, as shown below. The dynamic pressure can be modeled as proportional to the atmospheric density, which is a function of altitude, and proportional to the square of the relative velocity with respect to the wind. The direction of the drag force is opposite to the relative velocity vector relative to the wind. The parameters $C_d$ and $S$ represent the rocket's drag coefficient and cross-sectional area, respectively, and are assumed to be constant. Combining these factors, the drag force components in the NED (North-East-Down) coordinate system, $D_{\text{ned}} = (D_n, D_e, D_d)$ , can be calculated as follows:
\begin{align*} D_{ned} = \bmat{D_n \\ D_e \\ D_d} &= \overbrace{\overbrace{\frac{1}{2}\rho \|v_\text{rel} \|^2}^{\text{Dynamic pressure}}SC_d}^{\text{Magnitude of drag}} \overbrace{\left(-\frac{v_\text{rel}}{\|v_\text{rel} \|}\right)}^{\text{Direction of drag}} \\ &= -\frac{1}{2}\rho SC_d \|v_\text{rel} \| v_\text{rel} \end{align*}The atmospheric density $\rho(h)$ can be modeled as a function of altitude $h$ as follows. The units of density are in $\text{kg/m}^3$, and the units of altitude are in $\text{m}$:
$$ \rho(h) = \rho_0 \exp\left(-\frac{h}{H_s}\right) $$where:
This exponential model accounts for the decrease in atmospheric density with increasing altitude.
The rocket's relative velocity with respect to the wind ( $v_{\text{rel}} \in \R^3$ ) in the NED coordinate system can be expressed as:
\begin{align*} v_\text{rel} &= v_{\text{ned}}- v_{w} \\ &= \bmat{V\cos\gamma\cos\psi \\ V\cos\gamma\sin\psi \\ -V\sin\gamma} - \bmat{v_{w,n} \\ v_{w,e} \\ v_{w,d}} \end{align*}Here:
The drag force components $D_{\text{ned}} = (D_n, D_e, D_d)$ calculated in the NED coordinate system can be transformed into the trajectory-tangential coordinate system drag components $D_{xyz} = (D_x, D_y, D_z)$ through a rotation transformation. By computing these values at each moment using the state variables, the integration of the dynamics can be performed:
\begin{align*} D_{xyz} = \begin{bmatrix} D_x \\ D_y \\ D_z \end{bmatrix} = \begin{bmatrix} \cos\gamma \cos\psi & \cos\gamma \sin\psi & -\sin\gamma \\ -\sin\psi & \cos\psi & 0 \\ \sin\gamma \cos\psi & \sin\gamma \sin\psi & \cos\gamma \end{bmatrix} D_{\text{ned}} \end{align*}In this transformation matrix:
Simulation parameters are set as follows:
These constants are used to calculate drag forces and the overall dynamics of the rocket throughout its trajectory.
import numpy as np
import matplotlib.pyplot as plt
# parameters
m = 40 # mass
S = np.pi*0.08**2 # cross-section area
Cd = 0.2 # drag coefficient
g0 = 9.80665 # gravitational acceleration at sea level
# unit conversion
Rad2Deg = 180/np.pi # conversion: radian to degree
Deg2Rad = 1/Rad2Deg # conversion: degree to radian
np.random.seed(3001)
Wind is assumed to blow only horizontally, meaning there is no vertical component in the $h$- or $d$-direction. In the NED coordinate system, the wind velocity is expressed as $v_w = (v_{w,n}, v_{w,e}, 0)$.
By implementing the above modeling and performing numerical integration of the dynamics of a free-flying rocket launched to east with an initial flight path angle of 40 degrees and an initial velocity of 1 km/s, we can obtain results similar to those shown below. Observe that each time you run the simulation, different trajectories are obtained due to the random wind.
def state_derivative(z,v_w):
V, gamma, psi, pn, pe, h = z
rho = 1.225*np.exp(-h/8500)
g = g0*(6378137/(6378137+h))**2
c_gam, s_gam = np.cos(gamma), np.sin(gamma)
c_psi, s_psi = np.cos(psi), np.sin(psi)
vn = V*c_gam*c_psi
ve = V*c_gam*s_psi
vd = -V*s_gam
v_ned = np.array([vn, ve, vd])
v_rel = v_ned - v_w
v_mag = np.sqrt(v_rel.dot(v_rel))
D_ned = -0.5*rho*S*Cd*v_mag*v_rel
C_ned_to_xyz = np.array([[c_gam*c_psi, c_gam*s_psi, -s_gam],
[ -s_psi, c_psi, 0],
[s_gam*c_psi, s_gam*s_psi, c_gam]])
D_xyz = C_ned_to_xyz.dot(D_ned)
V_dot = D_xyz[0]/m - g*s_gam
gamma_dot = -D_xyz[2]/m/V - g*c_gam/V
psi_dot = D_xyz[1]/m/V
pn_dot = vn
pe_dot = ve
h_dot = -vd
return np.array([V_dot, gamma_dot, psi_dot, pn_dot, pe_dot, h_dot])
# integration parameters
tf = 200 # final time
dt = 0.1 # step size
t = np.arange(0, tf, dt) # time (tentative)
N = len(t) # number of time steps
# initial conditions
V = 1000.0 # velocity
gamma = 40*Deg2Rad # flight path angle
psi = 90*Deg2Rad # heading
pn, pe, h = np.zeros(3) # position
init = [V, gamma, psi, pn, pe, h]
# initialize time
t = np.arange(0, tf, dt) # time (tentative)
N = len(t) # number of time steps
# initial wind
v_w = 5*np.random.randn(3)
v_w[-1] = 0
# numerical integration
state = np.zeros((N,len(init)))
state[0] = init
deriv_p = state_derivative(state[0],v_w)
for k in range(N-1):
deriv = state_derivative(state[k],v_w)
state[k+1] = state[k] + dt*(3*deriv - deriv_p)/2
v_w[0] += np.random.randn()
v_w[1] += np.random.randn()
deriv_p = deriv
if state[k,-1]<0: # termination condition
break; # break if the last state variable (h) is negative
# solution
t_s = t[:k+1] # time
state = state[:k+1] # state history
# plot
fig, ax = plt.subplots(2,1, figsize=(12,6), dpi=100)
ax[0].plot(state[:,4]/1000, state[:,5]/1000)
ax[0].set_xlabel(r'$p_e$ (km)')
ax[0].set_ylabel(r'h (km)')
ax[0].grid()
ax[1].plot(state[:,4]/1000, state[:,3]/1000)
ax[1].set_xlabel(r'$p_e$ (km)')
ax[1].set_ylabel(r'$p_n$ (km)')
ax[1].grid()
plt.tight_layout()
plt.show()
print(f'Impact velocity: {state[-1,0]:.2f} m/s')
print(f'Impact angle: {state[-1,1]*Rad2Deg:.2f} deg')
print(f'Flight range: {state[-1,4]/1000:.2f} km')
plt.figure(figsize=(14,7), dpi=100)
plt.subplot(2,3,1)
plt.plot(t_s, state[:,0])
plt.xlabel(r'time (sec)')
plt.ylabel(r'$V$ (m/s)')
plt.grid()
plt.subplot(2,3,2)
plt.plot(t_s, state[:,1]*180/np.pi)
plt.xlabel(r'time (sec)')
plt.ylabel(r'$\gamma$ (deg)')
plt.grid()
plt.subplot(2,3,3)
plt.plot(t_s, state[:,2]*180/np.pi)
plt.xlabel(r'time (sec)')
plt.ylabel(r'$\psi$ (deg)')
plt.grid()
plt.subplot(2,3,4)
plt.plot(t_s, state[:,3]/1000)
plt.xlabel(r'time (sec)')
plt.ylabel(r'$p_n$ (km)')
plt.grid()
plt.subplot(2,3,5)
plt.plot(t_s, state[:,4]/1000)
plt.xlabel(r'time (sec)')
plt.ylabel(r'$p_e$ (km)')
plt.grid()
plt.subplot(2,3,6)
plt.plot(t_s, state[:,5]/1000)
plt.xlabel(r'time (sec)')
plt.ylabel(r'$h$ (km)')
plt.grid()
plt.show()
Impact velocity: 400.55 m/s Impact angle: -57.35 deg Flight range: 33.66 km
Now, let's examine the modeling of a guided rocket that includes guidance functionality. Note that there are two main differences compared to the modeling of the free-flight rocket without guidance functionality, as outlined below.
Inclusion of Maneuvering Accelerations:
The maneuvering accelerations $u_z$ and $u_y$ serve to modify the trajectory in the vertical and horizontal directions, respectively. In practice, these accelerations are generated by aerodynamic forces (or control forces), which are the result of inner flight control loops that function by actuating control surfaces (e.g., fins or nozzles). In this context, we assume an ideal flight control, meaning that the maneuvering accelerations are generated instantaneously. The equations of motion are modified to include these accelerations as follows:
\begin{align*} m\dot{V} &= D_x - m g \sin\gamma, \\ m V \dot{\gamma} &= -D_z - m g \cos\gamma - m u_z, \\ m V \dot{\psi} &= D_y + m u_y, \end{align*}where:
Drag component induced by maneuver acceleration:
Since the maneuvering accelerations for guidance are generated by the angle of attack ($\alpha$) and sideslip angle ($\beta$), the drag coefficient $C_d$ of the vehicle becomes a function of these two parameters. The greater the generated maneuvering force, the more the drag increases. The drag coefficient is modeled as:
$$ C_d = C_{d,0} + k_i \frac{4 m^2}{\rho^2 \| v_{\text{rel}} \|^4 S^2 C_{L,\alpha}^2} \left( u_z^2 + u_y^2 \right), $$where:
The first term $C_{d,0}$ represents the baseline drag when there is no angle of attack or sideslip angle (i.e., the rocket is flying straight without maneuvering), and the second term accounts for the additional drag induced by maneuvering. As the rocket generates lift to maneuver, it also experiences an increase in drag, known as induced drag.
By incorporating these modifications, we can model the guided rocket's dynamics more accurately, accounting for the effects of maneuvering on both the trajectory and the aerodynamic forces. This sets the foundation for implementing guidance laws, such as proportional navigation, to direct the rocket toward a desired target while considering the trade-offs between maneuverability and aerodynamic efficiency.
Summarizing the simulation parameters with the newly defined parameters, we have:
These constants are essential for modeling the guided rocket's dynamics accurately. The newly introduced constants $k_i$ , $C_{d,0}$ , and $C_{l,\alpha}$ are particularly important for calculating the increased drag due to maneuvering accelerations as per the modified drag coefficient equation.
import numpy as np
import matplotlib.pyplot as plt
# parameters
m = 40 # mass
S = np.pi*0.08**2 # cross-section area
g = 9.8 # gravitational acceleration
ki = 5 # induced drag coefficient
Cd0 = 0.2 # profile drag coefficient
Cla = 8 # lift curve slope
# unit conversion
Rad2Deg = 180/np.pi # conversion: radian to degree
Deg2Rad = 1/Rad2Deg # conversion: degree to radian
(Problem 1)
Using the same definitions of state variables and wind modeling as in the previous cells, modify the function state_derivative()
that calculates the time derivatives of the state variables for computing the trajectory of a rocket considering guidance functionality. The function should take as input the state variables $z_t$, wind velocity $v_w$, and guidance commands $u_t = (u_{t,z}, u_{t,y})$, and output the time derivatives $\dot{z}_t$ of the state variables. You may reuse most of the functions defined above, and clearly indicate the parts that have been modified.
def state_derivative(z,v_w,u):
V, gamma, psi, pn, pe, h = z
##################
# your code here #
# your code here #
##################
return np.array([V_dot, gamma_dot, psi_dot, pn_dot, pe_dot, h_dot])
Proportional Navigation (PN) guidance, which is the most widely used guidance algorithm, is based on the principle that a target appearing to approach from the same direction will eventually collide with the interceptor.
PN guidance can be implemented in various ways depending on the vehicle system and available sensors. Here, we introduce the simplest method that can be applied in three-dimensional intercept scenarios.
Define the relative position and relative velocity of the target with respect to the rocket in the absolute coordinate system:
\begin{align*} p_\text{r} &= p_\text{t} - p_\text{m}, \\ v_\text{r} &= v_\text{t} - v_\text{m}. \end{align*}Here, $p_\text{m}$ and $v_\text{m}$ represent the position and velocity (in the NED coordinate system) of the guided rocket, and $p_\text{t}$ and $v_\text{t}$ represent the position and velocity of the target. For a stationary target, $v_\text{t} = 0$.
The Line of Sight (LOS) vector $\lambda$, which represents the direction in which the target is seen, is equal to the relative position vector:
$$ \lambda = p_\text{r}. $$The rate of change of this LOS vector ($\dot\lambda$) has the following relationship with the relative position and relative velocity vectors:
$$ v_\text{r} = \dot\lambda \times p_\text{r}. $$By taking the cross product of both sides with $p_\text{r}$ and simplifying, we get:
\begin{align*} p_\text{r} \times v_\text{r} &= p_\text{r} \times \left( \dot\lambda \times p_\text{r} \right) \\ &= \dot\lambda \left( p_\text{r}^T p_\text{r} \right) - p_\text{r} \left( p_\text{r}^T \dot\lambda \right). \end{align*}Since the relative position and the LOS rate vector are perpendicular to each other ( $p_\text{r}^T \dot\lambda = 0$ ), we obtain the LOS rate vector as:
$$ \dot\lambda = \frac{p_\text{r} \times v_\text{r}}{\| p_\text{r} \|^2}. $$Proportional Navigation guidance generates the maneuver acceleration command proportional to the LOS rate, with the navigation constant $N$ (typically between 3 and 5) as follows:
$$ a_\text{cmd} = N v_\text{r}\times \dot\lambda. $$The trajectory-tangential $z$-axis and $y$-axis components of this commanded acceleration $a_\text{cmd}$ correspond to $u_z$ and $u_y$.
The unit vector to the $y$-axis, denoted by $e_y$, can be obtained as the direction of the cross product of the gravity direction and $v_\text{m}$:
\begin{align*} \tilde{e}_y &= \bmat{ 0 \\ 0 \\ 1 } \times v_\text{m}, \\ e_y &= \frac{ \tilde{e}_y }{ \| \tilde{e}_y \| }. \end{align*}Similarly, $e_z$, the unit vector to the $z$-axis, can be obtained as the direction of the cross product of $v_\text{m}$ and $e_y$:
\begin{align*} \tilde{e}_z &= v_\text{m} \times e_y, \\ e_z &= \frac{ \tilde{e}_z }{ \| \tilde{e}_z \| }. \end{align*}Finally, using these, the $z$-axis and $y$-axis components of the commanded acceleration can be calculated as follows. Since a gravitational acceleration of $+g\cos\gamma$ acts in the $z$-axis direction, we compensate for the effect of gravity by adding $-\bar{g}\cos\gamma$ to the maneuvering acceleration in the $z$-direction, where \bar{g}$ comes from the gravity model that the guidance computer uses.:
\begin{align*} u_z &= e_z^T a_\text{cmd} - \bar{g}\cos\gamma, \\ u_y &= e_y^T a_\text{cmd}. \end{align*}However, since the rocket's maneuver acceleration is generated by normal forces such as lift, every rocket has a limit on the maneuver acceleration it can generate, and guidance commands should not generate maneuvering accelerations exceeding this limit. Let $u_\text{max}$ be this maximum maneuvering acceleration. If the magnitude of $u = (u_z, u_y)$ exceeds $u_\text{max}$, we can limit the magnitude of the maneuvering acceleration to $u_\text{max}$ using the following method:
\begin{align*} u_z &= u_z \frac{u_\text{max}}{\sqrt{ u_z^2 + u_y^2 }}, \\ u_y &= u_y \frac{u_\text{max}}{\sqrt{ u_z^2 + u_y^2 }}. \end{align*}Lastly, immediately after launch, due to launch shocks, initial disturbances, and launch site safety considerations, it is common not to perform guidance immediately but to allow free flight for a certain period. Also, just before reaching the target, significant LOS rate changes can occur, so it is common not to perform guidance just before reaching the target.
We estimate the remaining flight time ($t_\text{go}$) using a simple method as follows:
$$ t_\text{go} = \frac{ \| p_\text{r} \| }{ \| v_\text{r} \| }. $$(Problem 2)
Write a function compute_guidance_cmd()
that takes as input the rocket's flight time, state variables, and the target's position and velocity, and computes the maneuvering acceleration command $u$ :
You may use the following guidance parameters:
def compute_guidance_cmd(t,state,p_target,v_target=0):
V, gamma, psi, pn, pe, h = state
gbar = 9.8
u_max = 10
t_start = 20
t_cutoff = 1
##################
# your code here #
# your code here #
##################
u = np.array([u_z,u_y])
return u
(Problem 3) Using the guidance algorithm implemented in the function written above, guide the rocket to a target located 40 kilometers to the east. Examine the trajectory and state variables. Verify whether the rocket is appropriately guided near the target and report the miss distance (how close the rocket impacts the target).
# integration parameters
tf = 200 # final time
dt = 0.1 # step size
t = np.arange(0, tf, dt) # time (tentative)
N = len(t) # number of time steps
# initial conditions
V = 1000.0 # velocity
gamma = 40*Deg2Rad # flight path angle
psi = 90*Deg2Rad # heading
pn, pe, h = np.zeros(3) # position
init = [V, gamma, psi, pn, pe, h]
# target position
p_target = np.array([0,40000,0])
v_target = np.array([0,0,0])
##################
# your code here #
# your code here #
##################
##################
# your code here #
# your code here #
##################
Impact velocity: 363.42 m/s Impact angle: -35.30 deg Flight range: 40.03 km Impact point (N): 0.0005 km Impact point (E): 40.0280 km
(Problem 4) Perform 2000 Monte-Carlo simulations and plot all 2000 trajectories on a single plane.
# integration parameters
tf = 200 # final time
dt = 0.1 # step size
t = np.arange(0, tf, dt) # time (tentative)
N = len(t) # number of time steps
# initial conditions
V = 1000.0 # velocity
gamma = 40*Deg2Rad # flight path angle
psi = 90*Deg2Rad # heading
pn, pe, h = np.zeros(3) # position
init = [V, gamma, psi, pn, pe, h]
# Monte-Carlo simulation parameter
n_mc = 2000 # number of Monte-Carlo runs
##################
# your code here #
# your code here #
##################
100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000
(Problem 5)
Using the results from the 2000 Monte-Carlo runs, plot the impact points on the NE (North-East) plane using the scatter()
function from matplotlib.pyplot
, and also plot the positions of the target and the average impact point.
The accuracy of guided rockets is typically expressed in terms of CEP (Circular Error Probable), which is defined as the radius of a circle centered on the target within which there is a 50% probability of impact. In other words, if a guided rocket has a CEP of 200 meters, it means that out of 100 launches, approximately 50 rockets will land within 200 meters of the target center.
Using the above calculation results, estimate the CEP of this guided rocket.
##################
# your code here #
# your code here #
##################
CEP: 1.2 m