RotationMatrix.cpp
#include "RotationMatrix.h"
#include "AxisVector.h"
using namespace Robotics;
using namespace System;
using namespace System::Diagnostics;
/// [ Constructor ]
RotationMatrix::RotationMatrix() :
Matrix( DefaultRow, DefaultColumn )
{
// 単位行列に初期化する
UnitMatrix();
Debug::Assert( IsOrthogonalMatrix(), "直交行列である" );
}
/// ロール・ピッチ・ヨー角から生成する [ Constructor ]
RotationMatrix::RotationMatrix( double roll, double pitch, double yaw ) :
Matrix( DefaultRow, DefaultColumn )
{
Debug::Assert( Angle::IsExpressedByRadian( roll ), "Radianで表されている" );
Debug::Assert( Angle::IsExpressedByRadian( pitch ), "Radianで表されている" );
Debug::Assert( Angle::IsExpressedByRadian( yaw ), "Radianで表されている" );
// ロール・ピッチ・ヨー角 それぞれのサインを求める
double sinRoll = Math::Sin( roll );
double sinPitch = Math::Sin( pitch );
double sinYaw = Math::Sin( yaw );
// ... コサインを求める
double cosRoll = Math::Cos( roll );
double cosPitch = Math::Cos( pitch );
double cosYaw = Math::Cos( yaw );
this[ 1 ][ 1 ] = cosYaw * cosPitch;
this[ 2 ][ 1 ] = sinYaw * cosPitch;
this[ 3 ][ 1 ] = -sinPitch;
this[ 1 ][ 2 ] = ( -sinYaw * cosRoll ) + ( cosYaw * sinPitch * sinRoll );
this[ 2 ][ 2 ] = ( cosYaw * cosRoll ) + ( sinYaw * sinPitch * sinRoll );
this[ 3 ][ 2 ] = cosPitch * sinRoll;
this[ 1 ][ 3 ] = ( sinYaw * sinRoll ) + ( cosYaw * sinPitch * cosRoll );
this[ 2 ][ 3 ] = ( -cosYaw * sinRoll ) + ( sinYaw * sinPitch * cosRoll );
this[ 3 ][ 3 ] = cosPitch * cosRoll;
Debug::Assert( IsOrthogonalMatrix(), "直交行列である" );
// [ Reference ]「ヒューマノイドロボット」梶田秀司(オーム社) P25
// [ Reference ]「ここが知りたいロボット創造設計」米田完(講談社) P67
}
/// 行列から生成する [ Constructor ]
RotationMatrix::RotationMatrix( Matrix^ matrix ) :
Matrix( matrix )
{
Debug::Assert( IsCorrectSize( matrix ), "行列の大きさは正しい" );
Debug::Assert( IsOrthogonalMatrix(), "直交行列である" );
}
/// XMLから生成する [ Constructor ]
RotationMatrix::RotationMatrix( System::Xml::XmlElement^ element ) :
Matrix( DefaultRow, DefaultColumn )
{
// 単位行列に初期化する
UnitMatrix();
// 要素を取得する
Xml::XmlElement^ node = element[ "rotationMatrix" ];
if( node != nullptr )
{
// 取得できたならば、属性から設定する
this[ 1 ][ 1 ] = Double::Parse( node->GetAttribute( "c11" ) );
this[ 1 ][ 2 ] = Double::Parse( node->GetAttribute( "c12" ) );
this[ 1 ][ 3 ] = Double::Parse( node->GetAttribute( "c13" ) );
this[ 2 ][ 1 ] = Double::Parse( node->GetAttribute( "c21" ) );
this[ 2 ][ 2 ] = Double::Parse( node->GetAttribute( "c22" ) );
this[ 2 ][ 3 ] = Double::Parse( node->GetAttribute( "c23" ) );
this[ 3 ][ 1 ] = Double::Parse( node->GetAttribute( "c31" ) );
this[ 3 ][ 2 ] = Double::Parse( node->GetAttribute( "c32" ) );
this[ 3 ][ 3 ] = Double::Parse( node->GetAttribute( "c33" ) );
}
Debug::Assert( IsOrthogonalMatrix(), "直交行列である" );
}
/// 積 * [ Operator Overloading ]
RotationMatrix^ RotationMatrix::operator*( RotationMatrix^ left, RotationMatrix^ right )
{
RotationMatrix^ result = gcnew RotationMatrix( left );
result *= right;
return result;
}
/// 積 * [ Operator Overloading ]
RotationMatrix^ RotationMatrix::operator*( RotationMatrix^ rotationMatrix, Matrix^ matrix )
{
Debug::Assert( IsCorrectSize( matrix ), "行列の大きさは正しい" );
RotationMatrix^ result = gcnew RotationMatrix( rotationMatrix );
result *= matrix;
return result;
}
/// 積 * [ Operator Overloading ]
RotationMatrix^ RotationMatrix::operator*( Matrix^ matrix, RotationMatrix^ rotationMatrix )
{
Debug::Assert( IsCorrectSize( matrix ), "行列の大きさは正しい" );
RotationMatrix^ result = gcnew RotationMatrix( matrix );
result *= rotationMatrix;
return result;
}
/// 値の等価を確認する
bool RotationMatrix::Equals( RotationMatrix^ obj, Angle precision )
{
// NULLとは等価ではない
if( safe_cast< System::Object^ >( obj ) == nullptr ) return false;
// ロール角、ピッチ角、ヨー角それぞれについて、精度を指定して比較する
return ( this->Roll.Equals( obj->Roll, precision ) )
&& ( this->Pitch.Equals( obj->Pitch, precision ) )
&& ( this->Yaw.Equals( obj->Yaw, precision ) );
}
/// 指数関数を返す (ロドリゲスの公式 / 角速度ベクトルによって生じる回転行列を求める)
RotationMatrix^ RotationMatrix::MatrixExponential( Vector^ axisVector, Angle angle )
{
// ひずみ対称行列(交代行列)を求める
Matrix^ skewSymmetricMatrix = axisVector->SkewSymmetricMatrix();
// ... それの2乗を求める
Matrix^ squareSkewSymmetricMatrix = skewSymmetricMatrix * skewSymmetricMatrix;
// ... そして、それぞれの行列をスカラー倍する
skewSymmetricMatrix *= Math::Sin( angle );
squareSkewSymmetricMatrix *= 1.0 - Math::Cos( angle );
RotationMatrix^ result = gcnew RotationMatrix();
Debug::Assert( result->IsUnitMatrix(), "回転行列は単位行列で初期化されている" );
// ... 先に求めた2つの行列を加算する
result += skewSymmetricMatrix + squareSkewSymmetricMatrix;
return result;
// [ Reference ]「ヒューマノイドロボット」梶田秀司(オーム社) P34
}
/// 行列対数を返す
Matrix^ RotationMatrix::LogMatrix()
{
// 角速度ベクトルを求める
Vector^ angularVelocity = AngularVelocityVector();
// ... それのひずみ対称行列を返す
return angularVelocity->SkewSymmetricMatrix();
// [ Reference ]「ヒューマノイドロボット」梶田秀司(オーム社) P35
}
/// 角速度ベクトルを返す (1秒間で 対応する回転行列に到達する値)
Vector^ RotationMatrix::AngularVelocityVector()
{
if( IsUnitMatrix() )
{
// 単位行列ならば 零ベクトルを返す
Vector^ result = gcnew Vector( 3 );
Debug::Assert( result->IsZeroVector(), "零ベクトルで初期化されている" );
return result;
}
double parameterOfArcCosine
= ( this[ 1 ][ 1 ] + this[ 2 ][ 2 ] + this[ 3 ][ 3 ] - 1.0 ) / 2.0;
Debug::Assert(
( -1.0 <= parameterOfArcCosine ) &&
( +1.0 >= parameterOfArcCosine ),
"逆余弦の引数は正当である"
);
double theta = Math::Acos( parameterOfArcCosine );
double scalar = theta / ( 2.0 * Math::Sin( theta ) );
Vector^ result = gcnew Vector( 3 );
result[ 1 ] = scalar * ( this[ 3 ][ 2 ] - this[ 2 ][ 3 ] );
result[ 2 ] = scalar * ( this[ 1 ][ 3 ] - this[ 3 ][ 1 ] );
result[ 3 ] = scalar * ( this[ 2 ][ 1 ] - this[ 1 ][ 2 ] );
return result;
// [ Reference ]「ヒューマノイドロボット」梶田秀司(オーム社) P35
}
/// X方向の軸ベクトルを取得する [ Property ]
AxisVector^ RotationMatrix::X::get()
{
AxisVector^ result = gcnew AxisVector(
this[ 1 ][ 1 ],
this[ 2 ][ 1 ],
this[ 3 ][ 1 ]
);
return result;
}
/// Y方向の軸ベクトルを取得する [ Property ]
AxisVector^ RotationMatrix::Y::get()
{
AxisVector^ result = gcnew AxisVector(
this[ 1 ][ 2 ],
this[ 2 ][ 2 ],
this[ 3 ][ 2 ]
);
return result;
}
/// Z方向の軸ベクトルを取得する [ Property ]
AxisVector^ RotationMatrix::Z::get()
{
AxisVector^ result = gcnew AxisVector(
this[ 1 ][ 3 ],
this[ 2 ][ 3 ],
this[ 3 ][ 3 ]
);
return result;
}
/// ロール角を取得する [ Property ]
Angle RotationMatrix::Roll::get()
{
double y = this[ 3 ][ 2 ];
double x = this[ 3 ][ 3 ];
return Angle( Atan2( y, x ) );
// [ Reference ]「ここが知りたいロボット創造設計」米田完(講談社) P67
}
/// ピッチ角を取得する [ Property ]
Angle RotationMatrix::Pitch::get()
{
double y = this[ 3 ][ 1 ];
double x = Math::Sqrt(
this[ 1 ][ 1 ] * this[ 1 ][ 1 ] +
this[ 2 ][ 1 ] * this[ 2 ][ 1 ]
);
return Angle( Atan2( -y, x ) );
// [ Reference ]「ここが知りたいロボット創造設計」米田完(講談社) P67
}
/// ヨー角を取得する [ Property ]
Angle RotationMatrix::Yaw::get()
{
double y = this[ 2 ][ 1 ];
double x = this[ 1 ][ 1 ];
return Angle( Atan2( y, x ) );
// [ Reference ]「ここが知りたいロボット創造設計」米田完(講談社) P67
}
/// 基準に近い ロール角を取得する
Angle RotationMatrix::GetRoll( Angle standard )
{
double y = this[ 3 ][ 2 ];
double x = this[ 3 ][ 3 ];
// 符号を変えて、2つの解を求める
Angle result1( Atan2( +y, +x ) );
Angle result2( Atan2( -y, -x ) );
// ... 基準に近い方を返す
return standard.Near( result1, result2 );
}
/// 基準に近い ピッチ角を取得する
Angle RotationMatrix::GetPitch( Angle standard )
{
double y = this[ 3 ][ 1 ];
double x = Math::Sqrt(
this[ 1 ][ 1 ] * this[ 1 ][ 1 ] +
this[ 2 ][ 1 ] * this[ 2 ][ 1 ]
);
// 符号を変えて、2つの解を求める
Angle result1( Atan2( -y, +x ) );
Angle result2( Atan2( -y, -x ) );
// ... 基準に近い方を返す
return standard.Near( result1, result2 );
}
/// 基準に近い ヨー角を取得する
Angle RotationMatrix::GetYaw( Angle standard )
{
double y = this[ 2 ][ 1 ];
double x = this[ 1 ][ 1 ];
// 符号を変えて、2つの解を求める
Angle result1( Atan2( +y, +x ) );
Angle result2( Atan2( -y, -x ) );
// ... 基準に近い方を返す
return standard.Near( result1, result2 );
}
/// 大きさは正しいか?
bool RotationMatrix::IsCorrectSize( Matrix^ obj )
{
// 行数は既定の行数であり、列数は既定の列数か?
return ( obj->Row == DefaultRow )
&& ( obj->Column == DefaultColumn );
}
/// 水平か?
bool RotationMatrix::IsHorizontal( double precision )
{
// ロール角とピッチ角が ゼロであることを確認する
return ( Math::Abs( Roll ) < precision )
&& ( Math::Abs( Pitch ) < precision );
}
/// 水平にする
void RotationMatrix::Horizontal()
{
double sinYaw = Math::Sin( Yaw );
double cosYaw = Math::Cos( Yaw );
this[ 1 ][ 1 ] = cosYaw;
this[ 2 ][ 1 ] = sinYaw;
this[ 3 ][ 1 ] = 0.0;
this[ 1 ][ 2 ] = -sinYaw;
this[ 2 ][ 2 ] = cosYaw;
this[ 3 ][ 2 ] = 0.0;
this[ 1 ][ 3 ] = 0.0;
this[ 2 ][ 3 ] = 0.0;
this[ 3 ][ 3 ] = 1.0;
Debug::Assert( IsHorizontal( Double::Epsilon ), "水平である" );
}
/// タンジェントが 2つの指定された数の商である角度を返す
double RotationMatrix::Atan2( double y, double x )
{
if( x == 0 && y == 0 )
{
// 2つの引数がゼロの場合は、ゼロを返す
return 0.0;
}
else
{
// Mathクラスの 同一関数を呼び出す
return Math::Atan2( y, x );
}
/// @note
/// 2つの引数がゼロの場合の戻り地について、C言語の標準ライブラリでは処理系定義とされている。
/// しかし.NETの Mathクラスにおいては、戻り値が明記されていない。
/// よって、ここで処理を明示的に行っている。
/// http://msdn2.microsoft.com/ja-jp/library/system.math.atan2.aspx
}
/// インスタンスの説明を文字列で返す (MATLAB形式)
System::String^ RotationMatrix::ToString()
{
return String::Format(
"[ {0:0.0000000} {1:0.0000000} {2:0.0000000};" + Environment::NewLine +
" {3:0.0000000} {4:0.0000000} {5:0.0000000};" + Environment::NewLine +
" {6:0.0000000} {7:0.0000000} {8:0.0000000} ]",
this[ 1 ][ 1 ], this[ 1 ][ 2 ], this[ 1 ][ 3 ],
this[ 2 ][ 1 ], this[ 2 ][ 2 ], this[ 2 ][ 3 ],
this[ 3 ][ 1 ], this[ 3 ][ 2 ], this[ 3 ][ 3 ]
);
}
/// インスタンスの説明をロール・ピッチ・ヨー表現の文字列で返す
System::String^ RotationMatrix::ToStringByRollPitchYaw()
{
return String::Format(
"Roll:{0:0.0} Pitch:{1:0.0} Yaw:{2:0.0}",
Roll.Degree, Pitch.Degree, Yaw.Degree
);
}
/// インスタンスの説明をXMLで返す
System::Xml::XmlElement^ RotationMatrix::ToXml( System::Xml::XmlDocument^ document )
{
// ルートを生成する
Xml::XmlElement^ root = document->CreateElement( "rotationMatrix" );
// ... フィールドを属性として設定する
root->SetAttribute( "c11", this[ 1 ][ 1 ].ToString() );
root->SetAttribute( "c12", this[ 1 ][ 2 ].ToString() );
root->SetAttribute( "c13", this[ 1 ][ 3 ].ToString() );
root->SetAttribute( "c21", this[ 2 ][ 1 ].ToString() );
root->SetAttribute( "c22", this[ 2 ][ 2 ].ToString() );
root->SetAttribute( "c23", this[ 2 ][ 3 ].ToString() );
root->SetAttribute( "c31", this[ 3 ][ 1 ].ToString() );
root->SetAttribute( "c32", this[ 3 ][ 2 ].ToString() );
root->SetAttribute( "c33", this[ 3 ][ 3 ].ToString() );
return root;
}