任务调度的初始化及上下文切换原理

2023-05-16

任务调度的初始化及上下文切换原理

  • 前言
    • 任务调度整体框架
    • 任务调度初始化源码分析
    • 启动第一个任务分析
    • PendSV中断
    • systick定时中断

前言

本文将从调度器的创建为入口,通过分析定时器中断以及PendSV中断的原理,刨析任务调度的本质原理。

任务调度整体框架

在这里插入图片描述
从上图可以看到,调度器是在main函数中初始化,同时初始化后会启动第一个任务函数;任务上下文切换实际也是借用了一个特殊的中断PendSV,此中断可以挂起,当有任务切换请求后,需要在没有更高优先级中断执行时才会执行PendSV。PendSV主要就是保存上文,切换到下文。

任务调度初始化源码分析

1.void vTaskStartScheduler( void )
函数实现流程及作用:
在这里插入图片描述

void vTaskStartScheduler( void )
{
BaseType_t xReturn;

	/* Add the idle task at the lowest priority. */
	#if( configSUPPORT_STATIC_ALLOCATION == 1 )
	{
		//如果宏开启,使用静态方式创建空闲任务
	}
	#else
	{
		//创建空闲任务,空闲任务主要工作是释放已经销毁的任务空间
		xReturn = xTaskCreate(	prvIdleTask,
								"IDLE", configMINIMAL_STACK_SIZE,
								( void * ) NULL,
								( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
								&xIdleTaskHandle ); 
	}
	#endif /* configSUPPORT_STATIC_ALLOCATION */

	#if ( configUSE_TIMERS == 1 )
	{
		if( xReturn == pdPASS )
		{//创建定时任务,也是系统中软定时的实现
			xReturn = xTimerCreateTimerTask();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_TIMERS */

	if( xReturn == pdPASS )
	{
		//屏蔽freertos可控优先级及以下的中断,当启动第一个任务是会解除屏蔽
		portDISABLE_INTERRUPTS();

		#if ( configUSE_NEWLIB_REENTRANT == 1 )
		{
			/* Switch Newlib's _impure_ptr variable to point to the _reent
			structure specific to the task that will run first. */
			_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
		}
		#endif /* configUSE_NEWLIB_REENTRANT */
		//初始化一些全局变量
		xNextTaskUnblockTime = portMAX_DELAY;
		xSchedulerRunning = pdTRUE;
		xTickCount = ( TickType_t ) 0U;

		//设置时基定时器
		portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

		//设置PendSV中断和systick中断优先级,设置tick时间,启动第一个任务
		if( xPortStartScheduler() != pdFALSE )
		{
			/* Should not reach here as if the scheduler is running the
			function will not return. */
		}
		else
		{
			/* Should only reach here if a task calls xTaskEndScheduler(). */
		}
	}
	else
	{
		/* This line will only be reached if the kernel could not be started,
		because there was not enough FreeRTOS heap to create the idle task
		or the timer task. */
		configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
	}

	/* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
	meaning xIdleTaskHandle is not used anywhere else. */
	( void ) xIdleTaskHandle;
}

2.BaseType_t xPortStartScheduler( void )
函数的功能及实现如下:
在这里插入图片描述

BaseType_t xPortStartScheduler( void )
{
	/* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0.  See
	http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */
	configASSERT( ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) );

	#if( configASSERT_DEFINED == 1 )
	{
		//中断优先级寄存器0:IPR0
		volatile uint32_t ulOriginalPriority;
		volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
		volatile uint8_t ucMaxPriorityValue;

		/*这一大段代码用来确定一个最高ISR优先级,在这个ISR或者更低优先级的ISR中可以安全的调用以FromISR结尾的API函数.

		保存中断优先级值,因为下面要覆写这个寄存器(IPR0)*/
		ulOriginalPriority = *pucFirstUserPriorityRegister;

		/*  确定有效的优先级位个数. 首先向所有位写1,然后再读出来,由于无效的优先级位读出为0,然后数一数有多少个1,就能知道有多少位优先级. */
		*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;

		/* Read the value back to see how many bits stuck. */
		ucMaxPriorityValue = *pucFirstUserPriorityRegister;

		/*用来防止用户不正确的设置RTOS可屏蔽中断优先级值 . */
		ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;

		/* 计算最大优先级组值 */
		ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;
		while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
		{
			ulMaxPRIGROUPValue--;
			ucMaxPriorityValue <<= ( uint8_t ) 0x01;
		}

		/* Shift the priority group value back to its position within the AIRCR
		register. */
		ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
		ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;

		/* 将IPR0寄存器的值复原. */
		*pucFirstUserPriorityRegister = ulOriginalPriority;
	}
	#endif /* conifgASSERT_DEFINED */

	/* 将PendSV和SysTick中断设置为最低优先级 */
	portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
	portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

	/* Configure the regions in the MPU that are common to all tasks. */
	prvSetupMPU();

	/* 启动系统节拍定时器,即SysTick定时器,初始化中断周期并使能定时器 */
	prvSetupTimerInterrupt();

	/* 初始化临界区嵌套计数器. */
	uxCriticalNesting = 0;

	/* Ensure the VFP is enabled - it should be anyway. */
	vPortEnableVFP();

	/* Lazy save always. */
	*( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;

	/* 启动第一个任务  */
	prvStartFirstTask();

	/* Should not get here! */
	return 0;
}

启动第一个任务分析

在初始化任务调度时,最后会执行prvStartFirstTask()函数去启动第一个任务,此部分以cortex-m3为例;此部分是汇编实现
源码如下:

__asm void prvStartFirstTask( void )
{
	PRESERVE8

	/* Cortext-M3硬件中,0xE000ED08地址处为VTOR(向量表偏移量)寄存器,存储向量表起始地址. */
	ldr r0, =0xE000ED08
	ldr r0, [r0]
	ldr r0, [r0]

	/* 将堆栈地址存入主堆栈指针  */
	msr msp, r0
	/*使能全局中断. */
	cpsie i
	cpsie f
	dsb
	isb
	/* 调用SVC启动第一个任务*/
	svc 0
	nop
	nop
}

调用SVC将会执行SVC中断函数:
SVC中断服务函数一开始就使用全局指针pxCurrentTCB获得第一个要启动的任务TCB,从而获得任务的当前堆栈栈顶指针。先将人为入栈的寄存器R4~R11出栈,将最新的堆栈栈顶指针赋值给线程堆栈指针PSP,再取消中断掩蔽。到这里,只要发生中断,就都能够被响应了;
执行bx r14指令后,硬件自动将寄存器xPSR、PC、LR、R12、R3~R0出栈,这时任务的任务函数指针vTask_func会出栈到PC指针中,从而开始执行任务函数。

至此,任务获得CPU执行权,调度器正式开始工作。

__asm void vPortSVCHandler( void )
{
	PRESERVE8
	//将当前的运行态任务TCB地址赋值给r3,如果没有任务创建,则pxCurrentTCB	指向空闲任务
	ldr	r3, =pxCurrentTCB	/* Restore the context. */
	ldr r1, [r3]			/* Use pxCurrentTCBConst to get the pxCurrentTCB address. */
	ldr r0, [r1]			/* 获取任务堆栈,TCB中第一个参数就是任务堆栈 */
	ldmia r0!, {r4-r11}		/* 出栈. */
	msr psp, r0				/* 最新的栈顶指针赋给线程堆栈指针PSP  */
	isb
	mov r0, #0
	msr	basepri, r0  //取消中断屏蔽
	orr r14, #0xd //这里0x0d表示:返回后进入线程模式,从进程堆栈中做出栈操作,返回Thumb状态
	bx r14
}

对于Cortex-M3架构,需要依次入栈xPSR、PC、LR、R12、R3-R0、R11-R4,其中r11~R4需要人为入栈,其它寄存器由硬件自动入栈。寄存器PC被初始化为任务函数指针vTask_func,这样当某次任务切换后,任务A获得CPU控制权,任务函数vTask_func被出栈到PC寄存器,之后会执行任务的代码;LR寄存器初始化为函数指针prvTaskExitError,这是由移植层提供的一个出错处理函数。

PendSV中断

PendSV(可挂起系统调用)用于完成任务切换,它的最大特性是如果当前有优先级比它高的中断在运行,PendSV会推迟执行,直到高优先级中断执行完毕。
当进行任务切换请求时,实际上就是将PendSV的悬起位置1,在没有其它中断运行时执行PendSV中断服务函数,在这个中断函数中实现任务切换。

#define taskYIELD()					portYIELD()

#define portYIELD()																\
{																				\
	/* Set a PendSV to request a context switch. */								\
	portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;								\
																				\
	/* Barriers are normally not required but do ensure the code is completely	\
	within the specified behaviour for the architecture. */						\
	__dsb( portSY_FULL_READ_WRITE );											\
	__isb( portSY_FULL_READ_WRITE );											\
}

1.实际在任务创建以及恢复时,会判断新加入就绪态的任务优先级是否大于当前执行的任务优先级,如果大于就会调用上述宏,进行中断请求;
2.针对中断内使用FromISR版本的任务恢复函数,实际是没有立马进行设置PendSV悬挂位,而是待退出中断后,判断标志,若需要请求任务切换,则设置PendSV悬挂位。
3.定时器systick中断轮询,每1ms轮询一次,判断是否满足任务切换的条件,如果满足,则请求任务切换。

systick定时中断

SysTick用于产生系统节拍时钟,提供一个时间片,如果多个任务共享同一个优先级,则每次SysTick中断,下一个任务将获得一个时间片。
初始化systick:

	void vPortSetupTimerInterrupt( void )
	{
		/* 配置中断触发时间. */
		portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
		portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
	}

中断函数:

void SysTick_Handler(void)
{	
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    {
        xPortSysTickHandler();	
    }
}

void xPortSysTickHandler( void )
{
	/* 屏蔽freertos系统可控优先级及以下的中断 */
	vPortRaiseBASEPRI();
	{
		/* 主要的执行函数*/
		if( xTaskIncrementTick() != pdFALSE )
		{
			/* 有上下文切换的请求,置位PendSV的悬挂位. */
			portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
		}
	}
	//恢复屏蔽
	vPortClearBASEPRIFromISR();
}

通过上面即可看出,systick定时中断主要操作是在xTaskIncrementTick
函数中执行的,且如果执行后返回值有任务上下文的请求,则进行PendSV请求。
下面分析xTaskIncrementTick函数
函数流程如下:
在这里插入图片描述
根据上述的函数流程图,相信看到下述代码不成问题。

BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;

	/* Called by the portable layer each time a tick interrupt occurs.
	Increments the tick then checks to see if the new tick value will cause any
	tasks to be unblocked. */
	traceTASK_INCREMENT_TICK( xTickCount );
	if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
	{
		/* Minor optimisation.  The tick count cannot change in this
		block. */
		const TickType_t xConstTickCount = xTickCount + 1;

		/* Increment the RTOS tick, switching the delayed and overflowed
		delayed lists if it wraps to 0. */
		xTickCount = xConstTickCount;

		if( xConstTickCount == ( TickType_t ) 0U )
		{
			taskSWITCH_DELAYED_LISTS();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		/* See if this tick has made a timeout expire.  Tasks are stored in
		the	queue in the order of their wake time - meaning once one task
		has been found whose block time has not expired there is no need to
		look any further down the list. */
		if( xConstTickCount >= xNextTaskUnblockTime )
		{
			for( ;; )
			{
				if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
				{
					/* The delayed list is empty.  Set xNextTaskUnblockTime
					to the maximum possible value so it is extremely
					unlikely that the
					if( xTickCount >= xNextTaskUnblockTime ) test will pass
					next time through. */
					xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
					break;
				}
				else
				{
					/* The delayed list is not empty, get the value of the
					item at the head of the delayed list.  This is the time
					at which the task at the head of the delayed list must
					be removed from the Blocked state. */
					pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
					xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );

					if( xConstTickCount < xItemValue )
					{
						/* It is not time to unblock this item yet, but the
						item value is the time at which the task at the head
						of the blocked list must be removed from the Blocked
						state -	so record the item value in
						xNextTaskUnblockTime. */
						xNextTaskUnblockTime = xItemValue;
						break;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					/* It is time to remove the item from the Blocked state. */
					( void ) uxListRemove( &( pxTCB->xStateListItem ) );

					/* Is the task waiting on an event also?  If so remove
					it from the event list. */
					if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
					{
						( void ) uxListRemove( &( pxTCB->xEventListItem ) );
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					/* Place the unblocked task into the appropriate ready
					list. */
					prvAddTaskToReadyList( pxTCB );

					/* A task being unblocked cannot cause an immediate
					context switch if preemption is turned off. */
					#if (  configUSE_PREEMPTION == 1 )
					{
						/* Preemption is on, but a context switch should
						only be performed if the unblocked task has a
						priority that is equal to or higher than the
						currently executing task. */
						if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
						{
							xSwitchRequired = pdTRUE;
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					#endif /* configUSE_PREEMPTION */
				}
			}
		}

		/* Tasks of equal priority to the currently running task will share
		processing time (time slice) if preemption is on, and the application
		writer has not explicitly turned time slicing off. */
		#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
		{
			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
			{
				xSwitchRequired = pdTRUE;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */

		#if ( configUSE_TICK_HOOK == 1 )
		{
			/* Guard against the tick hook being called when the pended tick
			count is being unwound (when the scheduler is being unlocked). */
			if( uxPendedTicks == ( UBaseType_t ) 0U )
			{
				vApplicationTickHook();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_TICK_HOOK */
	}
	else
	{
		++uxPendedTicks;

		/* The tick hook gets called at regular intervals, even if the
		scheduler is locked. */
		#if ( configUSE_TICK_HOOK == 1 )
		{
			vApplicationTickHook();
		}
		#endif
	}

	#if ( configUSE_PREEMPTION == 1 )
	{
		if( xYieldPending != pdFALSE )
		{
			xSwitchRequired = pdTRUE;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_PREEMPTION */

	return xSwitchRequired;
}

本文部分引用了网友的此篇博客,也十分感谢他的分享:FreeRTOS调度器启动过程分析.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

任务调度的初始化及上下文切换原理 的相关文章

  • LabVIEW 编程经验

    一个非常好的LabVIEW教程 链接 xff1a LabVIEW 编程经验
  • 【授以渔】教你使用Amesim帮助文档

    已剪辑自 https zhuanlan zhihu com p 340572169 Amesim的帮助文档是Amesim最全面 最权威的学习宝典 xff1a 它不仅有手把手的操作教程 xff0c 还有非常专业的理论介绍 xff1b 不仅有H
  • simulink仿真、libview仿真、 amesim仿真介绍

    simulink仿真 已剪辑自 https blog csdn net qq 41325078 article details 105406196 Simulink是MATLAB的重要组成部分 xff0c 可以用于建模 xff0c 仿真等
  • 基于模型的系统工程 | MBSE

    文章目录 1 什么是系统2 什么是系统工程3 什么是基于模型的系统工程4 MBSE要素5 MBSE相对于TBSE优势6 总结 已剪辑自 https modelbaba com mbse 101 html 1 什么是系统 系统 xff08 S
  • MBSE详细介绍

    文章目录 MBSE是什么 有什么用 怎么学习 xff1f 1 MBSE是什么 xff1f 2 MBSE有什么用 xff1f 3 MBSE的方法有哪些 xff1f 4 MBSE怎么学习 xff1f MBSE建模学习之一 xff1a 有26种分
  • CSDN,我的良师益友

    鲁迅曾说过 xff1a 不是缺乏天才 xff0c 而是缺乏培养天才的土壤 对于中国的 IT 行业来说 xff0c 从来不缺乏技术英雄 xff0c 缺少的是铸就技术英雄的平台 而 CSDN 就给了我们这样一个平台和机会 xff0c 所以我们是
  • 如何把PPT连背景一起复制?

    如何把PPT连背景一起复制 xff1f 如何将一个ppt中的版式原样复制到另外一个ppt上 xff0c 不改变那个ppt的背景模板 xff1f 1 首先打开一个PPT xff0c 点击上面的工具 插入 xff0c 选择 新建幻灯片 的下拉三
  • 智睿思维基于模型的系统工程软件 (MBSES)

    文章目录 智睿思维基于模型的系统工程软件 xff08 MBSES xff09 中文国产化MBSE软件工具支持SysML9种图模块定义图内部模块图参数图包图用例图活动图状态机图需求图序列图 模型检查 引用 子类查询行为模型的仿真执行报表和发布
  • 干货分享|基于模型的系统工程(MBSE)介绍

    干货分享 基于模型的系统工程 xff08 MBSE xff09 介绍 xff08 上 xff09 已剪辑自 https icv 51cto com posts 427 前言 汽车智能化主要体现在车辆自身功能的数字化与自动化及车辆与外界 xf
  • 嵌入式软件可靠性设计的编程要点?

    文章目录 1 判错2 判断实参是否合法3 仔细检查函数的返回值4 防止指针越界5 防止数组越界6 数学算数运算7 其它可能出现运行时错误的地方8 编译器语义检查9 关键数据多区备份 xff0c 取数据采用 表决法 10 非易失性存储器的数据
  • 基础知识 | 适航委任代表的前世今生

    已剪辑自 https mp weixin qq com s yxLKQ2uxkOe7 uAWW4rt4g 01 根据CCAR 183部 民用航空器适航委任代表和委任单位代表的规定 R1版的定义 xff0c 委任代表是指民航局委任适航部门以外
  • 经历分享:我是如何出版了人生的第一本书的

    已剪辑自 https mp weixin qq com s LK1d45SyKcvh1kcMwMCjog 对很多互联网人来说 xff0c 从输出 写文章到出书 xff0c 是一个重要的里程碑节点 但很多人仅仅是停留在文章阶段 xff0c 并
  • 需求工程与系统工程

  • 软件测试类型——集成测试

    已剪辑自 https blog csdn net vikeyyyy article details 80900540 简介 集成测试 xff08 Integration Testing xff09 xff0c 也叫组装测试或联合测试 在单元
  • 代理模型介绍大全

    已剪辑自 https zhuanlan zhihu com p 555814030 1 代理模型简介 一次看文献的时候 xff0c 看到一篇关于代理模型的文章 xff0c 讲解的十分细致 xff0c 深受启发 xff0c 醍醐灌顶 xff0
  • 一篇文章让你了解ISIGHT

    已剪辑自 https vsystemes com 35621 1 概述 经过了二十年的发展 xff0c 对于国内CAE仿真 xff0c 众多企业的目光已经不再只关注仿真本身 xff0c 更多的将目光放在了以下三个方面 xff1a 1 关注的
  • DDS(data distribution service,数据分发服务)简介

    已剪辑自 http www uml org cn modeler 20201010112 asp 编辑推荐 文章主要介绍了DDS高级数据中心接口取代了以消息为中心的编程和基本技术概念等相关内容 来自于dds foundation org 由
  • Oracle 删除表空间回收磁盘空间

    1 查询表空间和表空间数据库文件路径 span class token keyword SQL span span class token operator gt span span class token keyword select s
  • 在Ubuntu中使用雷电3外接显卡(eGPU)进行深度学习

    sud种种很凑巧的因素 xff0c 需要用这么一套设备做深度学习 笔记本是联想yoga s730 xff0c 两年前在某多多入手 xff0c 显卡坞是技嘉gaming box 43 GTX1070 xff0c 某鱼入手 折腾了一上午 xff

随机推荐

  • make和cmake命令

    我们在安装Ubuntu软件时 xff0c 有时我们会用到make和cmake命令 所以在这篇文章我们来讲解一下 xff0c make命令和cmake命令他们的作用 xff0c 以及什么关系和区别 首先假设你有一定的计算机基础 xff0c 能
  • 小觅双目相机自带参数配置ORBSLAM3双目参数文件

    ORBSLAM3相机需要的参数 打开 home knight ORB SLAM3 Examples Stereo EuRoC yaml 以这个开源数据集的参数为模板 1 Camera type PinHole 相机模型 小觅双目相机也是针孔
  • GVINS编译出现大量的error: ‘CV_CALIB_CB_ADAPTIVE_THRESH’ was not declared in this scope错误

    错误 xff1a home knight GVINS GI src GVINS main camera model src chessboard Chessboard cc 38 43 error CV CALIB CB NORMALIZE
  • 3.CMakeLists——完成脚本、说明文档、版权文件doc文件夹的指定位置安装

    1 实例 在CMakeLists txt目录下加入如下语句执行安装操作 xff1a INSTALL FILES COPYRIGHT README DESTINATION share doc cmake t2 INSTALL PROGRAMS
  • 1.vinsfusion-从数据流看程序

    本节介绍 xff0c 图像数据和惯导数据是如何传入程序中的 xff0c 以及之后的图像数据格式转化 和惯导数据的初步处理 图像数据流 1 订阅话题数据 xff1a 订阅左图 ros Subscriber sub img0 span clas
  • vlsam容易混洗的概念

    特征点 角点 关键点 xff1a 这些概念虽然在细节上有所不同 xff0c 但都可以统一称为特征点或者关键点 xff0c 也就是具有特征性质的点 外点 xff08 outlier point xff09 xff1a 简单的理解为不符合条件的
  • 5.vinsdusion globalOptNode.cpp

    IO输出的 nav msgs Odometry 类型消息 xff0c 这个定位信息包含了VIO的位置和姿态 xff0c 其坐标系原点位于VIO的第一帧处 GPS输出的sensor msgs NavSatFixConstPtr 类型消息 xf
  • ros节点启动和关闭相关

    1 ros运行单位 xff1a Ros程序运行的单位是ros node 2 ros 节点的启动 xff1a xff08 1 xff09 初始化ros节点 xff1a 通过调用ros init 接口实现 xff1b 可以通过参数指定默认节点名
  • VINSFUSION-运行,并评估。

    1 修改yaml文件 output path home tong output xff0c 改成自己的 xff0c 其他的看自己用的啥相机 xff0c 改话题名字和相关参数 2 修改程序中的输出 xff0c 改成tum格式 当前帧在滑动窗口
  • Linux Windows Mac 临时 HTTP 服务传输文件(python http server)

    http 传输文件比 scp 和 ftp 都要可靠稳定和方便 xff0c 在 Linux 系统上 xff08 Windows 和 Mac 也一样 xff09 xff0c 我们经常需要把一堆文件或者某个大文件发送到别处 xff0c 遇到这种需
  • Docker容器显示图像界面配置

    Docker容器显示图像界面配置 原理概述 可以把docker看做一台没配显示器的主机 xff0c linux目前的主流图像界面服务X11支持 客户端 服务端 xff08 Client Server xff09 的工作模式只要在容器启动的时
  • ubuntu服务器有多个网卡时设置网卡的优先级

    有时候服务器会有多个网卡 xff0c 分别连接局域网和外网 xff0c 当局域网的优先级更高时会出现无法访问外网的情况 只需要设置默认网关就行了 首先查看默认网关 ip route show 然后通过 span class token fu
  • C_C++架构如何学?2020必备的最新懒人攻略指南(持续更新)

    C C 43 43 架构师学习之路 如同加锁和解锁操作 xff0c 在看文章之前先点个赞 xff0c 如果你看完文章觉得对您没有帮助 xff0c 希望您取消您的点赞 您的点赞是对笔者的一次鼓励 xff0c 您的取消是让笔者有对文章质量的把控
  • 嵌入式

    嵌入式 嵌入式开发就是指在嵌入式操作系统下进行开发 xff0c 一般常用的系统有WinCE xff0c ucos xff0c vxworks xff0c linux xff0c android等 另外 xff0c 用c xff0c c 43
  • Autoware源码分析——astar_avoid

    概要 本文是对autoware中waypoint planner功能包的相关文件组成和具体节点的分析 由于程序比较复杂 xff0c 我认为还存在一些不完整的地方 xff0c 之后也会继续分析 xff0c 继续更新 在看代码的过程中也学习了一
  • C/C++开发之CMakeList(其二)

    嵌套式CMakeLists写法 xff0c 文件结构如下 xff1a span class token operator span examples span class token operator span span class tok
  • (1)MissionPlanner1.3.75源码编译问题及解决办法

    MissionPlanner源码编译问题及解决办法 前言错误1 NETSDK10452 NETSDK11473 CS02464 NU1104 结束语 前言 最近想在missionplanner上加一些功能 xff0c 于是就整了套源码想试试
  • 04-旭日X3派测评——原版ROS的安装与测试

    1 环境安装 具体安装过程不在这里赘述 xff0c 主要参考以下几篇文章 xff1a 详细介绍如何在ubuntu20 04中安装ROS系统 xff0c 超快完成安装 xff08 最新版教程 xff09 Ubuntu20 04安装ros教程
  • npm(四):剖析npm包版本管理机制

    Nodejs成功离不开 npm 优秀的依赖管理系统 在介绍整个依赖系统之前 xff0c 必须要了解 npm如何管理依赖包的版本 xff0c 本章将介绍 npm包 的版本发布规范 如何管理各种依赖包的版本以及一些关于包版本的最佳实践 系列文章
  • 任务调度的初始化及上下文切换原理

    任务调度的初始化及上下文切换原理 前言任务调度整体框架任务调度初始化源码分析启动第一个任务分析PendSV中断systick定时中断 前言 本文将从调度器的创建为入口 xff0c 通过分析定时器中断以及PendSV中断的原理 xff0c 刨