Абсолютная ориентация датчика

Я использую данные из локального набора данных MISB KLV и матричных операций. предоставлено в NASA Worldwind. Данные MISB обеспечивают ориентацию платформы по рысканию, тангажу и крену, а также ориентацию датчика относительно платформы по рысканью, тангажу и крену. Я пытаюсь рассчитать абсолютную ориентацию (рысканье, тангаж, крен) датчика относительно севера на основе ориентации платформы и относительной ориентации датчика.

В настоящее время я вычисляю матрицу вращения платформы и матрицу относительного вращения датчиков и умножаю результаты вместе. Полученная матрица вращения не кажется правильной. Согласно документации MISB в разделе 6.2.4, порядок операций с углом Эйлера: Yaw, Pitch, затем Roll. Каков правильный способ объединения матриц вращения для получения абсолютного вращения?

  //use transpose for clockwise rotation     
  Matrix mpYaw   = Matrix.fromRotationZ(pYaw).getTranspose();
  Matrix mpPitch = Matrix.fromRotationY(pPitch).getTranspose();
  Matrix mpRoll  = Matrix.fromRotationX(pRoll).getTranspose();      

  Matrix msYaw   = Matrix.fromRotationZ(sYaw).getTranspose();
  Matrix msPitch = Matrix.fromRotationY(sPitch).getTranspose();
  Matrix msRoll  = Matrix.fromRotationX(sRoll).getTranspose();

  Matrix mpRot = mpYaw.multiply(mpPitch).multiply(mpRoll); //platform      
  Matrix msRot = msYaw.multiply(msPitch).multiply(msRoll); //sensor

  Matrix maRot = mpRot.multiply(msRot); //absolute

Пример данных MISB:

Platform Heading Angle:175.66308079652094
Platform Pitch Angle:3.4296700949125647
Platform Roll Angle:-0.3982665486617634    
Sensor Rel. Az. Angle:326.08593764856596
Sensor Rel. El. Angle:-21.60937493741949
Sensor Rel. Roll Angle:0.0

Sensor Latitude:33.03482410173622
Sensor Longitude:-114.45451377632772
Sensor True Altitude:1022.4368657969026
Frame Center Lat.:33.01531312661958
Frame Center Lon.:-114.4367867216639
Frame Center El.:79.58953231097883
Slant Range:2883.640118614687

РЕДАКТИРОВАТЬ 1:

После применения исправления, предложенного @anjruu, похоже, что результат близок, но все же немного отличается. Я рассчитал локальные координаты NED до целевого местоположения, умножив прямой вектор матрицы вращения на целевое расстояние, предоставленное MISB. Затем я рассчитал локальные координаты NED (используя ViewUtil) из MISB предоставил целевое местоположение с источником, установленным на предоставленное местоположение платформы, и результаты немного отличаются.

  Matrix mpYaw   = Matrix.fromRotationZ(pYaw).getTranspose();
  Matrix mpPitch = Matrix.fromRotationY(pPitch).getTranspose();
  Matrix mpRoll  = Matrix.fromRotationX(pRoll).getTranspose();      

  Matrix msYaw   = Matrix.fromRotationZ(sYaw).getTranspose();
  Matrix msPitch = Matrix.fromRotationY(sPitch).getTranspose();
  Matrix msRoll  = Matrix.fromRotationX(sRoll).getTranspose();

  Matrix mpRot = mpRoll.multiply(mpPitch).multiply(mpYaw); //platform      
  Matrix msRot = msRoll.multiply(msPitch).multiply(msYaw); //sensor

  Matrix maRot = msRot.multiply(mpRot); //absolute

  Globe globe = new Earth();

  Position pPlatform = Position.fromDegrees(33.03482410173622, -114.45451377632772, 1022.4368657969026);
  Position pTarget   = Position.fromDegrees(33.01531312661958, -114.4367867216639, 79.58953231097883);
  double targetRange = 2883.640118614687;

  Vec4 vTarNED = new Vec4(1,0,0).transformBy3(maRot.getTranspose()).multiply3(targetRange);
  //NED = (-2165.935747907422, 1656.9597179630864, 937.3298046411029, 1.0)

  Matrix localENU = ViewUtil.computePositionTransform(globe, pPlatform);
  Vec4 vTarENU = globe.computePointFromPosition(pTarget).transformBy4(localENU);
  //ENU = (1656.3846316600684, -2163.7501770820236, -943.4305881811306, 1.0)
  //NED = (-2163.7501770820236, 1656.3846316600684,  943.4305881811306, 1.0)

