Exemplo n.º 1
0
def test_quadcopter_navigation():
    opti = asb.Opti()

    N = 300
    time_final = 1
    time = np.linspace(0, time_final, N)

    left_thrust = opti.variable(init_guess=0.5, scale=1, n_vars=N, lower_bound=0, upper_bound=1)
    right_thrust = opti.variable(init_guess=0.5, scale=1, n_vars=N, lower_bound=0, upper_bound=1)

    mass = 0.1

    dyn = asb.FreeBodyDynamics(
        opti_to_add_constraints_to=opti,
        time=time,
        xe=opti.variable(init_guess=np.linspace(0, 1, N)),
        ze=opti.variable(init_guess=np.linspace(0, -1, N)),
        u=opti.variable(init_guess=0, n_vars=N),
        w=opti.variable(init_guess=0, n_vars=N),
        theta=opti.variable(init_guess=np.linspace(np.pi / 2, np.pi / 2, N)),
        q=opti.variable(init_guess=0, n_vars=N),
        X=left_thrust + right_thrust,
        M=(right_thrust - left_thrust) * 0.1 / 2,
        mass=mass,
        Iyy=0.5 * mass * 0.1 ** 2,
        g=9.81,
    )

    opti.subject_to([  # Starting state
        dyn.xe[0] == 0,
        dyn.ze[0] == 0,
        dyn.u[0] == 0,
        dyn.w[0] == 0,
        dyn.theta[0] == np.radians(90),
        dyn.q[0] == 0,
    ])

    opti.subject_to([  # Final state
        dyn.xe[-1] == 1,
        dyn.ze[-1] == -1,
        dyn.u[-1] == 0,
        dyn.w[-1] == 0,
        dyn.theta[-1] == np.radians(90),
        dyn.q[-1] == 0,
    ])

    effort = np.sum(  # The average "effort per second", where effort is integrated as follows:
        np.trapz(left_thrust ** 2 + right_thrust ** 2) * np.diff(time)
    ) / time_final

    opti.minimize(effort)

    sol = opti.solve()
    dyn.substitute_solution(sol)

    assert sol.value(effort) == pytest.approx(0.714563, rel=0.01)

    print(sol.value(effort))
Exemplo n.º 2
0
def test_quadcopter_flip():
    opti = asb.Opti()

    N = 300
    time_final = opti.variable(init_guess=1, lower_bound=0)
    time = np.linspace(0, time_final, N)

    left_thrust = opti.variable(init_guess=0.7, scale=1, n_vars=N, lower_bound=0, upper_bound=1)
    right_thrust = opti.variable(init_guess=0.6, scale=1, n_vars=N, lower_bound=0, upper_bound=1)

    mass = 0.1

    dyn = asb.FreeBodyDynamics(
        opti_to_add_constraints_to=opti,
        time=time,
        xe=opti.variable(init_guess=np.linspace(0, 1, N)),
        ze=opti.variable(init_guess=0, n_vars=N),
        u=opti.variable(init_guess=0, n_vars=N),
        w=opti.variable(init_guess=0, n_vars=N),
        theta=opti.variable(init_guess=np.linspace(np.pi / 2, np.pi / 2 - 2 * np.pi, N)),
        q=opti.variable(init_guess=0, n_vars=N),
        X=left_thrust + right_thrust,
        M=(right_thrust - left_thrust) * 0.1 / 2,
        mass=mass,
        Iyy=0.5 * mass * 0.1 ** 2,
        g=9.81,
    )

    opti.subject_to([  # Starting state
        dyn.xe[0] == 0,
        dyn.ze[0] == 0,
        dyn.u[0] == 0,
        dyn.w[0] == 0,
        dyn.theta[0] == np.radians(90),
        dyn.q[0] == 0,
    ])

    opti.subject_to([  # Final state
        dyn.xe[-1] == 1,
        dyn.ze[-1] == 0,
        dyn.u[-1] == 0,
        dyn.w[-1] == 0,
        dyn.theta[-1] == np.radians(90 - 360),
        dyn.q[-1] == 0,
    ])

    opti.minimize(time_final)

    sol = opti.solve(verbose=False)
    dyn.substitute_solution(sol)

    assert sol.value(time_final) == pytest.approx(0.824, abs=0.01)
