MyEnigma

とある自律移動システムエンジニアのブログです。#Robotics #Programing #C++ #Python #MATLAB #Vim #Mathematics #Book #Movie #Traveling #Mac #iPhone

Model Predictive Controlによる倒立振子制御Pythonプログラム

目次

はじめに

これまで何度か

モデル予測制御(Model Predictive Control:MPC)に

関する記事を書きましたが、

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

 

これらの記事を元に、

倒立振子の制御プログラムを書かれた方がいらっしゃいました。

qiita.com

 

自分も実際に倒立振子の制御シミュレーションをしてみたいと

思ったので、

今回はそのシミュレーションコードの紹介をしたいと思います。

 

Model Predictive Controlによる倒立振子制御Pythonプログラム

PyAdvancedControl/animation2.gif at master · AtsushiSakai/PyAdvancedControl

下記が倒立振子制御のPythonシミュレーションになります。

#! /usr/bin/python
"""
Inverted Pendulum MPC control
author: Atsushi Sakai
"""

import matplotlib.pyplot as plt
import numpy as np
import math
import time
import cvxpy
from matplotrecorder import matplotrecorder


l_bar = 2.0  # length of bar
M = 1.0  # [kg]
m = 0.3  # [kg]
g = 9.8  # [m/s^2]

Q = np.diag([0.0, 1.0, 1.0, 0.0])
R = np.diag([0.01])
nx = 4
nu = 1   
T = 30  
delta_t = 0.1

animation = False


def main():
    x0 = np.array([
        [0.0],
        [0.0],
        [0.3],
        [0.0]
    ])

    x = np.copy(x0)

    for i in range(50):

        ox, dx, otheta, dtheta, ou = mpc_control(x)
        u = ou[0]
        x = simulation(x, u)
        print(i)
        print(x)
        print(u)

        plt.clf()
        px = float(x[0])
        theta = float(x[2])
        show_cart(px, theta)
        plt.xlim([-5.0, 2.0])
        plt.pause(0.001)

        if animation:
            matplotrecorder.save_frame()

    if animation:
        matplotrecorder.save_movie("animation.gif", 0.1)


def simulation(x, u):

    A, B = get_model_matrix()

    x = np.dot(A, x) + np.dot(B, u)

    return x


def mpc_control(x0):

    x = cvxpy.Variable(nx, T + 1)
    u = cvxpy.Variable(nu, T)

    A, B = get_model_matrix()

    cost = 0.0
    constr = []
    for t in range(T):
        cost += cvxpy.quad_form(x[:, t + 1], Q)
        cost += cvxpy.quad_form(u[:, t], R)
        constr += [x[:, t + 1] == A * x[:, t] + B * u[:, t]]
    constr += [x[:, 0] == x0]
    prob = cvxpy.Problem(cvxpy.Minimize(cost), constr)

    start = time.time()
    prob.solve(verbose=False)
    elapsed_time = time.time() - start
    print("calc time:{0} [sec]".format(elapsed_time))

    if prob.status == cvxpy.OPTIMAL:
        ox = get_nparray_from_matrix(x.value[0, :])
        dx = get_nparray_from_matrix(x.value[1, :])
        theta = get_nparray_from_matrix(x.value[2, :])
        dtheta = get_nparray_from_matrix(x.value[3, :])

        ou = get_nparray_from_matrix(u.value[0, :])

    return ox, dx, theta, dtheta, ou


def get_nparray_from_matrix(x):
    u"""
    get build-in list from matrix
    """
    return np.array(x).flatten()


def get_model_matrix():

    # Model Parameter
    A = np.array([
        [0.0, 1.0, 0.0, 0.0],
        [0.0, 0.0, m * g / M, 0.0],
        [0.0, 0.0, 0.0, 1.0],
        [0.0, 0.0, g * (M + m) / (l_bar * M), 0.0]
    ])
    A = np.eye(nx) + delta_t * A

    B = np.array([
        [0.0],
        [1.0 / M],
        [0.0],
        [1.0 / (l_bar * M)]
    ])
    B = delta_t * B

    return A, B


def flatten(a):
    return np.array(a).flatten()


def show_cart(xt, theta):
    cart_w = 1.0
    cart_h = 0.5
    radius = 0.1

    cx = np.matrix([-cart_w / 2.0, cart_w / 2.0, cart_w /
                    2.0, -cart_w / 2.0, -cart_w / 2.0])
    cy = np.matrix([0.0, 0.0, cart_h, cart_h, 0.0])
    cy += radius * 2.0

    cx = cx + xt

    bx = np.matrix([0.0, l_bar * math.sin(-theta)])
    bx += xt
    by = np.matrix([cart_h, l_bar * math.cos(-theta) + cart_h])
    by += radius * 2.0

    angles = np.arange(0.0, math.pi * 2.0, math.radians(3.0))
    ox = [radius * math.cos(a) for a in angles]
    oy = [radius * math.sin(a) for a in angles]

    rwx = np.copy(ox) + cart_w / 4.0 + xt
    rwy = np.copy(oy) + radius
    lwx = np.copy(ox) - cart_w / 4.0 + xt
    lwy = np.copy(oy) + radius

    wx = np.copy(ox) + float(bx[0, -1])
    wy = np.copy(oy) + float(by[0, -1])

    plt.plot(flatten(cx), flatten(cy), "-b")
    plt.plot(flatten(bx), flatten(by), "-k")
    plt.plot(flatten(rwx), flatten(rwy), "-k")
    plt.plot(flatten(lwx), flatten(lwy), "-k")
    plt.plot(flatten(wx), flatten(wy), "-k")
    plt.title("x:" + str(round(xt, 2)) + ",theta:" +
              str(round(math.degrees(theta), 2)))

    plt.axis("equal")


if __name__ == '__main__':
    main()

下記のGithubリポジトリでも公開しています。

github.com

 

基本的には、前述の記事のシミュレーションそのままですが、

  • 倒立振子のアニメーション動画機能を追加

  • コードの最適化(Problem関数をループの中で呼ばない)

などを実施しています。

 

上記のコードを実行すると、

冒頭の動画のように倒立振子のシミュレーションが実施されます。

今回はMPCのコスト関数で、振り子の角度と、

台車の速度をそれぞれ0にするように制御しているため、

初期値で倒れてかけていた振り子を、

台車が左に移動して振り子を立て直し、

その後台車も停止していることがわかります。

 

本当は倒立振子のシミュレーションを、

線形モデルではなく、

元の微分方程式をベースにした、

ちゃんとしたシミュレーションにしたかったのですが、

挫折しました。。。笑。

 

参考資料

qiita.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

   

MyEnigma Supporters

もしこの記事が参考になり、

ブログをサポートしたいと思われた方は、

こちらからよろしくお願いします。

myenigma.hatenablog.com