回答編集履歴
8
微加筆
test
CHANGED
@@ -33,7 +33,9 @@
|
|
33
33
|
という関係があります。これを使ってθを求めれば回転の方向もわかります (θの正負で方向が判別できます)。ただし今度は角度が90度を超えたかどうかを判別できませんが、1フレームの間にそんなに大きく回転しないのであれば問題ないでしょう。
|
34
34
|
|
35
35
|
【2024-03-11追記】さらに、θが微小な角度である場合は sin(θ) = θ とみなしてしまってもいいですよね。そうすると上の関係はさらに簡単になります。結局オイラー角 (の近似値) は、
|
36
|
-
```pyt
|
36
|
+
```python
|
37
|
+
xrot = np.cross(vec1yz, vec2yz)[0] / (vec1yzLen * vec2yzLen)
|
38
|
+
yrot = np.cross(vec1zx, vec2zx)[1] / (vec1zxLen * vec2zxLen)
|
37
39
|
zrot = np.cross(vec1xy, vec2xy)[2] / (vec1xyLen * vec2xyLen)
|
38
40
|
```
|
39
41
|
のようにして計算できることになります (これはラジアン単位なので、度への換算は必要ですが)。【追記終わり】
|
7
まとめ
test
CHANGED
@@ -68,3 +68,9 @@
|
|
68
68
|
4. ...
|
69
69
|
|
70
70
|
元の方向ベクトルそのものから角度を求めるのではなく、一つ前に計算した角度による回転を実際に適用してみた結果と次の方向ベクトルとの間の角度を求めます。このようにすれば直前の回転の計算による誤差が打ち消され、安定した動作をするようになるのではないかと考えます。
|
71
|
+
|
72
|
+
### まとめ
|
73
|
+
|
74
|
+
1. オイラー角の計算方法として、質問で提示されたコードのような方法 (x、y、zそれぞれの軸についての射影ベクトル同士が成す角を求める) でも、微小な回転については十分な近似になるのかもしれません。
|
75
|
+
2. ただし、質問で提示されたコード (内積の性質を使う) では回転の向きが判別できていません。判別できる方法 (外積の性質を使う) に変える必要があります。
|
76
|
+
3. 小さな回転を次々に適用していくと、誤差が蓄積して結果の方向ベクトルはどんどん元のデータとかけ離れていってしまいます。誤差を打ち消すような計算方法をとるべきです。
|
6
微修正
test
CHANGED
@@ -48,23 +48,23 @@
|
|
48
48
|
|
49
49
|
誤差を低減・解消する方法を考えてみました。
|
50
50
|
|
51
|
-
とりあえず、単純な一点だけの運動を考えます。
|
51
|
+
とりあえず、単純な一点だけの運動を考えます。方向ベクトルの系列vec0、vec1、vec2、vec3、……があるとします。
|
52
52
|
|
53
53
|
質問では次のようにして計算していると言えます。
|
54
54
|
|
55
|
-
1. vec0とvec1から、オイラー角rot
|
55
|
+
1. vec0とvec1から、オイラー角rot0を計算する。
|
56
|
-
2. vec1とvec2から、オイラー角rot
|
56
|
+
2. vec1とvec2から、オイラー角rot1を計算する。
|
57
|
-
3. vec2とvec3から、オイラー角rot
|
57
|
+
3. vec2とvec3から、オイラー角rot2を計算する。
|
58
58
|
4. ...
|
59
|
-
こうして得られたrot
|
59
|
+
こうして得られたrot0、rot1、rot2、……を、MOTIONデータとして用いています。これだと、計算されるそれぞれの角度には座標の不正確さと計算による誤差が乗っているため、実際に動作させて小さな回転を次々に適用していくと、結果の方向ベクトルはどんどん元のデータとかけ離れていってしまいます。
|
60
60
|
|
61
61
|
そこで、次のようにしてはどうでしょうか。
|
62
62
|
|
63
|
-
1. vec0とvec1から、オイラー角rot
|
63
|
+
1. vec0とvec1から、オイラー角rot0を計算する。
|
64
|
-
2. vec0をrot
|
64
|
+
2. vec0をrot0で回転させたものをvec1pとする。
|
65
|
-
vec1pとvec2から、オイラー角rot
|
65
|
+
vec1pとvec2から、オイラー角rot1を計算する。
|
66
|
-
3. vec1をrot
|
66
|
+
3. vec1をrot1で回転させたものをvec2pとする。
|
67
|
-
vec2pとvec3から、オイラー角rot
|
67
|
+
vec2pとvec3から、オイラー角rot2を計算する。
|
68
68
|
4. ...
|
69
69
|
|
70
|
-
元の
|
70
|
+
元の方向ベクトルそのものから角度を求めるのではなく、一つ前に計算した角度による回転を実際に適用してみた結果と次の方向ベクトルとの間の角度を求めます。このようにすれば直前の回転の計算による誤差が打ち消され、安定した動作をするようになるのではないかと考えます。
|
5
少し推敲
test
CHANGED
@@ -46,7 +46,7 @@
|
|
46
46
|
|
47
47
|
\#1ではオイラー角の求め方が正式なものではないことを指摘しましたが、そのことによる数値の不正確さは大したことではないと思われます。MediaPipeで取得できる座標データの精度は有効数字2、3桁程度のものでしょうから、どんなに正確な方式で角度を計算したところでかなり大きな誤差が避けられません。逆に言うと\#1、\#2で述べたような近似計算でも大勢に影響ないでしょう。
|
48
48
|
|
49
|
-
誤差を低減・解消する方法
|
49
|
+
誤差を低減・解消する方法を考えてみました。
|
50
50
|
|
51
51
|
とりあえず、単純な一点だけの運動を考えます。座標点の系列vec0、vec1、vec2、vec3、……があるとします。
|
52
52
|
|
@@ -56,7 +56,7 @@
|
|
56
56
|
2. vec1とvec2から、オイラー角rot2を計算する。
|
57
57
|
3. vec2とvec3から、オイラー角rot3を計算する。
|
58
58
|
4. ...
|
59
|
-
こうして得られたrot1、rot2、rot3、……を、MOTIONデータとして用いています。これだと、計算されるそれぞれの角度には座標の不正確さと計算による誤差が乗っているため、実際に動作させ
|
59
|
+
こうして得られたrot1、rot2、rot3、……を、MOTIONデータとして用いています。これだと、計算されるそれぞれの角度には座標の不正確さと計算による誤差が乗っているため、実際に動作させて小さな回転を次々に適用していくと、結果の座標はどんどん元のデータとかけ離れていってしまいます。
|
60
60
|
|
61
61
|
そこで、次のようにしてはどうでしょうか。
|
62
62
|
|
@@ -68,4 +68,3 @@
|
|
68
68
|
4. ...
|
69
69
|
|
70
70
|
元の座標そのものから角度を求めるのではなく、一つ前に計算した角度による回転を実際に適用してみた結果と次の座標との間の角度を求めます。このようにすれば直前の回転の計算による誤差が打ち消され、安定した動作をするようになるのではないかと考えます。
|
71
|
-
|
4
#3 計算誤差について
test
CHANGED
@@ -38,6 +38,34 @@
|
|
38
38
|
```
|
39
39
|
のようにして計算できることになります (これはラジアン単位なので、度への換算は必要ですが)。【追記終わり】
|
40
40
|
|
41
|
-
___
|
42
|
-
|
41
|
+
### \#3
|
43
42
|
|
43
|
+
計算誤差が蓄積します。
|
44
|
+
|
45
|
+
作成したBVHデータを実際に実行すると、小さな角度の回転の計算を何度も繰り返すため、計算誤差が急速に蓄積すると考えられます。その結果、期待した通りの動きをしなくなるのではないかと考えます。いろいろ考えているうちに、この計算誤差の問題が一番大きいのではないかと思うようになりました。
|
46
|
+
|
47
|
+
\#1ではオイラー角の求め方が正式なものではないことを指摘しましたが、そのことによる数値の不正確さは大したことではないと思われます。MediaPipeで取得できる座標データの精度は有効数字2、3桁程度のものでしょうから、どんなに正確な方式で角度を計算したところでかなり大きな誤差が避けられません。逆に言うと\#1、\#2で述べたような近似計算でも大勢に影響ないでしょう。
|
48
|
+
|
49
|
+
誤差を低減・解消する方法ですが、ご質問で提示されているように元の座標データの変化から角度を計算するのではなく、次のようにすればいいでしょう。
|
50
|
+
|
51
|
+
とりあえず、単純な一点だけの運動を考えます。座標点の系列vec0、vec1、vec2、vec3、……があるとします。
|
52
|
+
|
53
|
+
質問では次のようにして計算していると言えます。
|
54
|
+
|
55
|
+
1. vec0とvec1から、オイラー角rot1を計算する。
|
56
|
+
2. vec1とvec2から、オイラー角rot2を計算する。
|
57
|
+
3. vec2とvec3から、オイラー角rot3を計算する。
|
58
|
+
4. ...
|
59
|
+
こうして得られたrot1、rot2、rot3、……を、MOTIONデータとして用いています。これだと、計算されるそれぞれの角度には座標の不正確さと計算による誤差が乗っているため、実際に動作させると、小さな回転を次々に適用していった結果の座標はどんどん元のデータとかけ離れていってしまいます。
|
60
|
+
|
61
|
+
そこで、次のようにしてはどうでしょうか。
|
62
|
+
|
63
|
+
1. vec0とvec1から、オイラー角rot1を計算する。
|
64
|
+
2. vec0をrot1で回転させたものをvec1pとする。
|
65
|
+
vec1pとvec2から、オイラー角rot2を計算する。
|
66
|
+
3. vec1をrot2で回転させたものをvec2pとする。
|
67
|
+
vec2pとvec3から、オイラー角rot3を計算する。
|
68
|
+
4. ...
|
69
|
+
|
70
|
+
元の座標そのものから角度を求めるのではなく、一つ前に計算した角度による回転を実際に適用してみた結果と次の座標との間の角度を求めます。このようにすれば直前の回転の計算による誤差が打ち消され、安定した動作をするようになるのではないかと考えます。
|
71
|
+
|
3
Typo in code
test
CHANGED
@@ -34,7 +34,7 @@
|
|
34
34
|
|
35
35
|
【2024-03-11追記】さらに、θが微小な角度である場合は sin(θ) = θ とみなしてしまってもいいですよね。そうすると上の関係はさらに簡単になります。結局オイラー角 (の近似値) は、
|
36
36
|
```pytthon
|
37
|
-
zrot = np.cross(vec1xy, vec2xy) / (vec1xyLen * vec2xyLen)
|
37
|
+
zrot = np.cross(vec1xy, vec2xy)[2] / (vec1xyLen * vec2xyLen)
|
38
38
|
```
|
39
39
|
のようにして計算できることになります (これはラジアン単位なので、度への換算は必要ですが)。【追記終わり】
|
40
40
|
|
2
#2 に加筆 (計算を簡単にする)
test
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
コードで採用している計算方法は、z-x-z系やz-y-z系のような正規オイラー角ではないですし、x-y-z系やz-y-x系のようなカルダン角 (テイト-ブライアン角) とも違います ~(BVHでどちらの方式のどういう系を前提としているのかは、私は知らないです)~。
|
8
8
|
|
9
|
-
【追記】Jeff Thingvold (1999) “[Biovision BVH](https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html)” という資料がBVHフォーマットの説明としては最も信頼できそうです (著者は画像解析の専門家です)。これによると (訳)
|
9
|
+
【2024-03-05追記】Jeff Thingvold (1999) “[Biovision BVH](https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html)” という資料がBVHフォーマットの説明としては最も信頼できそうです (著者は画像解析の専門家です)。これによると (訳)
|
10
10
|
|
11
11
|
> (オイラー角のデータから実際の回転を実行するために必要な) 回転行列を作成するわかりやすい方法は、回転の軸毎にひとつずつ、3つの別々の回転行列を作成することだ。そして、それを左から右へY、X、Zの順に連結する。
|
12
12
|
|
@@ -32,3 +32,12 @@
|
|
32
32
|
|
33
33
|
という関係があります。これを使ってθを求めれば回転の方向もわかります (θの正負で方向が判別できます)。ただし今度は角度が90度を超えたかどうかを判別できませんが、1フレームの間にそんなに大きく回転しないのであれば問題ないでしょう。
|
34
34
|
|
35
|
+
【2024-03-11追記】さらに、θが微小な角度である場合は sin(θ) = θ とみなしてしまってもいいですよね。そうすると上の関係はさらに簡単になります。結局オイラー角 (の近似値) は、
|
36
|
+
```pytthon
|
37
|
+
zrot = np.cross(vec1xy, vec2xy) / (vec1xyLen * vec2xyLen)
|
38
|
+
```
|
39
|
+
のようにして計算できることになります (これはラジアン単位なので、度への換算は必要ですが)。【追記終わり】
|
40
|
+
|
41
|
+
___
|
42
|
+
数日中にもう少し続きを書きます。
|
43
|
+
|
1
追記、推敲
test
CHANGED
@@ -2,11 +2,17 @@
|
|
2
2
|
|
3
3
|
### \#1
|
4
4
|
|
5
|
-
オイラー角の求めかたが正
|
5
|
+
オイラー角の求めかたが正式な方法でないように思います。
|
6
6
|
|
7
|
-
コードで採用している計算方法は、z-x-z系やz-y-z系のような正規オイラー角ではないですし、x-y-z系やz-y-x系のようなカルダン角 (テイト-ブライアン角) とも違います (BVHでどちらの方式のどういう系を前提としているのかは、私は知らないです)。
|
7
|
+
コードで採用している計算方法は、z-x-z系やz-y-z系のような正規オイラー角ではないですし、x-y-z系やz-y-x系のようなカルダン角 (テイト-ブライアン角) とも違います ~(BVHでどちらの方式のどういう系を前提としているのかは、私は知らないです)~。
|
8
8
|
|
9
|
+
【追記】Jeff Thingvold (1999) “[Biovision BVH](https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html)” という資料がBVHフォーマットの説明としては最も信頼できそうです (著者は画像解析の専門家です)。これによると (訳)
|
10
|
+
|
11
|
+
> (オイラー角のデータから実際の回転を実行するために必要な) 回転行列を作成するわかりやすい方法は、回転の軸毎にひとつずつ、3つの別々の回転行列を作成することだ。そして、それを左から右へY、X、Zの順に連結する。
|
12
|
+
|
13
|
+
と言っています。ということで、BVHのMOTIONでのオイラー角というのはy-x-z系のカルダン角であると考えられます。【追記終わり】
|
14
|
+
|
9
|
-
|
15
|
+
ただし、コードのような方法 (x、y、zそれぞれの軸についての射影ベクトル同士が成す角を求める) でも、微小な回転については十分な近似になるのかもしれません。もっともそういう意図で採用した方法ではないのなら、これが原因で期待した結果にならないということも考えられます。
|
10
16
|
|
11
17
|
### \#2
|
12
18
|
|
@@ -19,10 +25,10 @@
|
|
19
25
|
|
20
26
|
という関係があることから、θを求めています。しかし内積は可換な演算なので、この方法だとどちら向きに回転しているかはわからないですね。
|
21
27
|
|
22
|
-
a、bの外積 (ベクトル積) には
|
28
|
+
一方、a、bの外積 (ベクトル積) には
|
23
29
|
|
24
30
|
> a × b = (ax\*by - bx\*ay) n = |a| |b| sin(θ) n
|
25
31
|
> (ただしnはz軸方向の単位ベクトル)
|
26
32
|
|
27
|
-
という関係があり
|
33
|
+
という関係があります。これを使ってθを求めれば回転の方向もわかります (θの正負で方向が判別できます)。ただし今度は角度が90度を超えたかどうかを判別できませんが、1フレームの間にそんなに大きく回転しないのであれば問題ないでしょう。
|
28
34
|
|