ZMP

永井 忠一 2019.8.17


Zero-Moment Point

床反力の中心点

(side view)

バランスが取れているときは、ZMPが支持多角形のほぼ中央にある。転倒しているときには、ZMPが支持多角形の縁に達して、その縁を中心にして回転している。ZMPをできるだけ支持多角形の中央にある状態にするのが、バランス制御の基本的な戦略になる。

支持多角形

(top view)

ZMPは支持多角形の領域の外に出ることはない。

3次元のモーメント

外積(cross product)\[ \boldsymbol{\tau}=\boldsymbol{p}\times\boldsymbol{f} \]

モーメント=位置×力\[ \begin{bmatrix} \tau_x \\ \tau_y \\ \tau_z \end{bmatrix}=\begin{bmatrix} x \\ y \\ z \end{bmatrix}\times\begin{bmatrix} f_x \\ f_y \\ f_z \end{bmatrix}=\begin{bmatrix} yf_{z}-zf_{y} \\ zf_{x}-xf_{z} \\ xf_{y}-yf_{x} \end{bmatrix} \]

(右手座標系)

2次元のモーメント

3次元の1成分が0になったと考える\[ \boldsymbol{\tau}=\boldsymbol{p}\times\boldsymbol{f} \]

モーメント=位置×力\[ \begin{bmatrix} 0 \\ \tau_y \\ 0 \end{bmatrix}=\begin{bmatrix} x \\ 0 \\ z \end{bmatrix}\times\begin{bmatrix} f_x \\ 0 \\ f_z \end{bmatrix}=\begin{bmatrix} 0 \\ zf_{x}-xf_{z} \\ 0 \end{bmatrix} \]\[ \tau=\tau_y=zf_x-xf_z \]

具体的なZMPの計算

簡単な例

ZMPまわりのモーメント(の水平成分)は0\[ \begin{align} f_{1}(p-x_{1})-f_{2}(x_{2}-p)&=0 \\ f_{1}p-f_{1}x_{1}-f_{2}f_{2}+f_{2}p&=0 \\ (f_1+f_2)p&=f_{1}x_{1}+f_{2}x_{2} \\ p&=\frac{f_{1}x_{1}+f_{2}x_{2}}{f_1+f_2} \end{align} \]

式の確認。全体重がつま先に集中している場合、かかとに作用している力f1=0、つま先だけにf2がかかる\[ p=\frac{0x_{1}+f_{2}x_{2}}{0+f_2}=x_2 \]ZMPはつま先に一致する

6軸力センサーによるZMPの計測

