ROS2通过话题的发布与订阅进行串口通信

2023-05-16

目录

  • 步骤
    • 新建一个cpp_header的包
    • 进入include,新建头文件minimal_publisher.hpp
    • 进入src目录,新建文件minimal_publisher.cpp
    • 进入src目录,新建文件minimal_publisher_node.cpp
    • 编辑package.xml
    • 编辑 CMakelist.txt
    • 编译包
    • 加载工作空间
    • 执行
    • 开启虚拟串口/dev/pts/4进行监听
    • 开启发布节点
    • 开启串口通信
  • 结果

步骤

新建一个cpp_header的包

cd ~/dev_ws/src
ros2 pkg create --build-type ament_cmake cpp_header

进入include,新建头文件minimal_publisher.hpp

cd ~/dev_ws/src/cpp_header/include/cpp_header
touch minimal_publisher.hpp

代码如下:

#include "cpp_header/minimal_publisher.hpp"

#include  <termios.h>

Serial::Serial()
{

}
Serial::~Serial()
{

}

int Serial::BaudRate( int baudrate)
{
    if(7 == baudrate)
        return B460800;
    else if(6 == baudrate)
        return B115200;
    else if(5 == baudrate)
        return B57600;
    else if(4 == baudrate)
        return B38400;
    else if(3 == baudrate)
        return B19200;
    else if(2 == baudrate)
        return B9600;
    else if(1 == baudrate)
        return B4800;
    else if(0 == baudrate)
        return B2400;
    else
        return B9600;

}

int Serial::SetPara(int serialfd,int speed,int databits , int stopbits ,int parity )
{
    struct termios termios_new;
    bzero( &termios_new, sizeof(termios_new));//等价于memset(&termios_new,sizeof(termios_new));
    cfmakeraw(&termios_new);//就是将终端设置为原始模式
    termios_new.c_cflag=BaudRate(speed);
    termios_new.c_cflag |= CLOCAL | CREAD;
  //  termios_new.c_iflag = IGNPAR | IGNBRK;

    termios_new.c_cflag &= ~CSIZE;
    switch (databits)
    {
    case 0:
        termios_new.c_cflag |= CS5;
        break;
    case 1:
        termios_new.c_cflag |= CS6;
        break;
    case 2:
        termios_new.c_cflag |= CS7;
        break;
    case 3:
        termios_new.c_cflag |= CS8;
        break;
    default:
        termios_new.c_cflag |= CS8;
        break;
    }

    switch (parity)
    {
    case 0:  				//as no parity
        termios_new.c_cflag &= ~PARENB;    //Clear parity enable
      //  termios_new.c_iflag &= ~INPCK; /* Enable parity checking */  //add by fu
        break;
    case 1:
        termios_new.c_cflag |= PARENB;     // Enable parity
        termios_new.c_cflag &= ~PARODD;
        break;
    case 2:
        termios_new.c_cflag |= PARENB;
        termios_new.c_cflag |= ~PARODD;
        break;
    default:
        termios_new.c_cflag &= ~PARENB;   // Clear parity enable
        break;
    }
    switch (stopbits)// set Stop Bit
    {
    case 1:
        termios_new.c_cflag &= ~CSTOPB;
        break;
    case 2:
        termios_new.c_cflag |= CSTOPB;
        break;
    default:
        termios_new.c_cflag &= ~CSTOPB;
        break;
    }
    tcflush(serialfd,TCIFLUSH); // 清除输入缓存
   tcflush(serialfd,TCOFLUSH); // 清除输出缓存
    termios_new.c_cc[VTIME] = 1;   // MIN与 TIME组合有以下四种:1.MIN = 0 , TIME =0  有READ立即回传 否则传回 0 ,不读取任何字元
    termios_new.c_cc[VMIN] = 1;  //    2、 MIN = 0 , TIME >0  READ 传回读到的字元,或在十分之一秒后传回TIME 若来不及读到任何字元,则传回0
    tcflush (serialfd, TCIFLUSH);  //    3、 MIN > 0 , TIME =0  READ 会等待,直到MIN字元可读
    return tcsetattr(serialfd,TCSANOW,&termios_new);  //    4、 MIN > 0 , TIME > 0 每一格字元之间计时器即会被启动 READ 会在读到MIN字元,传回值或
}

