『Python数値計算ノート』ではアフィリエイトプログラムを利用して商品を紹介しています。

回転行列

【Pythonで学ぶ線形代数学講座(17)】回転行列

回転行列

回転行列(rotation matrix)は任意のベクトルに作用したとき、ベクトルの始点を軸に回転させます。
 \[R(\theta)=\begin{bmatrix}\cos\theta&-\sin\theta\\\sin\theta&\cos\theta\end{bmatrix}\tag{1}\]
$\theta$ は回転角度で、正の値のときは反時計回り、負の値とのきは時計回りに回転します。たとえば、行列 $R(\theta)$ を単位ベクトル $\boldsymbol{u}=\begin{bmatrix}1\\0\end{bmatrix}$ に乗じると、
 \[R(\theta)\boldsymbol{u}=\begin{bmatrix}\cos\theta&-\sin\theta\\\sin\theta&\cos\theta\end{bmatrix}\begin{bmatrix}1\\0\end{bmatrix}=\begin{bmatrix}\cos\theta\\\sin\theta\end{bmatrix}\tag{2}\]
となります。$\boldsymbol{u}$ が位置ベクトルであれば、$R(\theta)\boldsymbol{u}$ は原点を軸とする回転を表します。Python で単位ベクトル $\boldsymbol{u}=\begin{bmatrix}1\\0\end{bmatrix}$ に回転行列
 \[R(\pi/4)=\begin{bmatrix}\cos\cfrac{\pi}{4}&-\sin\cfrac{\pi}{4}\\
\sin\cfrac{\pi}{4}&\cos\cfrac{\pi}{4}\end{bmatrix}\tag{3}\]
を作用させて、ベクトルを角度 $\pi/4$ だけ回転させてみましょう。予め コードライブラリ にある coordinate() と visual_vector() を実装しておいてください。

# python_rotation_matrix

# In[1]

import numpy as np
import matplotlib.pyplot as plt

# ここにcoordinate()を実装
# ここにvisual_vector()を実装

# ベクトル回転関数
# deg=Falseならばラジアンで角度を指定
# deg=Trueならば度数単位で角度を指定
def rotation_o(u, t, deg=False):

    # 度数単位の角度をラジアンに変換
    if deg == True:
        t = np.deg2rad(t)

    # 回転行列
    R = np.array([[np.cos(t), -np.sin(t)],
                  [np.sin(t),  np.cos(t)]])

    return  np.dot(R, u)

# 単位ベクトル
u = (1, 0)

# uを45°回転させる
Ru = rotation_o(u, np.pi/4)

# 描画領域を作成
fig, ax = plt.subplots(1, 1, figsize=(5, 5))
coordinate(ax, [-1.5, 1.5], [-1.5, 1.5])

# uをプロット
visual_vector(ax, (0, 0), u, color="blue")
ax.text(u[0], u[1]+0.05, "u", color="blue", size=16)

# Ruをプロット
visual_vector(ax, (0, 0), Ru, color="red")
ax.text(Ru[0], Ru[1], "Ru", color="red", size=16)

Python 回転行列Rによるベクトルの回転
$\boldsymbol{u}$ に $R(\pi/6)$ を繰り返し作用させて、$R(\pi/6)\boldsymbol{u}$ の終点は半径 $1$ の円周上にあることを確認してみましょう。

# In[2]

fig, ax = plt.subplots(1, 1, figsize=(5, 5))
coordinate(ax, [-1.5, 1.5], [-1.5, 1.5])

# ベクトルの初期値
Ru = (1, 0)

# 単位ベクトルに繰り返し回転行列を作用させる
for i in range(12):
    Ru = rotation_o(Ru, np.pi/6)
    visual_vector(ax, (0, 0), Ru, color="red")

Matplotlib 回転行列による単位ベクトルの連続回転
したがって、$R(\theta)$ は平面座標上の任意の点 $(x,\ y)$ を角度 $t$ だけ回転させる行列であるともいえます。また、複数の位置ベクトル
 \[\boldsymbol{r}_1=\begin{bmatrix}x_1\\y_1\end{bmatrix},\ \boldsymbol{r}_2=\begin{bmatrix}x_2\\y_2\end{bmatrix},\ \cdots,\ \boldsymbol{r}_k=\begin{bmatrix}x_k\\y_k\end{bmatrix}\tag{4}\]
を行列
 \[X=\begin{bmatrix}x_1 & x_2 & \cdots & x_k\\y_1 & y_2 & \cdots & y_k\end{bmatrix}\tag{5}\]