市販されている6軸力センサーの例(株式会社レプトリノ)[http://www.leptrino.co.jp/P_CFS.html]

6軸力センサーで計測される値: Fx, Fz(床からの垂直方向の力), τ(Y軸回りのモーメント)

ZMPの計算\[ \begin{align} -\tau&=F_{z}p \\ p&=-\frac{\tau}{F_z} \end{align} \](モーメント[Nm]/力[N]で)位置の次元の量になる。-τのマイナスは回転軸の方向

(実際には、6軸力センサーは足の裏に付いていない)

(6軸力センサーを取り付けてある)かかとの高さを考慮したZMPの計算\[ p=\frac{-\tau-aF_x}{F_z} \]

両足支持期のZMP

(上記は片足のZMP)

右足と左足それぞれについてZMPを計算して、それを力で配分することによって全体のZMPを計算する\[ p=\frac{f_R}{f_R+f_L}p_R+\frac{f_L}{f_R+f_L}p_L \]

ZMPと重心運動の関係

テーブル台車モデルで定式化(簡単なモデルで近似)

ZMPまわりに重力と台車の加速度が作り出すモーメントが0\[ \tau=(x-p)Mg-Z_{h}M\ddot{x}=0 \](ZMPまわりのモーメントは0)

ZMP方程式の導出(pに関して解く)\[ \begin{align} (x-p)Mg-Z_{h}M\ddot{x}&=0 \\ xMg-pMg-Z_{h}M\ddot{x}&=0 \\ -pMg&=-xMg+Z_{h}M\ddot{x} \\ -p&=\frac{-xMg+Z_{h}M\ddot{x}}{Mg} \\ -p&=-x+\frac{Z_{h}\ddot{x}}{g} \\ p&=x-\frac{Z_{h}}{g}\ddot{x} \end{align} \]

ZMP方程式の意味

ZMP方程式を実際に計算

ZMP方程式を変形\[ \begin{align} p&=x-\frac{Z_{h}}{g}\ddot{x} \\ -\frac{Z_{h}}{g}\ddot{x}&=p-x \\ \ddot{x}&=-\frac{g}{Z_h}(p-x) \\ \ddot{x}&=\frac{g}{Z_h}(x-p) \end{align} \]

台車(重心)の加速度\[ \ddot{x}=\frac{9.8}{0.98}(0.4-0)=4\,\mathrm{m/s^2} \]

ZMP方程式の解法

ZMP方程式は重心の運動xからZMPの位置pを求める式。本当に行いたいのは、ZMPを与えてxを求めたい。求めたいのは未知数の重心軌道x

ZMP →  \( \boxed{ p=x-\frac{Z_{h}}{g}\ddot{x} } \)  → 重心軌道x

ZMPはあらかじめ決定しておく(一歩を何秒ぐらいで歩いてほしいか、一歩をどれくらいの距離で動いてほしいか)

歩行パターン生成問題

連続だったZMP方程式を離散化する\[ p_i=x_i-\frac{Z_h}{g}\ddot x_i \]Δtは、たとえば5ms間隔。ΔtごとのZMP位置を考えて、Δtごとの重心位置を計算したい。

\( \ddot{x} \)を置き換える。加速度を位置の差分で表現する\[ \ddot{x_i}=\frac{1}{\Delta t}(v_{i+1}-v_i)=\frac{1}{\Delta t}(\frac{x_{i+1}-x_i}{\Delta t}-\frac{x_{i}-x_{i-1}}{\Delta t})\]

速度の差分は加速度。さらに、速度は位置の差分から計算できる。

式を整理してまとめる\[ \ddot x_i=\frac{1}{\Delta t^2}(x_{i-1}-2x_{i}+x_{i+1}) \](加速度の近似式)

\( \ddot x_i \)はxで表現できるので、これをZMP方程式に代入して、整理する。\[ \begin{align} p_i&=x_i-\frac{Z_h}{g}\frac{1}{\Delta t^2}(x_{i-1}-2x_{i}+x_{i+1}) \\ p_i&=\left(-\frac{Z_h}{g\Delta t^2}\right)x_{i-1}+\left(1+2\frac{Z_h}{g\Delta t^2}\right)x_i+\left(-\frac{Z_h}{g\Delta t^2}\right)x_{i+1} \end{align} \]

g, Zh, Δtはすべて定数。得られた、離散化されたZMP方程式\[ p_i=ax_{i-1}+bx_{i}+cx_{i+1} \](a, b, cはまとめられた定数)

知りたいのは重心の位置x、ZMPの位置pは与えられている。これを解くためには、未知数が3つで、既知の値がpだけなのでこれだけでは解けない。

方程式を並べる。たとえば、時刻0から時刻99まで\[ p_0=ax_0+bx_0+cx_1 \\ p_1=ax_0+bx_1+cx_2 \\ p_2=ax_1+bx_2+cx_3 \\ \vdots \\ p_{98}=ax_{97}+bx_{98}+cx_{99} \\ p_{99}=ax_{98}+bx_{99}+cx_{99} \]

これをベクトルと行列(マトリクス)で書き直す\[ \begin{bmatrix} p_0 \\ p_1 \\ p_2 \\ \vdots \\ p_{98} \\ p_{99} \end{bmatrix}=\begin{bmatrix} a+b & c & & & & \\ a & b & c & & & \\ & a & b & c & & \\ & & \ddots & \ddots & \ddots & \\ & & & a & b & c \\ & & & & a & b+c \end{bmatrix}\begin{bmatrix} x_0 \\ x_1 \\ x_3 \\ \vdots \\ x_{98} \\ x_{99} \end{bmatrix} \]

こう書いてしまえば線形代数の問題になる \[ \boldsymbol p=M\boldsymbol x \](pは100次元のZMPベクトル、xは100次元の重心ベクトル、100x100の大きな係数行列M)これもZMP方程式(重心軌道とZMPの関係式)

ZMP方程式がこう書かれたのであれば、逆行列を求めれば与えられたZMPから重心軌道xが求められる\[ \boldsymbol x=M^{-1}\boldsymbol p \](ZMPから重心軌道を得る)

計算デモプログラムの実行

産総研の梶田先生が公開されているコード「BasicGaitGen」を使わせていただきました。m(_o_)m

Octaveを起動して「tridiagonal_demo」を実行

$ octave --no-gui
GNU Octave, version 4.0.0
Copyright (C) 2015 John W. Eaton and others.
This is free software; see the source code for copying conditions.
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  For details, type 'warranty'.

Octave was configured for "x86_64-pc-linux-gnu".

Additional information about Octave is available at http://www.octave.org.

Please contribute if you find this software useful.
For more information, visit http://www.octave.org/get-involved.html

Read http://www.octave.org/bugs.html to learn how to submit bug reports.
For information about changes from previous versions, type 'news'.

>> tridiagonal_demo

与えている目標ZMPの軌跡

(処理ごとに「pause」しているので、プロンプトから何かキーを押下)

行列(三重対角行列とその逆行列)の可視化

>> figure; imagesc(M)
>> figure; imagesc(M^-1)
MM-1

重心の計算

>> x = M^-1 * zmpx;
>> y = M^-1 * zmpy;

ZMPと重心(の床面投影点)をプロット

X軸Y軸

重心の運動を足の関節角度に変換する

逆運動学

実時間で重心軌道を作り出す方法について

《積み残し》

予見制御(Preview control)

《積み残し》

歩行安定化制御技術について

《積み残し》

参考書


永井 忠一 2025.8.11

ZMP (Zero Moment Point)

Momentが0となる点(床反力の圧力中心(Center of Pressure)点)

計算ZMP(Computed ZMP)、「(計算上の)計画上のゼロモーメントポイント」について)《今後》

線形倒立振子(Linear Inverted Pendulum)

(振り子は、「しんし」とも読む)

  • m は集中質量
  • h は重心高さ
  • l は脚長
  • g は重力加速度
  • a は重心の加速度(\( a = \ddot x \))
  • N' は床反力

重心高さ h を一定に保つ。脚長 l は可変(ただし、脚の長さは有限)

(\( h = \text{const.} \) は、過剰拘束)《積み残し》

作図プログラム

Python
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

h = 500 # mm
x = 150 # mm

