ROS环境下的串口通讯

目录

1、前言

2、内容

2.1 准备工作

2.1.1 连接外部USB设备

2.1.2 串口调试工具的下载

2.1.3 serial库的安装

2.2 代码部分

2.2.1 编写发布节点

2.2.2 编写发布节点

2.2.3 编辑checklists文件

2.2.4 编辑package.xml文件

2.2.5 编写launch文件

2.2.6 运行节点

3 可能的问题

引用


1、前言

最近项目中有一个需求,在ROS2的环境下,需要接受到两个topic,作出逻辑判断,通过串口发送特定报文到外接USB设备上。之前在论坛中找了好久没有类似的文章,因此在这里记录一下,同学们可以参考,其中如果有问题或者可以优化的地方,欢迎大家指正。

2、内容

2.1 准备工作

由于我使用的是ros2的humble版本,因此一下工作环境皆为ubuntu22.04版本以及ROS2的humble版本。该部分工作在代码编写之前就需要准备好。

2.1.1 连接外部USB设备

在调试之前建议链接USB设备,一并查看系统上是否有串口驱动,通常ubuntu系统已经集成了串口驱动:s541,可以通过以下命令行来查看:

lsmod | grep usbserial

出现一下类似情况说明驱动已经存在

此刻通过频繁插拔外界设备,之后通过下述命令进行判断该设备串口名称

dmesg| grep ttyU*

对话框中会弹出很多内容,找到频繁出现“conect”和“disconect”的便是我们想要的串口。

2.1.2 串口调试工具的下载

在此我们安装使用的是cutecom工具,安装命令见下:

sudo apt-get install cutecom

在使用时可以通过以下命令打开:

sudo cutecom

打开界面见下:

可以在setting部分进行串口参数的配置,这里的将要采用的配置为:9600 8 n 1。

2.1.3 serial库的安装

为了完成ros和外界设备的通讯,还需要安装serial库文件,由于ros2中没有集成serial库,因此需要自己下载源码进行编译安装。btw,ros中可以直接通过apt-get进行安装的,这个我会再写一份文章。

该库虽然是针对foxy的,但是在humble中也可以使用,通过一下进行clone和编译

mkdir serial
git clone https://github.com/ZhaoXiangBox/serial
cd serial
mkdir build
cmake ..
make

此刻基本上已经安装编译好了。

截至目前位置,我们已经完成了一切准备工作,下面开始进行代码部分。

2.2 代码部分

我们首先整理一下需求:

1、接收两个topic;

2、根据topic内容进行逻辑判断;

3、根据判断结果,发送16进制的8个byte的报文到设备;

在正式编写代码之前,首先先建立一下ros2的工作空间和工作包

mkdir -p ~/ros2/src
cd ~/ros2/src
ros2 pkg create --build-type ament_cmake demo

以上代码意为,建立一个名字叫做demo的功能包使用c++进行编写。

2.2.1 编写发布节点

首先编写一个节点

cd ~/ros2/src/demo/src
touch talker.cpp

之后编写代码,talker节点会根据键盘的指令输入0或者1。

#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "sensor_msgs/msg/image.hpp"
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std::chrono_literals;

class MinimalPublisher : public rclcpp::Node
{
public:
  MinimalPublisher()
      : Node("minimal_publisher"), count_(0)
  {
    publisher_ = this->create_publisher<sensor_msgs::msg::Image>("topic", 10);
    timer_ = this->create_wall_timer(
        500ms, std::bind(&MinimalPublisher::timer_callback, this));
  }

private:
  void timer_callback()
  {
    int a = 0;
    auto message = sensor_msgs::msg::Image();
    int func(0);
    std::cout << "请输入数字";
    std::cin >> func;
    switch (func)
    {
    case 0:
      a = 0;
      break;
    case 1:
      a = 4;
      break;
    }
    message.height = a;
    publisher_->publish(message);
    // std::cout << message.height << std::endl;
    RCLCPP_INFO(get_logger(), "参数1:%d", message.height);
  }

  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr publisher_;
  size_t count_;
};

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

 通过以上方式在建立一个talker1,talker1会持续发送数字4.

#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "sensor_msgs/msg/image.hpp"
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std::chrono_literals;

class MinimalPublisher : public rclcpp::Node
{
public:
  MinimalPublisher()
      : Node("minimal_publisher1"), count_(0)
  {
    publisher_ = this->create_publisher<sensor_msgs::msg::Image>("topic1", 10);
    timer_ = this->create_wall_timer(
        500ms, std::bind(&MinimalPublisher::timer_callback, this));
  }

private:
  void timer_callback()
  {
    // int a = 0;
    auto message = sensor_msgs::msg::Image();
    message.height = 4;
    publisher_->publish(message);
    std::cout << message.height << std::endl;
    RCLCPP_INFO(get_logger(),"参数2:%d",message.height);
  }

  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr publisher_;
  size_t count_;
};

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

这样,两个发布节点编写完毕。

2.2.2 编写接收节点

