PS:本文需要的先行知识请自行补充
本文使用ROS提供的serial库,实现了部署于ROS中的串口通讯
环境部署
调试工具
测试环境 :VMware + Ubuntu 20.04.2 LTS + ROS-noetic
带界面的串口调试助手 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串口功能包
安装对应ROS版本的功能包:
sudo apt install ros-noetic-serial
serial使用
官网:http://wjwwood.io/serial/doc/1.1.0/index.html
通讯的使用:
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 20.04.2 LTS + ROS-noetic
默认工作空间已建立,环境变量已导入脚本;
创建功能包:catkin_create_pkg myserial_pkg roscpp rospy std_msgs serial
代码设计
以下仅贴部分代码作演示:
发布者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
| #include <ros/ros.h> #include "serial_pkg/Person.h" #include <serial/serial.h> #include <std_msgs/String.h> #include <iostream> #include <string> #include <sstream>
int main(int argc,char **argv) { ros::init(argc,argv,"serial_publisher");
ros::NodeHandle seuNB;
ros::Publisher tree = seuNB.advertise<std_msgs::String>("Serial_Topic",10);
serial_pkg::Person person_msg; person_msg.Name = "Tree"; /*----------------------------*/ serial::Serial ser; 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;
ser.setPort("/dev/ttyS0"); ser.setBaudrate(115200); ser.setTimeout(to); ser.setParity(pt); ser.setBytesize(bt); ser.setFlowcontrol(ft); ser.setStopbits(st);
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; }
ros::Rate loop_rate(1); //HZ while(ros::ok()) { size_t n = ser.available(); if(n!=0) { ROS_INFO_STREAM("Reading from serial port:\n"); std_msgs::String msg_s; msg_s.data = ser.read(ser.available()); tree.publish(msg_s); ROS_INFO_STREAM("Read:"<< msg_s.data); } tree.publish(person_msg); ROS_INFO("Publish Info:%s",person_msg.Name.c_str()); loop_rate.sleep();
} ser.close(); 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
| serial::Serial ser;
void Msg_callback(const std_msgs::String::ConstPtr &msg_p) { std::string msg = msg_p->data; ser.write(msg.c_str()); ROS_INFO("serial_subscriber:%s",msg_p->data.c_str()); }
int main(int argc,char **argv) { setlocale(LC_CTYPE,"zh_CN.utf8");
ros::init(argc,argv,"serial_subscriber");
ros::NodeHandle seuWB;
ros::Subscriber Tree = seuWB.subscribe("Serial_Topic",10,Msg_callback);
ros::spin();
return 0;
}
|
配置CMakeLists.txt:
1 2 3 4 5 6 7 8 9
| # 生成可执行文件 add_executable(myserial_pub src/myserial_pub.cpp) # 链接库 target_link_libraries(myserial_pub ${catkin_LIBRARIES})
# 生成可执行文件 add_executable(myserial_sub src/myserial_sub.cpp) # 链接库 target_link_libraries(myserial_sub ${catkin_LIBRARIES})
|
编写launch文件:
1 2 3 4 5
| <launch> <node pkg="serial_pkg" type="myserial_pub" name="pub_node" output="screen"/> <node pkg="serial_pkg" type="myserial_sub" name="sub_node" output="screen"/>
</launch>
|
工程概述
在/dev/ 中可以查询设备的ttyUSB* 或ttys*,选择需要的串口即可
本例程中使用/dev/ttyS0
设置串口相关参数:
波特率115200,设置超时时间为1000毫秒(1秒),不使用奇偶校验,每个字节包含8位,不使用流控制,每个字节后面有1个停止位;
功能:
发布者代码负责从串口读取数据,并通过ROS话题定时发布数据和消息。
订阅者代码负责订阅ROS话题,并将接收到的数据和消息写入串口。