左手座標系と右手座標系の壁を超えよう。

# はじめに

左手座標系における回転（コンピュータビジョン系やUnityでよく使われる）と右手座標系（ロボットや物理系でよく使われる）における回転を変換する必要があったのでその時の完全に個人的なメモです。多くのライブラリは右手座標系で作られているので、その際にどのように変換すればいいのかをまとめました。

# 回転を表す4つのもの

そもそも回転を表すものには

- オイラー角（ロールピッチヨー）
- 回転ベクトル
- 回転行列
- クォータニオン

の4つがあります。ここではそれぞれの説明については割愛します。たくさん説明されている記事があるので
例えば、

- [クォータニオン (Quaternion) を総整理！ ～ 三次元物体の回転と姿勢を鮮やかに扱う ～](https://qiita.com/drken/items/0639cf34cce14e8d58a5)
- [回転ベクトル・回転行列・クォータニオン・オイラー角についてまとめてみた](https://kamino.hatenablog.com/entry/rotation_expressions)

を参考にしていただければと思います。
ただ気になるのは次です。左手座標系で（任意軸回りに）$θ$回転させる、右手座標系で（任意軸回りに）$θ$回転させる場合で何か違いはあるのでしょうか？

# 結論

基本的には左手座標系は左手座標系の演算、右手座標系は右手座標系演算を考えれば良いです。
ただし、右手座標系の〜を左手座標系からみるとという形になる場合は、変換する必要があります！


# 回転行列

まず、回転行列について見ていきます。

## 右手座標系

x軸、y軸、z軸、任意軸回りについて見ていきます。
これはつまり、右手座標系で（ある軸回りに）θ回転させることはどのような変換をすれば良いのかを意味します。

### x軸

```math
R^{right}_x = 
  \left(
    \begin{array}{ccc}
      1 & 0 & 0 \\
      0 & \cos \theta & - \sin \theta \\
      0 & \sin \theta & \cos \theta
    \end{array}
  \right)
```

### y軸

```math
R^{right}_y = 
  \left(
    \begin{array}{ccc}
      \cos \theta & 0 & \sin \theta \\
      0 & 1 & 0 \\
      - \sin \theta & 0 & \cos \theta
    \end{array}
  \right)
```

### z軸


```math
R^{right}_z = 
  \left(
    \begin{array}{ccc}
      \cos \theta & - \sin \theta & 0 \\
      \sin \theta & \cos \theta & 0 \\
      0 & 0 & 1
    \end{array}
  \right)
```

### 任意軸回り

$n = [n_x, n_y, n_z]$で$\theta$回転させるとすると、

```math
R^{right}_n = 
 \left(
    \begin{array}{ccc}
      n_x^2(1- \cos \theta) + \cos \theta & 
      n_x n_y (1- \cos \theta) - n_z \sin \theta & 
      n_x n_z (1- \cos \theta) + n_y \sin \theta \\
      n_x n_y (1- \cos \theta) + n_z \sin \theta & 
      n_y^2(1- \cos \theta) + \cos \theta &
      n_y n_z (1- \cos \theta) - n_x \sin \theta \\
      n_x n_z (1- \cos \theta) - n_y \sin \theta & 
      n_y n_z (1- \cos \theta) + n_x \sin \theta & 
      n_z^2(1- \cos \theta) + \cos \theta
    \end{array}
  \right)
```


## 左手座標系

x軸、y軸、z軸、任意軸回りについて見ていきます。
これはつまり、左手座標系で（ある軸回りに）θ回転させることはどのような変換をすれば良いのかを意味します。
**（一番はじめにあげた記事間違えてました。。。修正です。）**

### x軸

```math
R^{left}_x = 
  \left(
    \begin{array}{ccc}
      1 & 0 & 0 \\
      0 & \cos \theta & - \sin \theta \\
      0 & \sin \theta & \cos \theta
    \end{array}
  \right)
```


### y軸

```math
R^{left}_y = 
  \left(
    \begin{array}{ccc}
      \cos \theta & 0 & \sin \theta \\
      0 & 1 & 0 \\
      -\sin \theta & 0 & \cos \theta
    \end{array}
  \right)
```

### z軸


```math
R^{left}_z = 
  \left(
    \begin{array}{ccc}
      \cos \theta & -\sin \theta & 0 \\
      \sin \theta & \cos \theta & 0 \\
      0 & 0 & 1
    \end{array}
  \right)
```

### 任意軸回り

$n = [n_x, n_y, n_z]$で$\theta$回転させるとすると、

```math
R^{left}_n = 
 \left(
    \begin{array}{ccc}
      n_x^2(1- \cos \theta) + \cos \theta & 
      n_x n_y (1- \cos \theta) - n_z \sin \theta & 
      n_x n_z (1- \cos \theta) + n_y \sin \theta \\
      n_x n_y (1- \cos \theta) + n_z \sin \theta & 
      n_y^2(1- \cos \theta) + \cos \theta &
      n_y n_z (1- \cos \theta) - n_x \sin \theta \\
      n_x n_z (1- \cos \theta) - n_y \sin \theta & 
      n_y n_z (1- \cos \theta) + n_x \sin \theta & 
      n_z^2(1- \cos \theta) + \cos \theta
    \end{array}
  \right)
```

同じです。
これは座標系自体がそうつじつまが合うように設定されているからだと思います。
（θがそもそも逆回転に設定され、かつ、軸も入れ替わっているからだと思ってます。）
（ちょっと自信ないので、もしコメントある方いたらお願いします。）

## 右手座標系の回転行列を左手座標系へ変換

これは、右手座標系の回転行列を左手座標系からみた場合、どうなるか？という話です。

**転置すれば大丈夫です！**

なのですべてに転置をかければ良いです。
簡単。

# クォータニオン

## 右手座標系

右手座標系においてクォータニオン（ある任意軸回りに回転させることを意味する）のは、

```math
q^{right} = [q_w, q_x, q_y, q_z]
```

です。

## 左手座標系

左手座標系においてクォータニオン（ある任意軸回りに回転させることを意味する）のは

```math
q^{left} = [q_w, q_x, q_y, q_z]
```

です。さて、ここで、同じでは？となるかと思いますが、同じです。当たり前ですが同じです。
（別に左手座標系でクォータニオンがどうとかそういう話ではないので。。。）

## 右手座標系のクォータニオンから左手座標系のクォータニオンへ変換

しかし、上記の話をもし右手座標系のクォータニオンを左手座標系で見た場合どうなるか？
という話にするのであれば別です。

```math
q^{right} = [q_{w1}, q_{x1}, q_{y1}, q_{z1}]
```

は

```math
q^{left} = [q_{w1}, -q_{x1}, q_{y1}, -q_{z1}]
```

になります。

# 回転ベクトル

これについても同じですね！定義は同じですが！

## 右手座標系

右手座標系においての回転ベクトル（ある任意軸回りに回転させることを意味する）のは、

```math
n^{right} = [u, v, w]
```

## 左手座標系

左手座標系においての回転ベクトル（ある任意軸回りに回転させることを意味する）のは

```math
n^{left} = [u, v, w]
```

## 右手座標系の回転ベクトルから左手座標系の回転ベクトルへ変換

しかし、上記の話をもし右手座標系の回転ベクトルを左手座標系で見た場合どうなるか？
という話にするのであれば別です。

```math
n^{right} = [u_1, v_1, w_1]
```

は

```math
n^{left} = [-u_1, v_1, -w_1]
```

になります。

# 外積（クロス積）

## 右手座標系

右手座標系において、2つのベクトル、$a = [a_x, a_y, a_z]$、$b = [b_x, b_y, b_z]$の外積を取ることは

```math
a \times b = 
  \left(
    \begin{array}{c}
      a_y b_z - a_z b_y \\
      a_z b_x - a_x b_z \\
      a_x b_y - a_y b_x
    \end{array}
  \right)
```

となります。numpyだと、

```py
>>> a = np.array([1., 2., 3.])
>>> b = np.array([4., 5., 6.])

>>> np.cross(a, b)
# array([-3.,  6., -3.])
```

で計算できますね！

## 左手座標系

外積の向きが反対になるのですが、θも反対になっているので、結局そのまま使えるはず。。。

```math
a \times b = 
  \left(
    \begin{array}{c}
      a_z b_y - a_y b_z \\
      a_x b_z - a_z b_x \\
      a_y b_x - a_x b_y
    \end{array}
  \right)
```

なので変わらずです！

```py
>>> a = np.array([1., 2., 3.])
>>> b = np.array([4., 5., 6.])

>>> np.cross(a, b)
# array([-3.,  6., -3.])
```

# atan

## 右手座標系

右手座標系においてarctanを取ることは、numpyで

```py
>>> a = 1.
>>> b = 2.
>>> np.arctan2(a, b)
# 0.4636476090008061
```

と計算できます。

## 左手座標系

左手座標系でarctanを取ることは右手座標系の演算の反対になるのですが、左手座標系のベクトルを右手座標系になおして、（y軸反転）その後、arctanを取ります。ここでは、θにはマイナスがついているのですが、その角度を左手座標系に戻す（θが逆向きに設定されているので）と。。。結局、そのままになります。

```py
>>> a = 1.
>>> b = 2.
>>> np.arctan2(a, b)
# 0.4636476090008061
```

# クォータニオンから回転行列へ変換

## 右手座標系

右手座標系におけるクォータニオン$q^{right} = [q_{w1}, q_{x1}, q_{y1}, q_{z1}]$を回転行列$R^{right}$に変換したい。
一般的にはクォータニオンから回転行列の変換は次です。

```math
R^{right}_{q^{right}} = 
 \left(
    \begin{array}{ccc}
      q_w^2 + q_x^2 - q_y^2 - q_z^2 & 
      2(q_x q_y - q_w q_z) & 
      2(q_z q_x - q_w q_y) \\
      2(q_x q_y - q_w q_z) & 
      q_w^2 - q_x^2 + q_y^2 - q_z^2 &
      2(q_y q_z - q_w q_x) \\
      2(q_z q_x - q_w q_y) & 
      2(q_y q_z - q_w q_x) & 
      q_w^2 - q_x^2 + q_y^2 - q_z^2
    \end{array}
  \right)
```

## 左手座標系

左手座標系におけるクォータニオン$q^{left} = [q_{w1}, q_{x1}, q_{y1}, q_{z1}]$を回転行列$R^{left}$に変換したい。
変わらず使えます。

```math
R^{left}_{q^{left}} = 
 \left(
    \begin{array}{ccc}
      q_w^2 + q_x^2 - q_y^2 - q_z^2 & 
      2(q_x q_y - q_w q_z) & 
      2(q_z q_x - q_w q_y) \\
      2(q_x q_y - q_w q_z) & 
      q_w^2 - q_x^2 + q_y^2 - q_z^2 &
      2(q_y q_z - q_w q_x) \\
      2(q_z q_x - q_w q_y) & 
      2(q_y q_z - q_w q_x) & 
      q_w^2 - q_x^2 + q_y^2 - q_z^2
    \end{array}
  \right)^T
```

# 回転行列から角軸等価ベクトルへ変換

[とある文献](https://www.jstage.jst.go.jp/article/jrsj/29/3/29_3_269/_pdf/-char/ja)で回転行列の差を回転ベクトルで算出する話がでていました。具体的には付録Aを見てほしいですが、要は、回転行列を角軸等価ベクトル（回転ベクトルとは少し異なります）する話です。文献自体は右手座標系での算出方法を述べています。が上記の流れでこのまま使えます。

# まとめ

上記を使えば、

右手座標系のクォータニオンを右手座標系の回転行列にして、左手座標系の回転行列にしたい

といったこともできます！

あくまでメモなので間違っている点等あればご遠慮なくコメントいただければと思います。