ROS与STM32串口通信测试功能包程序详解
publish_node.cpp文件
/
//包含ros库下的ros.h头文件
#include "ros/ros.h"
//包含std_msgs库下的String.h头文件
#include "std_msgs/String.h"
//包含mbot_linux_serial.h头文件
#include "mbot_linux_serial.h"
//测试发送数据两
double testSend1=5555.0;
double testSend2=2222.0;
unsigned char testSend3=0x07;
//测试接受数据变量
double testRece1=0.0;
double testRece2=0.0;
double testRece3=0.0;
unsigned char testRece4=0x00;
int main(int agrc,char **argv)
{
//创建一个ros节点,节点名称为public_node
ros::init(agrc,argv,"public_node");
//创建句柄,用于管理节点信息
ros::NodeHandle nh;
//设置频率,10HZ
ros::Rate loop_rate(10);
//串口初始化,相关定义在mbot_linux_serial.cpp有描述
serialInit();
/*
ros::ok()在不进行任何操作时,就相当于返回True,只有在以下几种情况下会变成返回False
(1)运行终端时,按下Ctrl-C时
(2)我们被一个同名同姓的节点从网络中踢出。
(3)ros::shutdown()被应用程序的另一部分调用。
(4)所有的ros::NodeHandles都被销毁了。
*/
while(ros::ok())
{
/*
ros::spinOnce()和ros::spin()是ros消息回调处理函数
ros消息回调处理函数原理:如果你的程序写了相关的消息订阅函数,那么程序在执行过程中,除了主程序以外,ROS还会自动在后台按照你规定的格式,接受订阅的消息,但是所接到的消息并不是立刻就被处理,而是必须要等到ros::spin()或ros::spinOnce()执行的时候才被调用
他们的区别在于ros::spinOnce调用后会继续执行其后面的语句,而ros::spin()则在调用后不会继续执行其后面的语句
*/
ros::spinOnce();
//向STM32端发送数据,前两个为double类型,最后一个为unsigned char类型,其函数相关定义在mbot_linux_serial.cpp有描述
writeSpeed(testSend1,testSend2,testSend3);
//从STM32接收数据,输入参数依次转化为小车的线速度、角速度、航向角(角度)、预留控制位,其函数相关定义在mbot_linux_serial.cpp有描述
readSpeed(testRece1,testRece2,testRece3,testRece4);
//打印数据
ROS_INFO("%f,%f,%f,%dn",testRece1,testRece2,testRece3,testRece4);
//等待100ms的时间
loop_rate.sleep();
}
return 0;
}
mbot_linux_serial.cpp文件
//包含对应头文件
#include "mbot_linux_serial.h"
using namespace std;//设定工作空间的名称
using namespace boost::asio;//设定工作空间的名称
//串口相关对象
//创建一个 io_service实例
boost::asio::io_service iosev;
//构造一个串口,将"/dev/ttySUB0"转移给实例iosev
boost::asio::serial_port sp(iosev, "/dev/ttyUSB0");
boost::system::error_code err;
/********************************************************
串口发送接收相关常量、变量、共用体对象
********************************************************/
const unsigned char ender[2] = {0x0d, 0x0a};//数据尾
const unsigned char header[2] = {0x55, 0xaa};//数据头
//发送左右轮速控制速度共用体
union sendData
{
short d;
unsigned char data[2];
}leftVelSet,rightVelSet;
//接收数据(左轮速、右轮速、角度)共用体(-32767 - +32768)
union receiveData
{
short d;
unsigned char data[2];
}leftVelNow,rightVelNow,angleNow;
/********************************************************
函数功能:串口参数初始化
入口参数:无
出口参数:
********************************************************/
void serialInit()
{
sp.set_option(serial_port::baud_rate(115200));//设置波特率
sp.set_option(serial_port::flow_control(serial_port::flow_control::none));//流量控制
sp.set_option(serial_port::parity(serial_port::parity::none));//奇偶校验
sp.set_option(serial_port::stop_bits(serial_port::stop_bits::one));//停止位
sp.set_option(serial_port::character_size(8)); //字符大小
}
/********************************************************
函数功能:将对机器人的左右轮子控制速度,打包发送给下位机
入口参数:机器人线速度、角速度
出口参数:
********************************************************/
void writeSpeed(double Left_v, double Right_v,unsigned char ctrlFlag)
{
unsigned char buf[11] = {0};//
int i, length = 0;
leftVelSet.d = Left_v;//mm/s
rightVelSet.d = Right_v;
// 设置消息头
for(i = 0; i < 2; i++)
buf
= header; //buf[0] buf[1]
// 设置机器人左右轮速度
length = 5;
buf[2] = length; //buf[2]
for(i = 0; i < 2; i++)
{
buf[i + 3] = leftVelSet.data; //buf[3] buf[4]
buf[i + 5] = rightVelSet.data; //buf[5] buf[6]
}
// 预留控制指令
buf[3 + length - 1] = ctrlFlag; //buf[7]
// 设置校验值、消息尾
buf[3 + length] = getCrc8(buf, 3 + length);//buf[8]
buf[3 + length + 1] = ender[0]; //buf[9]
buf[3 + length + 2] = ender[1]; //buf[10]
// 通过串口下发数据
boost::asio::write(sp, boost::asio::buffer(buf));
}
/********************************************************
函数功能:从下位机读取数据
入口参数:机器人左轮轮速、右轮轮速、角度,预留控制位
出口参数:bool
********************************************************/
bool readSpeed(double &Left_v,double &Right_v,double &Angle,unsigned char &ctrlFlag)
{
char i, length = 0;
unsigned char checkSum;
unsigned char buf[150]={0};
//=========================================================
//此段代码可以读数据的结尾,进而来进行读取数据的头部
try
{
boost::asio::streambuf response;
boost::asio::read_until(sp, response, "rn",err);
copy(istream_iterator(istream(&response)>>noskipws),
istream_iterator(),
buf);
}
catch(boost::system::system_error &err)
{
ROS_INFO("read_until error");
}
//=========================================================
// 检查信息头
if (buf[0]!= header[0] || buf[1] != header[1]) //buf[0] buf[1]
{
ROS_ERROR("Received message header error!");
return false;
}
// 数据长度
length = buf[2]; //buf[2]
// 检查信息校验值
checkSum = getCrc8(buf, 3 + length); //buf[10] 计算得出
if (checkSum != buf[3 + length]) //buf[10] 串口接收
{
ROS_ERROR("Received data check sum error!");
return false;
}
// 读取速度值
for(i = 0; i < 2; i++)
{
leftVelNow.data = buf[i + 3]; //buf[3] buf[4]
rightVelNow.data = buf[i + 5]; //buf[5] buf[6]
angleNow.data = buf[i + 7]; //buf[7] buf[8]
}
// 读取控制标志位
ctrlFlag = buf[9];
Left_v =leftVelNow.d;
Right_v =rightVelNow.d;
Angle =angleNow.d;
return true;
}
/********************************************************
函数功能:获得8位循环冗余校验值
入口参数:数组地址、长度
出口参数:校验值
********************************************************/
unsigned char getCrc8(unsigned char *ptr, unsigned short len)
{
unsigned char crc;
unsigned char i;
crc = 0;
while(len--)
{
crc ^= *ptr++;
for(i = 0; i < 8; i++)
{
if(crc&0x01)
crc=(crc>>1)^0x8C;
else
crc >>= 1;
}
}
return crc;
}
mbot_linux_serial.h文件
//类似stm32程序头文件的编写规则
#ifndef MBOT_LINUX_SERIAL_H
#define MBOT_LINUX_SERIAL_H
#include
#include
#include
#include
#include
#include
#include
extern void serialInit();
extern void writeSpeed(double Left_v, double Right_v,unsigned char ctrlFlag);
extern bool readSpeed(double &Left_v,double &Right_v,double &Angle,unsigned char &ctrlFlag);
unsigned char getCrc8(unsigned char *ptr, unsigned short len);
#endif
三、ROS与STM32串口通信实验及运行效果
在STM32端,下载测试工程文件后,打开keil5软件,对程序进行编译烧录;在ROS端,打开vscode软件,在/home/ubuntu/fjy_xm路径下创建catkin_serial/src文件,对工作空间进行编译,并在/catkin_serial/src文件加下添加topic_example功能包,再进行工作空间的编译,并设置环境变量source devel/setup.bash

