Hanjie's Blog

一只有理想的羊驼


  • Home

  • Categories

  • Archives

  • Tags

  • About

  • Search
close

通过串口实现SystemView分析FreeRTOS v10.3.1

Posted on 2021-03-22   |   In Tech , Hardware   |  

准备文件

我的STM32项目是经由STM32CubeMX v6.2.0生成的Makefile项目;使用VS-Code-STM32-IDE来生成VS Code项目;使用v10.3.1的FreeRTOS系统;芯片为STM32F407VE;使用USART1作为输出端口,开启DMA传输。

下载SystemView v3.30版本。在安装目录内的SystemView_V330\Src文件夹中,将SEGGER和Sample内的相关文件复制到STM32项目的文件夹STM32_Project\Middlewares\Third_Party\SystemView中[1][2]。注意根据项目的需求,选择复制Sample中对应的系统和SEGGER\Syscalls中对应的编译器:

SystemView
├── Config
│ ├── Global.h
│ ├── SEGGER_RTT_Conf.h
│ ├── SEGGER_SYSVIEW_Conf.h
│ └── SEGGER_SYSVIEW_Config_FreeRTOS.c
└── SEGGER
├── SEGGER.h
├── SEGGER_RTT.c
├── SEGGER_RTT.h
├── SEGGER_RTT_ASM_ARMv7M.s
├── SEGGER_RTT_printf.c
├── SEGGER_SYSVIEW.c
├── SEGGER_SYSVIEW.h
├── SEGGER_SYSVIEW_ConfDefaults.h
├── SEGGER_SYSVIEW_FreeRTOS.c
├── SEGGER_SYSVIEW_FreeRTOS.h
├── SEGGER_SYSVIEW_Int.h
└── Syscalls
└── SEGGER_RTT_Syscalls_GCC.c

注意:SEGGER_RTT_ASM_ARMv7M.S需要改名为SEGGER_RTT_ASM_ARMv7M.s,不然后面编译时会出现No rule to make target错误。

FreeRTOS补丁

SystemView提供了针对FreeRTOS v10.0.0版本的补丁FreeRTOSV10_Core.patch,但不适合新的版本。这里我们提供了针对FreeRTOS v10.3.1版本的补丁。复制下面内容,并且保存名为patch的文件,并且放到STM32_Project\Middlewares\Third_Party,内含FreeRTOS的文件夹下:

