身处机器人行业,不想一直只做低端的单片机控制,老是待在舒适区,所以一直都想学一下ROS系统,但看了几个月资料后,感觉还是云里雾里,似懂非懂,感念似乎都很清楚,但要实际去做,却又感觉无从下手。

  于是想先找点眼前马上能用的着东西来实验一下,串口无疑是最合适的,来个ROS串口通信,就简单的跟单片机通通信也是不错的,反正其他难的也还做不了;

  当然之前看的资料也没白看,系统安装、基本概念、基本操作都得心里有底,因为Linux基础并不是很好,所以还是比较畏惧敲命令的方式的,不管在Linux系统上做什么,我都还是很乐意去找相应IDE,ROS也不例外,官网上教程全是命令行,痛苦了一阵子之后,实在觉得没必要回到原始人状态,写代码就不用说了,光是那一堆依赖都能让人崩溃,于是网上一顿搜,发现ROS开发也是有IDE的,那个欣喜之情啊,简直溢于言表;马上下载安装上了,用了一段时间,发现这工具有很多bug,有时候莫名其妙的错误,但无妨,至少不用再自己去手动添加依赖了,我心足以!

  下面详细介绍如何利用RoboWare-Studio创建一个可以跟串口通信的小例子;

  1、创建工作空间,打开Studio创建一个工作区就行了,工作区中的src文件夹会自动创建,编译一下;

  2、在src文件夹上右键选择添加C++节点,输入节点名和相应的依赖即可;依赖后面可以随时改;

  3、创建节点后,里面的src文件夹自动创建,下一步就可以创建C++文件了,如下部分,是串口操作:

#include <ros/ros.h>
#include <serial/serial.h> //ROS已经内置了的串口包
#include <std_msgs/String.h>
#include <std_msgs/Empty.h>
#include <stm32f407/tdata.h>
#include "std_msgs/UInt8.h"
#include "std_msgs/UInt8MultiArray.h"
serial::Serial ser; //声明串口对象
//回调函数
void datasend_callback(const stm32f407::tdata::ConstPtr &frm)
{
int len = frm->data.size();
printf("Send:%02d 0x", len);
for (int i = 0; i < len; i++)
{
// printf("%02X ", frm->data[i]);
printf("%02X ", frm->data.at(i));
}
printf("\r\n");
ser.write(frm->data); //发送串口数据
}
int main (int argc, char** argv)
{
//初始化节点
ros::init(argc, argv, "serial_readwrite_node");
//声明节点句柄
ros::NodeHandle nh;
static int len;
stm32f407::tdata RecvData;
//订阅主题,并配置回调函数
ros::Subscriber send_sub = nh.subscribe("datasend", 1000, datasend_callback);
//发布主题
ros::Publisher read_pub = nh.advertise<stm32f407::tdata>("datarecv", 1000);
try
{
//设置串口属性,并打开串口
ser.setPort("/dev/ttyUSB1");
ser.setBaudrate(115200);
serial::Timeout to = serial::Timeout::simpleTimeout(1000);
ser.setTimeout(to);
ser.open();
}
catch (serial::IOException& e)
{
ROS_ERROR_STREAM("Unable to open port ");
return -1;
}
//检测串口是否已经打开,并给出提示信息
if(ser.isOpen())
{
ROS_INFO_STREAM("Serial Port initialized");
}
else
{
return -1;
}
//指定循环的频率
ros::Rate loop_rate(10);
while(ros::ok())
{
len = ser.available();
if(len>0)
{
std_msgs::UInt8MultiArray serial_data;
// len = ser.available();
ser.read(serial_data.data,len) ;
printf("Recv:%02d 0x", len);
RecvData.data.clear();//清除上一次的数据
for (size_t i = 0; i < len; i++)
{
// ROS_INFO("%02X", serial_data.data[i]);
printf("%02X ", serial_data.data[i]);
RecvData.data.push_back(serial_data.data[i]);
}
printf("\r\n");
read_pub.publish(RecvData);
}
//处理ROS的信息,比如订阅消息,并调用回调函数
ros::spinOnce();
loop_rate.sleep();
}
}
 

  其中操作非常简单,定义了一个订阅句柄,用来接收其他节点发布的数据,然后在回调当中,用串口直接发送出去,另外定义一个发布句柄,在主循环不断扫描串口,读取数据,

如果有读到数据,则发布出去,其他节点订阅后既可查看单片机上发的数据;

  其中无论订阅还是发布都使用到了一个自定义消息,用来规定要传输数据的格式,添加Msg文件夹后,再添加msg文件,内容如下:

  这便是两个话题对应消息格式;

  上面这个节点主要用作直接对硬件串口进行收发,它只管收发,并不理会收发的数据是什么,具体流程为,如果接收其他节点发布的消息,则将之原样发送到串口,如何

