[关闭]
@danren-aa120 2020-10-13T09:31:51.000000Z 字数 3253 阅读 300

ROS中的串口通信

通信


参考网站
https://blog.csdn.net/qq_21830903/article/details/91457392
https://blog.csdn.net/m0_37598942/article/details/80713512
http://stevenshi.me/2017/07/18/control-mobile-base-by-serial/
http://stevenshi.me/2017/05/17/ros-serial/
https://www.cnblogs.com/hyhy904/p/11001412.html

一、ROS通讯原理
在ROS平台下,设计一个串口节点,该节点订阅talker控制节点发来的命令主题,将命令通过串口设备发送到移动底座也可以是飞控设备;同时串口节点实时接收移动底座通过串口发送过来的传感器实时数据,并将该数据封装后以 sensor 主题的模式进行发布, listenner 节点可以实现订阅该主题。这样就实现了ROS与移动底座的串口通信过程。
image.png-47.1kB
RXD 接收数据 Receive(rx) Data的简写形式。Rx:底座接收;Tx:底座发送。x没有特定的意思,就是一开始这么写,之后都这么用了,约定俗成 。

一、环境的建立及问题的解决
groundstation_interface代码中所用的是ROS系统自身的serial code,所以需要安装一个serial包,包的命令为:
$ sudo apt-get install ros-<版本号>-serial

进入下载的软件包的位置:
roscd serial

若是安装成功会看到:
$:/opt/ros/kinetic/share/serial

编译时要在Cmake和package文件夹里添加serial的依赖。实现过程中如出现如下错误,则是因为权限不够引起的:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl >'
what(): open: Permission denied
或者Unable to open serial port /dev/ttyUSB0

首先查看电脑链接的串口信息(名称),据此更改程序,查询命令为:
dmesg | grep ttyS*

打开串口权限不够解决办法:
1、非永久性:
sudo chmod 666 /dev/ttyUSB0

2、永久性解决:
将用户名加入dialout用户组:
sudo adduser USER_NAME dialout
重启系统后就可以了,通过下面的指令查看是否加入dialout用户组:
cat /etc/group

发送串口数据给下位机,则需写一个publish文件让串口节点去订阅他的话题,需要注意协议格式和消息类型。在串口获取到有效数据后打包送至底层,接着借助ros-serial的write函数功能即可(在串口程序的Callback函数中)。
接收下位机的串口数据时,串口程序会借助ros-serial的read函数去读取下位机相应的串口数据,然后以话题的形式发布出去。则需写一个subscribe文件去订阅串口节点发布的话题,同样需要注意协议格式和消息类型。

二、示例程序
串口程序:

#include <serial/serial.h>
#include <std_msgs/String.h>
#include <std_msgs/Empty.h>
serial::Serial ros_ser;
//回调函数
void callback(const std_msgs::String::ConstPtr& msg){
     ROS_INFO_STREAM("Write to serial port" << msg->data);
     ros_ser.write(msg->data);
 }
int main (int argc, char** argv){
     ros::init(argc, argv, "my_serial_node");
     ros::NodeHandle n;
     //订阅主题command
     ros::Subscriber command_sub = n.subscribe("command", 1000, callback);
     //发布主题sensor
     ros::Publisher sensor_pub = n.advertise<std_msgs::String>("sensor", 1000);
     try
     {
         ros_ser.setPort("/dev/ttyUSB0");
         ros_ser.setBaudrate(115200);
         ros_serial::Timeout to = serial::Timeout::simpleTimeout(1000);
         ros_ser.setTimeout(to);
         ros_ser.open();
     }
     catch (serial::IOException& e)
     {
         ROS_ERROR_STREAM("Unable to open port ");
         return -1;
     }
     if(ros_ser.isOpen()){
         ROS_INFO_STREAM("Serial Port opened");
     }else{
         return -1;
     }
     ros::Rate loop_rate(10);
     while(ros::ok()){
         ros::spinOnce();
         if(ros_ser.available()){
             ROS_INFO_STREAM("Reading from serial port");
             std_msgs::String serial_data;
             //获取串口数据
             serial_data.data = ros_ser.read(ros_ser.available());
             ROS_INFO_STREAM("Read: " << serial_data.data);
             //将串口数据发布到主题sensor
             sensor_pub.publish(serial_data);
         }
         loop_rate.sleep();
     }
 }

pub程序:

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
 ros::init(argc, argv, "talker");
 ros::NodeHandle n;
 //发布主题command
 ros::Publisher command_pub = n.advertise<std_msgs::String>("command", 1000);
 ros::Rate loop_rate(10);
 int count = 0;
 while (ros::ok())
 {
   std_msgs::String msg;
   std::stringstream ss_data;
   ss_data << "talker publish command " << count;
   msg.data = ss_data.str();
   ROS_INFO("%s", msg.data.c_str());
   command_pub.publish(msg);
   ros::spinOnce();
   loop_rate.sleep();
   ++count;
  }
  return 0;
}

sub程序:

#include "ros/ros.h"
#include "std_msgs/String.h"
//回调函数
void callback(const std_msgs::String::ConstPtr& msg)
{
 ROS_INFO("listener got: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
 ros::init(argc, argv, "listener");
 ros::NodeHandle n;
 //订阅主题
 ros::Subscriber sub = n.subscribe("sensor", 1000, callback);
 ros::spin();
 return 0;
}
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注