diff -rupwN FreeRTOS/Source/include/FreeRTOS.h FreeRTOS_systemview/Source/include/FreeRTOS.h
--- FreeRTOS/Source/include/FreeRTOS.h 2021-03-22 00:24:29.699956400 +0800
+++ FreeRTOS_systemview/Source/include/FreeRTOS.h 2021-03-14 03:13:48.049285300 +0800
@@ -160,10 +160,6 @@ extern "C" {
#define INCLUDE_uxTaskGetStackHighWaterMark2 0
#endif

-#ifndef INCLUDE_pxTaskGetStackStart
- #define INCLUDE_pxTaskGetStackStart 0
-#endif
-
#ifndef INCLUDE_eTaskGetState
#define INCLUDE_eTaskGetState 0
#endif
@@ -420,22 +416,6 @@ hold explicit before calling the code. *
#define tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
#endif

-#ifndef traceREADDED_TASK_TO_READY_STATE
- #define traceREADDED_TASK_TO_READY_STATE( pxTCB ) traceMOVED_TASK_TO_READY_STATE( pxTCB )
-#endif
-
-#ifndef traceMOVED_TASK_TO_DELAYED_LIST
- #define traceMOVED_TASK_TO_DELAYED_LIST()
-#endif
-
-#ifndef traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST
- #define traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST()
-#endif
-
-#ifndef traceMOVED_TASK_TO_SUSPENDED_LIST
- #define traceMOVED_TASK_TO_SUSPENDED_LIST( pxTCB )
-#endif
-
#ifndef traceQUEUE_CREATE
#define traceQUEUE_CREATE( pxNewQueue )
#endif
@@ -680,18 +660,6 @@ hold explicit before calling the code. *
#define traceTASK_NOTIFY_GIVE_FROM_ISR()
#endif

-#ifndef traceISR_EXIT_TO_SCHEDULER
- #define traceISR_EXIT_TO_SCHEDULER()
-#endif
-
-#ifndef traceISR_EXIT
- #define traceISR_EXIT()
-#endif
-
-#ifndef traceISR_ENTER
- #define traceISR_ENTER()
-#endif
-
#ifndef traceSTREAM_BUFFER_CREATE_FAILED
#define traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer )
#endif
diff -rupwN FreeRTOS/Source/include/task.h FreeRTOS_systemview/Source/include/task.h
--- FreeRTOS/Source/include/task.h 2021-03-22 00:27:05.253377700 +0800
+++ FreeRTOS_systemview/Source/include/task.h 2021-03-14 03:13:48.062250900 +0800
@@ -1468,25 +1468,6 @@ UBaseType_t uxTaskGetStackHighWaterMark(
*/
configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) PRIVILEGED_FUNCTION;

-/**
- * task.h
- * <PRE>uint8_t* pxTaskGetStackStart( TaskHandle_t xTask);</PRE>
- *
- * INCLUDE_pxTaskGetStackStart must be set to 1 in FreeRTOSConfig.h for
- * this function to be available.
- *
- * Returns the start of the stack associated with xTask. That is,
- * the highest stack memory address on architectures where the stack grows down
- * from high memory, and the lowest memory address on architectures where the
- * stack grows up from low memory.
- *
- * @param xTask Handle of the task associated with the stack returned.
- * Set xTask to NULL to return the stack of the calling task.
- *
- * @return A pointer to the start of the stack.
- */

-uint8_t* pxTaskGetStackStart( TaskHandle_t xTask) PRIVILEGED_FUNCTION;
-
/* When using trace macros it is sometimes necessary to include task.h before
FreeRTOS.h. When this is done TaskHookFunction_t will not yet have been defined,
so the following two prototypes will cause a compilation error. This can be
diff -rupwN FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c FreeRTOS_systemview/Source/portable/GCC/ARM_CM4F/port.c
--- FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c 2021-03-22 00:29:26.581083000 +0800
+++ FreeRTOS_systemview/Source/portable/GCC/ARM_CM4F/port.c 2021-03-14 03:13:48.114111800 +0800
@@ -492,20 +492,14 @@ void xPortSysTickHandler( void )
save and then restore the interrupt mask value as its value is already
known. */

portDISABLE_INTERRUPTS();
- traceISR_ENTER();
{
/* Increment the RTOS tick. */
if( xTaskIncrementTick() != pdFALSE )
{
- traceISR_EXIT_TO_SCHEDULER();
/* A context switch is required. Context switching is performed in
the PendSV interrupt. Pend the PendSV interrupt. */

portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
- else
- {
- traceISR_EXIT();
- }
}
portENABLE_INTERRUPTS();
}
diff -rupwN FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h FreeRTOS_systemview/Source/portable/GCC/ARM_CM4F/portmacro.h
--- FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h 2021-03-22 00:30:13.377937500 +0800
+++ FreeRTOS_systemview/Source/portable/GCC/ARM_CM4F/portmacro.h 2021-03-14 03:13:48.115109400 +0800
@@ -89,7 +89,7 @@ typedef unsigned long UBaseType_t;

#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) )
#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL )
-#define portEND_SWITCHING_ISR( xSwitchRequired ) { if( xSwitchRequired != pdFALSE ) { traceISR_EXIT_TO_SCHEDULER(); portYIELD(); } else { traceISR_EXIT(); } }
+#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
/*-----------------------------------------------------------*/

diff -rupwN FreeRTOS/Source/tasks.c FreeRTOS_systemview/Source/tasks.c
--- FreeRTOS/Source/tasks.c 2021-03-22 00:48:55.028709000 +0800
+++ FreeRTOS_systemview/Source/tasks.c 2021-03-14 03:13:48.213845800 +0800
@@ -220,16 +220,6 @@ count overflows. */
taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
-/*
- * Place the task represented by pxTCB which has been in a ready list before
- * into the appropriate ready list for the task.
- * It is inserted at the end of the list.
- */
-#define prvReaddTaskToReadyList( pxTCB ) \
- traceREADDED_TASK_TO_READY_STATE( pxTCB ); \
- taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
- vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
- tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
/*-----------------------------------------------------------*/

/*
@@ -1682,7 +1672,7 @@ static void prvAddNewTaskToReadyList( TC
{
mtCOVERAGE_TEST_MARKER();
}
- prvReaddTaskToReadyList( pxTCB );
+ prvAddTaskToReadyList( pxTCB );
}
else
{
@@ -1744,7 +1734,6 @@ static void prvAddNewTaskToReadyList( TC
mtCOVERAGE_TEST_MARKER();
}

- traceMOVED_TASK_TO_SUSPENDED_LIST(pxTCB);
vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );

#if( configUSE_TASK_NOTIFICATIONS == 1 )
@@ -3893,20 +3882,6 @@ static void prvCheckTasksWaitingTerminat
#endif /* INCLUDE_uxTaskGetStackHighWaterMark */
/*-----------------------------------------------------------*/

-#if (INCLUDE_pxTaskGetStackStart == 1)
- uint8_t* pxTaskGetStackStart( TaskHandle_t xTask)
- {
- TCB_t *pxTCB;
- UBaseType_t uxReturn;
- (void)uxReturn;
-
- pxTCB = prvGetTCBFromHandle( xTask );
- return ( uint8_t * ) pxTCB->pxStack;
- }
-
-#endif /* INCLUDE_pxTaskGetStackStart */
-/*-----------------------------------------------------------*/
-
#if ( INCLUDE_vTaskDelete == 1 )

static void prvDeleteTCB( TCB_t *pxTCB )
@@ -4081,7 +4056,7 @@ TCB_t *pxTCB;

/* Inherit the priority before being moved into the new list. */
pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
- prvReaddTaskToReadyList( pxMutexHolderTCB );
+ prvAddTaskToReadyList( pxMutexHolderTCB );
}
else
{
@@ -4171,7 +4146,7 @@ TCB_t *pxTCB;
any other purpose if this task is running, and it must be
running to give back the mutex. */
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
- prvReaddTaskToReadyList( pxTCB );
+ prvAddTaskToReadyList( pxTCB );

/* Return true to indicate that a context switch is required.
This is only actually required in the corner case whereby
@@ -5233,7 +5208,6 @@ const TickType_t xConstTickCount = xTick
/* Add the task to the suspended task list instead of a delayed task
list to ensure it is not woken by a timing event. It will block
indefinitely. */
- traceMOVED_TASK_TO_SUSPENDED_LIST(pxCurrentTCB);
vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
@@ -5250,14 +5224,12 @@ const TickType_t xConstTickCount = xTick
{
/* Wake time has overflowed. Place this item in the overflow
list. */
- traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST();
vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
/* The wake time has not overflowed, so the current block list
is used. */
- traceMOVED_TASK_TO_DELAYED_LIST();
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

/* If the task entering the blocked state was placed at the
@@ -5287,13 +5259,11 @@ const TickType_t xConstTickCount = xTick
if( xTimeToWake < xConstTickCount )
{
/* Wake time has overflowed. Place this item in the overflow list. */
- traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST();
vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
/* The wake time has not overflowed, so the current block list is used. */
- traceMOVED_TASK_TO_DELAYED_LIST();
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

/* If the task entering the blocked state was placed at the head of the

在命令行窗口中输入打补丁指令:

patch -p0 < patch

监听程序

在STM32_Project\Inc\FreeRTOSConfig.h下添加以下语句:

/* USER CODE BEGIN Defines */
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */

#define INCLUDE_xTaskGetIdleTaskHandle 1
#define INCLUDE_pxTaskGetStackStart 1

#include "SEGGER_SYSVIEW_FreeRTOS.h"
/* USER CODE END Defines */

新建文件STM32_Project\Modules\include\systemview_task.h和STM32_Project\Modules\src\systemview_task.c[3]:

#ifndef __SYSTEMVIEW_TASK_H
#define __SYSTEMVIEW_TASK_H

#include "FreeRTOS.h"

#include "config.h"
#include "utils.h"
#include "usart_com.h"

void SystemViewLaunch(void);

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);

#endif /* __SYSTEMVIEW_TASK_H */
#include "systemview_task.h"

#define SYSVIEW_SINGLE_TX 256

TaskHandle_t system_view_notify;

STATIC_MEM_TASK_ALLOC(systemview_task, SYSTEMVIEW_TASK_STACKSIZE);
static void SystemViewTask(void *param);

uint8_t hello_message[32] = {
'S', 'E', 'G', 'G', 'E', 'R', ' ',
'S', 'y', 's', 't', 'e', 'm', 'V', 'i', 'e', 'w',
' ', 'V', '0' + SEGGER_SYSVIEW_MAJOR,
'.', '0' + (SEGGER_SYSVIEW_MINOR / 10),
'0' + (SEGGER_SYSVIEW_MINOR % 10),
'.', '0' + (SEGGER_SYSVIEW_REV / 10),
'0' + (SEGGER_SYSVIEW_REV % 10),
'\0', 0, 0, 0, 0, 0};

void SystemViewLaunch(void) {

CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

SEGGER_SYSVIEW_Conf();

STATIC_MEM_TASK_CREATE(systemview_task, SystemViewTask, "SystemViewTask", NULL, SYSTEMVIEW_TASK_PRI);
}

void SystemViewTask(void *param) {
system_view_notify = xTaskGetCurrentTaskHandle();

// 获取Channel ID
int channel_id = SEGGER_SYSVIEW_GetChannelID();

//发送HELLO包
HAL_UART_Transmit_DMA(&huart1, hello_message, 32);

uint8_t rx_buf;
uint8_t tx_buf[SYSVIEW_SINGLE_TX];
uint32_t notify_flag;
bool dma_in_progress = true;
TickType_t prev_send;

//启动记录
SEGGER_SYSVIEW_Start();

while (1) {
if (xTaskNotifyWait(0x00, 0x03, &notify_flag, pdMS_TO_TICKS(400)) == pdTRUE) {
if (notify_flag & 0x01) {
SEGGER_RTT_WriteDownBufferNoLock(channel_id, &rx_buf, 0x01);
HAL_UART_Receive_IT(&huart1, &rx_buf, 0x01);
} else if (notify_flag & 0x02) {
if (dma_in_progress) {
dma_in_progress = false;
prev_send = xTaskGetTickCount();
}
}
}

if (dma_in_progress == false && xTaskGetTickCount() - prev_send >= pdMS_TO_TICKS(400)) {
unsigned int tx_length = SEGGER_RTT_GetBytesInBuffer(channel_id);
if (tx_length >= SYSVIEW_SINGLE_TX) {
uint32_t num = SEGGER_RTT_ReadUpBufferNoLock(channel_id, tx_buf, SYSVIEW_SINGLE_TX);
HAL_UART_Transmit_DMA(&huart1, tx_buf, num);
} else if (tx_length != 0) {
uint32_t num = SEGGER_RTT_ReadUpBufferNoLock(channel_id, tx_buf, tx_length);
HAL_UART_Transmit_DMA(&huart1, tx_buf, num);
}
dma_in_progress = true;
}

}
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
BaseType_t higher_woken = pdFALSE;
xTaskNotifyFromISR(system_view_notify, 0x01, eSetBits, &higher_woken);
portYIELD_FROM_ISR(higher_woken);
}
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
BaseType_t higher_woken = pdFALSE;
xTaskNotifyFromISR(system_view_notify, 0x02, eSetBits, &higher_woken);
portYIELD_FROM_ISR(higher_woken);
}
}

