Carma-platform v4.2.0
CARMA Platform is built on robot operating system (ROS) and utilizes open source software (OSS) that enables Cooperative Driving Automation (CDA) features to allow Automated Driving Systems to interact and cooperate with infrastructure and other vehicles through communication.
GNSSToMapConvertor.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2018-2021 LEIDOS.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
18#include <wgs84_utils/proj_tools.h>
19#include <lanelet2_core/geometry/Point.h>
20#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
21
23{
25 std::string map_frame_id, std::string base_link_frame_id,
26 std::string heading_frame_id,
27 std::shared_ptr<carma_ros2_utils::CarmaLifecycleNode> nh)
28 : pose_pub_(pose_pub)
29 , tf_lookup_(tf_lookup)
30 , map_frame_id_(map_frame_id)
31 , base_link_frame_id_(base_link_frame_id)
32 , heading_frame_id_(heading_frame_id)
33 , nh_(nh)
34{
35}
36
37void GNSSToMapConvertor::gnssFixCb(gps_msgs::msg::GPSFix::UniquePtr fix_msg)
38{
39 if (!baselink_in_sensor_) // Extract the assumed static transform between baselink and the sensor if it was not
40 // already optained
41 {
42 auto tf_msg = tf_lookup_(fix_msg->header.frame_id, base_link_frame_id_);
43 if (!tf_msg) // Failed to get transform
44 {
45 RCLCPP_WARN_STREAM_THROTTLE(nh_->get_logger(), *nh_->get_clock(), 1000,
46 "Throttled Log 1s: Ignoring fix message: Could not locate static transform between "
47 << fix_msg->header.frame_id << " and " << base_link_frame_id_);
48 return;
49 }
50
51 tf2::Transform tf;
52 tf2::convert(tf_msg.get().transform, tf);
53
55 }
56
57 if (!sensor_in_ned_heading_rotation_) // Extract the assumed static transform between heading frame and the position
58 // sensor frame if it was not already optained
59 {
60 auto tf_msg = tf_lookup_(fix_msg->header.frame_id, heading_frame_id_);
61 if (!tf_msg) // Failed to get transform
62 {
63 RCLCPP_WARN_STREAM_THROTTLE(nh_->get_logger(), *nh_->get_clock(), 1000,
64 "Throttled Log 1s: Ignoring fix message: Could not locate static transform between "
65 << heading_frame_id_ << " and "
66 << fix_msg->header.frame_id);
67 return;
68 }
69
70 tf2::Transform tf;
71 tf2::convert(tf_msg.get().transform, tf);
72
73 if (tf.getOrigin().x() != 0.0 || tf.getOrigin().y() != 0.0 || tf.getOrigin().z() != 0.0)
74 {
75 RCLCPP_WARN_STREAM(nh_->get_logger(), "Heading frame does not have rotation only transform with sensor frame. The translation will not "
76 "be handled by the GNSS convertor");
77 }
78
79 sensor_in_ned_heading_rotation_ = tf.getRotation();
80 }
81
82 if (!map_projector_ || !ned_in_map_rotation_) // Check if map projection is available
83 {
84 RCLCPP_WARN_STREAM_THROTTLE(nh_->get_logger(), *nh_->get_clock(), 1000,
85 "Throttled Log 1s: Ignoring fix message as no map projection has been received.");
86 return;
87 }
88
89 geometry_msgs::msg::PoseWithCovarianceStamped pose_msg =
91
92 geometry_msgs::msg::PoseStamped msg; // TODO until covariance is added drop it from the output
93 msg.header = pose_msg.header;
94 msg.header.frame_id = map_frame_id_;
95 msg.pose = pose_msg.pose.pose;
96
97 pose_pub_(msg);
98}
99
100void GNSSToMapConvertor::geoReferenceCallback(std_msgs::msg::String::UniquePtr geo_ref)
101{
102 if (georeference_ != geo_ref->data)
103 {
104 georeference_ = geo_ref->data;
105 map_projector_ = std::make_shared<lanelet::projection::LocalFrameProjector>(
106 geo_ref->data.c_str()); // Build projector from proj string
107
108 RCLCPP_INFO_STREAM(nh_->get_logger(), "Received map georeference: " << geo_ref->data);
109
110 std::string axis = wgs84_utils::proj_tools::getAxisFromProjString(geo_ref->data); // Extract axis for orientation calc
111
112 RCLCPP_INFO_STREAM(nh_->get_logger(), "Extracted Axis: " << axis);
113
114 ned_in_map_rotation_ = wgs84_utils::proj_tools::getRotationOfNEDFromProjAxis(axis); // Extract map rotation from axis
115
116 RCLCPP_INFO_STREAM(nh_->get_logger(), "Extracted NED in Map Rotation (x,y,z,w) : ( "
117 << ned_in_map_rotation_.get().x() << ", " << ned_in_map_rotation_.get().y() << ", "
118 << ned_in_map_rotation_.get().z() << ", " << ned_in_map_rotation_.get().w());
119 }
120}
121
122boost::optional<tf2::Quaternion> GNSSToMapConvertor::getNedInMapRotation()
123{
125}
126
127std::shared_ptr<lanelet::projection::LocalFrameProjector> GNSSToMapConvertor::getMapProjector()
128{
129 return map_projector_;
130}
131
132
133geometry_msgs::msg::PoseWithCovarianceStamped GNSSToMapConvertor::poseFromGnss(
134 const tf2::Transform& baselink_in_sensor,
135 const tf2::Quaternion& sensor_in_ned_heading_rotation,
136 const lanelet::projection::LocalFrameProjector& projector,
137 const tf2::Quaternion& ned_in_map_rotation,
138 gps_msgs::msg::GPSFix fix_msg)
139{
141 const double lat = fix_msg.latitude;
142 const double lon = fix_msg.longitude;
143 const double alt = fix_msg.altitude;
144
145 lanelet::BasicPoint3d map_point = projector.forward({ lat, lon, alt });
146 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "map_point: " << map_point.x() << ", " << map_point.y() << ", " << map_point.z());
147
148 if (fabs(map_point.x()) > 10000.0 || fabs(map_point.y()) > 10000.0)
149 { // Above 10km from map origin earth curvature will start to have a negative impact on system performance
150
151 RCLCPP_WARN_STREAM(nh_->get_logger(), "Distance from map origin is larger than supported by system assumptions. Strongly advise "
152 "alternative map origin be used. ");
153 }
154
156 // This logic assumes that the orientation difference between an NED frame located at the map origin and an NED frame
157 // located at the GNSS point are sufficiently small that they can be ignored. Therefore it is assumed the heading
158 // report of the GNSS system reguardless of its poition in the map without change in its orientation will give the
159 // same result (as far as we are concered).
160
161 tf2::Quaternion R_m_n(ned_in_map_rotation); // Rotation of NED frame in map frame
162 tf2::Quaternion R_n_h; // Rotation of sensor heading report in NED frame
163 R_n_h.setRPY(0, 0, fix_msg.track * wgs84_utils::DEG2RAD);
164
165 tf2::Quaternion R_h_s = sensor_in_ned_heading_rotation; // Rotation of heading report in sensor frame
166
167 tf2::Quaternion R_m_s =
168 R_m_n * R_n_h * R_h_s; // Rotation of sensor in map frame under assumption that distance from map origin is
169 // sufficiently small so as to ignore local changes in NED orientation
170
171 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "R_m_n (x,y,z,w) : ( "
172 << R_m_n.x() << ", " << R_m_n.y() << ", "
173 << R_m_n.z() << ", " << R_m_n.w());
174
175 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "R_n_h (x,y,z,w) : ( "
176 << R_n_h.x() << ", " << R_n_h.y() << ", "
177 << R_n_h.z() << ", " << R_n_h.w());
178
179 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "R_h_s (x,y,z,w) : ( "
180 << R_h_s.x() << ", " << R_h_s.y() << ", "
181 << R_h_s.z() << ", " << R_h_s.w());
182
183 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "R_m_s (x,y,z,w) : ( "
184 << R_m_s.x() << ", " << R_m_s.y() << ", "
185 << R_m_s.z() << ", " << R_m_s.w());
186
187 tf2::Transform T_m_s(R_m_s,
188 tf2::Vector3(map_point.x(), map_point.y(), map_point.z())); // Reported position and orientation
189 // of sensor frame in map frame
190 tf2::Transform T_s_b(baselink_in_sensor); // Transform between sensor and baselink frame
191 tf2::Transform T_m_b = T_m_s * T_s_b; // Transform between map and baselink frame
192
193 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "T_m_s (x,y,z,w) : ( "
194 << T_m_s.getRotation().x() << ", " << T_m_s.getRotation().y() << ", "
195 << T_m_s.getRotation().z() << ", " << T_m_s.getRotation().w());
196 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "T_s_b (x,y,z,w) : ( "
197 << T_s_b.getRotation().x() << ", " << T_s_b.getRotation().y() << ", "
198 << T_s_b.getRotation().z() << ", " << T_s_b.getRotation().w());
199 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "T_m_b (x,y,z,w) : ( "
200 << T_m_b.getRotation().x() << ", " << T_m_b.getRotation().y() << ", "
201 << T_m_b.getRotation().z() << ", " << T_m_b.getRotation().w());
202
203 // TODO handle covariance
204
205 // Populate message
206 geometry_msgs::msg::PoseWithCovarianceStamped pose;
207 pose.header = fix_msg.header;
208
209 pose.pose.pose.position.x = T_m_b.getOrigin().getX();
210 pose.pose.pose.position.y = T_m_b.getOrigin().getY();
211 pose.pose.pose.position.z = T_m_b.getOrigin().getZ();
212
213 pose.pose.pose.orientation.x = T_m_b.getRotation().getX();
214 pose.pose.pose.orientation.y = T_m_b.getRotation().getY();
215 pose.pose.pose.orientation.z = T_m_b.getRotation().getZ();
216 pose.pose.pose.orientation.w = T_m_b.getRotation().getW();
217
218 return pose;
219}
220} // namespace gnss_to_map_convertor
std::shared_ptr< lanelet::projection::LocalFrameProjector > map_projector_
std::function< void(geometry_msgs::msg::PoseStamped)> PosePubCallback
void geoReferenceCallback(std_msgs::msg::String::UniquePtr geo_ref)
Map georeference callback The geodetic description of the map frame should be provided to this callba...
std::function< boost::optional< geometry_msgs::msg::TransformStamped >(const std::string &, const std::string &)> TransformLookupCallback
boost::optional< tf2::Quaternion > sensor_in_ned_heading_rotation_
void gnssFixCb(gps_msgs::msg::GPSFix::UniquePtr fix_msg)
GNSS Fix callback which will publish a pose representing that fix in the map frame if the required tr...
std::shared_ptr< lanelet::projection::LocalFrameProjector > getMapProjector()
Get the projector built from the provided georeference via the callback.
boost::optional< tf2::Transform > baselink_in_sensor_
boost::optional< tf2::Quaternion > ned_in_map_rotation_
geometry_msgs::msg::PoseWithCovarianceStamped poseFromGnss(const tf2::Transform &baselink_in_sensor, const tf2::Quaternion &sensor_in_ned_heading_rotation, const lanelet::projection::LocalFrameProjector &projector, const tf2::Quaternion &ned_in_map_rotation, gps_msgs::msg::GPSFix fix_msg)
Converts a provided GNSS fix message into a pose message for the map frame describibed by the provide...
GNSSToMapConvertor(PosePubCallback pose_pub, TransformLookupCallback tf_lookup, std::string map_frame_id, std::string base_link_frame_id, std::string heading_frame_id, std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode > nh)
Constructor.
boost::optional< tf2::Quaternion > getNedInMapRotation()
Get the rotation computed from the recieved georeference or boost::none if unset.
std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode > nh_
void convert(const carma_v2x_msgs::msg::PSM &in_msg, carma_perception_msgs::msg::ExternalObject &out_msg, const std::string &map_frame_id, double pred_period, double pred_step_size, const lanelet::projection::LocalFrameProjector &map_projector, const tf2::Quaternion &ned_in_map_rotation, rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock)