Automatic Control Knowledge Repository

You currently have javascript disabled. Some features will be unavailable. Please consider enabling javascript.

Details for: "furuta pendulum"

Name: furuta pendulum (Key: AEYHV)
Path: ackrep_data/system_models/furuta_pendulum_system View on GitHub
Type: system_model
Short Description: underactuated mechanical system
Created: 2022-11-15
Compatible Environment: default_conda_environment (Key: CDAMA)
Source Code [ / ] simulation.py
import numpy as np
import system_model
from scipy.integrate import solve_ivp

from ackrep_core import ResultContainer
from ackrep_core.system_model_management import save_plot_in_dir
import matplotlib.pyplot as plt
import os

# link to documentation with examples: https://ackrep-doc.readthedocs.io/en/latest/devdoc/contributing_data.html


def simulate():
    """
    simulate the system model with scipy.integrate.solve_ivp

    :return: result of solve_ivp, might contains input function
    """

    model = system_model.Model()

    rhs_xx_pp_symb = model.get_rhs_symbolic()
    print("Computational Equations:\n")
    for i, eq in enumerate(rhs_xx_pp_symb):
        print(f"dot_x{i+1} =", eq)

    rhs = model.get_rhs_func()

    # initial state values
    xx0 = [0, 0, 0, 0]

    t_end = 8
    tt = np.linspace(0, t_end, 10000)
    simulation_data = solve_ivp(rhs, (0, t_end), xx0, t_eval=tt)

    u = []
    for i in range(len(simulation_data.t)):
        u.append(model.uu_func(simulation_data.t[i], xx0)[0])
    simulation_data.uu = u

    save_plot(simulation_data)

    return simulation_data


def save_plot(simulation_data):
    """
    plot your data and save the plot
    access to data via: simulation_data.t   array of time values
                        simulation_data.y   array of data components
                        simulation_data.uu  array of input values

    :param simulation_data: simulation_data of system_model
    :return: None
    """
    # ---------start of edit section--------------------------------------
    # plot of your data
    fig1, axs = plt.subplots(nrows=5, ncols=1, figsize=(12.8, 9))

    # print in axes top left
    axs[0].plot(simulation_data.t, simulation_data.y[0])
    axs[0].set_ylabel(r"$x_1 \, [rad]$")  # y-label
    axs[0].grid()

    axs[1].plot(simulation_data.t, simulation_data.y[1])
    axs[1].set_ylabel(r"$x_2 \, [rad]$")  # y-label
    axs[1].grid()

    axs[2].plot(simulation_data.t, simulation_data.y[2])
    axs[2].set_ylabel(r"$x_3 \, [rad/s]$")  # y-label
    axs[2].grid()

    axs[3].plot(simulation_data.t, simulation_data.y[3])
    axs[3].set_ylabel(r"$x_4 \, [rad/s]$")  # y-label
    axs[3].grid()

    axs[4].plot(simulation_data.t, simulation_data.uu)
    axs[4].set_ylabel(r"$u_1 \, [Nm]$")  # y-label
    axs[4].set_xlabel("Time [s]")  # x-label
    axs[4].grid()

    # ---------end of edit section----------------------------------------

    plt.tight_layout()

    save_plot_in_dir()


def evaluate_simulation(simulation_data):
    """
    assert that the simulation results are as expected

    :param simulation_data: simulation_data of system_model
    :return:
    """
    # ---------start of edit section--------------------------------------
    # fill in final states of simulation to check your model
    # simulation_data.y[i][-1]
    expected_final_state = [149.755395984016, -1.15553895270285, 35.92443402010459, 6.114497336949881]

    # ---------end of edit section----------------------------------------

    rc = ResultContainer(score=1.0)
    simulated_final_state = simulation_data.y[:, -1]
    rc.final_state_errors = [
        simulated_final_state[i] - expected_final_state[i] for i in np.arange(0, len(simulated_final_state))
    ]
    rc.success = np.allclose(expected_final_state, simulated_final_state, rtol=0, atol=1e-2)

    return rc
system_model.py
import sympy as sp
import symbtools as st
from symbtools import modeltools as mt
import importlib
import sys, os

# from ipydex import IPS, activate_ips_on_exception

from ackrep_core.system_model_management import GenericModel, import_parameters

# Import parameter_file
params = import_parameters()


# link to documentation with examples: https://ackrep-doc.readthedocs.io/en/latest/devdoc/contributing_data.html


