当前位置:   article > 正文

基于ROS 2的IMU驱动节点,C++版本,imu型号,维特的jy901b_ros2 维特imu

ros2 维特imu

python的代码网上有一堆,c++的没有找到,在此补充一下,实际测试运行好像c++占用cpu要比python低一些

一、头文件包含部分

  1. #include <chrono>
  2. #include <memory>
  3. #include <string>
  4. #include <vector>
  5. #include <thread>
  6. #include <rclcpp/rclcpp.hpp>
  7. #include <sensor_msgs/msg/imu.hpp>
  8. #include <termios.h>
  9. #include <fcntl.h>
  10. #include <unistd.h>
  11. #include <cmath>
  12. #include <array>

这些头文件包含了所需的标准C++库、ROS 2相关库以及串口通信相关的头文件。

二、类定义:IMUDriverNode

  1. class IMUDriverNode : public rclcpp::Node {
  2. public:
  3. IMUDriverNode()
  4. : Node("imu_driver_node") {
  5. // 创建IMU数据发布者
  6. imu_pub_ = this->create_publisher<sensor_msgs::msg::Imu>("imu", 10);
  7. // 声明ROS参数,并从参数服务器获取串口和波特率参数
  8. this->declare_parameter<std::string>("port", "/dev/ttyUSB0");
  9. this->declare_parameter<int>("baud", 115200);
  10. port_ = this->get_parameter("port").as_string();
  11. baud_rate_ = this->get_parameter("baud").as_int();
  12. // 打开串口
  13. serial_fd_ = open_serial_port(port_, baud_rate_);
  14. if (serial_fd_ < 0) {
  15. RCLCPP_ERROR(this->get_logger(), "Failed to open serial port");
  16. rclcpp::shutdown();
  17. return;
  18. }
  19. // 创建驱动线程
  20. driver_thread_ = std::thread(&IMUDriverNode::driver_loop, this);
  21. }
  22. ~IMUDriverNode() {
  23. // 在节点析构时关闭串口和等待驱动线程结束
  24. if (serial_fd_ >= 0) {
  25. close(serial_fd_);
  26. }
  27. if (driver_thread_.joinable()) {
  28. driver_thread_.join();
  29. }
  30. }

私有成员变量:

  1. private:
  2. int serial_fd_;
  3. std::string port_;
  4. int baud_rate_;
  5. std::thread driver_thread_;
  6. rclcpp::Publisher<sensor_msgs::msg::Imu>::SharedPtr imu_pub_;
  7. std::array<double, 3> angular_velocity_ = {0, 0, 0};
  8. std::array<double, 3> acceleration_ = {0, 0, 0};
  9. std::array<double, 3> angle_degree_ = {0, 0, 0};
  • serial_fd_: 串口文件描述符。
  • port_baud_rate_: 存储串口和波特率参数。
  • driver_thread_: 用于串口数据读取和处理的线程。
  • imu_pub_: IMU数据发布者。
  • angular_velocity_, acceleration_, angle_degree_: 存储IMU测量值的数组

三、打开串口,这里根据官方给出的ros1版本做了修改:

这里附上官方文档的链接:SDK适用TTL/232 · 深圳维特智能科技有限公司

  1. private:
  2. int open_serial_port(const std::string& port, int baud_rate) {
  3. // 打开并配置串口
  4. int fd = open(port.c_str(), O_RDWR | O_NOCTTY);
  5. if (fd < 0) {
  6. RCLCPP_ERROR(this->get_logger(), "Error opening serial port");
  7. return -1;
  8. }
  9. // 获取当前串口配置
  10. struct termios tty;
  11. if (tcgetattr(fd, &tty) != 0) {
  12. RCLCPP_ERROR(this->get_logger(), "Error getting termios attributes");
  13. close(fd);
  14. return -1;
  15. }
  16. // 设置波特率
  17. cfsetospeed(&tty, B230400);
  18. cfsetispeed(&tty, B230400);
  19. // 配置串口属性
  20. tty.c_cflag &= ~PARENB; // 禁用奇偶校验
  21. tty.c_cflag &= ~CSTOPB; // 设置为1个停止位
  22. tty.c_cflag &= ~CSIZE;//清除字符大小掩码
  23. tty.c_cflag |= CS8; //设置字符大小为8位
  24. tty.c_cflag &= ~CRTSCTS; // 禁用硬件流控制
  25. tty.c_cflag |= CREAD | CLOCAL; // 启用接收 & 忽略控制线状态
  26. tty.c_lflag &= ~ICANON;//禁用规范模式
  27. tty.c_lflag &= ~ECHO; //禁用回显
  28. tty.c_lflag &= ~ECHOE; //禁用擦除
  29. tty.c_lflag &= ~ECHONL; //禁用换行回显
  30. tty.c_lflag &= ~ISIG; // 禁用特殊字符的信号处理
  31. tty.c_iflag &= ~(IXON | IXOFF | IXANY); //关闭软件流控制
  32. tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // 禁用特殊字符处理
  33. tty.c_oflag &= ~OPOST; // 禁止对输出字节进行特殊处理
  34. tty.c_oflag &= ~ONLCR; //禁止将换行符转换为回车换行
  35. tty.c_cc[VTIME] = 1; // 设置等待时间为0.1秒
  36. tty.c_cc[VMIN] = 0;
  37. // 应用新的串口配置
  38. if (tcsetattr(fd, TCSANOW, &tty) != 0) {
  39. RCLCPP_ERROR(this->get_logger(), "Error setting termios attributes");
  40. close(fd);
  41. return -1;
  42. }
  43. return fd;
  44. }

四、串口数据读取和处理的主循环。在循环中,通过调用 read 读取串口数据,处理每个字节数据,并根据处理结果发布IMU数据:

  1. private:
  2. bool handle_serial_data(uint8_t raw_data) {
  3. // 静态缓冲区和索引
  4. static std::vector<uint8_t> buff(11, 0);
  5. static int key = 0;
  6. // 将接收到的字节存入缓冲区
  7. buff[key] = raw_data;
  8. key++;
  9. // 检查帧头
  10. if (buff[0] != 0x55) {
  11. // 如果帧头不对,重置缓冲区和索引
  12. key = 0;
  13. return false;
  14. }
  15. // 等待接收完整帧
  16. if (key < 11) {
  17. return false;
  18. }
  19. // 检查校验和
  20. bool angle_flag = false;
  21. if (check_sum(buff.data(), 10, buff[10])) {
  22. // 解析数据
  23. switch (buff[1]) {
  24. case 0x51:
  25. acceleration_ = parse_data(buff, 16, 9.8);
  26. break;
  27. case 0x52:
  28. angular_velocity_ = parse_data(buff, 2000, M_PI / 180);
  29. break;
  30. case 0x53:
  31. angle_degree_ = parse_data(buff, 180, 1);
  32. angle_flag = true;
  33. break;
  34. default:
  35. break;
  36. }
  37. }
  38. // 重置缓冲区和索引
  39. buff.assign(11, 0);
  40. key = 0;
  41. return angle_flag;
  42. }

五、处理接收到的串口数据。检查帧头是否正确,等待接收完整帧,验证校验和,解析数据,并根据数据类型进行相应的处理。

  1. private:
  2. std::array<double, 3> parse_data(const std::vector<uint8_t>& data, double scale, double unit) {
  3. std::array<double, 3> result;
  4. for (int i = 0; i < 3; ++i) {
  5. // 解析数据并转换为物理量
  6. result[i] = static_cast<int16_t>((data[3 + 2 * i] << 8) | data[2 + 2 * i]) / 32768.0 * scale * unit;
  7. }
  8. return result;
  9. }

六、计算并验证数据的校验和,确保数据完整性。

  1. private:
  2. bool check_sum(const uint8_t* data, size_t len, uint8_t checksum) {
  3. // 计算校验和
  4. uint8_t sum = 0;
  5. for (size_t i = 0; i < len; ++i) {
  6. sum += data[i];
  7. }
  8. // 比较校验和
  9. return sum == checksum;
  10. }

七、创建IMU消息并填充加速度、角速度和姿态数据,然后发布至ROS主题。

  1. private:
  2. void publish_imu_data() {
  3. // 创建IMU消息并填充数据
  4. auto imu_msg = sensor_msgs::msg::Imu();
  5. imu_msg.header.stamp = this->now();
  6. imu_msg.header.frame_id = "imu_link";
  7. imu_msg.linear_acceleration.x = acceleration_[0];
  8. imu_msg.linear_acceleration.y = acceleration_[1];
  9. imu_msg.linear_acceleration.z = acceleration_[2];
  10. imu_msg.angular_velocity.x = angular_velocity_[0];
  11. imu_msg.angular_velocity.y = angular_velocity_[1];
  12. imu_msg.angular_velocity.z = angular_velocity_[2];
  13. // 将角度转换为四元数并填充到消息中
  14. auto quat = get_quaternion_from_euler(angle_degree_[0] * M_PI / 180, angle_degree_[1] * M_PI / 180, angle_degree_[2] * M_PI / 180);
  15. imu_msg.orientation.x = quat[0];
  16. imu_msg.orientation.y = quat[1];
  17. imu_msg.orientation.z = quat[2];
  18. imu_msg.orientation.w = quat[3];
  19. // 发布IMU消息
  20. imu_pub_->publish(imu_msg);
  21. }

八、根据给定的欧拉角(roll、pitch、yaw)计算对应的四元数表示。

  1. private:
  2. std::array<double, 4> get_quaternion_from_euler(double roll, double pitch, double yaw) {
  3. // 根据欧拉角计算四元数
  4. std::array<double, 4> quat;
  5. double cy = cos(yaw * 0.5);
  6. double sy = sin(yaw * 0.5);
  7. double cp = cos(pitch * 0.5);
  8. double sp = sin(pitch * 0.5);
  9. double cr = cos(roll * 0.5);
  10. double sr = sin(roll * 0.5);
  11. quat[0] = sr * cp * cy - cr * sp * sy;
  12. quat[1] = cr * sp * cy + sr * cp * sy;
  13. quat[2] = cr * cp * sy - sr * sp * cy;
  14. quat[3] = cr * cp * cy + sr * sp * sy;
  15. return quat;
  16. }

九、最后主函数收尾:

  1. int main(int argc, char* argv[]) {
  2. // 初始化ROS节点
  3. rclcpp::init(argc, argv);
  4. // 创建IMU驱动节点实例,并进入ROS事件循环
  5. auto node = std::make_shared<IMUDriverNode>();
  6. rclcpp::spin(node);
  7. // 关闭ROS节点
  8. rclcpp::shutdown();
  9. return 0;
  10. }

十,最后附上CMakeList.txt,如果有遗漏的包可以补充,自己测的时候没有遗漏。

  1. cmake_minimum_required(VERSION 3.5)
  2. project(imu_driver)
  3. find_package(ament_cmake REQUIRED)
  4. find_package(rclcpp REQUIRED)
  5. find_package(sensor_msgs REQUIRED)
  6. find_package(serial REQUIRED)
  7. add_executable(imu_driver src/imu_driver.cpp)
  8. ament_target_dependencies(imu_driver
  9. rclcpp
  10. sensor_msgs
  11. serial
  12. )
  13. install(TARGETS
  14. imu_driver
  15. DESTINATION lib/${PROJECT_NAME})
  16. ament_package()

运行:colcon build
          source install/setup.bash
          ros2 run imu_driver imu_driver

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/885377
推荐阅读
相关标签
  

闽ICP备14008679号