修改STM32_Project\Src\main.c函数:

...
#include "systemview_task.h"
...

...
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_TIM4_Init();
MX_USART2_UART_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */

SystemViewLaunch();

/* USER CODE END 2 */

/* Call init function for freertos objects (in freertos.c) */
MX_FREERTOS_Init();
/* Start scheduler */
osKernelStart();
...

修改STM32_Project\.vscode\c_cpp_properties.json,添加:

...
"____________________USER_FIELDS_CAN_BE_MODIFIED____________________": "",
"user_cSources": [
"Modules/src/usart_com.c",
"Modules/src/motor.c",
"Modules/src/mavlink_task.c",
"Modules/src/utils.c",
"Modules/src/mpu6050.c",
"Modules/src/ms5611.c",
"Modules/src/hmc5883l.c",
"Modules/src/sensors.c",
"Modules/src/i2c1.c",
"Modules/src/dmp.c",
"Modules/src/system_task.c",
"Modules/src/systemview_task.c",
"Middlewares/Third_Party/SystemView/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c",
"Middlewares/Third_Party/SystemView/SEGGER/SEGGER_RTT_printf.c",
"Middlewares/Third_Party/SystemView/SEGGER/SEGGER_RTT.c",
"Middlewares/Third_Party/SystemView/SEGGER/SEGGER_SYSVIEW_FreeRTOS.c",
"Middlewares/Third_Party/SystemView/SEGGER/SEGGER_SYSVIEW.c",
"Middlewares/Third_Party/SystemView/SEGGER/Syscalls/SEGGER_RTT_Syscalls_GCC.c"
],
"user_asmSources": [
"Middlewares/Third_Party/SystemView/SEGGER/SEGGER_RTT_ASM_ARMv7M.s"
],
"user_ldSources": [],
"user_cIncludes": [
"Modules/include",
"Middlewares/Third_Party/SystemView/Config",
"Middlewares/Third_Party/SystemView/SEGGER"
],
"user_asmIncludes": [],
"user_ldIncludes": [],
"user_cDefines": [],
"user_asmDefines": [],
"user_cFlags": [],
"user_asmFlags": [],
"user_ldFlags": [
"-u _printf_float"
],
...

