ubuntu环境下实现ROS 2 与 Arduino 通信

news2024/9/21 6:29:11

本教程为https://blog.csdn.net/2301_81924597/article/details/141757091?spm=1001.2014.3001.5501的进一步拓展

ROS 2 与 Arduino 通信指南

准备工作

  1. 确保已安装 ROS 2(本指南基于 ROS 2 Humble)
  2. 确保已安装 Arduino IDE 并能正常使用
  3. 安装必要的 ROS 2 包:
    sudo apt install ros-humble-serial-driver
    

Arduino 端设置

  1. 打开 Arduino IDE,创建新项目
  2. 将以下代码复制到 Arduino IDE 中:
#include <Arduino.h>

void printMenu() {
  Serial.println("\n--- Arduino Menu ---");
  Serial.println("1. Say Hello");
  Serial.println("2. Get Arduino Uptime");
  Serial.println("3. Blink LED");
  Serial.println("Enter your choice:");
}

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ; // Wait for serial port to connect (needed for native USB port only)
  }
  delay(1000); // 添加一个短暂的延迟
  Serial.println("Arduino ready for communication!");
  pinMode(LED_BUILTIN, OUTPUT);
  printMenu();
}

void loop() {
  if (Serial.available() > 0) {
    char choice = Serial.read();
  
    // Clear the serial buffer
    while(Serial.available() > 0) {
      Serial.read();
    }
  
    switch(choice) {
      case '1':
        Serial.println("Hello from Arduino!");
        break;
      case '2':
        Serial.print("Arduino uptime: ");
        Serial.print(millis() / 1000);
        Serial.println(" seconds");
        break;
      case '3':
        Serial.println("Blinking LED 3 times...");
        for(int i = 0; i < 3; i++) {
          digitalWrite(LED_BUILTIN, HIGH);
          delay(500);
          digitalWrite(LED_BUILTIN, LOW);
          delay(500);
        }
        Serial.println("Blinking complete!");
        break;
      default:
        Serial.println("Invalid choice. Please try again.");
    }
  
    printMenu();
  }
}
  1. 将代码上传到 Arduino

ROS 2 端设置

  1. 创建一个新的 ROS 2 包(如果还没有):

    mkdir ros2_ws/src/arduino_communication
    cd ros2_ws/src/arduino_communication
    ros2 pkg create --build-type ament_cmake arduino_communication
    
  2. arduino_communication 包中创建 src/arduino_serial_node.cpp 文件,并添加以下代码:

#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>
#include <serial_driver/serial_driver.hpp>
#include <vector>
#include <string>

using drivers::serial_driver::SerialDriver;
using drivers::serial_driver::SerialPortConfig;
using drivers::common::IoContext;

class ArduinoSerialNode : public rclcpp::Node {
public:
    ArduinoSerialNode()
        : Node("arduino_serial_node")
    {
        // 创建 IoContext
        io_context_ = std::make_shared<IoContext>(1);

        // 创建 SerialPortConfig
        auto device_config = std::make_shared<SerialPortConfig>(
            9600,
            drivers::serial_driver::FlowControl::NONE,
            drivers::serial_driver::Parity::NONE,
            drivers::serial_driver::StopBits::ONE
        );

        // 创建 SerialDriver
        serial_driver_ = std::make_unique<SerialDriver>(*io_context_);

        // 打开串口
        try {
            serial_driver_->init_port("/dev/ttyACM0", *device_config);
            serial_driver_->port()->open();
            RCLCPP_INFO(this->get_logger(), "Serial port opened successfully");
        } catch (const std::exception &ex) {
            RCLCPP_ERROR(this->get_logger(), "Error opening serial port: %s", ex.what());
            return;
        }

        // 创建定时器和发布者
        timer_ = this->create_wall_timer(
            std::chrono::milliseconds(100),
            std::bind(&ArduinoSerialNode::timer_callback, this));

        publisher_ = this->create_publisher<std_msgs::msg::String>("arduino_data", 10);
    }

private:
    void timer_callback() {
        std::vector<uint8_t> buffer(256);
        size_t bytes_read = 0;

        try {
            bytes_read = serial_driver_->port()->receive(buffer);
        } catch (const std::exception &ex) {
            RCLCPP_ERROR(this->get_logger(), "Error reading from serial port: %s", ex.what());
            return;
        }

        if (bytes_read > 0) {
            std::string data(buffer.begin(), buffer.begin() + bytes_read);
            process_and_publish_data(data);
        }
    }

