[其他ST产品] ROS与STM32通信

[复制链接]
6914|12
 楼主| 逢dududu必shu 发表于 2023-5-31 00:14 | 显示全部楼层 |阅读模式
一、相关原理及准备工作
完成ROS与STM32之间的串口通信,需要准备的硬件有STM32串口,TTL转USB模块,Linux硬件设备,博主使用的STM32是正点原子的STM32精英板,Linux硬件设备下搭载的rROS环境是ros-kinetic,其工作原理如下图所示
20954647620bd8d7ee.png
在开始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

32665647620e680ff4.png
 楼主| 逢dududu必shu 发表于 2023-5-31 00:14 | 显示全部楼层
STM32与ROS串口通信测试工程文件程序详解
main.c文件
  1. #include "delay.h"
  2. #include "sys.h"
  3. #include "usart.h"
  4. #include "led.h"
  5. #include "mbotLinuxUsart.h"
  6. //测试发送变量
  7. short testSend1   =5000;
  8. short testSend2   =2000;
  9. short testSend3   =1000;
  10. unsigned char testSend4 = 0x05;
  11. //测试接收变量
  12. int testRece1     =0;
  13. int testRece2     =0;
  14. unsigned char testRece3 = 0x00;

  15. int main(void)
  16. {       
  17.         delay_init();                                          
  18.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  19.         uart_init(115200);                               
  20.         while(1)
  21.         {
  22.             usartSendData(testSend1,testSend2,testSend3,testSend4);
  23.                 delay_ms(13);
  24.         }
  25. }
  26. //串口中断服务函数,接收数据
  27. void USART1_IRQHandler()
  28. {
  29.         if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  30.          {
  31.                  USART_ClearITPendingBit(USART1,USART_IT_RXNE);
  32.                  //从ROS接收数据,并把他们存放到以下三个变量当中
  33.                  usartReceiveOneData(&testRece1,&testRece2,&testRece3);
  34.          }
  35. }
 楼主| 逢dududu必shu 发表于 2023-5-31 00:15 | 显示全部楼层