class Model(GenericModel):
    def initialize(self):
        """
        this function is called by the constructor of GenericModel

        :return: None
        """

        # ---------start of edit section--------------------------------------
        # Define number of inputs -- MODEL DEPENDENT
        self.u_dim = 1

        # Set "sys_dim" to constant value, if system dimension is constant
        self.sys_dim = 4

        # ---------end of edit section----------------------------------------

        # check existence of params file
        self.has_params = True
        self.params = params

    # ----------- SET DEFAULT INPUT FUNCTION ---------- #
    # --------------- Only for non-autonomous Systems
    def uu_default_func(self):
        """
        define input function

        :return:(function with 2 args - t, xx_nv) default input function
        """

        # ---------start of edit section--------------------------------------
        def uu_rhs(t, xx_nv):
            """
            sequence of numerical input values

            :param t:(scalar or vector) time
            :param xx_nv:(vector or array of vectors) numeric state vector
            :return:(list) numeric inputs
            """
            u1 = 0.5

            return [u1]

        # ---------end of edit section----------------------------------------

        return uu_rhs

    # ----------- SYMBOLIC RHS FUNCTION ---------- #

    def get_rhs_symbolic(self):
        """
        define symbolic rhs function

        :return: matrix of symbolic rhs-functions
        """
        if self.dxx_dt_symb is not None:
            return self.dxx_dt_symb

        x1, x2, x3, x4 = self.xx_symb  # state components
        x = sp.Matrix([[x1], [x2]])
        xdot1, xdot2 = sp.symbols("xdot1, xdot2")

        m2, l1, l2, J1, J2, g = self.pp_symb  # parameters

        u1 = self.uu_symb[0]  # inputs

        # positions
        S1 = 0
        G1 = sp.Matrix([l1 * sp.sin(x1), l1 * sp.cos(x1), 0])

        S2 = G1 + sp.Matrix([l2 * sp.sin(x2) * sp.cos(x1), -l2 * sp.sin(x2) * sp.sin(x1), l2 * sp.cos(x2)])

        # velocities
        Sd2 = st.time_deriv(S2, x)

        # kinetic energy
        T_trans = (m2 * Sd2.T * Sd2) / 2
        T_rot = (J1 * xdot1**2 + J2 * xdot2**2) / 2
        T = T_trans[0] + T_rot

        # potential energy
        V = m2 * g * l2 * (sp.cos(x2) - 1)

        external_forces = sp.Matrix([[u1, 0]])
        mod = mt.generate_symbolic_model(T, V, x, external_forces)

        mod.calc_state_eq(simplify=False)

        state_eq = mod.state_eq.subs([(xdot1, x3), (xdot2, x4)])

        return state_eq
parameters.py
import sys
import os
import numpy as np
import sympy as sp

import tabulate as tab


# link to documentation with examples: https://ackrep-doc.readthedocs.io/en/latest/devdoc/contributing_data.html


# set model name
model_name = "furuta pendulum"


# ---------- create symbolic parameters
pp_symb = [m2, l1, l2, J1, J2, g] = sp.symbols("m2, l1, l2, J1, J2, g", real=True)


# ---------- create symbolic parameter functions
# parameter values can be constant/fixed values OR set in relation to other parameters (for example: a = 2*b)
m2_sf = 0.2
l1_sf = 0.5
l2_sf = 0.5
J1_sf = 0.02
J2_sf = 0.02
g_sf = 9.81

# list of symbolic parameter functions
# tailing "_sf" stands for "symbolic parameter function"
pp_sf = [m2_sf, l1_sf, l2_sf, J1_sf, J2_sf, g_sf]


#  ---------- list for substitution
# -- entries are tuples like: (independent symbolic parameter, numerical value)
pp_subs_list = []


# OPTONAL: Dictionary which defines how certain variables shall be written
# in the table - key: Symbolic Variable, Value: LaTeX Representation/Code
# useful for example for complex variables: {Z: r"\underline{Z}"}
latex_names = {}


# ---------- Define LaTeX table

# Define table header
# DON'T CHANGE FOLLOWING ENTRIES: "Symbol", "Value"
tabular_header = ["Parameter Name", "Symbol", "Value", "Unit"]

# Define column text alignments
col_alignment = ["left", "center", "left", "center"]


# Define Entries of all columns before the Symbol-Column
# --- Entries need to be latex code
col_1 = [
    "mass of the pendulum",
    "length of the arm",
    "length of the pendulum",
    "moment of inertia of the arm",
    "moment of inertia of the pendulum",
    "acceleration due to gravity",
]

# contains all lists of the columns before the "Symbol" Column
# --- Empty list, if there are no columns before the "Symbol" Column
start_columns_list = [col_1]


# Define Entries of the columns after the Value-Column
# --- Entries need to be latex code
col_4 = ["kg", "m", "m", r"$kg \cdot m^2$", r"$kg \cdot m^2$", r"$\frac{m}{s^2}$"]

# contains all lists of columns after the FIX ENTRIES
# --- Empty list, if there are no columns after the "Value" column
end_columns_list = [col_4]

Related Problems:
Extensive Material:
Download pdf
Result: Success.
Last Build: Checkout CI Build
Runtime: 7.7 (estimated: 10s)