15#include <lanelet2_extension/regulatory_elements/CarmaTrafficSignal.h>
16#include <tf2/LinearMath/Transform.h>
17#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
18#include <wgs84_utils/wgs84_utils.h>
24#include <boost/date_time/posix_time/posix_time.hpp>
27#include <rclcpp/logger.hpp>
28#include <rclcpp/logging.hpp>
29#include <rclcpp/rclcpp.hpp>
31#include <carma_perception_msgs/msg/external_object.hpp>
32#include <carma_v2x_msgs/msg/psm.hpp>
39 const carma_v2x_msgs::msg::PSM & in_msg, carma_perception_msgs::msg::ExternalObject & out_msg,
40 const std::string & map_frame_id,
double pred_period,
double pred_step_size,
42 const lanelet::projection::LocalFrameProjector & map_projector,
43 const tf2::Quaternion & ned_in_map_rotation,
44 rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock)
47 out_msg.dynamic_obj =
true;
49 out_msg.presence_vector |= carma_perception_msgs::msg::ExternalObject::DYNAMIC_OBJ_PRESENCE;
54 for (
int i = in_msg.id.id.size() - 1;
i >= 0;
59 out_msg.id |= in_msg.id.id[
i] << (8 *
i);
61 out_msg.presence_vector |= carma_perception_msgs::msg::ExternalObject::ID_PRESENCE_VECTOR;
65 out_msg.bsm_id = in_msg.id.id;
66 out_msg.presence_vector |= carma_perception_msgs::msg::ExternalObject::BSM_ID_PRESENCE_VECTOR;
72 out_msg.header.stamp = builtin_interfaces::msg::Time(psm_timestamp);
73 out_msg.header.frame_id = map_frame_id;
79 in_msg.basic_type.type == j2735_v2x_msgs::msg::PersonalDeviceUserType::A_PEDESTRIAN ||
80 in_msg.basic_type.type == j2735_v2x_msgs::msg::PersonalDeviceUserType::A_PUBLIC_SAFETY_WORKER ||
81 in_msg.basic_type.type ==
82 j2735_v2x_msgs::msg::PersonalDeviceUserType::AN_ANIMAL)
88 out_msg.object_type = carma_perception_msgs::msg::ExternalObject::PEDESTRIAN;
99 in_msg.basic_type.type == j2735_v2x_msgs::msg::PersonalDeviceUserType::A_PEDALCYCLIST) {
100 out_msg.object_type = carma_perception_msgs::msg::ExternalObject::
105 out_msg.size.x = 1.0;
106 out_msg.size.y = 0.5;
107 out_msg.size.z = 1.0;
109 out_msg.object_type = carma_perception_msgs::msg::ExternalObject::UNKNOWN;
112 out_msg.size.x = 0.5;
113 out_msg.size.y = 0.5;
114 out_msg.size.z = 1.0;
116 out_msg.presence_vector |= carma_perception_msgs::msg::ExternalObject::SIZE_PRESENCE_VECTOR;
121 out_msg.velocity.twist.linear.x = in_msg.speed.velocity;
122 out_msg.presence_vector |= carma_perception_msgs::msg::ExternalObject::VELOCITY_PRESENCE_VECTOR;
132 double largest_position_std = 0.0;
133 double lat_variance = 0.0;
134 double lon_variance = 0.0;
135 double heading_variance = 0.0;
137 double position_confidence = 0.0;
142 constexpr double MAX_POSITION_STD = 1.85;
145 (in_msg.accuracy.presence_vector |
146 carma_v2x_msgs::msg::PositionalAccuracy::ACCURACY_AVAILABLE) &&
147 (in_msg.accuracy.presence_vector |
148 carma_v2x_msgs::msg::PositionalAccuracy::ACCURACY_ORIENTATION_AVAILABLE)) {
154 largest_position_std = std::max(in_msg.accuracy.semi_major, in_msg.accuracy.semi_minor);
155 lat_variance = in_msg.accuracy.semi_minor * in_msg.accuracy.semi_minor;
156 lon_variance = in_msg.accuracy.semi_major * in_msg.accuracy.semi_major;
157 heading_variance = in_msg.accuracy.orientation * in_msg.accuracy.orientation;
168 out_msg.confidence = 1.0 - std::min(1.0, fabs(largest_position_std / MAX_POSITION_STD));
169 out_msg.presence_vector |=
170 carma_perception_msgs::msg::ExternalObject::CONFIDENCE_PRESENCE_VECTOR;
172 in_msg.accuracy.presence_vector | carma_v2x_msgs::msg::PositionalAccuracy::ACCURACY_AVAILABLE) {
175 largest_position_std = std::max(in_msg.accuracy.semi_major, in_msg.accuracy.semi_minor);
176 lat_variance = in_msg.accuracy.semi_minor * in_msg.accuracy.semi_minor;
177 lon_variance = in_msg.accuracy.semi_major * in_msg.accuracy.semi_major;
178 heading_variance = 1.0;
182 out_msg.confidence = 1.0 - std::min(1.0, fabs(largest_position_std / MAX_POSITION_STD));
183 out_msg.presence_vector |=
184 carma_perception_msgs::msg::ExternalObject::CONFIDENCE_PRESENCE_VECTOR;
187 in_msg.accuracy.presence_vector |
188 carma_v2x_msgs::msg::PositionalAccuracy::ACCURACY_ORIENTATION_AVAILABLE) {
194 heading_variance = in_msg.accuracy.orientation * in_msg.accuracy.orientation;
198 heading_variance = 1.0;
203 lanelet::GPSPoint gps_point(
204 {in_msg.position.latitude, in_msg.position.longitude, in_msg.position.elevation});
206 map_projector, ned_in_map_rotation, gps_point, in_msg.heading.heading, lat_variance,
207 lon_variance, heading_variance);
208 out_msg.presence_vector |= carma_perception_msgs::msg::ExternalObject::POSE_PRESENCE_VECTOR;
215 std::vector<geometry_msgs::msg::Pose> predicted_poses;
217 if (in_msg.presence_vector & carma_v2x_msgs::msg::PSM::HAS_PATH_PREDICTION) {
221 out_msg.pose.pose, out_msg.velocity.twist.linear.x,
222 -in_msg.path_prediction.radius_of_curvature, pred_period, pred_step_size);
225 out_msg.pose.pose, out_msg.velocity.twist.linear.x, pred_period, pred_step_size);
229 predicted_poses, out_msg.velocity.twist.linear.x, rclcpp::Time(out_msg.header.stamp),
230 rclcpp::Duration(pred_step_size * 1e9), map_frame_id, out_msg.confidence, out_msg.confidence);
231 out_msg.presence_vector |= carma_perception_msgs::msg::ExternalObject::PREDICTION_PRESENCE_VECTOR;
237 const geometry_msgs::msg::Pose & pose,
double velocity,
double radius_of_curvature,
double period,
240 std::vector<geometry_msgs::msg::Pose> output;
241 output.reserve((period / step_size) + 1);
243 tf2::Vector3 pose_in_map_translation(pose.position.x, pose.position.y, pose.position.z);
244 tf2::Quaternion pose_in_map_quat(
245 pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w);
246 tf2::Transform pose_in_map(pose_in_map_quat, pose_in_map_translation);
250 double center_x_in_pose = 0;
251 double center_y_in_pose = radius_of_curvature;
255 while (total_dt < period) {
257 total_dt += step_size;
258 double delta_arc_length = velocity * total_dt;
260 double turning_angle = delta_arc_length / radius_of_curvature;
261 double dx_from_center = radius_of_curvature * sin(turning_angle);
262 double dy_from_center = radius_of_curvature * cos(turning_angle);
264 double x = center_x_in_pose + dx_from_center;
265 double y = center_y_in_pose + dy_from_center;
267 tf2::Vector3 position(
x,
y, 0);
269 tf2::Quaternion quat;
270 quat.setRPY(0, 0, turning_angle);
273 tf2::Transform pose_to_sample(quat, position);
274 tf2::Transform map_to_sample = pose_in_map * pose_to_sample;
276 geometry_msgs::msg::Pose sample_pose;
278 sample_pose.position.x = map_to_sample.getOrigin().x();
279 sample_pose.position.y = map_to_sample.getOrigin().y();
280 sample_pose.position.z = pose.orientation.z;
282 sample_pose.orientation.x = map_to_sample.getRotation().x();
283 sample_pose.orientation.y = map_to_sample.getRotation().y();
284 sample_pose.orientation.z = map_to_sample.getRotation().z();
285 sample_pose.orientation.w = map_to_sample.getRotation().w();
287 output.emplace_back(sample_pose);
294 const geometry_msgs::msg::Pose & pose,
double velocity,
double period,
double step_size)
296 std::vector<geometry_msgs::msg::Pose> output;
297 output.reserve((period / step_size) + 1);
299 tf2::Vector3 pose_in_map_translation(pose.position.x, pose.position.y, pose.position.z);
300 tf2::Quaternion pose_in_map_quat(
301 pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w);
303 tf2::Transform pose_in_map(pose_in_map_quat, pose_in_map_translation);
307 while (total_dt < period) {
309 total_dt += step_size;
310 double dx_from_start = velocity * total_dt;
312 double x = pose.position.x + dx_from_start;
314 tf2::Vector3 position(
x, 0, 0);
317 tf2::Transform pose_to_sample(tf2::Quaternion::getIdentity(), position);
318 tf2::Transform map_to_sample = pose_in_map * pose_to_sample;
320 geometry_msgs::msg::Pose sample_pose;
322 sample_pose.position.x = map_to_sample.getOrigin().x();
323 sample_pose.position.y = map_to_sample.getOrigin().y();
324 sample_pose.position.z = map_to_sample.getOrigin().z();
326 sample_pose.orientation.x = map_to_sample.getRotation().x();
327 sample_pose.orientation.y = map_to_sample.getRotation().y();
328 sample_pose.orientation.z = map_to_sample.getRotation().z();
329 sample_pose.orientation.w = map_to_sample.getRotation().w();
331 output.emplace_back(sample_pose);
340 const lanelet::projection::LocalFrameProjector & projector,
341 const tf2::Quaternion & ned_in_map_rotation,
const lanelet::GPSPoint & gps_point,
342 const double & heading,
const double lat_variance,
const double lon_variance,
343 const double heading_variance)
347 lanelet::BasicPoint3d map_point = projector.forward(gps_point);
350 rclcpp::get_logger(
"motion_computation::conversion"),
351 "map_point: " << map_point.x() <<
", " << map_point.y() <<
", " << map_point.z());
354 fabs(map_point.x()) > 10000.0 ||
355 fabs(map_point.y()) > 10000.0) {
359 rclcpp::get_logger(
"motion_computation::conversion"),
360 "Distance from map origin is larger than supported by system "
361 "assumptions. Strongly advise "
362 "alternative map origin be used. ");
373 tf2::Quaternion R_m_n(ned_in_map_rotation);
375 tf2::Quaternion R_n_h;
376 R_n_h.setRPY(0, 0, heading * wgs84_utils::DEG2RAD);
378 tf2::Quaternion R_m_s = R_m_n * R_n_h;
383 rclcpp::get_logger(
"motion_computation::conversion"),
384 "R_m_n (x,y,z,w) : ( " << R_m_n.x() <<
", " << R_m_n.y() <<
", " << R_m_n.z() <<
", "
388 rclcpp::get_logger(
"motion_computation::conversion"),
389 "R_n_h (x,y,z,w) : ( " << R_n_h.x() <<
", " << R_n_h.y() <<
", " << R_n_h.z() <<
", "
393 rclcpp::get_logger(
"motion_computation::conversion"),
394 "R_m_s (x,y,z,w) : ( " << R_m_s.x() <<
", " << R_m_s.y() <<
", " << R_m_s.z() <<
", "
397 tf2::Transform T_m_s(
399 map_point.x(), map_point.y(),
403 tf2::Transform T_m_n_no_heading(
404 R_m_n, tf2::Vector3(0, 0, 0));
410 std::array<double, 36> input_covariance = {
411 lat_variance, 0, 0, 0, 0, 0,
412 0, lon_variance, 0, 0, 0, 0,
416 0, 0, 0, 0, 0, heading_variance
428 geometry_msgs::msg::PoseWithCovariance pose;
429 pose.pose.position.x = T_m_s.getOrigin().getX();
430 pose.pose.position.y = T_m_s.getOrigin().getY();
431 pose.pose.position.z = T_m_s.getOrigin().getZ();
433 pose.pose.orientation.x = T_m_s.getRotation().getX();
434 pose.pose.orientation.y = T_m_s.getRotation().getY();
435 pose.pose.orientation.z = T_m_s.getRotation().getZ();
436 pose.pose.orientation.w = T_m_s.getRotation().getW();
438 pose.covariance = new_cov;
444 const std::vector<geometry_msgs::msg::Pose> & poses,
double constant_velocity,
445 const rclcpp::Time & start_time,
const rclcpp::Duration & step_size,
const std::string & frame,
446 double initial_pose_confidence,
double initial_vel_confidence)
448 std::vector<carma_perception_msgs::msg::PredictedState> output;
449 output.reserve(poses.size());
451 rclcpp::Time time(start_time);
453 for (
auto p : poses) {
455 carma_perception_msgs::msg::PredictedState pred_state;
456 pred_state.header.stamp = time;
457 pred_state.header.frame_id = frame;
459 pred_state.predicted_position = p;
460 pred_state.predicted_position_confidence =
461 0.9 * initial_pose_confidence;
462 initial_pose_confidence = pred_state.predicted_position_confidence;
464 pred_state.predicted_velocity.linear.x = constant_velocity;
465 pred_state.predicted_velocity_confidence =
466 0.9 * initial_vel_confidence;
468 initial_vel_confidence = pred_state.predicted_velocity_confidence;
470 output.push_back(pred_state);
477 const carma_v2x_msgs::msg::PSM & in_msg, rclcpp::Clock::SharedPtr clock)
479 boost::posix_time::ptime utc_time_of_current_psm;
482 static const boost::posix_time::ptime inception_boost(
483 boost::posix_time::time_from_string(
"1970-01-01 00:00:00.000"));
493 const auto initial_position_time{in_msg.path_history.initial_position.utc_time};
494 if ((in_msg.presence_vector &
495 carma_v2x_msgs::msg::PSM::HAS_PATH_HISTORY) &&
496 (in_msg.path_history.presence_vector &
497 carma_v2x_msgs::msg::PathHistory::HAS_INITIAL_POSITION) &&
498 (initial_position_time.presence_vector & j2735_v2x_msgs::msg::DDateTime::YEAR) &&
499 (initial_position_time.presence_vector & j2735_v2x_msgs::msg::DDateTime::MONTH) &&
500 (initial_position_time.presence_vector & j2735_v2x_msgs::msg::DDateTime::DAY) &&
501 (initial_position_time.presence_vector & j2735_v2x_msgs::msg::DDateTime::HOUR) &&
502 (initial_position_time.presence_vector & j2735_v2x_msgs::msg::DDateTime::MINUTE) &&
503 (initial_position_time.presence_vector & j2735_v2x_msgs::msg::DDateTime::SECOND) &&
504 (in_msg.sec_mark.millisecond == initial_position_time.second.millisecond)) {
507 rclcpp::get_logger(
"motion_computation::conversion"),
508 "Using UTC time of path history to determine PSM "
509 "timestamp. Assumed valid since UTC is fully specified "
510 "and sec_mark == utc_time.seconds in this message.");
512 boost::posix_time::time_duration time_of_day =
513 boost::posix_time::hours(in_msg.path_history.initial_position.utc_time.hour.hour) +
514 boost::posix_time::minutes(in_msg.path_history.initial_position.utc_time.minute.minute) +
515 boost::posix_time::milliseconds(
516 in_msg.path_history.initial_position.utc_time.second.millisecond);
518 boost::gregorian::date utc_day(
519 in_msg.path_history.initial_position.utc_time.year.year,
520 in_msg.path_history.initial_position.utc_time.month.month,
521 in_msg.path_history.initial_position.utc_time.day.day);
523 utc_time_of_current_psm = boost::posix_time::ptime(utc_day) + time_of_day;
527 RCLCPP_WARN_STREAM_THROTTLE(
528 rclcpp::get_logger(
"motion_computation::conversion"), *clock.get(),
529 rclcpp::Duration(5, 0).nanoseconds(),
530 "PSM PathHistory utc timstamp does not match "
531 "sec_mark. Unable to determine the minute of "
532 "the year used for PSM data. Assuming local "
533 "clock is exactly synced. This is NOT "
537 auto current_time = clock->now();
540 boost::posix_time::time_duration duration_since_inception(
541 lanelet::time::durationFromSec(current_time.seconds()));
544 auto curr_time_boost = inception_boost + duration_since_inception;
547 auto duration_in_day_till_current_time = curr_time_boost.time_of_day();
550 auto hour_count_in_day = duration_in_day_till_current_time.hours();
551 auto minute_count_in_hour = duration_in_day_till_current_time.minutes();
554 auto start_of_minute_in_day = boost::posix_time::hours(hour_count_in_day) +
555 boost::posix_time::minutes(minute_count_in_hour);
558 boost::posix_time::ptime start_of_day(curr_time_boost.date());
561 boost::posix_time::ptime utc_start_of_current_minute = start_of_day + start_of_minute_in_day;
565 boost::posix_time::time_duration s_in_cur_minute =
566 boost::posix_time::milliseconds(in_msg.sec_mark.millisecond);
568 utc_time_of_current_psm = utc_start_of_current_minute + s_in_cur_minute;
571 boost::posix_time::time_duration nsec_since_epoch = utc_time_of_current_psm - inception_boost;
573 if (nsec_since_epoch.is_special()) {
575 rclcpp::get_logger(
"motion_computation::conversion"),
576 "Computed psm nsec_since_epoch is special (computation "
577 "failed). Value effectively undefined.");
580 return rclcpp::Time(nsec_since_epoch.total_nanoseconds());
geometry_msgs::msg::PoseWithCovariance::_covariance_type transformCovariance(const geometry_msgs::msg::PoseWithCovariance::_covariance_type &cov_in, const tf2::Transform &transform)
Transform the covariance matrix of a PoseWithCovariance message to a new frame.
std::vector< geometry_msgs::msg::Pose > sample_2d_path_from_radius(const geometry_msgs::msg::Pose &pose, double velocity, double radius_of_curvature, double period, double step_size)
geometry_msgs::msg::PoseWithCovariance pose_from_gnss(const lanelet::projection::LocalFrameProjector &projector, const tf2::Quaternion &ned_in_map_rotation, const lanelet::GPSPoint &gps_point, const double &heading, const double lat_variance, const double lon_variance, const double heading_variance)
std::vector< carma_perception_msgs::msg::PredictedState > predicted_poses_to_predicted_state(const std::vector< geometry_msgs::msg::Pose > &poses, double constant_velocity, const rclcpp::Time &start_time, const rclcpp::Duration &step_size, const std::string &frame, double initial_pose_confidence, double initial_vel_confidence)
rclcpp::Time get_psm_timestamp(const carma_v2x_msgs::msg::PSM &in_msg, rclcpp::Clock::SharedPtr clock)
std::vector< geometry_msgs::msg::Pose > sample_2d_linear_motion(const geometry_msgs::msg::Pose &pose, double velocity, double period, double step_size)
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)