[General] Code Tmax sun occult mission

オープン
#619 コメント 2 件 リアクション 1 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@ocots がすでに取り組んでいます。

2025年9月5日 から。

評価

この issue はまだ評価されていません。

説明

The problem is formulated in terms of "what is the minimum amount of thrusters needed for relations these two points fixed in time and state (position & velocity), mass is free"
@jbcaillau

using SPICE
using OptimalControl
using NLPModelsIpopt
using JLD2
using OrdinaryDiffEq
using DataInterpolations
using CSV
using JLD2

using Plots
using Plots.PlotMeasures # for leftmargin, bottommargin
using LaTeXStrings

# SPICE Downloads
using Downloads: download


# SPICE files
const LSK = "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/lsk/naif0012.tls"
const SPK = "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de440.bsp"
const PCK = "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/pck00010.tpc"

# Download kernels
download(LSK, "naif0012.tls")
download(SPK, "de440.bsp")
download(PCK, "pck00010.tpc")

furnsh("naif0012.tls") # Leap seconds kernel
furnsh("de430.bsp")
furnsh("pck00010.tpc")  # PCK kernel for planetary constants

# Astrodynamical parameters
G = 6.6743e-11 / 1000^3 # km^3 kg^-1 s^-2
M_moon = 7.346e22 # kg
M_earth = 5.972168e24 # kg
M_sun = 1.989e30 # kg
AU = 1.495978707e11 / 1e3 # km
LU = 384399 # km
TU = sqrt(LU^3 / G / (M_moon + M_earth))
MUnit =  M_moon + M_earth
mu_Earth = 3.9860044188e14 / 1000^3 / LU^3 * TU^2
mu_Moon = 4.90486959e12 / 1000^3 / LU^3 * TU^2
mu_Sun = 1.327124400189e20  / 1000^3 / LU^3 * TU^2
mass_moon = M_moon / MUnit
mass_earth = M_earth / MUnit
mass_sun = M_sun / MUnit

# Spacecraft parameters
g0 = 9.80665 / 1000 / LU * TU^2
Isp = 1660 / TU 
wet_mass = 400 / MUnit 
fuel_mass = 90 / MUnit 
dry_mass = wet_mass - fuel_mass
Tmax =  0.090 / 1000 / MUnit / LU * TU^2 #thrust force in N
Q = Tmax / Isp / g0

mass_scaling_factor = dry_mass + fuel_mass

# COASTING parameter
deltat = 2 * 24 * 3600 / TU

