Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add robosense support #77

Merged
merged 89 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
6e8233e
Added Helios and Bpearl as supported models
mebasoglu Sep 12, 2023
743477f
Created Robosense hardware interface
mebasoglu Sep 12, 2023
060f881
Created hardware interface ROS 2 wrapper for Robosense
mebasoglu Sep 12, 2023
4cce5a6
Creating Robosense decoder
mebasoglu Sep 13, 2023
7ed4ea4
Created Robosense angle corrector
mebasoglu Sep 14, 2023
25904ff
Created Robosense decoder
mebasoglu Sep 14, 2023
4acc4ac
Created decoder ROS 2 wrapper
mebasoglu Sep 18, 2023
e9d271c
Fix pragma pack and packet
mebasoglu Sep 19, 2023
8c2da77
Delete forgotten parenthesis
mebasoglu Sep 19, 2023
d78f331
Fix byte order issue
mebasoglu Sep 19, 2023
e73e12e
Calculate timestamp for each point
mebasoglu Sep 19, 2023
3271e3a
Consider return type
mebasoglu Sep 19, 2023
b3b462d
Use scan phase and get distance unit from packet
mebasoglu Sep 19, 2023
07d898f
Format code and convert header guards to #pragma once
mebasoglu Sep 19, 2023
672f408
Added time offsets for dual return mode
mebasoglu Sep 19, 2023
cf7a106
Save calibration file
mebasoglu Sep 20, 2023
b2343ac
Get calibration from sensor
mebasoglu Sep 20, 2023
fcbc26a
Fix stringstream new line issue
mebasoglu Sep 21, 2023
a9c08dc
Load calibration from sensor by default
mebasoglu Sep 21, 2023
597d862
Close info packet UDP driver after receiving for the first time
mebasoglu Sep 22, 2023
8028bf0
Fix calibration file format
mebasoglu Sep 22, 2023
7230188
Created hardware monitor
mebasoglu Sep 25, 2023
5657534
Receive DIFOP packet at the same time
mebasoglu Sep 26, 2023
0f9508e
Refactor hardware monitor
mebasoglu Sep 26, 2023
3b39dd5
Create calibration file name dynamically
mebasoglu Sep 26, 2023
4e55331
Get return mode from DIFOP packet
mebasoglu Sep 26, 2023
53d218d
Delete comments
mebasoglu Sep 26, 2023
83e286f
Get n_returns from sensor config
mebasoglu Sep 28, 2023
cdb1d27
Added dual return distance threshold
mebasoglu Sep 28, 2023
c2738d9
Created LoadFromStream method
mebasoglu Sep 28, 2023
d7c2fb9
Added Bpearl
mebasoglu Sep 29, 2023
559c74f
Get hardware info of Bpearl
mebasoglu Sep 29, 2023
1cbf6b2
Added time offsets for Bpearl
mebasoglu Sep 29, 2023
f8a6a61
Remove calibration configuration from Bpearl hw monitor
mebasoglu Sep 29, 2023
81a49a3
Fix the format of hardware monitor
mebasoglu Sep 29, 2023
7fbb06c
Check calibration format while loading it
mebasoglu Oct 2, 2023
3f8ed65
Wait for first DIFOP packet before continuing
mebasoglu Oct 2, 2023
6bec597
Tidied up the code
mebasoglu Oct 2, 2023
ea61a21
Fix Bpearl return mode issue
mebasoglu Oct 4, 2023
7797394
Created robosense_msgs
mebasoglu Oct 4, 2023
a3d3186
Use robosense_msgs on hw interface and decoder
mebasoglu Oct 4, 2023
013e32c
Fix package name
mebasoglu Oct 4, 2023
03e4218
Publish both MSOP and DIFOP packets from hw interface ros wrapper
mebasoglu Oct 4, 2023
b69a265
Subscribe packets on decoder ros wrapper
mebasoglu Oct 5, 2023
acbda3e
Subscribe to robosense difop packets on hw monitor
mebasoglu Oct 5, 2023
f7cbb82
Updated MAX_SCAN_BUFFER_POINTS
mebasoglu Oct 5, 2023
db58d63
Fixed point return type
mebasoglu Oct 5, 2023
6060de0
Fixed return mode diagnostic
mebasoglu Oct 5, 2023
46f0935
Fixed calibration sign issue
mebasoglu Oct 6, 2023
8b95864
Fixed return mode types
mebasoglu Oct 6, 2023
7066b57
Deleted unused code
mebasoglu Oct 6, 2023
6d54c16
Added lidar model to the packet message
mebasoglu Oct 9, 2023
76c5f7b
Deleted hw_interface from decoder ros wrapper
mebasoglu Oct 9, 2023
0cc0204
Checking model type in decoder ros wrapper
mebasoglu Oct 9, 2023
4de59b7
Initialize driver for different Bpearl models
mebasoglu Oct 9, 2023
21492b9
Added Bpearl v4.0
mebasoglu Oct 9, 2023
4f9a07b
Added Bpearl v4 diag
mebasoglu Oct 9, 2023
7ad6226
Added Bpearl V4 to hw monitor
mebasoglu Oct 9, 2023
6bd0767
Fixed packet structs
mebasoglu Oct 11, 2023
f3772f4
Fix Bpearl v3 timestamp
mebasoglu Oct 11, 2023
bad2a98
Handle sensor timestamp usage
mebasoglu Oct 11, 2023
83a489f
Deleted unnecessary params
mebasoglu Oct 11, 2023
324026c
Fix info driver initialization
mebasoglu Oct 11, 2023
9b488c7
Fixed diagnostic format
mebasoglu Oct 11, 2023
a90ad92
Added Robosense to python launch file
mebasoglu Oct 12, 2023
7d46fe0
Deleted unnecessary parts
mebasoglu Oct 12, 2023
b24cef5
Fix Helios range resolution
mebasoglu Oct 16, 2023
23d2857
Fix diagnostic info for Bpearl v3
mebasoglu Oct 20, 2023
8214790
Added generic float function
mebasoglu Oct 20, 2023
c0b7269
Fixed bpearl diag
mebasoglu Oct 20, 2023
699f770
Fixed Helios diag
mebasoglu Oct 20, 2023
7643e28
Fixed getSyncStatus
mebasoglu Oct 20, 2023
bafbefe
Fix axes
mebasoglu Oct 20, 2023
54a1533
Change Helios 5515 name to only Helios
mebasoglu Oct 30, 2023
c354a69
Add robosense words to .cspell.json
mebasoglu Nov 22, 2023
26a2e33
refactor(nebula_common.hpp): convert if chain to switch-case
mebasoglu Nov 27, 2023
7c42e2e
refactor(nebula_decoders): add "using namespace" to simplify big endi…
mebasoglu Nov 27, 2023
91908c8
refactor(nebula_decoders): use switch statements over if-else
mebasoglu Nov 27, 2023
a2b44bd
fix(nebula_decoders): add necessary header
mebasoglu Nov 27, 2023
93cceb1
refactor(nebula_decoders): get rid of magic numbers
mebasoglu Nov 27, 2023
bbf326d
refactor(nebula_decoders): add using namespace for boost:endian
mebasoglu Nov 27, 2023
34dfbe9
refactor: get rid of magic numbers
mebasoglu Nov 27, 2023
74cbe32
chore: add formulas for unpacking difop packet
mebasoglu Nov 27, 2023
44dd2cf
refactor(robosense_hw_monitor): remove unnecessary log
mebasoglu Nov 27, 2023
6699532
fix(robosense_packet.hpp): change nanoseconds to microseconds
mebasoglu Nov 27, 2023
3dcdbfd
refactor(robosense_packet.hpp): throw unknown resolution error
mebasoglu Nov 28, 2023
3538b5d
chore(cspell): add robosense current and voltage words
mebasoglu Nov 28, 2023
ad004c8
fix(robosense): create corrected channel numbers
mebasoglu Dec 6, 2023
eac1c8b
fix(robosense): handle ptp synchronization
mebasoglu Dec 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@
"Vccint",
"XT",
"XTM",
"DHAVE"
"DHAVE",
"Bpearl",
"Helios",
"Msop",
"Difop",
"gptp",
"Idat",
"Vdat"
]
}
84 changes: 81 additions & 3 deletions nebula_common/include/nebula_common/nebula_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ enum class SensorModel {
VELODYNE_VLP32MR,
VELODYNE_HDL32,
VELODYNE_VLP16,
ROBOSENSE_HELIOS,
ROBOSENSE_BPEARL,
ROBOSENSE_BPEARL_V3,
ROBOSENSE_BPEARL_V4,
};