编译并且烧录到单片机中。

SystemView

打开SystemView软件,打开Target->Recorder Configuration,选择UART并且填好相应的串口和波特率。

点击Start Recording,重启单片机,成功监控FreeRTOS:

system_vie

Github: https://github.com/HanjieLuo/drone


  1. https://github.com/nghiaphamsg/STM32F4_FreeRTOS/blob/master/002_SEGGER_Tools/README.md ↩

  2. https://www.segger.com/downloads/systemview/UM08027 ↩

  3. https://blog.imi.moe/systemview-freertos/ ↩

STM32 HAL_I2C_MemTxCpltCallback

Posted on 2021-03-18   |   In Tech , Hardware   |  

Recently, I updated the STM32Cube_FW_F4 library of my drone project from V1.25.0 to V1.26.0, which solved some I2C's problems. However, the MPU-6050 initialization program failed since the library was updated. I found that the HAL_I2C_MemTxCpltCallback was not triggered after executing the DEVICE_RESET instruction of PWR_MGMT_1 using HAL_I2C_Mem_Read_DMA:

mpu6050

uint8_t i2c_data = 0x80;
HAL_I2C_Mem_Read_DMA(&hi2c1, 0xD0, 0x6B, &data, 1);

A HAL_I2C_ERROR_AT error will be raised and the oscilloscope shows that the there is no acknowledge from the MPU-6050:

