打印
[其他ST产品]

ROS与STM32通信

[复制链接]
3443|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一、相关原理及准备工作
完成ROS与STM32之间的串口通信,需要准备的硬件有STM32串口,TTL转USB模块,Linux硬件设备,博主使用的STM32是正点原子的STM32精英板,Linux硬件设备下搭载的rROS环境是ros-kinetic,其工作原理如下图所示

在开始ROS与STM32的通信之前,需要注意以下几点
(1)确保stm32端和ros端波特率一定一致
(2)确保是stm32串口,TTL转USB模块和Linux设备之间是按照上图所示连接,TX与RX相连,RX与TX相连,同时,也要测试连接线的好坏
(3)确保linux设备系统中有CH340/CH341驱动,一般情况下都会有,如果没有CH340/CH341驱动,需要到网上查找相关资料下载驱动
(4)确保自己的串口在Linux系统中具有超级用户权限(一般都默认不具有该权限),一般插上TTL转USB模块,Linux系统中就可以检测到ttyUSB0的串口设备
(5)如果插上TTL转USB模块后,Linux系统中查询的串口设备不是ttyUSB0的话,在编写相应的程序时,要注意串口设备名称的一致性

使用特权

评论回复
沙发
逢dududu必shu|  楼主 | 2023-5-31 00:14 | 只看该作者
STM32与ROS工程文件下载
所需要的的STM32与ROS工程文件百度云链接如下
链接: https://pan.baidu.com/s/1oOcfHVRdzKeyQKiBKLHsrg.
提取码:abcd

STM32与ROS工程文件所包含的工程文件
(1)STM32端:STM32与ROS串口通信测试工程文件
(2)ROS端:ROS与STM32串口通信测试功能包文件

使用特权

评论回复
板凳
逢dududu必shu|  楼主 | 2023-5-31 00:14 | 只看该作者
STM32测试工程文件结构
主要包括:
(1)主函数main.c
(2)文件mbotLinuxUsart.c

使用特权

评论回复
地板
逢dududu必shu|  楼主 | 2023-5-31 00:14 | 只看该作者
STM32与ROS串口通信测试工程文件程序详解
main.c文件
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "led.h"
#include "mbotLinuxUsart.h"
//测试发送变量
short testSend1   =5000;
short testSend2   =2000;
short testSend3   =1000;
unsigned char testSend4 = 0x05;
//测试接收变量
int testRece1     =0;
int testRece2     =0;
unsigned char testRece3 = 0x00;

int main(void)
{       
        delay_init();                                          
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
        uart_init(115200);                               
        while(1)
        {
            usartSendData(testSend1,testSend2,testSend3,testSend4);
                delay_ms(13);
        }
}
//串口中断服务函数,接收数据
void USART1_IRQHandler()
{
        if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
         {
                 USART_ClearITPendingBit(USART1,USART_IT_RXNE);
                 //从ROS接收数据,并把他们存放到以下三个变量当中
                 usartReceiveOneData(&testRece1,&testRece2,&testRece3);
         }
}

使用特权

评论回复
5
逢dududu必shu|  楼主 | 2023-5-31 00:15 | 只看该作者
mbotLinuxUsart.c文件
#include "mbotLinuxUsart.h"
#include "usart.h"      
//数据接收暂存区
unsigned char  receiveBuff[16] = {0};   
//通信协议常量
const unsigned char header[2]  = {0x55, 0xaa};
const unsigned char ender[2]   = {0x0d, 0x0a};
//发送数据共用体
union sendData
{
        short d;
        unsigned char data[2];
}leftVelNow,rightVelNow,angleNow;
//接收数据共用体
union receiveData
{
        short d;
        unsigned char data[2];
}leftVelSet,rightVelSet;

int usartReceiveOneData(int *p_leftSpeedSet,int *p_rightSpeedSet,unsigned char *p_crtlFlag)
{
        unsigned char USART_Receiver              = 0;        
        static unsigned char checkSum             = 0;
        static unsigned char USARTBufferIndex     = 0;
        static short j=0,k=0;
        static unsigned char USARTReceiverFront   = 0;
        static unsigned char Start_Flag           = START;   
        static short dataLength                   = 0;

        USART_Receiver = USART_ReceiveData(USART1);   

        if(Start_Flag == START)
        {
                if(USART_Receiver == 0xaa)                           
                {  
                        if(USARTReceiverFront == 0x55)      
                        {
                                Start_Flag = !START;            
                                printf("header ok\n");
                                receiveBuff[0]=header[0];      
                                receiveBuff[1]=header[1];  
                                USARTBufferIndex = 0;           
                                checkSum = 0x00;                               
                        }
                }
                else
                {
                        USARTReceiverFront = USART_Receiver;  
                }
        }
        else
    {
                switch(USARTBufferIndex)
                {
                        case 0:
                                receiveBuff[2] = USART_Receiver;
                                dataLength     = receiveBuff[2];            //buf[2]
                                USARTBufferIndex++;
                                break;
                        case 1:
                                receiveBuff[j + 3] = USART_Receiver;        //buf[3] - buf[7]                                       
                                j++;
                                if(j >= dataLength)                        
                                {
                                        j = 0;
                                        USARTBufferIndex++;
                                }
                                break;
                        case 2:
                                receiveBuff[3 + dataLength] = USART_Receiver;
                                checkSum = getCrc8(receiveBuff, 3 + dataLength);
                                if (checkSum != receiveBuff[3 + dataLength]) //buf[8]
                                {
                                        printf("Received data check sum error!");
                                        return 0;
                                }
                                USARTBufferIndex++;
                                break;
                        case 3:
                                if(k==0)
                                {
                                        k++;
                                }
                                else if (k==1)
                                {                       
                                         for(k = 0; k < 2; k++)
                                        {
                                                leftVelSet.data[k]  = receiveBuff[k + 3]; //buf[3]  buf[4]
                                                rightVelSet.data[k] = receiveBuff[k + 5]; //buf[5]  buf[6]
                                        }                               
                                        *p_leftSpeedSet  = (int)leftVelSet.d;
                                        *p_rightSpeedSet = (int)rightVelSet.d;
                                        //ctrlFlag
                                        *p_crtlFlag = receiveBuff[7];                //buf[7]
                                        USARTBufferIndex   = 0;
                                        USARTReceiverFront = 0;
                                        Start_Flag         = START;
                                        checkSum           = 0;
                                        dataLength         = 0;
                                        j = 0;
                                        k = 0;
                                }
                                break;
                         default:break;
                }               
        }
        return 0;
}

