diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1ee68680ede..b582398f27d 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -28,7 +28,7 @@ jobs:
# Pull docker image from docker hub
# XTERM used for better catkin_make output
docker:
- - image: usdotfhwastol/carma-base:carma-system-4.4.0
+ - image: usdotfhwastol/carma-base:carma-system-4.5.0
user: carma
environment:
TERM: xterm
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 8bcf250837f..0e34d0352dc 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -1,13 +1,17 @@
-name: Docker
+name: Docker build
on:
push:
branches-ignore:
- "carma-develop"
+ - "master"
+ - "release/*"
+ pull_request:
+ types: [opened, synchronize, reopened]
jobs:
docker:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-latest-8-cores
steps:
- name: Checkout
uses: actions/checkout@v3
@@ -16,4 +20,4 @@ jobs:
- name: Build
uses: docker/build-push-action@v3
with:
- context: .
+ context: .
\ No newline at end of file
diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml
index 8767c90c969..c024dbf20cb 100644
--- a/.github/workflows/dockerhub.yml
+++ b/.github/workflows/dockerhub.yml
@@ -1,26 +1,16 @@
-name: DockerHub
+name: Docker Hub build
on:
push:
branches:
- "carma-develop"
-
+ - "master"
+ - "release/*"
+ tags:
+ - "carma-system-*"
jobs:
- docker:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v3
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
- - name: Login to DockerHub
- uses: docker/login-action@v2
- with:
- username: ${{ secrets.DOCKERHUB_USERNAME }}
- password: ${{ secrets.DOCKERHUB_TOKEN }}
- - name: Build
- uses: docker/build-push-action@v3
- with:
- context: .
- push: true
- tags: usdotfhwastoldev/${{ github.event.repository.name }}:develop
+ dockerhub:
+ uses: usdot-fhwa-stol/actions/.github/workflows/dockerhub.yml@main
+ secrets:
+ DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
+ DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
diff --git a/Dockerfile b/Dockerfile
index 9ea6ed8fb55..90adbe65625 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM usdotfhwastol/carma-base:carma-system-4.4.0 AS base_image
+FROM usdotfhwastol/carma-base:carma-system-4.5.0 AS base_image
FROM base_image AS build
diff --git a/common/autoware_build_flags/CMakeLists.txt b/common/autoware_build_flags/CMakeLists.txt
index eab8f612fe5..87524e36c69 100644
--- a/common/autoware_build_flags/CMakeLists.txt
+++ b/common/autoware_build_flags/CMakeLists.txt
@@ -14,6 +14,9 @@ else() #ROS2
find_package(ament_cmake_auto REQUIRED)
ament_auto_find_build_dependencies()
+ list(APPEND ${PROJECT_NAME}_CONFIG_EXTRAS
+ cmake/autoware_build_flags-extras.cmake
+ )
ament_auto_package()
endif()
diff --git a/common/autoware_build_flags/cmake/autoware_build_flags-extras.cmake b/common/autoware_build_flags/cmake/autoware_build_flags-extras.cmake
index 7f6c7531058..cb597900bd4 100644
--- a/common/autoware_build_flags/cmake/autoware_build_flags-extras.cmake
+++ b/common/autoware_build_flags/cmake/autoware_build_flags-extras.cmake
@@ -4,11 +4,18 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-undefined,error")
endif()
+find_package(ros_environment REQUIRED)
+set(ROS_VERSION $ENV{ROS_VERSION})
+
+if (${ROS_VERSION} EQUAL 1)
# Enable support for C++14
-if(${CMAKE_VERSION} VERSION_LESS "3.1.0")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
-else()
- set(CMAKE_CXX_STANDARD 14)
+ if(${CMAKE_VERSION} VERSION_LESS "3.1.0")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
+ else()
+ set(CMAKE_CXX_STANDARD 14)
+ endif()
+else() # ROS2
+ set(CMAKE_CXX_STANDARD 17)
endif()
message(STATUS "CUDA compilation status: $ENV{AUTOWARE_COMPILE_WITH_CUDA}.")
diff --git a/common/autoware_build_flags/package.xml b/common/autoware_build_flags/package.xml
index 90fd9743b02..908e8c349f1 100644
--- a/common/autoware_build_flags/package.xml
+++ b/common/autoware_build_flags/package.xml
@@ -9,6 +9,7 @@
Apache 2
+ ros_environment
catkin
ament_cmake_auto
diff --git a/common/hardcoded_params/include/hardcoded_params/control_limits/control_limits.h b/common/hardcoded_params/include/hardcoded_params/control_limits/control_limits.h
index 96f89c5432d..ae081e0bc3e 100644
--- a/common/hardcoded_params/include/hardcoded_params/control_limits/control_limits.h
+++ b/common/hardcoded_params/include/hardcoded_params/control_limits/control_limits.h
@@ -21,9 +21,19 @@ namespace hardcoded_params
namespace control_limits
{
/**
- * The maximum allowable longitudinal velocity of the vehicle in m/s
+ * The maximum allowable longitudinal velocity of the vehicle in m/s
*/
constexpr double MAX_LONGITUDINAL_VELOCITY_MPS = 35.7632;
constexpr double MAX_LONGITUDINAL_ACCEL_MPS2 = 3.5;
+
+/**
+* The maximum allowable simulation parameters
+* At the time of writing this, platform uses CARLA 0.9.10 as the vehicle's physics simulator.
+* CARLA 0.9.10 uses below hardcoded value for its vehicles (so even if increasing this value,
+* actual simulation won't support larger values unless forked and compiled)
+* https://github.com/carla-simulator/ros-bridge/blob/0.9.10.1/carla_ackermann_control/src/carla_ackermann_control/carla_control_physics.py#L254
+*/
+constexpr double MAX_SIMULATION_LONGITUDINAL_ACCEL_MPS2 = 8.0;
+
}
} // namespace hardcoded_params
diff --git a/common/lanelet2_extension/include/lanelet2_extension/regulatory_elements/CarmaTrafficSignal.h b/common/lanelet2_extension/include/lanelet2_extension/regulatory_elements/CarmaTrafficSignal.h
index 15741929d17..bad4c6abefc 100644
--- a/common/lanelet2_extension/include/lanelet2_extension/regulatory_elements/CarmaTrafficSignal.h
+++ b/common/lanelet2_extension/include/lanelet2_extension/regulatory_elements/CarmaTrafficSignal.h
@@ -25,9 +25,9 @@
namespace lanelet
{
/**
- * @brief: Enum representing Traffic Light States.
+ * @brief: Enum representing Traffic Light States.
* These states match the SAE J2735 PhaseState definitions used for SPaT messages
- *
+ *
* UNAVAILABLE : No data available
* DARK : Light is non-functional
* STOP_THEN_PROCEED : Flashing Red
@@ -53,6 +53,15 @@ enum class CarmaTrafficSignalState {
CAUTION_CONFLICTING_TRAFFIC=9
};
+namespace time{
+
+//This int is max representable number by 32bit which the posix::time library is using.
+//It corresponds to 03:14:07 on Tuesday, 19 January 2038.
+
+constexpr auto INFINITY_END_TIME_FOR_NOT_ENOUGH_STATES{2147483647};
+
+}
+
struct CarmaTrafficSignalRoleNameString
{
static constexpr char ControlStart[] = "control_start";
@@ -67,7 +76,7 @@ std::ostream& operator<<(std::ostream& os, CarmaTrafficSignalState s);
/**
* @brief: Class representing a known timing traffic light.
* Normally the traffic light timing information is provided by SAE J2735 SPaT messages although alternative data sources can be supported
- *
+ *
* @ingroup RegulatoryElementPrimitives
* @ingroup Primitives
*/
@@ -79,7 +88,7 @@ class CarmaTrafficSignal : public lanelet::RegulatoryElement
int revision_ = 0; //indicates when was this last modified
boost::posix_time::time_duration fixed_cycle_duration;
- std::vector recorded_start_time_stamps; //user must ensure it's 1 to 1 with recorded_time_stamps ,
+ std::vector recorded_start_time_stamps; //user must ensure it's 1 to 1 with recorded_time_stamps ,
//used in dynamic SPAT processing
std::vector> recorded_time_stamps;
std::unordered_map signal_durations;
@@ -98,11 +107,11 @@ class CarmaTrafficSignal : public lanelet::RegulatoryElement
* NOTE: Order of the lanelets does not correlate to the order of the control_end lanelets
*/
lanelet::ConstLanelets getControlStartLanelets() const;
-
+
/**
* @brief getControlEndLanelets function returns lanelets where this element's control ends
* NOTE: Order of the lanelets does not correlate to the order of the control_start lanelets
- *
+ *
*/
lanelet::ConstLanelets getControlEndLanelets() const;
@@ -115,7 +124,7 @@ class CarmaTrafficSignal : public lanelet::RegulatoryElement
* @throw InvalidInputError if timestamps recorded somehow did not have full cycle
*/
boost::optional> predictState(boost::posix_time::ptime time_stamp);
-
+
/**
* @brief Return the stop_lines related to the entry lanelets in order if exists.
*/
@@ -124,16 +133,16 @@ class CarmaTrafficSignal : public lanelet::RegulatoryElement
/**
* @brief Return the stop_lines related to the specified entry lanelet
- * @param llt entry_lanelet
- * @return optional stop line linestring.
- * Empty optional if no stopline, or no entry lanelets, or if specified entry lanelet is not recorded.
+ * @param llt entry_lanelet
+ * @return optional stop line linestring.
+ * Empty optional if no stopline, or no entry lanelets, or if specified entry lanelet is not recorded.
*/
Optional getConstStopLine(const ConstLanelet& llt);
Optional getStopLine(const ConstLanelet& llt);
explicit CarmaTrafficSignal(const lanelet::RegulatoryElementDataPtr& data);
/**
- * @brief: Creating one is not directly usable unless setStates is called
+ * @brief: Creating one is not directly usable unless setStates is called
* Static helper function that creates a stop line data object based on the provided inputs
*
* @param id The lanelet::Id of this element
diff --git a/common/lanelet2_extension/lib/CarmaTrafficSignal.cpp b/common/lanelet2_extension/lib/CarmaTrafficSignal.cpp
index 570af43cf78..7436f1955a7 100755
--- a/common/lanelet2_extension/lib/CarmaTrafficSignal.cpp
+++ b/common/lanelet2_extension/lib/CarmaTrafficSignal.cpp
@@ -60,7 +60,7 @@ LineStrings3d CarmaTrafficSignal::stopLine()
return getParameters(RoleName::RefLine);
}
-Optional CarmaTrafficSignal::getStopLine(const ConstLanelet& llt)
+Optional CarmaTrafficSignal::getStopLine(const ConstLanelet& llt)
{
auto sl = stopLine();
if (sl.empty()) {
@@ -78,15 +78,15 @@ Optional CarmaTrafficSignal::getStopLine(const ConstLanelet& llt)
return sl.at(size_t(std::distance(llts.begin(), it)));
}
-Optional CarmaTrafficSignal::getConstStopLine(const ConstLanelet& llt)
+Optional CarmaTrafficSignal::getConstStopLine(const ConstLanelet& llt)
{
Optional mutable_stop_line = getStopLine(llt);
-
+
if (!mutable_stop_line)
return boost::none;
ConstLineString3d const_stop_line = mutable_stop_line.get();
-
+
return const_stop_line;
}
@@ -120,18 +120,13 @@ boost::optional> Ca
return boost::none;
}
- if (recorded_time_stamps.size() == 1) // if only 1 timestamp recorded, this signal doesn't change
- {
- return std::pair(recorded_time_stamps.front().first, recorded_time_stamps.front().second);
- }
-
if (lanelet::time::toSec(fixed_cycle_duration) < 1.0) // there are recorded states, but no fixed_cycle_duration means it is dynamic
{
if (recorded_time_stamps.size() != recorded_start_time_stamps.size())
{
throw std::invalid_argument("recorded_start_time_stamps size is not equal to recorded_time_stamps size");
}
-
+
// if requested time is BEFORE recorded states' time interval, return STOP_AND_REMAIN
if (recorded_start_time_stamps.front() >= time_stamp)
{
@@ -141,11 +136,11 @@ boost::optional> Ca
// if requested time is AFTER recorded states' time interval, return STOP_AND_REMAIN
if (recorded_time_stamps.back().first <= time_stamp)
{
- auto end_infinity_time = timeFromSec(2147483647); //corresponds to 03:14:07 on Tuesday, 19 January 2038.
- LOG_WARN_STREAM("CarmaTrafficSignal doesn't have enough state saved, so returning STOP_AND_REMAIN state! End_time: " << end_infinity_time);
+ auto end_infinity_time = timeFromSec(time::INFINITY_END_TIME_FOR_NOT_ENOUGH_STATES); //corresponds to 03:14:07 on Tuesday, 19 January 2038.
+ LOG_DEBUG_STREAM("CarmaTrafficSignal doesn't have enough state saved, so returning STOP_AND_REMAIN state! End_time: " << end_infinity_time);
return std::pair(end_infinity_time, CarmaTrafficSignalState::STOP_AND_REMAIN);
}
-
+
// iterate through states in the dynamic states to get the signal.
for (size_t i = 0; i < recorded_time_stamps.size(); i++)
{
@@ -155,15 +150,16 @@ boost::optional> Ca
}
}
}
-
+
+ // This part of the code is used for predicting state if fixed_cycle_duration is set using setStates function
// shift starting time to the future or to the past to fit input into a valid cycle
boost::posix_time::time_duration accumulated_offset_duration;
double offset_duration_dir = recorded_time_stamps.front().first > time_stamp ? -1.0 : 1.0; // -1 if past, +1 if time_stamp is in future
int num_of_cycles = std::abs(lanelet::time::toSec(recorded_time_stamps.front().first - time_stamp) / lanelet::time::toSec(fixed_cycle_duration));
accumulated_offset_duration = durationFromSec( num_of_cycles * lanelet::time::toSec(fixed_cycle_duration));
-
- if (offset_duration_dir < 0)
+
+ if (offset_duration_dir < 0)
{
while (recorded_time_stamps.front().first - accumulated_offset_duration > time_stamp)
{
@@ -175,7 +171,7 @@ boost::optional> Ca
{
double end_time = lanelet::time::toSec(recorded_time_stamps[i].first) + offset_duration_dir * lanelet::time::toSec(accumulated_offset_duration);
if (end_time >= lanelet::time::toSec(time_stamp))
- {
+ {
return std::pair(timeFromSec(end_time), recorded_time_stamps[i].second);
}
}
@@ -186,7 +182,7 @@ boost::optional> Ca
lanelet::ConstLanelets CarmaTrafficSignal::getControlStartLanelets() const
{
return getParameters(CarmaTrafficSignalRoleNameString::ControlStart);
-}
+}
lanelet::ConstLanelets CarmaTrafficSignal::getControlEndLanelets() const
{
@@ -235,4 +231,4 @@ namespace
lanelet::RegisterRegulatoryElement reg;
} // namespace
-} // namespace lanelet
\ No newline at end of file
+} // namespace lanelet
diff --git a/common/map_file_ros2/CMakeLists.txt b/common/map_file_ros2/CMakeLists.txt
new file mode 100644
index 00000000000..ee07ddf1abd
--- /dev/null
+++ b/common/map_file_ros2/CMakeLists.txt
@@ -0,0 +1,123 @@
+# Copyright (C) 2023 LEIDOS.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+cmake_minimum_required(VERSION 3.5)
+project(map_file_ros2)
+
+# Declare carma package and check ROS version
+find_package(carma_cmake_common REQUIRED)
+carma_check_ros_version(2)
+carma_package()
+
+find_package(PCL COMPONENTS REQUIRED)
+find_package(CURL REQUIRED)
+
+## Find dependencies using ament auto
+find_package(ament_cmake_auto REQUIRED)
+ament_auto_find_build_dependencies()
+
+
+# Includes
+include_directories(
+ include
+ ${PCL_INCLUDE_DIRS}
+ ${CURL_INCLUDE_DIRS}
+)
+
+# Build
+ament_auto_add_library(get_file SHARED
+ lib/map_file/get_file.cpp
+)
+target_link_libraries(get_file ${CURL_LIBRARIES})
+
+ament_auto_add_library(points_map_loader_lib SHARED
+ nodes/points_map_loader/points_map_loader.cpp
+)
+ament_auto_add_executable(points_map_loader_exec
+ nodes/points_map_loader/points_map_loader_node.cpp
+)
+
+ament_auto_add_library(map_param_loader_lib SHARED
+ nodes/map_param_loader/map_param_loader.cpp
+)
+ament_auto_add_executable(map_param_loader_exec
+ nodes/map_param_loader/map_param_loader_node.cpp
+)
+
+ament_auto_add_library(lanelet2_map_loader_lib SHARED
+ nodes/lanelet2_map_loader/lanelet2_map_loader.cpp
+)
+ament_auto_add_executable(lanelet2_map_loader_exec
+ nodes/lanelet2_map_loader/lanelet2_map_loader_node.cpp
+)
+
+ament_auto_add_library(lanelet2_map_visualization_lib SHARED
+ nodes/lanelet2_map_visualization/lanelet2_map_visualization.cpp
+)
+ament_auto_add_executable(lanelet2_map_visualization_exec
+ nodes/lanelet2_map_visualization/lanelet2_map_visualization_node.cpp
+)
+
+# Register component
+rclcpp_components_register_nodes(points_map_loader_lib "points_map_loader::PointsMapLoader")
+
+rclcpp_components_register_nodes(map_param_loader_lib "map_param_loader::MapParamLoader")
+
+rclcpp_components_register_nodes(lanelet2_map_loader_lib "lanelet2_map_loader::Lanelet2MapLoader")
+
+rclcpp_components_register_nodes(lanelet2_map_visualization_lib "lanelet2_map_visualization::Lanelet2MapVisualization")
+
+# All locally created targets will need to be manually linked
+# ament auto will handle linking of external dependencies
+target_link_libraries(points_map_loader_lib get_file ${PCL_LIBRARIES})
+
+target_link_libraries(
+ points_map_loader_exec
+ points_map_loader_lib
+)
+
+target_link_libraries(
+ map_param_loader_exec
+ map_param_loader_lib
+)
+
+target_link_libraries(
+ lanelet2_map_loader_exec
+ lanelet2_map_loader_lib
+)
+
+target_link_libraries(
+ lanelet2_map_visualization_exec
+ lanelet2_map_visualization_lib
+)
+
+# Testing
+if(BUILD_TESTING)
+
+ find_package(ament_lint_auto REQUIRED)
+ ament_lint_auto_find_test_dependencies() # This populates the ${${PROJECT_NAME}_FOUND_TEST_DEPENDS} variable
+
+ ament_add_gtest(test_route
+ test/test_get_transform.cpp
+ )
+
+ ament_target_dependencies(test_route ${${PROJECT_NAME}_FOUND_TEST_DEPENDS})
+
+ target_link_libraries(test_route map_param_loader_lib)
+
+endif()
+
+ament_auto_package(
+ INSTALL_TO_SHARE launch
+)
\ No newline at end of file
diff --git a/common/map_file_ros2/include/get_file.hpp b/common/map_file_ros2/include/get_file.hpp
new file mode 100644
index 00000000000..69db2e89c40
--- /dev/null
+++ b/common/map_file_ros2/include/get_file.hpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef _GET_FILE_H_
+#define _GET_FILE_H_
+
+#include
+#include
+#include
+
+
+#define HTTP_HOSTNAME "133.6.148.90"
+#define HTTP_PORT (80)
+#define HTTP_USER ""
+#define HTTP_PASSWORD ""
+
+class GetFile {
+private:
+ std::string host_name_;
+ int port_;
+ std::string user_;
+ std::string password_;
+ int sock;
+ struct sockaddr_in server;
+
+public:
+ GetFile();
+ explicit GetFile(const std::string& host_name, int port, const std::string& user, const std::string& password);
+
+ int GetHTTPFile(const std::string& value);
+};
+
+
+#endif /* _GET_FILE_H_ */
\ No newline at end of file
diff --git a/common/map_file_ros2/include/lanelet2_map_loader.hpp b/common/map_file_ros2/include/lanelet2_map_loader.hpp
new file mode 100644
index 00000000000..36204969d8f
--- /dev/null
+++ b/common/map_file_ros2/include/lanelet2_map_loader.hpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+namespace lanelet2_map_loader{
+void printUsage()
+{
+ RCLCPP_ERROR_STREAM(rclcpp::get_logger("map_file_ros2"), "Usage:");
+ RCLCPP_ERROR_STREAM(rclcpp::get_logger("map_file_ros2"), "ros2 launch map_file lanelet2_map_loader lanelet2_filename:=[.OSM]:");
+ RCLCPP_ERROR_STREAM(rclcpp::get_logger("map_file_ros2"), "ros2 launch map_file lanelet2_map_loader load_type:=download [X] [Y]: WARNING not implemented");
+}
+
+class Lanelet2MapLoader : public carma_ros2_utils::CarmaLifecycleNode
+{
+ private:
+ rclcpp::Publisher::SharedPtr map_bin_pub;
+
+ // Parameters
+ std::string load_type;
+ std::string lanelet2_filename;
+
+ rclcpp::TimerBase::SharedPtr timer_;
+ autoware_lanelet2_msgs::msg::MapBin map_bin_msg_;
+
+ void timer_callback();
+
+
+ public:
+ Lanelet2MapLoader(const rclcpp::NodeOptions &options);
+
+ carma_ros2_utils::CallbackReturn handle_on_configure(const rclcpp_lifecycle::State &);
+
+};
+
+} //namespace lanelet2_map_loader
+
diff --git a/common/map_file_ros2/include/lanelet2_map_visualization.hpp b/common/map_file_ros2/include/lanelet2_map_visualization.hpp
new file mode 100644
index 00000000000..bd97b7fba68
--- /dev/null
+++ b/common/map_file_ros2/include/lanelet2_map_visualization.hpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+static bool g_viz_lanelets_centerline = true;
+
+namespace lanelet2_map_visualization{
+ class Lanelet2MapVisualization : public carma_ros2_utils::CarmaLifecycleNode
+ {
+ private:
+
+ rclcpp::Publisher::SharedPtr g_map_pub;
+ rclcpp::Subscription ::SharedPtr bin_map_sub;
+
+ void insertMarkerArray(visualization_msgs::msg::MarkerArray* a1, const visualization_msgs::msg::MarkerArray& a2);
+ void setColor(std_msgs::msg::ColorRGBA* cl, double r, double g, double b, double a);
+ void binMapCallback(const autoware_lanelet2_msgs::msg::MapBin::SharedPtr msg);
+
+ public:
+ Lanelet2MapVisualization(const rclcpp::NodeOptions &options);
+
+ carma_ros2_utils::CallbackReturn handle_on_configure(const rclcpp_lifecycle::State &);
+
+ };
+}
+
+
+
diff --git a/common/map_file_ros2/include/map_param_loader.hpp b/common/map_file_ros2/include/map_param_loader.hpp
new file mode 100644
index 00000000000..286bc6bffa0
--- /dev/null
+++ b/common/map_file_ros2/include/map_param_loader.hpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace map_param_loader
+{
+ class MapParamLoader : public carma_ros2_utils::CarmaLifecycleNode
+ {
+ private:
+ rclcpp::Publisher::SharedPtr georef_pub_;
+ std::shared_ptr timer_;
+ std::string target_frame_;
+
+ void timer_callback();
+
+ // Broadcast the input transform to tf_static.
+ void broadcastTransform(const tf2::Transform& transform);
+
+ //parameters
+ std::string lanelet2_filename;
+ bool broadcast_earth_frame = false;
+
+ public:
+ explicit MapParamLoader(const rclcpp::NodeOptions &options);
+
+ // Get transform from map_frame to ecef_frame using respective proj strings.
+ tf2::Transform getTransform(const std::string& map_frame);
+
+ carma_ros2_utils::CallbackReturn handle_on_configure(const rclcpp_lifecycle::State &);
+ carma_ros2_utils::CallbackReturn handle_on_activate(const rclcpp_lifecycle::State &);
+
+ };
+
+}
\ No newline at end of file
diff --git a/common/map_file_ros2/include/points_map_loader.hpp b/common/map_file_ros2/include/points_map_loader.hpp
new file mode 100644
index 00000000000..4baf0299a00
--- /dev/null
+++ b/common/map_file_ros2/include/points_map_loader.hpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "tf2_ros/buffer.h"
+
+#include
+
+namespace points_map_loader
+{
+
+ struct Area {
+ std::string path;
+ double x_min;
+ double y_min;
+ double z_min;
+ double x_max;
+ double y_max;
+ double z_max;
+ };
+
+ constexpr int DEFAULT_UPDATE_RATE = 1000; // ms
+ constexpr double MARGIN_UNIT = 100; // meter
+ constexpr int ROUNDING_UNIT = 1000; // meter
+ const std::string AREALIST_FILENAME = "arealist.txt";
+ const std::string TEMPORARY_DIRNAME = "/tmp/";
+
+ typedef std::vector AreaList;
+ typedef std::vector> Tbl;
+
+ int fallback_rate;
+ double margin;
+ bool can_download;
+
+ rclcpp::Time gnss_time;
+ rclcpp::Time current_time;
+
+ std_msgs::msg::Bool stat_msg;
+ AreaList all_areas;
+ AreaList downloaded_areas;
+ std::mutex downloaded_areas_mtx;
+ std::vector cached_arealist_paths;
+
+ class PointsMapLoader : public carma_ros2_utils::CarmaLifecycleNode {
+ private:
+
+ std::queue queue_; // takes priority over look_ahead_queue_
+ std::queue look_ahead_queue_;
+ std::mutex mtx_;
+ std::condition_variable cv_;
+
+ // Publishers
+ rclcpp::Publisher::SharedPtr pcd_pub;
+ rclcpp::Publisher::SharedPtr stat_pub;
+
+ // Subscribers
+ rclcpp::Subscription::SharedPtr gnss_sub;
+ rclcpp::Subscription::SharedPtr current_sub;
+ rclcpp::Subscription::SharedPtr initial_sub;
+ rclcpp::Subscription::SharedPtr waypoints_sub;
+
+ GetFile gf;
+
+ // Parameters
+ int update_rate = DEFAULT_UPDATE_RATE;
+ std::string area = "";
+ std::string load_type = "";
+ std::string path_area_list = "";
+ std::vector pcd_path = {};
+ std::string host_name = HTTP_HOSTNAME;
+ int port = HTTP_PORT;
+ std::string user = HTTP_USER;
+ std::string password = HTTP_PASSWORD;
+
+
+ void enqueue(const geometry_msgs::msg::Point& p);
+ void enqueue_look_ahead(const geometry_msgs::msg::Point& p);
+ void clear();
+ void clear_look_ahead();
+ geometry_msgs::msg::Point dequeue();
+
+ void publish_pcd(sensor_msgs::msg::PointCloud2 pcd, const int* errp);
+
+ Tbl read_csv(const std::string& path);
+ void write_csv(const std::string& path, const Tbl& tbl);
+ AreaList read_arealist(const std::string& path);
+ void write_arealist(const std::string& path, const AreaList& areas);
+ bool is_downloaded(const std::string& path);
+ bool is_in_area(double x, double y, const Area& area, double m);
+ std::string create_location(int x, int y);
+ void cache_arealist(const Area& area, AreaList& areas);
+ int download(GetFile gf, const std::string& tmp, const std::string& loc, const std::string& filename);
+
+ void download_map();
+
+ sensor_msgs::msg::PointCloud2 create_pcd(const geometry_msgs::msg::Point& p);
+ sensor_msgs::msg::PointCloud2 create_pcd(const std::vector& pcd_paths, int* ret_err);
+
+ void publish_gnss_pcd(const geometry_msgs::msg::PoseStamped::SharedPtr msg);
+ void publish_dragged_pcd(const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr msg);
+ void publish_current_pcd(const geometry_msgs::msg::PoseStamped::SharedPtr msg);
+ void request_lookahead_download(const autoware_msgs::msg::LaneArray::SharedPtr msg);
+
+ rclcpp::TimerBase::SharedPtr timer_;
+ void timer_callback();
+
+ void run();
+
+ public:
+ explicit PointsMapLoader(const rclcpp::NodeOptions &);
+
+ carma_ros2_utils::CallbackReturn handle_on_configure(const rclcpp_lifecycle::State &prev_state);
+ carma_ros2_utils::CallbackReturn handle_on_activate(const rclcpp_lifecycle::State &prev_state);
+
+
+ };
+
+ void print_usage()
+ {
+ RCLCPP_ERROR_STREAM(rclcpp::get_logger("map_file_ros2"), "Usage:");
+ RCLCPP_ERROR_STREAM(rclcpp::get_logger("map_file_ros2"), "ros2 launch map_file_ros2 points_map_loader.launch.py --load_type:=noupdate --pcd_path_parameter:=[PCD]...");
+ RCLCPP_ERROR_STREAM(rclcpp::get_logger("map_file_ros2"), "ros2 launch map_file_ros2 points_map_loader.launch.py --path_area_list:={1x1|3x3|5x5|7x7|9x9} --pcd_path_parameter:=[PCD]...");
+ RCLCPP_ERROR_STREAM(rclcpp::get_logger("map_file_ros2"), "ros2 launch map_file_ros2 points_map_loader.launch --path_area_list:{1x1|3x3|5x5|7x7|9x9} --load_type:=download");
+ }
+}
\ No newline at end of file
diff --git a/common/map_file_ros2/launch/lanelet2_map_loader.launch.py b/common/map_file_ros2/launch/lanelet2_map_loader.launch.py
new file mode 100644
index 00000000000..39d32ed9cba
--- /dev/null
+++ b/common/map_file_ros2/launch/lanelet2_map_loader.launch.py
@@ -0,0 +1,52 @@
+from ament_index_python import get_package_share_directory
+from launch import LaunchDescription
+from launch.substitutions import LaunchConfiguration
+from launch.actions import DeclareLaunchArgument
+
+from launch_ros.actions import ComposableNodeContainer
+from launch_ros.descriptions import ComposableNode
+from carma_ros2_utils.launch.get_current_namespace import GetCurrentNamespace
+
+import os
+
+def generate_launch_description():
+
+ log_level = LaunchConfiguration('log_level')
+ declare_log_level_arg = DeclareLaunchArgument(name = 'log_level', default_value='WARN')
+
+ load_type = LaunchConfiguration('load_type')
+ declare_load_type = DeclareLaunchArgument(name='load_type', default_value='')
+
+
+ lanelet2_filename = LaunchConfiguration('lanelet2_filename')
+ declare_lanelet2_filename = DeclareLaunchArgument(name='lanelet2_filename', default_value='')
+
+ lanelet2_map_loader_container = ComposableNodeContainer(
+ package='carma_ros2_utils',
+ name='lanelet2_map_loader_container',
+ executable='carma_component_container_mt',
+ namespace=GetCurrentNamespace(),
+ composable_node_descriptions=[
+ ComposableNode(
+ package='map_file_ros2',
+ plugin='lanelet2_map_loader::Lanelet2MapLoader',
+ name='lanelet2_map_loader',
+ parameters=[
+ {'load_type' : load_type},
+ {'lanelet2_filename' : lanelet2_filename}
+ ]
+ ),
+ ComposableNode(
+ package='map_file_ros2',
+ plugin='lanelet2_map_visualization::Lanelet2MapVisualization',
+ name='lanelet2_map_visualization'
+ )
+
+ ])
+
+ return LaunchDescription([
+ declare_log_level_arg,
+ declare_load_type,
+ declare_lanelet2_filename,
+ lanelet2_map_loader_container,
+ ])
\ No newline at end of file
diff --git a/common/map_file_ros2/launch/map_loader.launch.py b/common/map_file_ros2/launch/map_loader.launch.py
new file mode 100644
index 00000000000..38d2baa5128
--- /dev/null
+++ b/common/map_file_ros2/launch/map_loader.launch.py
@@ -0,0 +1,92 @@
+from launch import LaunchDescription
+from launch.substitutions import LaunchConfiguration
+from launch.actions import DeclareLaunchArgument
+from launch.actions import GroupAction
+from launch.substitutions import ThisLaunchFileDir
+from launch.actions import IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.conditions import IfCondition
+from launch.substitutions import PythonExpression
+
+from launch.substitutions import PathJoinSubstitution
+
+
+import os
+
+
+def generate_launch_description():
+
+
+ load_type = LaunchConfiguration('load_type')
+ declare_load_type = DeclareLaunchArgument(name='load_type', default_value='noupdate', description="Enum of the map loading approach to use. Can be 'download', 'noupdate', or 'arealist'")
+
+ single_pcd_path = LaunchConfiguration('single_pcd_path')
+ declare_single_pcd_path = DeclareLaunchArgument(name='single_pcd_path', default_value='pcd_map.pcd', description='Path to the map pcd file if using the noupdate load type')
+
+ area = LaunchConfiguration('area')
+ declare_area = DeclareLaunchArgument(name='area', default_value='1x1', description='Dimensions of the square of cells loaded at runtime using the download and arealist load types')
+
+ arealist_path = LaunchConfiguration('arealist_path')
+ declare_arealist_path = DeclareLaunchArgument(name='arealist_path', default_value='arealist.txt', description='Path to the arealist.txt file which contains the paths and dimensions of each map cell to load with the arealist load_type')
+
+ area_paths=LaunchConfiguration('area_paths')
+ declare_area_paths = DeclareLaunchArgument(name='area_paths', default_value='', description='List of cell paths to load when using the arealist load type. If this is not filled all the cells will be loaded based on the arealist.txt file')
+
+
+ download_launch_group = GroupAction(
+ condition = IfCondition(PythonExpression(["'",load_type,"' == 'download'"])),
+ actions = [
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ PathJoinSubstitution([ThisLaunchFileDir(), '/launch/points_map_loader.launch.py'])
+ ]),
+ launch_arguments = {
+ 'load_type' : load_type,
+ 'area' : area,
+ }.items()
+ )
+ ]
+ )
+
+ noupdate_launch_group = GroupAction(
+ condition = IfCondition(PythonExpression(["'",load_type,"' == 'noupdate'"])),
+ actions = [
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ PathJoinSubstitution([ThisLaunchFileDir(), 'points_map_loader.launch.py'])
+ ]),
+ launch_arguments = {
+ 'load_type' : load_type,
+ 'pcd_path_parameter' : single_pcd_path
+ }.items()
+ ),
+ ]
+ )
+
+ arealist_launch_group = GroupAction(
+ condition = IfCondition(PythonExpression(["'",load_type,"' == 'arealist'"])),
+ actions = [
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ PathJoinSubstitution([ThisLaunchFileDir(), '/points_map_loader.launch.py'])
+ ]),
+ launch_arguments = {
+ 'load_type' : load_type,
+ 'area' : area,
+ 'arealist_path' : arealist_path,
+ 'area_paths' : area_paths,
+ }.items(),
+ )
+ ]
+ )
+
+ return LaunchDescription([
+ declare_load_type,
+ declare_single_pcd_path,
+ declare_area,
+ declare_arealist_path,
+ declare_area_paths,
+ download_launch_group,
+ noupdate_launch_group,
+ arealist_launch_group
+ ])
\ No newline at end of file
diff --git a/common/map_file_ros2/launch/map_param_loader.launch.py b/common/map_file_ros2/launch/map_param_loader.launch.py
new file mode 100644
index 00000000000..cf20a944308
--- /dev/null
+++ b/common/map_file_ros2/launch/map_param_loader.launch.py
@@ -0,0 +1,48 @@
+from ament_index_python import get_package_share_directory
+from launch import LaunchDescription
+from launch.substitutions import LaunchConfiguration
+from launch.actions import DeclareLaunchArgument
+
+from launch_ros.actions import ComposableNodeContainer
+from launch_ros.descriptions import ComposableNode
+from carma_ros2_utils.launch.get_current_namespace import GetCurrentNamespace
+\
+
+import os
+
+def generate_launch_description():
+
+ log_level = LaunchConfiguration('log_level')
+ declare_log_level_arg = DeclareLaunchArgument(name = 'log_level', default_value='WARN')
+
+ broadcast_earth_frame = LaunchConfiguration('broadcast_earth_frame')
+ declare_broadcast_earth_frame = DeclareLaunchArgument(name='broadcast_earth_frame', default_value='false',
+ description='If true this node will attempt to compute a homogenious transformation matrix between earth and the map frame origin. NOTE: Not all projections can be linearly described in this way and such a transformation may be inaccurate')
+
+ file_name = LaunchConfiguration('file_name')
+ declare_file_name = DeclareLaunchArgument(name='file_name', default_value='')
+
+
+ map_param_loader_container = ComposableNodeContainer(
+ package='carma_ros2_utils',
+ name='map_param_loader_container',
+ executable='carma_component_container_mt',
+ namespace=GetCurrentNamespace(),
+ composable_node_descriptions=[
+ ComposableNode(
+ package='map_file_ros2',
+ plugin='map_param_loader::MapParamLoader',
+ name='map_param_loader',
+ parameters=[
+ {'broadcast_earth_frame' : broadcast_earth_frame},
+ {'file_name' : file_name}
+ ]
+ )
+ ])
+
+ return LaunchDescription([
+ declare_log_level_arg,
+ declare_broadcast_earth_frame,
+ declare_file_name,
+ map_param_loader_container
+ ])
\ No newline at end of file
diff --git a/common/map_file_ros2/launch/points_map_loader.launch.py b/common/map_file_ros2/launch/points_map_loader.launch.py
new file mode 100644
index 00000000000..66129cb5c57
--- /dev/null
+++ b/common/map_file_ros2/launch/points_map_loader.launch.py
@@ -0,0 +1,86 @@
+from ament_index_python import get_package_share_directory
+from launch import LaunchDescription
+from launch.substitutions import LaunchConfiguration
+from launch.actions import DeclareLaunchArgument
+
+from launch_ros.actions import ComposableNodeContainer
+from launch_ros.descriptions import ComposableNode
+from carma_ros2_utils.launch.get_current_namespace import GetCurrentNamespace
+
+import os
+
+def generate_launch_description():
+
+ log_level = LaunchConfiguration('log_level')
+ declare_log_level_arg = DeclareLaunchArgument(name = 'log_level', default_value='WARN')
+
+ update_rate = LaunchConfiguration('update_rate')
+ declare_update_rate = DeclareLaunchArgument(name='update_rate', default_value='1000')
+
+ area = LaunchConfiguration('area')
+ declare_area = DeclareLaunchArgument(name='area', default_value="")
+
+ load_type = LaunchConfiguration('load_type')
+ declare_load_type = DeclareLaunchArgument(name='load_type', default_value="")
+
+ path_area_list = LaunchConfiguration('path_area_list')
+ declare_path_area_list = DeclareLaunchArgument(name='path_area_list', default_value="")
+
+ pcd_path_parameter = LaunchConfiguration('pcd_path_parameter')
+ declare_pcd_path_parameter = DeclareLaunchArgument(name='pcd_path_parameter', default_value='["",""]')
+
+ host_name = LaunchConfiguration('host_name')
+ declare_host_name = DeclareLaunchArgument(name='host_name', default_value="133.6.148.90")
+
+ port = LaunchConfiguration('port')
+ declare_port = DeclareLaunchArgument(name='port', default_value='80')
+
+ user = LaunchConfiguration('user')
+ declare_user = DeclareLaunchArgument(name='user', default_value="")
+
+ password = LaunchConfiguration('password')
+ declare_password = DeclareLaunchArgument(name='password', default_value="")
+
+ container = ComposableNodeContainer(
+ package='carma_ros2_utils',
+ name='map_file_nodes_container',
+ namespace=GetCurrentNamespace(),
+ executable='carma_component_container_mt',
+ composable_node_descriptions=[
+
+ ComposableNode(
+ package='map_file_ros2',
+ plugin='points_map_loader::PointsMapLoader',
+ name='point_map_loader_node',
+ extra_arguments=[
+ {'use_intra_process_comms': True},
+ {'log_level':log_level}
+ ],
+ parameters=[
+ {'update_rate': update_rate},
+ {'area': area},
+ {'load_type': load_type},
+ {'path_area_list': path_area_list},
+ {'pcd_path_parameter': pcd_path_parameter},
+ {'host_name': host_name},
+ {'port' : port},
+ {'user': user},
+ {'password': password}
+ ]
+ ),
+ ]
+ )
+
+ return LaunchDescription([
+ declare_log_level_arg,
+ declare_update_rate,
+ declare_area,
+ declare_load_type,
+ declare_path_area_list,
+ declare_pcd_path_parameter,
+ declare_host_name,
+ declare_port,
+ declare_user,
+ declare_password,
+ container
+ ])
\ No newline at end of file
diff --git a/common/map_file_ros2/lib/map_file/get_file.cpp b/common/map_file_ros2/lib/map_file/get_file.cpp
new file mode 100644
index 00000000000..a070aa3bca0
--- /dev/null
+++ b/common/map_file_ros2/lib/map_file/get_file.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2015-2019 Autoware Foundation. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+
+GetFile::GetFile()
+ : GetFile(HTTP_HOSTNAME, HTTP_PORT, HTTP_USER, HTTP_PASSWORD)
+{
+}
+
+GetFile::GetFile(const std::string& host_name, int port, const std::string& user, const std::string& password)
+ : host_name_(host_name), port_(port), user_(user), password_(password)
+{
+}
+
+typedef std::basic_ofstream::__filebuf_type buffer_t;
+typedef __gnu_cxx::stdio_filebuf io_buffer_t;
+static FILE* cfile(buffer_t* const fb) {
+ return (static_cast(fb))->file();
+}
+
+int GetFile::GetHTTPFile(const std::string& value)
+{
+ CURL *curl;
+
+ curl = curl_easy_init();
+ if (! curl) {
+ std::cerr << "curl_easy_init failed" << std::endl;
+ return -1;
+ }
+
+ std::string filepath("/tmp/" + value);
+ std::ofstream ofs;
+ ofs.open(filepath);
+ if (! ofs.is_open()) {
+ std::cerr << "cannot open /tmp/" << value << std::endl;
+ curl_easy_cleanup(curl);
+ return -2;
+ }
+
+ std::ostringstream urlss;
+ urlss << "http://" << host_name_ << ":" << port_ << "/" << value;
+ curl_easy_setopt(curl, CURLOPT_URL, urlss.str().c_str());
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, cfile(ofs.rdbuf()));
+ if (user_ != "" && password_ != "") {
+ std::string userpwd = user_ + ":" + password_;
+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
+ curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd.c_str());
+ }
+ CURLcode res = curl_easy_perform(curl);
+ if (res != CURLE_OK) {
+ std::cerr << "curl_easy_perform failed: " <<
+ curl_easy_strerror(res) << std::endl;
+ curl_easy_cleanup(curl);
+ ofs.close();
+ unlink(filepath.c_str());
+ return -3;
+ }
+ long response_code;
+ res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
+ if (res != CURLE_OK) {
+ std::cerr << "curl_easy_getinfo failed: " <<
+ curl_easy_strerror(res) << std::endl;
+ curl_easy_cleanup(curl);
+ ofs.close();
+ unlink(filepath.c_str());
+ return -4;
+ }
+ curl_easy_cleanup(curl);
+ ofs.close();
+ if (response_code != 200) {
+ unlink(filepath.c_str());
+ return -5;
+ }
+
+ return 0;
+}
diff --git a/common/map_file_ros2/nodes/lanelet2_map_loader/lanelet2_map_loader.cpp b/common/map_file_ros2/nodes/lanelet2_map_loader/lanelet2_map_loader.cpp
new file mode 100644
index 00000000000..1e02248f48c
--- /dev/null
+++ b/common/map_file_ros2/nodes/lanelet2_map_loader/lanelet2_map_loader.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+
+namespace lanelet2_map_loader{
+
+ Lanelet2MapLoader::Lanelet2MapLoader(const rclcpp::NodeOptions &options) : carma_ros2_utils::CarmaLifecycleNode(options)
+ {
+ // Declare Parameters
+ load_type = declare_parameter("load_type", load_type);
+ lanelet2_filename = declare_parameter("lanelet2_filename", lanelet2_filename);
+
+ // Get Parameters
+ get_parameter("load_type", load_type);
+ get_parameter("lanelet2_filename", lanelet2_filename);
+ }
+
+ carma_ros2_utils::CallbackReturn Lanelet2MapLoader::handle_on_configure(const rclcpp_lifecycle::State &)
+ {
+
+ lanelet::ErrorMessages errors;
+ lanelet::LaneletMapPtr map;
+
+ int projector_type = 0;
+ std::string target_frame;
+
+ // Parse geo reference info from the lanelet map (.osm)
+ lanelet::io_handlers::AutowareOsmParser::parseMapParams(lanelet2_filename, &projector_type, &target_frame);
+
+ if(projector_type == 0)
+ {
+ lanelet::projection::MGRSProjector projector;
+ map = lanelet::load(lanelet2_filename, projector, &errors);
+ } else if(projector_type == 1)
+ {
+ lanelet::projection::LocalFrameProjector local_projector(target_frame.c_str());
+ map = lanelet::load(lanelet2_filename, local_projector, &errors);
+ }
+
+ for(const auto &error: errors)
+ {
+ RCLCPP_ERROR_STREAM(rclcpp::get_logger("map_file_ros2"), error);
+ }
+ if(!errors.empty())
+ {
+ // return EXIT_FAILURE;
+ return CallbackReturn::FAILURE;
+ }
+
+ std::string format_version, map_version;
+ lanelet::io_handlers::AutowareOsmParser::parseVersions(lanelet2_filename, &format_version, &map_version);
+
+ autoware_lanelet2_msgs::msg::MapBin map_bin_msg;
+ map_bin_msg.header.stamp = this->now();
+ map_bin_msg.header.frame_id = "map";
+ map_bin_msg.format_version = format_version;
+ if (!map_version.empty()) {
+ map_bin_msg.map_version = stoi(map_version); // CARMA Uses monotonically increasing map version numbers in carma_wm
+ }
+ lanelet::utils::conversion::toBinMsg(map, &map_bin_msg);
+ map_bin_msg_ = map_bin_msg;
+
+ // Set Publishers
+ // NOTE: Currently, intra-process comms must be disabled for the following two publishers that are transient_local: https://github.com/ros2/rclcpp/issues/1753
+ rclcpp::PublisherOptions intra_proc_disabled;
+ intra_proc_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable; // Disable intra-process comms for this PublisherOptions object
+ // Create a publisher that will send all previously published messages to late-joining subscribers ONLY If the subscriber is transient_local too
+ auto pub_qos_transient_local = rclcpp::QoS(rclcpp::KeepLast(1)); // A publisher with this QoS will store the "Last" message that it has sent on the topic
+ pub_qos_transient_local.transient_local();
+
+ map_bin_pub = create_publisher("lanelet_map_bin", pub_qos_transient_local, intra_proc_disabled); //Make latched
+
+ timer_ = this->create_wall_timer(std::chrono::seconds(1), std::bind(&Lanelet2MapLoader::timer_callback, this));
+
+ return CallbackReturn::SUCCESS;
+ }
+
+ void Lanelet2MapLoader::timer_callback(){
+
+ if (get_current_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE){
+
+ if(!map_bin_msg_.header.frame_id.empty()){
+ map_bin_msg_.header.stamp = this->now();
+ map_bin_pub->publish(map_bin_msg_);
+ timer_->cancel();
+ }
+
+ }
+ }
+
+
+} //namespace lanelet2_map_loader
+
+
+#include "rclcpp_components/register_node_macro.hpp"
+
+// Register the component with class_loader
+RCLCPP_COMPONENTS_REGISTER_NODE(lanelet2_map_loader::Lanelet2MapLoader)
\ No newline at end of file
diff --git a/common/map_file_ros2/nodes/lanelet2_map_loader/lanelet2_map_loader_node.cpp b/common/map_file_ros2/nodes/lanelet2_map_loader/lanelet2_map_loader_node.cpp
new file mode 100644
index 00000000000..e9a803f8dce
--- /dev/null
+++ b/common/map_file_ros2/nodes/lanelet2_map_loader/lanelet2_map_loader_node.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include
+#include
+
+int main(int argc, char **argv)
+{
+ rclcpp::init(argc, argv);
+ auto node = std::make_shared(rclcpp::NodeOptions());
+
+ rclcpp::executors::MultiThreadedExecutor executor;
+ executor.add_node(node->get_node_base_interface());
+ executor.spin();
+
+ rclcpp::shutdown();
+ return 0;
+}
diff --git a/common/map_file_ros2/nodes/lanelet2_map_visualization/lanelet2_map_visualization.cpp b/common/map_file_ros2/nodes/lanelet2_map_visualization/lanelet2_map_visualization.cpp
new file mode 100644
index 00000000000..d71197000f8
--- /dev/null
+++ b/common/map_file_ros2/nodes/lanelet2_map_visualization/lanelet2_map_visualization.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+
+#include
+
+namespace lanelet2_map_visualization{
+
+ Lanelet2MapVisualization::Lanelet2MapVisualization(const rclcpp::NodeOptions &options) : carma_ros2_utils::CarmaLifecycleNode(options)
+ {
+
+
+ }
+
+ carma_ros2_utils::CallbackReturn Lanelet2MapVisualization::handle_on_configure(const rclcpp_lifecycle::State &)
+ {
+
+ // Set Publisher
+ // NOTE: Currently, intra-process comms must be disabled for the following two publishers that are transient_local: https://github.com/ros2/rclcpp/issues/1753
+ rclcpp::PublisherOptions intra_proc_disabled;
+ intra_proc_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable; // Disable intra-process comms for this PublisherOptions object
+ // Create a publisher that will send all previously published messages to late-joining subscribers ONLY If the subscriber is transient_local too
+ auto pub_qos_transient_local = rclcpp::QoS(rclcpp::KeepLast(1)); // A publisher with this QoS will store the "Last" message that it has sent on the topic
+ pub_qos_transient_local.transient_local();
+ g_map_pub = create_publisher("lanelet2_map_viz", pub_qos_transient_local, intra_proc_disabled);
+
+ //Set Subscriber
+ rclcpp::SubscriptionOptions sub_options;
+ sub_options.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
+ auto sub_qos_transient_local = rclcpp::QoS(rclcpp::KeepLast(1));
+ sub_qos_transient_local.transient_local();
+ bin_map_sub = create_subscription("lanelet_map_bin", sub_qos_transient_local, std::bind(&Lanelet2MapVisualization::binMapCallback, this, std::placeholders::_1), sub_options);
+
+ return CallbackReturn::SUCCESS;
+ }
+
+
+ void Lanelet2MapVisualization::insertMarkerArray(visualization_msgs::msg::MarkerArray* a1, const visualization_msgs::msg::MarkerArray& a2)
+ {
+ a1->markers.insert(a1->markers.end(), a2.markers.begin(), a2.markers.end());
+ }
+
+ void Lanelet2MapVisualization::setColor(std_msgs::msg::ColorRGBA* cl, double r, double g, double b, double a)
+ {
+ cl->r = r;
+ cl->g = g;
+ cl->b = b;
+ cl->a = a;
+ }
+
+ void Lanelet2MapVisualization::binMapCallback(const autoware_lanelet2_msgs::msg::MapBin::SharedPtr msg)
+ {
+ lanelet::LaneletMapPtr viz_lanelet_map(new lanelet::LaneletMap);
+
+ lanelet::utils::conversion::fromBinMsg(*msg, viz_lanelet_map);
+ RCLCPP_INFO(get_logger(), "Map loaded");
+
+ // get lanelets etc to visualize
+ lanelet::ConstLanelets all_lanelets = lanelet::utils::query::laneletLayer(viz_lanelet_map);
+ lanelet::ConstLanelets road_lanelets = lanelet::utils::query::roadLanelets(all_lanelets);
+ lanelet::ConstLanelets crosswalk_lanelets = lanelet::utils::query::crosswalkLanelets(all_lanelets);
+
+ std::vector tl_stop_lines = lanelet::utils::query::getTrafficLightStopLines(road_lanelets);
+ std::vector ss_stop_lines = lanelet::utils::query::getStopSignStopLines(road_lanelets);
+ std::vector tl_reg_elems = lanelet::utils::query::trafficLights(all_lanelets);
+ std::vector aw_tl_reg_elems =
+ lanelet::utils::query::autowareTrafficLights(all_lanelets);
+
+ std_msgs::msg::ColorRGBA cl_road, cl_cross, cl_ll_borders, cl_tl_stoplines, cl_ss_stoplines, cl_trafficlights;
+ setColor(&cl_road, 0.2, 0.7, 0.7, 0.3);
+ setColor(&cl_cross, 0.2, 0.7, 0.2, 0.3);
+ setColor(&cl_ll_borders, 1.0, 1.0, 1.0, 1.0);
+ setColor(&cl_tl_stoplines, 1.0, 0.5, 0.0, 0.5);
+ setColor(&cl_ss_stoplines, 1.0, 0.0, 0.0, 0.5);
+ setColor(&cl_trafficlights, 0.7, 0.7, 0.7, 0.8);
+
+ visualization_msgs::msg::MarkerArray map_marker_array;
+
+ insertMarkerArray(&map_marker_array, lanelet::visualization::laneletsBoundaryAsMarkerArray(
+ road_lanelets, cl_ll_borders, g_viz_lanelets_centerline));
+ insertMarkerArray(&map_marker_array, lanelet::visualization::laneletsAsTriangleMarkerArray(
+ "road_lanelets", road_lanelets, cl_road));
+ insertMarkerArray(&map_marker_array, lanelet::visualization::laneletsAsTriangleMarkerArray(
+ "crosswalk_lanelets", crosswalk_lanelets, cl_cross));
+ insertMarkerArray(&map_marker_array, lanelet::visualization::laneletDirectionAsMarkerArray(
+ road_lanelets));
+ insertMarkerArray(&map_marker_array, lanelet::visualization::lineStringsAsMarkerArray(
+ tl_stop_lines, "traffic_light_stop_lines", cl_tl_stoplines, 0.5));
+ insertMarkerArray(&map_marker_array, lanelet::visualization::lineStringsAsMarkerArray(
+ ss_stop_lines, "stop_sign_stop_lines", cl_ss_stoplines, 0.5));
+ insertMarkerArray(&map_marker_array, lanelet::visualization::autowareTrafficLightsAsMarkerArray(
+ aw_tl_reg_elems, cl_trafficlights));
+
+ RCLCPP_INFO(get_logger(), "Visualizing lanelet2 map with %lu lanelets, %lu stop lines, and %lu traffic lights",
+ all_lanelets.size(), tl_stop_lines.size() + ss_stop_lines.size(), aw_tl_reg_elems.size());
+
+ g_map_pub->publish(map_marker_array);
+ }
+
+
+} //namespace lanelet2_map_visualization
+
+
+#include "rclcpp_components/register_node_macro.hpp"
+
+// Register the component with class_loader
+RCLCPP_COMPONENTS_REGISTER_NODE(lanelet2_map_visualization::Lanelet2MapVisualization)
\ No newline at end of file
diff --git a/common/map_file_ros2/nodes/lanelet2_map_visualization/lanelet2_map_visualization_node.cpp b/common/map_file_ros2/nodes/lanelet2_map_visualization/lanelet2_map_visualization_node.cpp
new file mode 100644
index 00000000000..cbd21afa6f9
--- /dev/null
+++ b/common/map_file_ros2/nodes/lanelet2_map_visualization/lanelet2_map_visualization_node.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include
+#include
+
+int main(int argc, char **argv)
+{
+ rclcpp::init(argc, argv);
+ auto node = std::make_shared(rclcpp::NodeOptions());
+
+ rclcpp::executors::MultiThreadedExecutor executor;
+ executor.add_node(node->get_node_base_interface());
+ executor.spin();
+
+ rclcpp::shutdown();
+ return 0;
+}
\ No newline at end of file
diff --git a/common/map_file_ros2/nodes/map_param_loader/map_param_loader.cpp b/common/map_file_ros2/nodes/map_param_loader/map_param_loader.cpp
new file mode 100644
index 00000000000..73a20050d08
--- /dev/null
+++ b/common/map_file_ros2/nodes/map_param_loader/map_param_loader.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "map_param_loader.hpp"
+
+
+namespace map_param_loader
+{
+ namespace std_ph = std::placeholders;
+
+ MapParamLoader::MapParamLoader(const rclcpp::NodeOptions &options) : carma_ros2_utils::CarmaLifecycleNode(options)
+ {
+
+ }
+
+ carma_ros2_utils::CallbackReturn MapParamLoader::handle_on_configure(const rclcpp_lifecycle::State &)
+ {
+ lanelet2_filename = declare_parameter("file_name", lanelet2_filename);
+ broadcast_earth_frame = declare_parameter("broadcast_earth_frame", broadcast_earth_frame);
+
+ // NOTE: Currently, intra-process comms must be disabled for the following two publishers that are transient_local: https://github.com/ros2/rclcpp/issues/1753
+ rclcpp::PublisherOptions intra_proc_disabled;
+ intra_proc_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable; // Disable intra-process comms for this PublisherOptions object
+
+ // Create a publisher that will send all previously published messages to late-joining subscribers ONLY If the subscriber is transient_local too
+ auto pub_qos_transient_local = rclcpp::QoS(rclcpp::KeepAll()); // A publisher with this QoS will store all messages that it has sent on the topic
+ pub_qos_transient_local.transient_local(); // A publisher with this QoS will re-send all (when KeepAll is used) messages to all late-joining subscribers
+ // NOTE: The subscriber's QoS must be set to transient_local() as well for earlier messages to be resent to the later-joiner.
+
+ georef_pub_ = this->create_publisher("georeference", pub_qos_transient_local, intra_proc_disabled);
+
+ return CallbackReturn::SUCCESS;
+ }
+
+ carma_ros2_utils::CallbackReturn MapParamLoader::handle_on_activate(const rclcpp_lifecycle::State &)
+ {
+ int projector_type = 1; // default value
+
+ // Parse geo reference info from the lanelet map (.osm)
+ lanelet::io_handlers::AutowareOsmParser::parseMapParams(lanelet2_filename, &projector_type, &target_frame_);
+
+ if (broadcast_earth_frame) {
+ // Get the transform to ecef (when parsed target_frame is map_frame)
+ tf2::Transform tf = getTransform(target_frame_);
+
+ // Broadcast the transform
+ broadcastTransform(tf);
+ }
+
+ timer_ = this->create_wall_timer(std::chrono::milliseconds(500), std::bind(&MapParamLoader::timer_callback, this));
+
+ return CallbackReturn::SUCCESS;
+ }
+
+ void MapParamLoader::timer_callback()
+ {
+ std_msgs::msg::String georef_msg;
+ georef_msg.data = target_frame_;
+ georef_pub_->publish(georef_msg);
+ }
+
+
+ // Get transform from map_frame coord to ecef_frame coord using respective proj strings
+ tf2::Transform MapParamLoader::getTransform(const std::string& map_frame)
+ {
+
+ lanelet::projection::LocalFrameProjector local_projector(map_frame.c_str());
+
+ tf2::Matrix3x3 rot_mat, id = tf2::Matrix3x3::getIdentity();
+ lanelet::BasicPoint3d origin_in_map{0,0,0}, origin_in_ecef;
+
+ // Solve map_to_ecef transformation
+ // Get translation of map with respect to ecef
+ origin_in_ecef = local_projector.projectECEF(origin_in_map, 1);
+
+ // Solve rotation matrix using (1,0,0), (0,1,0), (0,0,1) vectors in map
+ for (auto i = 0; i < 3; i ++)
+ {
+ lanelet::BasicPoint3d rot_mat_row = local_projector.projectECEF(lanelet::BasicPoint3d{id[i][0],id[i][1],id[i][2]}, 1) - origin_in_ecef;
+ rot_mat[i][0] = rot_mat_row[0];
+ rot_mat[i][1] = rot_mat_row[1];
+ rot_mat[i][2] = rot_mat_row[2];
+ }
+ // Transpose due to the way tf2::Matrix3x3 supposed to be stored.
+ tf2::Vector3 v_origin_in_ecef{origin_in_ecef[0],origin_in_ecef[1],origin_in_ecef[2]};
+ tf2::Transform tf(rot_mat.transpose(), v_origin_in_ecef);
+
+ // map_to_ecef tf
+ return tf;
+ }
+
+ // broadcast the transform to tf_static topic
+ void MapParamLoader::broadcastTransform(const tf2::Transform& transform)
+ {
+ static tf2_ros::StaticTransformBroadcaster br(this);
+ geometry_msgs::msg::TransformStamped transformStamped;
+
+ tf2::Vector3 translation = transform.getOrigin();
+ tf2::Quaternion rotation = transform.getRotation();
+
+ transformStamped.header.stamp = rclcpp::Time(0,0); //TODO:: Check if node->now() needed
+ transformStamped.header.frame_id = "earth";
+ transformStamped.child_frame_id = "map";
+ transformStamped.transform.translation.x = translation[0];
+ transformStamped.transform.translation.y = translation[1];
+ transformStamped.transform.translation.z = translation[2];
+ transformStamped.transform.rotation.x = rotation[0];
+ transformStamped.transform.rotation.y = rotation[1];
+ transformStamped.transform.rotation.z = rotation[2];
+ transformStamped.transform.rotation.w = rotation[3];
+ br.sendTransform(transformStamped);
+ }
+
+} // namespace map_param_loader
+
+
+#include "rclcpp_components/register_node_macro.hpp"
+
+// Register the component with class_loader
+RCLCPP_COMPONENTS_REGISTER_NODE(map_param_loader::MapParamLoader)
\ No newline at end of file
diff --git a/common/map_file_ros2/nodes/map_param_loader/map_param_loader_node.cpp b/common/map_file_ros2/nodes/map_param_loader/map_param_loader_node.cpp
new file mode 100644
index 00000000000..a72083c8f74
--- /dev/null
+++ b/common/map_file_ros2/nodes/map_param_loader/map_param_loader_node.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include
+#include "map_param_loader.hpp"
+
+int main(int argc, char **argv)
+{
+ rclcpp::init(argc, argv);
+ auto node = std::make_shared(rclcpp::NodeOptions());
+ rclcpp::executors::MultiThreadedExecutor executor;
+ executor.add_node(node->get_node_base_interface());
+ executor.spin();
+
+ rclcpp::shutdown();
+ return 0;
+}
diff --git a/common/map_file_ros2/nodes/points_map_loader/points_map_loader.cpp b/common/map_file_ros2/nodes/points_map_loader/points_map_loader.cpp
new file mode 100644
index 00000000000..a81110106a5
--- /dev/null
+++ b/common/map_file_ros2/nodes/points_map_loader/points_map_loader.cpp
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include
+// #include
+
+
+namespace points_map_loader {
+
+
+ PointsMapLoader::PointsMapLoader(const rclcpp::NodeOptions &options) : carma_ros2_utils::CarmaLifecycleNode(options)
+ {
+ // Initialize parameters
+ update_rate = declare_parameter("update_rate", update_rate);
+
+ area = declare_parameter("area", area);
+ load_type = declare_parameter("load_type", load_type);
+ path_area_list = declare_parameter("path_area_list", path_area_list);
+ // parameter as lists
+ declare_parameter("pcd_path_parameter", pcd_path);
+
+ host_name = declare_parameter("host_name", host_name);
+ port = declare_parameter("port", port);
+ user = declare_parameter("user", user);
+ password = declare_parameter("password", password);
+
+ }
+
+ carma_ros2_utils::CallbackReturn PointsMapLoader::handle_on_configure(const rclcpp_lifecycle::State &prev_state)
+ {
+ // Get Parameters
+ get_parameter("update_rate", update_rate);
+ get_parameter("area", area);
+ get_parameter("load_type", load_type);
+ get_parameter("path_area_list", path_area_list);
+
+ // path pcd - list of strings
+ rclcpp::Parameter pcd_path_parameter = get_parameter("pcd_path_parameter");
+ pcd_path = pcd_path_parameter.as_string_array();
+
+
+ // Create publishers
+ // NOTE: Currently, intra-process comms must be disabled for the following two publishers that are transient_local: https://github.com/ros2/rclcpp/issues/1753
+ rclcpp::PublisherOptions intra_proc_disabled;
+ intra_proc_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable; // Disable intra-process comms for this PublisherOptions object
+ // Create a publisher that will send all previously published messages to late-joining subscribers ONLY If the subscriber is transient_local too
+ auto pub_qos_transient_local = rclcpp::QoS(rclcpp::KeepLast(1)); // A publisher with this QoS will store the "Last" message that it has sent on the topic
+ pub_qos_transient_local.transient_local();
+
+ pcd_pub = create_publisher("points_map", pub_qos_transient_local, intra_proc_disabled); //Make latched
+ stat_pub = create_publisher("pmap_stat", pub_qos_transient_local, intra_proc_disabled); //Make latched
+
+ return CallbackReturn::SUCCESS;
+ }
+
+ carma_ros2_utils::CallbackReturn PointsMapLoader::handle_on_activate(const rclcpp_lifecycle::State &prev_state)
+ {
+ timer_ = this->create_wall_timer(std::chrono::milliseconds(500), std::bind(&PointsMapLoader::timer_callback, this));
+ return CallbackReturn::SUCCESS;
+ }
+
+ void PointsMapLoader::timer_callback()
+ {
+ if (get_current_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE){
+ run();
+ timer_->cancel();
+ }
+ }
+
+ void PointsMapLoader::run(){
+ if (load_type == "noupdate")
+ margin = -1;
+ else if (area == "1x1")
+ margin = 0;
+ else if (area == "3x3")
+ margin = MARGIN_UNIT * 1;
+ else if (area == "5x5")
+ margin = MARGIN_UNIT * 2;
+ else if (area == "7x7")
+ margin = MARGIN_UNIT * 3;
+ else if (area == "9x9")
+ margin = MARGIN_UNIT * 4;
+ else {
+ print_usage();
+ RCLCPP_ERROR_STREAM(get_logger(), "Invalid area argument");
+
+ }
+
+ std::string arealist_path;
+ std::vector pcd_paths;
+ if (margin < 0) {
+ can_download = false;
+ // If area = no_update get pcd paths
+ pcd_paths.insert(pcd_paths.end(), pcd_path.begin(), pcd_path.end());
+ } else {
+ if (load_type == "download")
+ {
+ can_download = true;
+
+ get_parameter("host_name", host_name);
+ get_parameter("port", port);
+ get_parameter("user", user);
+ get_parameter("password", password);
+
+ gf = GetFile(host_name, port, user, password);
+ }
+ else{
+ can_download = false;
+ arealist_path += path_area_list;
+ pcd_paths.insert(pcd_paths.end(), pcd_path.begin(), pcd_path.end());
+ }
+
+ }
+
+ stat_msg.data = false;
+ stat_pub->publish(stat_msg);
+ sensor_msgs::msg::PointCloud2 pcd;
+
+ if (margin < 0) {
+ int err = 0;
+ pcd = create_pcd(pcd_paths, &err);
+ RCLCPP_INFO_STREAM(get_logger(), "Publishing pcd");
+ publish_pcd(pcd, &err);
+ } else{
+ fallback_rate = update_rate * 2; // XXX better way?
+
+ // Create subscribers
+ gnss_sub = create_subscription("gnss_pose", 1000, std::bind(&PointsMapLoader::publish_gnss_pcd, this, std::placeholders::_1));
+ current_sub = create_subscription("current_pose", 1000, std::bind(&PointsMapLoader::publish_current_pcd, this, std::placeholders::_1));
+ initial_sub = create_subscription("initialpose", 1, std::bind(&PointsMapLoader::publish_dragged_pcd, this, std::placeholders::_1));
+
+ if (can_download){
+ waypoints_sub = create_subscription("traffic_waypoints_array", 1, std::bind(&PointsMapLoader::request_lookahead_download, this, std::placeholders::_1));
+ try {
+ std::thread downloader([this]{this->download_map();});
+ downloader.detach();
+ } catch (std::exception &ex) {
+ RCLCPP_ERROR_STREAM(get_logger(), "failed to create thread from " << ex.what());
+ }
+ } else{
+ AreaList areas = read_arealist(arealist_path);
+ for (const Area& area : areas) {
+ // Check if the user entered pcd paths in addition to the arealist.txt file
+ if (pcd_paths.size() > 0) {
+ // Only load cells which the user specified
+ for (const std::string& path : pcd_paths) {
+ if (path == area.path)
+ cache_arealist(area, downloaded_areas);
+ }
+ } else {
+ // The user did not specify any cells to load all the cells contained in the arealist.txt file
+ cache_arealist(area, downloaded_areas);
+ }
+ }
+ }
+
+ gnss_time = current_time = this->now();
+ }
+ }
+
+ void PointsMapLoader::enqueue(const geometry_msgs::msg::Point& p)
+ {
+ std::unique_lock lock(mtx_);
+ queue_.push(p);
+ cv_.notify_all();
+ }
+
+ void PointsMapLoader::enqueue_look_ahead(const geometry_msgs::msg::Point& p)
+ {
+ std::unique_lock lock(mtx_);
+ look_ahead_queue_.push(p);
+ cv_.notify_all();
+ }
+
+ void PointsMapLoader::clear()
+ {
+ std::unique_lock lock(mtx_);
+ while (!queue_.empty())
+ queue_.pop();
+ }
+
+ void PointsMapLoader::clear_look_ahead()
+ {
+ std::unique_lock lock(mtx_);
+ while (!look_ahead_queue_.empty())
+ look_ahead_queue_.pop();
+ }
+
+ geometry_msgs::msg::Point PointsMapLoader::dequeue()
+ {
+ std::unique_lock lock(mtx_);
+ while (queue_.empty() && look_ahead_queue_.empty())
+ cv_.wait(lock);
+ if (!queue_.empty()) {
+ geometry_msgs::msg::Point p = queue_.front();
+ queue_.pop();
+ return p;
+ } else {
+ geometry_msgs::msg::Point p = look_ahead_queue_.front();
+ look_ahead_queue_.pop();
+ return p;
+ }
+ }
+
+
+ Tbl PointsMapLoader::read_csv(const std::string& path)
+ {
+ std::ifstream ifs(path.c_str());
+ std::string line;
+ Tbl ret;
+ while (std::getline(ifs, line)) {
+ std::istringstream iss(line);
+ std::string col;
+ std::vector cols;
+ while (std::getline(iss, col, ','))
+ cols.push_back(col);
+ ret.push_back(cols);
+ }
+ return ret;
+ }
+
+ void PointsMapLoader::write_csv(const std::string& path, const Tbl& tbl)
+ {
+ std::ofstream ofs(path.c_str());
+ for (const std::vector& cols : tbl) {
+ std::string line;
+ for (size_t i = 0; i < cols.size(); ++i) {
+ if (i == 0)
+ line += cols[i];
+ else
+ line += "," + cols[i];
+ }
+ ofs << line << std::endl;
+ }
+ }
+
+ AreaList PointsMapLoader::read_arealist(const std::string& path)
+ {
+ Tbl tbl = read_csv(path);
+ AreaList ret;
+ for (const std::vector& cols : tbl) {
+ Area area;
+ area.path = cols[0];
+ area.x_min = std::stod(cols[1]);
+ area.y_min = std::stod(cols[2]);
+ area.z_min = std::stod(cols[3]);
+ area.x_max = std::stod(cols[4]);
+ area.y_max = std::stod(cols[5]);
+ area.z_max = std::stod(cols[6]);
+ ret.push_back(area);
+ }
+ return ret;
+ }
+
+
+ void PointsMapLoader::write_arealist(const std::string& path, const AreaList& areas)
+ {
+ Tbl tbl;
+ for (const Area& area : areas) {
+ std::vector cols;
+ cols.push_back(area.path);
+ cols.push_back(std::to_string(area.x_min));
+ cols.push_back(std::to_string(area.y_min));
+ cols.push_back(std::to_string(area.z_min));
+ cols.push_back(std::to_string(area.x_max));
+ cols.push_back(std::to_string(area.y_max));
+ cols.push_back(std::to_string(area.z_max));
+ tbl.push_back(cols);
+ }
+ write_csv(path, tbl);
+ }
+
+ bool PointsMapLoader::is_downloaded(const std::string& path)
+ {
+ struct stat st;
+ return (stat(path.c_str(), &st) == 0);
+ }
+
+ bool PointsMapLoader::is_in_area(double x, double y, const Area& area, double m)
+ {
+ return ((area.x_min - m) <= x && x <= (area.x_max + m) && (area.y_min - m) <= y && y <= (area.y_max + m));
+ }
+
+ std::string PointsMapLoader::create_location(int x, int y)
+ {
+ x -= x % ROUNDING_UNIT;
+ y -= y % ROUNDING_UNIT;
+ return ("data/map/" + std::to_string(y) + "/" + std::to_string(x) + "/pointcloud/");
+ }
+
+ void PointsMapLoader::cache_arealist(const Area& area, AreaList& areas)
+ {
+ for (const Area& a : areas) {
+ if (a.path == area.path)
+ return;
+ }
+ areas.push_back(area);
+ }
+
+ int PointsMapLoader::download(GetFile gf, const std::string& tmp, const std::string& loc, const std::string& filename)
+ {
+ std::string pathname;
+ pathname += tmp;
+ std::istringstream iss(loc);
+ std::string col;
+ while (std::getline(iss, col, '/')) {
+ pathname += col + "/";
+ mkdir(pathname.c_str(), 0755);
+ }
+
+ return gf.GetHTTPFile(loc + filename);
+ }
+
+ void PointsMapLoader::download_map()
+ {
+ while (true) {
+ geometry_msgs::msg::Point p = dequeue();
+
+ int x = static_cast(p.x);
+ int y = static_cast(p.y);
+ int x_min = static_cast(p.x - margin);
+ int y_min = static_cast(p.y - margin);
+ int x_max = static_cast(p.x + margin);
+ int y_max = static_cast(p.y + margin);
+
+ std::vector locs;
+ locs.push_back(create_location(x, y));
+ locs.push_back(create_location(x_min, y_min));
+ locs.push_back(create_location(x_min, y_max));
+ locs.push_back(create_location(x_max, y_min));
+ locs.push_back(create_location(x_max, y_max));
+ for (const std::string& loc : locs) { // XXX better way?
+ std::string arealist_path = TEMPORARY_DIRNAME + loc + AREALIST_FILENAME;
+
+ bool cached = false;
+ for (const std::string& path : cached_arealist_paths) {
+ if (path == arealist_path) {
+ cached = true;
+ break;
+ }
+ }
+ if (cached)
+ continue;
+
+ AreaList areas;
+ if (is_downloaded(arealist_path))
+ areas = read_arealist(arealist_path);
+ else {
+ if (download(gf, TEMPORARY_DIRNAME, loc, AREALIST_FILENAME) != 0)
+ continue;
+ areas = read_arealist(arealist_path);
+ for (Area& area : areas)
+ area.path = TEMPORARY_DIRNAME + loc + basename(area.path.c_str());
+ write_arealist(arealist_path, areas);
+ }
+ for (const Area& area : areas)
+ cache_arealist(area, all_areas);
+ cached_arealist_paths.push_back(arealist_path);
+ }
+
+ for (const Area& area : all_areas) {
+ if (is_in_area(p.x, p.y, area, margin)) {
+ int x_area = static_cast(area.x_max - MARGIN_UNIT);
+ int y_area = static_cast(area.y_max - MARGIN_UNIT);
+ std::string loc = create_location(x_area, y_area);
+ if (is_downloaded(area.path) ||
+ download(gf, TEMPORARY_DIRNAME, loc, basename(area.path.c_str())) == 0) {
+ std::unique_lock lock(downloaded_areas_mtx);
+ cache_arealist(area, downloaded_areas);
+ }
+ }
+ }
+ }
+ }
+
+
+ sensor_msgs::msg::PointCloud2 PointsMapLoader::create_pcd(const geometry_msgs::msg::Point& p)
+ {
+ sensor_msgs::msg::PointCloud2 pcd, part;
+ std::unique_lock lock(downloaded_areas_mtx);
+ for (const Area& area : downloaded_areas) {
+ if (is_in_area(p.x, p.y, area, margin)) {
+ if (pcd.width == 0)
+ pcl::io::loadPCDFile(area.path.c_str(), pcd);
+ else {
+ pcl::io::loadPCDFile(area.path.c_str(), part);
+ pcd.width += part.width;
+ pcd.row_step += part.row_step;
+ pcd.data.insert(pcd.data.end(), part.data.begin(), part.data.end());
+ }
+ }
+ }
+
+ return pcd;
+ }
+
+ sensor_msgs::msg::PointCloud2 PointsMapLoader::create_pcd(const std::vector& pcd_paths, int* ret_err = NULL)
+ {
+ sensor_msgs::msg::PointCloud2 pcd, part;
+ for (const std::string& path : pcd_paths) {
+ // Following outputs are used for progress bar of Runtime Manager.
+ if (pcd.width == 0) {
+ if (pcl::io::loadPCDFile(path.c_str(), pcd) == -1) {
+ std::cerr << "load failed " << path << std::endl;
+ if (ret_err) *ret_err = 1;
+ }
+ } else {
+ if (pcl::io::loadPCDFile(path.c_str(), part) == -1) {
+ std::cerr << "load failed " << path << std::endl;
+ if (ret_err) *ret_err = 1;
+ }
+ pcd.width += part.width;
+ pcd.row_step += part.row_step;
+ pcd.data.insert(pcd.data.end(), part.data.begin(), part.data.end());
+ }
+ std::cerr << "load " << path << std::endl;
+ if (!rclcpp::ok()) break;
+ }
+
+ return pcd;
+ }
+
+ void PointsMapLoader::publish_pcd(sensor_msgs::msg::PointCloud2 pcd, const int* errp = NULL)
+ {
+
+ if (pcd.width != 0) {
+ pcd.header.frame_id = "map";
+ pcd_pub->publish(pcd);
+
+ if (errp == NULL || *errp == 0) {
+ stat_msg.data = true;
+ stat_pub->publish(stat_msg);
+ }
+ }
+ }
+
+ void PointsMapLoader::publish_gnss_pcd(const geometry_msgs::msg::PoseStamped::SharedPtr msg)
+ {
+ rclcpp::Time now = this->now();
+ if (((now - current_time).seconds() * 1000) < fallback_rate)
+ return;
+ if (((now - gnss_time).seconds() * 1000) < update_rate)
+ return;
+ gnss_time = now;
+
+ if (can_download)
+ enqueue(msg->pose.position);
+
+ publish_pcd(create_pcd(msg->pose.position));
+ }
+
+ void PointsMapLoader::publish_current_pcd(const geometry_msgs::msg::PoseStamped::SharedPtr msg)
+ {
+ rclcpp::Time now = this->now();
+ if (((now - current_time).seconds() * 1000) < update_rate)
+ return;
+ current_time = now;
+
+ if (can_download)
+ enqueue(msg->pose.position);
+
+ publish_pcd(create_pcd(msg->pose.position));
+ }
+
+ void PointsMapLoader::publish_dragged_pcd(const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr msg)
+ {
+ tf2_ros::Buffer buffer(this->get_clock());
+ tf2_ros::TransformListener listener(buffer);
+ geometry_msgs::msg::TransformStamped tf_geom;
+ tf2::Transform transform;
+ try {
+ rclcpp::Time zero = this->now();
+ tf_geom = buffer.lookupTransform("map", msg->header.frame_id, zero, rclcpp::Duration(10,0));
+ } catch (tf2::TransformException &ex) {
+ RCLCPP_ERROR_STREAM(get_logger(),"failed to create transform from " << ex.what());
+ }
+
+ // ros1 implementation called getOrigin from returned transform
+ // ros2 implementation fills getOrigin in geom msg: https://github.com/ros2/geometry2/blob/d1dc38b8a0e706fbd67a800a241fee950fce39f4/tf2/src/buffer_core.cpp#L636
+ geometry_msgs::msg::Point p;
+ p.x = msg->pose.pose.position.x + tf_geom.transform.translation.x;
+ p.y = msg->pose.pose.position.y + tf_geom.transform.translation.y;
+
+ if (can_download)
+ enqueue(p);
+
+ publish_pcd(create_pcd(p));
+ }
+
+ void PointsMapLoader::request_lookahead_download(const autoware_msgs::msg::LaneArray::SharedPtr msg)
+ {
+ clear_look_ahead();
+
+ for (const autoware_msgs::msg::Lane& l : msg->lanes) {
+ size_t end = l.waypoints.size() - 1;
+ double distance = 0;
+ double threshold = (MARGIN_UNIT / 2) + margin; // XXX better way?
+ for (size_t i = 0; i <= end; ++i) {
+ if (i == 0 || i == end) {
+ geometry_msgs::msg::Point p;
+ p.x = l.waypoints[i].pose.pose.position.x;
+ p.y = l.waypoints[i].pose.pose.position.y;
+ enqueue_look_ahead(p);
+ } else {
+ geometry_msgs::msg::Point p1, p2;
+ p1.x = l.waypoints[i].pose.pose.position.x;
+ p1.y = l.waypoints[i].pose.pose.position.y;
+ p2.x = l.waypoints[i - 1].pose.pose.position.x;
+ p2.y = l.waypoints[i - 1].pose.pose.position.y;
+ distance += hypot(p2.x - p1.x, p2.y - p1.y);
+ if (distance > threshold) {
+ enqueue_look_ahead(p1);
+ distance = 0;
+ }
+ }
+ }
+ }
+ }
+
+
+} // namespace
+
+
+#include "rclcpp_components/register_node_macro.hpp"
+
+// Register the component with class_loader
+RCLCPP_COMPONENTS_REGISTER_NODE(points_map_loader::PointsMapLoader)
\ No newline at end of file
diff --git a/common/map_file_ros2/nodes/points_map_loader/points_map_loader_node.cpp b/common/map_file_ros2/nodes/points_map_loader/points_map_loader_node.cpp
new file mode 100644
index 00000000000..10cff2ab6b7
--- /dev/null
+++ b/common/map_file_ros2/nodes/points_map_loader/points_map_loader_node.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include
+#include
+
+int main(int argc, char **argv)
+{
+ rclcpp::init(argc, argv);
+
+
+ if (argc < 3) {
+ points_map_loader::print_usage();
+ return EXIT_FAILURE;
+ }
+
+ auto node = std::make_shared(rclcpp::NodeOptions());
+
+ rclcpp::executors::MultiThreadedExecutor executor;
+ executor.add_node(node->get_node_base_interface());
+ executor.spin();
+
+ rclcpp::shutdown();
+ return 0;
+}
diff --git a/common/map_file_ros2/package.xml b/common/map_file_ros2/package.xml
new file mode 100644
index 00000000000..a8639acdce6
--- /dev/null
+++ b/common/map_file_ros2/package.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+ map_file_ros2
+ 4.0.0
+ The map_file package
+
+ carma
+
+ Apache 2.0
+
+ ament_cmake
+ carma_cmake_common
+ ament_auto_cmake
+
+ autoware_msgs
+ rclcpp
+ rclcpp_components
+ std_msgs
+ geometry_msgs
+ curl
+ libpcl-all-dev
+
+ tf2_geometry_msgs
+ tf2_ros
+ tf2
+ visualization_msgs
+ lanelet2_extension
+ carma_ros2_utils
+ autoware_lanelet2_ros_interface
+
+
+ ament_lint_auto
+ ament_cmake_gtest
+
+ launch
+ launch_ros
+
+
+ ament_cmake
+
+
diff --git a/common/map_file_ros2/test/test_get_transform.cpp b/common/map_file_ros2/test/test_get_transform.cpp
new file mode 100644
index 00000000000..9b74d3241f6
--- /dev/null
+++ b/common/map_file_ros2/test/test_get_transform.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+
+#include
+#include
+
+
+TEST(GetTransformTest, testECEFPoints)
+{
+ //Example frames
+ std::string map_frame = "+proj=tmerc +lat_0=38.95197911150576 +lon_0=-77.14835128349988 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +vunits=m +no_defs";
+
+ rclcpp::NodeOptions options;
+ auto worker_node = std::make_shared(options);
+ worker_node->configure(); //Call configure state transition
+ worker_node->activate(); //Call activate state transition to get not read for runtime
+
+ //Points are calculated using cs2cs command line function of proj library.
+ tf2::Transform tf = worker_node->getTransform(map_frame);
+ tf2::Vector3 origin = tf(tf2::Vector3{0,0,0});
+ ASSERT_FLOAT_EQ (origin[0],1104726.07);
+ ASSERT_FLOAT_EQ (origin[1],-4842261.44);
+ ASSERT_FLOAT_EQ (origin[2],3988172.62);
+
+ tf2::Vector3 e1 = tf(tf2::Vector3{1,0,0});
+ ASSERT_FLOAT_EQ (e1[0],1104727.04);
+ ASSERT_FLOAT_EQ (e1[1],-4842261.22);
+ ASSERT_FLOAT_EQ (e1[2],3988172.62);
+
+ tf2::Vector3 random = tf(tf2::Vector3{4,3,5});
+ ASSERT_FLOAT_EQ (random[0],1104730.41);
+ ASSERT_FLOAT_EQ (random[1],-4842262.50);
+ ASSERT_FLOAT_EQ (random[2],3988178.10);
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ //Initialize ROS
+ rclcpp::init(argc, argv);
+
+ bool success = RUN_ALL_TESTS();
+
+ //shutdown ROS
+ rclcpp::shutdown();
+
+ return success;
+}
+
diff --git a/core_perception/dead_reckoner/CMakeLists.txt b/core_perception/dead_reckoner/CMakeLists.txt
new file mode 100644
index 00000000000..99de04dc1c8
--- /dev/null
+++ b/core_perception/dead_reckoner/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Copyright (C) 2023 LEIDOS.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+cmake_minimum_required(VERSION 3.5)
+project(dead_reckoner)
+
+# Declare carma package and check ROS version
+find_package(carma_cmake_common REQUIRED)
+carma_check_ros_version(2)
+carma_package()
+
+## Find dependencies using ament auto
+find_package(ament_cmake_auto REQUIRED)
+ament_auto_find_build_dependencies()
+
+
+# Includes
+include_directories(
+ include
+)
+
+# Name build targets
+set(node_exec dead_reckoner_exec)
+set(node_lib dead_reckoner)
+
+# Build
+ament_auto_add_library(${node_lib} SHARED
+ src/dead_reckoner.cpp
+)
+
+ament_auto_add_executable(${node_exec}
+ src/main.cpp
+)
+
+# Register component
+rclcpp_components_register_nodes(dead_reckoner "dead_reckoner::DeadReckoner")
+
+ament_auto_package(
+ INSTALL_TO_SHARE launch
+)
\ No newline at end of file
diff --git a/core_perception/deadreckoner/README.md b/core_perception/dead_reckoner/README.md
similarity index 92%
rename from core_perception/deadreckoner/README.md
rename to core_perception/dead_reckoner/README.md
index 53ac1a01266..24b1cf2cb11 100644
--- a/core_perception/deadreckoner/README.md
+++ b/core_perception/dead_reckoner/README.md
@@ -1,3 +1,3 @@
-## Deadreckoner Package
+## DeadReckoner Package
This package is responsible for providing odometry to NDT Matching. Currently only the Twist field of the nav_msgs::Odometry message is populated.
This node was originally developed by AutonomouStuff and the git history can be found in their fork of the Autoware repo (https://github.com/astuff/autoware).
\ No newline at end of file
diff --git a/core_perception/dead_reckoner/include/dead_reckoner.hpp b/core_perception/dead_reckoner/include/dead_reckoner.hpp
new file mode 100644
index 00000000000..f66eadf2277
--- /dev/null
+++ b/core_perception/dead_reckoner/include/dead_reckoner.hpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef DEADRECKONER_H
+#define DEADRECKONER_H
+
+#include
+#include
+#include
+#include
+
+namespace dead_reckoner
+{
+
+class DeadReckoner : public carma_ros2_utils::CarmaLifecycleNode
+{
+public:
+ explicit DeadReckoner(const rclcpp::NodeOptions& options);
+
+ ////
+ // Overrides
+ ////
+ carma_ros2_utils::CallbackReturn handle_on_configure(const rclcpp_lifecycle::State &);
+ carma_ros2_utils::CallbackReturn handle_on_activate(const rclcpp_lifecycle::State &);
+
+private:
+ carma_ros2_utils::SubPtr twist_sub_;
+ carma_ros2_utils::PubPtr odom_pub_;
+
+ void twist_cb(geometry_msgs::msg::TwistStamped::UniquePtr msg);
+};
+
+} // namespace dead_reckoner
+
+#endif // DEADRECKONER_H
diff --git a/core_perception/dead_reckoner/launch/dead_reckoner.launch.py b/core_perception/dead_reckoner/launch/dead_reckoner.launch.py
new file mode 100644
index 00000000000..e3481f555a5
--- /dev/null
+++ b/core_perception/dead_reckoner/launch/dead_reckoner.launch.py
@@ -0,0 +1,63 @@
+# Copyright (C) 2023 LEIDOS.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from launch import LaunchDescription
+from launch_ros.actions import ComposableNodeContainer
+from launch_ros.descriptions import ComposableNode
+from launch.actions import DeclareLaunchArgument
+from launch.substitutions import LaunchConfiguration
+from carma_ros2_utils.launch.get_current_namespace import GetCurrentNamespace
+
+'''
+This file is can be used to launch the CARMA dead_reckoner_node.
+ Though in carma-platform it may be launched directly from the base launch file.
+'''
+
+def generate_launch_description():
+
+ # Declare the log_level launch argument
+ log_level = LaunchConfiguration('log_level')
+ declare_log_level_arg = DeclareLaunchArgument(
+ name ='log_level', default_value='WARN')
+
+ # Launch node(s) in a carma container to allow logging to be configured
+ container = ComposableNodeContainer(
+ package='carma_ros2_utils',
+ name='dead_reckoner_container',
+ namespace=GetCurrentNamespace(),
+ executable='carma_component_container_mt',
+ composable_node_descriptions=[
+
+ # Launch the core node(s)
+ ComposableNode(
+ package='dead_reckoner',
+ plugin='dead_reckoner::DeadReckoner',
+ name='dead_reckoner_node',
+ extra_arguments=[
+ {'use_intra_process_comms': True},
+ {'--log-level' : log_level }
+ ],
+ remappings=[
+ ("current_twist", "/vehicle/twist" ),
+ ("current_odom", "/vehicle/odom")
+ ],
+ ),
+ ]
+ )
+
+ return LaunchDescription([
+ declare_log_level_arg,
+ container
+ ])
+
diff --git a/core_perception/dead_reckoner/package.xml b/core_perception/dead_reckoner/package.xml
new file mode 100644
index 00000000000..eea1f77ee3b
--- /dev/null
+++ b/core_perception/dead_reckoner/package.xml
@@ -0,0 +1,43 @@
+
+
+
+
+ dead_reckoner
+ 4.0.0
+ The dead_reckoner package
+
+ carma
+
+ Apache 2.0
+ ament_cmake
+ carma_cmake_common
+ ament_auto_cmake
+
+ carma_ros2_utils
+ std_msgs
+ nav_msgs
+ geometry_msgs
+ rclcpp
+
+ ament_lint_auto
+ ament_cmake_gtest
+
+ launch
+ launch_ros
+
+
+ ament_cmake
+
+
+
diff --git a/core_perception/dead_reckoner/src/dead_reckoner.cpp b/core_perception/dead_reckoner/src/dead_reckoner.cpp
new file mode 100644
index 00000000000..64ef20a9121
--- /dev/null
+++ b/core_perception/dead_reckoner/src/dead_reckoner.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "dead_reckoner.hpp"
+
+
+namespace dead_reckoner
+{
+
+namespace std_ph = std::placeholders;
+
+DeadReckoner::DeadReckoner(const rclcpp::NodeOptions& options) : carma_ros2_utils::CarmaLifecycleNode(options)
+{
+}
+
+carma_ros2_utils::CallbackReturn DeadReckoner::handle_on_configure(const rclcpp_lifecycle::State &)
+{
+ twist_sub_ = create_subscription("current_twist", 1, std::bind(&DeadReckoner::twist_cb, this, std_ph::_1));
+ odom_pub_ = create_publisher("current_odom", 10);
+ return CallbackReturn::SUCCESS;
+}
+
+carma_ros2_utils::CallbackReturn DeadReckoner::handle_on_activate(const rclcpp_lifecycle::State &)
+{
+ return CallbackReturn::SUCCESS;
+}
+
+void DeadReckoner::twist_cb(geometry_msgs::msg::TwistStamped::UniquePtr msg)
+{
+ // TODO: calculate odom.pose.pose by accumulating
+ nav_msgs::msg::Odometry odom;
+ odom.header = msg->header;
+ odom.twist.twist = msg->twist;
+ odom_pub_->publish(odom);
+}
+
+} // namespace dead_reckoner
+
+
+#include "rclcpp_components/register_node_macro.hpp"
+
+// Register the component with class_loader
+RCLCPP_COMPONENTS_REGISTER_NODE(dead_reckoner::DeadReckoner)
diff --git a/core_perception/dead_reckoner/src/main.cpp b/core_perception/dead_reckoner/src/main.cpp
new file mode 100644
index 00000000000..730fa088733
--- /dev/null
+++ b/core_perception/dead_reckoner/src/main.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+#include
+#include "dead_reckoner.hpp"
+
+int main(int argc, char **argv)
+{
+ rclcpp::init(argc, argv);
+
+ auto node = std::make_shared(rclcpp::NodeOptions());
+
+ rclcpp::executors::MultiThreadedExecutor executor;
+ executor.add_node(node->get_node_base_interface());
+ executor.spin();
+
+ rclcpp::shutdown();
+
+ return 0;
+}
diff --git a/core_perception/deadreckoner/CMakeLists.txt b/core_perception/deadreckoner/CMakeLists.txt
deleted file mode 100644
index 5538a7e0499..00000000000
--- a/core_perception/deadreckoner/CMakeLists.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-cmake_minimum_required(VERSION 2.8.3)
-project(deadreckoner)
-
-find_package(carma_cmake_common REQUIRED)
-carma_check_ros_version(1)
-
-add_compile_options(-std=c++14)
-
-find_package(catkin REQUIRED COMPONENTS
- roscpp
- std_msgs
- nav_msgs
- geometry_msgs
-)
-
-catkin_package(
-# INCLUDE_DIRS include
-# LIBRARIES deadreckoner
-# CATKIN_DEPENDS other_catkin_pkg
-# DEPENDS system_lib
-)
-
-include_directories(
- ${catkin_INCLUDE_DIRS}
-)
-
-add_executable(deadreckoner
- nodes/deadreckoner_node.cpp
- nodes/deadreckoner.cpp
-)
-
-target_link_libraries(deadreckoner
- ${catkin_LIBRARIES}
-)
-
-install(TARGETS
- deadreckoner
- ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
- LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
- RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
-)
-
-install(DIRECTORY launch/
- DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
-)
diff --git a/core_perception/deadreckoner/launch/deadreckoner.launch b/core_perception/deadreckoner/launch/deadreckoner.launch
deleted file mode 100644
index e37fb037688..00000000000
--- a/core_perception/deadreckoner/launch/deadreckoner.launch
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core_perception/deadreckoner/nodes/deadreckoner.cpp b/core_perception/deadreckoner/nodes/deadreckoner.cpp
deleted file mode 100644
index bfd5053e4d7..00000000000
--- a/core_perception/deadreckoner/nodes/deadreckoner.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2017, Nagoya University
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of Autoware nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#include "deadreckoner.h"
-
-DeadRecokner::DeadRecokner() : nh_(), private_nh_("~")
-{
- twist_sub_ = nh_.subscribe("current_twist", 10, &DeadRecokner::callbackFromCurrentTwist, this);
- odom_pub_ = nh_.advertise("current_odom", 10);
-}
-
-DeadRecokner::~DeadRecokner()
-{
-}
-
-void DeadRecokner::callbackFromCurrentTwist(const geometry_msgs::TwistStampedConstPtr& msg)
-{
- // TODO: calurate odom.pose.pose by accumulating
- nav_msgs::Odometry odom;
- odom.header = msg->header;
- odom.twist.twist = msg->twist;
- odom_pub_ .publish(odom);
-}
diff --git a/core_perception/deadreckoner/nodes/deadreckoner.h b/core_perception/deadreckoner/nodes/deadreckoner.h
deleted file mode 100644
index 4516b11f28f..00000000000
--- a/core_perception/deadreckoner/nodes/deadreckoner.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2017, Nagoya University
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of Autoware nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"geometry
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#ifndef DEADRECKONER_H
-#define DEADRECKONER_H
-
-#include
-#include
-#include
-
-class DeadRecokner
-{
-public:
- DeadRecokner();
- ~DeadRecokner();
-private:
- ros::NodeHandle nh_, private_nh_;
- ros::Subscriber twist_sub_;
- ros::Publisher odom_pub_;
-
- void callbackFromCurrentTwist(const geometry_msgs::TwistStampedConstPtr& msg);
-};
-#endif // DEADRECKONER_H
diff --git a/core_perception/deadreckoner/nodes/deadreckoner_node.cpp b/core_perception/deadreckoner/nodes/deadreckoner_node.cpp
deleted file mode 100644
index 1f9765fc2d1..00000000000
--- a/core_perception/deadreckoner/nodes/deadreckoner_node.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2017, Nagoya University
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of Autoware nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#include
-
-#include "deadreckoner.h"
-
-int main(int argc, char** argv)
-{
- ros::init(argc, argv, "deadreckoner");
- DeadRecokner node;
- ros::spin();
- return 0;
-}
diff --git a/core_perception/deadreckoner/package.xml b/core_perception/deadreckoner/package.xml
deleted file mode 100644
index e7c8ea8beb1..00000000000
--- a/core_perception/deadreckoner/package.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
- deadreckoner
- 0.0.0
- The deadreckoner package
-
- Akihito Ohsato
-
- TODO
-
- Akihito Ohsato
-
- catkin
- carma_cmake_common
-
-
-
-
-
diff --git a/core_perception/ekf_localizer/CMakeLists.txt b/core_perception/ekf_localizer/CMakeLists.txt
index 19023a01b15..ef070547de3 100644
--- a/core_perception/ekf_localizer/CMakeLists.txt
+++ b/core_perception/ekf_localizer/CMakeLists.txt
@@ -1,77 +1,65 @@
-cmake_minimum_required(VERSION 2.8.3)
+# Copyright (C) 2023 LEIDOS.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+cmake_minimum_required(VERSION 3.5)
project(ekf_localizer)
+# Declare carma package and check ROS version
find_package(carma_cmake_common REQUIRED)
-carma_check_ros_version(1)
-
-find_package(catkin REQUIRED
- autoware_build_flags
- roscpp
- std_msgs
- tf2
- tf2_ros
- geometry_msgs
- autoware_msgs
- sensor_msgs
- rostest
- rosunit
- amathutils_lib
-)
+carma_check_ros_version(2)
+carma_package()
find_package(Eigen3 REQUIRED)
+## Find dependencies using ament auto
+find_package(ament_cmake_auto REQUIRED)
+ament_auto_find_build_dependencies()
-catkin_package(
- INCLUDE_DIRS include
- CATKIN_DEPENDS
- autoware_build_flags
- roscpp
- std_msgs
- tf2
- tf2_ros
- geometry_msgs
- autoware_msgs
- sensor_msgs
- amathutils_lib
+# Includes
+include_directories(
+ include
+ ${EIGEN3_INCLUDE_DIR}
)
-###########
-## Build ##
-###########
+ament_auto_add_library(ekf_localizer_lib SHARED
+ src/ekf_localizer.cpp)
-include_directories(
- include
- ${catkin_INCLUDE_DIRS}
- ${EIGEN3_INCLUDE_DIR}
+ament_auto_add_executable(ekf_localizer_exec
+ src/ekf_localizer_node.cpp)
+
+# Register component
+rclcpp_components_register_nodes(ekf_localizer_lib "ekf_localizer::EKFLocalizer")
+
+target_link_libraries(
+ ekf_localizer_exec
+ ekf_localizer_lib
)
-add_executable(ekf_localizer src/ekf_localizer_node.cpp src/ekf_localizer.cpp)
-add_dependencies(ekf_localizer ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
-target_link_libraries(ekf_localizer ${catkin_LIBRARIES})
-
-#############
-## Install ##
-#############
-
-## Install executables and/or libraries
-install(TARGETS ekf_localizer
- ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
- LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
- RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
-
-## Install project namespaced headers
-install(DIRECTORY include/ekf_localizer/
- DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION})
-
-install(DIRECTORY launch/
- DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
- PATTERN ".svn" EXCLUDE)
-
-
-if(CATKIN_ENABLE_TESTING)
- find_package(rostest REQUIRED)
- add_rostest_gtest(ekf_localizer-test test/test_ekf_localizer.test
- test/src/test_ekf_localizer.cpp
- src/ekf_localizer.cpp)
- target_link_libraries(ekf_localizer-test ${catkin_LIBRARIES})
+# Testing
+if(BUILD_TESTING)
+
+ find_package(ament_lint_auto REQUIRED)
+ ament_lint_auto_find_test_dependencies() # This populates the ${${PROJECT_NAME}_FOUND_TEST_DEPENDS} variable
+
+ ament_add_gtest(test_ekf_localizer test/test_ekf_localizer.cpp)
+
+ ament_target_dependencies(test_ekf_localizer ${${PROJECT_NAME}_FOUND_TEST_DEPENDS})
+
+ target_link_libraries(test_ekf_localizer ekf_localizer_lib)
+
endif()
+
+ament_auto_package(
+ INSTALL_TO_SHARE launch
+)
diff --git a/core_perception/ekf_localizer/include/ekf_localizer/ekf_localizer.h b/core_perception/ekf_localizer/include/ekf_localizer/ekf_localizer.h
deleted file mode 100644
index 812083596ef..00000000000
--- a/core_perception/ekf_localizer/include/ekf_localizer/ekf_localizer.h
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright 2018-2019 Autoware Foundation. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "amathutils_lib/kalman_filter.hpp"
-#include "amathutils_lib/time_delay_kalman_filter.hpp"
-
-class EKFLocalizer
-{
-public:
- EKFLocalizer();
- ~EKFLocalizer();
-
-private:
- ros::NodeHandle nh_; //!< @brief ros public node handler
- ros::NodeHandle pnh_; //!< @brief ros private node handler
- ros::Publisher pub_pose_; //!< @brief ekf estimated pose publisher
- ros::Publisher pub_pose_cov_; //!< @brief estimated ekf pose with covariance publisher
- ros::Publisher pub_twist_; //!< @brief ekf estimated twist publisher
- ros::Publisher pub_twist_cov_; //!< @brief ekf estimated twist with covariance publisher
- ros::Publisher pub_debug_; //!< @brief debug info publisher
- ros::Publisher pub_measured_pose_; //!< @brief debug measurement pose publisher
- ros::Publisher pub_yaw_bias_; //!< @brief ekf estimated yaw bias publisher
- ros::Subscriber sub_initialpose_; //!< @brief initial pose subscriber
- ros::Subscriber sub_pose_; //!< @brief measurement pose subscriber
- ros::Subscriber sub_twist_; //!< @brief measurement twist subscriber
- ros::Subscriber sub_pose_with_cov_; //!< @brief measurement pose with covariance subscriber
- ros::Subscriber sub_twist_with_cov_; //!< @brief measurement twist with covariance subscriber
- ros::Timer timer_control_; //!< @brief time for ekf calculation callback
- tf2_ros::TransformBroadcaster tf_br_; //!< @brief tf broadcaster
-
- TimeDelayKalmanFilter ekf_; //!< @brief extended kalman filter instance.
-
- /* parameters */
- bool show_debug_info_;
- double ekf_rate_; //!< @brief EKF predict rate
- double ekf_dt_; //!< @brief = 1 / ekf_rate_
- bool enable_yaw_bias_estimation_; //!< @brief for LiDAR mount error. if true, publish /estimate_yaw_bias
- std::string pose_frame_id_; //!< @brief Parent frame for pose and tf output
- std::string child_frame_id_; //!< @brief Child frame for pose and tf output
-
- int dim_x_; //!< @brief dimension of EKF state
- int extend_state_step_; //!< @brief for time delay compensation
- int dim_x_ex_; //!< @brief dimension of extended EKF state (dim_x_ * extended_state_step)
-
- /* Pose */
- double pose_additional_delay_; //!< @brief compensated pose delay time = (pose.header.stamp - now) +
- //!< additional_delay [s]
- double pose_measure_uncertainty_time_; //!< @brief added for measurement covariance
- double pose_rate_; //!< @brief pose rate [s], used for covariance calculation
- double pose_gate_dist_; //!< @brief pose measurement is ignored if the maharanobis distance is larger than this
- //!< value.
- double pose_stddev_x_; //!< @brief standard deviation for pose position x [m]
- double pose_stddev_y_; //!< @brief standard deviation for pose position y [m]
- double pose_stddev_yaw_; //!< @brief standard deviation for pose position yaw [rad]
- bool use_pose_with_covariance_; //!< @brief use covariance in pose_with_covarianve message
- bool use_twist_with_covariance_; //!< @brief use covariance in twist_with_covarianve message
-
- /* twist */
- double twist_additional_delay_; //!< @brief compensated delay time = (twist.header.stamp - now) + additional_delay
- //!< [s]
- double twist_rate_; //!< @brief rate [s], used for covariance calculation
- double twist_gate_dist_; //!< @brief measurement is ignored if the maharanobis distance is larger than this value.
- double twist_stddev_vx_; //!< @brief standard deviation for linear vx
- double twist_stddev_wz_; //!< @brief standard deviation for angular wx
-
- /* process noise variance for discrete model */
- double proc_cov_yaw_d_; //!< @brief discrete yaw process noise
- double proc_cov_yaw_bias_d_; //!< @brief discrete yaw bias process noise
- double proc_cov_vx_d_; //!< @brief discrete process noise in d_vx=0
- double proc_cov_wz_d_; //!< @brief discrete process noise in d_wz=0
-
- enum IDX
- {
- X = 0,
- Y = 1,
- YAW = 2,
- YAWB = 3,
- VX = 4,
- WZ = 5,
- };
-
- /* for model prediction */
- std::shared_ptr current_twist_ptr_; //!< @brief current measured twist
- std::shared_ptr current_pose_ptr_; //!< @brief current measured pose
- geometry_msgs::PoseStamped current_ekf_pose_; //!< @brief current estimated pose
- geometry_msgs::TwistStamped current_ekf_twist_; //!< @brief current estimated twist
- boost::array current_pose_covariance_;
- boost::array current_twist_covariance_;
-
- /**
- * @brief computes update & prediction of EKF for each ekf_dt_[s] time
- */
- void timerCallback(const ros::TimerEvent& e);
-
- /**
- * @brief publish tf
- */
- void broadcastTF();
-
- /**
- * @brief set pose measurement
- */
- void callbackPose(const geometry_msgs::PoseStamped::ConstPtr& msg);
-
- /**
- * @brief set twist measurement
- */
- void callbackTwist(const geometry_msgs::TwistStamped::ConstPtr& msg);
-
- /**
- * @brief set poseWithCovariance measurement
- */
- void callbackPoseWithCovariance(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& msg);
-
- /**
- * @brief set twistWithCovariance measurement
- */
- void callbackTwistWithCovariance(const geometry_msgs::TwistWithCovarianceStamped::ConstPtr& msg);
-
- /**
- * @brief set initial_pose to current EKF pose
- */
- void callbackInitialPose(const geometry_msgs::PoseWithCovarianceStamped& msg);
-
- /**
- * @brief initialization of EKF
- */
- void initEKF();
-
- /**
- * @brief compute EKF prediction
- */
- void predictKinematicsModel();
-
- /**
- * @brief compute EKF update with pose measurement
- * @param pose measurement value
- */
- void measurementUpdatePose(const geometry_msgs::PoseStamped& pose);
-
- /**
- * @brief compute EKF update with pose measurement
- * @param twist measurement value
- */
- void measurementUpdateTwist(const geometry_msgs::TwistStamped& twist);
-
- /**
- * @brief check whether a measurement value falls within the mahalanobis distance threshold
- * @param dist_max mahalanobis distance threshold
- * @param estimated current estimated state
- * @param measured measured state
- * @param estimated_cov current estimation covariance
- * @return whether it falls within the mahalanobis distance threshold
- */
- bool mahalanobisGate(const double& dist_max, const Eigen::MatrixXd& estimated, const Eigen::MatrixXd& measured,
- const Eigen::MatrixXd& estimated_cov);
-
- /**
- * @brief get transform from frame_id
- */
- bool getTransformFromTF(std::string parent_frame, std::string child_frame,
- geometry_msgs::TransformStamped& transform);
-
- /**
- * @brief normalize yaw angle
- * @param yaw yaw angle
- * @return normalized yaw
- */
- double normalizeYaw(const double& yaw);
-
- /**
- * @brief set current EKF estimation result to current_ekf_pose_ & current_ekf_twist_
- */
- void setCurrentResult();
-
- /**
- * @brief publish current EKF estimation result
- */
- void publishEstimateResult();
-
- /**
- * @brief for debug
- */
- void showCurrentX();
-
- friend class EKFLocalizerTestSuite; // for test code
-};
diff --git a/core_perception/ekf_localizer/include/ekf_localizer/ekf_localizer.hpp b/core_perception/ekf_localizer/include/ekf_localizer/ekf_localizer.hpp
new file mode 100644
index 00000000000..a09132b7be5
--- /dev/null
+++ b/core_perception/ekf_localizer/include/ekf_localizer/ekf_localizer.hpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2023 LEIDOS.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "amathutils_lib_ros2/kalman_filter.hpp"
+#include "amathutils_lib_ros2/time_delay_kalman_filter.hpp"
+#include
+
+namespace ekf_localizer{
+
+class EKFLocalizer: public carma_ros2_utils::CarmaLifecycleNode
+{
+ private:
+
+ rclcpp::Publisher::SharedPtr pub_pose_; //!< @brief ekf estimated pose publisher
+ rclcpp::Publisher::SharedPtr pub_pose_cov_; //!< @brief estimated ekf pose with covariance publisher
+ rclcpp::Publisher::SharedPtr pub_twist_; //!< @brief ekf estimated twist publisher
+ rclcpp::Publisher::SharedPtr pub_twist_cov_; //!< @brief ekf estimated twist with covariance publisher
+ rclcpp::Publisher::SharedPtr pub_debug_; //!< @brief debug info publisher
+ rclcpp::Publisher::SharedPtr pub_measured_pose_; //!< @brief debug measurement pose publisher
+ rclcpp::Publisher::SharedPtr pub_yaw_bias_; //!< @brief ekf estimated yaw bias publisher
+
+ rclcpp::Subscription::SharedPtr sub_initialpose_; //!< @brief initial pose subscriber
+ rclcpp::Subscription