# Plot functions 
function plot_trajNV_multiarc_multidyn_withcoasting_1arc(sol, nb_arcs, tvec, moon_arcs, uncontrolled_arcs)

    sol_time = sol.time_grid.value 

    Nstate = length(sol.state.value(sol.time_grid.value[1]))


    Nsol = length(sol_time)
    sol_state = zeros(Nstate * nb_arcs, Nsol)
    sol_control = zeros(3 * (nb_arcs - length(uncontrolled_arcs)), Nsol)
    # sol_control = zeros(3 * nb_arcs, Nsol)
    # k = 1
    for i in range(1, Nsol)
        sol_state[:, i] = sol.state.value(sol.time_grid.value[i])
        sol_control[:, i] = sol.control.value(sol.time_grid.value[i])
        # if i ∉ uncontrolled_arcs
            # k += 1
        # end
    end

    for m in moon_arcs
        for i in range(1, Nsol)
            Nbegin = 1 + Nstate * (m - 1)
            Nend = 7 + Nstate * (m - 1)
            sol_state[Nbegin : Nend, i] = moon2emb(phi(sol_time[i], tvec[m], tvec[m+1]), sol_state[Nbegin : Nend, i])
        end
    end

    x_vals_opti = zeros(nb_arcs, Nsol)
    y_vals_opti = zeros(nb_arcs, Nsol)
    z_vals_opti = zeros(nb_arcs, Nsol)
    vx_vals_opti = zeros(nb_arcs, Nsol)
    vy_vals_opti = zeros(nb_arcs, Nsol)
    vz_vals_opti = zeros(nb_arcs, Nsol)
    m_vals_opti = zeros(nb_arcs, Nsol)

    ux_vals_opti = zeros(nb_arcs, Nsol)
    uy_vals_opti = zeros(nb_arcs, Nsol)
    uz_vals_opti = zeros(nb_arcs, Nsol)
    u_norm = zeros(nb_arcs, Nsol)

    k = 1
    for i in range(1, nb_arcs)
        x_vals_opti[i, :] = sol_state[1 + Nstate * (i - 1), :]
        y_vals_opti[i, :] = sol_state[2 + Nstate * (i - 1), :]
        z_vals_opti[i, :] = sol_state[3 + Nstate * (i - 1), :]
        vx_vals_opti[i, :] = sol_state[4 + Nstate * (i - 1), :]
        vy_vals_opti[i, :] = sol_state[5 + Nstate * (i - 1), :]
        vz_vals_opti[i, :] = sol_state[6 + Nstate * (i - 1), :]
        m_vals_opti[i, :] = sol_state[7 + Nstate * (i - 1), :]
        if i ∉ uncontrolled_arcs
            ux_vals_opti[i, :] = sol_control[1 + 3 * (k - 1), :]
            uy_vals_opti[i, :] = sol_control[2 + 3 * (k - 1), :]
            uz_vals_opti[i, :] = sol_control[3 + 3 * (k - 1), :]
            u_norm[i, :] = (ux_vals_opti[i, :].^2 + uy_vals_opti[i, :].^2 + uz_vals_opti[i, :].^2).^(1/2)
            k += 1
        end
    end

    sol_time_plot = LinRange(tvec[1], tvec[end], N)
    x_moon_pos = [x_moon(t) for t in sol_time_plot]
    y_moon_pos = [y_moon(t) for t in sol_time_plot]
    z_moon_pos = [z_moon(t) for t in sol_time_plot]
    x_earth_pos = [x_earth(t) for t in sol_time_plot]
    y_earth_pos = [y_earth(t) for t in sol_time_plot]
    z_earth_pos = [z_earth(t) for t in sol_time_plot]

    p = plot(x_moon_pos, y_moon_pos, z_moon_pos, label = "moon orbit")
    plot!(x_earth_pos, y_earth_pos, z_earth_pos, label = "earth orbit")

    scatter!([x_vals_opti[1, 1]], [y_vals_opti[1, 1]], [z_vals_opti[1, 1]], label = "previous occultation", color = :blue)

    labels = ["next occultation"]

    for i in range(1, nb_arcs)
        rand_color = RGB(rand(), rand(), rand()) 
        plot!(x_vals_opti[i, :], y_vals_opti[i, :], z_vals_opti[i, :], size=(600, 600), camera = (10,30), xlabel="x", ylabel="y", label = false, zlabel="z", linewidth = 2, color = "black", legend =:topleft)
        scatter!([x_vals_opti[i, end]], [y_vals_opti[i, end]], [z_vals_opti[i, end]], label = labels[i], color = :red)
        arrow_factor = 1e-1
        inds = findall(u_norm[i, :] .> 0.001)[1:2:end]
        quiver!(x_vals_opti[i, inds], y_vals_opti[i, inds], z_vals_opti[i, inds], quiver=(ux_vals_opti[i, inds] * arrow_factor, uy_vals_opti[i, inds] * arrow_factor, uz_vals_opti[i, inds] * arrow_factor), label="thrust directions", arrow=true, color = "red")
            
        # if i ∉ uncontrolled_arcs
            # arrow_factor = 1e-1
            # inds = findall(u_norm[k, :] .> 0.01)[1:2:end]
            # quiver!(x_vals_opti[i, inds], y_vals_opti[i, inds], z_vals_opti[i, inds], quiver=(ux_vals_opti[k, inds] * arrow_factor, uy_vals_opti[k, inds] * arrow_factor, uz_vals_opti[k, inds] * arrow_factor), label="thrust directions", arrow=true, color = "red")
            # k += 1
        # end
    end


    xlims!(-2, 2)
    ylims!(-2, 2)
    zlims!(-2, 2)
    
    display(p)
    

