![]() | |||||||||||||
| |||||||||||||
Hang glider trajectory Description Maximize the total horizontal distance a hang glider flies subject to different configurable wind conditions.
Source Files By clicking on a file name, a preview is opened at the bottom of this page.
Glidert.cpp // (c) 2024-2024 Fair Isaac Corporation /** * Maximize the total horizontal distance a hang glider flies. * * Configure the wind conditions by setting the parameter wind: wind = 0 : no * wind wind = 1 : thermal uplift 250m ahead (positive in a neighborhood of 250m * but drops to zero exponentially away from this point) wind = 2 : wind shear * (a rapidly diminishing head wind that may cause the glider to stall) * * Trapezoidal discretization. * * Based on AMPL models hang_midpt.mod, shear_midpt.mod, stableair_midpt.mod * Source: http://www.orfe.princeton.edu/~rvdb/ampl/nlmodels/hang/ Reference: R. * Bulirsch, E. Nerz, H.J. Pesch, and O. von Stryk, "Combining Direct and * Indirect Methods in Optimal Control: Range Maximization of a Hang Glider", in * "Optimal Control: Calculus of Variations, Optimal Control Theory and * Numerical Methods", ed. by R. Bulirsch, A. Miele, J. Stoer, and K.H. Well * (1993) Birkhauser * * *** This model cannot be run with a Community Licence for the provided data * instance *** */ #include <cmath> #include <iostream> #include <xpress.hpp> using namespace xpress; using namespace xpress::objects; // The functions we use for building the model. using xpress::objects::utils::arctan; using xpress::objects::utils::div; using xpress::objects::utils::exp; using xpress::objects::utils::pow; using xpress::objects::utils::sqrt; class Glidert { public: /** * Wind conditions; possible values: 0, 1, 2 (see description at the beginning * of the file). */ static inline int wind = 1; /** Number of discretization points */ static constexpr int const n = 150; /** Initial horizontal position */ static constexpr double const x_0 = 0.0; /** Initial vertical position */ static constexpr double const y_0 = 1000.0; /** Initial horizontal velocity */ static constexpr double const vx_0 = 13.2275675; /** Initial vertical velocity */ static constexpr double const vy_0 = -1.28750052; /** Final vertical position */ static constexpr double const yN = 900.0; /** Final horizontal velocity */ static constexpr double const vxN = 13.2275675; /** Final vertical velocity */ static constexpr double const vyN = -1.28750052; /** Upward wind-velocity coefficient (m/sec) */ static constexpr double const um = 2.5; /** Upward wind-velocity ratio (m) */ static constexpr double const r = 100.0; /** First drag coefficient constant */ static constexpr double const c0 = 0.034; /** Second drag coefficient constant */ static constexpr double const k = 0.069662; /** Mass of glider & pilot */ static constexpr double const mass = 100.0; /** Wing area */ static constexpr double const span = 14.0; /** Air density */ static constexpr double const rho = 1.13; /** Acceleration due to gravity */ static constexpr double const grav = 9.80665; /** Weight of glider & pilot (= m*g) */ static constexpr double const weight = mass * grav; /** Minimum lift */ static constexpr double const clMin = 0.0; /** Maximum lift */ static constexpr double const clMax = 1.4; /** Minimum velocity */ static constexpr double const velMin = -100.0; /** Maximum velocity */ static constexpr double const velMax = 100.0; /** Minimum acceleration */ static constexpr double const accMin = -3.0; /** Maximum acceleration */ static constexpr double const accMax = 3.0; /** Minimum duration */ static constexpr double const durMin = 10.0; /** Maximum duration */ static constexpr double const durMax = 200.0; static int solve() { XpressProblem prob; // Output all messages. prob.callbacks.addMessageCallback(XpressProblem::console); /**** VARIABLES ****/ // total duration of the flight auto dur = prob.addVariable(durMin, durMax, ColumnType::Continuous, "dur"); // horizontal position at time step t auto x = prob.addVariables(n + 1).withName("x_%d").toArray(); // vertical position at time step t auto y = prob.addVariables(n + 1).withName("y_%d").toArray(); // horizontal velocity at time step t auto vx = prob.addVariables(n + 1) .withName("vx_%d") .withLB(velMin) .withUB(velMax) .toArray(); // vertical velocity at time step t auto vy = prob.addVariables(n + 1) .withName("vy_%d") .withLB(velMin) .withUB(velMax) .toArray(); // derivative of vx at time step t auto vxDot = prob.addVariables(n + 1) .withName("vxDot_%d") .withLB(accMin) .withUB(accMax) .toArray(); // derivative of vy at time step t auto vyDot = prob.addVariables(n + 1) .withName("vyDot_%d") .withLB(accMin) .withUB(accMax) .toArray(); // Lift coefficient (control variable) at time step t auto cLift = prob.addVariables(n + 1) .withName("cLift_%d") .withLB(clMin) .withUB(clMax) .toArray(); // Objective is to maximize the horizontal position in the final time step prob.setObjective(x[n], ObjSense::Maximize); // initial and final states prob.addConstraint(x[0] == x_0); prob.addConstraint(y[0] == y_0); if (wind == 2) { prob.addConstraint(vx[0] == vx_0 - 2.5 * (1 + atan((y_0 - 30) / 10))); } else { prob.addConstraint(vx[0] == vx_0); } prob.addConstraint(vy[0] == vy_0); prob.addConstraint(y[n] == yN); prob.addConstraint(vx[n] == vxN); prob.addConstraint(vy[n] == vyN); // monotonicity prob.addConstraints(n, [&](auto k) { return x[k + 1] >= x[k]; }); // trapezoidal discretization // x[i+1] = x[i] + 0.5*(dur/N)*(vx[i+1] + vx[i]) prob.addConstraints(n, [&](auto i) { return x[i + 1] == x[i] + 0.5 * dur / n * (vx[i + 1] + vx[i]); }); // y[i+1] = y[i] + 0.5*(dur/N)*(vy[i+1] + vy[i]) prob.addConstraints(n, [&](auto i) { return y[i + 1] == y[i] + 0.5 * dur / n * (vy[i + 1] + vy[i]); }); // vx[i+1] = vx[i] + 0.5*(dur/N)*(vxDot[i+1] + vxDot[i]) prob.addConstraints(n, [&](auto i) { return vx[i + 1] == vx[i] + 0.5 * dur / n * (vxDot[i + 1] + vxDot[i]); }); // vy[i+1] = vy[i] + 0.5*(dur/N)*(vyDot[i+1] + vyDot[i]) prob.addConstraints(n, [&](auto i) { return vy[i + 1] == vy[i] + 0.5 * dur / n * (vyDot[i + 1] + vyDot[i]); }); // Adjust velocity for the effects of the wind std::vector<Expression> vxWindAdjusted(n + 1); std::vector<Expression> vyWindAdjusted(n + 1); std::vector<Expression> drag(n + 1); std::vector<Expression> lift(n + 1); std::vector<Expression> sinEta(n + 1); std::vector<Expression> cosEta(n + 1); for (int i = 0; i <= n; i++) { if (wind == 2) { // vx_adj = vx + 5*(1 + arctan((y - 30) / 10)) vxWindAdjusted[i] = vx[i] + 5.0 * (1.0 + arctan((y[i] - 30.0) / 10.0)); } else { vxWindAdjusted[i] = vx[i]; } if (wind == 1) { // upWindVel = (x[i]/R - 2.5)^2 Expression upWindVel = pow(x[i] / r - 2.5, 2.0); // vy_adj = vy - UM * (1 - upWindVel) * e^(-upWindVel) vyWindAdjusted[i] = vy[i] - um * (1.0 - upWindVel) * exp(-upWindVel); } else { vyWindAdjusted[i] = vy[i]; } // vr = sqrt(vx^2 + vy^2) Expression vr = sqrt(pow(vxWindAdjusted[i], 2.0) + pow(vyWindAdjusted[i], 2.0)); // drag coefficient = c0 + K * clift^2 Expression drag_coef = c0 + k * pow(cLift[i], 2.0); // drag = 0.5 * rho * span * drag_coef * vr^2 drag[i] = 0.5 * rho * span * drag_coef * pow(vr, 2.0); // lift = 0.5 * rho * span * cLift * vr^2 lift[i] = 0.5 * rho * span * cLift[i] * pow(vr, 2.0); // sinEta = vy / vr sinEta[i] = vyWindAdjusted[i] / vr; // cosEta = vx / vr cosEta[i] = vxWindAdjusted[i] / vr; } // Equations of motion: F = m * a // vxDot = (-Lift * sinEta - Drag * cosEta) / mass prob.addConstraints(n, [&](auto i) { return vxDot[i + 1] == (-lift[i + 1] * sinEta[i + 1] - drag[i + 1] * cosEta[i + 1]) / mass; }); // vyDot = (Lift * cosEta - Drag * sinEta - weight) / mass prob.addConstraints(n, [&](auto i) { return vyDot[i + 1] == (lift[i + 1] * cosEta[i + 1] - drag[i + 1] * sinEta[i + 1] - weight) / mass; }); // Solve to local optimality since solving to global optimality takes some // time prob.controls.setNlpSolver(XPRS_NLPSOLVER_LOCAL); // Choose between solving with SLP or Knitro // prob.controls().setLocalSolver(XPRS_LOCALSOLVER_XSLP); // Set initial values for the local solve std::vector<double> xInitial(n + 1); std::vector<double> yInitial(n + 1); std::vector<double> vxInitial(n + 1); std::vector<double> vyInitial(n + 1); std::vector<double> vxDotInitial(n + 1); std::vector<double> vyDotInitial(n + 1); std::vector<double> cLiftInitial(n + 1); for (int i = 0; i <= n; i++) { xInitial[i] = 5000 * i / n; yInitial[i] = -100 * i / (n + 1000); vxInitial[i] = vx_0; vyInitial[i] = vy_0; vxDotInitial[i] = 0.0; vyDotInitial[i] = 0.0; cLiftInitial[i] = 0.0; } prob.nlpSetInitVal(x, xInitial); prob.nlpSetInitVal(y, yInitial); prob.nlpSetInitVal(vx, vxInitial); prob.nlpSetInitVal(vy, vyInitial); prob.nlpSetInitVal(vxDot, vxDotInitial); prob.nlpSetInitVal(vyDot, vyDotInitial); prob.nlpSetInitVal(cLift, cLiftInitial); // Dump the problem to disk so that we can inspect it. prob.writeProb("glidert.lp", "l"); // Solve prob.optimize(); // print solution if (prob.attributes.getSolStatus() != SolStatus::Optimal && prob.attributes.getSolStatus() != SolStatus::Feasible) throw std::runtime_error("optimization failed with status " + to_string(prob.attributes.getSolStatus())); auto sol = prob.getSolution(); std::cout << "Flight distance " << x[n].getValue(sol) << " over a time of " << dur.getValue(sol) << "." << std::endl; return 0; } }; int main(void) { Glidert::solve(); return 0; }
| |||||||||||||
© Copyright 2025 Fair Isaac Corporation. |