Skip to content

Commit 9c962cd

Browse files
authored
Merge pull request #3782 from JdeRobot/cpp-follow-line-update
C++ update Follow Line
2 parents c0a0854 + 96c0283 commit 9c962cd

24 files changed

Lines changed: 667 additions & 26227 deletions
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
project(VacuumCleanerLibs CXX)
3+
4+
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
7+
8+
find_package(ament_cmake REQUIRED)
9+
10+
set(ROS2_PACKAGES
11+
rclcpp
12+
nav_msgs
13+
gazebo_msgs
14+
geometry_msgs
15+
sensor_msgs
16+
std_msgs
17+
tf2
18+
tf2_geometry_msgs
19+
ros_gz_interfaces
20+
cv_bridge
21+
)
22+
23+
foreach(pkg IN LISTS ROS2_PACKAGES)
24+
find_package(${pkg} REQUIRED)
25+
endforeach()
26+
27+
find_package(Boost REQUIRED COMPONENTS system)
28+
find_package(nlohmann_json REQUIRED)
29+
find_package(OpenCV REQUIRED)
30+
31+
set(COMMON_INTERFACES_INCLUDE "/home/ws/install/common_interfaces_cpp/include")
32+
set(COMMON_INTERFACES_LIB "/home/ws/install/common_interfaces_cpp/lib/libcommon_interfaces_cpp.so")
33+
34+
set(WEBGUI_ROS_DEPS rclcpp nav_msgs gazebo_msgs tf2 tf2_geometry_msgs sensor_msgs cv_bridge)
35+
set(WEBGUI_SYS_DEPS Frequency Boost::system nlohmann_json::nlohmann_json)
36+
set(HAL_ROS_DEPS rclcpp geometry_msgs sensor_msgs std_msgs ros_gz_interfaces cv_bridge)
37+
38+
39+
# --- libFrequency.so ---
40+
add_library(Frequency SHARED src/Frequency.cpp)
41+
target_include_directories(Frequency PUBLIC include)
42+
43+
# --- libWebGUI.so ---
44+
add_library(WebGUI SHARED src/WebGUI.cpp src/Lap.cpp)
45+
target_include_directories(WebGUI PUBLIC include ${COMMON_INTERFACES_INCLUDE} ${OpenCV_INCLUDE_DIRS})
46+
47+
ament_target_dependencies(WebGUI ${WEBGUI_ROS_DEPS})
48+
target_link_libraries(WebGUI ${WEBGUI_SYS_DEPS} ${COMMON_INTERFACES_LIB} ${OpenCV_LIBS})
49+
50+
# --- libHAL.so ---
51+
add_library(HAL SHARED src/HAL.cpp)
52+
target_include_directories(HAL PUBLIC include ${COMMON_INTERFACES_INCLUDE} ${OpenCV_INCLUDE_DIRS})
53+
54+
ament_target_dependencies(HAL ${HAL_ROS_DEPS})
55+
target_link_libraries(HAL ${COMMON_INTERFACES_LIB} ${OpenCV_LIBS})
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef INCLUDE_FREQUENCY_HPP_
2+
#define INCLUDE_FREQUENCY_HPP_
3+
4+
#include <chrono>
5+
#include <cmath>
6+
#include <thread>
7+
8+
class Frequency
9+
{
10+
private:
11+
std::chrono::high_resolution_clock::time_point last_time;
12+
int is_first_iteration;
13+
14+
public:
15+
Frequency();
16+
~Frequency();
17+
18+
static int rate;
19+
void tick(int ideal_cycle = 50);
20+
};
21+
22+
#endif
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#ifndef INCLUDE_HAL_HPP_
2+
#define INCLUDE_HAL_HPP_
3+
4+
#include <opencv2/opencv.hpp>
5+
#include <memory>
6+
#include <thread>
7+
8+
// Forward declarations to speed up compilation by avoiding heavy ROS 2 includes.
9+
// - MotorsNode links to "common_interfaces_cpp/hal/motors.hpp"
10+
// - CameraNode links to "common_interfaces_cpp/hal/camera.hpp"
11+
class MotorsNode;
12+
class CameraNode;
13+
namespace rclcpp::executors { class SingleThreadedExecutor; }
14+
15+
class HAL
16+
{
17+
public:
18+
// Prevent instantiation. HAL acts as a global static utility.
19+
HAL() = delete;
20+
21+
static void set_v(const float velocity);
22+
static void set_w(const float velocity);
23+
static cv::Mat get_image();
24+
25+
private:
26+
static void init();
27+
friend class SystemBootstrapper;
28+
29+
// Hidden internal state. Not accessible to the user.
30+
static std::shared_ptr<MotorsNode> motors_node_;
31+
static std::shared_ptr<CameraNode> camera_node_;
32+
static std::shared_ptr<rclcpp::executors::SingleThreadedExecutor> executor_;
33+
static std::thread spin_thread_;
34+
};
35+
36+
#endif
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#ifndef LAP_HPP_
2+
#define LAP_HPP_
3+
4+
#include "common_interfaces_cpp/hal/odometry.hpp"
5+
#include <chrono>
6+
#include <string>
7+
#include <memory>
8+
#include <mutex>
9+
10+
class Lap {
11+
public:
12+
explicit Lap(std::shared_ptr<OdometryNode> pose3d);
13+
std::string check_threshold();
14+
std::string return_lap_time();
15+
void reset();
16+
void pause();
17+
void unpause();
18+
19+
private:
20+
std::shared_ptr<OdometryNode> pose3d_;
21+
std::chrono::system_clock::time_point start_time_;
22+
std::chrono::duration<double> lap_time_;
23+
bool lap_rest_;
24+
bool buffer_;
25+
bool pause_condition_;
26+
std::mutex lap_mutex_;
27+
};
28+
29+
#endif
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef INCLUDE_WEBGUI_HPP_
2+
#define INCLUDE_WEBGUI_HPP_
3+
4+
#include <opencv2/opencv.hpp>
5+
#include <memory>
6+
#include <thread>
7+
8+
// Forward declarations to speed up compilation by avoiding heavy ROS 2 includes.
9+
// - WebGUINode links to "common_interfaces_cpp/webgui/WebGUIBridge.hpp"
10+
class WebGUINode;
11+
namespace rclcpp::executors { class MultiThreadedExecutor; }
12+
13+
class WebGUI
14+
{
15+
public:
16+
// Prevent instantiation. WebGUI acts as a global static utility.
17+
WebGUI() = delete;
18+
19+
static void show_image(const cv::Mat& image);
20+
21+
private:
22+
static void init();
23+
friend class SystemBootstrapper;
24+
25+
// Hidden internal state. Not accessible to the user.
26+
static std::shared_ptr<WebGUINode> gui_node_;
27+
static std::shared_ptr<rclcpp::executors::MultiThreadedExecutor> executor_;
28+
static std::thread spin_thread_;
29+
};
30+
31+
#endif
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include "Frequency.hpp"
2+
3+
int Frequency::rate = 0;
4+
5+
Frequency::Frequency() : is_first_iteration(1) {}
6+
7+
Frequency::~Frequency() {}
8+
9+
void Frequency::tick(int ideal_cycle)
10+
{
11+
const auto ideal_ms = std::chrono::milliseconds(1000 / ideal_cycle);
12+
const auto now = std::chrono::high_resolution_clock::now();
13+
14+
if (is_first_iteration == 1)
15+
{
16+
last_time = now;
17+
is_first_iteration = 0;
18+
return;
19+
}
20+
21+
const auto iter_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_time);
22+
23+
if (iter_ms < ideal_ms)
24+
{
25+
rate = std::round(1000.0 / ideal_ms.count());
26+
std::this_thread::sleep_for(ideal_ms - iter_ms);
27+
}
28+
else
29+
{
30+
rate = std::round(1000.0 / iter_ms.count());
31+
}
32+
33+
last_time = now;
34+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include "HAL.hpp"
2+
#include "common_interfaces_cpp/hal/motors.hpp"
3+
#include "common_interfaces_cpp/hal/camera.hpp"
4+
#include "rclcpp/rclcpp.hpp"
5+
#include <chrono>
6+
7+
using namespace std::chrono_literals;
8+
9+
std::shared_ptr<MotorsNode> HAL::motors_node_ = nullptr;
10+
std::shared_ptr<CameraNode> HAL::camera_node_ = nullptr;
11+
std::shared_ptr<rclcpp::executors::SingleThreadedExecutor> HAL::executor_ = nullptr;
12+
std::thread HAL::spin_thread_;
13+
14+
void HAL::init()
15+
{
16+
if (!motors_node_) {
17+
motors_node_ = std::make_shared<MotorsNode>("/cmd_vel", 4.0, 0.3);
18+
camera_node_ = std::make_shared<CameraNode>("/cam_f1_left/image_raw");
19+
20+
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
21+
executor_->add_node(motors_node_);
22+
executor_->add_node(camera_node_);
23+
24+
spin_thread_ = std::thread([]() {
25+
executor_->spin();
26+
});
27+
spin_thread_.detach();
28+
}
29+
}
30+
31+
void HAL::set_v(const float velocity)
32+
{
33+
if (motors_node_) motors_node_->sendV(static_cast<double>(velocity));
34+
}
35+
36+
void HAL::set_w(const float velocity)
37+
{
38+
if (motors_node_) motors_node_->sendW(static_cast<double>(velocity));
39+
}
40+
41+
cv::Mat HAL::get_image()
42+
{
43+
if (!camera_node_) return cv::Mat();
44+
auto image = camera_node_->getImage();
45+
while (!image && rclcpp::ok()) {
46+
std::this_thread::sleep_for(5ms);
47+
image = camera_node_->getImage();
48+
}
49+
return (image) ? image->data.clone() : cv::Mat();
50+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include "Lap.hpp"
2+
#include <iomanip>
3+
#include <sstream>
4+
#include <cmath>
5+
6+
Lap::Lap(std::shared_ptr<OdometryNode> pose3d) : pose3d_(pose3d) {
7+
reset();
8+
}
9+
10+
std::string Lap::check_threshold() {
11+
std::lock_guard<std::mutex> lock(lap_mutex_);
12+
13+
if (!pause_condition_) {
14+
if (start_time_.time_since_epoch().count() != 0 && !lap_rest_) {
15+
auto now = std::chrono::system_clock::now();
16+
if (lap_time_.count() == 0) {
17+
lap_time_ = now - start_time_;
18+
} else {
19+
lap_time_ += now - start_time_;
20+
}
21+
start_time_ = now;
22+
}
23+
24+
if (start_time_.time_since_epoch().count() == 0 && lap_rest_) {
25+
start_time_ = std::chrono::system_clock::now();
26+
lap_rest_ = false;
27+
}
28+
}
29+
30+
if (lap_time_.count() == 0) {
31+
return "0";
32+
}
33+
34+
double total_seconds = lap_time_.count();
35+
int hours = static_cast<int>(total_seconds / 3600);
36+
int minutes = static_cast<int>((total_seconds - hours * 3600) / 60);
37+
double seconds = total_seconds - hours * 3600 - minutes * 60;
38+
39+
std::stringstream ss;
40+
ss << hours << ":"
41+
<< std::setfill('0') << std::setw(2) << minutes << ":"
42+
<< std::setfill('0') << std::setw(2) << static_cast<int>(seconds) << "."
43+
<< std::setfill('0') << std::setw(6) << static_cast<int>(std::round((seconds - std::floor(seconds)) * 1000000));
44+
45+
return ss.str();
46+
}
47+
48+
std::string Lap::return_lap_time() {
49+
std::lock_guard<std::mutex> lock(lap_mutex_);
50+
return std::to_string(lap_time_.count());
51+
}
52+
53+
void Lap::reset() {
54+
std::lock_guard<std::mutex> lock(lap_mutex_);
55+
start_time_ = std::chrono::system_clock::time_point();
56+
lap_time_ = std::chrono::duration<double>::zero();
57+
lap_rest_ = true;
58+
buffer_ = false;
59+
pause_condition_ = false;
60+
}
61+
62+
void Lap::pause() {
63+
std::lock_guard<std::mutex> lock(lap_mutex_);
64+
pause_condition_ = true;
65+
}
66+
67+
void Lap::unpause() {
68+
std::lock_guard<std::mutex> lock(lap_mutex_);
69+
if (pause_condition_) {
70+
start_time_ = std::chrono::system_clock::now();
71+
}
72+
pause_condition_ = false;
73+
}

0 commit comments

Comments
 (0)