end


function plot_trajNV_multiarc_multidyn_synodic_1arc(sol, nb_arcs, tvec, moon_arcs)

    sol_time = sol.time_grid.value 

    Nstate = length(sol.state.value(sol.time_grid.value[1]))

    Nsol = length(sol_time)
    sol_state = zeros(Nstate * nb_arcs, Nsol)
    for i in range(1, Nsol)
        sol_state[:, i] = sol.state.value(sol.time_grid.value[i])
    end

    for m in moon_arcs
        for i in range(1, Nsol)
            Nbegin = 1 + Nstate * (m - 1)
            Nend = Nstate + Nstate * (m - 1)
            sol_state[Nbegin : Nend, i] = moon2emb(sol_time[i], sol_state[Nbegin : Nend, i])
        end
    end

    x_vals_opti = zeros(Nsol)
    y_vals_opti = zeros(Nsol)
    z_vals_opti = zeros(Nsol)
    vx_vals_opti = zeros(Nsol)
    vy_vals_opti = zeros(Nsol)
    vz_vals_opti = zeros(Nsol)
    m_vals_opti = zeros(Nsol)

    for i in range(1, nb_arcs)
        x_vals_opti = sol_state[1 , :]
        y_vals_opti = sol_state[2 , :]
        z_vals_opti = sol_state[3 , :]
        vx_vals_opti = sol_state[4 , :]
        vy_vals_opti = sol_state[5 , :]
        vz_vals_opti = sol_state[6 , :]
        m_vals_opti = sol_state[7 , :]
    end

    x_syn = zeros( Nsol)
    y_syn = zeros(Nsol)
    z_syn = zeros( Nsol)

    for ii = 1:Nsol
        
        x_sol_syn = ECLIPJ2000toSynodic(sol_time[ii] * TU,  
            sol_state[1 : 3, ii])
        x_syn[ii] = x_sol_syn[1]
        y_syn[ii] = x_sol_syn[2]
        z_syn[ii] = x_sol_syn[3]

    end

    p = plot(x_syn, y_syn, z_syn, legend = false, linewidth = 2, color = :black)
    scatter!([x_syn[end]], [y_syn[end]], [z_syn[end]], color = :red, xlabel = "x", ylabel = "y", zlabel = "z")
    scatter!([x_syn[1]], [y_syn[1]], [z_syn[1]], color = :red, xlabel = "x", ylabel = "y", zlabel = "z")
    
    scatter!([norm([x_moon(t0), y_moon(t0), z_moon(t0)])], [0], [0], color = :grey, label = "moon", markersize=7)
    scatter!([norm([x_earth(t0), y_earth(t0), z_earth(t0)])], [0], [0], color = :blue, label = "earth", markersize=10)

    xlims!(-2, 2)
    ylims!(-2, 2)
    zlims!(-2, 2)
    
    display(p)
    

end


function ECLIPJ2000toSynodic(et, x)

    xm    = spkezr("MOON", et, "ECLIPJ2000", "NONE", "EARTH")
        
    r     = xm[1][1:3]
    v     = xm[1][4:6]

    normr = norm(r)

    h     = cross(r, v)
    normh = norm(h)

    er    = r/normr
    eh    = h/normh
    et    = cross(eh, er)/norm(cross(eh, er))

    IS    = [er[1] er[2] er[3];
             et[1] et[2] et[3];
             eh[1] eh[2] eh[3]]

    xs    = IS * x[1:3]

    return xs
end




# Dynamics functions

