基于RO2 Humble平台配置32线禾赛雷达,需要遵循以下步骤:### 1. 确认硬件兼容性- **RO2 Humble平台**:确认你的RO2 Humble平台是否支持32线禾赛雷达。- **禾赛雷达型号**:确保你购买的禾赛雷达型号与RO2 Humb
摘要:博客地址:https:www.cnblogs.comzylyehuo 雷达型号:32线禾赛 IMU 型号:ALUBI lpms cu3 使用环境: RO2 humble 首先将雷达以及 IMU 都连接到电脑上 配置 32线禾赛雷达
博客地址:https://www.cnblogs.com/zylyehuo/
雷达型号:32线禾赛
IMU 型号:ALUBI lpms cu3
使用环境: RO2 humble
首先将雷达以及 IMU 都连接到电脑上
配置 32线禾赛雷达
查看雷达 IP 地址
安装辅助工具
sudo apt install wireshark-qt
启动辅助工具
sudo wireshark
记录下这个 IP 地址
在网页中访问雷达 IP 地址
为电脑配置 IP 地址
测试电脑能否 ping 通雷达
配置 ALUBI lpms cu3
为 IMU 的 USB 接口赋予权限
ls /dev/tty # 双击 Tab 键
sudo chmod 777 /dev/ttyUSB0
下载并编译驱动功能包
sudo apt install libpcap-dev libyaml-cpp-dev
sudo apt-get update
mkdir -p driver_ws/src
cd driver_ws/src
git clone --recurse-submodules https://github.com/HesaiTechnology/HesaiLidar_ROS_2.0.git
cd ..
colcon build --symlink-install
. install/local_setup.bash
cd driver_ws/src
git clone https://bitbucket.org/lpresearch/lpmsig1opensourcelib
cd lpmsig1opensourcelib/
mkdir build
cd build/
cmake ..
make
make package
sudo dpkg -i libLpmsIG1_OpenSource-0.3.3-Linux.deb
测试
测试 32线禾赛雷达
启动 launch 文件进行测试
source install/setup.bash
ros2 launch hesai_ros_driver start.py
测试 ALUBI lpms cu3
编译 linux 示例程序
cd /home/reallab/driver_ws/src/lpmsig1opensourcelib
cd linux_example/
mkdir build
cd build
cmake ..
make
sudo chmod 777 /dev/ttyUSB0
./LpmsIG1_SimpleExample /dev/ttyUSB0 921600
运行 ROS2 节点
添加 lpms_curs3_node.cpp
#include <string>
#include <thread>
#include <chrono>
#include "rclcpp/rclcpp.hpp"
#include "sensor_msgs/msg/imu.hpp"
#include "sensor_msgs/msg/magnetic_field.hpp"
#include "sensor_msgs/msg/nav_sat_fix.hpp"
#include "sensor_msgs/msg/nav_sat_status.hpp"
#include "std_srvs/srv/set_bool.hpp"
#include "std_srvs/srv/trigger.hpp"
#include "std_msgs/msg/bool.hpp"
#include "lpsensor/LpmsIG1I.h"
#include "lpsensor/SensorDataI.h"
#include "lpsensor/LpmsIG1Registers.h"
struct IG1Command
{
short command;
union Data {
uint32_t i[64];
float f[64];
unsigned char c[256];
} data;
int dataLength;
};
class LpIG1Proxy: public rclcpp::Node
{
public:
rclcpp::TimerBase::SharedPtr updateTimer;
// Publisher
rclcpp::Publisher<sensor_msgs::msg::Imu>::SharedPtr imu_pub;
rclcpp::Publisher<sensor_msgs::msg::MagneticField>::SharedPtr mag_pub;
rclcpp::Publisher<std_msgs::msg::Bool>::SharedPtr autocalibration_status_pub;
// Service
rclcpp::Service<std_srvs::srv::SetBool>::SharedPtr autocalibration_serv;
rclcpp::Service<std_srvs::srv::SetBool>::SharedPtr autoReconnect_serv;
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr gyrocalibration_serv;
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr resetHeading_serv;
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr getImuData_serv;
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr setStreamingMode_serv;
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr setCommandMode_serv;
sensor_msgs::msg::Imu imu_msg;
sensor_msgs::msg::MagneticField mag_msg;
// Parameters
std::string comportNo;
int baudrate;
bool autoReconnect;
std::string frame_id;
int rate;
LpIG1Proxy()
: Node("lpms_ig1_node")
{
// Get node parameters
this->declare_parameter<std::string>("port", "/dev/ttyUSB0");
this->declare_parameter<int>("baudrate", 921600);
this->declare_parameter<bool>("autoreconnect", true);
this->declare_parameter<std::string>("frame_id", "imu");
this->declare_parameter<int>("rate", 200);
this->get_parameter("port", comportNo);
this->get_parameter("baudrate", baudrate);
this->get_parameter("autoreconnect", autoReconnect);
this->get_parameter("frame_id", frame_id);
this->get_parameter("rate", rate);
// Create LpmsIG1 object
sensor1 = IG1Factory();
sensor1->setVerbose(VERBOSE_INFO);
sensor1->setAutoReconnectStatus(autoReconnect);
RCLCPP_INFO(this->get_logger(), "Settings");
RCLCPP_INFO(this->get_logger(), "Port: %s", comportNo.c_str());
RCLCPP_INFO(this->get_logger(), "Baudrate: %d", baudrate);
RCLCPP_INFO(this->get_logger(), "Auto reconnect: %s", autoReconnect? "Enabled":"Disabled");
imu_pub = this->create_publisher<sensor_msgs::msg::Imu>("data",1);
mag_pub = this->create_publisher<sensor_msgs::msg::MagneticField>("mag",1);
autocalibration_status_pub = this->create_publisher<std_msgs::msg::Bool>("is_autocalibration_active",1);
autocalibration_serv = this->create_service<std_srvs::srv::SetBool>(
"enable_gyro_autocalibration",std::bind(&LpIG1Proxy::setAutocalibration, this,std::placeholders::_1, std::placeholders::_2));
autoReconnect_serv = this->create_service<std_srvs::srv::SetBool>(
"enable_auto_reconnect",std::bind(&LpIG1Proxy::setAutoReconnect, this, std::placeholders::_1, std::placeholders::_2));
gyrocalibration_serv = this->create_service<std_srvs::srv::Trigger>(
"calibrate_gyroscope",std::bind(&LpIG1Proxy::calibrateGyroscope, this, std::placeholders::_1, std::placeholders::_2));
resetHeading_serv = this->create_service<std_srvs::srv::Trigger>(
"reset_heading",std::bind(&LpIG1Proxy::resetHeading, this, std::placeholders::_1, std::placeholders::_2));
getImuData_serv = this->create_service<std_srvs::srv::Trigger>(
"get_imu_data",std::bind(&LpIG1Proxy::getImuData, this, std::placeholders::_1, std::placeholders::_2));
setStreamingMode_serv = this->create_service<std_srvs::srv::Trigger>(
"set_streaming_mode",std::bind(&LpIG1Proxy::setStreamingMode, this, std::placeholders::_1, std::placeholders::_2));
setCommandMode_serv = this->create_service<std_srvs::srv::Trigger>(
"set_command_mode",std::bind(&LpIG1Proxy::setCommandMode, this, std::placeholders::_1, std::placeholders::_2));
// Connects to sensor
if (!sensor1->connect(comportNo, baudrate))
{
RCLCPP_ERROR(this->get_logger(), "Error connecting to sensor\n");
sensor1->release();
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
}
do
{
RCLCPP_INFO(this->get_logger(), "Waiting for sensor to connect %d", sensor1->getStatus());
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
} while(
rclcpp::ok() &&
(
!(sensor1->getStatus() == STATUS_CONNECTED) &&
!(sensor1->getStatus() == STATUS_CONNECTION_ERROR)
)
);
if (sensor1->getStatus() == STATUS_CONNECTED)
{
RCLCPP_INFO(this->get_logger(), "Sensor connected");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
sensor1->commandGotoStreamingMode();
}
else
{
RCLCPP_INFO(this->get_logger(), "Sensor connection error: %d.", sensor1->getStatus());
rclcpp::shutdown();
}
}
~LpIG1Proxy(void)
{
sensor1->release();
}
void update()
{
static bool runOnce = false;
if (sensor1->getStatus() == STATUS_CONNECTED &&
sensor1->hasImuData())
{
if (!runOnce)
{
publishIsAutocalibrationActive();
runOnce = true;
}
IG1ImuDataI sd;
sensor1->getImuData(sd);
/* Fill the IMU message */
// Fill the header
imu_msg.header.stamp = this->now();
imu_msg.header.frame_id = frame_id;
// Fill orientation quaternion
imu_msg.orientation.w = sd.quaternion.data[0];
imu_msg.orientation.x = sd.quaternion.data[1];
imu_msg.orientation.y = sd.quaternion.data[2];
imu_msg.orientation.z = sd.quaternion.data[3];
// Fill angular velocity data
// - scale from deg/s to rad/s
imu_msg.angular_velocity.x = sd.gyroIAlignmentCalibrated.data[0]*3.1415926/180;
imu_msg.angular_velocity.y = sd.gyroIAlignmentCalibrated.data[1]*3.1415926/180;
imu_msg.angular_velocity.z = sd.gyroIAlignmentCalibrated.data[2]*3.1415926/180;
// Fill linear acceleration data
imu_msg.linear_acceleration.x = -sd.accCalibrated.data[0]*9.81;
imu_msg.linear_acceleration.y = -sd.accCalibrated.data[1]*9.81;
imu_msg.linear_acceleration.z = -sd.accCalibrated.data[2]*9.81;
/* Fill the magnetometer message */
mag_msg.header.stamp = imu_msg.header.stamp;
mag_msg.header.frame_id = frame_id;
// Units are microTesla in the LPMS library, Tesla in ROS.
mag_msg.magnetic_field.x = sd.magRaw.data[0]*1e-6;
mag_msg.magnetic_field.y = sd.magRaw.data[1]*1e-6;
mag_msg.magnetic_field.z = sd.magRaw.data[2]*1e-6;
// Publish the messages
imu_pub->publish(imu_msg);
mag_pub->publish(mag_msg);
}
}
void run(void)
{
updateTimer = this->create_wall_timer(
std::chrono::milliseconds(1000 / abs(rate)),
std::bind(&LpIG1Proxy::update, this));
}
void publishIsAutocalibrationActive()
{
std_msgs::msg::Bool msg;
IG1SettingsI settings;
sensor1->getSettings(settings);
msg.data = settings.enableGyroAutocalibration;
autocalibration_status_pub->publish(msg);
}
///////////////////////////////////////////////////
// Service Callbacks
///////////////////////////////////////////////////
bool setAutocalibration (
const std::shared_ptr<std_srvs::srv::SetBool::Request> req,
std::shared_ptr<std_srvs::srv::SetBool::Response> res)
{
RCLCPP_INFO(this->get_logger(), "set_autocalibration");
// clear current settings
IG1SettingsI settings;
sensor1->getSettings(settings);
// Send command
cmdSetEnableAutocalibration(req->data);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
cmdGetEnableAutocalibration();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
double retryElapsedTime = 0;
int retryCount = 0;
while (!sensor1->hasSettings())
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
RCLCPP_INFO(this->get_logger(), "set_autocalibration wait");
retryElapsedTime += 0.1;
if (retryElapsedTime > 2.0)
{
retryElapsedTime = 0;
cmdGetEnableAutocalibration();
retryCount++;
}
if (retryCount > 5)
break;
}
RCLCPP_INFO(this->get_logger(), "set_autocalibration done");
// Get settings
sensor1->getSettings(settings);
std::string msg;
if (settings.enableGyroAutocalibration == req->data)
{
res->success = true;
msg.append(std::string("[Success] autocalibration status set to: ") + (settings.enableGyroAutocalibration?"True":"False"));
}
else
{
res->success = false;
msg.append(std::string("[Failed] current autocalibration status set to: ") + (settings.enableGyroAutocalibration?"True":"False"));
}
RCLCPP_INFO(this->get_logger(), "%s", msg.c_str());
res->message = msg;
publishIsAutocalibrationActive();
return res->success;
}
// Auto reconnect
bool setAutoReconnect (
const std::shared_ptr<std_srvs::srv::SetBool::Request> req,
std::shared_ptr<std_srvs::srv::SetBool::Response> res)
{
RCLCPP_INFO(this->get_logger(), "set_auto_reconnect");
sensor1->setAutoReconnectStatus(req->data);
res->success = true;
std::string msg;
msg.append(std::string("[Success] auto reconnection status set to: ") + (sensor1->getAutoReconnectStatus()?"True":"False"));
RCLCPP_INFO(this->get_logger(), "%s", msg.c_str());
res->message = msg;
return res->success;
}
// reset heading
bool resetHeading (
const std::shared_ptr<std_srvs::srv::Trigger::Request> req,
std::shared_ptr<std_srvs::srv::Trigger::Response> res)
{
RCLCPP_INFO(this->get_logger(), "reset_heading");
// Send command
cmdResetHeading();
res->success = true;
res->message = "[Success] Heading reset";
return true;
}
bool calibrateGyroscope (
const std::shared_ptr<std_srvs::srv::Trigger::Request> req,
std::shared_ptr<std_srvs::srv::Trigger::Response> res)
{
RCLCPP_INFO(this->get_logger(), "calibrate_gyroscope: Please make sure the sensor is stationary for 4 seconds");
cmdCalibrateGyroscope();
std::this_thread::sleep_for(std::chrono::milliseconds(4000));
res->success = true;
res->message = "[Success] Gyroscope calibration procedure completed";
RCLCPP_INFO(this->get_logger(), "calibrate_gyroscope: Gyroscope calibration procedure completed");
return true;
}
bool getImuData (
const std::shared_ptr<std_srvs::srv::Trigger::Request> req,
std::shared_ptr<std_srvs::srv::Trigger::Response> res)
{
cmdGotoCommandMode();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
cmdGetImuData();
res->success = true;
res->message = "[Success] Get imu data";
return true;
}
bool setStreamingMode (
const std::shared_ptr<std_srvs::srv::Trigger::Request> req,
std::shared_ptr<std_srvs::srv::Trigger::Response> res)
{
cmdGotoStreamingMode();
res->success = true;
res->message = "[Success] Set streaming mode";
return true;
}
bool setCommandMode (
const std::shared_ptr<std_srvs::srv::Trigger::Request> req,
std::shared_ptr<std_srvs::srv::Trigger::Response> res)
{
cmdGotoCommandMode();
res->success = true;
res->message = "[Success] Set command mode";
return true;
}
///////////////////////////////////////////////////
// Helpers
///////////////////////////////////////////////////
void cmdGotoCommandMode ()
{
IG1Command cmd;
cmd.command = GOTO_COMMAND_MODE;
cmd.dataLength = 0;
sensor1->sendCommand(cmd.command, cmd.dataLength, cmd.data.c);
}
void cmdGotoStreamingMode ()
{
IG1Command cmd;
cmd.command = GOTO_STREAM_MODE;
cmd.dataLength = 0;
sensor1->sendCommand(cmd.command, cmd.dataLength, cmd.data.c);
}
void cmdGetImuData()
{
IG1Command cmd;
cmd.command = GET_IMU_DATA;
cmd.dataLength = 0;
sensor1->sendCommand(cmd.command, cmd.dataLength, cmd.data.c);
}
void cmdCalibrateGyroscope()
{
IG1Command cmd;
cmd.command = START_GYR_CALIBRATION;
cmd.dataLength = 0;
sensor1->sendCommand(cmd.command, cmd.dataLength, cmd.data.c);
}
void cmdResetHeading()
{
IG1Command cmd;
cmd.command = SET_ORIENTATION_OFFSET;
cmd.dataLength = 4;
cmd.data.i[0] = LPMS_OFFSET_MODE_HEADING;
sensor1->sendCommand(cmd.command, cmd.dataLength, cmd.data.c);
}
void cmdSetEnableAutocalibration(int status)
{
IG1Command cmd;
cmd.command = SET_ENABLE_GYR_AUTOCALIBRATION;
cmd.dataLength = 4;
cmd.data.i[0] = status;
sensor1->sendCommand(cmd.command, cmd.dataLength, cmd.data.c);
}
void cmdGetEnableAutocalibration()
{
IG1Command cmd;
cmd.command = GET_ENABLE_GYR_AUTOCALIBRATION;
cmd.dataLength = 0;
sensor1->sendCommand(cmd.command, cmd.dataLength, cmd.data.c);
}
private:
// Access to LPMS data
IG1I* sensor1;
};
int main(int argc, char *argv[])
{
rclcpp::init(argc, argv);
auto lpIG1 = std::make_shared<LpIG1Proxy>();
lpIG1->run();
rclcpp::spin(lpIG1);
rclcpp::shutdown();
return 0;
}
修改 CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(lpms_ig1)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(std_srvs REQUIRED)
find_package(tf2 REQUIRED)
add_executable(lpms_ig1_node
src/lpms_ig1_node.cpp
)
add_executable(lpms_be1_node
src/lpms_be1_node.cpp
)
add_executable(lpms_nav3_node
src/lpms_nav3_node.cpp
)
add_executable(lpms_ig1_rs485_node
src/lpms_ig1_rs485_node.cpp
)
add_executable(lpms_ig1_rs485_client
src/lpms_ig1_rs485_client.cpp
)
add_executable(lpms_si1_node
src/lpms_si1_node.cpp
)
add_executable(quat_to_euler_node
src/quat_to_euler_node.cpp
)
add_executable(imudata_rad_to_deg_node
src/imudata_rad_to_deg_node.cpp
)
# 新增的 lpms_curs3_node
add_executable(lpms_curs3_node
src/lpms_curs3_node.cpp
)
target_link_libraries(lpms_ig1_node LpmsIG1_OpenSourceLib)
target_link_libraries(lpms_be1_node LpmsIG1_OpenSourceLib)
target_link_libraries(lpms_nav3_node LpmsIG1_OpenSourceLib)
target_link_libraries(lpms_ig1_rs485_node LpmsIG1_OpenSourceLib)
target_link_libraries(lpms_si1_node LpmsIG1_OpenSourceLib)
# 新增的 lpms_curs3_node 链接库
target_link_libraries(lpms_curs3_node LpmsIG1_OpenSourceLib)
ament_target_dependencies(lpms_ig1_node rclcpp std_msgs std_srvs sensor_msgs)
ament_target_dependencies(lpms_be1_node rclcpp std_msgs std_srvs sensor_msgs)
ament_target_dependencies(lpms_nav3_node rclcpp std_msgs std_srvs sensor_msgs)
ament_target_dependencies(lpms_ig1_rs485_node rclcpp std_msgs std_srvs sensor_msgs)
ament_target_dependencies(lpms_ig1_rs485_client rclcpp std_srvs)
ament_target_dependencies(lpms_si1_node rclcpp std_msgs std_srvs sensor_msgs)
ament_target_dependencies(quat_to_euler_node rclcpp sensor_msgs tf2)
ament_target_dependencies(imudata_rad_to_deg_node rclcpp sensor_msgs tf2)
# 新增的 lpms_curs3_node 依赖
ament_target_dependencies(lpms_curs3_node rclcpp std_msgs std_srvs sensor_msgs)
install(TARGETS lpms_ig1_node
DESTINATION lib/${PROJECT_NAME})
install(TARGETS lpms_be1_node
DESTINATION lib/${PROJECT_NAME})
install(TARGETS lpms_nav3_node
DESTINATION lib/${PROJECT_NAME})
install(TARGETS lpms_ig1_rs485_node
DESTINATION lib/${PROJECT_NAME})
install(TARGETS lpms_ig1_rs485_client
DESTINATION lib/${PROJECT_NAME})
install(TARGETS lpms_si1_node
DESTINATION lib/${PROJECT_NAME})
install(TARGETS quat_to_euler_node
DESTINATION lib/${PROJECT_NAME})
install(TARGETS imudata_rad_to_deg_node
DESTINATION lib/${PROJECT_NAME})
# 新增的 lpms_curs3_node 安装
install(TARGETS lpms_curs3_node
DESTINATION lib/${PROJECT_NAME})
install(DIRECTORY
launch
DESTINATION share/${PROJECT_NAME}/
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
#set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
#set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
运行指令
cd /home/reallab/driver_ws
ln -s ./src/lpmsig1opensourcelib/ros2_example ./lpms_ig1_ros2_example
source /opt/ros/humble/setup.bash
colcon build
sudo chmod 777 /dev/ttyUSB0
source install/setup.bash
ros2 run lpms_ig1 lpms_curs3_node --ros-args --param port:=/dev/ttyUSB0 --param baudrate:=921600
rviz2
编译 ROS2 示例程序
cd /home/reallab/driver_ws
ln -s ./src/lpmsig1opensourcelib/ros2_example ./lpms_ig1_ros2_example
source /opt/ros/humble/setup.bash
colcon build
sudo chmod 777 /dev/ttyUSB0
source install/setup.bash
ros2 launch lpms_ig1 lpms_si1_launch.py
在 rviz 中可视化
sudo apt install ros-humble-rviz-imu-plugin
rviz2