mbotLinuxUsart.c文件
  1. #include "mbotLinuxUsart.h"
  2. #include "usart.h"      
  3. //数据接收暂存区
  4. unsigned char  receiveBuff[16] = {0};   
  5. //通信协议常量
  6. const unsigned char header[2]  = {0x55, 0xaa};
  7. const unsigned char ender[2]   = {0x0d, 0x0a};
  8. //发送数据共用体
  9. union sendData
  10. {
  11.         short d;
  12.         unsigned char data[2];
  13. }leftVelNow,rightVelNow,angleNow;
  14. //接收数据共用体
  15. union receiveData
  16. {
  17.         short d;
  18.         unsigned char data[2];
  19. }leftVelSet,rightVelSet;

  20. int usartReceiveOneData(int *p_leftSpeedSet,int *p_rightSpeedSet,unsigned char *p_crtlFlag)
  21. {
  22.         unsigned char USART_Receiver              = 0;        
  23.         static unsigned char checkSum             = 0;
  24.         static unsigned char USARTBufferIndex     = 0;
  25.         static short j=0,k=0;
  26.         static unsigned char USARTReceiverFront   = 0;
  27.         static unsigned char Start_Flag           = START;   
  28.         static short dataLength                   = 0;

  29.         USART_Receiver = USART_ReceiveData(USART1);   

  30.         if(Start_Flag == START)
  31.         {
  32.                 if(USART_Receiver == 0xaa)                           
  33.                 {  
  34.                         if(USARTReceiverFront == 0x55)      
  35.                         {
  36.                                 Start_Flag = !START;            
  37.                                 printf("header ok\n");
  38.                                 receiveBuff[0]=header[0];      
  39.                                 receiveBuff[1]=header[1];  
  40.                                 USARTBufferIndex = 0;           
  41.                                 checkSum = 0x00;                               
  42.                         }
  43.                 }
  44.                 else
  45.                 {
  46.                         USARTReceiverFront = USART_Receiver;  
  47.                 }
  48.         }
  49.         else
  50.     {
  51.                 switch(USARTBufferIndex)
  52.                 {
  53.                         case 0:
  54.                                 receiveBuff[2] = USART_Receiver;
  55.                                 dataLength     = receiveBuff[2];            //buf[2]
  56.                                 USARTBufferIndex++;
  57.                                 break;
  58.                         case 1:
  59.                                 receiveBuff[j + 3] = USART_Receiver;        //buf[3] - buf[7]                                       
  60.                                 j++;
  61.                                 if(j >= dataLength)                        
  62.                                 {
  63.                                         j = 0;
  64.                                         USARTBufferIndex++;
  65.                                 }
  66.                                 break;
  67.                         case 2:
  68.                                 receiveBuff[3 + dataLength] = USART_Receiver;
  69.                                 checkSum = getCrc8(receiveBuff, 3 + dataLength);
  70.                                 if (checkSum != receiveBuff[3 + dataLength]) //buf[8]
  71.                                 {
  72.                                         printf("Received data check sum error!");
  73.                                         return 0;
  74.                                 }
  75.                                 USARTBufferIndex++;
  76.                                 break;
  77.                         case 3:
  78.                                 if(k==0)
  79.                                 {
  80.                                         k++;
  81.                                 }
  82.                                 else if (k==1)
  83.                                 {                       
  84.                                          for(k = 0; k < 2; k++)
  85.                                         {
  86.                                                 leftVelSet.data[k]  = receiveBuff[k + 3]; //buf[3]  buf[4]
  87.                                                 rightVelSet.data[k] = receiveBuff[k + 5]; //buf[5]  buf[6]
  88.                                         }                               
  89.                                         *p_leftSpeedSet  = (int)leftVelSet.d;
  90.                                         *p_rightSpeedSet = (int)rightVelSet.d;
  91.                                         //ctrlFlag
  92.                                         *p_crtlFlag = receiveBuff[7];                //buf[7]
  93.                                         USARTBufferIndex   = 0;
  94.                                         USARTReceiverFront = 0;
  95.                                         Start_Flag         = START;
  96.                                         checkSum           = 0;
  97.                                         dataLength         = 0;
  98.                                         j = 0;
  99.                                         k = 0;
  100.                                 }
  101.                                 break;
  102.                          default:break;
  103.                 }               
  104.         }
  105.         return 0;
  106. }

  107. void usartSendData(short leftVel, short rightVel,short angle,unsigned char ctrlFlag)
  108. {
  109.         unsigned char buf[13] = {0};
  110.         int i, length = 0;
  111.         leftVelNow.d  = leftVel;
  112.         rightVelNow.d = rightVel;
  113.         angleNow.d    = angle;
  114.         for(i = 0; i < 2; i++)
  115.                 buf[i] = header[i];                      // buf[0] buf[1]
  116.         length = 7;
  117.         buf[2] = length;                             // buf[2]
  118.         for(i = 0; i < 2; i++)
  119.         {
  120.                 buf[i + 3] = leftVelNow.data[i];         // buf[3] buf[4]
  121.                 buf[i + 5] = rightVelNow.data[i];        // buf[5] buf[6]
  122.                 buf[i + 7] = angleNow.data[i];           // buf[7] buf[8]
  123.         }
  124.         buf[3 + length - 1] = ctrlFlag;              // buf[9]
  125.         buf[3 + length] = getCrc8(buf, 3 + length);  // buf[10]
  126.         buf[3 + length + 1] = ender[0];              // buf[11]
  127.         buf[3 + length + 2] = ender[1];              // buf[12]
  128.         USART_Send_String(buf,sizeof(buf));
  129. }
  130. void USART_Send_String(u8 *p,u16 sendSize)
  131. {
  132.         static int length =0;
  133.         while(length<sendSize)
  134.         {   
  135.                 while( !(USART1->SR&(0x01<<7)) );
  136.                 USART1->DR=*p;                  
  137.                 p++;
  138.                 length++;
  139.         }
  140.         length =0;
  141. }
  142. unsigned char getCrc8(unsigned char *ptr, unsigned short len)
  143. {
  144.         unsigned char crc;
  145.         unsigned char i;
  146.         crc = 0;
  147.         while(len--)
  148.         {
  149.                 crc ^= *ptr++;
  150.                 for(i = 0; i < 8; i++)
  151.                 {
  152.                         if(crc&0x01)
  153.                 crc=(crc>>1)^0x8C;
  154.                         else
  155.                 crc >>= 1;
  156.                 }
  157.         }
  158.         return crc;
  159. }
 楼主| 逢dududu必shu 发表于 2023-5-31 00:15 | 显示全部楼层
