PS:本文需要的先行知识请自行补充
本文通过移植serial功能包,实现了部署于ROS2中的串口通讯
环境部署
调试工具
测试环境 :VMware + Ubuntu 22.04.2 LTS + ROS2-humble
带界面的串口调试助手 CuteCom :sudo apt-get install cutecom
虚拟串口模拟器 socat:sudo apt-get install socat
1 2 3 4 5 6 7 8 9 10 11
| 虚拟串口生成 `socat -d -d pty,b115200 pty,b115200`
-d:启用调试输出
pty:生成伪终端
raw:使数据保存原始模式
echo = 0:禁用输入字符的回显
b“115200”:设置波特率为 “115200”
|
观察日志,会返回两个虚拟串口对,不同的设备生成的串口名称可能每次都会不一样,输出格式例:/dev/pts/4 和 /dev/pts/5;且终端需要一直开启,虚拟串口才会保持有效;
1 2 3
| cat < /dev/pts/4 监听/dev/pts/4的数据
sudo echo "Tree" > /dev/pts/5 通过/dev/pts/5向/dev/pts/4发送数据"Tree"
|
serial安装
Github:https://github.com/ZhaoXiangBox/serial
截止2024-1:
在ROS1中可以直接安装官方提供的serial库,直接可用APT下载
而在ROS2中需要自行下载源码移植
安装ROS2-serial-driver:sudo apt install ros-humble-serial-driver
Git下载源码:
git clone https://github.com/ZhaoXiangBox/serial.git
编译源码:
1 2 3 4 5
| cd serial mkdir build
cd build camke .. & make -j
|
这里不直接编译 install 文件,可以放到功能包后在编译;
serial使用
官网:http://wjwwood.io/serial/doc/1.1.0/index.html
与ROS1中的serial使用方式一致
安装的serial是可支持ROS - humble的
1、首先我们需要添加serial的头文件
#include "serial/serial.h"
2、相关初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| serial::Serial ser; serial::Timeout to = serial::Timeout::simpleTimeout(1000); #方法设置超时时间为1000毫秒(1秒) serial::parity_t pt = serial::parity_t::parity_none; #表示不使用奇偶校验 serial::bytesize_t bt = serial::bytesize_t::eightbits; #表示每个字节包含8位 serial::flowcontrol_t ft = serial::flowcontrol_t::flowcontrol_none; #表示不使用流控制 serial::stopbits_t st = serial::stopbits_t::stopbits_one; #表示每个字节后面有1个停止位
ser.setPort("/dev/ttyS0"); ser.setBaudrate(115200); ser.setTimeout(to); ser.setParity(pt); # 设置奇偶校验 ser.setBytesize(bt); #设置字节大小 ser.setFlowcontrol(ft); #设置流控制 ser.setStopbits(st); #设置停止位
|
3、开启串口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| try { ser.open();
} catch(const std::exception &e) { ROS_ERROR_STREAM("Unable to open port."); return -1; }
if(ser.isOpen()) { ROS_INFO_STREAM("Serial Port is opende.\n");
} else { return -1; }
|
4、读写操作
1 2 3
| ser.read(ser.available());
ser.write(msg.c_str());
|
以上均可结合测试环境和虚拟串口模拟器 socat 进行测试或验证代码
ROS 实现串口收发
工程环境:RK3588 + Ubuntu 22.04.2 LTS + ROS2-humble
默认工作空间已建立,环境变量已导入脚本;
创建功能包:
ros2 pkg create <package-name> --build-type {cmake,ament_cmake,ament_python} --dependencies <依赖名字>
代码设计
以下仅贴部分代码作演示:
发布者myserial_pub.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" #include <serial/serial.h>
#define BAUDRATE 115200
class tree_node : public rclcpp::Node { public: tree_node(const std::string& name):Node(name) { RCLCPP_INFO(this->get_logger(),"This is Tree's Test");
serial_publisher_ = this->create_publisher<std_msgs::msg::String>("serial_msg",10);
timer_ = this->create_wall_timer(std::chrono::milliseconds(1000), std::bind(&tree_node::timer_callback, this));
serial_Init(); }
private:
void timer_callback() { std_msgs::msg::String message; message.data = "tree_test\r\n";
RCLCPP_INFO(this->get_logger(),"Publisher:'%s'",message.data.c_str());
// ros_ser.write(message.data.c_str());
serial_publisher_->publish(message);
size_t n = ros_ser.available(); if(n!=0) { std_msgs::msg::String msg_s; msg_s.data = ros_ser.read(ros_ser.available()); serial_publisher_->publish(msg_s); } }
void serial_Init() {
serial::Timeout to = serial::Timeout::simpleTimeout(1000); serial::parity_t pt = serial::parity_t::parity_none; serial::bytesize_t bt = serial::bytesize_t::eightbits; serial::flowcontrol_t ft = serial::flowcontrol_t::flowcontrol_none; serial::stopbits_t st = serial::stopbits_t::stopbits_one;
ros_ser.setPort("/dev/ttyS0"); // ros_ser.setPort("/dev/pts/5"); //TEST ros_ser.setBaudrate(BAUDRATE); ros_ser.setTimeout(to); ros_ser.setParity(pt); ros_ser.setBytesize(bt); ros_ser.setFlowcontrol(ft); ros_ser.setStopbits(st);
try { ros_ser.open();
} catch(const std::exception &e) { RCLCPP_ERROR(this->get_logger(), "Failed to open the serial port."); // return -1; }
} serial::Serial ros_ser;
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr serial_publisher_; };
int main(int argc, char **argv) {
// auto logger = rclcpp::get_logger("my_logger");
rclcpp::init(argc,argv); auto node = std::make_shared<tree_node>("tree_new_node");
rclcpp::spin(node);
rclcpp::shutdown(); return 0;
}
|
订阅者myserial_sub.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| class serial_sub_node : public rclcpp::Node { public: serial_sub_node(std::string name) : Node(name) { serial_Init(); serial_subscriber_ = this->create_subscription<std_msgs::msg::String>("serial_msg", 10, std::bind(&serial_sub_node::command_callback, this, std::placeholders::_1)); } private: rclcpp::Subscription<std_msgs::msg::String>::SharedPtr serial_subscriber_;
void command_callback(const std_msgs::msg::String::SharedPtr msg) { ros_ser.write(msg->data.c_str()); }
void serial_Init() {
/**/
}
serial::Serial ros_ser; };
int main(int argc, char **argv) {
// auto logger = rclcpp::get_logger("my_logger");
rclcpp::init(argc,argv); auto node = std::make_shared<serial_sub_node>("tree_serial_sub_node");
rclcpp::spin(node);
rclcpp::shutdown(); return 0;
}
|
配置CMakeLists.txt:
1 2 3 4 5 6 7 8 9 10
| add_executable(myserial_pub src/myserial_pub.cpp) ament_target_dependencies(myserial_pub rclcpp std_msgs serial)
add_executable(myserial_sub src/myserial_sub.cpp) ament_target_dependencies(myserial_sub rclcpp std_msgs serial)
install(TARGETS myserial_pub myserial_sub DESTINATION lib/${PROJECT_NAME})
|
工程概述
在/dev/ 中可以查询设备的ttyUSB* 或ttys*,选择需要的串口即可
本例程中使用/dev/ttyS0
设置串口相关参数:
波特率115200,设置超时时间为1000毫秒(1秒),不使用奇偶校验,每个字节包含8位,不使用流控制,每个字节后面有1个停止位;
功能:
发布者代码负责从串口读取数据,并通过ROS2话题定时发布数据和消息。
订阅者代码负责订阅ROS2话题,并将接收到的数据和消息写入串口。