Skip to content

Commit 3e81caa

Browse files
committed
[Feature] Integrated support for gps to amrl map integration
1 parent 811ada3 commit 3e81caa

File tree

1 file changed

+183
-84
lines changed

1 file changed

+183
-84
lines changed

math/gps_util.h

+183-84
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,202 @@
11
#ifndef GPS_UTIL_H_
22
#define GPS_UTIL_H_
33

4-
#include "eigen3/Eigen/Dense"
54
#include <math.h>
5+
6+
#include <cmath>
7+
#include <string>
68
#include <tuple>
79

10+
#include "eigen3/Eigen/Dense"
11+
#include "shared/math/math_util.h"
12+
#include "shared/util/helpers.h"
13+
14+
using Eigen::Rotation2Dd;
15+
using Eigen::Vector2d;
16+
using math_util::DegToRad;
17+
using std::string;
18+
819
namespace gps_util {
920

10-
inline std::tuple<double, double, int> gpsToUTM(double lat, double lon) {
11-
// Constants for UTM conversion
12-
constexpr double a = 6378137.0; // WGS-84 major axis
13-
constexpr double f = 1 / 298.257223563; // WGS-84 flattening
14-
constexpr double k0 = 0.9996; // UTM scale factor
15-
constexpr double e = sqrt(f * (2 - f)); // Eccentricity
16-
constexpr double e2 = e * e;
17-
18-
// UTM Zone
19-
int zone = static_cast<int>((lon + 180) / 6) + 1;
20-
21-
// Convert latitude and longitude to radians
22-
double lat_rad = lat * M_PI / 180.0;
23-
double lon_rad = lon * M_PI / 180.0;
24-
25-
// Central meridian of the UTM zone
26-
double lon_origin = (zone - 1) * 6 - 180 + 3;
27-
double lon_origin_rad = lon_origin * M_PI / 180.0;
28-
29-
// Calculations for UTM coordinates
30-
double N = a / sqrt(1 - e2 * sin(lat_rad) * sin(lat_rad));
31-
double T = tan(lat_rad) * tan(lat_rad);
32-
double C = e2 / (1 - e2) * cos(lat_rad) * cos(lat_rad);
33-
double A = cos(lat_rad) * (lon_rad - lon_origin_rad);
34-
35-
double M = a * ((1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256) * lat_rad
36-
- (3 * e2 / 8 + 3 * e2 * e2 / 32 + 45 * e2 * e2 * e2 / 1024) * sin(2 * lat_rad)
37-
+ (15 * e2 * e2 / 256 + 45 * e2 * e2 * e2 / 1024) * sin(4 * lat_rad)
38-
- (35 * e2 * e2 * e2 / 3072) * sin(6 * lat_rad));
39-
40-
double easting = k0 * N * (A + (1 - T + C) * A * A * A / 6
41-
+ (5 - 18 * T + T * T + 72 * C - 58 * e2) * A * A * A * A * A / 120) + 500000.0;
42-
43-
double northing = k0 * (M + N * tan(lat_rad) * (A * A / 2
44-
+ (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24
45-
+ (61 - 58 * T + T * T + 600 * C - 330 * e2) * A * A * A * A * A * A / 720));
46-
47-
// Correct for southern hemisphere
48-
if (lat < 0) {
49-
northing += 10000000.0;
21+
struct GPSTranslator {
22+
string GetMapFileFromName(const string& maps_dir, const string& map) {
23+
return maps_dir + "/" + map + "/" + map + ".gpsmap.txt";
24+
}
25+
26+
bool Load(const string& maps_dir, const string& map) {
27+
const string file = GetMapFileFromName(maps_dir, map);
28+
ScopedFile fid(file, "r", true);
29+
if (fid() == nullptr) return false;
30+
if (fscanf(fid(), "%lf, %lf, %lf", &gps_origin_latitude,
31+
&gps_origin_longitude, &map_orientation) != 3) {
32+
return false;
5033
}
34+
printf("Map origin: %12.8lf, %12.8lf\n", gps_origin_latitude,
35+
gps_origin_longitude);
36+
return true;
37+
}
38+
39+
Vector2d GPSToMetric(const double latitude, const double longitude) {
40+
const double theta = DegToRad(latitude);
41+
const double c = std::cos(theta);
42+
const double s = std::sin(theta);
43+
const double r =
44+
sqrt(Sq(wgs_84_a * wgs_84_b) / (Sq(c * wgs_84_b) + Sq(s * wgs_84_a)));
45+
const double dlat = DegToRad(latitude - gps_origin_latitude);
46+
const double dlong = DegToRad(longitude - gps_origin_longitude);
47+
const double r1 = r * c;
48+
const double x = r1 * dlong;
49+
const double y = r * dlat;
50+
return Rotation2Dd(map_orientation) * Vector2d(x, y);
51+
}
52+
53+
void MetricToGPS(const Vector2d& loc, double* longitude, double* latitude) {
54+
const double theta = DegToRad(gps_origin_latitude);
55+
const double c = std::cos(theta);
56+
const double s = std::sin(theta);
57+
const double r =
58+
sqrt(Sq(wgs_84_a * wgs_84_b) / (Sq(c * wgs_84_b) + Sq(s * wgs_84_a)));
59+
const double r1 = r * c;
60+
const double dlat = loc.y() / r;
61+
const double dlong = loc.x() / r1;
62+
*longitude = gps_origin_longitude + dlong;
63+
*latitude = gps_origin_latitude + dlat;
64+
}
65+
66+
double gps_origin_longitude;
67+
double gps_origin_latitude;
68+
double map_orientation;
69+
70+
// Earth geoid parameters from WGS 84 system
71+
// https://en.wikipedia.org/wiki/World_Geodetic_System#A_new_World_Geodetic_System:_WGS_84
72+
// a = Semimajor (Equatorial) axis
73+
static constexpr double wgs_84_a = 6378137.0;
74+
// b = Semiminor (Polar) axis
75+
static constexpr double wgs_84_b = 6356752.314245;
76+
};
5177

52-
return std::make_tuple(easting, northing, zone);
78+
inline std::tuple<double, double, int> gpsToUTM(double lat, double lon) {
79+
// Constants for UTM conversion
80+
constexpr double a = 6378137.0; // WGS-84 major axis
81+
constexpr double f = 1 / 298.257223563; // WGS-84 flattening
82+
constexpr double k0 = 0.9996; // UTM scale factor
83+
constexpr double e = sqrt(f * (2 - f)); // Eccentricity
84+
constexpr double e2 = e * e;
85+
86+
// UTM Zone
87+
int zone = static_cast<int>((lon + 180) / 6) + 1;
88+
89+
// Convert latitude and longitude to radians
90+
double lat_rad = lat * M_PI / 180.0;
91+
double lon_rad = lon * M_PI / 180.0;
92+
93+
// Central meridian of the UTM zone
94+
double lon_origin = (zone - 1) * 6 - 180 + 3;
95+
double lon_origin_rad = lon_origin * M_PI / 180.0;
96+
97+
// Calculations for UTM coordinates
98+
double N = a / sqrt(1 - e2 * sin(lat_rad) * sin(lat_rad));
99+
double T = tan(lat_rad) * tan(lat_rad);
100+
double C = e2 / (1 - e2) * cos(lat_rad) * cos(lat_rad);
101+
double A = cos(lat_rad) * (lon_rad - lon_origin_rad);
102+
103+
double M =
104+
a * ((1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256) * lat_rad -
105+
(3 * e2 / 8 + 3 * e2 * e2 / 32 + 45 * e2 * e2 * e2 / 1024) *
106+
sin(2 * lat_rad) +
107+
(15 * e2 * e2 / 256 + 45 * e2 * e2 * e2 / 1024) * sin(4 * lat_rad) -
108+
(35 * e2 * e2 * e2 / 3072) * sin(6 * lat_rad));
109+
110+
double easting =
111+
k0 * N *
112+
(A + (1 - T + C) * A * A * A / 6 +
113+
(5 - 18 * T + T * T + 72 * C - 58 * e2) * A * A * A * A * A / 120) +
114+
500000.0;
115+
116+
double northing =
117+
k0 *
118+
(M + N * tan(lat_rad) *
119+
(A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 +
120+
(61 - 58 * T + T * T + 600 * C - 330 * e2) * A * A * A * A * A *
121+
A / 720));
122+
123+
// Correct for southern hemisphere
124+
if (lat < 0) {
125+
northing += 10000000.0;
126+
}
127+
128+
return std::make_tuple(easting, northing, zone);
53129
}
54130

55-
inline std::tuple<double, double> gpsToGlobalCoord(double lat0, double lon0, double lat1, double lon1) {
56-
// Convert latitude and longitude to utm coordinates
57-
const auto& [e0, n0, zone0] = gpsToUTM(lat0, lon0);
58-
const auto& [e1, n1, zone1] = gpsToUTM(lat1, lon1);
59-
return {e1 - e0, n1 - n0};
131+
inline double gpsDistance(double lat0, double lon0, double lat1, double lon1) {
132+
// Convert latitude and longitude to radians
133+
double lat0_rad = lat0 * M_PI / 180.0;
134+
double lon0_rad = lon0 * M_PI / 180.0;
135+
double lat1_rad = lat1 * M_PI / 180.0;
136+
double lon1_rad = lon1 * M_PI / 180.0;
137+
138+
// Haversine formula for distance between two GPS coordinates
139+
double dlat = lat1_rad - lat0_rad;
140+
double dlon = lon1_rad - lon0_rad;
141+
double a = pow(sin(dlat / 2), 2) +
142+
cos(lat0_rad) * cos(lat1_rad) * pow(sin(dlon / 2), 2);
143+
double c = 2 * atan2(sqrt(a), sqrt(1 - a));
144+
return 6371000 * c; // Radius of Earth in meters
60145
}
61146

62-
inline Eigen::Affine2f gpsToLocal(
63-
double current_lat, double current_lon, double current_heading,
64-
double goal_lat, double goal_lon, double goal_heading) {
65-
66-
// Step 1: Convert current and goal GPS coordinates to UTM
67-
const auto& [curr_easting, curr_northing, curr_zone] = gpsToUTM(current_lat, current_lon);
68-
const auto& [goal_easting, goal_northing, goal_zone] = gpsToUTM(goal_lat, goal_lon);
69-
// Ensure that both coordinates are in the same UTM zone
70-
if (goal_zone != curr_zone) {
71-
throw std::runtime_error("GPS points are in different UTM zones.");
72-
}
73-
74-
// Step 2: Calculate the translation vector from the current position to the goal in UTM
75-
double dx = goal_easting - curr_easting;
76-
double dy = goal_northing - curr_northing;
77-
78-
// Step 3: Convert the current heading to radians and adjust for x-axis reference
79-
// Since 0 degrees points along the y-axis and rotates counter-clockwise,
80-
// convert to radians with 0 degrees aligned along the x-axis
81-
double current_heading_rad = (90.0 - current_heading) * M_PI / 180.0;
82-
83-
// Step 4: Rotate the translation vector to the robot's local frame
84-
double local_x = dx * cos(-current_heading_rad) - dy * sin(-current_heading_rad);
85-
double local_y = dx * sin(-current_heading_rad) + dy * cos(-current_heading_rad);
86-
87-
// Step 5: Convert the goal heading to the local frame
88-
double goal_heading_rad = (90.0 - goal_heading) * M_PI / 180.0;
89-
double local_heading = goal_heading_rad - current_heading_rad;
90-
91-
// Normalize local heading to the range [-pi, pi]
92-
while (local_heading > M_PI) local_heading -= 2 * M_PI;
93-
while (local_heading < -M_PI) local_heading += 2 * M_PI;
94-
95-
// Step 6: Create the affine transformation for the local pose of the goal
96-
Eigen::Affine2f transform = Eigen::Translation2f(local_x, local_y) * Eigen::Rotation2Df(local_heading);
97-
98-
return transform;
147+
inline std::tuple<double, double> gpsToGlobalCoord(double lat0, double lon0,
148+
double lat1, double lon1) {
149+
// Convert latitude and longitude to utm coordinates
150+
const auto& [e0, n0, zone0] = gpsToUTM(lat0, lon0);
151+
const auto& [e1, n1, zone1] = gpsToUTM(lat1, lon1);
152+
return {e1 - e0, n1 - n0};
99153
}
100154

155+
inline Eigen::Affine2f gpsToLocal(double current_lat, double current_lon,
156+
double current_heading, double goal_lat,
157+
double goal_lon, double goal_heading) {
158+
// Step 1: Convert current and goal GPS coordinates to UTM
159+
const auto& [curr_easting, curr_northing, curr_zone] =
160+
gpsToUTM(current_lat, current_lon);
161+
const auto& [goal_easting, goal_northing, goal_zone] =
162+
gpsToUTM(goal_lat, goal_lon);
163+
// Ensure that both coordinates are in the same UTM zone
164+
if (goal_zone != curr_zone) {
165+
throw std::runtime_error("GPS points are in different UTM zones.");
166+
}
167+
168+
// Step 2: Calculate the translation vector from the current position to the
169+
// goal in UTM
170+
double dx = goal_easting - curr_easting;
171+
double dy = goal_northing - curr_northing;
172+
173+
// Step 3: Convert the current heading to radians and adjust for x-axis
174+
// reference Since 0 degrees points along the y-axis and rotates
175+
// counter-clockwise, convert to radians with 0 degrees aligned along the
176+
// x-axis
177+
double current_heading_rad = (90.0 - current_heading) * M_PI / 180.0;
178+
179+
// Step 4: Rotate the translation vector to the robot's local frame
180+
double local_x =
181+
dx * cos(-current_heading_rad) - dy * sin(-current_heading_rad);
182+
double local_y =
183+
dx * sin(-current_heading_rad) + dy * cos(-current_heading_rad);
184+
185+
// Step 5: Convert the goal heading to the local frame
186+
double goal_heading_rad = (90.0 - goal_heading) * M_PI / 180.0;
187+
double local_heading = goal_heading_rad - current_heading_rad;
188+
189+
// Normalize local heading to the range [-pi, pi]
190+
while (local_heading > M_PI) local_heading -= 2 * M_PI;
191+
while (local_heading < -M_PI) local_heading += 2 * M_PI;
192+
193+
// Step 6: Create the affine transformation for the local pose of the goal
194+
Eigen::Affine2f transform = Eigen::Translation2f(local_x, local_y) *
195+
Eigen::Rotation2Df(local_heading);
196+
197+
return transform;
101198
}
102199

200+
} // namespace gps_util
201+
103202
#endif

0 commit comments

Comments
 (0)