plt.clf(); plt.grid()
plt.gca().set_aspect('equal')
plt.scatter([x], [h], label=r'$ m $')
plt.plot([x, 0], [h, 0], ls='--', label=r'$ l $')
plt.scatter([0], [0], marker='.', label='ZMP')
plt.scatter([x], [0], marker='.', label=r'$ x_\mathrm{CoM} $')
plt.scatter([h], [0], marker='none') # adhoc
plt.legend(loc='best')
plt.xticks([0, x], [0, r'$ x $']); plt.yticks([0, h], [0, r'$ h $'])

plt.quiver(x, h, 0, -h, scale=2000); plt.text(x + 10, h - 60, r'$ mg $')
plt.quiver(x, h, -x, 0, scale=2000); plt.text(x - 40, h, r'$ ma $', ha='right', va='bottom')
plt.quiver(0, 0, x, h, scale=2000); plt.text(0 + 30, 0 + 50, r"$ N' $")

plt.title('LIP (Linear Inverted Pendulum)')
plt.savefig('fig_LIP.svg')

微分方程式\[ \begin{align} \frac{ma}{mg} = \frac{m\ddot x}{mg} &= \frac{x}{h} \\ \frac{\ddot x}{g} &= \frac{x}{h} \\ \ddot x &= \frac{g}{h}x \end{align} \]

「\( \ddot x = \frac{g}{h}x \)」を解く(ode2() 関数を利用)

Maxima
$ maxima

Maxima 5.46.0 https://maxima.sourceforge.io
using Lisp GNU Common Lisp (GCL) GCL 2.6.14 git tag Version_2_6_15pre7
Distributed under the GNU Public License. See the file COPYING.
Dedicated to the memory of William Schelter.
The function bug_report() provides bug reporting information.

(%i1) assume(g/h > 0);
(%o1)                              [g h > 0]
(%i2) 'diff(x, t, 2) = (g/h)*x;
                                    2
                                   d x   g x
(%o2)                              --- = ---
                                     2    h
                                   dt
(%i3) ode2(%, x, t);
                              sqrt(g) t           sqrt(g) t
                              ---------         - ---------
                               sqrt(h)             sqrt(h)
(%o3)               x = %k1 %e          + %k2 %e
(%i4) tex(%);
\[ x={\it \%k}_{1}\,e^{{{\sqrt{g}\,t}\over{\sqrt{h}}}}+{\it \%k}_{2}\, e^ {- {{\sqrt{g}\,t}\over{\sqrt{h}}} } \]

得られた、一般解\[ x = C_1 e^{\sqrt\frac{g}{h}t} + C_2 e^{-\sqrt\frac{g}{h}t} \]

初期条件を用いて任意定数を決定する。(t = 0 のとき)「初期位置 x0、初期速度 v0」として、C1, C2 を求める

Maxima
(%i1) x = C_1*exp(sqrt(g/h)*t) + C_2*exp(-sqrt(g/h)*t);
                            sqrt(g/h) t         - sqrt(g/h) t
(%o1)             x = C_1 %e            + C_2 %e
(%i2) eq1: x_0 = subst(t = 0, rhs(%));
(%o2)                           x_0 = C_2 + C_1
(%i3) diff(rhs(%th(2)), t);
                     g    sqrt(g/h) t            g    - sqrt(g/h) t
(%o3)       C_1 sqrt(-) %e            - C_2 sqrt(-) %e
                     h                           h
(%i4) eq2: v_0 = subst(t = 0, %);
                                       g             g
(%o4)                   v_0 = C_1 sqrt(-) - C_2 sqrt(-)
                                       h             h
(%i5) solve([eq1, eq2], [C_1, C_2]);
                          g                        g
                     sqrt(-) x_0 + v_0        sqrt(-) x_0 - v_0
                          h                        h
(%o5)        [[C_1 = -----------------, C_2 = -----------------]]
                                g                        g
                         2 sqrt(-)                2 sqrt(-)
                                h                        h
(%i6) string(%[1][1]);
(%o6)               C_1 = (sqrt(g/h)*x_0+v_0)/(2*sqrt(g/h))
(%i7) string(%th(2)[1][2]);
(%o7)               C_2 = (sqrt(g/h)*x_0-v_0)/(2*sqrt(g/h))

初期位置が「x0 = 0」(原点)、初期速度が「v0 = 0」(静止)の場合の C1, C2 を、それぞれ確認

Maxima
(%i1) x = C_1*exp(sqrt(g/h)*t) + C_2*exp(-sqrt(g/h)*t);
                            sqrt(g/h) t         - sqrt(g/h) t
(%o1)             x = C_1 %e            + C_2 %e
(%i2) subst(t = 0, rhs(%)) = 0;
(%o2)                            C_2 + C_1 = 0
(%i3) v_0 = subst(t = 0, diff(rhs(%th(2)), t));
                                       g             g
(%o3)                   v_0 = C_1 sqrt(-) - C_2 sqrt(-)
                                       h             h
(%i4) solve([%th(2), %], [C_1, C_2]);
                               v_0                v_0
(%o4)               [[C_1 = ---------, C_2 = - ---------]]
                                   g                  g
                            2 sqrt(-)          2 sqrt(-)
                                   h                  h
(%i5) subst(x_0 = 0, C_1 = (sqrt(g/h)*x_0+v_0)/(2*sqrt(g/h))); /* verify */
                                         v_0
(%o5)                           C_1 = ---------
                                             g
                                      2 sqrt(-)
                                             h
(%i6) subst(x_0 = 0, C_2 = (sqrt(g/h)*x_0-v_0)/(2*sqrt(g/h))); /* verify */
                                          v_0
