鱼香ROS社区
    • 版块
    • 最新
    • 未解决
    • 已解决
    • 群组
    • 注册
    • 登录
    紧急通知:禁止一切关于政治&VPN翻墙等话题,发现相关帖子会立马删除封号
    提问前必看的发帖注意事项: 社区问答规则(小鱼个人)更新 | 高质量帖子发布指南

    stm32系列MICROROS环境配置(使用cubemx+clion+freertos)

    已定时 已固定 已锁定 已移动
    MicroROS
    microros microrosagent
    7
    15
    3.3k
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • 24758873092
      量子之海里的卡夫卡
      最后由 编辑


      前言

      `本文主要分享一下如何快速在stm32f103或stm32系列的任意板子上运行microros的具体步骤。(所需要的文件已经生成好了)。(目前,已经跑通stm32f103以及stm32f407)
      生成的步骤极其踩坑,是一段令人痛苦的回忆。
      生成静态链接库的踩坑主要内容就是 所有资源都是外网,需要代理,如果网络不通畅所有的步骤都得重新来,尤其是在docker 容器运行时的代理配置,直接给我干吐血了.....
      好的废话不多说,此篇文章的只需要下载工程后,即可使用(对于stm32f103rct6),其他芯片的可通过cubemx软件选择对应芯片即可。
      对于microros如何从源码开始编译可点击以下链接

      microros部署教程
      microros github源码
      microros 官网

      一、工程下载(stm32f103rct6版本)

      链接:https://pan.baidu.com/s/1QUw-3e9ZpeAaA7Khyjd_Dg?pwd=iebe
      提取码:iebe
      --来自百度网盘超级会员V4的分享

      二、使用步骤

      1.了解每个文件夹的作用

      microros2pc_test 用于开发板与linux端ros2通信测试功能包
      microros_ws 构建静态链接库,agent等功能包的setup功能包 具体使用可参考microros官网
      microros_agent_ws linux端运行microros的agent功能包
      freertos_rosnode stm32工程文件,包含了静态链接 .a文件
      在freertos_rosnode下的micro_ros_stm32cubemx_utils文件夹是极其重要的文件夹,一般不做修改,移植所需文件夹 。同时可参考其中的main.c文件如何编程microros

      2.移植到自己的工程

      打开stm32 cubemx,创建一个新的工程 (我以stm32f103rct6为例)

      1.正常配置时钟树即可

      2.rcc配置 HSE选择crystal

      选择中间件 middleware,选择freertos。如下配置 选择v2版本 选择tasks and queues双击已存在的tasks
      配置 stack size 3000 allocation static
      d15c5631-f633-439e-b951-c12444151e51-image.png

      3.配置usart:
      mode 选择asynchronous
      dma settings 点击add
      rx mode选择circular
      tx默认即可
      两者优先级都为非常高
      nvic settings 打开中断

      配置图如下
      f87ce72d-3f11-4012-bf4d-6e565f62dc0e-image.png !
      4.sys配置
      打开debug
      timebase source 选择tim1

      b0b6077d-4dac-4a5f-bc6b-c6f10dbfcaeb-image.png
      5.工程管理:
      toolchan/ide 选择makefile
      d8581f1e-4d09-42b8-a161-85ca6b6617d4-image.png

      6.代码生成:
      生成代码 打开工程所在位置,将百度网盘下载的工程中的micro_ros_stm32cubemx_utils文件夹复制到新生成的工程文件夹目录下
      如图所示
      dbfd8a4d-96b8-4679-9ab7-6e49c17aa4a6-image.png

      使用你最常用的ide或者编辑器打开工程(我以clion为例,配置文件可复制.idea文件夹到新工程目录下):需要配置好工具链,如果没有配置,可参考稚晖君配置教程(clion)

      编辑makefile文件:
      将以下内容复制到 build the application之前(需要注意 @echo $(CFLAGS) 这个最好在复制后,删掉前面的空格,使用tab补齐,是语法问题,注意即可)

      #######################################
      # micro-ROS addons
      #######################################
      LDFLAGS += micro_ros_stm32cubemx_utils/microros_static_library/libmicroros/libmicroros.a
      C_INCLUDES += -Imicro_ros_stm32cubemx_utils/microros_static_library/libmicroros/microros_include
      
      # Add micro-ROS utils
      C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/custom_memory_manager.c
      C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/microros_allocators.c
      C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/microros_time.c
      
      # Set here the custom transport implementation
      C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/microros_transports/dma_transport.c
      
      print_cflags:
         @echo $(CFLAGS)
      

      d077a885-4f82-4639-8c1b-e4eb41d04862-image.png
      最好按照cubemx语法规则将代码块中的内容放入如下类似的注释之间,这样在cubemx软件新添配置后代码不会被覆盖

      /* USER CODE BEGIN PM */
      
      /* USER CODE END PM */
      

      将以下头文件复制到freertos.c文件中

      #include "usart.h"
      #include <rcl/rcl.h>
      #include <rcl/error_handling.h>
      #include <rclc/rclc.h>
      #include <rclc/executor.h>
      #include <uxr/client/transport.h>
      #include <rmw_microxrcedds_c/config.h>
      #include <rmw_microros/rmw_microros.h>
      #include <std_msgs/msg/int32.h>
      
      bool cubemx_transport_open(struct uxrCustomTransport * transport);
      bool cubemx_transport_close(struct uxrCustomTransport * transport);
      size_t cubemx_transport_write(struct uxrCustomTransport* transport, const uint8_t * buf, size_t len, uint8_t * err);
      size_t cubemx_transport_read(struct uxrCustomTransport* transport, uint8_t* buf, size_t len, int timeout, uint8_t* err);
      
      void * microros_allocate(size_t size, void * state);
      void microros_deallocate(void * pointer, void * state);
      void * microros_reallocate(void * pointer, size_t size, void * state);
      void * microros_zero_allocate(size_t number_of_elements, size_t size_of_element, void * state);
      

      再将StartDefaultTask()函数替换为以下内容

      void StartDefaultTask(void *argument)
      {
          /* USER CODE BEGIN StartDefaultTask */
          /* Infinite loop */
          // micro-ROS configuration
      
          rmw_uros_set_custom_transport(
            true,
            (void *) &huart1,
            cubemx_transport_open,
            cubemx_transport_close,
            cubemx_transport_write,
            cubemx_transport_read);
      
          rcl_allocator_t freeRTOS_allocator = rcutils_get_zero_initialized_allocator();
          freeRTOS_allocator.allocate = microros_allocate;
          freeRTOS_allocator.deallocate = microros_deallocate;
          freeRTOS_allocator.reallocate = microros_reallocate;
          freeRTOS_allocator.zero_allocate =  microros_zero_allocate;
      
          if (!rcutils_set_default_allocator(&freeRTOS_allocator)) {
              printf("Error on default allocators (line %d)\n", __LINE__);
          }
      
          // micro-ROS app
      
          rcl_publisher_t publisher;
          std_msgs__msg__Int32 msg;
          rclc_support_t support;
          rcl_allocator_t allocator;
          rcl_node_t node;
      
          allocator = rcl_get_default_allocator();
      
          //create init_options
          rclc_support_init(&support, 0, NULL, &allocator);
      
          // create node
          rclc_node_init_default(&node, "cubemx_node", "", &support);
      
          // create publisher
          rclc_publisher_init_default(
            &publisher,
            &node,
            ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32),
            "cubemx_publisher");
      
          msg.data = 0;
      
          for(;;)
          {
            rcl_ret_t ret = rcl_publish(&publisher, &msg, NULL);
            if (ret != RCL_RET_OK)
            {
              printf("Error publishing (line %d)\n", __LINE__);
            }
      
            msg.data++;
            osDelay(10);
          }
          /* USER CODE END StartDefaultTask */
      }
      

      添加syscalls.c:
      在工程目录的core/src目录下新建syscalls.c文件,并复制以下内容到新建文件中

      /**
      *****************************************************************************
      **
      **  File        : syscalls.c
      **
      **  Author        : Auto-generated by System workbench for STM32
      **
      **  Abstract    : System Workbench Minimal System calls file
      **
      **                   For more information about which c-functions
      **                need which of these lowlevel functions
      **                please consult the Newlib libc-manual
      **
      **  Target      : STMicroelectronics STM32
      **
      **  Distribution: The file is distributed “as is,” without any warranty
      **                of any kind.
      **
      *****************************************************************************
      ** @attention
      **
      ** <h2><center>&copy; COPYRIGHT(c) 2019 STMicroelectronics</center></h2>
      **
      ** Redistribution and use in source and binary forms, with or without modification,
      ** are permitted provided that the following conditions are met:
      **   1. Redistributions of source code must retain the above copyright notice,
      **      this list of conditions and the following disclaimer.
      **   2. Redistributions in binary form must reproduce the above copyright notice,
      **      this list of conditions and the following disclaimer in the documentation
      **      and/or other materials provided with the distribution.
      **   3. Neither the name of STMicroelectronics nor the names of its contributors
      **      may be used to endorse or promote products derived from this software
      **      without specific prior written permission.
      **
      ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
      ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
      ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
      ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
      ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      **
      *****************************************************************************
      */
      
      // the code was modified by Fynn Boyer
      
      /* Includes */
      #include <sys/stat.h>
      #include <stdlib.h>
      #include <errno.h>
      #include <stdio.h>
      #include <signal.h>
      #include <time.h>
      #include <sys/time.h>
      #include <sys/times.h>
      
      
      /* Variables */
      //#undef errno
      extern int errno;
      extern int __io_putchar(int ch) __attribute__((weak));
      extern int __io_getchar(void) __attribute__((weak));
      
      register char * stack_ptr asm("sp");
      
      char *__env[1] = { 0 };
      char **environ = __env;
      
      extern char _estack;  // see ld file
      extern char _Min_Stack_Size;  // see ld file
      
      /* Functions */
      void initialise_monitor_handles()
      {
      }
      
      int _getpid(void)
      {
          return 1;
      }
      
      int _kill(int pid, int sig)
      {
          errno = EINVAL;
          return -1;
      }
      
      void _exit (int status)
      {
          _kill(status, -1);
          while (1) {}        /* Make sure we hang here */
      }
      
      __attribute__((weak)) int _read(int file, char *ptr, int len)
      {
          int DataIdx;
      
          for (DataIdx = 0; DataIdx < len; DataIdx++)
          {
              *ptr++ = __io_getchar();
          }
      
          return len;
      }
      
      __attribute__((weak)) int _write(int file, char *ptr, int len)
      {
          int DataIdx;
      
          for (DataIdx = 0; DataIdx < len; DataIdx++)
          {
              __io_putchar(*ptr++);
          }
          return len;
      }
      
      caddr_t _sbrk(int incr) {
          extern char __heap_start__ asm("end");  // Defined by the linker.
          static char *heap_end;
          char *prev_heap_end;
      
          if (heap_end == NULL) heap_end = &__heap_start__;
      
          prev_heap_end = heap_end;
      
          if (heap_end + incr > &_estack - _Min_Stack_Size) {
              __asm("BKPT #0\n");
              errno = ENOMEM;
              return (caddr_t)-1;
      
          }
      
          heap_end += incr;
          return (caddr_t)prev_heap_end;
      
      }
      
      int _close(int file)
      {
          return -1;
      }
      
      
      int _fstat(int file, struct stat *st)
      {
          st->st_mode = S_IFCHR;
          return 0;
      }
      
      int _isatty(int file)
      {
          return 1;
      }
      
      int _lseek(int file, int ptr, int dir)
      {
          return 0;
      }
      
      int _open(char *path, int flags, ...)
      {
          /* Pretend like we always fail */
          return -1;
      }
      
      int _wait(int *status)
      {
          errno = ECHILD;
          return -1;
      }
      
      int _unlink(char *name)
      {
          errno = ENOENT;
          return -1;
      }
      
      int _times(struct tms *buf)
      {
          return -1;
      }
      
      int _stat(char *file, struct stat *st)
      {
          st->st_mode = S_IFCHR;
          return 0;
      }
      
      int _link(char *old, char *new)
      {
          errno = EMLINK;
          return -1;
      }
      
      int _fork(void)
      {
          errno = EAGAIN;
          return -1;
      }
      
      int _execve(char *name, char **argv, char **env)
      {
          errno = ENOMEM;
          return -1;
      }
      

      再编辑makefile文件,加入syscalls.c文件路径
      在
      ######################################
      source
      ######################################
      C sources
      下加入

      Core/Src/syscalls.c \
      

      最后编译即可
      d9eb7e8d-7054-4d69-b8fe-439ddea7f4bf-image.png
      最后下载固件到开发板上即可。

      3.运行!

      1.将agent功能包上传到linux中(linux需安装ros2系统,版本为foxy)
      2.如果为其他ros2版本,可根据官网的教程进行创建功能包(可能构建工程也需要点魔法,但网络要求不高,或者使用小鱼的docker agent也可以 效果一样)
      3.最好编译一下,source功能包后运行以下命令

      ros2 run micro_ros_agent micro_ros_agent serial -b 115200 --dev /dev/ttyUSB0
      

      其中ttyUSB0要根据开发板连接的代号来修改
      注意 一定要先运行上述命令后再连接开发板
      效果如下
      7280f180-0d83-4401-8c76-a919c43d0d08-image.png
      出现以上情况后,再连接开发板,会出现以下情况
      dc0ea371-5151-481e-9885-7847a3c42c15-image.png
      说明开发板与ros2连接成功
      再运行`

      ros2 topic echo /cubemx_publisher
      

      可查看microros发布的信息
      如图

      rqt_graph
      

      4669123c-ec21-446d-89c2-07e9878294b4-image.png
      说明stm32f103的节点已启动!

      24758873092 小鱼小 P 3 条回复 最后回复 回复 引用 2
      • 24758873092
        量子之海里的卡夫卡 @2475887309
        最后由 编辑

        @2475887309 rqt_graph 图如下
        53bb0dbe-18a9-4a0b-a9cc-32f1b2745bb3-image.png

        1 条回复 最后回复 回复 引用 0
        • 小鱼小
          小鱼 技术大佬 @2475887309
          最后由 编辑

          @2475887309 申请转载到公众号,可以嘛

          新书配套视频:https://www.bilibili.com/video/BV1GW42197Ck/

          24758873092 1 条回复 最后回复 回复 引用 0
          • 24758873092
            量子之海里的卡夫卡 @小鱼
            最后由 编辑

            @小鱼 可以呀,完全可以的呀

            小鱼小 1 条回复 最后回复 回复 引用 0
            • 小鱼小
              小鱼 技术大佬 @2475887309
              最后由 编辑

              @2475887309 感谢~

              新书配套视频:https://www.bilibili.com/video/BV1GW42197Ck/

              1 条回复 最后回复 回复 引用 0
              • 1
                某不知名的热心南京大学牲
                最后由 编辑

                我是在cubeide里弄的stm32f407,但是我在将单片机与ubuntu连接后,应该是在这第194行出现了问题
                e25e54a2-1050-4237-844b-7c23e4209d68-image.png
                运行agent后将单片机进行复位,此时我电脑上的agent直接报错退出了
                73ffe99d-08c8-43f9-9c7c-b10b77ce785e-8__X7Y6}N_TMJCAQ1DJ94{L_tmb.jpg
                这个不知道是啥问题,我查看了电脑上的串口收到的信息是这样的
                9460d253-4fb6-48d6-b5d3-ca58ff1a93b4-25a8d313e54b69b0a4af5baec63553ea.JPG
                我不知道如何解决

                24758873092 1 条回复 最后回复 回复 引用 0
                • Y
                  yunze
                  最后由 编辑

                  我在stm32 405rgt6上编译,cmake可以通过,但是编译报错。有大佬可以帮我看看 问题在哪里吗?
                  c425880f-4c3e-49e2-8c06-efa9de852ca5-image.png

                  24758873092 2 条回复 最后回复 回复 引用 0
                  • 24758873092
                    量子之海里的卡夫卡 @yunze
                    最后由 编辑

                    此回复已被删除!
                    1 条回复 最后回复 回复 引用 0
                    • 24758873092
                      量子之海里的卡夫卡 @1414417391
                      最后由 编辑

                      @1414417391 你好,这个可以先尝试一下断开与stm32的链接,最好是单片机断电,断电后再Linux启动命令,然后再启动stm32

                      1 条回复 最后回复 回复 引用 0
                      • 24758873092
                        量子之海里的卡夫卡 @yunze
                        最后由 编辑

                        @yunze 不好意思,好久没看论坛了,现在您应该是解决了,我现在回复,可以给后续出现相同问题的朋友有点启发
                        问题其实是f4系列是有硬件浮点的功能,而这个工程用的是软件的,所以要在makefile里面讲硬件浮点修改为软件浮点,具体如何设置可以搜索一下f4芯片使用freertos时使用软件浮点

                        1 条回复 最后回复 回复 引用 0
                        • P
                          pow
                          最后由 编辑

                          此回复已被删除!
                          1 条回复 最后回复 回复 引用 0
                          • P
                            法蘭克葉 @2475887309
                            最后由 编辑

                            @2475887309

                            我目前是用ROS2的Subscriber 範例去抓cubemx_publisher丟出來的資料,在程序中將預設topic改為cubemx_publisher,然後執行ros2 run 文件夾名 listener 後畫面停住沒反應.

                            請問該如何用Subscriber 去抓cubemx_publisher丟出來的資料?

                            24758873092 1 条回复 最后回复 回复 引用 0
                            • 24758873092
                              量子之海里的卡夫卡 @pireergame
                              最后由 编辑

                              @pireergame 你好,首先我们得确定Linux端的ros使用topic echo 能够看到stm32发送的数据后说明通信已经没问题了,之后再看sub订阅的topic数据类型是否和stm32发送的数据类型是否一致,一般来说这两个都没问题的话已经可以实现订阅了,若是还不行最好就是自己写一个简单的sub订阅者来订阅消息,若是都尝试过的话,请告诉我,我去测试一下(我这边已经可以使用cmdvel话题来进行控制小车运动以及订阅小车反馈的车轮速度)

                              1 条回复 最后回复 回复 引用 0
                              • L
                                lupop
                                最后由 编辑

                                大佬,你stm32rct6,这一套ram占用多少啊?我这用CLion + MX 生成 cmake 项目,一样的配置,占用有 90+%。。。

                                24758873092 1 条回复 最后回复 回复 引用 0
                                • 24758873092
                                  量子之海里的卡夫卡 @lupop
                                  最后由 编辑

                                  @lupop 没仔细看过,不过配置一样的话应该就差不多,毕竟任务占用大小已经设置10kb了,怎么也小不了🤣

                                  1 条回复 最后回复 回复 引用 0
                                  • 第一个帖子
                                    最后一个帖子
                                  皖ICP备16016415号-7
                                  Powered by NodeBB | 鱼香ROS