扫描到串口有数据,则将读到的数据发布到话题上;

  所以如果我们要发特定数据并对接收到的做数据处理,则需添加另外一个节点,名称定为datapro,内容如下:

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
#include <stm32f407/tdata.h>
#include "CRC.h"
//回调函数
void datarecv_callback(const stm32f407::tdata::ConstPtr &frm)
{
int len = frm->data.size();
// std::cout << "frm->data.size=" << cnt << std::endl;
// ROS_INFO_STREAM("Writing to serial port" << msg->data);
// ROS_INFO_STREAM("Send: " << frm->data);
// printf("Recv:%ld 0x", cnt);
printf("Recv:%02d 0x", len);
for (int i = 0; i < len; i++)
{
// printf("%02X ", frm->data[i]);
printf("%02X ", frm->data.at(i));
}
printf("\r\n");
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "datapro");
ros::NodeHandle nh;
//订阅主题,并配置回调函数
ros::Subscriber recv_sub = nh.subscribe("datarecv", 1000, datarecv_callback);
ros::Publisher send_pub = nh.advertise<stm32f407::tdata>("datasend", 1000);
ros::Rate loop_rate(1);//10hz
int len=0;
stm32f407::tdata SendData;
CRC cc_crc;
//复位 A6 B7 01 02 04 00 B7 1B 02 A6 B7 01 02 04 05 B7 E4 F7
uint8_t Buffer[9] ={0xA6, 0xB7, 0x01, 0x02, 0x04, 0x05, 0xB7, 0xE4, 0xF7};
uint16_t CrcSum=0;
CrcSum = cc_crc.CRC16_ccitt(Buffer,0,7);
// printf("0x%04X\r\n",CrcSum);
Buffer[7] = (uint8_t)(CrcSum>>8);
Buffer[8] = (uint8_t)CrcSum;
SendData.data.clear();
for (size_t i = 0; i < 9; i++)
{
SendData.data.push_back(Buffer[i]);
}
 
// SendData.data.push_back(0xA6);
// SendData.data.push_back(0xB7);
// SendData.data.push_back(0x01);
// SendData.data.push_back(0x02);
// SendData.data.push_back(0x04);
// SendData.data.push_back(0x05);
// SendData.data.push_back(0xB7);
// SendData.data.push_back(0xE4);
// SendData.data.push_back(0xF7);
// frame_pub.publish(SendData);
while (ros::ok())
{
len = SendData.data.size();
printf("Send:%02d 0x", len);
for (int i = 0; i < len; i++)
{
// printf("%02X ", SendData.data[i]);
printf("%02X ", SendData.data.at(i));
}
printf("\r\n");
 
send_pub.publish(SendData);
ros::spinOnce();
loop_rate.sleep();
}
return 0;
}
 
  该节点的作用就是发布特定格式的数据到发送话题上,以及接收上一个节点发布的话题消息;逻辑很简单,其中需要注意的是在添加CRC校验时,添加:C++类,输入类名称后
会有几个选项,如下:
            

    之前一直没搞懂他们的区别,都是选的“加入到新的可执行文件中”,如何选这一项,则改类的代码会单独形成一个可执行文件,在运行时需要单独开启,所以如果两个

功能相对独立,则可以选一项,但是如果代码是调用关系,比如上面的CRC类实际上是被datapro调用的,所以这里我们选择第二项会更好,运行时我们只需要驱动datapro就

可以使用CRC类功能;

  另外Studio工具有一个bug是,添加C++类时,会报错,提示找不到头文件,这问题折腾了很久,后来发现需要把自动生成的头文件,提到上一级目标才可编译通过,即将

头文件直接到include文件夹下。

  另一个小提醒就是,每次添加新文件或新类时,最好点一下刷新按钮,有时候看不到添加的类文件,需要手动刷新一下才会出来;