/// @brief not used?
Expand Down Expand Up @@ -401,6 +405,18 @@ inline std::ostream & operator<<(std::ostream & os, nebula::drivers::SensorModel
case SensorModel::VELODYNE_VLP16:
os << "VLP16";
break;
case SensorModel::ROBOSENSE_HELIOS:
os << "HELIOS";
break;
case SensorModel::ROBOSENSE_BPEARL:
os << "BPEARL";
break;
case SensorModel::ROBOSENSE_BPEARL_V3:
os << "BPEARL V3.0";
break;
case SensorModel::ROBOSENSE_BPEARL_V4:
os << "BPEARL V4.0";
break;
case SensorModel::UNKNOWN:
os << "Sensor Unknown";
break;
Expand All @@ -424,6 +440,7 @@ struct SensorConfigurationBase
double max_range;
bool remove_nans; /// todo: consider changing to only_finite
std::vector<PointField> fields;
bool use_sensor_time{false};
};

/// @brief Convert SensorConfigurationBase to string (Overloading the << operator)
Expand All @@ -436,7 +453,8 @@ inline std::ostream & operator<<(
os << "SensorModel: " << arg.sensor_model << ", ReturnMode: " << arg.return_mode
<< ", HostIP: " << arg.host_ip << ", SensorIP: " << arg.sensor_ip
<< ", FrameID: " << arg.frame_id << ", DataPort: " << arg.data_port
<< ", Frequency: " << arg.frequency_ms << ", MTU: " << arg.packet_mtu_size;
<< ", Frequency: " << arg.frequency_ms << ", MTU: " << arg.packet_mtu_size
<< ", Use sensor time: " << arg.use_sensor_time;
return os;
}