def test_airfoil_multielement():
    a = asb.AirfoilInviscid(airfoil=[
        asb.Airfoil("e423").repanel(n_points_per_side=50),
        asb.Airfoil("naca6408").repanel(n_points_per_side=25).scale(
            0.4, 0.4).rotate(np.radians(-20)).translate(0.9, -0.05),
    ],
                            op_point=asb.OperatingPoint(velocity=1, alpha=5))
def default_CL_function(alpha, Re, mach, deflection):
    """
    Lift coefficient.
    """
    print_default_warning()
    Cl_inc = 2 * np.pi * np.radians(alpha)
    beta = (1 - mach)**2

    Cl = Cl_inc * beta
    return Cl
Exemplo n.º 5
0
    def compute_rotation_matrix_wind_to_geometry(self) -> np.ndarray:
        """
        Computes the 3x3 rotation matrix that transforms from wind axes to geometry axes.

        Returns: a 3x3 rotation matrix.

        """

        alpha_rotation = np.rotation_matrix_3D(angle=np.radians(self.alpha),
                                               axis=np.array([0, 1, 0]),
                                               _axis_already_normalized=True)
        beta_rotation = np.rotation_matrix_3D(angle=np.radians(self.beta),
                                              axis=np.array([0, 0, 1]),
                                              _axis_already_normalized=True)
        axes_flip = np.rotation_matrix_3D(
            angle=np.pi,
            axis=np.array([0, 1, 0]),
            _axis_already_normalized=True
        )  # Since in geometry axes, X is downstream by convention, while in wind axes, X is upstream by convetion. Same with Z being up/down respectively.

        r = axes_flip @ alpha_rotation @ beta_rotation  # where "@" is the matrix multiplication operator

        return r
Exemplo n.º 6
0
def scattering_factor(elevation_angle):
    """
    Calculates a scattering factor (a factor that gives losses due to atmospheric scattering at low elevation angles).
    Source: AeroSandbox/studies/SolarPanelScattering
    :param elevation_angle: Angle between the horizon and the sun [degrees]
    :return: Fraction of the light that is not lost to scattering.
    """
    elevation_angle = np.clip(elevation_angle, 0, 90)
    theta = 90 - elevation_angle  # Angle between panel normal and the sun, in degrees

    # # Model 1
    # c = (
    #     0.27891510500505767300438719757949,
    #     -0.015994330894744987481281839336589,
    #     -19.707332432605799255043166340329,
    #     -0.66260979582573353852126274432521
    # )
    # scattering_factor = c[0] + c[3] * theta_rad + cas.exp(
    #     c[1] * (
    #             cas.tan(theta_rad) + c[2] * theta_rad
    #     )
    # )

    # Model 2
    c = (
        -0.04636,
        -0.3171
    )
    scattering_factor = np.exp(
        c[0] * (
                np.tand(theta * 0.999) + c[1] * np.radians(theta)
        )
    )

    # # Model 3
    # p1 = -21.74
    # p2 = 282.6
    # p3 = -1538
    # p4 = 1786
    # q1 = -923.2
    # q2 = 1456
    # x = theta_rad
    # scattering_factor = ((p1*x**3 + p2*x**2 + p3*x + p4) /
    #            (x**2 + q1*x + q2))

    # Keep this:
    # scattering_factor = cas.fmin(cas.fmax(scattering_factor, 0), 1)
    return scattering_factor
Exemplo n.º 7
0
        plt.ylim(-4, 1.1)
        plt.gca().invert_yaxis()
        plt.xlabel(r"$x/c$")
        plt.ylabel(r"$C_p$")
        plt.title(r"$C_p$ on Surface")
        plt.tight_layout()
        if show:
            plt.show()


if __name__ == '__main__':
    a = AirfoilInviscid(
        airfoil=[
            # Airfoil("naca4408")
            #     .repanel(50)
            Airfoil("e423").repanel(n_points_per_side=50),
            Airfoil("naca6408").repanel(n_points_per_side=25).scale(
                0.4, 0.4).rotate(np.radians(-20)).translate(0.9, -0.05),
        ],
        op_point=OperatingPoint(
            velocity=1,
            alpha=5,
        ))
    a.draw_streamlines()
    a.draw_cp()

    opti2 = Opti()
    b = AirfoilInviscid(airfoil=Airfoil("naca4408"),
                        op_point=OperatingPoint(velocity=1, alpha=5),
                        opti=opti2)