function propagate!(du, u, p, t)
    r = u[1:3]
    v = u[4:6]
    m = u[7] * mass_scaling_factor

    x_e = [x_earth(t), y_earth(t), z_earth(t)]
    x_m = [x_moon(t), y_moon(t), z_moon(t)]
    x_s = [x_sun(t), y_sun(t), z_sun(t)]

    normrE = sqrt(sum((x_e - r).^2))
    normrM = sqrt(sum((x_m - r).^2))
    normrS = sqrt(sum((x_s - r).^2))

    normrSE = sqrt(sum((x_e - x_s).^2))
    normrMS = sqrt(sum((x_m - x_s).^2))
    
    dv = mu_Earth / normrE^3 * (x_e - r) + mu_Moon / normrM^3 * (x_m - r) + mu_Sun / normrS^3 * (x_s - r) - (1 / (mass_earth + mass_moon)) * ( x_s * (mu_Sun * mass_earth / normrSE^3 + mu_Moon * mass_sun / normrMS^3) - x_m * (mu_Moon * mass_sun / normrMS^3) - x_e * (mu_Sun * mass_earth / normrSE^3)) 
    dvu = Tmax * p / m

    dm = - Q * sqrt(sum(p.^2)) / mass_scaling_factor

    du[1:3] = v
    du[4:6] = dv + dvu
    du[7]   = dm
end

function F0(t, x, u)
    # Earth & Moon & Sun gravity
    
    r     = x[1:3]
    v     = x[4:6]
    m     = x[7] * mass_scaling_factor
    N_th  = x[8]

    x_e = [x_earth(t), y_earth(t), z_earth(t)]
    x_m = [x_moon(t), y_moon(t), z_moon(t)]
    x_s = [x_sun(t), y_sun(t), z_sun(t)]

    normrE = sqrt(sum((x_e - r).^2))
    normrM = sqrt(sum((x_m - r).^2))
    normrS = sqrt(sum((x_s - r).^2))

    normrSE = sqrt(sum((x_e - x_s).^2))
    normrMS = sqrt(sum((x_m - x_s).^2))

    dv = mu_Earth / normrE^3 * (x_e - r) + mu_Moon / normrM^3 * (x_m - r) + mu_Sun / normrS^3 * (x_s - r) - (1 / (mass_earth + mass_moon)) * ( x_s * (mu_Sun * mass_earth / normrSE^3 + mu_Moon * mass_sun / normrMS^3) - x_m * (mu_Moon * mass_sun / normrMS^3) - x_e * (mu_Sun * mass_earth / normrSE^3)) 
    dvu = N_th * Tmax * u / m
    dm = - N_th * Q * sqrt(sum(u.^2)) / mass_scaling_factor

    dx = [v; dv + dvu; dm; 0]
    return dx
end

function Fu(t, x, u)
    # Earth & Moon & Sun gravity
    
    r     = x[1:3]
    v     = x[4:6]
    m     = x[7] * mass_scaling_factor
    N_th  = x[8]

    x_e = [x_earth(t), y_earth(t), z_earth(t)]
    x_m = [x_moon(t), y_moon(t), z_moon(t)]
    x_s = [x_sun(t), y_sun(t), z_sun(t)]

    normrE = sqrt(sum((x_e - r).^2))
    normrM = sqrt(sum((x_m - r).^2))
    normrS = sqrt(sum((x_s - r).^2))

    normrSE = sqrt(sum((x_e - x_s).^2))
    normrMS = sqrt(sum((x_m - x_s).^2))

    dvg = mu_Earth / normrE^3 * (x_e - r) + mu_Moon / normrM^3 * (x_m - r) + mu_Sun / normrS^3 * (x_s - r) - (1 / (mass_earth + mass_moon)) * ( x_s * (mu_Sun * mass_earth / normrSE^3 + mu_Moon * mass_sun / normrMS^3) - x_m * (mu_Moon * mass_sun / normrMS^3) - x_e * (mu_Sun * mass_earth / normrSE^3)) 
    dvu = N_th * Tmax * u / m
    dv = dvg + dvu
    dm = - N_th * Q * sqrt(sum(u.^2)) / mass_scaling_factor

    dx = [ v; dv; dm; 0 ]
    return dx
end

t0_J200 = 1.107747244e9
tf_J200 = 1.110817407e9
N = 50000 
t_values = LinRange(t0_J200, tf_J200, N-3)
t_values = vcat(-5e9, 0.0, collect(t_values), tf_J200 * 5)
states_moon = zeros(6, N)
states_earth = zeros(6, N)
states_sun = zeros(6, N)
states_moon_wrt_sun = zeros(6, N)
states_earth_wrt_moon = zeros(6, N)
t0 = t0_J200 / TU
tf = tf_J200 / TU