ROS测试功能包文件结构
主要包括:
(1)通信协议以及串口配置程序文件mbot_linux_serial.cpp
(2)系统结构测试主程序文件publish_node.cpp
(3)一个基础ROS功能包具备的其他程序文件
 楼主| 逢dududu必shu 发表于 2023-5-31 00:15 | 显示全部楼层
 楼主| 逢dududu必shu 发表于 2023-5-31 00:15 | 显示全部楼层
ROS与STM32串口通信测试功能包程序详解
publish_node.cpp文件
  1. //包含ros库下的ros.h头文件
  2. #include "ros/ros.h"
  3. //包含std_msgs库下的String.h头文件
  4. #include "std_msgs/String.h"
  5. //包含mbot_linux_serial.h头文件
  6. #include "mbot_linux_serial.h"

  7. //测试发送数据两
  8. double testSend1=5555.0;
  9. double testSend2=2222.0;
  10. unsigned char testSend3=0x07;
  11. //测试接受数据变量
  12. double testRece1=0.0;
  13. double testRece2=0.0;
  14. double testRece3=0.0;
  15. unsigned char testRece4=0x00;

  16. int main(int agrc,char **argv)
  17. {
  18.     //创建一个ros节点,节点名称为public_node
  19.     ros::init(agrc,argv,"public_node");
  20.     //创建句柄,用于管理节点信息
  21.     ros::NodeHandle nh;
  22.     //设置频率,10HZ
  23.     ros::Rate loop_rate(10);
  24.     //串口初始化,相关定义在mbot_linux_serial.cpp有描述
  25.     serialInit();
  26.     /*
  27.     ros::ok()在不进行任何操作时,就相当于返回True,只有在以下几种情况下会变成返回False
  28.     (1)运行终端时,按下Ctrl-C时
  29.     (2)我们被一个同名同姓的节点从网络中踢出。
  30.     (3)ros::shutdown()被应用程序的另一部分调用。
  31.     (4)所有的ros::NodeHandles都被销毁了。
  32.     */
  33.     while(ros::ok())
  34.     {
  35.         /*
  36.         ros::spinOnce()和ros::spin()是ros消息回调处理函数
  37.         ros消息回调处理函数原理:如果你的程序写了相关的消息订阅函数,那么程序在执行过程中,除了主程序以外,ROS还会自动在后台按照你规定的格式,接受订阅的消息,但是所接到的消息并不是立刻就被处理,而是必须要等到ros::spin()或ros::spinOnce()执行的时候才被调用
  38.         他们的区别在于ros::spinOnce调用后会继续执行其后面的语句,而ros::spin()则在调用后不会继续执行其后面的语句
  39.         */
  40.         ros::spinOnce();
  41.         //向STM32端发送数据,前两个为double类型,最后一个为unsigned char类型,其函数相关定义在mbot_linux_serial.cpp有描述
  42.             writeSpeed(testSend1,testSend2,testSend3);
  43.         //从STM32接收数据,输入参数依次转化为小车的线速度、角速度、航向角(角度)、预留控制位,其函数相关定义在mbot_linux_serial.cpp有描述
  44.             readSpeed(testRece1,testRece2,testRece3,testRece4);
  45.         //打印数据
  46.             ROS_INFO("%f,%f,%f,%d\n",testRece1,testRece2,testRece3,testRece4);
  47.             //等待100ms的时间
  48.         loop_rate.sleep();
  49.     }
  50.     return 0;
  51. }
 楼主| 逢dududu必shu 发表于 2023-5-31 00:15 | 显示全部楼层