person user66332    schedule 22.09.2014    source источник
comment
Можете ли вы включить класс Matrix или хотя бы спецификацию для multiply? Я бы предположил, что Matrix::multiply — это правое умножение, и что поза камеры относится к позе платформы, а это означает, что она должна быть msRot.multiply(mpRot), и что вы должны перевернуть цепочку умножения для получения mpRot и msRot, но я не могу сказать не зная, что на самом деле делает multiply.   -  person anjruu    schedule 23.09.2014
comment
@anjruu Ссылка NASA WorldWind в посте указывает на класс Matrix.   -  person user66332    schedule 23.09.2014
comment
Так бывает, извините. Да, A.multiply(B) это A*B (что разумно), поэтому я думаю, что это должно быть Matrix mpRot = mpRoll.multiply(mpPitch).multiply(mpYaw);, аналогично для msRot и maRot.   -  person anjruu    schedule 23.09.2014
comment
Также см. en.wikipedia.org/wiki/Euler_angles#Rotation_matrix.   -  person anjruu    schedule 23.09.2014
comment
@anjruu Я добавил тестовый пример, чтобы проверить ваши изменения, но я все еще получаю немного неверные результаты.   -  person user66332    schedule 23.09.2014


Ответы (1)


Для дальнейших исследователей я столкнулся с той же проблемой. Основная проблема заключается в коэффициенте ошибок датчиков, чтобы напрямую установить положение и ориентацию вида на основе данных датчиков, вам необходимо рассчитать эти ошибки и добавить их в качестве значений смещения. Но, скорее всего, у нас есть World Wind, который сделает за нас большинство вычислений.

При использовании любого 3D-движка вы на самом деле не получаете никакой угловой информации, потому что у вас уже есть и глаза, и позиции взгляда. Вы можете рассчитать необходимые значения ориентации из этих позиций, а также управлять ими вручную и автоматически.

Здесь в моей функции устанавливается положение камеры в соответствии с заданными данными MISB KLV.

public void setCameraPosition(BTelemetryData pData){


    // Get Platform Location Information
    Angle tPlatformLatitude    = Angle.fromDegrees(Double.parseDouble(pData.getAlternatePlatformLatitude()));
    Angle tPlatformLongitude   = Angle.fromDegrees(Double.parseDouble(pData.getAlternatePlatformLongitude()));
    double tPlatformAltitude   = Double.parseDouble(pData.getPlatformGPSAltitude());
    Position tPlatfromPosition = new Position(tPlatformLatitude, tPlatformLongitude ,tPlatformAltitude);

    // Get LookAt Location Information
    Angle tLookAtLatitude = Angle.fromDegrees(Double.parseDouble(pData.getFrameCenterLatitude()));
    Angle tLookAtLongitude= Angle.fromDegrees(Double.parseDouble(pData.getFrameCenterLongitude()));
    // Note must take into account the surface elevation at given lat lon.
    double tLookAtAltitude= getWwd().getModel().getGlobe().getElevation(tLookAtLatitude, tLookAtLongitude);
    Position tLookAtPosition = new Position(tLookAtLatitude, tLookAtLongitude ,tLookAtAltitude);

    // First things first, we need to Set Field of View
    getView().setFieldOfView(Angle.fromDegrees(Double.parseDouble(pData.getSensorHorizontalFieldofView())));
    if (useAutoCameraPosition())
        setCameraPositionAutomatically(tLookAtPosition, tPlatfromPosition);
    else
        calculateAndSetCameraPosition(tLookAtPosition, tPlatfromPosition);

    getView().firePropertyChange(AVKey.VIEW, null, getView());

}