void usartSendData(short leftVel, short rightVel,short angle,unsigned char ctrlFlag)
{
        unsigned char buf[13] = {0};
        int i, length = 0;
        leftVelNow.d  = leftVel;
        rightVelNow.d = rightVel;
        angleNow.d    = angle;
        for(i = 0; i < 2; i++)
                buf[i] = header[i];                      // buf[0] buf[1]
        length = 7;
        buf[2] = length;                             // buf[2]
        for(i = 0; i < 2; i++)
        {
                buf[i + 3] = leftVelNow.data[i];         // buf[3] buf[4]
                buf[i + 5] = rightVelNow.data[i];        // buf[5] buf[6]
                buf[i + 7] = angleNow.data[i];           // buf[7] buf[8]
        }
        buf[3 + length - 1] = ctrlFlag;              // buf[9]
        buf[3 + length] = getCrc8(buf, 3 + length);  // buf[10]
        buf[3 + length + 1] = ender[0];              // buf[11]
        buf[3 + length + 2] = ender[1];              // buf[12]
        USART_Send_String(buf,sizeof(buf));
}
void USART_Send_String(u8 *p,u16 sendSize)
{
        static int length =0;
        while(length<sendSize)
        {   
                while( !(USART1->SR&(0x01<<7)) );
                USART1->DR=*p;                  
                p++;
                length++;
        }
        length =0;
}
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;
}

使用特权

评论回复
6
逢dududu必shu|  楼主 | 2023-5-31 00:15 | 只看该作者
ROS测试功能包文件结构
主要包括:
(1)通信协议以及串口配置程序文件mbot_linux_serial.cpp
(2)系统结构测试主程序文件publish_node.cpp
(3)一个基础ROS功能包具备的其他程序文件

使用特权

评论回复
7
逢dududu必shu|  楼主 | 2023-5-31 00:15 | 只看该作者

使用特权

评论回复
8
逢dududu必shu|  楼主 | 2023-5-31 00:15 | 只看该作者
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,%d\n",testRece1,testRece2,testRece3,testRece4);
            //等待100ms的时间
        loop_rate.sleep();
    }
    return 0;
}

使用特权

评论回复
9
逢dududu必shu|  楼主 | 2023-5-31 00:15 | 只看该作者
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[i] = header[i];             //buf[0]  buf[1]
   
    // 设置机器人左右轮速度
    length = 5;
    buf[2] = length;                    //buf[2]
    for(i = 0; i < 2; i++)
    {
        buf[i + 3] = leftVelSet.data[i];  //buf[3] buf[4]
        buf[i + 5] = rightVelSet.data[i]; //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, "\r\n",err);   
        copy(istream_iterator<unsigned char>(istream(&response)>>noskipws),
        istream_iterator<unsigned char>(),
        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[i]  = buf[i + 3]; //buf[3] buf[4]
        rightVelNow.data[i] = buf[i + 5]; //buf[5] buf[6]
        angleNow.data[i]    = 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;
}

使用特权

评论回复
10
逢dududu必shu|  楼主 | 2023-5-31 00:16 | 只看该作者
mbot_linux_serial.h文件
//类似stm32程序头文件的编写规则
#ifndef MBOT_LINUX_SERIAL_H
#define MBOT_LINUX_SERIAL_H

#include <ros/ros.h>
#include <ros/time.h>
#include <geometry_msgs/TransformStamped.h>
#include <tf/transform_broadcaster.h>
#include <nav_msgs/Odometry.h>
#include <boost/asio.hpp>
#include <geometry_msgs/Twist.h>

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

使用特权

评论回复
11
逢dududu必shu|  楼主 | 2023-5-31 00:16 | 只看该作者
ROS与STM32串口通信实验及运行效果
在STM32端,下载测试工程文件后,打开keil5软件,对程序进行编译烧录;在ROS端,打开vscode软件,在/home/ubuntu/fjy_xm路径下创建catkin_serial/src文件,对工作空间进行编译,并在/catkin_serial/src文件加下添加topic_example功能包,再进行工作空间的编译,并设置环境变量source devel/setup.bash

使用特权

评论回复
12
逢dududu必shu|  楼主 | 2023-5-31 00:16 | 只看该作者
将TTY转USB模块按照对应的连接关系分别与STM32和ROS端相连,确保设备连接好之后,上电,首先需要查看以下串口设备,显示如下,说明设备之间连接正常
#查看串口设备
ls -l /dev/ttyUSB*

使用特权

评论回复
13
逢dududu必shu|  楼主 | 2023-5-31 00:17 | 只看该作者
其次,为串口添加超级用户权限

#添加设备权限
sudo chmod 777 /dev/ttyUSB0

然后,新建一个终端 ,启动rosmaster,即在终端中输入roscore
在原终端下运行publish_node节点

rosrun topic_example publish_node





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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

442

帖子

0

粉丝