mbot_linux_serial.cpp文件
  1. //包含对应头文件
  2. #include "mbot_linux_serial.h"
  3. using namespace std;//设定工作空间的名称
  4. using namespace boost::asio;//设定工作空间的名称
  5. //串口相关对象
  6. //创建一个 io_service实例
  7. boost::asio::io_service iosev;
  8. //构造一个串口,将"/dev/ttySUB0"转移给实例iosev
  9. boost::asio::serial_port sp(iosev, "/dev/ttyUSB0");
  10. boost::system::error_code err;
  11. /********************************************************
  12.             串口发送接收相关常量、变量、共用体对象
  13. ********************************************************/
  14. const unsigned char ender[2] = {0x0d, 0x0a};//数据尾
  15. const unsigned char header[2] = {0x55, 0xaa};//数据头
  16. //发送左右轮速控制速度共用体
  17. union sendData
  18. {
  19.         short d;
  20.         unsigned char data[2];
  21. }leftVelSet,rightVelSet;
  22. //接收数据(左轮速、右轮速、角度)共用体(-32767 - +32768)
  23. union receiveData
  24. {
  25.         short d;
  26.         unsigned char data[2];
  27. }leftVelNow,rightVelNow,angleNow;
  28. /********************************************************
  29. 函数功能:串口参数初始化
  30. 入口参数:无
  31. 出口参数:
  32. ********************************************************/
  33. void serialInit()
  34. {
  35.     sp.set_option(serial_port::baud_rate(115200));//设置波特率
  36.     sp.set_option(serial_port::flow_control(serial_port::flow_control::none));//流量控制
  37.     sp.set_option(serial_port::parity(serial_port::parity::none));//奇偶校验
  38.     sp.set_option(serial_port::stop_bits(serial_port::stop_bits::one));//停止位
  39.     sp.set_option(serial_port::character_size(8)); //字符大小
  40. }
  41. /********************************************************
  42. 函数功能:将对机器人的左右轮子控制速度,打包发送给下位机
  43. 入口参数:机器人线速度、角速度
  44. 出口参数:
  45. ********************************************************/
  46. void writeSpeed(double Left_v, double Right_v,unsigned char ctrlFlag)
  47. {
  48.     unsigned char buf[11] = {0};//
  49.     int i, length = 0;

  50.     leftVelSet.d  = Left_v;//mm/s
  51.     rightVelSet.d = Right_v;

  52.     // 设置消息头
  53.     for(i = 0; i < 2; i++)
  54.         buf[i] = header[i];             //buf[0]  buf[1]
  55.    
  56.     // 设置机器人左右轮速度
  57.     length = 5;
  58.     buf[2] = length;                    //buf[2]
  59.     for(i = 0; i < 2; i++)
  60.     {
  61.         buf[i + 3] = leftVelSet.data[i];  //buf[3] buf[4]
  62.         buf[i + 5] = rightVelSet.data[i]; //buf[5] buf[6]
  63.     }
  64.     // 预留控制指令
  65.     buf[3 + length - 1] = ctrlFlag;       //buf[7]

  66.     // 设置校验值、消息尾
  67.     buf[3 + length] = getCrc8(buf, 3 + length);//buf[8]
  68.     buf[3 + length + 1] = ender[0];     //buf[9]
  69.     buf[3 + length + 2] = ender[1];     //buf[10]

  70.     // 通过串口下发数据
  71.     boost::asio::write(sp, boost::asio::buffer(buf));
  72. }
  73. /********************************************************
  74. 函数功能:从下位机读取数据
  75. 入口参数:机器人左轮轮速、右轮轮速、角度,预留控制位
  76. 出口参数:bool
  77. ********************************************************/
  78. bool readSpeed(double &Left_v,double &Right_v,double &Angle,unsigned char &ctrlFlag)
  79. {
  80.     char i, length = 0;
  81.     unsigned char checkSum;
  82.     unsigned char buf[150]={0};
  83.     //=========================================================
  84.     //此段代码可以读数据的结尾,进而来进行读取数据的头部
  85.     try
  86.     {
  87.         boost::asio::streambuf response;
  88.         boost::asio::read_until(sp, response, "\r\n",err);   
  89.         copy(istream_iterator<unsigned char>(istream(&response)>>noskipws),
  90.         istream_iterator<unsigned char>(),
  91.         buf);
  92.     }  
  93.     catch(boost::system::system_error &err)
  94.     {
  95.         ROS_INFO("read_until error");
  96.     }
  97.     //=========================================================        

  98.     // 检查信息头
  99.     if (buf[0]!= header[0] || buf[1] != header[1])   //buf[0] buf[1]
  100.     {
  101.         ROS_ERROR("Received message header error!");
  102.         return false;
  103.     }
  104.     // 数据长度
  105.     length = buf[2];                                 //buf[2]

  106.     // 检查信息校验值
  107.     checkSum = getCrc8(buf, 3 + length);             //buf[10] 计算得出
  108.     if (checkSum != buf[3 + length])                 //buf[10] 串口接收
  109.     {
  110.         ROS_ERROR("Received data check sum error!");
  111.         return false;
  112.     }   

  113.     // 读取速度值
  114.     for(i = 0; i < 2; i++)
  115.     {
  116.         leftVelNow.data[i]  = buf[i + 3]; //buf[3] buf[4]
  117.         rightVelNow.data[i] = buf[i + 5]; //buf[5] buf[6]
  118.         angleNow.data[i]    = buf[i + 7]; //buf[7] buf[8]
  119.     }

  120.     // 读取控制标志位
  121.     ctrlFlag = buf[9];
  122.    
  123.     Left_v  =leftVelNow.d;
  124.     Right_v =rightVelNow.d;
  125.     Angle   =angleNow.d;
  126.     return true;
  127. }
  128. /********************************************************
  129. 函数功能:获得8位循环冗余校验值
  130. 入口参数:数组地址、长度
  131. 出口参数:校验值
  132. ********************************************************/
  133. unsigned char getCrc8(unsigned char *ptr, unsigned short len)
  134. {
  135.     unsigned char crc;
  136.     unsigned char i;
  137.     crc = 0;
  138.     while(len--)
  139.     {
  140.         crc ^= *ptr++;
  141.         for(i = 0; i < 8; i++)
  142.         {
  143.             if(crc&0x01)
  144.                 crc=(crc>>1)^0x8C;
  145.             else
  146.                 crc >>= 1;
  147.         }
  148.     }
  149.     return crc;
  150. }
 楼主| 逢dududu必shu 发表于 2023-5-31 00:16 | 显示全部楼层