int Serial::WriteData(int fd,const char *data, int datalength )//index 代表串口号 0 串口/dev/ttyAMA1 ......
{
    if(fd <0){ return -1;}
    int len = 0, total_len = 0;//modify8.
    for (total_len = 0 ; total_len < datalength;)
    {
        len = 0;
        len = write(fd, &data[total_len], datalength - total_len);
        printf("WriteData fd = %d ,len =%d,data = %s\n",fd,len,data);
        if (len > 0)
        {
            total_len += len;
        }
        else if(len <= 0)
        {
            len = -1;
            break;
        }
     }
       return len;
}

int Serial::ReadData(int fd,unsigned char *data, int datalength)
{
       if(fd <0){ return -1;}
       int len = 0;
       memset(data,0,datalength);

       int max_fd = 0;
       fd_set readset ={0};
       struct timeval tv ={0};

       FD_ZERO(&readset);
       FD_SET((unsigned int)fd, &readset);
       max_fd = fd +1;
       tv.tv_sec=0;
       tv.tv_usec=1000;
       if (select(max_fd, &readset, NULL, NULL,&tv ) < 0)
       {
               printf("ReadData: select error\n");
       }
       int nRet = FD_ISSET(fd, &readset);
      if (nRet)
       {
              len = read(fd, data, datalength);
       }
       return len;
}

void Serial::ClosePort(int fd)
{
    struct termios termios_old;
    if(fd > 0)
    {
        tcsetattr (fd, TCSADRAIN, &termios_old);
        ::close (fd);
    }
}

int  Serial::OpenPort(int index)
{
    char *device;
    struct termios termios_old;
    int fd;
    switch(index)
    {
    case COM0:  device="/dev/ttyS0";  break;
    case COM1:  device="/dev/ttyS1";  break;
    case COM2:  device="/dev/ttyS2";  break;
    case COM3:  device="/dev/pts/3";  break;
    case ttyUSB0:  device="/dev/ttyUSB0";  break;
    case ttyUSB1:  device="/dev/ttyUSB1";  break;
    case ttyUSB2:  device="/dev/ttyUSB2";  break;
    default: device="/dev/ttyAMA2"; break;
    }

    fd = open( device, O_RDWR | O_NOCTTY |O_NONBLOCK);//O_RDWR | O_NOCTTY | O_NDELAY   //O_NONBLOCK
    if (fd < 0)
    { return -1;}
    tcgetattr(fd , &termios_old);
    return fd;
}

注意:case COM3: device=“/dev/pts/3”; break;
这里的端口号需要打开socat进行查看,选择其中一个作为这里面的端口。
可以看我另一篇文章:链接: ROS2实现虚拟串口通信

进入src目录,新建文件minimal_publisher.cpp

cd ~/dev_ws/src/cpp_header/src
touch minimal_publisher.cpp

代码如下:

#ifndef PUB_SERIALPORT_HPP
#define PUB_SERIALPORT_HPP

#include  <stdio.h>
#include  <stdlib.h>
#include  <unistd.h>
#include  <sys/types.h>
#include  <sys/signal.h>
#include  <sys/stat.h>
#include  <fcntl.h>
#include  <errno.h>
#include  <limits.h>
#include  <string.h>


enum sp_dev_e
{
    COM0 = 0,
    COM1,
    COM2,
    COM3,
    ttyUSB0,
    ttyUSB1,
    ttyUSB2
};
class Serial
{
    public:
    Serial();
    ~Serial();

    int OpenPort(int index);
    int SetPara(int serialfd,int speed=2,int databits=8,int stopbits=1,int parity=0);
    int WriteData(int fd,const char *data,int datalength);
    int ReadData(int fd,unsigned char *data,int datalength);
    void ClosePort(int fd);
    int BaudRate( int baudrate);
};


