回答編集履歴

8

微加筆

2024/03/12 23:19

投稿

ikedas
ikedas

スコア4443

test CHANGED
@@ -33,7 +33,9 @@
33
33
  という関係があります。これを使ってθを求めれば回転の方向もわかります (θの正負で方向が判別できます)。ただし今度は角度が90度を超えたかどうかを判別できませんが、1フレームの間にそんなに大きく回転しないのであれば問題ないでしょう。
34
34
 
35
35
  【2024-03-11追記】さらに、θが微小な角度である場合は sin(θ) = θ とみなしてしまってもいいですよね。そうすると上の関係はさらに簡単になります。結局オイラー角 (の近似値) は、
36
- ```pytthon
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

まとめ

2024/03/12 23:12

投稿

ikedas
ikedas

スコア4443

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

微修正

2024/03/12 23:03

投稿

ikedas
ikedas

スコア4443

test CHANGED
@@ -48,23 +48,23 @@
48
48
 
49
49
  誤差を低減・解消する方法を考えてみました。
50
50
 
51
- とりあえず、単純な一点だけの運動を考えます。座標点の系列vec0、vec1、vec2、vec3、……があるとします。
51
+ とりあえず、単純な一点だけの運動を考えます。方向ベクトルの系列vec0、vec1、vec2、vec3、……があるとします。
52
52
 
53
53
  質問では次のようにして計算していると言えます。
54
54
 
55
- 1. vec0とvec1から、オイラー角rot1を計算する。
55
+ 1. vec0とvec1から、オイラー角rot0を計算する。
56
- 2. vec1とvec2から、オイラー角rot2を計算する。
56
+ 2. vec1とvec2から、オイラー角rot1を計算する。
57
- 3. vec2とvec3から、オイラー角rot3を計算する。
57
+ 3. vec2とvec3から、オイラー角rot2を計算する。
58
58
  4. ...
59
- こうして得られたrot1、rot2、rot3、……を、MOTIONデータとして用いています。これだと、計算されるそれぞれの角度には座標の不正確さと計算による誤差が乗っているため、実際に動作させて小さな回転を次々に適用していくと、結果の座標はどんどん元のデータとかけ離れていってしまいます。
59
+ こうして得られたrot0、rot1、rot2、……を、MOTIONデータとして用いています。これだと、計算されるそれぞれの角度には座標の不正確さと計算による誤差が乗っているため、実際に動作させて小さな回転を次々に適用していくと、結果の方向ベクトルはどんどん元のデータとかけ離れていってしまいます。
60
60
 
61
61
  そこで、次のようにしてはどうでしょうか。
62
62
 
63
- 1. vec0とvec1から、オイラー角rot1を計算する。
63
+ 1. vec0とvec1から、オイラー角rot0を計算する。
64
- 2. vec0をrot1で回転させたものをvec1pとする。
64
+ 2. vec0をrot0で回転させたものをvec1pとする。
65
- vec1pとvec2から、オイラー角rot2を計算する。
65
+ vec1pとvec2から、オイラー角rot1を計算する。
66
- 3. vec1をrot2で回転させたものをvec2pとする。
66
+ 3. vec1をrot1で回転させたものをvec2pとする。
67
- vec2pとvec3から、オイラー角rot3を計算する。
67
+ vec2pとvec3から、オイラー角rot2を計算する。
68
68
  4. ...
69
69
 
70
- 元の座標そのものから角度を求めるのではなく、一つ前に計算した角度による回転を実際に適用してみた結果と次の座標との間の角度を求めます。このようにすれば直前の回転の計算による誤差が打ち消され、安定した動作をするようになるのではないかと考えます。
70
+ 元の方向ベクトルそのものから角度を求めるのではなく、一つ前に計算した角度による回転を実際に適用してみた結果と次の方向ベクトルとの間の角度を求めます。このようにすれば直前の回転の計算による誤差が打ち消され、安定した動作をするようになるのではないかと考えます。

5

少し推敲

2024/03/12 10:52

投稿

ikedas
ikedas

スコア4443

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 計算誤差について

2024/03/11 23:43

投稿

ikedas
ikedas

スコア4443

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

2024/03/11 09:33

投稿

ikedas
ikedas

スコア4443

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 に加筆 (計算を簡単にする)

2024/03/11 09:32

投稿

ikedas
ikedas

スコア4443

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

追記、推敲

2024/03/05 09:43

投稿

ikedas
ikedas

スコア4443

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
- かすると微小な回転についてはコードのような方法 (x、y、zそれぞれの軸についての射影ベクトル同士が成す角を求める) で近似できという意図なのかもしれません。そうでない場合、こが原因で期待した結果にならないのかれません
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
- という関係がありこれを使ってθを求めれば回転の向もわかります。ただし今度は角度が90度を超えたかどうかを判別できませんが、1フレームの間にそんなに大きく回転しないのであれば問題ないでしょう。
33
+ という関係があります。これを使ってθを求めれば回転の向もわかります (θの正負で方向が判別できます)。ただし今度は角度が90度を超えたかどうかを判別できませんが、1フレームの間にそんなに大きく回転しないのであれば問題ないでしょう。
28
34