001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.math.geometry; 006 007import edu.wpi.first.math.Matrix; 008import edu.wpi.first.math.Nat; 009 010/** A helper class that converts Pose3d objects between different standard coordinate frames. */ 011public class CoordinateSystem { 012 private static final CoordinateSystem m_nwu = 013 new CoordinateSystem(CoordinateAxis.N(), CoordinateAxis.W(), CoordinateAxis.U()); 014 private static final CoordinateSystem m_edn = 015 new CoordinateSystem(CoordinateAxis.E(), CoordinateAxis.D(), CoordinateAxis.N()); 016 private static final CoordinateSystem m_ned = 017 new CoordinateSystem(CoordinateAxis.N(), CoordinateAxis.E(), CoordinateAxis.D()); 018 019 // Rotation from this coordinate system to NWU coordinate system when applied extrinsically 020 private final Rotation3d m_rotation; 021 022 /** 023 * Constructs a coordinate system with the given cardinal directions for each axis. 024 * 025 * @param positiveX The cardinal direction of the positive x-axis. 026 * @param positiveY The cardinal direction of the positive y-axis. 027 * @param positiveZ The cardinal direction of the positive z-axis. 028 * @throws IllegalArgumentException if the coordinate system isn't special orthogonal 029 */ 030 public CoordinateSystem( 031 CoordinateAxis positiveX, CoordinateAxis positiveY, CoordinateAxis positiveZ) { 032 // Construct a change of basis matrix from the source coordinate system to the 033 // NWU coordinate system. Each column vector in the change of basis matrix is 034 // one of the old basis vectors mapped to its representation in the new basis. 035 var R = new Matrix<>(Nat.N3(), Nat.N3()); 036 R.assignBlock(0, 0, positiveX.m_axis); 037 R.assignBlock(0, 1, positiveY.m_axis); 038 R.assignBlock(0, 2, positiveZ.m_axis); 039 040 // The change of basis matrix should be a pure rotation. The Rotation3d 041 // constructor will verify this by checking for special orthogonality. 042 m_rotation = new Rotation3d(R); 043 } 044 045 /** 046 * Returns an instance of the North-West-Up (NWU) coordinate system. 047 * 048 * <p>The +X axis is north, the +Y axis is west, and the +Z axis is up. 049 * 050 * @return An instance of the North-West-Up (NWU) coordinate system. 051 */ 052 public static CoordinateSystem NWU() { 053 return m_nwu; 054 } 055 056 /** 057 * Returns an instance of the East-Down-North (EDN) coordinate system. 058 * 059 * <p>The +X axis is east, the +Y axis is down, and the +Z axis is north. 060 * 061 * @return An instance of the East-Down-North (EDN) coordinate system. 062 */ 063 public static CoordinateSystem EDN() { 064 return m_edn; 065 } 066 067 /** 068 * Returns an instance of the North-East-Down (NED) coordinate system. 069 * 070 * <p>The +X axis is north, the +Y axis is east, and the +Z axis is down. 071 * 072 * @return An instance of the North-East-Down (NED) coordinate system. 073 */ 074 public static CoordinateSystem NED() { 075 return m_ned; 076 } 077 078 /** 079 * Converts the given translation from one coordinate system to another. 080 * 081 * @param translation The translation to convert. 082 * @param from The coordinate system the pose starts in. 083 * @param to The coordinate system to which to convert. 084 * @return The given translation in the desired coordinate system. 085 */ 086 public static Translation3d convert( 087 Translation3d translation, CoordinateSystem from, CoordinateSystem to) { 088 // Convert to NWU, then convert to the new coordinate system 089 return translation.rotateBy(from.m_rotation).rotateBy(to.m_rotation.unaryMinus()); 090 } 091 092 /** 093 * Converts the given rotation from one coordinate system to another. 094 * 095 * @param rotation The rotation to convert. 096 * @param from The coordinate system the rotation starts in. 097 * @param to The coordinate system to which to convert. 098 * @return The given rotation in the desired coordinate system. 099 */ 100 public static Rotation3d convert( 101 Rotation3d rotation, CoordinateSystem from, CoordinateSystem to) { 102 // Convert to NWU, then convert to the new coordinate system 103 return rotation.rotateBy(from.m_rotation).rotateBy(to.m_rotation.unaryMinus()); 104 } 105 106 /** 107 * Converts the given pose from one coordinate system to another. 108 * 109 * @param pose The pose to convert. 110 * @param from The coordinate system the pose starts in. 111 * @param to The coordinate system to which to convert. 112 * @return The given pose in the desired coordinate system. 113 */ 114 public static Pose3d convert(Pose3d pose, CoordinateSystem from, CoordinateSystem to) { 115 return new Pose3d( 116 convert(pose.getTranslation(), from, to), convert(pose.getRotation(), from, to)); 117 } 118 119 /** 120 * Converts the given transform from one coordinate system to another. 121 * 122 * @param transform The transform to convert. 123 * @param from The coordinate system the transform starts in. 124 * @param to The coordinate system to which to convert. 125 * @return The given transform in the desired coordinate system. 126 */ 127 public static Transform3d convert( 128 Transform3d transform, CoordinateSystem from, CoordinateSystem to) { 129 // coordRot is the rotation that converts between the coordinate systems when applied 130 // extrinsically. It first converts to NWU, then converts to the new coordinate system. 131 var coordRot = from.m_rotation.rotateBy(to.m_rotation.unaryMinus()); 132 // The new rotation is the extrinsic rotation from convert(zero) to 133 // convert(transformRot). That is, applying convertedRot extrinsically to 134 // convert(zero) produces convert(transformRot). In the below snippet, we 135 // use matrix notation, so rotA rotB applies rotA extrinsically to rotB. 136 // 137 // convertedRot convert(zero) = convert(transformRot) 138 // convertedRot = convert(transformRot) convert(zero)⁻¹ 139 // = (coordRot transformRot) (coordRot zero)⁻¹ 140 // = (coordRot transformRot) coordRot⁻¹ 141 // 142 // In code, the equivalent for rotA rotB is rotB.rotateBy(rotA) (note the 143 // change in order), and the equivalent for rot⁻¹ is rot.unaryMinus(). 144 return new Transform3d( 145 convert(transform.getTranslation(), from, to), 146 coordRot.unaryMinus().rotateBy(transform.getRotation().rotateBy(coordRot))); 147 } 148}