public void setCameraPositionAutomatically(Position pLookAtPosition, Position pEyePosition){
    getView().setEyePosition(pEyePosition);
    getView().setOrientation(pEyePosition, pLookAtPosition);
}
public void calculateAndSetCameraPosition(Position pLookAtPosition, Position pEyePosition){
        double tPitch   = getPitchAngleBetweenPositionInDegrees(pLookAtPosition, pEyePosition);
        double tHeading = getHeadingAngleBetweenPositionInDegrees(pLookAtPosition, pEyePosition);

        getView().setEyePosition(pEyePosition);
        getView().setHeading(Angle.fromDegrees(tHeading));
        getView().setPitch(Angle.fromDegrees(tPitch));
}


public double getPitchAngleBetweenPositionInDegrees(Position pLookAt, Position pEyePosition) {

    // Calculate the radius at given look at position
    double tRadius = getWwd().getModel().getGlobe().getRadiusAt(pLookAt);

    // Find the Surrounding Radial Length Between those positions
    double tRadialDistance = Position.greatCircleDistance(pLookAt, pEyePosition).getRadians() * tRadius;

    // Find the Ratio Between Distance, which will give the offset and Angle
    double tTheta = tRadialDistance / tRadius;

    // Get the surface elevation of lookatposition
    double tLookAtElevation = pLookAt.getElevation();

    // Get Altitude of given eye position
    double tEyeAltitude = pEyePosition.getAltitude();

    // Delta Location Changes in cartesian
    double tDeltaX  = (tRadius + tLookAtElevation) * Math.cos(tTheta);
    double tDeltaY  = (tRadius + tLookAtElevation) * Math.sin(tTheta);
    double tDeltaZ  =  tRadius + tEyeAltitude - tDeltaX;

    double alpha = Math.atan(tDeltaZ / tDeltaY)  - tTheta;

    // Convert NED to World Wind Coordinate System. The Pitch angle should be 90 - calculated.
    double degrees = 90 - Math.toDegrees(alpha); 

    System.out.println("Elevation Angle Between Positions = " + degrees);
    return degrees;
}

public  double getHeadingAngleBetweenPositionInDegrees(Position pLookAtPosition, Position  pEyePosition) {

    double tLatEye     = pEyePosition.getLatitude().radians;
    double tLatLookAt  = pLookAtPosition.getLatitude().radians;

    double tLonLookAt = pLookAtPosition.getLongitude().radians;
    double tLonEye    = pEyePosition.getLongitude().radians;
    double dLon = (tLonLookAt - tLonEye);

    double y = Math.sin(dLon) * Math.cos(tLatLookAt);
    double x = Math.cos(tLatEye) * Math.sin(tLatLookAt) - Math.sin(tLatEye)
             * Math.cos(tLatLookAt) * Math.cos(dLon);

    // Calculate the Bearing Angle. 
    double tBearing = Math.toDegrees(Math.atan2(y, x));

    // Calculate the absolute value of that Angle
    tBearing = (tBearing + 360) % 360;

    // Note that world wind takes the Heading in clockwise, if you want to make it counter clockwise, you need to subtract it from 360 degrees
   //tBearing = 360 - tBearing;

    return tBearing;
}

! //

В моем коде я не устанавливал угол крена, но, согласно документу, вы можете просто суммировать углы крена датчика и платформы, а затем установить угол крена.

Одно маленькое замечание: у World Wind есть два разных класса представления: BasicOrbitView и BasicFlyView, для имитации заданных данных вы должны использовать BasicFlyView. Причина этого в том, что в FlyView вы сохраняете положение камеры при установке углов, но, с другой стороны, в OrbitView вы сохраняете положение взгляда и изменяете как углы, так и положение камеры относительно этих углов. Вы можете использовать метод setOrientation, если вам достаточно точности.

Результат:

Хорошая кодировка :)

person john whitegun    schedule 13.07.2017