将TTY转USB模块按照对应的连接关系分别与STM32和ROS端相连,确保设备连接好之后,上电,首先需要查看以下串口设备,显示如下,说明设备之间连接正常
#查看串口设备
ls -l /dev/ttyUSB*

其次,为串口添加超级用户权限
#添加设备权限
sudo chmod 777 /dev/ttyUSB0
然后,新建一个终端 ,启动rosmaster,即在终端中输入roscore
在原终端下运行publish_node节点
rosrun topic_example publish_node


ROS端正常接收到STM32端发送过来的信息,stm32查看ROS端发送过来的信息,可以在串口调试助手上进行查看,这样,我们就完成了一个简单的ROS与STM32之间的串口通信,为我们的ROS操作系统的开发提供了些许帮助
ROS与STM32串口通信测试功能包程序详解
publish_node.cpp文件
/
//包含ros库下的ros.h头文件
#include "ros/ros.h"
//包含std_msgs库下的String.h头文件
#include "std_msgs/String.h"
//包含mbot_linux_serial.h头文件
#include "mbot_linux_serial.h"
//测试发送数据两
double testSend1=5555.0;
double testSend2=2222.0;
unsigned char testSend3=0x07;
//测试接受数据变量
double testRece1=0.0;
double testRece2=0.0;
double testRece3=0.0;
unsigned char testRece4=0x00;
int main(int agrc,char **argv)
{
//创建一个ros节点,节点名称为public_node
ros::init(agrc,argv,"public_node");
//创建句柄,用于管理节点信息
ros::NodeHandle nh;
//设置频率,10HZ
ros::Rate loop_rate(10);
//串口初始化,相关定义在mbot_linux_serial.cpp有描述
serialInit();
/*
ros::ok()在不进行任何操作时,就相当于返回True,只有在以下几种情况下会变成返回False
(1)运行终端时,按下Ctrl-C时
(2)我们被一个同名同姓的节点从网络中踢出。
(3)ros::shutdown()被应用程序的另一部分调用。
(4)所有的ros::NodeHandles都被销毁了。
*/
while(ros::ok())
{
/*
ros::spinOnce()和ros::spin()是ros消息回调处理函数
ros消息回调处理函数原理:如果你的程序写了相关的消息订阅函数,那么程序在执行过程中,除了主程序以外,ROS还会自动在后台按照你规定的格式,接受订阅的消息,但是所接到的消息并不是立刻就被处理,而是必须要等到ros::spin()或ros::spinOnce()执行的时候才被调用
他们的区别在于ros::spinOnce调用后会继续执行其后面的语句,而ros::spin()则在调用后不会继续执行其后面的语句
*/
ros::spinOnce();
//向STM32端发送数据,前两个为double类型,最后一个为unsigned char类型,其函数相关定义在mbot_linux_serial.cpp有描述
writeSpeed(testSend1,testSend2,testSend3);
//从STM32接收数据,输入参数依次转化为小车的线速度、角速度、航向角(角度)、预留控制位,其函数相关定义在mbot_linux_serial.cpp有描述
readSpeed(testRece1,testRece2,testRece3,testRece4);
//打印数据
ROS_INFO("%f,%f,%f,%dn",testRece1,testRece2,testRece3,testRece4);
//等待100ms的时间
loop_rate.sleep();
}
return 0;
}
mbot_linux_serial.cpp文件
//包含对应头文件
#include "mbot_linux_serial.h"
using namespace std;//设定工作空间的名称
using namespace boost::asio;//设定工作空间的名称
//串口相关对象
//创建一个 io_service实例
boost::asio::io_service iosev;
//构造一个串口,将"/dev/ttySUB0"转移给实例iosev
boost::asio::serial_port sp(iosev, "/dev/ttyUSB0");
boost::system::error_code err;
/********************************************************
串口发送接收相关常量、变量、共用体对象
********************************************************/
const unsigned char ender[2] = {0x0d, 0x0a};//数据尾
const unsigned char header[2] = {0x55, 0xaa};//数据头
//发送左右轮速控制速度共用体
union sendData
{
short d;
unsigned char data[2];
}leftVelSet,rightVelSet;
//接收数据(左轮速、右轮速、角度)共用体(-32767 - +32768)
union receiveData
{
short d;
unsigned char data[2];
}leftVelNow,rightVelNow,angleNow;
/********************************************************
函数功能:串口参数初始化
入口参数:无
出口参数:
********************************************************/
void serialInit()
{
sp.set_option(serial_port::baud_rate(115200));//设置波特率
sp.set_option(serial_port::flow_control(serial_port::flow_control::none));//流量控制
sp.set_option(serial_port::parity(serial_port::parity::none));//奇偶校验
sp.set_option(serial_port::stop_bits(serial_port::stop_bits::one));//停止位
sp.set_option(serial_port::character_size(8)); //字符大小
}
/********************************************************
函数功能:将对机器人的左右轮子控制速度,打包发送给下位机
入口参数:机器人线速度、角速度
出口参数:
********************************************************/
void writeSpeed(double Left_v, double Right_v,unsigned char ctrlFlag)
{
unsigned char buf[11] = {0};//
int i, length = 0;
leftVelSet.d = Left_v;//mm/s
rightVelSet.d = Right_v;
// 设置消息头
for(i = 0; i < 2; i++)
buf
= header; //buf[0] buf[1]
// 设置机器人左右轮速度
length = 5;
buf[2] = length; //buf[2]
for(i = 0; i < 2; i++)
{
buf[i + 3] = leftVelSet.data; //buf[3] buf[4]
buf[i + 5] = rightVelSet.data; //buf[5] buf[6]
}
// 预留控制指令
buf[3 + length - 1] = ctrlFlag; //buf[7]
// 设置校验值、消息尾
buf[3 + length] = getCrc8(buf, 3 + length);//buf[8]
buf[3 + length + 1] = ender[0]; //buf[9]
buf[3 + length + 2] = ender[1]; //buf[10]
// 通过串口下发数据
boost::asio::write(sp, boost::asio::buffer(buf));
}
/********************************************************
函数功能:从下位机读取数据
入口参数:机器人左轮轮速、右轮轮速、角度,预留控制位
出口参数:bool
********************************************************/
bool readSpeed(double &Left_v,double &Right_v,double &Angle,unsigned char &ctrlFlag)
{
char i, length = 0;
unsigned char checkSum;
unsigned char buf[150]={0};
//=========================================================
//此段代码可以读数据的结尾,进而来进行读取数据的头部
try
{
boost::asio::streambuf response;
boost::asio::read_until(sp, response, "rn",err);
copy(istream_iterator(istream(&response)>>noskipws),
istream_iterator(),
buf);
}
catch(boost::system::system_error &err)
{
ROS_INFO("read_until error");
}
//=========================================================
// 检查信息头
if (buf[0]!= header[0] || buf[1] != header[1]) //buf[0] buf[1]
{
ROS_ERROR("Received message header error!");
return false;
}
// 数据长度
length = buf[2]; //buf[2]
// 检查信息校验值
checkSum = getCrc8(buf, 3 + length); //buf[10] 计算得出
if (checkSum != buf[3 + length]) //buf[10] 串口接收
{
ROS_ERROR("Received data check sum error!");
return false;
}
// 读取速度值
for(i = 0; i < 2; i++)
{
leftVelNow.data = buf[i + 3]; //buf[3] buf[4]
rightVelNow.data = buf[i + 5]; //buf[5] buf[6]
angleNow.data = buf[i + 7]; //buf[7] buf[8]
}
// 读取控制标志位
ctrlFlag = buf[9];
Left_v =leftVelNow.d;
Right_v =rightVelNow.d;
Angle =angleNow.d;
return true;
}
/********************************************************
函数功能:获得8位循环冗余校验值
入口参数:数组地址、长度
出口参数:校验值
********************************************************/
unsigned char getCrc8(unsigned char *ptr, unsigned short len)
{
unsigned char crc;
unsigned char i;
crc = 0;
while(len--)
{
crc ^= *ptr++;
for(i = 0; i < 8; i++)
{
if(crc&0x01)
crc=(crc>>1)^0x8C;
else
crc >>= 1;
}
}
return crc;
}
mbot_linux_serial.h文件
//类似stm32程序头文件的编写规则
#ifndef MBOT_LINUX_SERIAL_H
#define MBOT_LINUX_SERIAL_H
#include
#include
#include
#include
#include
#include
#include
extern void serialInit();
extern void writeSpeed(double Left_v, double Right_v,unsigned char ctrlFlag);
extern bool readSpeed(double &Left_v,double &Right_v,double &Angle,unsigned char &ctrlFlag);
unsigned char getCrc8(unsigned char *ptr, unsigned short len);
#endif
三、ROS与STM32串口通信实验及运行效果
在STM32端,下载测试工程文件后,打开keil5软件,对程序进行编译烧录;在ROS端,打开vscode软件,在/home/ubuntu/fjy_xm路径下创建catkin_serial/src文件,对工作空间进行编译,并在/catkin_serial/src文件加下添加topic_example功能包,再进行工作空间的编译,并设置环境变量source devel/setup.bash

将TTY转USB模块按照对应的连接关系分别与STM32和ROS端相连,确保设备连接好之后,上电,首先需要查看以下串口设备,显示如下,说明设备之间连接正常
#查看串口设备
ls -l /dev/ttyUSB*

其次,为串口添加超级用户权限
#添加设备权限
sudo chmod 777 /dev/ttyUSB0
然后,新建一个终端 ,启动rosmaster,即在终端中输入roscore
在原终端下运行publish_node节点
rosrun topic_example publish_node


ROS端正常接收到STM32端发送过来的信息,stm32查看ROS端发送过来的信息,可以在串口调试助手上进行查看,这样,我们就完成了一个简单的ROS与STM32之间的串口通信,为我们的ROS操作系统的开发提供了些许帮助
举报