    void process_and_publish_data(const std::string& data) {
        static std::string buffer;
        buffer += data;

        size_t pos;
        while ((pos = buffer.find('\n')) != std::string::npos) {
            std::string line = buffer.substr(0, pos);
            buffer.erase(0, pos + 1);

            if (line.find("Arduino ready for communication!") != std::string::npos ||
                line.find("Hello from Arduino!") != std::string::npos ||
                line.find("Arduino uptime:") != std::string::npos ||
                line.find("Blinking LED") != std::string::npos ||
                line.find("Blinking complete!") != std::string::npos ||
                line.find("Invalid choice") != std::string::npos) {
            
                auto message = std_msgs::msg::String();
                message.data = line;
                publisher_->publish(message);
                RCLCPP_INFO(this->get_logger(), "Published data: %s", line.c_str());
            }
        }
    }

    std::shared_ptr<IoContext> io_context_;
    std::unique_ptr<SerialDriver> serial_driver_;
    rclcpp::TimerBase::SharedPtr timer_;
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
};

int main(int argc, char *argv[]) {
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<ArduinoSerialNode>());
    rclcpp::shutdown();
    return 0;
}


  1. 修改 CMakeLists.txt 文件,添加以下内容:
cmake_minimum_required(VERSION 3.5)
project(arduino_communication)

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(serial_driver REQUIRED)

add_executable(arduino_serial_node src/arduino_serial_node.cpp)
ament_target_dependencies(arduino_serial_node rclcpp std_msgs serial_driver)

install(TARGETS
  arduino_serial_node
  DESTINATION lib/${PROJECT_NAME})

ament_package()
  1. 修改 package.xml 文件,添加以下依赖项:
<package format="3">
  <name>arduino_communication</name>
  <version>0.0.0</version>
  <description>ROS 2 package for communicating with Arduino</description>
  <maintainer email="you@example.com">Your Name</maintainer>
  <license>Apache-2.0</license>

  <buildtool_depend>ament_cmake</buildtool_depend>
  <build_depend>rclcpp</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>serial_driver</build_depend>

  <exec_depend>rclcpp</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  <exec_depend>serial_driver</exec_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>
  1. 构建并安装包:
cd ~/文档/PlatformIO/Projects/arduino_ros2_communicate/ros2_ws
colcon build --symlink-install
source install/setup.bash
  1. 运行节点:

在运行节点之前,需要 source ROS 2 和工作空间的环境设置:

source /opt/ros/humble/setup.bash
source ~/文档/PlatformIO/Projects/arduino_ros2_communicate/ros2_ws/install/setup.bash
ros2 run arduino_communication arduino_serial_node
  1. 在另一个终端中,查看从 Arduino 发布的数据:
source /opt/ros/humble/setup.bash
source ~/文档/PlatformIO/Projects/arduino_ros2_communicate/ros2_ws/install/setup.bash
ros2 topic echo /arduino_data

运行成功的话,可以看到 Arduino 发送的数据。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

故障排除

重新编译和运行

  1. 导航到工作空间目录:

    cd ~/文档/PlatformIO/Projects/arduino_ros2_communicate/ros2_ws
    
  2. 清理之前的构建并重新编译包:

    colcon build --symlink-install --cmake-clean-cache
    
  3. 如果上述命令失败,尝试只构建 arduino_communication 包:

    colcon build --symlink-install --packages-select arduino_communication
    
  4. 重新加载环境:

    source /opt/ros/humble/setup.bash
    source ~/文档/PlatformIO/Projects/arduino_ros2_communicate/ros2_ws/install/setup.bash
    
  5. 运行节点:

    ros2 run arduino_communication arduino_serial_node
    

通过以上步骤,你应该能够成功实现 ROS 2 与 Arduino 的通信。如果仍然遇到问题,请检查代码中的错误并确保所有依赖都已正确安装。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2096426.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

