Transform from ECEF to a local frame where z points up. (#662)

For a given latitude and longitude, return a transformation that takes a point in ECEF coordinates to
a local frame, where the z axis points up.

PAIR=wohe

[RFC=0007](https://github.com/googlecartographer/rfcs/blob/master/text/0007-nav-sat-support.md)
master
Susanne Pielawa 2018-01-12 11:39:29 +01:00 committed by Wally B. Feed
parent 960d1a487c
commit b0a937b7ed
3 changed files with 70 additions and 9 deletions

View File

@ -16,6 +16,9 @@
#include "cartographer_ros/msg_conversion.h" #include "cartographer_ros/msg_conversion.h"
#include <cmath>
#include "cartographer/common/math.h"
#include "cartographer/common/port.h" #include "cartographer/common/port.h"
#include "cartographer/common/time.h" #include "cartographer/common/time.h"
#include "cartographer/transform/proto/transform.pb.h" #include "cartographer/transform/proto/transform.pb.h"
@ -252,22 +255,33 @@ geometry_msgs::Point ToGeometryMsgPoint(const Eigen::Vector3d& vector3d) {
Eigen::Vector3d LatLongAltToEcef(const double latitude, const double longitude, Eigen::Vector3d LatLongAltToEcef(const double latitude, const double longitude,
const double altitude) { const double altitude) {
// https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates // https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates
constexpr double a = 6378137; // semi-major axis, equator to center. constexpr double a = 6378137.; // semi-major axis, equator to center.
constexpr double f = 1 / 298.257223563; constexpr double f = 1. / 298.257223563;
constexpr double b = a * (1 - f); // semi-minor axis, pole to center. constexpr double b = a * (1. - f); // semi-minor axis, pole to center.
constexpr double a_squared = a * a; constexpr double a_squared = a * a;
constexpr double b_squared = b * b; constexpr double b_squared = b * b;
constexpr double e_squared = (a_squared - b_squared) / a_squared; constexpr double e_squared = (a_squared - b_squared) / a_squared;
constexpr double kDegreesToRadians = M_PI / 180; const double sin_phi = std::sin(cartographer::common::DegToRad(latitude));
const double sin_phi = sin(latitude * kDegreesToRadians); const double cos_phi = std::cos(cartographer::common::DegToRad(latitude));
const double cos_phi = cos(latitude * kDegreesToRadians); const double sin_lambda = std::sin(cartographer::common::DegToRad(longitude));
const double sin_lambda = sin(longitude * kDegreesToRadians); const double cos_lambda = std::cos(cartographer::common::DegToRad(longitude));
const double cos_lambda = cos(longitude * kDegreesToRadians); const double N = a / std::sqrt(1 - e_squared * sin_phi * sin_phi);
const double N = a / sqrt(1 - e_squared * sin_phi * sin_phi);
const double x = (N + altitude) * cos_phi * cos_lambda; const double x = (N + altitude) * cos_phi * cos_lambda;
const double y = (N + altitude) * cos_phi * sin_lambda; const double y = (N + altitude) * cos_phi * sin_lambda;
const double z = (b_squared / a_squared * N + altitude) * sin_phi; const double z = (b_squared / a_squared * N + altitude) * sin_phi;
return Eigen::Vector3d(x, y, z); return Eigen::Vector3d(x, y, z);
} }
cartographer::transform::Rigid3d ComputeLocalFrameFromLatLong(
const double latitude, const double longitude) {
const Eigen::Vector3d translation = LatLongAltToEcef(latitude, longitude, 0.);
const Eigen::Quaterniond rotation =
Eigen::AngleAxisd(cartographer::common::DegToRad(latitude - 90.),
Eigen::Vector3d::UnitY()) *
Eigen::AngleAxisd(cartographer::common::DegToRad(-longitude),
Eigen::Vector3d::UnitZ());
return cartographer::transform::Rigid3d(rotation * -translation, rotation);
}
} // namespace cartographer_ros } // namespace cartographer_ros

View File

@ -74,6 +74,11 @@ Eigen::Quaterniond ToEigen(const geometry_msgs::Quaternion& quaternion);
Eigen::Vector3d LatLongAltToEcef(double latitude, double longitude, Eigen::Vector3d LatLongAltToEcef(double latitude, double longitude,
double altitude); double altitude);
// Returns a transform that takes ECEF coordinates from nearby points to a local
// frame that has z pointing upwards.
cartographer::transform::Rigid3d ComputeLocalFrameFromLatLong(double latitude,
double longitude);
} // namespace cartographer_ros } // namespace cartographer_ros
#endif // CARTOGRAPHER_ROS_MSG_CONVERSION_H_ #endif // CARTOGRAPHER_ROS_MSG_CONVERSION_H_

View File

@ -17,6 +17,7 @@
#include "cartographer_ros/msg_conversion.h" #include "cartographer_ros/msg_conversion.h"
#include <cmath> #include <cmath>
#include <random>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "sensor_msgs/LaserScan.h" #include "sensor_msgs/LaserScan.h"
@ -109,5 +110,46 @@ TEST(MsgConversion, LatLongAltToEcef) {
<< somewhere_on_earth; << somewhere_on_earth;
} }
TEST(MsgConversion, ComputeLocalFrameFromLatLong) {
cartographer::transform::Rigid3d north_pole =
ComputeLocalFrameFromLatLong(90., 0.);
EXPECT_TRUE((north_pole * LatLongAltToEcef(90., 0., 1.))
.isApprox(Eigen::Vector3d::UnitZ()));
cartographer::transform::Rigid3d south_pole =
ComputeLocalFrameFromLatLong(-90., 0.);
EXPECT_TRUE((south_pole * LatLongAltToEcef(-90., 0., 1.))
.isApprox(Eigen::Vector3d::UnitZ()));
cartographer::transform::Rigid3d prime_meridian_equator =
ComputeLocalFrameFromLatLong(0., 0.);
EXPECT_TRUE((prime_meridian_equator * LatLongAltToEcef(0., 0., 1.))
.isApprox(Eigen::Vector3d::UnitZ()))
<< prime_meridian_equator * LatLongAltToEcef(0., 0., 1.);
cartographer::transform::Rigid3d meridian_90_equator =
ComputeLocalFrameFromLatLong(0., 90.);
EXPECT_TRUE((meridian_90_equator * LatLongAltToEcef(0., 90., 1.))
.isApprox(Eigen::Vector3d::UnitZ()))
<< meridian_90_equator * LatLongAltToEcef(0., 90., 1.);
std::mt19937 rng(42);
std::uniform_real_distribution<double> lat_distribution(-90., 90.);
std::uniform_real_distribution<double> long_distribution(-180., 180.);
std::uniform_real_distribution<double> alt_distribution(-519., 519.);
for (int i = 0; i < 1000; ++i) {
const double latitude = lat_distribution(rng);
const double longitude = long_distribution(rng);
const double altitude = alt_distribution(rng);
cartographer::transform::Rigid3d transform_to_local_frame =
ComputeLocalFrameFromLatLong(latitude, longitude);
EXPECT_TRUE((transform_to_local_frame *
LatLongAltToEcef(latitude, longitude, altitude))
.isApprox(altitude * Eigen::Vector3d::UnitZ(), 1e-6))
<< transform_to_local_frame *
LatLongAltToEcef(latitude, longitude, altitude)
<< "\n"
<< altitude;
}
}
} // namespace } // namespace
} // namespace cartographer_ros } // namespace cartographer_ros