mbot_linux_serial.h文件
  1. //类似stm32程序头文件的编写规则
  2. #ifndef MBOT_LINUX_SERIAL_H
  3. #define MBOT_LINUX_SERIAL_H

  4. #include <ros/ros.h>
  5. #include <ros/time.h>
  6. #include <geometry_msgs/TransformStamped.h>
  7. #include <tf/transform_broadcaster.h>
  8. #include <nav_msgs/Odometry.h>
  9. #include <boost/asio.hpp>
  10. #include <geometry_msgs/Twist.h>

  11. extern void serialInit();
  12. extern void writeSpeed(double Left_v, double Right_v,unsigned char ctrlFlag);
  13. extern bool readSpeed(double &Left_v,double &Right_v,double &Angle,unsigned char &ctrlFlag);
  14. unsigned char getCrc8(unsigned char *ptr, unsigned short len);

  15. #endif
 楼主| 逢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 70998647621568d34b.png
 楼主| 逢dududu必shu 发表于 2023-5-31 00:16 | 显示全部楼层
将TTY转USB模块按照对应的连接关系分别与STM32和ROS端相连,确保设备连接好之后,上电,首先需要查看以下串口设备,显示如下,说明设备之间连接正常
#查看串口设备
ls -l /dev/ttyUSB*
52161647621750c3ea.png
 楼主| 逢dududu必shu 发表于 2023-5-31 00:17 | 显示全部楼层
其次,为串口添加超级用户权限

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

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

rosrun topic_example publish_node

5139264762184a6623.png

493386476218af20aa.png

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

本版积分规则

78

主题

502

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部