ROS串口通信的更多相关文章

  1. .NET 串口通信

    这段时间做了一个和硬件设备通信的小项目,涉及到扫描头.输送线.称重机.贴标机等硬件.和各设备之间通信使用的是串口或网络(Socket)的方式.扫描头和贴标机使用的网络通信,输送线和称重机使用的是串口通 ...

  2. BluetoothChat用于蓝牙串口通信的修改方法

    本人最近在研究嵌入式的串口通信,任务是要写一个手机端的遥控器用来遥控双轮平衡小车.界面只用了一个小时就写好了,重要的问题是如何与板子所带的SPP-CA蓝牙模块进行通信. SPP-CA模块自带代码,在这 ...

  3. Win10 IoT C#开发 4 - UART 串口通信

    Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,既可以开发设备UI与用户交互式操作,又可以控制GPIO等接口,使得原来嵌入式繁琐的开发变得简单.通过Remote Debug ...

  4. 使用Java实现简单串口通信

    最近一门课要求编写一个上位机串口通信工具,我基于Java编写了一个带有图形界面的简单串口通信工具,下面详述一下过程,供大家参考 ^_^ 一: 首先,你需要下载一个额外的支持Java串口通信操作的jar ...

  5. Java 串口通信

    在Windows系统下,用Java开发串口通信相关的程序时,需要用到几个文件. (1)win32com.dll 要放在jdk\jre\bin目录下. (2)comm.jar 和javax.comm.p ...

  6. .NET 串口通信中断接收,包含0X1A(作为EOF)

    .NET串口通信中将`0X1A`当做EOF处理,.NET接收到EOF会触发一次接收中断,此时事件形参`SerialDataReceivedEventArgs`值为枚举 `Eof`,其他为`Chars` ...

  7. STC12C5A60S2 双串口通信

    STC12C5A60S2单片机是一款功能比较强大的单片机,它拥有两个全双工串行通信接口,串口1的功能及操作与传统51单片机串行口相同:特殊的是STC12C5A60S2单片机内部有一个独立波特率发生器, ...

  8. mfc 调用Windows的API函数实现同步异步串口通信(源码)

    在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信.串口通信方便易行,应用广泛. 一般情况下,工控机和各智能仪表通过RS485总线进行通信.RS485的通信方式是半 ...

  9. VS2008基于对话框的MFC上位机串口通信(C++实现)简单例程

    首先,在 vs2008 环境下创建 MFC 运用程序 设置项目名称为 ComTest(这个地方随意命名,根据个人习惯),点击确定后,点击下一步 出现如下界面 选择"基于对话框"模式 ...

随机推荐

  1. 【easy】112.path sum 113.-----------------

    求是否有从根到叶的路径,节点和等于某个值. /** * Definition for a binary tree node. * struct TreeNode { * int val; * Tree ...

  2. 史上最明白的 NULL、0、nullptr 区别分析(老师讲N篇都没讲明白的东东),今天终于明白了,如果和我一样以前不明白的可以好好的看看...

    C的NULL 在C语言中,我们使用NULL表示空指针,也就是我们可以写如下代码: int *i = NULL; foo_t *f = NULL; 实际上在C语言中,NULL通常被定义为如下: #def ...

  3. 网络流24题 ——运输问题 luogu 4015

    题目描述:这里 题面已经提示我们这是费用流了 那么由源点向所有仓库连边,容量为仓库原有货物量,费用为0 然后由所有零售商店向汇点连边,容量为一个零售商店的需求量,费用为0 最后由仓库向零售商店连边,容 ...

  4. 剑指offer字符串1

    面试题5:替换空格 请实现一个函数,将一个字符串中的每个空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. class Solu ...

  5. Angular路由——辅助路由

    一.辅助路由语法 同时控制多个插座内容. 第一步: 模版上除了主插座,还需要声明一个带name属性的插座 第二步: 路由配置中配置name为aux的插座上可以显示哪些组件,比如显示xxx和yyy组件. ...

  6. shaderlab UV动画所需的变量声明

    优化资源.美术需要迫使自己的顶点shader能够进行TRANSFORM_TEX运算,进行该运算的前提是需要声明一个 _MainTex_ST 变量,类型为float4即可. 此时就可以使用unity c ...

  7. mysql配置主从复制

    1.原理: MySQL之间数据复制的基础是二进制日志文件(binary log file).一台MySQL数据库一旦启用二进制日志后,其作为master,它的数据库中所有操作都会以“事件”的方式记录在 ...

  8. django模板中获取域名地址

    获取域名: {{ request.get_host }} 获取路径:{{ request.path }} 获取协议 {{ request.scheme }}

  9. VB封装的WebSocket模块,拿来即用

    一共就下面的两个模块,调用只使用到mWSProtocol模块,所有调用函数功能简单介绍一下: 建立连接后就开始握手,服务端用Handshake()验证,如果是客户端自己发送握手封包接收数据,先用Ana ...

  10. Linux bash基础特性二

    shell脚本的组成部分 shebang 各种命令组合 编程变量种类 本地变量: 仅仅在当前的shell生效 环境变量: 在当前和子shell生效 局部变量: shell进程某代码片段 位置变量: $ ...