IMG_0122_smal

HAL_I2C_MemTxCpltCallback runs normally when the instruction is changed to others like wake up the sensor:

uint8_t i2c_data = 0x00;
HAL_I2C_Mem_Read_DMA(&hi2c1, 0xD0, 0x6B, &data, 1);

It seems that it is the problem of MPU6050 though the STM32Cube_FW_F4V 1.25.0 never happened. It is a considerable problem when you using the new version of STM32Cube_FW_F4V.

一种基于KLT的直线段匹配算法

Posted on 2021-02-04   |   In Tech , Vision   |  

罗汉杰. 直线段匹配方法、装置、存储介质及终端[P]. 中国专利: CN109919190A, 2019-06-21. Github: https://github.com/HanjieLuo/line_matching

背景

在计算机视觉系统中,其中一种重要任务是图像中的特征匹配。我们在多张图片中提取出一些特征纹理,并且将这些纹理匹配起来。在传统的方法中,我们一般使用梯度值大的,处于边角位置的点作为特征来进行匹配。但在实际使用中,图像中能够提取的,稳定的特征点是有限的。

我们发现,比起特征点,图像中的直线具有更强的稳定性和抗干扰能力;并且在室内等经典场景中,直线纹理出现的概率比特征点要多。

linematching1

而传统的基于描述子的直线匹配方法(如LBD[1],MSLD[2]等),采用直线的局部外观特征和几何约束特征,对每一根直线段进行数学建模,然后进行匹配。但这些方法运算量巨大,难以满足实时任务的需求;并且,匹配的成功率也一般。