首先需要接受两个节点的话题,并作逻辑处理,之后使用serial库文件的函数进行数据的发送,目前我们的设备可以通过不同的报文开灯和关灯,发送报文:      0x01,0x05,0x0,0x02,0x00,0x00,0x6C,0x0A;为开灯。0x01,0x05,0x0,0x02,0xff,0x00,0x2D,0xFA;为关灯。

建立可执行文件

cd ~/ros2/src/demo/src
touch lis.cpp

以下是具体代码。

#include <functional>
#include <memory>
#include <string>
#include "serial/serial.h"
#include "rclcpp/rclcpp.hpp"
#include "message_filters/subscriber.h"
#include "message_filters/time_synchronizer.h"
#include "sensor_msgs/msg/image.hpp"
using std::placeholders::_1;
using std::placeholders::_2;


serial::Serial ros_ser;
class MinimalSubscriber : public rclcpp::Node
{
public:
  MinimalSubscriber()
      : Node("minimal_sync_subscriber")
  {
    sub1_.subscribe(this, "topic");
    sub2_.subscribe(this, "topic1");
    sync_ = std::make_shared<message_filters::TimeSynchronizer<sensor_msgs::msg::Image, sensor_msgs::msg::Image>>(sub1_, sub2_, 10);
    sync_->registerCallback(std::bind(&MinimalSubscriber::topic_callback, this, _1, _2));
  }

private:
  void topic_callback(const sensor_msgs::msg::Image::ConstSharedPtr msg1,
                      const sensor_msgs::msg::Image::ConstSharedPtr msg2) const
  {
    int c = 0;
    unsigned char buffer[8] = {0};
    c = (msg1->height) * (msg2->height);
    // std::cout <<  c << ","
    //           <<  msg1->height << ","
    //           <<  msg2->height << std::endl;
    if (c != 0)
    {
      buffer[0] = 0x01;
      buffer[1] = 0x05;
      buffer[2] = 0x00;
      buffer[3] = 0x02;
      buffer[4] = 0x00;
      buffer[5] = 0x00;
      buffer[6] = 0x6C;
      buffer[7] = 0x0A;
    }
    else
    {
      buffer[0] = 0x01;
      buffer[1] = 0x05;
      buffer[2] = 0x00;
      buffer[3] = 0x02;
      buffer[4] = 0xff;
      buffer[5] = 0x00;
      buffer[6] = 0x2D;
      buffer[7] = 0xFA;
    }
    // RCLCPP_INFO(get_logger(), "结果:%d", c);
    for (int i = 0; i < 8; i++)
    {
      std::cout << std::hex << (buffer[i] &0xff)<< " ";
    }
    std::cout<<std::endl;
    ros_ser.write(buffer,8);
  }
  message_filters::Subscriber<sensor_msgs::msg::Image> sub1_;
  message_filters::Subscriber<sensor_msgs::msg::Image> sub2_;
  std::shared_ptr<message_filters::TimeSynchronizer<sensor_msgs::msg::Image, sensor_msgs::msg::Image>> sync_;
};

int main(int argc, char *argv[])
{
  rclcpp::init(argc, argv);
  ros_ser.setPort("/dev/ttyUSB0");
  ros_ser.setBaudrate(9600);
  serial::Timeout to =serial::Timeout::simpleTimeout(1000);
  ros_ser.setTimeout(to);
  try
  {
    ros_ser.open();
  }
  catch(serial::IOException &e)
  {
    std::cout<<"unable to open"<<std::endl;
    return -1;
  }
  if(ros_ser.isOpen())
  {
    std::cout<<"open"<<std::endl;
  }
  else
  {
    return -1;
  }

  rclcpp::spin(std::make_shared<MinimalSubscriber>());
  rclcpp::shutdown();
  ros_ser.close();
  return 0;
}

2.2.3 编辑cmakelists文件

在cmakelist中添加代码中的依赖,同时给三个节点定义可执行文件。

添加依赖库

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(message_filters REQUIRED)
find_package(serial REQUIRED)

添加节点说明

add_executable(talker src/talker.cpp)
ament_target_dependencies(talker rclcpp sensor_msgs)
add_executable(talker1 src/talker1.cpp)
ament_target_dependencies(talker1 rclcpp sensor_msgs)
add_executable(lis src/lis.cpp)
ament_target_dependencies(lis rclcpp message_filters serial sensor_msgs)

2.2.4 编辑package.xml文件

同样,在package文件中添加依赖说明

  <buildtool_depend>ament_cmake</buildtool_depend>
  <buildtool_depend>rclcpp</buildtool_depend>
  <buildtool_depend>sensor_msgs</buildtool_depend>
  <buildtool_depend>message_filters</buildtool_depend>
  <buildtool_depend>serial</buildtool_depend>

2.2.5 编写launch文件

(未完待续)

2.2.6 运行节点

完成以上内容后编译一下功能包

cd ~/ros2
colcon build --packages-select demo

(未完待续)

3 可能的问题

(未完待续)

引用

1、ROS2 Humble如何使用串口驱动?(Serial)_腾腾任天真的博客-CSDN博客

(未完待续)

  • 8
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值