microros的构建比roserial更加折磨人,首先不同于rosserial的库可以通过rossserial功能包直接在ROS环境中编译出来就能用。microros官方虽然提供了micro_ros_setup构建功能包和用于cubemx项目的框架micro_ros_stm32cubemx_utils,在这些仓库下官方提供了对应的构建方法,但是针对国内网络环境,官方提供的容器和其构建方法不能直接用。我这里针对官方提供的docker构建方法做一个补充,并添加一个非docker本地构建的方法,干货满满
前期准备
无论是哪种方法构建microros,都需要科学上网方法,提前准备科学上网工具
将micro_ros_stm32cubemx_utils克隆到你的stm32项目文件夹中(cubemx/cubeide),将micro_ros_setup克隆到ros工作空间并编译和补全rosdep依赖(micro_ros_setup不仅用来生成microros库,而且用来做节点与stm32通信,如果你用docker方式生成microros库,那就仅用来通信),克隆后注意签出分支,以humble为例:git checkout humble
一、docker构建microros
1.docker容器
本质上microros的构建还是在ros环境中,而stm32的工作环境一般是在windows,docker工具可以让我们在stm32环境中利用linux容器快速构建一个本地microros
既然是要用docker,需要先安装docker并做配置,这里提供对windows和ubuntu22两个平台的安装和配置方法
1)Windows
windows端docker可以用官方提供的一个名叫docker desktop的桌面工具,去官网下载安装就可以,并且一定要注册登录,由于docker的源在国外,这里需要添加一些国内的镜像源
{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"experimental": false,
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com",
"https://your_preferred_mirror",
"https://dockerhub.icu",
"https://docker.registry.cyou",
"https://docker-cf.registry.cyou",
"https://dockercf.jsdelivr.fyi",
"https://docker.jsdelivr.fyi",
"https://dockertest.jsdelivr.fyi",
"https://mirror.aliyuncs.com",
"https://dockerproxy.com",
"https://mirror.baidubce.com",
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn",
"https://docker.mirrors.sjtug.sjtu.edu.cn",
"https://docker.mirrors.ustc.edu.cn",
"https://mirror.iscas.ac.cn",
"https://docker.rainbond.cc"
]
}
为避免在后面使用docker的过程中,出现访问问题,在docker desktop中设置软件代理,这里跟你的代理软件保持一样的端口即可
另外这里的builder需要是linux,不是就切换一下
2)Ubuntu22(既然是在ubuntu上了,我更推荐不用docker的方法)
Ubuntu22用apt安装docker
sudo apt install docker.io
Ubuntu端同样设置代理,代理内容同windows
sudo nano /etc/docker/daemon.json
Ubuntu端同样需要登录
docker login
2.构建microros容器
终端输入,拉取镜像,这里拉取镜像出现登录问题时,在终端输入docker login登录解决
docker pull microros/micro_ros_static_library_builder:humble
配置容器,这里 C:\Users\hrilug\OneDrive\stm32\HAL\f405_microros_ide是你的项目文件夹, -e参数用来配置代理服务器,注意代理地址不是127.0.0.1,而是代理主机在局域网中的地址,docker的网络环境与主机隔离,同时在地理软件中开启局域网代理
对于MICROROS_LIBRARY_FOLDER=micro_ros_stm32cubemx_utils/microros_static_library_ide,根据你所使用的cube工具,如果你是cubemx,改成micro_ros_stm32cubemx_utils/microros_static_library
docker run -it -v C:\Users\hrilug\OneDrive\stm32\HAL\f405_microros_ide:/project \
--env MICROROS_LIBRARY_FOLDER=micro_ros_stm32cubemx_utils/microros_static_library_ide \
-e http_proxy=http://192.168.110.230:7897 -e https_proxy=http://192.168.110.230:7897 \
--name=microros --entrypoint /bin/bash \
microros/micro_ros_static_library_builder:humble
这一段运行后会直接进入容器内,在容器内也需要做一定配置,首先,更换一下软件源
sudo apt update
sudo apt install nano
sudo nano /etc/apt/sources.list
//用清华源,先注释掉原本内容,写入如下内容:
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
sudo apt update
论坛上提到容器中的git需要自己编译一个,这里贴出来,一样全部执行一遍即可
wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.17.0.tar.gz
sudo apt install autoconf build-essential tcl-dev libssl-dev libcurl4-openssl-dev gettext
tar -xf git-2.17.0.tar.gz
cd git-2.17.0
make configure
./configure --prefix=/usr/
make -j8
make install
最后生成库,生成的库会在你的项目文件夹下的micro_ros_stm32cubemx_utils\microros_static_library_ide\libmicroros,在cubeide内引入静态库
bash /project/$MICROROS_LIBRARY_FOLDER/library_generation/library_generation.sh
构建完成后可以exit退出容器,推出后容器会自动关闭,下一次需要进入容器配置,可以在docker desktop内启动和配置
3.关于可能的情况
1)[ros2run]: Process exited with failure 1/7
这一步其实就是网络问题,最可能的就是系统安装arm-gcc-none-eabi和git时出现的,正确配置代理环境,在代理软件中配置局域网代理
另外对于国内git环境经常性抽风的问题,参考这一篇开源仓库:https://github.com/maxiaof/github-hosts,修改hosts
#Github Hosts Start
#Update Time: 2025-03-08
#Project Address: https://github.com/maxiaof/github-hosts
#Update URL: https://raw.githubusercontent.com/maxiaof/github-hosts/master/hosts
140.82.112.26 alive.github.com
140.82.114.25 live.github.com
185.199.108.154 github.githubassets.com
140.82.113.22 central.github.com
185.199.111.133 desktop.githubusercontent.com
185.199.108.133 camo.githubusercontent.com
185.199.110.133 github.map.fastly.net
146.75.121.194 github.global.ssl.fastly.net
140.82.121.3 gist.github.com
185.199.109.153 github.io
140.82.121.3 github.com
192.0.66.2 github.blog
140.82.121.6 api.github.com
185.199.110.133 raw.githubusercontent.com
185.199.109.133 user-images.githubusercontent.com
185.199.111.133 favicons.githubusercontent.com
185.199.108.133 avatars5.githubusercontent.com
185.199.109.133 avatars4.githubusercontent.com
185.199.108.133 avatars3.githubusercontent.com
185.199.110.133 avatars2.githubusercontent.com
185.199.110.133 avatars1.githubusercontent.com
185.199.109.133 avatars0.githubusercontent.com
185.199.109.133 avatars.githubusercontent.com
140.82.121.9 codeload.github.com
54.231.137.17 github-cloud.s3.amazonaws.com
54.231.137.17 github-com.s3.amazonaws.com
52.217.231.9 github-production-release-asset-2e65be.s3.amazonaws.com
3.5.0.2 github-production-user-asset-6210df.s3.amazonaws.com
52.217.169.161 github-production-repository-file-5c1aeb.s3.amazonaws.com
185.199.111.153 githubstatus.com
140.82.113.18 github.community
51.137.3.17 github.dev
140.82.113.22 collector.github.com
13.107.42.16 pipelines.actions.githubusercontent.com
185.199.110.133 media.githubusercontent.com
185.199.110.133 cloud.githubusercontent.com
185.199.111.133 objects.githubusercontent.com
#Github Hosts End
刷新网络环境
sudo systemctl restart systemd-resolved
或者利用代理工具代理github
git config --global http.proxy http://127.0.0.1:7897
git config --global https.proxy https://127.0.0.1:7897
2)Firmware already created
micro_ros_setup每一次构建库都会在容器内的/uros_ws目录下创建一个firmware文件夹,需要删除这个文件夹才能够重新构建
二、非docker的ROS2环境构建microros
使用docker工具本质上还是利用ROS2环境构建microros,那我们可以直接在ROS2环境中构建microros,而不是docker环境。这里要做的代理配置大致跟第一种方法一致,需要配置代理工具,并配置hosts
首先一样将micro_ros_setup克隆到ros工作空间下编译,将micro_ros_stm32cubemx_utils克隆到任意位置用于生成microros库
根据你所需要的开发环境修改library_generation.sh文件,以cubeide为例,则这个文件在micro_ros_stm32cubemx_utils/microros_static_library_ide/library_generation,具体修改内容如下:
- 修改第4行:
export BASE_PATH=/home/hrilug/Desktop/micro_ros_stm32cubemx_utils/microros_static_library_ide
- 第5行增加:
export MICROROS_LIBRARY_FOLDER=/home/hrilug/Desktop/micro_ros_stm32cubemx_utils/microros_static_library_ide export ROS_DISTRO=humble
这里的BASE_PATH和MICROROS_LIBRARY_FOLDER都是你micro_ros_stm32cubemx_utils下的路
- 修改第13行:
export RET_CFLAGS=$(find /project -type f -name *.mk -exec cat {} \; | python3 $BASE_PATH/library_generation /extract_flags.py)
将其中/project改成你的stm32工程的路径,会通过python爬取你使用的单片机配置
三、使用CubeIDE构建microros项目
1)CubeIDE配置
我这里用的单片机是stm32f405rgt6,使用usart与ROS2通信,我也尝试过使用usbcdc,但未成功,我的单片机配置如下:
使能USART1并打开中断
使能一个LED引脚用于监控状态
使用FreeRTOS cmsis v2,这个是必要的,将默认task大小改为3000,并设置为静态内存
由于使用了FreeRTOS,在sys中将系统时钟改成TIM1
在Project Manager页打开Generate peripheral initialization as a pair of '.c/.h' file
a.配置microros静态库,这个官方也有教程,你可以看到我左边的项目目录的文件,将这些必要文件复制进项目中
b.配置include头文件
c.配置source源文件
我这里给一个官方改的freertos.c文件的案例:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#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>
/* USER CODE BEGIN Header */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
typedef StaticTask_t osStaticThreadDef_t;
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
extern UART_HandleTypeDef huart1;
/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
uint32_t defaultTaskBuffer[ 3000 ];
osStaticThreadDef_t defaultTaskControlBlock;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.cb_mem = &defaultTaskControlBlock,
.cb_size = sizeof(defaultTaskControlBlock),
.stack_mem = &defaultTaskBuffer[0],
.stack_size = sizeof(defaultTaskBuffer),
.priority = (osPriority_t) osPriorityNormal,
};
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
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);
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/**
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
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__);
HAL_GPIO_TogglePin(GPIOA,LED_Pin);
}
msg.data++;
if(msg.data == 99999)
msg.data=0;
osDelay(1);
}
/* USER CODE END StartDefaultTask */
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
/* USER CODE END Application */
2)配置micro_ros_agent
这个配置在官方仓库下很清楚了:
ros2 run micro_ros_setup create_agent_ws.sh
ros2 run micro_ros_setup build_agent.sh
source install/local_setup.sh
将单片机连上端口,赋权限,然后启动agent
sudo chmod 777 /dev/ttyACM0
ros2 run micro_ros_agent micro_ros_agent serial -b 115200 --dev /dev/ttyACM0
启动玩agent后,需要复位一下单片机然后就能看到topic了