の形にまとめて回転行列を作用させると、すべての点を一斉に回転させることができます。以下のコードは放物線 $P_0(x)=x^2$ 全体を反時計回りに $90^\circ$ 回転させます。

# In[3]

# FigureとAxesを作成
fig, ax = plt.subplots(1, 1, figsize=(5, 5))

# 座標を設定
coordinate(ax, [-2, 2], [-2, 2])

# 放物線のデータを作成
x = np.linspace(-2, 2, 33)
y = x ** 2

# xとyを縦軸方向に連結して行列を作成
p0 = np.vstack((x, y))

# 放物線p0を左に90°回転
p1 = rotation_o(p0, np.pi/2)

# p0とp1をAxesにプロット
ax.plot(p0[0], p0[1], label="P0", color="blue")
ax.plot(p1[0], p1[1], label="P1", color="darkorange")

# 凡例を表示
ax.legend(loc = "lower right")

plt.show()

回転行列による2次関数の左90°の回転
次は $xy$ 平面上点 $\mathrm{Q}(x,\ y)$ を点 $\mathrm{P}(a,\ b)$ の周りに回転させることを考えます (下図)。
 
任意軸の周りのベクトルの回転
図にあるように、ベクトル $\vec{PQ}$ は
 \[\vec{PQ}=\begin{bmatrix}x-a\\y-b\end{bmatrix}\tag{6}\]
で表されます。$\vec{PQ}$ が $R(t)$ によって $\vec{PQ’}$ に移ったとすると、線形写像の式は
 \[\begin{bmatrix}x’-a\\y’-b\end{bmatrix}=\begin{bmatrix}\cos\theta&-\sin\theta\\\sin\theta&\cos\theta\end{bmatrix}\begin{bmatrix}x-a\\y-b\end{bmatrix}\tag{7}\]
と表せます。したがって、$\mathrm{Q’}(x’,\ y’)$ は
 \[\begin{bmatrix}x’\\y’\end{bmatrix}=\begin{bmatrix}\cos\theta&-\sin\theta\\\sin\theta&\cos\theta\end{bmatrix}\begin{bmatrix}x-a\\y-b\end{bmatrix}+\begin{bmatrix}a\\b\end{bmatrix}\tag{8}\]
と表されます。任意の軸周りの回転操作を Python で実装してみましょう。

# In[4]

# 平面上の任意の点の回転関数
# qに軸の座標を指定
# pに回転させたい点の座標を指定
# tに回転角を指定
def rotation(xy, r_axis, t, deg=False):

    # 度数単位の角度をラジアンに変換
    if deg == True:
        t = np.deg2rad(t)

    xy = np.array(xy)
    r_axis = np.array(r_axis)

    # 回転行列
    R = np.array([[np.cos(t), -np.sin(t)],
                  [np.sin(t),  np.cos(t)]])

    return  np.dot(R, xy - r_axis) + r_axis

rotation 関数を使って、平面上の点 $(3,\ 3)$ を点 $(2,\ 1)$ の周りに角度 $\pi/6$ ずつ回転させてみます。

# In[5]

fig, ax = plt.subplots(1, 1, figsize=(5, 5))
coordinate(ax, [-3, 5], [-3, 5])

# 座標点
q = (3, 3)

# 回転軸の座標
p = (2, 1)

# 点qに繰り返し回転行列を作用させる
for i in range(12):
    q = rotation(q, p, np.pi/6)
    ax.scatter(q[0], q[1], color="red")

Python 回転行列による点の連続回転
rotation() の xy に回転対象となる複数の座標点を二次元配列の形でまとめて渡して、グラフや図形を回転させることができます。放物線 $P_0(x)=x^2$ を点 $(2,\ 2)$ の周りに $\pi/4$ 回転させてみます。

# In[6]

# グラフ描画領域を作成
fig, ax = plt.subplots(1, 1, figsize=(5, 5))
coordinate(ax, [-3, 5], [-3, 5])

# 放物線のデータを作成
x = np.linspace(-4, 4, 65)
y = x**2

# xとyを縦軸方向に連結して行列を作成
p0 = np.vstack((x, y))

# 回転軸の座標
r_axis = np.array([[2], [2]])

# 放物線p0を左に45°回転
p1 = rotation(f0, r_axis, np.pi/4)

# p0とp1をAxesにプロット
ax.plot(p0[0], p0[1], label="P0", color="blue")
ax.plot(p1[0], p1[1], label="P1", color="darkorange")

# 凡例を表示
ax.legend(loc = "lower right")

plt.show()

python 回転行列Rによる図形の45度回転

 

コメント