#endif // PUB_SERIALPORT_HPP

进入src目录,新建文件minimal_publisher_node.cpp

cd ~/dev_ws/src/cpp_header/src
touch minimal_publisher_node.cpp

代码如下:

#include "cpp_header/minimal_publisher.hpp"
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"


int fp;
unsigned char Msg[128] = { 0 };
const char* constc = nullptr;
Serial sp;
class TopicSuscribe01 : public rclcpp::Node
{
public:
    TopicSuscribe01(std::string name) : Node(name)
    {
        RCLCPP_INFO(this->get_logger(),"我是%s,订阅话题为:/rostopic.",name.c_str());
        command_subscribe_ = this->create_subscription<std_msgs::msg::String>("rostopic",11,std::bind(&TopicSuscribe01::command_callback,this,std::placeholders::_1));
    }
private:
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr command_subscribe_;
    void command_callback(const std_msgs::msg::String::SharedPtr msg)
    {
        double speed = 0.0f;
        if(msg->data == "9999")
        {
            speed = 0.2f;

        }
        RCLCPP_INFO(this->get_logger(),"收到的[%s]指令,发送速度%f",msg->data.c_str(),speed);
        
        constc = msg->data.c_str();
        
        sp.WriteData(fp,constc,11);
    }

};


int main(int argc, char **argv)
{
    /* code */
    rclcpp::init(argc, argv);
    /*产生一个Wang2的节点*/
    // auto node = std::make_shared<rclcpp::Node>("pub_serial");
    auto node = std::make_shared<TopicSuscribe01>("topic_subscribe_01");
    // 打印一句自我介绍
    // int fp;
    // Serial sp;
    sp.BaudRate(115200);
    fp = sp.OpenPort(COM3);
    if(fp>0){
      RCLCPP_INFO(node->get_logger(), "serial success!.");
    }
    sp.WriteData(fp,"1024",11);
    // cout<<fp<<endl;

    RCLCPP_INFO(node->get_logger(), "大家好,我是topic_subscribe_01.");
    /* 运行节点,并检测退出信号*/
    rclcpp::spin(node);
    rclcpp::shutdown();
    sp.ClosePort(fp);
    return 0;
    
    return 0;
}

**注意:**RCLCPP_INFO(this->get_logger(),“我是%s,订阅话题为:/rostopic”,name.c_str());
这里面的rostopic是我自己定义的一个话题,一方面是为了避免与其他人的话题相同而造成数据乱传,另一方面我的发布节点用到的话题也是这个名字。

编辑package.xml

在<buildtool_depend>ament_cmake</buildtool_depend>后增加

<depend>rclcpp</depend>
<depend>std_msgs</depend>

编辑 CMakelist.txt

在find_package(ament_cmake REQUIRED)后增加

find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

再增加可执行文件,ros2 run能够调用的名称

include_directories(include)
add_executable(minimal_publisher_node src/minimal_publisher_node.cpp src/minimal_publisher.cpp) 
ament_target_dependencies(minimal_publisher_node rclcpp std_msgs)

增加可执行文件位置,ros2 run可以找到这个可执行文件

install(TARGETS
  minimal_publisher_node
  DESTINATION lib/${PROJECT_NAME})
ament_package()

编译包

colcon build --symlink-install --packages-select cpp_header

加载工作空间

. install/setup.bash

执行

ros2 run cpp_header minimal_publisher_node

开启虚拟串口/dev/pts/4进行监听

参照我的另一篇文章:
链接: ROS2实现虚拟串口通信

cat < /dev/pts/4

开启发布节点

ros2 run example_topic_rclcpp topic_publisher_01 

这里可以参考我的另一篇博客:
链接: 利用RCLCPP实现话题的发布与订阅

开启串口通信

ros2 run cpp_header minimal_publisher_node 

