小鱼 ROS 2 新书上线!点击链接查看, 新书配套视频点击链接查看。
提问前必看的发帖注意事项—— 提问前必看!不符合要求的问题拒绝回答!!
社区使用指南—如何添加标签修改密码
stm32系列MICROROS环境配置(使用cubemx+clion+freertos)
-
前言
`本文主要分享一下如何快速在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文件如何编程microros2.移植到自己的工程
打开stm32 cubemx,创建一个新的工程 (我以stm32f103rct6为例)
1.正常配置时钟树即可
2.rcc配置 HSE选择crystal
选择中间件 middleware,选择freertos。如下配置 选择v2版本 选择tasks and queues双击已存在的tasks
配置 stack size 3000 allocation static
3.配置usart:
mode 选择asynchronous
dma settings 点击add
rx mode选择circular
tx默认即可
两者优先级都为非常高
nvic settings 打开中断
配置图如下
!
4.sys配置
打开debug
timebase source 选择tim1
5.工程管理:
toolchan/ide 选择makefile
6.代码生成:
生成代码 打开工程所在位置,将百度网盘下载的工程中的micro_ros_stm32cubemx_utils文件夹复制到新生成的工程文件夹目录下
如图所示
使用你最常用的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)
最好按照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>© 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 \
最后编译即可
最后下载固件到开发板上即可。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要根据开发板连接的代号来修改
注意 一定要先运行上述命令后再连接开发板
效果如下
出现以上情况后,再连接开发板,会出现以下情况
说明开发板与ros2连接成功
再运行`ros2 topic echo /cubemx_publisher
可查看microros发布的信息
如图rqt_graph
说明stm32f103的节点已启动! -
@2475887309 rqt_graph 图如下
-
@2475887309 申请转载到公众号,可以嘛
-
@小鱼 可以呀,完全可以的呀
-
@2475887309 感谢~
-
我是在cubeide里弄的stm32f407,但是我在将单片机与ubuntu连接后,应该是在这第194行出现了问题
运行agent后将单片机进行复位,此时我电脑上的agent直接报错退出了
这个不知道是啥问题,我查看了电脑上的串口收到的信息是这样的
我不知道如何解决 -
我在stm32 405rgt6上编译,cmake可以通过,但是编译报错。有大佬可以帮我看看 问题在哪里吗?