uss
This commit is contained in:
parent
3ba59b157b
commit
ca36d8289d
57
src/perception/uss/CMakeLists.txt
Normal file
57
src/perception/uss/CMakeLists.txt
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(uss)
|
||||||
|
|
||||||
|
# Default to C99
|
||||||
|
if(NOT CMAKE_C_STANDARD)
|
||||||
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Default to C++14
|
||||||
|
if(NOT CMAKE_CXX_STANDARD)
|
||||||
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
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(sweeper_interfaces REQUIRED)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
include
|
||||||
|
${catkin_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(uss_node
|
||||||
|
src/uss_node.cpp
|
||||||
|
src/uss.cpp
|
||||||
|
src/can_driver.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
ament_target_dependencies(uss_node
|
||||||
|
rclcpp
|
||||||
|
std_msgs
|
||||||
|
sweeper_interfaces
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS
|
||||||
|
uss_node
|
||||||
|
DESTINATION lib/${PROJECT_NAME}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(DIRECTORY
|
||||||
|
config
|
||||||
|
launch
|
||||||
|
DESTINATION share/${PROJECT_NAME}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
find_package(ament_lint_auto REQUIRED)
|
||||||
|
ament_lint_auto_find_test_dependencies()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
ament_package()
|
||||||
12
src/perception/uss/config/uss_params.yaml
Normal file
12
src/perception/uss/config/uss_params.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
uss_node:
|
||||||
|
ros__parameters:
|
||||||
|
can_interface: "can0"
|
||||||
|
publish_rate: 10
|
||||||
|
sonic_1: 0
|
||||||
|
sonic_2: 0
|
||||||
|
sonic_3: 0
|
||||||
|
sonic_4: 0
|
||||||
|
sonic_5: 0
|
||||||
|
sonic_6: 0
|
||||||
|
sonic_7: 0
|
||||||
|
sonic_8: 0
|
||||||
70
src/perception/uss/include/can_driver.h
Normal file
70
src/perception/uss/include/can_driver.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#ifndef CAN_DRIVER_H
|
||||||
|
#define CAN_DRIVER_H
|
||||||
|
|
||||||
|
#include <linux/can.h>
|
||||||
|
#include <linux/can/raw.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct CANFrame
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
uint8_t data[8];
|
||||||
|
uint8_t dlc;
|
||||||
|
bool ext; // 扩展帧
|
||||||
|
bool rtr; // 远程帧
|
||||||
|
};
|
||||||
|
|
||||||
|
class CANDriver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ReceiveCallback = std::function<void(const CANFrame&, void*)>;
|
||||||
|
|
||||||
|
CANDriver();
|
||||||
|
~CANDriver();
|
||||||
|
|
||||||
|
// 打开CAN接口
|
||||||
|
bool open(const std::string& interface);
|
||||||
|
|
||||||
|
// 关闭CAN接口
|
||||||
|
void close();
|
||||||
|
|
||||||
|
// 发送CAN帧
|
||||||
|
bool sendFrame(const CANFrame& frame);
|
||||||
|
|
||||||
|
// 设置接收回调
|
||||||
|
void setReceiveCallback(ReceiveCallback callback, void* userData = nullptr);
|
||||||
|
|
||||||
|
// 设置硬件过滤规则
|
||||||
|
bool setFilter(const std::vector<can_filter>& filters);
|
||||||
|
|
||||||
|
// 追加一个过滤器
|
||||||
|
bool addFilter(const can_filter& filter);
|
||||||
|
|
||||||
|
// 追加一组过滤器
|
||||||
|
bool addFilters(const std::vector<can_filter>& filters);
|
||||||
|
|
||||||
|
// 检查是否已打开
|
||||||
|
bool isOpen() const { return running; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void receiveThreadFunc();
|
||||||
|
bool applyFilters(); // 应用当前filters_
|
||||||
|
|
||||||
|
int sockfd = -1;
|
||||||
|
std::atomic<bool> running{false};
|
||||||
|
std::thread receiveThread;
|
||||||
|
ReceiveCallback callback;
|
||||||
|
void* userData = nullptr;
|
||||||
|
std::vector<can_filter> filters_; // 当前所有过滤器
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CAN_DRIVER_H
|
||||||
49
src/perception/uss/include/uss.h
Normal file
49
src/perception/uss/include/uss.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef USS_H
|
||||||
|
#define USS_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "can_driver.h"
|
||||||
|
|
||||||
|
class UssDriver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UssDriver();
|
||||||
|
~UssDriver();
|
||||||
|
|
||||||
|
bool init(const std::string& can_interface = "can0");
|
||||||
|
void close();
|
||||||
|
|
||||||
|
bool isOpen() const { return can_driver_.isOpen(); }
|
||||||
|
|
||||||
|
struct UssData
|
||||||
|
{
|
||||||
|
uint8_t distances[8]; // 8路超声波距离数据 (cm)
|
||||||
|
uint8_t distances_calib[8]; // 8路校准后距离数据 (cm)
|
||||||
|
bool valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
const UssData& getUssData() const;
|
||||||
|
|
||||||
|
void setCalibration(int index, int offset);
|
||||||
|
void setAllCalibrations(const int offsets[8]);
|
||||||
|
|
||||||
|
void setPrintDebug(bool enable) { print_debug_ = enable; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void canReceiveCallback(const CANFrame& frame, void* user_data);
|
||||||
|
void processCanFrame(const CANFrame& frame);
|
||||||
|
|
||||||
|
CANDriver can_driver_;
|
||||||
|
UssData uss_data_;
|
||||||
|
mutable std::mutex data_mutex_;
|
||||||
|
|
||||||
|
int calibration_offsets_[8];
|
||||||
|
bool print_debug_;
|
||||||
|
bool initialized_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USS_H
|
||||||
23
src/perception/uss/launch/uss.launch.py
Normal file
23
src/perception/uss/launch/uss.launch.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import os
|
||||||
|
from ament_index_python.packages import get_package_share_directory
|
||||||
|
from launch import LaunchDescription
|
||||||
|
from launch_ros.actions import Node
|
||||||
|
|
||||||
|
|
||||||
|
def generate_launch_description():
|
||||||
|
# 获取参数文件路径
|
||||||
|
config = os.path.join(
|
||||||
|
get_package_share_directory('uss'),
|
||||||
|
'config',
|
||||||
|
'uss_params.yaml'
|
||||||
|
)
|
||||||
|
|
||||||
|
return LaunchDescription([
|
||||||
|
Node(
|
||||||
|
package='uss',
|
||||||
|
executable='uss_node',
|
||||||
|
name='uss_node',
|
||||||
|
output='screen',
|
||||||
|
parameters=[config]
|
||||||
|
)
|
||||||
|
])
|
||||||
22
src/perception/uss/package.xml
Normal file
22
src/perception/uss/package.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
|
<package format="3">
|
||||||
|
<name>uss</name>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
<description>Ultrasonic Sensor (USS) ROS2 package for sweeper</description>
|
||||||
|
<maintainer email="zxwl@56.com">root</maintainer>
|
||||||
|
<license>MIT</license>
|
||||||
|
|
||||||
|
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||||
|
|
||||||
|
<depend>rclcpp</depend>
|
||||||
|
<depend>std_msgs</depend>
|
||||||
|
<depend>sweeper_interfaces</depend>
|
||||||
|
|
||||||
|
<test_depend>ament_lint_auto</test_depend>
|
||||||
|
<test_depend>ament_lint_common</test_depend>
|
||||||
|
|
||||||
|
<export>
|
||||||
|
<build_type>ament_cmake</build_type>
|
||||||
|
</export>
|
||||||
|
</package>
|
||||||
176
src/perception/uss/src/can_driver.cpp
Normal file
176
src/perception/uss/src/can_driver.cpp
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#include "can_driver.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/can.h>
|
||||||
|
#include <linux/can/raw.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
CANDriver::CANDriver() = default;
|
||||||
|
|
||||||
|
CANDriver::~CANDriver() { close(); }
|
||||||
|
|
||||||
|
bool CANDriver::open(const std::string& interface)
|
||||||
|
{
|
||||||
|
if (running) return false;
|
||||||
|
|
||||||
|
sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
|
||||||
|
if (sockfd < 0)
|
||||||
|
{
|
||||||
|
perror("socket");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ifreq ifr;
|
||||||
|
std::strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ - 1);
|
||||||
|
ifr.ifr_name[IFNAMSIZ - 1] = '\0';
|
||||||
|
|
||||||
|
if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0)
|
||||||
|
{
|
||||||
|
perror("ioctl");
|
||||||
|
::close(sockfd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_can addr{};
|
||||||
|
addr.can_family = AF_CAN;
|
||||||
|
addr.can_ifindex = ifr.ifr_ifindex;
|
||||||
|
|
||||||
|
if (bind(sockfd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0)
|
||||||
|
{
|
||||||
|
perror("bind");
|
||||||
|
::close(sockfd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置为非阻塞
|
||||||
|
int flags = fcntl(sockfd, F_GETFL, 0);
|
||||||
|
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
running = true;
|
||||||
|
receiveThread = std::thread(&CANDriver::receiveThreadFunc, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CANDriver::close()
|
||||||
|
{
|
||||||
|
if (!running) return;
|
||||||
|
|
||||||
|
running = false;
|
||||||
|
if (receiveThread.joinable()) receiveThread.join();
|
||||||
|
|
||||||
|
if (sockfd >= 0)
|
||||||
|
{
|
||||||
|
::close(sockfd);
|
||||||
|
sockfd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CANDriver::sendFrame(const CANFrame& frame)
|
||||||
|
{
|
||||||
|
if (!running) return false;
|
||||||
|
|
||||||
|
struct can_frame raw_frame{};
|
||||||
|
raw_frame.can_id = frame.id;
|
||||||
|
if (frame.ext) raw_frame.can_id |= CAN_EFF_FLAG;
|
||||||
|
if (frame.rtr) raw_frame.can_id |= CAN_RTR_FLAG;
|
||||||
|
raw_frame.can_dlc = frame.dlc;
|
||||||
|
std::memcpy(raw_frame.data, frame.data, frame.dlc);
|
||||||
|
|
||||||
|
if (write(sockfd, &raw_frame, sizeof(raw_frame)) != sizeof(raw_frame))
|
||||||
|
{
|
||||||
|
perror("write");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CANDriver::setReceiveCallback(ReceiveCallback callback, void* userData)
|
||||||
|
{
|
||||||
|
this->callback = callback;
|
||||||
|
this->userData = userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CANDriver::setFilter(const std::vector<can_filter>& filters)
|
||||||
|
{
|
||||||
|
if (!running) return false;
|
||||||
|
|
||||||
|
filters_ = filters;
|
||||||
|
return applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CANDriver::addFilter(const can_filter& filter)
|
||||||
|
{
|
||||||
|
filters_.push_back(filter);
|
||||||
|
return applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CANDriver::addFilters(const std::vector<can_filter>& filters)
|
||||||
|
{
|
||||||
|
filters_.insert(filters_.end(), filters.begin(), filters.end());
|
||||||
|
return applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CANDriver::applyFilters()
|
||||||
|
{
|
||||||
|
if (!running) return false;
|
||||||
|
|
||||||
|
if (filters_.empty())
|
||||||
|
{
|
||||||
|
setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, nullptr, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, filters_.data(), filters_.size() * sizeof(can_filter)) < 0)
|
||||||
|
{
|
||||||
|
perror("setsockopt");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CANDriver::receiveThreadFunc()
|
||||||
|
{
|
||||||
|
struct can_frame raw_frame;
|
||||||
|
struct pollfd fds;
|
||||||
|
fds.fd = sockfd;
|
||||||
|
fds.events = POLLIN;
|
||||||
|
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
int ret = poll(&fds, 1, 100); // 等待最多100ms
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
perror("poll");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (ret == 0)
|
||||||
|
{
|
||||||
|
// 超时无数据,可继续
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t nbytes = read(sockfd, &raw_frame, sizeof(raw_frame));
|
||||||
|
if (nbytes == sizeof(raw_frame) && callback)
|
||||||
|
{
|
||||||
|
CANFrame frame;
|
||||||
|
frame.id = raw_frame.can_id & CAN_EFF_MASK;
|
||||||
|
frame.ext = !!(raw_frame.can_id & CAN_EFF_FLAG);
|
||||||
|
frame.rtr = !!(raw_frame.can_id & CAN_RTR_FLAG);
|
||||||
|
frame.dlc = raw_frame.can_dlc;
|
||||||
|
memcpy(frame.data, raw_frame.data, raw_frame.can_dlc);
|
||||||
|
|
||||||
|
// 调用回调函数,处理接收到的帧
|
||||||
|
callback(frame, userData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
153
src/perception/uss/src/uss.cpp
Normal file
153
src/perception/uss/src/uss.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#include "uss.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// 超声波传感器的CAN ID定义 (J1939格式: 0x18EC5700)
|
||||||
|
// Priority: 0x18, PGN: 0xEC00 (60416), Device ID: 0x57 (87)
|
||||||
|
constexpr uint32_t USS_CAN_ID = 0x18EC5700;
|
||||||
|
constexpr uint32_t USS_CAN_MASK = 0xFFFFFFFF; // 精确匹配
|
||||||
|
|
||||||
|
UssDriver::UssDriver() : print_debug_(false), initialized_(false)
|
||||||
|
{
|
||||||
|
// 初始化校准偏移
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
calibration_offsets_[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
uss_data_.valid = false;
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
uss_data_.distances[i] = 0;
|
||||||
|
uss_data_.distances_calib[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UssDriver::~UssDriver() { close(); }
|
||||||
|
|
||||||
|
bool UssDriver::init(const std::string& can_interface)
|
||||||
|
{
|
||||||
|
if (initialized_)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!can_driver_.open(can_interface))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to open CAN interface: " << can_interface << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置CAN过滤器,只接收超声波的CAN数据
|
||||||
|
struct can_filter filter;
|
||||||
|
filter.can_id = USS_CAN_ID;
|
||||||
|
filter.can_mask = USS_CAN_MASK;
|
||||||
|
std::vector<struct can_filter> filters;
|
||||||
|
filters.push_back(filter);
|
||||||
|
|
||||||
|
if (!can_driver_.setFilter(filters))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to set CAN filter for USS" << std::endl;
|
||||||
|
can_driver_.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
can_driver_.setReceiveCallback(canReceiveCallback, this);
|
||||||
|
initialized_ = true;
|
||||||
|
|
||||||
|
if (print_debug_)
|
||||||
|
{
|
||||||
|
std::cout << "USS driver initialized successfully on " << can_interface << std::endl;
|
||||||
|
std::cout << "CAN filter set for ID: 0x" << std::hex << USS_CAN_ID << std::dec << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UssDriver::close()
|
||||||
|
{
|
||||||
|
if (initialized_)
|
||||||
|
{
|
||||||
|
can_driver_.close();
|
||||||
|
initialized_ = false;
|
||||||
|
if (print_debug_)
|
||||||
|
{
|
||||||
|
std::cout << "USS driver closed" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const UssDriver::UssData& UssDriver::getUssData() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||||
|
return uss_data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UssDriver::setCalibration(int index, int offset)
|
||||||
|
{
|
||||||
|
if (index >= 0 && index < 8)
|
||||||
|
{
|
||||||
|
calibration_offsets_[index] = offset;
|
||||||
|
// 更新校准后的数据
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||||
|
int calib_distance = static_cast<int>(uss_data_.distances[index]) + offset;
|
||||||
|
uss_data_.distances_calib[index] = static_cast<uint8_t>(std::max(0, std::min(255, calib_distance)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UssDriver::setAllCalibrations(const int offsets[8])
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
calibration_offsets_[i] = offsets[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
int calib_distance = static_cast<int>(uss_data_.distances[i]) + calibration_offsets_[i];
|
||||||
|
uss_data_.distances_calib[i] = static_cast<uint8_t>(std::max(0, std::min(255, calib_distance)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UssDriver::canReceiveCallback(const CANFrame& frame, void* user_data)
|
||||||
|
{
|
||||||
|
if (user_data == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UssDriver* driver = static_cast<UssDriver*>(user_data);
|
||||||
|
driver->processCanFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UssDriver::processCanFrame(const CANFrame& frame)
|
||||||
|
{
|
||||||
|
if (frame.id == USS_CAN_ID)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||||
|
|
||||||
|
// 更新原始数据
|
||||||
|
int data_len = std::min(static_cast<int>(frame.dlc), 8);
|
||||||
|
for (int i = 0; i < data_len; i++)
|
||||||
|
{
|
||||||
|
uss_data_.distances[i] = frame.data[i];
|
||||||
|
// 计算校准后的数据
|
||||||
|
int calib_distance = static_cast<int>(frame.data[i]) + calibration_offsets_[i];
|
||||||
|
uss_data_.distances_calib[i] = static_cast<uint8_t>(std::max(0, std::min(255, calib_distance)));
|
||||||
|
}
|
||||||
|
|
||||||
|
uss_data_.valid = true;
|
||||||
|
|
||||||
|
if (print_debug_)
|
||||||
|
{
|
||||||
|
std::cout << "Received USS data: ";
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
std::cout << static_cast<int>(uss_data_.distances[i]) << " ";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
109
src/perception/uss/src/uss_node.cpp
Normal file
109
src/perception/uss/src/uss_node.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "rclcpp/rclcpp.hpp"
|
||||||
|
#include "std_msgs/msg/int8_multi_array.hpp"
|
||||||
|
#include "uss.h"
|
||||||
|
|
||||||
|
class UssNode : public rclcpp::Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UssNode() : Node("uss_node")
|
||||||
|
{
|
||||||
|
RCLCPP_INFO(this->get_logger(), "USS node has been started.");
|
||||||
|
|
||||||
|
// 声明参数
|
||||||
|
this->declare_parameter<std::string>("can_interface", "can0");
|
||||||
|
this->declare_parameter<int>("publish_rate", 10); // 10Hz
|
||||||
|
|
||||||
|
// 声明超声波校准参数
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
this->declare_parameter<int>("sonic_" + std::to_string(i + 1), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取参数
|
||||||
|
std::string can_interface = this->get_parameter("can_interface").as_string();
|
||||||
|
int publish_rate = this->get_parameter("publish_rate").as_int();
|
||||||
|
|
||||||
|
// 初始化USS驱动
|
||||||
|
if (!uss_driver_.init(can_interface))
|
||||||
|
{
|
||||||
|
RCLCPP_ERROR(this->get_logger(), "Failed to initialize USS driver on interface %s", can_interface.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载超声波校准参数
|
||||||
|
int sonic[8];
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
sonic[i] = this->get_parameter("sonic_" + std::to_string(i + 1)).as_int();
|
||||||
|
}
|
||||||
|
uss_driver_.setAllCalibrations(sonic);
|
||||||
|
RCLCPP_INFO(this->get_logger(), "Config loaded successfully");
|
||||||
|
|
||||||
|
// 创建发布者
|
||||||
|
uss_publisher_ = this->create_publisher<std_msgs::msg::Int8MultiArray>("USS", 10);
|
||||||
|
|
||||||
|
// 创建定时器,定期发布数据
|
||||||
|
timer_ = this->create_wall_timer(std::chrono::milliseconds(1000 / publish_rate),
|
||||||
|
std::bind(&UssNode::timer_callback, this));
|
||||||
|
|
||||||
|
RCLCPP_INFO(this->get_logger(), "USS driver initialized successfully on %s", can_interface.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
~UssNode() { uss_driver_.close(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void timer_callback()
|
||||||
|
{
|
||||||
|
auto message = std_msgs::msg::Int8MultiArray();
|
||||||
|
const UssDriver::UssData& uss_data = uss_driver_.getUssData();
|
||||||
|
|
||||||
|
if (uss_data.valid)
|
||||||
|
{
|
||||||
|
message.data.clear();
|
||||||
|
message.data.resize(8);
|
||||||
|
|
||||||
|
message.data[0] = static_cast<int8_t>(uss_data.distances_calib[0]);
|
||||||
|
message.data[1] = static_cast<int8_t>(uss_data.distances_calib[1]);
|
||||||
|
message.data[2] = static_cast<int8_t>(uss_data.distances_calib[2]);
|
||||||
|
message.data[3] = static_cast<int8_t>(uss_data.distances_calib[3]);
|
||||||
|
message.data[4] = static_cast<int8_t>(uss_data.distances_calib[4]);
|
||||||
|
message.data[5] = static_cast<int8_t>(uss_data.distances_calib[5]);
|
||||||
|
message.data[6] = static_cast<int8_t>(uss_data.distances_calib[6]);
|
||||||
|
message.data[7] = static_cast<int8_t>(uss_data.distances_calib[7]);
|
||||||
|
|
||||||
|
// 发布消息
|
||||||
|
uss_publisher_->publish(message);
|
||||||
|
|
||||||
|
// 调试打印
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
RCLCPP_INFO(this->get_logger(), "USS %d: %d cm", i + 1, static_cast<int>(message.data[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RCLCPP_WARN_THROTTLE(this->get_logger(), *this->get_clock(), 1000, "No valid USS data received");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UssDriver uss_driver_;
|
||||||
|
rclcpp::TimerBase::SharedPtr timer_;
|
||||||
|
rclcpp::Publisher<std_msgs::msg::Int8MultiArray>::SharedPtr uss_publisher_;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
rclcpp::init(argc, argv);
|
||||||
|
auto node = std::make_shared<UssNode>();
|
||||||
|
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
rclcpp::spin(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
rclcpp::shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user