结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ROS2通过话题的发布与订阅进行串口通信 的相关文章

  • 【ROS2 入门】虚拟机环境 ubuntu 18.04 ROS2 安装

    大家好 xff0c 我是虎哥 xff0c 从今天开始 xff0c 我将花一段时间 xff0c 开始将自己从ROS1切换到ROS2 xff0c 做为有别于ROS1的版本 xff0c 做了很多更新和改变 xff0c 我还是很期待自己逐步去探索R
  • ROS2交叉编译操作

    ROS2移植过程 在移植ROS2之前 先确认需要移植的版本以及其对应的依赖 这些信息可以在 ROS 2 Releases and Target Platforms 中有介绍 可依据自身需要使用的平台 参考该链接进行选择 下面以ROS2 Hu
  • 六、ROS2通信机制(服务)

    一 服务介绍 客户端发送请求给服务端 xff0c 服务端可以根据客户端的请求做一些处理 xff0c 然后返回结果给客户端 这里与话题的主要区别就是 xff1a 话题是没有返回的 xff0c 适用于单向或大量的数据传递 而服务是双向的 xff
  • ROS2的RVIZ2无法启动

    在新安装的 xff32 xff2f xff33 2中启动rviz2 xff0c 启动错误 xff0c 显示 Failed to create an OpenGL context BadValue integer parameter out
  • ros2串口通信

    目录 前期准备新建工程添加头文件添加源文件添加节点源文件修改package xml和CMakeLists txt安装依赖编译执行打开虚拟串口打开发布者打开订阅者打开监听 xff0c 查看是否有数据接收 前期准备 登录github下载代码ht
  • ROS2下使用鱼香ROS的串口通信库完成串口通信

    目录 一 安装编译FishProtocol二 ROS2串口例程1 编译运行功能包2 出现的问题3 串口通信 鱼香ROS的串口通信库Github地址 一 安装编译FishProtocol span class token function s
  • ROS1代码转ROS2

    先占个坑 xff0c 等我做完写总结
  • 【ROS2 入门】虚拟机环境 ubuntu 18.04 ROS2 安装

    大家好 xff0c 我是虎哥 xff0c 从今天开始 xff0c 我将花一段时间 xff0c 开始将自己从ROS1切换到ROS2 xff0c 做为有别于ROS1的版本 xff0c 做了很多更新和改变 xff0c 我还是很期待自己逐步去探索R
  • 为PX4建立ROS2环境,x86与树梅派

    本文参考官网 xff1a a href https docs px4 io master en ros ros2 comm html title htt htt a ps docs px4 io master en ros ros2 com
  • ROS2中使用gazebo仿真时找不到libgazebo_ros_openni_kinect.so

    因为ros2的gazebo ros pkgs中 xff0c 已经将该插件移除 xff0c 或者说将该插件的功能合并到libgazebo ros camera so中 xff0c 这里是作者的说明 合并后 xff0c 深度相机的用法参考这里
  • 【ros2订阅报错】 ros2 forming pointer to reference type ‘const std::shared_ptr<const sensor_msgs::msg::Las

    这里写自定义目录标题 使用ROS2创建发布者 使用ROS2创建发布者 在创建发布者的时候 xff0c 出现 xff0c 如下问题 xff0c 经过修改cmakeList发现并不是cmake版本问题 usr include c 43 43 9
  • ROS2话题入门

    1 订阅发布模式 一个节点发布数据到某个话题上 xff0c 另外一个节点就可以通过订阅话题拿到数据 除了上述这种一个节点发布 xff0c 一个节点接受的形式外 xff0c ROS2话题通信其实还可以是1对n xff0c n对1 xff0c
  • 通过 Debian Packages安装ROS 2(Linux Mint20.2安装ROS2 foxy)

    安装ROS foxy的文章较少 这里记录一下自己安装时遇到的一些坑 1 https raw githubusercontent com访问不了 1 设置语言环境 locale check for UTF 8 sudo apt update
  • 在ros2下使用ros1_bridge与ros1自定义消息桥接

    在ros2下使用ros1 bridge与ros1自定义消息桥接 示例环境 操作系统 ubuntu 20 04 amd64 ros版本 noetic ros2版本 foxy ros1示例代码 创建ros1的工作空间catkin ws 功能包c
  • 《动手学ROS2》10.1 机器人自主导航技术概述

    动手学ROS2 10 1 机器人自主导航技术概述 本系列教程作者 小鱼 公众号 鱼香ROS QQ交流群 139707339 教学视频地址 小鱼的B站 完整文档地址 鱼香ROS官网 版权声明 如非允许禁止转载与商业用途 10 1 机器人自主导
  • ros2+xacro文件示例代码备份

    重要提示 在xacro文件虽然是xml文件 但是如果在xacro文件中随意插入自定义标签 虽然check urdf不会报错 但是最后rviz2解析的时候会出现错误 例如 如果在上述xml文件中加入以下代码将出现显示异常
  • Caught exception in launch(see debug for traceback)

    Caught exception in launch see debug for traceback Caught exception when trying to load file of format xml Caught except
  • Caught exception in launch(see debug for traceback)

    Caught exception in launch see debug for traceback Caught exception when trying to load file of format xml Caught except
  • ros2 基础学习14-- Launch:多节点启动与配置脚本

    到目前为止 每当我们运行一个ROS节点 都需要打开一个新的终端运行一个命令 机器人系统中节点很多 每次都这样启动好麻烦呀 有没有一种方式可以一次性启动所有节点呢 答案当然是肯定的 那就是Launch启动文件 它是ROS系统中多节点启动与配置
  • 使用 docker-compose up 运行时如何优雅地停止 Dockerized Python ROS2 节点?

    我有一个基于 Python 的 ROS2 节点在 Docker 容器内运行 我试图通过捕获来处理节点的正常关闭SIGTERM SIGINT信号和 或通过捕捉KeyboardInterrupt例外 问题是当我使用以下命令在容器中运行节点时do