对于相继拍摄的前后两张图片,可以观察到特征纹理的偏移量较小。基于这个前提,我们可以对直线段使用一些本用于特征点的,高效的匹配方法来实现直线匹配功能。

本文提出了一种图像直线段的匹配方法,它使用了高效的一维光流法特征点匹配方法,对直线段上的点进行匹配操作,然后对匹配点进行统计,从而实现直线段跟踪的功能。

方法

直线检测

假设已知有前后相继拍摄的两张图片$I_0$和$I_1$。使用LSD[3],EDLines[4][5]等直线检测算法,在图片$I_0$中获得直线段集合$L_0={^0l_i│^0l_i =(^0p_{i,0}, ^0p_{i,1}), 0≤i<M_0}$,$M_0$表示线段数目;$^0p_{i,0}$,$^0p_{i,1}$分别表示图$I_0$上第$i$根线段$^0l_i$的两个端点的二维坐标,$p=[px,py]^T$,$T$表示矩阵转置,$p$为一列向量。同样,我们图片$I_1$中获得直线段集合$L_1={^1l_i│^1l_i =(^1p_{k,0}, ^1p_{k,1}),0≤k<M_1}$。

linematching2

锚点匹配

对图$I_0$上的每一条线段$^0l_i ∈L_0$,$^0l_i =(^0p_{i,0}, ^0p_{i,1})$:

a. 计算线段的方向向量$^0a_i =[^0a_{i,0}, ^0a_{i,1}]^T = (^0p_{i,1} -^0p_{i,0})/‖^0p_{i,1}-^0p_{i,0}‖$和法向量$^0n_i =[-^0a_{i,1}, ^0a_{i,0}]^T$。

linematching3

b. 以$^0p_{i,0}$为起点,$^0p_{i,1}$为终点,$^0a_i$为方向,每隔距离$s$,选取一系列锚点,并将它们的坐标集合记作$^0Q_{i} ={^0q_{i,j}|0≤j<N_{i}}$,$N_i$为线段$^0l_{i}$的锚点数目,$s$我们一般设置为20。

linematching4

c. 使用一维光流匹配法[6],对图$I_0$线段$^0l_{i}$上每一个锚点$^0q_{i,j}$,在图$I_1$上,以$^0q_{i,j}$为起始位置,$^0n_{i}$为搜索方向,在图$I_1$上找到匹配点$^1q_{i,j}$。

linematching5

点-线匹配

经过上一个步骤后,我们在图$I_1$上找到图$I_0$上所有锚点$^0q_{i,j}$的匹配点$^1q_{i,j}$。在这一个步骤中,我们希望知道匹配点是属于图$I_1$上哪一条直线段的,我们称这一步骤为点-线匹配。

$^0q_{i,j}$为图$I_0$上的锚点,$^1q_{i,j}$为在图$I_1$上的相应匹配点;图$I_1$的直线段集合为$L_1$,$L_1={^1l_{k} │^1l_{k} =(^1p_{k,0}, ^1p_{k,1}), 0≤k<M_1 }$。