(%o6)                          C_2 = - ---------
                                              g
                                       2 sqrt(-)
                                              h
(%i7) %o1;
                            sqrt(g/h) t         - sqrt(g/h) t
(%o7)             x = C_1 %e            + C_2 %e
(%i8) x_0 = subst(t = 0, rhs(%));
(%o8)                           x_0 = C_2 + C_1
(%i9) subst(t = 0, diff(rhs(%th(2)), t)) = 0;
                                  g             g
(%o9)                    C_1 sqrt(-) - C_2 sqrt(-) = 0
                                  h             h
(%i10) solve([%th(2), %], [C_1, C_2]);
                                   x_0        x_0
(%o10)                     [[C_1 = ---, C_2 = ---]]
                                    2          2
(%i11) subst(v_0 = 0, C_1 = (sqrt(g/h)*x_0+v_0)/(2*sqrt(g/h))); /* verify */
                                         x_0
(%o11)                             C_1 = ---
                                          2
(%i12) subst(v_0 = 0, C_2 = (sqrt(g/h)*x_0-v_0)/(2*sqrt(g/h))); /* verify */
                                         x_0
(%o12)                             C_2 = ---
                                          2

(ic2() 関数を利用)

Maxima
(%i1) assume(g/h > 0);
(%o1)                              [g h > 0]
(%i2) ode2('diff(x, t, 2) = (g/h)*x, x, t);
                              sqrt(g) t           sqrt(g) t
                              ---------         - ---------
                               sqrt(h)             sqrt(h)
(%o2)               x = %k1 %e          + %k2 %e
(%i3) ic2(%, t = 0, x = x_0, 'diff(x, t) = v_0);
            sqrt(g) t
            ---------
             sqrt(h)
          %e          (g x_0 + sqrt(g) sqrt(h) v_0)
(%o3) x = -----------------------------------------
                             2 g
                                        sqrt(g) t
                                      - ---------
                                         sqrt(h)
                                    %e            (g x_0 - sqrt(g) sqrt(h) v_0)
                                  + -------------------------------------------
                                                        2 g
(%i4) v = diff(rhs(%), t);
            sqrt(g) t
            ---------
             sqrt(h)
          %e          (g x_0 + sqrt(g) sqrt(h) v_0)
(%o4) v = -----------------------------------------
                      2 sqrt(g) sqrt(h)
                                        sqrt(g) t
                                      - ---------
                                         sqrt(h)
                                    %e            (g x_0 - sqrt(g) sqrt(h) v_0)
                                  - -------------------------------------------
                                                 2 sqrt(g) sqrt(h)

(%i5) fortran(%th(2));
      x = ((exp((sqrt(g)*t)/sqrt(h))*(g*x_0+sqrt(g)*sqrt(h)*v_0))/g)/2.0
     1   E+0+((exp(-(sqrt(g)*t)/sqrt(h))*(g*x_0-sqrt(g)*sqrt(h)*v_0))/g)
     2   /2.0E+0
(%o5)                                done
(%i6) fortran(%th(2));
      v = ((exp((sqrt(g)*t)/sqrt(h))*(g*x_0+sqrt(g)*sqrt(h)*v_0))/(sqrt(
     1   g)*sqrt(h)))/2.0E+0-((exp(-(sqrt(g)*t)/sqrt(h))*(g*x_0-sqrt(g)*
     2   sqrt(h)*v_0))/(sqrt(g)*sqrt(h)))/2.0E+0
(%o6)                                done

位相平面(phase portrait)(位相面)、位相線図

作図プログラム

Python
# parameter
g = 9.80665 # m/s^2
h = 0.5 # m

from numpy import linspace, exp, sqrt

t = linspace(-3, # time-reversal symmetry
             3, 2*1024) # s

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

plt.clf(); plt.grid()
palette = ['#80FFFF', '#9FFFFF', '#BFFFFF', '#DFFFFF' # cm.colors(8)[1:4]
           , '#DFFFFF', '#BFFFFF', '#9FFFFF', '#80FFFF']
for i, (x_0, v_0) in enumerate(zip([-0.20, -0.15, -0.10, -0.05, 0.05, 0.10, 0.15, 0.20] # m
                                   , [0, 0, 0, 0, 0, 0, 0, 0] # m/s
                                   )): # initial value
    x = ((exp((sqrt(g)*t)/sqrt(h))*(g*x_0+sqrt(g)*sqrt(h)*v_0))/g)/2.0E+0+((exp(-(sqrt(g)*t)/sqrt(h))*(g*x_0-sqrt(g)*sqrt(h)*v_0))/g)/2.0E+0
    v = ((exp((sqrt(g)*t)/sqrt(h))*(g*x_0+sqrt(g)*sqrt(h)*v_0))/(sqrt(g)*sqrt(h)))/2.0E+0-((exp(-(sqrt(g)*t)/sqrt(h))*(g*x_0-sqrt(g)*sqrt(h)*v_0))/(sqrt(g)*sqrt(h)))/2.0E+0
    plt.plot(x, v, color=palette[i], label=(r'$ E < 0 $' if i == 0 else None))
palette = ['#FF80FF', '#FF9FFF', '#FFBFFF', '#FFDFFF',
           '#FFDFFF', '#FFBFFF', '#FF9FFF', '#FF80FF' # cm.colors(8)[5:8]
           ]
for i, (x_0, v_0) in enumerate(zip([0, 0, 0, 0, 0, 0, 0, 0] # m
                                   , [-1, -0.8, -0.6, -0.4, 0.4, 0.6, 0.8, 1] # m/s
                                   )): # initial value
    x = ((exp((sqrt(g)*t)/sqrt(h))*(g*x_0+sqrt(g)*sqrt(h)*v_0))/g)/2.0E+0+((exp(-(sqrt(g)*t)/sqrt(h))*(g*x_0-sqrt(g)*sqrt(h)*v_0))/g)/2.0E+0
    v = ((exp((sqrt(g)*t)/sqrt(h))*(g*x_0+sqrt(g)*sqrt(h)*v_0))/(sqrt(g)*sqrt(h)))/2.0E+0-((exp(-(sqrt(g)*t)/sqrt(h))*(g*x_0-sqrt(g)*sqrt(h)*v_0))/(sqrt(g)*sqrt(h)))/2.0E+0
    plt.plot(x, v, color=palette[i], label=(r'$ E > 0 $' if i == 0 else None))
plt.legend(loc='best')
plt.xticks([0]); plt.yticks([0])
plt.xlim(-0.3, 0.3); plt.ylim(-1.35, 1.35)
plt.xlabel(r'$ x $'); plt.ylabel(r'$ \dot x $');
plt.title('Phase Portrait')
plt.savefig('fig_phase_portrait.svg')

足踏み運動

問題設定。線形倒立振子、定常状態(定常歩行)

(定常的な)足踏み運動の周期 T [s] を求める

Maxima
(%i1) 'diff(x, t, 2) = (g/h)*x;
                                    2
                                   d x   g x
(%o1)                              --- = ---
                                     2    h
                                   dt
(%i2) subst([g = 9.80665, h = 0.9], %);
                            2
                           d x
(%o2)                      --- = 10.89627777777778 x
                             2
                           dt
(%i3) ode2(%, x, t);

rat: replaced -10.89627777777778 by -196133/18000 = -10.89627777777778
                         sqrt(196133) t           sqrt(196133) t
                         --------------         - --------------
                                3/2                      3/2
                            12 5                     12 5
(%o3)          x = %k1 %e               + %k2 %e
(%i4) r: 0.5/2;
(%o4)                                0.25
(%i5) A: 0.3/2;
(%o5)                                0.15
(%i6) ic2(%th(3), t = 0, x = r - A, 'diff(x, t) = 0);

rat: replaced 0.1 by 1/10 = 0.1
                         sqrt(196133) t       sqrt(196133) t
                         --------------     - --------------
                                3/2                  3/2
                            12 5                 12 5
                       %e                 %e
(%o6)              x = ---------------- + ------------------
                              20                  20
(%i7) find_root(rhs(%) = r, t, 0, 3);
(%o7)                         0.4746508558647776
(%i8) %*4;
(%o8)                          1.898603423459111

(%i9) fortran(%th(3));
      x = exp((5**((-3.0E+0)/2.0E+0)*sqrt(196133)*t)/1.2E+1)/2.0E+1+exp(
     1   -(5**((-3.0E+0)/2.0E+0)*sqrt(196133)*t)/1.2E+1)/2.0E+1
(%o9)                                done

(find_root() 関数を利用して)求めた数値解は、およそ 1.899 秒

答えを図示。重心の運動の軌跡

作図プログラム

Python
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

from numpy import linspace, exp, sqrt

r = 0.5/2
A = 0.3/2

quarter_period = 0.4746508558647776

t = linspace(-quarter_period, # time-reversal symmetry
             quarter_period, 1024) # s

plt.clf(); plt.grid()
y = exp((5**((-3.0E+0)/2.0E+0)*sqrt(196133)*t)/1.2E+1)/2.0E+1+exp(-(5**((-3.0E+0)/2.0E+0)*sqrt(196133)*t)/1.2E+1)/2.0E+1
plt.plot(t, y - r, label='CoM')
plt.plot(t + 2*quarter_period, -y + r, label='CoM')
plt.plot(t + 4*quarter_period, y - r, label='CoM')
plt.plot([-quarter_period, quarter_period, None, quarter_period, 3*quarter_period, None, 3*quarter_period, 5*quarter_period], [-r, -r, None, r, r, None, -r, -r], color='k', alpha=0.8, label='ZMP')
plt.plot([quarter_period, quarter_period, None, 3*quarter_period, 3*quarter_period], [-r, r, None, r, -r], color='k', ls='--', alpha=0.5)
plt.legend(loc='best')
plt.xticks([0, quarter_period, 2*quarter_period, 3*quarter_period, 4*quarter_period], [0, round(quarter_period, 3), round(2*quarter_period, 3), round(3*quarter_period, 3), round(4*quarter_period, 3)])
plt.yticks([-r, -A, 0, A, r], ['-r', '-A', 0, 'A', 'r'])
plt.xlabel('t [s]'); plt.ylabel('y [mm]')
plt.title('Trajectory')
plt.savefig('fig_stepping.svg')

トロット歩容(trot gait)

問題設定。4足歩行ロボット

支持多角形(support polygon)(足裏が点ならば、2脚支持脚、2脚遊脚で、線分になる)\[ y = Bx\]目標ZMP\[ y_\mathrm{ZMP} = Bvt \]ZMP方程式\[ y_\mathrm{ZMP} = y_\mathrm{CG} - \frac{h}{g}\ddot y_\mathrm{CG} \]

重心軌道を求める微分方程式\[ Bvt = y_\mathrm{CG} - \frac{h}{g}\ddot y_\mathrm{CG} \]

2階微分方程式の一般解

Maxima
(%i1) B*v*t = y_CG - (h/g)*'diff(y_CG, t, 2);
                                              2
                                             d y_CG
                                           h ------
                                                2
                                              dt
(%o1)                       B t v = y_CG - --------
                                              g
(%i2) assume(h/g > 0);
(%o2)                              [g h > 0]
(%i3) ode2(%th(2), y_CG, t);
                                   sqrt(g) t           sqrt(g) t
                                   ---------         - ---------
                                    sqrt(h)             sqrt(h)
(%o3)         y_CG = B t v + %k1 %e          + %k2 %e
(%i4) 'diff(y_CG, t) = diff(rhs(%), t);
                                   sqrt(g) t                   sqrt(g) t
                                   ---------                 - ---------
                                    sqrt(h)                     sqrt(h)
       dy_CG         %k1 sqrt(g) %e            %k2 sqrt(g) %e
(%o4)  ----- = B v + ----------------------- - -------------------------
        dt                   sqrt(h)                    sqrt(h)

(%i5) string(%th(2));
(%o5) y_CG = B*t*v+%k1*%e^((sqrt(g)*t)/sqrt(h))+%k2*%e^-((sqrt(g)*t)/sqrt(h))
(%i6) string(%th(2));
(%o6) 'diff(y_CG,t,1) = B*v+(%k1*sqrt(g)*%e^((sqrt(g)*t)/sqrt(h)))/sqrt(h)-(%k\
2*sqrt(g)*%e^-((sqrt(g)*t)/sqrt(h)))/sqrt(h)

重心位置\[ y_\mathrm{CG}(t) = C_1\exp\left(\sqrt\frac{g}{h}t\right) + C_2\exp\left(-\sqrt\frac{g}{h}t\right) + Bvt \]重心速度\[ \dot y_\mathrm{CG}(t) = C_1\sqrt\frac{g}{h}\exp\left(\sqrt\frac{g}{h}t\right) - C_2\sqrt\frac{g}{h}\exp\left(-\sqrt\frac{g}{h}t\right) + Bv \]

境界条件。時刻0で原点に位置する\[ y_\mathrm{CG}(0) = 0 \]4分の1周期で、横方向の速度をゼロ(脚切り替えで、次の軌道と滑らかにつながるようにする)\[ \dot y_\mathrm{CG}(\frac{T}{4}) = 0 \]ここで、T は歩行周期

任意定数を決定する

Maxima
(%i1) subst(t = 0, B*t*v+%k1*%e^((sqrt(g)*t)/sqrt(h))+%k2*%e^-((sqrt(g)*t)/sqrt(h))) = 0;
(%o1)                            %k2 + %k1 = 0
(%i2) subst(t = T/4, B*v+(%k1*sqrt(g)*%e^((sqrt(g)*t)/sqrt(h)))/sqrt(h)-(%k2*sqrt(g)*%e^-((sqrt(g)*t)/sqrt(h)))/sqrt(h)) = 0;
                             T sqrt(g)                   T sqrt(g)
                             ---------                 - ---------
                             4 sqrt(h)                   4 sqrt(h)
               %k1 sqrt(g) %e            %k2 sqrt(g) %e
(%o2)    B v + ----------------------- - ------------------------- = 0
                       sqrt(h)                    sqrt(h)
(%i3) solve([%th(2), %], [%k1, %k2]);
                               T sqrt(g)
                               ---------
                               4 sqrt(h)
                   B sqrt(h) %e          v
(%o3) [[%k1 = - -----------------------------, 
                          T sqrt(g)
                          ---------
                          2 sqrt(h)
                sqrt(g) %e          + sqrt(g)
                                                               T sqrt(g)
                                                               ---------
                                                               4 sqrt(h)
                                                   B sqrt(h) %e          v
                                          %k2 = -----------------------------]]
                                                          T sqrt(g)
                                                          ---------
                                                          2 sqrt(h)
                                                sqrt(g) %e          + sqrt(g)

(%i4) string(%[1][1]);
(%o4) %k1 = -(B*sqrt(h)*%e^((T*sqrt(g))/(4*sqrt(h)))*v)/(sqrt(g)*%e^((T*sqrt(g\
))/(2*sqrt(h)))+sqrt(g))
(%i5) string(%th(2)[1][2]);
(%o5) %k2 = (B*sqrt(h)*%e^((T*sqrt(g))/(4*sqrt(h)))*v)/(sqrt(g)*%e^((T*sqrt(g)\
)/(2*sqrt(h)))+sqrt(g))

重心軌道を計算

Maxima
(%i1) subst(T = 0.5, %k1 = -(B*sqrt(h)*%e^((T*sqrt(g))/(4*sqrt(h)))*v)/(sqrt(g)*%e^((T*sqrt(g))/(2*sqrt(h)))+sqrt(g)));
                                         0.125 sqrt(g)
                                         -------------
                                            sqrt(h)
                             B sqrt(h) %e              v
(%o1)              %k1 = - --------------------------------
                                     0.25 sqrt(g)
                                     ------------
                                       sqrt(h)
                           sqrt(g) %e             + sqrt(g)
(%i2) subst(T = 0.5, %k2 = (B*sqrt(h)*%e^((T*sqrt(g))/(4*sqrt(h)))*v)/(sqrt(g)*%e^((T*sqrt(g))/(2*sqrt(h)))+sqrt(g)));
                                        0.125 sqrt(g)
                                        -------------
                                           sqrt(h)
                            B sqrt(h) %e              v
(%o2)               %k2 = --------------------------------
                                    0.25 sqrt(g)
                                    ------------
                                      sqrt(h)
                          sqrt(g) %e             + sqrt(g)
(%i3) subst([%o1, %o2], y_CG = B*t*v+%k1*%e^((sqrt(g)*t)/sqrt(h))+%k2*%e^-((sqrt(g)*t)/sqrt(h)));
                            sqrt(g) t   0.125 sqrt(g)
                            --------- + -------------
                             sqrt(h)       sqrt(h)
                B sqrt(h) %e                          v
(%o3) y_CG = (- ---------------------------------------)
                             0.25 sqrt(g)
                             ------------
                               sqrt(h)
                   sqrt(g) %e             + sqrt(g)
                                            0.125 sqrt(g)   sqrt(g) t
                                            ------------- - ---------
                                               sqrt(h)       sqrt(h)
                                B sqrt(h) %e                          v
                              + --------------------------------------- + B t v
                                             0.25 sqrt(g)
                                             ------------
                                               sqrt(h)
                                   sqrt(g) %e             + sqrt(g)
(%i4) subst([%o1, %o2], vy_CG = B*v+(%k1*sqrt(g)*%e^((sqrt(g)*t)/sqrt(h)))/sqrt(h)-(%k2*sqrt(g)*%e^-((sqrt(g)*t)/sqrt(h)))/sqrt(h));
                             sqrt(g) t   0.125 sqrt(g)
                             --------- + -------------
                              sqrt(h)       sqrt(h)
                 B sqrt(g) %e                          v
(%o4) vy_CG = (- ---------------------------------------)
                              0.25 sqrt(g)
                              ------------
                                sqrt(h)
                    sqrt(g) %e             + sqrt(g)
                                              0.125 sqrt(g)   sqrt(g) t
                                              ------------- - ---------
                                                 sqrt(h)       sqrt(h)
                                  B sqrt(g) %e                          v
                                - --------------------------------------- + B v
                                               0.25 sqrt(g)
                                               ------------
                                                 sqrt(h)
                                     sqrt(g) %e             + sqrt(g)
(%i5) subst([B = 1, v = 1], %th(2));
                          sqrt(g) t   0.125 sqrt(g)
                          --------- + -------------
                           sqrt(h)       sqrt(h)
                sqrt(h) %e
(%o5) y_CG = (- -----------------------------------)
                           0.25 sqrt(g)
                           ------------
                             sqrt(h)
                 sqrt(g) %e             + sqrt(g)
                                                  0.125 sqrt(g)   sqrt(g) t
                                                  ------------- - ---------
                                                     sqrt(h)       sqrt(h)
                                        sqrt(h) %e
                                      + ----------------------------------- + t
                                                   0.25 sqrt(g)
                                                   ------------
                                                     sqrt(h)
                                         sqrt(g) %e             + sqrt(g)
(%i6) subst([B = 1, v = 1], %th(2));
                           sqrt(g) t   0.125 sqrt(g)
                           --------- + -------------
                            sqrt(h)       sqrt(h)
                 sqrt(g) %e
(%o6) vy_CG = (- -----------------------------------)
                            0.25 sqrt(g)
                            ------------
                              sqrt(h)
                  sqrt(g) %e             + sqrt(g)
                                                  0.125 sqrt(g)   sqrt(g) t
                                                  ------------- - ---------
                                                     sqrt(h)       sqrt(h)
                                        sqrt(g) %e
                                      - ----------------------------------- + 1
                                                   0.25 sqrt(g)
                                                   ------------
                                                     sqrt(h)
                                         sqrt(g) %e             + sqrt(g)
(%i7) fortran(%th(2));
      y_CG = (-(sqrt(h)*exp((sqrt(g)*t)/sqrt(h)+(1.25E-1*sqrt(g))/sqrt(h
     1   )))/(sqrt(g)*exp((2.5E-1*sqrt(g))/sqrt(h))+sqrt(g)))+(sqrt(h)*e
     2   xp((1.25E-1*sqrt(g))/sqrt(h)-(sqrt(g)*t)/sqrt(h)))/(sqrt(g)*exp
     3   ((2.5E-1*sqrt(g))/sqrt(h))+sqrt(g))+t
(%o7)                                done
(%i8) fortran(%th(2));
      vy_CG = (-(sqrt(g)*exp((sqrt(g)*t)/sqrt(h)+(1.25E-1*sqrt(g))/sqrt(
     1   h)))/(sqrt(g)*exp((2.5E-1*sqrt(g))/sqrt(h))+sqrt(g)))-(sqrt(g)*
     2   exp((1.25E-1*sqrt(g))/sqrt(h)-(sqrt(g)*t)/sqrt(h)))/(sqrt(g)*ex
     3   p((2.5E-1*sqrt(g))/sqrt(h))+sqrt(g))+1
(%o8)                                done

ZMPと重心 yCG の軌道

歩容線図(位相 0, 0.5, 1 のところで、脚切り替え)

重心位置と速度(境界条件に使用した速度の方程式も表示して、検算)

作図プログラム

Python
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

from numpy import linspace, exp, sqrt

h = 0.5 # m
g = 9.80665 # m/s^2

T = 0.5 # s
t = linspace(0, T/4, 1024) # s

y_CG = (-(sqrt(h)*exp((sqrt(g)*t)/sqrt(h)+(1.25E-1*sqrt(g))/sqrt(h)))/(sqrt(g)*exp((2.5E-1*sqrt(g))/sqrt(h))+sqrt(g)))+(sqrt(h)*exp((1.25E-1*sqrt(g))/sqrt(h)-(sqrt(g)*t)/sqrt(h)))/(sqrt(g)*exp((2.5E-1*sqrt(g))/sqrt(h))+sqrt(g))+t # m
y_CG *= 1000; # mm

A = y_CG.max() # mm
# print('peak-to-peak:', round(2*A, 1), 'mm')

plt.clf(); plt.grid()
plt.plot(t, y_CG, label='CG')
lines = plt.plot(t + T/4, y_CG[::-1], label='CG')
plt.plot(t + 2*(T/4), -y_CG, color=lines[-1].get_color())
plt.plot(t + 3*(T/4), -y_CG[::-1], label='CG')

v = 1 # m/s
r = v*((1/4)*T) # peak
r *= 1000 # mm

plt.plot([0, (1/4)*T, (3/4)*T, T], [0, r, -r, 0], color='k', alpha=0.8, ls='--', label='ZMP')
plt.legend(loc='best')
plt.xticks([0, (1/4)*T, (2/4)*T, (3/4)*T, T]); plt.yticks([r, A, 0, -A, -r])
plt.xlabel(r'$ t $' + ' [s]'); plt.ylabel(r'$ y $' + ' [mm]')
plt.title('Trot Gait')
plt.savefig('fig_trot.svg')
plt.close()


fig, ax = plt.subplots(2)
ax[0].grid()
ax[0].plot(t, y_CG, label='CG')
lines = ax[0].plot(t + T/4, y_CG[::-1], label='CG')
ax[0].plot(t + 2*(T/4), -y_CG, color=lines[-1].get_color())
ax[0].plot(t + 3*(T/4), -y_CG[::-1], label='CG')
ax[0].legend(loc='best')
ax[0].set_xticks([0, (1/4)*T, (2/4)*T, (3/4)*T, T], [f'{0} [s]', f'{(1/4)*T} [s]', f'{(2/4)*T} [s]', f'{(3/4)*T} [s]', f'{T} [s]'])
ax[0].set_yticks([-A, 0, A], [-round(A, 1), 0, round(A, 1)])
ax[0].set_ylabel(r'$ y $' + ' [mm]')
ax[0].set_title('Trot Gait (Duty Ratio: 0.5)')

ax[1].grid()
ax[1].invert_yaxis()
ax[1].plot([0, 1/4, None, 0, 1/4], [1, 1, None, 4, 4], color='k', alpha=0.8) # stance legs
ax[1].plot([1/4, 3/4, None, 1/4, 3/4], [2, 2, None, 3, 3], color='k', alpha=0.8)
ax[1].plot([3/4, 1, None, 3/4, 1], [1, 1, None, 4, 4], color='k', alpha=0.8)
ax[1].set_xticks([1/4, 3/4], [0.5, '1(=0)'])
ax[1].set_xlabel('Phase'); ax[1].set_ylabel('Leg Number')
fig.savefig('fig_trot-gait.svg')
plt.close()


plt.clf(); plt.grid()
line1, = plt.plot(t, y_CG, label=r'$ y_\mathrm{CG} $', alpha=0.8)
my_color = line1.get_color()
plt.plot(t + T/4, y_CG[::-1], color=my_color, alpha=0.8)
plt.plot(t + 2*(T/4), -y_CG, color=my_color, alpha=0.8)
plt.plot(t + 3*(T/4), -y_CG[::-1], color=my_color, alpha=0.8)

plt.xticks([0, (1/4)*T, (2/4)*T, (3/4)*T, T], [f'{0}', (1/4)*T, f'{(2/4)*T}', (3/4)*T, f'{T}']); plt.yticks([A, 0, -A], [round(A, 1), 0, -round(A, 1)])
plt.xlabel(r'$ t $' + ' [s]'); plt.ylabel(r'$ y $' + ' [mm]')

vy_CG = (-(sqrt(g)*exp((sqrt(g)*t)/sqrt(h)+(1.25E-1*sqrt(g))/sqrt(h)))/(sqrt(g)*exp((2.5E-1*sqrt(g))/sqrt(h))+sqrt(g)))-(sqrt(g)*exp((1.25E-1*sqrt(g))/sqrt(h)-(sqrt(g)*t)/sqrt(h)))/(sqrt(g)*exp((2.5E-1*sqrt(g))/sqrt(h))+sqrt(g))+1 # m/s

ax2 = plt.gca().twinx()
ax2.plot([], []); ax2.plot([], []); ax2.plot([], []) # adhoc
line2, = ax2.plot(t.tolist() + (t + (T/4)).tolist() + (t + 2*(T/4)).tolist() + (t + 3*(T/4)).tolist(),
                  vy_CG.tolist() + (-vy_CG[::-1]).tolist() + (-vy_CG).tolist() + vy_CG[::-1].tolist(), alpha=0.8, label=r'$ \dot y_\mathrm{CG} $')
ax2.set_yticks([-vy_CG.max(), 0, vy_CG.max()], [round(v, 3) for v in [-vy_CG.max(), 0, vy_CG.max()]], rotation=90)
ax2.get_yticklabels()[0].set_va('bottom')
ax2.set_ylabel(r'$ \dot y $' + ' [m/s]')
ax2.legend(handles=[line1, line2], loc='best')
plt.title('Trot Gait (Position-Velocity)')
plt.savefig('fig_trot_velocity.svg')

教科書


© 2019, 2025 Tadakazu Nagai