随机推荐

  • Kubernetes:(十八)flannel网络

    目录 一 xff1a 什么是Flannel 1 1 Flannel实现原理 1 2 数据转发流程 二 xff1a Flannel网络概述 2 1 Vxlan 模式 2 1 1 通信流程 2 1 2 部署 2 1 3 相关配置 2 1 4 卸
  • 串口调试工具 O-ComTool V1.1.3

    新版本 O ComTool V2 0 0点击访问 写在之前 由于本人从事嵌入式工作 xff08 物联网方向 xff09 xff0c 经常需要和串口打交道 xff0c 面对各种规约 协议 xff0c 调试实在麻烦 xff0c 于是本人根据同事
  • 关于VSCODE的插件 一

    官方API文档 1 要学好TypeScript 官方教程 1 1TypeScript是一门弱类型语言 强类型和弱类型主要是站在变量类型处理的角度进行分类的 这些概念未经过严格定义 xff0c 它们并不是属于语言本身固有的属性 xff0c 而
  • Keil_debug

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 目录 前言 一 使用步骤 1 引入库 2 读入数据 总结 前言 程序员的工作中调试 debug xff0c 修bug xff0c 改bug
  • 前端基础知识梳理——html中的长度单位与颜色RGB值

    前言 我们在编写前端业务的时候很定会使用到长度单位 xff0c 这对于我们构建前端元素 xff0c 布局 xff0c 定位是很重要的 就像我们在盖房子的时候 xff0c 需要使用标尺线精确的测量 xff0c 也要使用颜色用于装饰页面 在ht
  • 【Hadoop】熟悉常用的HBase操作(Java实现)

    一 实验平台 操作系统 xff1a Linux deepin Hadoop版本 xff1a 2 7 7 HBase版本 xff1a 1 2 6 Java IDE xff1a Eclipse 二 实验内容 1 使用 Hadoop提供的Java
  • Hadoop权威指南:知识梳理(一)

    第一章 xff1a 初识Hadoop MapReduce三大设计目标 xff1a 为只需要短短几分钟或几个小时就可以完成的作业提供服务运行于同一个内部有高速网络连接的数据中心内数据中心内的计算器都是可靠的 专门的硬件 提供Hadoop支持的
  • tasksel —– ubuntu里面方便安装服务的软件

    用这个软件可以方便安装dns server lamp kubuntu desktop ubuntu desktop xubuntu之类的软件包 这个软件在ubuntu server里是预装的 xff0c 而在桌面版里是不预装的 xff0c
  • 信号量能被 FixedThreadPool 替代吗?

    Semaphore 信号量 从图中可以看出 xff0c 信号量的一个最主要的作用就是 xff0c 来控制那些需要限制并发访问量的资源 具体来讲 xff0c 信号量会维护 许可证 的计数 xff0c 而线程去访问共享资源前 xff0c 必须先
  • 带参数的宏定义(宏函数)

    宏函数没有普通函数压栈 跳转 返回等的开销 xff0c 可以提高程序的效率 宏的名字中不能有空格 xff1b 用括号括住每一个参数 xff0c 并括住宏的整体定义 xff1b 用大写字母表示宏的函数名 define SUM xff08 a
  • OVN&OVS代码下载、编译安装以及运行步骤

    1 代码下载 新建代码目录 home code 下载ovs代码 xff1a git clone b branch 2 15 https github com openvswitch ovs git 下载ovn代码 xff1a git clo
  • CMMI过程改进反例

    xfeff xfeff 最近一直在看 CMMI 的资料 xff0c 越看觉得越有意思 xff0c 今天看到过程改进的时候 xff0c 突然想起来之前所在的公司发生的过程改进相关的事儿来 公司通过 CMMI3 级认证之后 xff0c PMO
  • hadoop 超详细入门wordcount

    概述 今天博客收到了第一条评论 xff0c 感觉很赞哦 xff0c 最近一直在学习hadoop xff0c 主要是结合 实战Hadop xff1a 开启通向云计算的捷径 刘鹏 xff0c 然后apache官网的doc xff08 还是要以官
  • NOIP2018集训总结

    由于语文水平有限 xff0c 精美的桥段 xff0c 跌宕起伏的情节是不可能的了 xff0c 也许看起来会很智障 初赛 前排沙发祝贺墙根火 这次初赛主要在 读程序填空 上失分较多 先找几个不是原因的原因 xff1a 考前那晚宿舍里人巨吵考前
  • Hadoop大数据入门到实战(第四节) - HDFS文件系统(使用)

    这一小节我们来学习 xff1a 1 HDFS的设计 xff0c 2 HDFS常用命令 HDFS的设计 分布式文件系统 客户 xff1a 帮我保存一下这几天的数据 程序猿 xff1a 好嘞 xff0c 有多大呢 xff1f 客户 xff1a
  • HashMap什么时候重写hashcode和equals方法,为什么需要重写

    转载自 xff1a http bdcwl blog 163 com blog static 765222652009112744733937 HashSet内部是通过HashMap实现 只有使用排序的时候才使用TreeMap 否知使用Has
  • 运维如何解决终端部门投诉

    东部某省会城市的联通分公司 xff0c 内部业务系统都运行在VMware为基础的虚拟化环境中 xff0c 但联通的网络运维部在运维时却遇到了很多难题 由于V center的operation manager等云管产品只能监控到虚拟化网络的基
  • Latex并排显示两张图片

    begin figure htbp begin minipage t 0 5 linewidth centering includegraphics width 61 textwidth figures karate graph pdf c
  • HashMap原理深入理解

    hashing 哈希法 的概念 散列法 xff08 Hashing xff09 是一种将字符组成的字符串转换为固定长度 xff08 一般是更短长度 xff09 的数值或索引值的方法 xff0c 称为散列法 xff0c 也叫哈希法 由于通过更
  • ROS2通过话题的发布与订阅进行串口通信

    目录 步骤新建一个cpp header的包进入include xff0c 新建头文件minimal publisher hpp进入src目录 xff0c 新建文件minimal publisher cpp进入src目录 xff0c 新建文件