for i = 1:N
    states_moon[:,i], _             = spkezr("MOON", t_values[i], "ECLIPJ2000", "NONE", "EARTH MOON BARYCENTER")
    states_earth[:,i], _            = spkezr("EARTH", t_values[i], "ECLIPJ2000", "NONE", "EARTH MOON BARYCENTER")
    states_sun[:,i], _              = spkezr("SUN", t_values[i], "ECLIPJ2000", "NONE", "EARTH MOON BARYCENTER")
    states_moon_wrt_sun[:,i], _     = spkezr("MOON", t_values[i], "ECLIPJ2000", "NONE", "SUN")
    states_earth_wrt_moon[:,i], _   = spkezr("EARTH", t_values[i], "ECLIPJ2000", "NONE", "MOON")
end


x_moon           = DataInterpolations.LinearInterpolation(states_moon[1,:] / LU,           t_values / TU) 
y_moon           = DataInterpolations.LinearInterpolation(states_moon[2,:] / LU,           t_values / TU) 
z_moon           = DataInterpolations.LinearInterpolation(states_moon[3,:] / LU,           t_values / TU) 

vx_moon          = DataInterpolations.LinearInterpolation(states_moon[4,:] / LU * TU,      t_values / TU) 
vy_moon          = DataInterpolations.LinearInterpolation(states_moon[5,:] / LU * TU,      t_values / TU) 
vz_moon          = DataInterpolations.LinearInterpolation(states_moon[6,:] / LU * TU,      t_values / TU) 

x_earth          = DataInterpolations.LinearInterpolation(states_earth[1,:] / LU,          t_values / TU) 
y_earth          = DataInterpolations.LinearInterpolation(states_earth[2,:] / LU,          t_values / TU) 
z_earth          = DataInterpolations.LinearInterpolation(states_earth[3,:] / LU,          t_values / TU) 

vx_earth         = DataInterpolations.LinearInterpolation(states_earth[4,:] / LU * TU,     t_values / TU) 
vy_earth         = DataInterpolations.LinearInterpolation(states_earth[5,:] / LU * TU,     t_values / TU) 
vz_earth         = DataInterpolations.LinearInterpolation(states_earth[6,:] / LU * TU,     t_values / TU) 

x_sun            = DataInterpolations.LinearInterpolation(states_sun[1,:] / LU,            t_values / TU) 
y_sun            = DataInterpolations.LinearInterpolation(states_sun[2,:] / LU,            t_values / TU) 
z_sun            = DataInterpolations.LinearInterpolation(states_sun[3,:] / LU,            t_values / TU) 

x_earth_wrt_moon = DataInterpolations.LinearInterpolation(states_earth_wrt_moon[1,:] / LU, t_values / TU)
y_earth_wrt_moon = DataInterpolations.LinearInterpolation(states_earth_wrt_moon[2,:] / LU, t_values / TU)
z_earth_wrt_moon = DataInterpolations.LinearInterpolation(states_earth_wrt_moon[3,:] / LU, t_values / TU)

x_moon_wrt_sun   = DataInterpolations.LinearInterpolation(states_moon_wrt_sun[1,:] / LU,   t_values / TU)
y_moon_wrt_sun   = DataInterpolations.LinearInterpolation(states_moon_wrt_sun[2,:] / LU,   t_values / TU)
z_moon_wrt_sun   = DataInterpolations.LinearInterpolation(states_moon_wrt_sun[3,:] / LU,   t_values / TU)


t1_J200      = 1.10829103e9
t1_moon, _   = spkezr("MOON", t1_J200, "ECLIPJ2000", "NONE", "EARTH MOON BARYCENTER")
t1           = t1_J200 / TU
occult1      = zeros(6)
occult1_ECI  = [-297664.1158..., 212871.4885..., -76.96308643..., 1.453863208..., -1.136754567..., -0.032783807...]
occult1[1:3] = (occult1_ECI[1:3] + t1_moon[1:3]) / LU
occult1[4:6] = (occult1_ECI[4:6] + t1_moon[4:6]) / LU * TU