Expand Down Expand Up @@ -468,9 +486,63 @@ inline SensorModel SensorModelFromString(const std::string & sensor_model)
if (sensor_model == "VLP32MR") return SensorModel::VELODYNE_VLP32MR;
if (sensor_model == "HDL32") return SensorModel::VELODYNE_HDL32;
if (sensor_model == "VLP16") return SensorModel::VELODYNE_VLP16;
// Robosense
if (sensor_model == "Helios") return SensorModel::ROBOSENSE_HELIOS;
if (sensor_model == "Bpearl") return SensorModel::ROBOSENSE_BPEARL;
if (sensor_model == "Bpearl_V3") return SensorModel::ROBOSENSE_BPEARL_V3;
if (sensor_model == "Bpearl_V4") return SensorModel::ROBOSENSE_BPEARL_V4;
return SensorModel::UNKNOWN;
}

inline std::string SensorModelToString(const SensorModel & sensor_model)
{
switch (sensor_model) {
// Hesai
case SensorModel::HESAI_PANDAR64:
return "Pandar64";
case SensorModel::HESAI_PANDAR40P:
return "Pandar40P";
case SensorModel::HESAI_PANDAR40M:
return "Pandar40M";
case SensorModel::HESAI_PANDARXT32:
return "PandarXT32";
case SensorModel::HESAI_PANDARXT32M:
return "PandarXT32M";
case SensorModel::HESAI_PANDARAT128:
return "PandarAT128";
case SensorModel::HESAI_PANDARQT64:
return "PandarQT64";
case SensorModel::HESAI_PANDARQT128:
return "PandarQT128";
case SensorModel::HESAI_PANDAR128_E4X:
return "Pandar128E4X";
// Velodyne
case SensorModel::VELODYNE_VLS128:
return "VLS128";
case SensorModel::VELODYNE_HDL64:
return "HDL64";
case SensorModel::VELODYNE_VLP32:
return "VLP32";
case SensorModel::VELODYNE_VLP32MR:
return "VLP32MR";
case SensorModel::VELODYNE_HDL32:
return "HDL32";
case SensorModel::VELODYNE_VLP16:
return "VLP16";
// Robosense
case SensorModel::ROBOSENSE_HELIOS:
return "Helios";
case SensorModel::ROBOSENSE_BPEARL:
return "Bpearl";
case SensorModel::ROBOSENSE_BPEARL_V3:
return "Bpearl_V3";
case SensorModel::ROBOSENSE_BPEARL_V4:
return "Bpearl_V4";
default:
return "UNKNOWN";
}
}