对于任意点$q$,到直线段$l=(p_0, p_1)$有最短距离$d$:

$$\begin{aligned} v &= p_1 - p_0 \newline u &= p_0 - q \newline t &= min⁡(max⁡(-(v^T u)/(v^T v),0),1) \newline d &= ‖tv+u‖ \end{aligned}$$

其中$v$,$u$,$t$为中间变量。对于每一个匹配点$^1q_{i,j}$,做:

计算到图$I_1$所有直线段$^1l_{k}$的最短距离,获的距离集合${d_k}$。比较${d_k}$,获得的最短距离$d_{min}=min⁡({d_k})$和所对应的直线段$^1l_{closest(i,j)}$,$0≤closest(i,j)<M_1$。如果$d_{min}<d_{threshold}$,则我们认为点$^1q_{i,j}$是属于直线$^1l_{closest(i,j)}$的。$d_{threshold}$为一经验阀值,我们设置为1。

通过上述步骤,我们获得了匹配点在图$I_1$上所对应的直线段。因此,每一个图$I_0$上的锚点,都有可能匹配到图$I_1$上的任一直线段。

线-线匹配

对于图$I_0$上的任意直线段$^0l_i$,$0≤i<M_0$,拥锚点$^0q_{i,j}$,$0≤j<N_i$;每一个锚点在图$I_1$上存在唯一的匹配点$^1q_{i,j}$,并且我们知道匹配点落在了图$I_1$的任一直线$^1l_{closest(i,j)}$上。

对于任一直线段$^0l_i$:

统计它拥有的锚点在图$I_1$所匹配直线段,假设最多有$Z_i$个锚点匹配到了直线$^1l_{match(i)}$上, $0≤match(i)<M_1$。如果$Z_i/N_i >R$,则我们称这两条直线$^0l_i$与直线$^1l_{match(i)}$互相匹配。$R$为一经验阀值,我们设置为0.4。

实验结果

Github: https://github.com/HanjieLuo/line_matching


  1. Zhang, Lilian, and Reinhard Koch. "An efficient and robust line segment matching approach based on LBD descriptor and pairwise geometric consistency." Journal of Visual Communication and Image Representation 24.7 (2013): 794-805. ↩

  2. Wang, Zhiheng, Fuchao Wu, and Zhanyi Hu. "MSLD: A robust descriptor for line matching." Pattern Recognition 42.5 (2009): 941-953. ↩

  3. Von Gioi, Rafael Grompone, et al. "LSD: a line segment detector." Image Processing On Line 2 (2012): 35-55. ↩

  4. Akinlar, Cuneyt, and Cihan Topal. "EDLines: A real-time line segment detector with a false detection control." Pattern Recognition Letters 32.13 (2011): 1633-1642. ↩

  5. http://luohanjie.com/2021-02-03/edline-parallel.html ↩

  6. http://luohanjie.com/2018-12-26/1d-klt-feature-tracker.html ↩

A Parallel Implementation of EDLine Algorithm

Posted on 2021-02-03   |   In Tech , Vision   |  

Github: https://github.com/HanjieLuo/EDLine_parallel

A parallel implementation of EDLine algorithm which based on the Line Segment Detector module of opencv_contrib. Compared to the original version, the parallel one results in a almost 50% time reduction on PC with 4 cores CPU.

test

Requirements

The code is tested on Ubuntu 14.04. It requires the following tools and libraries: CMake, OpenCV 3.4.

Building

#!bash
git clone https://github.com/HanjieLuo/EDLine_parallel.git
cd EDLine_parallel
mkdir build
cd build
cmake ..
make

Test

#!bash
./bin/test_edline_detector

Notes on State Estimation for Robotics

Posted on 2021-01-09   |   In Tech , Robotics   |  

Reference

Barfoot, Timothy D. State estimation for robotics. Cambridge University Press, 2017.

12…22
Hanjie Luo

Hanjie Luo

108 posts
17 categories
106 tags
github
© 2016 - 2021 Hanjie Luo
Powered by Hexo
Theme - NexT.Pisces