Exemplo n.º 8
0
def display_graph(n_clicks, alpha, height, streamline_density,
                  operating_checklist, *kulfan_inputs):
    ### Figure out if a button was pressed
    global n_clicks_last
    if n_clicks is None:
        n_clicks = 0

    analyze_button_pressed = n_clicks > n_clicks_last
    n_clicks_last = n_clicks

    ### Parse the checklist
    ground_effect = "ground_effect" in operating_checklist

    ### Start constructing the figure
    airfoil = asb.Airfoil(coordinates=asb.get_kulfan_coordinates(
        lower_weights=np.array(kulfan_inputs[n_kulfan_inputs_per_side:]),
        upper_weights=np.array(kulfan_inputs[:n_kulfan_inputs_per_side]),
        TE_thickness=0,
        enforce_continuous_LE_radius=False,
        n_points_per_side=200,
    ))

    ### Do coordinates output
    coordinates_output = "\n".join(
        ["```"] + ["AeroSandbox Airfoil"] +
        ["\t%f\t%f" % tuple(coordinate)
         for coordinate in airfoil.coordinates] + ["```"])

    ### Continue doing the airfoil things
    airfoil = airfoil.rotate(angle=-np.radians(alpha))
    airfoil = airfoil.translate(0, height + 0.5 * np.sind(alpha))
    fig = go.Figure()
    fig.add_trace(
        go.Scatter(
            x=airfoil.x(),
            y=airfoil.y(),
            mode="lines",
            name="Airfoil",
            fill="toself",
            line=dict(color="blue"),
        ))

    ### Default text output
    text_output = 'Click "Analyze" to compute aerodynamics!'

    xrng = (-0.5, 1.5)
    yrng = (-0.6, 0.6) if not ground_effect else (0, 1.2)

    if analyze_button_pressed:

        analysis = asb.AirfoilInviscid(
            airfoil=airfoil.repanel(50),
            op_point=asb.OperatingPoint(
                velocity=1,
                alpha=0,
            ),
            ground_effect=ground_effect,
        )

        x = np.linspace(*xrng, 100)
        y = np.linspace(*yrng, 100)
        X, Y = np.meshgrid(x, y)
        u, v = analysis.calculate_velocity(x_field=X.flatten(),
                                           y_field=Y.flatten())
        U = u.reshape(X.shape)
        V = v.reshape(Y.shape)

        streamline_fig = ff.create_streamline(
            x,
            y,
            U,
            V,
            arrow_scale=1e-16,
            density=streamline_density,
            line=dict(color="#ff82a3"),
            name="Streamlines",
        )

        fig = go.Figure(data=streamline_fig.data + fig.data)

        text_output = make_table(
            pd.DataFrame({
                "Engineering Quantity": ["C_L"],
                "Value": [f"{analysis.Cl:.3f}"]
            }))

    fig.update_layout(
        xaxis_title="x/c",
        yaxis_title="y/c",
        showlegend=False,
        yaxis=dict(scaleanchor="x", scaleratio=1),
        margin={"t": 0},
        title=None,
    )

    fig.update_xaxes(range=xrng)
    fig.update_yaxes(range=yrng)

    return fig, text_output, [coordinates_output]
Exemplo n.º 9
0
        plt.tight_layout()
        if show:
            plt.show()


if __name__ == '__main__':
    a = AirfoilInviscid(
        airfoil=[
            # Airfoil("naca4408")
            #     .repanel(50)
            Airfoil("e423")
                .repanel(n_points_per_side=50),
            Airfoil("naca6408")
                .repanel(n_points_per_side=25)
                .scale(0.4, 0.4)
                .rotate(np.radians(-20))
                .translate(0.9, -0.05),
        ],
        op_point=OperatingPoint(
            velocity=1,
            alpha=5,
        )
    )
    a.draw_streamlines()
    a.draw_cp()

    opti2 = Opti()
    b = AirfoilInviscid(
        airfoil=Airfoil("naca4408"),
        op_point=OperatingPoint(
            velocity=1,
Exemplo n.º 10
0
def CL_function(alpha, Re, mach, deflection):
    return 2 * np.pi * np.radians(alpha)