/// @brief Convert return mode name to ReturnMode enum
/// @param return_mode Return mode name (Upper and lower case letters must match)
/// @return Corresponding ReturnMode
Expand All @@ -496,12 +568,18 @@ pcl::PointCloud<PointXYZIRADT>::Ptr convertPointXYZIRCAEDTToPointXYZIRADT(
/// @brief Converts degrees to radians
/// @param radians
/// @return degrees
static inline float deg2rad(double degrees) { return degrees * M_PI / 180.0; }
static inline float deg2rad(double degrees)
{
return degrees * M_PI / 180.0;
}

/// @brief Converts radians to degrees
/// @param radians
/// @return degrees
static inline float rad2deg(double radians) { return radians * 180.0 / M_PI; }
static inline float rad2deg(double radians)
{
return radians * 180.0 / M_PI;
}
} // namespace drivers
} // namespace nebula

Expand Down
202 changes: 202 additions & 0 deletions nebula_common/include/nebula_common/robosense/robosense_common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
#pragma once

#include "nebula_common/nebula_common.hpp"
#include "nebula_common/nebula_status.hpp"

#include <bitset>
#include <cmath>
#include <fstream>
#include <iostream>
#include <optional>
#include <sstream>
#include <vector>

namespace nebula
{
namespace drivers
{

// Flag for detecting Bpearl version
constexpr uint8_t BPEARL_V4_FLAG = 0x04;

/// @brief struct for Robosense sensor configuration
struct RobosenseSensorConfiguration : SensorConfigurationBase
{
uint16_t gnss_port{}; // difop
double scan_phase{}; // start/end angle
double dual_return_distance_threshold{};
uint16_t rotation_speed;
uint16_t cloud_min_angle;
uint16_t cloud_max_angle;
};

/// @brief Convert RobosenseSensorConfiguration to string (Overloading the << operator)
/// @param os
/// @param arg
/// @return stream
inline std::ostream & operator<<(std::ostream & os, RobosenseSensorConfiguration const & arg)
{
os << (SensorConfigurationBase)(arg) << ", GnssPort: " << arg.gnss_port
<< ", ScanPhase:" << arg.scan_phase << ", RotationSpeed:" << arg.rotation_speed
<< ", FOV(Start):" << arg.cloud_min_angle << ", FOV(End):" << arg.cloud_max_angle;
return os;
}

/// @brief Convert return mode name to ReturnMode enum (Robosense-specific ReturnModeFromString)
/// @param return_mode Return mode name (Upper and lower case letters must match)
/// @return Corresponding ReturnMode
inline ReturnMode ReturnModeFromStringRobosense(const std::string & return_mode)
{
if (return_mode == "Dual") return ReturnMode::DUAL;
if (return_mode == "Strongest") return ReturnMode::SINGLE_STRONGEST;
if (return_mode == "Last") return ReturnMode::SINGLE_LAST;
if (return_mode == "First") return ReturnMode::SINGLE_FIRST;

return ReturnMode::UNKNOWN;
}

size_t GetChannelSize(const SensorModel & model)
{
switch (model) {
case SensorModel::ROBOSENSE_BPEARL_V3:
return 32;
case SensorModel::ROBOSENSE_HELIOS:
return 32;
}
}

struct ChannelCorrection
{
float azimuth{NAN};
float elevation{NAN};
uint16_t channel{};

[[nodiscard]] bool has_value() const { return !std::isnan(azimuth) && !std::isnan(elevation); }
};

/// @brief struct for Robosense calibration configuration
struct RobosenseCalibrationConfiguration : CalibrationConfigurationBase
{
std::vector<ChannelCorrection> calibration;

void SetChannelSize(const size_t channel_num) { calibration.resize(channel_num); }

template <typename stream_t>
inline nebula::Status LoadFromStream(stream_t & stream)
{
std::string header;
std::getline(stream, header);

char sep;
int laser_id;
float elevation;
float azimuth;
Status load_status = Status::OK;
for (size_t i = 0; i < calibration.size(); ++i) {
stream >> laser_id >> sep >> elevation >> sep >> azimuth;

if (laser_id <= 0 || laser_id > calibration.size()) {
std::cout << "Invalid laser id: " << laser_id << std::endl;
load_status = Status::INVALID_CALIBRATION_FILE;
}
if (std::isnan(elevation) || std::isnan(azimuth)) {
std::cout << "Invalid calibration data" << laser_id << "," << elevation << "," << azimuth
<< std::endl;
load_status = Status::INVALID_CALIBRATION_FILE;
}
if (
calibration[laser_id - 1].has_value() && calibration[laser_id - 1].elevation != elevation &&
calibration[laser_id - 1].azimuth != azimuth) {
std::cout << "Duplicate calibration data for laser id: " << laser_id << std::endl;
load_status = Status::INVALID_CALIBRATION_FILE;
}

ChannelCorrection correction{azimuth, elevation};
calibration[laser_id - 1] = correction;
}

for (auto & calib : calibration) {
if (!calib.has_value()) {
std::cout << calib.elevation << "," << calib.azimuth << std::endl;
std::cout << "Missing calibration data" << std::endl;
load_status = Status::INVALID_CALIBRATION_FILE;
}
}

if (load_status != Status::OK) {
for (auto & correction : calibration) {
correction.elevation = NAN;
correction.azimuth = NAN;
}
}

return load_status;
}

inline nebula::Status LoadFromFile(const std::string & calibration_file)
{
std::ifstream ifs(calibration_file);
if (!ifs) {
return Status::INVALID_CALIBRATION_FILE;
}

const auto status = LoadFromStream(ifs);
ifs.close();
return status;
}

/// @brief Loading calibration data (not used)
/// @param calibration_content
/// @return Resulting status
inline nebula::Status LoadFromString(const std::string & calibration_content)
{
std::stringstream ss;
ss << calibration_content;

const auto status = LoadFromStream(ss);
return status;
}

// inline nebula::Status LoadFromDifop(const std::string & calibration_file)

/// @brief Saving calibration data (not used)
/// @param calibration_file
/// @return Resulting status
inline nebula::Status SaveFile(const std::string & calibration_file)
{
std::ofstream ofs(calibration_file);
if (!ofs) {
return Status::CANNOT_SAVE_FILE;
}
ofs << "Laser id,Elevation,Azimuth" << std::endl;

for (size_t i = 0; i < calibration.size(); ++i) {
auto laser_id = i + 1;
float elevation = calibration[i].elevation;
float azimuth = calibration[i].azimuth;
ofs << laser_id << "," << elevation << "," << azimuth << std::endl;
}

ofs.close();
return Status::OK;
}

[[nodiscard]] inline ChannelCorrection GetCorrection(const size_t channel_id) const
{
return calibration[channel_id];
}

void CreateCorrectedChannels()
{
for(auto& correction : calibration) {
uint16_t channel = 0;
for(const auto& compare:calibration) {
if(compare.elevation < correction.elevation) ++channel;
}
correction.channel = channel;
}
}
};

} // namespace drivers
} // namespace nebula
10 changes: 10 additions & 0 deletions nebula_decoders/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ find_package(PCL REQUIRED)
find_package(pcl_conversions REQUIRED)
find_package(yaml-cpp REQUIRED)
find_package(nebula_common REQUIRED)
find_package(robosense_msgs REQUIRED)

include_directories(
include
Expand All @@ -41,6 +42,15 @@ ament_auto_add_library(nebula_decoders_velodyne SHARED
src/nebula_decoders_velodyne/decoders/vlp32_decoder.cpp
)

# Robosense
ament_auto_add_library(nebula_decoders_robosense SHARED
src/nebula_decoders_robosense/robosense_driver.cpp
)

ament_auto_add_library(nebula_decoders_robosense_info SHARED
src/nebula_decoders_robosense/robosense_info_driver.cpp
)

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
Expand Down
Loading
Loading