系统架构师考试学习笔记第三篇——架构设计高级知识(10)系统质量属性与架构评估

本章知识点&#xff1a; 第10课时主要学习软件系统质量属性、系统架构评估以及ATAM方法评估实践等内容。 本课时内容侧重于概念知识&#xff0c;根据以往全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;。考试的出题规律&#xff0c;考查的知识点多来源于教材&a…

在Web服务应用中,如何编程使用Redis的缓存功能?包括缓存页面内容、缓存数据库查询结果、用户会话信息等代码分享

目录 一、概述 二、redis介绍 1、简介 2、Redis作为缓存的原理 &#xff08;1&#xff09;内存存储 &#xff08;2&#xff09;数据结构 &#xff08;3&#xff09;工作原理 3、Redis作为缓存的作用 三、redis缓存页面内容 1、作用 2、实现方法 3、示例代码&#x…

python07-单元测试框架unittest1-2

5 fixture 可以看作case的前置条件、后置条件 5.1 fixture的用例执行顺序 fixture分为 方法级别类级别模块级别 5.1.1方法级fixture 每个测试用例之前要调用setUp每个测试用例执行后要调用tearDowntestCase中有多少测试用例,那么setUp和tearDown就被调用多少次 def add(…

【Java】Spring-AOP与拦截器简洁实操 (上手图解)

Java系列文章目录 补充内容 Windows通过SSH连接Linux 第一章 Linux基本命令的学习与Linux历史 文章目录 Java系列文章目录一、前言二、学习内容&#xff1a;三、问题描述四、解决方案&#xff1a;4.1 认识依赖4.2 使用AOP与拦截器4.2.1 使用AOP4.2.1.1 设置DemoAop类4.2.2.2 设…

【SpringBoot】电脑商城-11-显示购物车功能

加入购物车 1 购物车-创建数据表 1.使用use命令先选中store数据库。 USE store;2.在store数据库中创建t_cart用户数据表。 CREATE TABLE t_cart (cid INT AUTO_INCREMENT COMMENT 购物车数据id,uid INT NOT NULL COMMENT 用户id,pid INT NOT NULL COMMENT 商品id,price BIG…

java fastxml json 科学计数法转换处理

背景&#xff1a; 由于 canal 切换为 tx dbbridge后&#xff0c;发现dbbridge对于canal的兼容性存在较大问题&#xff0c;从而引发 该文档的实践。 就目前发现 dbbrige 的字段 大小写 和 数据类型格式 从binlog 写入kafka 同canal 都会存在差异。 canal之前导出都是小写&…

编程要由 “手动挡” 变 “自动挡” 了?Cursor+Claude-3.5-Sonnet,Karpathy 大神点赞的 AI 代码神器!如何使用详细教程

Cursor 情况简介 AI 大神 Andrej Karpathy 都被震惊了&#xff01;他最近在试用 VS Code Cursor Claude Sonnet 3.5&#xff0c;结果发现这玩意儿比 GitHub Copilot 还好用&#xff01; Cursor 在短短时间内迅速成为程序员群体的顶流神器&#xff0c;其背后的原因在于其默认使…

[VirtualBox+ubuntu24]设置linux学习环境

1)设置网络为桥接网卡&#xff0c;不然发现ifconfig出不来ip地址 依然设置为经典的: 2核4G内存 50G硬盘 2)设置默认root账户登录 // 不然每次都得输入sudo -s // step1: 打开配置文件 sudo vim/etc/gdm3/custom.conf// step2: 默认以root登录 [daemon] AutomaticLoginEnableT…

NTFS硬盘支持工具Paragon NTFS for Mac 15.4.44 中文破解版

Paragon NTFS for Mac 15.4.44 中文破解版是一个底层的文件系统驱动程序,专门开发用来弥合Windows和Mac OS X之间的不兼容性&#xff0c;通过在Mac OS X系统下提供对任何版本的NTFS文件系统完全的读写访问服务来弥合这种不兼容性。为您轻松解决Mac不能识别Windows NTFS文件难题…

C语言:大小端模式、判断大小端、大小端转换

目录 1. 什么是大端和小端 2.为什么会存在大小端的问题 3. 判断主机字节序 (主机大小端) 3.1 使用联合体 (union) 3.2 使用指针 3.3 强制转为 char 类型法 4. 大小端转换 1. 什么是大端和小端 对于一个存储空间大于 1 个字节的数据&#xff0c;在内存中有两种存储模式&a…

VCTP论文精读

机器视觉推理自从引入神经符号机制以来取得了巨大进步&#xff0c;这使得机器能够发展出多步骤的推理链。然而&#xff0c;正如早期认知科学家所预示的那样&#xff0c;这种逻辑和符号系统基本上不适合于现实世界、常识知识的表示和推理&#xff0c;因为它们仅依赖于封闭世界的…

一个人独立开发前后端,终于有属于自己的一套产品

大家好&#xff0c;我是兔兔答题的开发者。兔兔答题是一款简单、易用的答题考试系统&#xff0c;可应用于微信考试、付费考试、社会调查问卷、明星知识问答、员工培训考核、模拟自测、企业面试、试题库等多种场景。兔兔答题会根据不同的场景&#xff0c;开发不同的模版。例如驾…

[Algorithm][综合训练][循环汉诺塔][kotori和素因子][dd爱科学]详细讲解

目录 1.循环汉诺塔1.题目链接2.算法原理详解 && 代码实现 2.kotori和素因子1.题目链接2.算法原理详解 && 代码实现 3.dd爱科学1.题目链接2.算法原理详解 && 代码实现 1.循环汉诺塔 1.题目链接 循环汉诺塔 2.算法原理详解 && 代码实现 解法&a…

虚幻5|C++第三人称射击(1)添加摄像机

一.在C类创建一个一个角色类蓝图&#xff0c;命名为BasePlayer 1.得到cpp和h文件 2.打开BasePlayer.h&#xff0c;定义摄像机内容 编译以下代码&#xff0c;定义摄像机和摄像机组件 private: //定义摄像机 UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category"…

【数学分析笔记】第2章第4节收敛准则(7)

2. 数列极限 2.4 收敛准则 2.4.8 实数系的基本定理 确界存在定理&#xff08;实数系的连续性&#xff09;单调有界数列收敛定理闭区间套定理Bolzanp-Weierstrass&#xff08;波尔查诺&#xff0d;魏尔斯特拉斯&#xff09;定理Cauchy&#xff08;柯西&#xff09;收敛原理&a…

前端与后端的身份认证

这里写目录标题 前端与后端的身份认证Web开发模式服务端渲染的Web开发模式前后端分离的Web开发模式根据场景选择开发模式 身份认证为什么需要身份认证不同开发模式下的身份认证 Session认证机制HTTP协议下的无状态性如何突破HTTP无状态的限制CookieCookie的几大特性&#xff1a…

代码随想录 刷题记录-23 单调栈

题目对“下一个更高”或者“下一个更低”有要求&#xff0c;可以考虑单调栈 &#xff08;也可以考虑双指针&#xff0c;双指针往往能够把时间复杂度的指数减一&#xff09; 1.739. 每日温度 思路 首先想到的当然是暴力解法&#xff0c;两层for循环&#xff0c;把至少需要等…

豆包MarsCode编程助手:让编程更简单

在编程的浩瀚宇宙中&#xff0c;每一个开发者都在寻找那把能够开启高效与创意之门的钥匙。随着AI技术的飞速发展&#xff0c;智能编程助手应运而生&#xff0c;为开发者们带来了前所未有的便捷与灵感。今天&#xff0c;我们将以五子棋小游戏开发为例&#xff0c;深入解读豆包Ma…

从文本坐标数据转换为矢量(点线面)

从坐标数据转换为空间几何(点线面) 介绍 只要文件中包含空间数据(如经纬度信息),转换为点、线或面要素。我们将使用内置的 excel Reader 参数、VertexCreator 转换器将坐标转换为点要素。我们还将使用 VertexCreator 转换器和 LineBuilder 转换器和AreaBuilder转换为线和…

第七讲 开发环境的搭建

1 首先是mdk5 的安装破解 然后就是 安装 f103 的 库。 然后是破解 &#xff1a; 2 然后是 进行编译测试。 随便打开一个工程 开始编译 3 然后是 ch340 的驱动的安装 目前就是安装完毕