미리 준비해야하는 것
- ROS2
- Docker
- CubeMX
마이크로로스는 지원보드가 있습니다.
https://micro.ros.org/docs/overview/hardware/
Supported Hardware
ROS 2 for microcontrollers
micro.ros.org
지원하는 ST사 누클레오로는
- F446ZE
- F746ZG
- H743ZI
이렇게 3개만 오피셜하게 커뮤니티가 지원한다고 기술되어있습니다.
하지만, 제가 소유하고 있는 누클레오 보드는 STM32F429ZI 입니다.
그래서 이걸로 마이크로로스를 탑재해보려고 합니다.
https://www.devicemart.co.kr/goods/view?no=1361243
NUCLEO-F429ZI
고성능 MCU STM32 F4 32비트 Cortex™-M4 마이크로컨트롤러(MCU)는STM32 F2 시리즈와 호환되는 핀투핀 및 소프트웨어이자 성능과 DSP 기능이 더 우수하고 SRAM이 추가되어있습니다.
www.devicemart.co.kr
저는 CubeMX를 사용합니다. 여기서 코드를 생성하고 Clion에서 작업을 하기 때문입니다.
보드를 선택합니다. 저는 F429ZI로 진행할거에요.
카테고리에서 System Core -> RCC의 HSE 값을 Crystal/Ceramic Resonator로 선택합니다.
FreeRTOS를 사용할 것이기 때문에, SYS에서는 Timebase Source를 TIM1로 변경합니다.
그리고 FreeRTOS 사용을 위해 세팅해줍니다.
마이크로로스를 사용하기 위해서는 Task의 스택이 10,000 바이트 이상이여야합니다.
Tasks and Queues의 설정에서 default TASK의 stack Size를 1만 이상으로 높이기 위해 3000워드를 적어줍니다.
STM32는 1워드당 4바이트기 때문에 총 12000 바이트가 됩니다.
또한 Allocation은 Static으로 바꿔줍니다.
다음은 microROS를 통신할 포트를 설정합니다. 통신하고자 하는 포트를 선택하면 됩니다.
저는 UART2로 진행하겠습니다.
그후 DMA 세팅에서 Add를 눌러 USART2_RX와 USART2_TX를 추가하고
Priority를 Very HIGH로 세팅합니다.
RX는 Mode를 Circular로 해줍시다.
그리고 NVIC Settings에 들어가 USART2 global interrupt를 Enabled해줍니다.
Projetc Manager에 들어가 코드 관리를 편리하게 하기 위해 소스와 헤더파일을 분리하여 생성합니다.
Code Generator의 Generate peripheral initialization 을 체크합니다.
그리고 Project 카테고리에 들어가 Toolchain/IDE를 Makefile로 해줍니다. Clion IDE에서 작업할거기 때문이죠.
적당한 프로젝트 로케이션을 선택한 뒤, 코드를 생성해줍니다.
그 다음은 터미널로 해당 파일로 들어가
git clone https://github.com/micro-ROS/micro_ros_stm32cubemx_utils.git
클론 해줍니다.
클론 후 ROS2 버전에 맞는 브랜치로 변경해주세요.
저는 Humble을 사용하므로 그대로 진행합니다.
그 후 Makefile에서 build the application 주석 다음에 다음 내용을 추가해줍니다
#######################################
# 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)
그 다음, Makefile 등 여러 파일들이 있는 자리에서 다음 명령어로 도커를 이용해 마이크로로스 라이브러리를 생성해주어야합니다.
sudo docker pull microros/micro_ros_static_library_builder:humble
sudo docker run -it --rm -v $(pwd):/project --env MICROROS_LIBRARY_FOLDER=micro_ros_stm32cubemx_utils/microros_static_library microros/micro_ros_static_library_builder:humble
이때 로스 정적 라이브러리 빌더 뒤에 본인의 ROS2 버전을 입력합니다.저는 humble 버전이므로 위와같이 입력하였습니다.
진행하다보면
'rcutils' exports library 'dl' which couldn't be found
라는 에러가 뜨는데 이는 무시해도 됩니다.
설치가 다되면, IDE로 들어가 freertos.c안에 헤더파일로
#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 *) &huart2,
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 */
}
로 변경해줍니다.
그 후 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.
**
*****************************************************************************
*/
/* 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에서 C_SOURCES = 아래에 syscalls.c를 추가해줍니다.
그리고 빌드하고 업로드해줍니다.
여기까지가 STM 보드에 마이크로로스를 넣는 과정입니다.
이 다음부터는 마이크로로스 에이전트를 통해 임베디드 보드와 통신하는 부분입니다.
별도의 워크스페이스 파일을 만들어서 진행합니다.
source /opt/ros/$ROS_DISTRO/setup.bash
# Create a workspace and download the micro-ROS tools
mkdir microros_ws
cd microros_ws
git clone -b $ROS_DISTRO https://github.com/micro-ROS/micro_ros_setup.git src/micro_ros_setup
# Update dependencies using rosdep
sudo apt update && rosdep update
rosdep install --from-paths src --ignore-src -y
# Install pip
sudo apt-get install python3-pip
# Build micro-ROS tools and source them
colcon build
source install/local_setup.bash
이후 에이전트 패키지들을 빌드합니다.
ros2 run micro_ros_setup create_agent_ws.sh
ros2 run micro_ros_setup build_agent.sh
source install/local_setup.sh
이 이후 STM 누클레오 보드에 세팅한 UART핀에 UART2USB를 이용하여 PC에 연결합니다.
마지막으로 micro-ros-agent를 실행합니다.
ros2 run micro_ros_agent micro_ros_agent serial -b <버드레이트> --dev <디바이스포트>
저는 버드레이트를 115200으로 설정하였고 /dev/ttyUSB0에 연결되어있으므로
ros2 run micro_ros_agent micro_ros_agent serial -b 115200 --dev /dev/ttyUSB0
라 칩니다.
그 후
ros2 topic echo /cubemx_publisher
명령어로 DATA가 나오는 것을 확인하면 됩니다.
참고)
https://github.com/micro-ROS/micro_ros_stm32cubemx_utils
GitHub - micro-ROS/micro_ros_stm32cubemx_utils: A set of utilities for integrating micro-ROS in a STM32CubeMX project
A set of utilities for integrating micro-ROS in a STM32CubeMX project - GitHub - micro-ROS/micro_ros_stm32cubemx_utils: A set of utilities for integrating micro-ROS in a STM32CubeMX project
github.com
https://github.com/lFatality/stm32_micro_ros_setup
GitHub - lFatality/stm32_micro_ros_setup: Example of how to setup micro-ROS on any STM32 microcontroller
Example of how to setup micro-ROS on any STM32 microcontroller - GitHub - lFatality/stm32_micro_ros_setup: Example of how to setup micro-ROS on any STM32 microcontroller
github.com
'Robot > ROS' 카테고리의 다른 글
[ROS2] 치트시트 (0) | 2022.08.14 |
---|---|
[MicroROS] 참고할만한 자료들 (0) | 2022.08.06 |
[MicroROS] Publisher와 Subscriber 생성하기 (2) | 2022.08.06 |
[MicroROS] MicroROS + Arduino DUE 튜토리얼 (9) | 2022.07.24 |
[ROS2] zsh 자동완성 이슈 (0) | 2022.06.28 |
댓글