t6_J200      = 1.110817407e9
t6_moon, _   = spkezr("MOON", t6_J200, "ECLIPJ2000", "NONE", "EARTH MOON BARYCENTER")
t6           = t6_J200 / TU
occult6      = zeros(6)
occult6_ECI  = [-365732.5404..., 39738.61049..., -83.77279923..., 1.854101596..., -0.310457164..., 0.010142901...]
occult6[1:3] = (occult6_ECI[1:3] + t6_moon[1:3]) / LU
occult6[4:6] = (occult6_ECI[4:6] + t6_moon[4:6]) / LU * TU




@def ocp begin
    t ∈ [ t1, t6 ], time
    x ∈ R^8, state
    u ∈ R³, control

    thrust = x[8]
    mass = x[7]

    -10 ≤ x₁(t) ≤ 10
    -10 ≤ x₂(t) ≤ 10
    -10 ≤ x₃(t) ≤ 10
    -10 ≤ x₄(t) ≤ 10
    -10 ≤ x₅(t) ≤ 10
    -10 ≤ x₆(t) ≤ 10
    0   ≤ x₇(t) ≤ 1 # careful here ! dry mass > 0   &&   dry_mass ≤ x₇(t) ≤ dry_mass + fuel_mass
    1e-5   ≤ x₈(t) ≤ 50

    0 ≤ sum(u(t).^2) ≤ 1

    x[1:6](t1) == occult1
    x₇(t1) == mass_init 
    x[1:6](t6) == occult6

    ẋ(t) == Fu(t, x(t), u(t)) 
    
    x₈(t6) → min

end


# Works but is slower:
# sol = solve(ocp, grid_size = 100, max_iter = 100, tol = 1e-6, acceptable_tol = 1e-5, disc_method=:gauss_legendre_2)


# Creating initial guess 
ppr = 1e-16 * ones(3)
x0 = [occult1..., 1...,  1..., ]
tspan = (t1, t6)
time_points = LinRange(t1, t6, 500)
prob = ODEProblem(propagate!, x0, tspan, ppr)
solpr = solve(prob, Tsit5(), saveat=time_points)

Nsolu = length(solpr.u)
plot([solpr.u[i][1] for i in range(1, Nsolu)], [solpr.u[i][2] for i in range(1, Nsolu)], [solpr.u[i][3] for i in range(1, Nsolu)])
xlims!(-2, 2)
ylims!(-2, 2)
zlims!(-2, 2)


sol = solve(ocp, 
        init = (time=solpr.t, state=solpr.u, control=ppr), 
        grid_size = 100, max_iter = 1000, tol = 1e-6, acceptable_tol = 1e-5,
        disc_method=:gauss_legendre_2)
    
initial_guess = sol

sol = solve(ocp, init = initial_guess, 
        grid_size = 150, max_iter = 1000, tol = 1e-6, disc_method=:gauss_legendre_2)

    
time_intervals = [t1, t6]

plot(sol,size = (600,1500))

control_matrix = zeros(3, length(sol.time_grid.value))
for i in range(1, length(sol.time_grid.value))
    control_matrix[:,i] =  sol.control.value(sol.time_grid.value[i])
end
control_norm = [norm(col) for col in eachcol(control_matrix)]
plot(sol.time_grid.value, control_norm, linewidth = 2, label = false)
ylims!(0,1)

moon_arcs = []
uncontrolled_arcs = []
plot_trajNV_multiarc_multidyn_withcoasting_1arc(sol, 1, time_intervals, moon_arcs, uncontrolled_arcs)

plot_trajNV_multiarc_multidyn_synodic_1arc(sol, 1, time_intervals, moon_arcs)

主要言語
Julia
スター
135
フォーク
11
平均マージ
7時間 49分
マージ済み PR(30日)
23

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

control-toolbox/OptimalControl.jl のほかの issue

control-toolbox/OptimalControl.jl の issue をすべて見る

似ている issue

Julia の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。