Torque.cpp

#include "StdAfx.h"
#include "Torque.h"


#include "Model.h"
#include "Link.h"


using namespace Core;
using namespace Robotics;

using namespace System;
using namespace System::Diagnostics;


/// [ Constructor ]
Torque::Torque( Model^ model ) :
    m_model( model )
{
    m_totalCenterOfGravity = model->CenterOfGravity;    // 全体の重心
    m_totalMass = model->Mass;                          // 全体の質量

    // 床反力を求める
    CalculateGroundReactionForce();
}


/// トルクを取得する
double Torque::default::get( Link^ targetLink )
{
    // 胴体ならば、トルクは生じない
    if( targetLink->IsBody() ) return 0.0;


    LinkList^ operationLinks = gcnew LinkList();

    // 対象とするリンクから 末端までのリンクを取得する
    targetLink->AddOwnToRouteToEnd( operationLinks );


    /// @todo 末端以外のリンクが支持相の場合にも対応する。


    // 末端のリンクを取得する
    Link^ endLink = operationLinks[ operationLinks->Count - 1 ];

    // ... それが支持相ならば、演算対象を変更する
    if( endLink->IsSupportingPhase() )
    {
        // 末端までのリンク以外に支持相が存在するならば、その反力も考慮する
        if( m_supportingLink->Count != 1 )
        {
            double torqueByGroundReactionForce = 0.0;

            // 床反力による トルクの和を求める
            for( int i = 0; i < m_supportingLink->Count; i++ )
            {
                torqueByGroundReactionForce += targetLink->CalculateTorque(
                    m_groundReactionForce[ i ],             // 床反力
                    m_supportingLink[ i ]->AbsolutePosition // 支持相のリンクの絶対位置
                    );
            }


            // 質量と重力加速度の積から 重量を求める (重力加速度は、Z軸の負の方向にしか働かない)
            PositionVector^ weight = gcnew PositionVector( 0.0, 0.0, m_totalMass * ( -AccelerationOfGravity ) );

            // ... 重心によるトルクを求める
            double torque = targetLink->CalculateTorque( weight, m_totalCenterOfGravity );

            // ... それに 床反力によるトルクを加算して返す
            return( torque + torqueByGroundReactionForce );
        }


        // 全てのリンクを取得する
        LinkList^ newOperationLinks = gcnew LinkList( m_model->LinkLists );

        // ... そこから 対象とするリンク以外の 末端までのリンクを削除する
        for( int i = 1; i < operationLinks->Count; i++ )
        {
            newOperationLinks->Remove( operationLinks[ i ] );
        }

        // それを演算対象とする
        operationLinks = newOperationLinks;
    }


    // 対象とするリンクの 質量の合計を求める
    double mass = m_model->CalculateMass( operationLinks );

    // 対象とするリンクの 全体の重心を求める
    PositionVector^ centerOfGravity = m_model->CalculateCenterOfGravity( operationLinks );


    // 質量と重力加速度の積から 重量を求める (重力加速度は、Z軸の負の方向にしか働かない)
    PositionVector^ weight = gcnew PositionVector( 0.0, 0.0, mass * ( -AccelerationOfGravity ) );

    // ... 重心によるトルクを求める
    return targetLink->CalculateTorque( weight, centerOfGravity );
}


/// 床反力を求める
void Torque::CalculateGroundReactionForce()
{
    // 質量と重力加速度の積から 重量を求める (反力を考えるので、重力加速度はZ軸の正の方向に働くものとする)
    PositionVector^ weight = gcnew PositionVector( 0.0, 0.0, m_totalMass * AccelerationOfGravity );


    // 支持相のリンクを取得する
    m_supportingLink = GetLinkWithSupportingPhase( m_model->LinkLists );

    int supportingSum = m_supportingLink->Count;    // 支持相のリンクの数
    Debug::Assert( 0 < supportingSum, "支持相のリンクは存在する" );
    Debug::Assert( supportingSum <= 2, "現時点では、支持相が2つまでしか対応できない" );


    // 床反力を格納するフィールドを 支持相の数だけ生成する
    m_groundReactionForce = gcnew PositionVectors( supportingSum );

    if( supportingSum == 1 )
    {
        // 支持相が1つだけならば、そこに全ての重量がかかる
        m_groundReactionForce[ 0 ] = weight;
    }
    else
    {
        array< double >^ momentArm = gcnew array< double >( supportingSum );

        // 反力の位置までの モーメント・アームの大きさを求める
        for( int i = 0; i < supportingSum; i++ )
        {
            // 支持相のリンクから重心までの 変位を求める
            PositionVector^ displacement = m_supportingLink[ i ]->AbsolutePosition - m_totalCenterOfGravity;


            // 反力の軸ベクトルを定義する
            AxisVector^ axisOfReactionForce = gcnew AxisVector( 0.0, 0.0, 1.0 );

            // ... それと変位とのベクトルの外積から モーメント・アームの大きさを求める
            Vector^ momentArmVector = Vector::CrossProduct( axisOfReactionForce, displacement );


            // ベクトルのノルムから それの大きさを求める
            momentArm[ i ] = momentArmVector->Norm();
        }

        if( supportingSum == 2 )
        {
            double length = momentArm[ 0 ] + momentArm[ 1 ];

            // モーメント・アームの大きさに応じて それぞれのリンクに重量がかかる
            m_groundReactionForce[ 0 ] = ( momentArm[ 1 ] / length ) * weight;
            m_groundReactionForce[ 1 ] = ( momentArm[ 0 ] / length ) * weight;
        }
    }
}


/// 支持相のリンクを取得する
LinkList^ Torque::GetLinkWithSupportingPhase( LinkList^ links )
{
    LinkList^ result = gcnew LinkList();

    for each( Link^ link in links )
    {
        if( link->IsSupportingPhase() )
        {
            // 支持相のリンクならば それを追加する
            result->Add( link );
        }
    }
    return result;
}