CmBacktrace库在工程中的添加和应用

news2024/10/2 14:29:46

CmBacktrace

    • 介绍
    • 在工程中添加CmBacktrace
    • 断言
    • 打印全局变量的值
    • 循环输出错误信息
    • 串口处理
    • 看门狗处理

介绍

CmBacktrace下载

CmBacktrace (Cortex Microcontroller Backtrace)是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库。主要特性如下:

①支持的错误包括:
断言(assert)
故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault)
②故障原因 自动诊断 :可在故障发生时,自动分析出故障的原因,定位发生故障的代码位置,而无需再手动分析繁杂的故障寄存器;
③输出错误现场的 函数调用栈(需配合 addr2line 工具进行精确定位),还原发生错误时的现场信息,定位问题代码位置、逻辑更加快捷、 精准。也可以在正常状态下使用该库,获取当前的函数调用栈;
④支持 裸机 及以下操作系统平台:
RT-Thread
UCOS
FreeRTOS(需修改源码)
⑤根据错误现场状态,输出对应的 线程栈 或 C 主栈;
⑥故障诊断信息支持多国语言(目前:简体中文、英文);
⑦适配 Cortex-M0/M3/M4/M7 MCU;
⑧支持 IAR、KEIL、GCC 编译器;

在工程中添加CmBacktrace

①下载源码解压之后,将cm_backtrace文件夹复制到工程中,同时复制CmBacktrace-master\demos\non_os\stm32f10x\app\src目录下的fault_test.c文件到自己工程中用于测试。

②在keil中添加分组并添加相应文件如下:
在这里插入图片描述
同时添加相应的路径:
在这里插入图片描述
③在项目中包含头文件,定义软硬件版本,软硬件版本为字符串,可以自己随便写,只要不超过最大长度限制CMB_NAME_MAX,CMB_NAME_MAX宏定义在cmb_def.h文件中。

#include <cm_backtrace.h>

#define HARDWARE_VERSION               "V1.0.0"
#define SOFTWARE_VERSION               "V0.1.0"

在main函数中使用cm_backtrace_init()函数初始化CmBacktrace,第一个参数为自己工程生成文件的名称。

cm_backtrace_init("CmBacktrace", HARDWARE_VERSION, SOFTWARE_VERSION);

在这里插入图片描述
④在中断的.c文件中注释掉HardFault_Handler()函数,否则会与cmb_fault.S中的HardFault_Handler重定义。

⑤通过cmb_cfg.h文件配置CmBacktrace。

宏定义说明示例
cmb_println(…)打印函数printf(VA_ARGS);printf(“\r\n”)
CMB_USING_BARE_METAL_PLATFORM不使用OS
CMB_USING_OS_PLATFORM使用OS
CMB_OS_PLATFORM_TYPEOS类型CMB_OS_PLATFORM_RTT或CMB_OS_PLATFORM_UCOSIII或CMB_OS_PLATFORM_FREERTOS等
CMB_CPU_PLATFORM_TYPEMCU平台CMB_CPU_ARM_CORTEX_M0/3/4/7
CMB_USING_DUMP_STACK_INFO是否输出栈信息
CMB_PRINT_LANGUAGE输出语言CMB_PRINT_LANGUAGE_ENGLISH(默认)或CMB_PRINT_LANGUAGE_CHINESE

⑥配置好CmBacktrace之后,在程序中加入测试代码,以除零错误为例,为了更直观,多添加几层函数调用。

void assert_test6()
{
	uint8_t buf[12] = {0xFF,0XFF,0xFF,0XFF,0xFF,0XFF,0xFF,0XFF,0xFF,0XFF,0xFF,0XFF};
    fault_test_by_div0();
}

void assert_test5()
{
	uint8_t buf[12] = {0xEE,0XEE,0xEE,0XEE,0xEE,0XEE,0xEE,0XEE,0xEE,0XEE,0xEE,0XEE};
	assert_test6();
}


void assert_test4()
{
	uint8_t buf[12] = {0xDD,0XDD,0xDD,0XDD,0xDD,0XDD,0xDD,0XDD,0xDD,0XDD,0xDD,0XDD};
	assert_test5();
}

void assert_test3()
{
	uint8_t buf[12] = {0xCC,0XCC,0xCC,0XCC,0xCC,0XCC,0xCC,0XCC,0xCC,0XCC,0xCC,0XCC};
	assert_test4();
}

void assert_test2()
{
	uint8_t buf[12] = {0xBB,0XBB,0xBB,0XBB,0xBB,0XBB,0xBB,0XBB,0xBB,0XBB,0xBB,0XBB};
	assert_test3();
}

void assert_test1()
{
	uint8_t buf[12] = {0xAA,0XAA,0xAA,0XAA,0xAA,0XAA,0xAA,0XAA,0xAA,0XAA,0xAA,0XAA};
	assert_test2();
}

运行结果如下,可以在输出的栈信息中看到上边函数中定义的局部数组的值,从中也可以看出来栈是向下生长的:
在这里插入图片描述
在工程的Output路径下打开命令行工具,需要用到的是CmBacktrace.axf文件,使用addr2line命令运行后如下,可以看到函数的层层调用关系。函数的最大调用深度为16层,由cmb_def.h中宏定义CMB_CALL_STACK_MAX_DEPTH决定。
在这里插入图片描述
⑦至此就可以使用CmBacktrace了。

断言

如果使用断言的话可以使用系统的断言函数,打开宏定义USE_FULL_ASSERT,然后在assert_failed()函数中把CmBacktrace的断言函数cm_backtrace_assert()添加进去。

void assert_failed(uint8_t *file, uint32_t line)
{
    cm_backtrace_assert(cmb_get_sp());
    printf("assert failed at %s:%d \n", file, line);
    while (1) {
    }
}

这样就可以调用assert_param()函数进行参数检查,如果参数有误就输出错误信息。带来的问题就是比如在写flash时,发现参数有误进入断言错误,这样会影响程序执行。
不使用系统的断言函数,自己写一个:

#define my_assert_param(expr) ((expr) ? (bool)0U : my_assert_failed((uint8_t *)__FILE__, __LINE__))
bool my_assert_failed(uint8_t *file, uint32_t line);

bool my_assert_failed(uint8_t *file, uint32_t line)
{
	if(*(uint8_t *)address == 失能)
	{
		return true;
	}
    cm_backtrace_assert(cmb_get_sp());
    printf("assert failed at %s:%d \n", file, line);
    while (1) {
    }
}

这样通过判断my_assert_param执行之后的返回值可以进行其他的操作,比如输出断言错误信息或者函数直接return等。

打印全局变量的值

①如果在发生错误的时候需要分析项目中的全局变量的当前值,那么上面的组件就不能满足,这时候可以自己添加一些代码来实现这个功能。
正常来说没有自己定义分散加载文件的话,keil会在编译的时候自动生成一个,一般如下:

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00040000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00040000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x0000C000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

全局变量分为初始化的全局变量和未初始化的全局变量,初始化的全局变量位于RW段(.data),未初始化的全局变量位于ZI段(.bss)。

可以通过ARM的系统变量来获取RW和ZI数据区的信息,比如起始地址,结束地址,长度等。keil下Help→Open Books Window,然后打开ARM Linker前缀的pdf文件,然后就可以找到系统变量的使用方法。
在这里插入图片描述
②在cmb_cfg.h文件中添加自己的宏定义CMB_USING_VAR_INFO,用于控制是否输出变量的值。

③在cm_backtrace.h中添加RAM名称

#ifdef CMB_USING_VAR_INFO		
		/* ram section name, default is ER_IROM1 */
    #ifndef CMB_RAM_SECTION_NAME
    #define CMB_RAM_SECTION_NAME           RW_IRAM1
    #endif
#endif	

在cm_backtrace.c中添加系统变量

#ifdef CMB_USING_VAR_INFO		
		#define RAM_ZI_SECTION_START(_name_)          Image$$##_name_##$$ZI$$Base
		#define RAM_ZI_SECTION_LENTH(_name_)          Image$$##_name_##$$ZI$$Length
		#define RAM_ZI_SECTION_END(_name_)            Image$$##_name_##$$ZI$$Limit
		#define CMB_RAM_ZI_SECTION_START(_name_)      RAM_ZI_SECTION_START(_name_)
		#define CMB_RAM_ZI_SECTION_LENTH(_name_)      RAM_ZI_SECTION_LENTH(_name_) 
		#define CMB_RAM_ZI_SECTION_END(_name_)        RAM_ZI_SECTION_END(_name_) 

		extern const int CMB_RAM_ZI_SECTION_START(CMB_RAM_SECTION_NAME);
		extern const int CMB_RAM_ZI_SECTION_LENTH(CMB_RAM_SECTION_NAME);
		extern const int CMB_RAM_ZI_SECTION_END(CMB_RAM_SECTION_NAME);

		#define RAM_RW_SECTION_START(_name_)          Image$$##_name_##$$RW$$Base
		#define RAM_RW_SECTION_LENTH(_name_)          Image$$##_name_##$$RW$$Length
		#define RAM_RW_SECTION_END(_name_)            Image$$##_name_##$$RW$$Limit
		#define CMB_RAM_RW_SECTION_START(_name_)      RAM_RW_SECTION_START(_name_)
		#define CMB_RAM_RW_SECTION_LENTH(_name_)      RAM_RW_SECTION_LENTH(_name_) 
		#define CMB_RAM_RW_SECTION_END(_name_)        RAM_RW_SECTION_END(_name_) 

		extern const int CMB_RAM_RW_SECTION_START(CMB_RAM_SECTION_NAME);
		extern const int CMB_RAM_RW_SECTION_LENTH(CMB_RAM_SECTION_NAME);
		extern const int CMB_RAM_RW_SECTION_END(CMB_RAM_SECTION_NAME);
#endif

##的作用是链接字符的作用,RAM_RW_SECTION_START(name)实际内容是Image$$RW_IRAM1$$RW$$Base,使用的时候需要再用CMB_RAM_RW_SECTION_START(name)包一层,否则编译器会报错。使用系统变量的时候如果定义全局变量并直接赋值,如下:

uint32_t RamRwStart = (uint32_t)&CMB_RAM_RW_SECTION_START(CMB_RAM_SECTION_NAME);

这样编译的时候会报警告…/Core/Src/main.c(59): warning: #1296-D: extended constant initialiser used,可以先定义全局变量,然后在其他地方赋值。

同时添加变量用来表示RW和ZI段起始地址和长度等

#ifdef CMB_USING_VAR_INFO
static uint32_t ram_zi_start_addr = 0;
static size_t ram_zi_size = 0;
static uint32_t ram_rw_start_addr = 0;
static size_t ram_rw_size = 0;
#endif

④有了以上就可以开始实现具体的变量打印函数了,输出格式按照一般bin文件的格式,16字节一行,同时显示具体的地址。

#ifdef CMB_USING_VAR_INFO
static void print_zi_info(uint32_t ram_zi_addr, size_t ram_zi_size) {
	cmb_println("============ram_zi_start================");
	for (uint32_t i = 0; i < ram_zi_size; i++) {
			if(!(i%16))
			{
				printf("\r\naddr:[%08X]:",(ram_zi_addr+i));
			}
			printf("%02X ",*(uint8_t *)(ram_zi_addr+i));
	}
	printf("\r\n");
	cmb_println("============ram_zi_end==================");
}

static void print_rw_info(uint32_t ram_rw_addr, size_t ram_rw_size) {
	cmb_println("============ram_rw_start================");
	for (uint32_t i = 0; i < ram_rw_size; i++) {
			if(!(i%16))
			{
				printf("\r\naddr:[%08X]:",(ram_rw_addr+i));
			}
			printf("%02X ",*(uint8_t *)(ram_rw_addr+i));
	}
	printf("\r\n");
	cmb_println("============ram_rw_end==================");
}
#endif

⑤变量打印函数调用,在cm_backtrace_fault函数中调用上面的输出函数。

#ifdef CMB_USING_VAR_INFO	
		print_rw_info(ram_rw_start_addr,ram_rw_size);
		print_zi_info(ram_zi_start_addr,ram_zi_size);
#endif

⑥测试,定义两个数组,一个定义并初始化,一个不初始化。

uint8_t ALLbuf[12] = {0x66,0X66,0x66,0X66,0x66,0X66,0x66,0X66,0x66,0X66,0x66,0X66};
uint8_t ALLbuf_NoInit[12];

这时候ALLbuf的位置应该在RW段,ALLbuf_NoInit的位置应该在ZI段,在main函数中将两个数组的第一个和最后一个字节都赋值为0x88,方便观察,运行效果如下:
在这里插入图片描述
同时也可以对应的在工程生成的map文件中找到这两个变量的地址。
在这里插入图片描述

循环输出错误信息

如果在研发阶段,可以直接通过串口接收到的错误信息来定位问题,但是到了现场的话,就面临着需要保留错误现场的问题,这时候要么把数据错误信息存储在flash,要么就通过串口循环输出。
通过串口循环输出的话可以改造一下cm_backtrace_fault()函数,具体如下:

void report_delay(void)
{
	for(int32_t i = 0;i < 10000000;i++)
	{
		__nop();
	}
}

void fault_report(uint32_t fault_handler_lr, uint32_t fault_handler_sp)
{
	while(1)
	{
		cm_backtrace_fault(fault_handler_lr,fault_handler_sp);
		on_fault = false;
		report_delay();
	}
} 

同时cmb_fault.S中也改一下:

    AREA |.text|, CODE, READONLY, ALIGN=2
    THUMB
    REQUIRE8
    PRESERVE8

; NOTE: If use this file's HardFault_Handler, please comments the HardFault_Handler code on other file.
    ;IMPORT cm_backtrace_fault
	IMPORT fault_report
    EXPORT HardFault_Handler

HardFault_Handler    PROC
    MOV     r0, lr                  ; get lr
    MOV     r1, sp                  ; get stack pointer (current is MSP)
    ;BL      cm_backtrace_fault
	BL      fault_report

Fault_Loop
    BL      Fault_Loop              ;while(1)
    ENDP

    END

这样产生HardFault的时候先调用fault_report()函数,在这里面可以做一些事情,比如初始化串口,判断一下要不要输出错误信息等。添加on_fault = false这行代码是因为在cm_backtrace_fault()函数中通过这个变量来判断是否重入(使用os的情况下可能会出现),但是如果这个变量为真的话就会因为CMB_ASSERT(!on_fault);这行代码,进入CmBacktrace的断言函数。

/* assert for developer. */
#define CMB_ASSERT(EXPR)                                                       \
if (!(EXPR))                                                                   \
{                                                                              \
    cmb_println("(%s) has assert failed at %s.", #EXPR, __FUNCTION__);         \
    while (1);                                                                 \
}

如果屏蔽掉CMB_ASSERT(!on_fault);的话会导致输出的信息少一层函数调用关系,也就是产生HardFault错误的那个函数。这样在裸机中就可以循环打印错误信息。

串口处理

在输出错误信息之前,需要确保串口已经初始化。STM32L431使用HAL库的话,串口句柄是一个全局变量,初始化函数封装也比较复杂,再次使用句柄也会影响当前的值,所以就直接用寄存器来初始化串口,串口引脚只初始化Tx脚就可以了,也可以避免额外的开销。

void FaultReportUartInit(void)
{
	__HAL_RCC_USART1_CLK_ENABLE();
	__HAL_RCC_GPIOA_CLK_ENABLE();
	
	MODIFY_REG(USART1->CR1, USART_CR1_UE, 0);
	while (READ_BIT(USART1->CR1, USART_CR1_UE) != 0);
	
	//io初始化
	MODIFY_REG(GPIOA->OSPEEDR, GPIO_OSPEEDR_OSPEED9, GPIO_OSPEEDR_OSPEED9);
	MODIFY_REG(GPIOA->OTYPER, GPIO_OTYPER_OT9, 0);
	MODIFY_REG(GPIOA->MODER, GPIO_MODER_MODE9, GPIO_MODER_MODE9_1);
	MODIFY_REG(GPIOA->PUPDR, GPIO_PUPDR_PUPD9, 0);
	MODIFY_REG(GPIOA->AFR[1],GPIO_AFRH_AFSEL9, (GPIO_AF7_USART1 << GPIO_AFRH_AFSEL9_Pos));
	
	uint32_t UartDiv = 40000000/115200;
	
	USART1->BRR =  (UartDiv & 0xFFFFFFF0) | (UartDiv & 0x0F);
	MODIFY_REG(USART1->CR1, ((uint32_t)(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE |USART_CR1_OVER8)),\
							((uint32_t)(UART_WORDLENGTH_8B | UART_PARITY_NONE | UART_MODE_TX | UART_OVERSAMPLING_16)));
	MODIFY_REG(USART1->CR2, USART_CR2_STOP, UART_STOPBITS_1);
	MODIFY_REG(USART1->CR3, ((uint32_t)(USART_CR3_RTSE | USART_CR3_CTSE | USART_CR3_ONEBIT)), UART_ONE_BIT_SAMPLE_DISABLE);
	
	MODIFY_REG(USART1->CR1, USART_CR1_UE, USART_CR1_UE);
}

看门狗处理

软件看门狗启动之后,除非产生一次系统复位,否则是不会停止的,因此需要处理一下看门狗,防止看门狗复位,这样就需要保证输出自己需要全部信息的时间要小于看门狗的复位周期。在循环打印错误信息的时候也需要喂狗。

WRITE_REG(IWDG->KR, IWDG_KEY_RELOAD);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/436893.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Javaee Spring template实现查询数据库表内容 基于半xml半注解

昨天用基于xml配置实现template查询数据库&#xff0c;今天基于半xml半注解方式实现,使用注解需要导入spring-aop-5.3.8.jar 导入jar包 项目结构&#xff1a; 其他代码在&#xff0c;先前上一篇文章已经给出 AccountServiceImpl package wwx.dao;import org.springframework…

定时器中断实验

实现内容 利用TIM3的定时器中断来控制DS1的翻转&#xff0c;在主函数用DS0 的翻转来提示程序正在运行。 定时器介绍 定时器可以认为是一个计数器&#xff1b;给定计数器一个初值&#xff0c;每当计数一次&#xff0c;就会走过一个固定的时间&#xff0c;当达到我们给定的初值…

强大的图像处理:ImageKit10.E ActiveX Crack

强大的图像处理&#xff01; ImageKit10 ActiveX 是一个组件&#xff0c;允许您快速轻松地向应用程序添加图像处理功能。使用 ImageKit10 ActiveX&#xff0c;您可以编写从 TWAIN 扫描仪和数码相机检索图像的应用程序;加载和保存图像文件并将图像从一种格式转换为另一种格式;编…

数字电路和模拟电路-半导体三极管

目录 1 什么是三极管&#xff1f; 1.1 放大状态时的偏执条件 1.1.1发射结加正向电压&#xff0c;扩散运动形成发射极电流IE 1.1.2扩散到基区的自由电子与空穴的复合运动形成基极电流IB 1.1.3集电结加反向电压&#xff0c;漂移运动形成集电极电流Ⅰc 2 三极管工作原理 2.…

5.图论(0x3f:从周赛中学算法 2022下)

来自0x3f【从周赛中学算法 - 2022 年周赛题目总结&#xff08;下篇&#xff09;】&#xff1a;https://leetcode.cn/circle/discuss/WR1MJP/ 周赛中的图论题目比较少&#xff0c;除了下面选的 DFS、BFS、拓扑排序、基环树、二分图判定等&#xff0c;还有最短路、DFS 时间戳等&a…

CloudCompare二次开发之如何设计界面ui与功能实现?

文章目录 0.引言1.创建界面ui相关文件2.添加界面ui相关文件到CloudCompare工程3.修改工程相关文件4.结果展示 0.引言 CloudCompare源代码编译成功后&#xff0c;即可进行二次开发&#xff0c;可以通过修改源码实现二次开发&#xff0c;二次开发基础功能见&#xff08;CloudComp…

什么是文件共享软件?文件传输软件如何共享?

它是一个文件共享软件应用程序&#xff0c;可让强大的数据保护层下将任何大小的文件发送到世界上的任何地方。以光速发送和共享无限数量的文件。可以提交门户并使用语言&#xff0c;品牌&#xff0c;存储等自定义门户。可以选择一个存储点&#xff0c;例如文件传输软件&#xf…

[入门必看]数据结构4.2:串的模式匹配

[入门必看]数据结构4.2&#xff1a;串的模式匹配 第四章 串4.2 串的模式匹配知识总览4.2.1_朴素模式匹配算法4.2.2_1_KMP算法4.2.2_2_求next数组4.2.3_KMP算法的进一步优化 4.2.1_朴素模式匹配算法什么是字符串的模式匹配朴素模式匹配算法通过数组下标实现朴素模式匹配算法代码…

http(1)

主要介绍http 1.0 我们在浏览器中输入一个网址&#xff0c;稍等片刻就看见了网页 客户端会发送一个http请求&#xff0c;要求返回cn.bing.com这个网址&#xff0c;服务器收到请求后就会返回一个html页面 &#xff08;服务器根据请求找到客户端想要的资源&#xff0c;然后把这个…

[LeetCode]路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶子节点 是指没有子节点…

【并发编程】线程池的原理和源码分析

线程使用上可能的问题 我们一般通过new Thread().start();来创建和运行一个线程&#xff0c;如果在业务过程中有大量场景需要使用多线程来并发&#xff0c;那么就会有以下问题 需要频繁的创建和销毁线程 &#xff0c;需要消耗CPU资源如果创建和销毁的线程的数量过多(大于CPU核…

CMOS图像传感器——从传感器冗余说起

在这先抛出一个概念,什么是成像圈?众所周知,相机的镜头近似于圆柱体,光线透过圆筒子投射出的大都是圆形。我们可以拿一个镜头演示一下,当这个圆圈投在传感器所在焦平面时,我们称之为像场。像场的边界我们称之为成像圈,成像圈是圆的,但是传感器是矩形,天圆地方的怎么放…

Lombok插件下载与离线安装

Lombok插件下载与离线安装 首先你既然搜要离线安装或下载&#xff0c;那么肯定也是在IDEA工具里面&#xff0c;无法搜索到&#xff0c;或者自动下载安装失败吧&#xff1f; 安装包下载地址 记得和 idea版本一样&#xff0c; 如果不知道啥版本看下面

CleanMyMac X4.15重大更新 新功能菜单发布

CleanMyMac&#xff0c;一款电脑清理软件&#xff0c;可以帮助你清理垃圾文件、优化系统性能、管理应用程序等。它就像你的电脑管家&#xff0c;让你的电脑始终保持最佳状态。无论是手机还是电脑&#xff0c;在使用一段时间之后都可能会发生卡顿的现象&#xff0c;很多小伙伴会…

C++ 高级数据结构————[ 单调栈 ]

每周一篇的算法文章来了 今天讲解的是高级数据结构中的——单调栈 单调栈&#xff0c;顾名思义&#xff0c;就是升级版的栈&#xff08;&#xff09; 先回顾一下栈把 栈&#xff0c;是一种线性表&#xff0c;它的特点是只能从一边进出&#xff0c;并且先进后出&#xff0c;后进…

Windows入门篇一之MSDN手册的使用和第一个窗口程序

Windows入门篇之MSDN手册的使用和第一个窗口程序 MSDN手册MSDN手册是什么MSDN手册的下载和安装MSDN手册的使用 第一个窗口程序项目的创建第一个简单的窗口程序 MSDN手册 MSDN手册是什么 MSDN手册是VS中的一个帮助手册&#xff0c;帮助初学者学习Windows编程&#xff0c;来查找…

opencv实现卡尔曼滤波

卡尔曼滤波是一种递归的估计&#xff0c;即只要获知上一时刻状态的估计值以及当前状态的观测值就可以计算出当前状态的估计值&#xff0c;因此不需要记录观测或者估计的历史信息。 卡尔曼滤波器分为两个阶段&#xff1a;预测与更新。在预测阶段&#xff0c;滤波器使用上一状态…

VScode配置远端服务器深度学习项目

前提准备已安装VScode。 1.安装插件Remote Development 安装完成后左侧就多了远程资源管理器图标&#xff1a; 1.点击远程资源管理器。 2.点击小齿轮&#xff08;配置&#xff09;。 3.选择config配置文件&#xff0c;如果没有自己按照相似路径新建config文件后重复1、2、3步骤…

组合总和III

组合总和III 题目 力扣题目链接:https://leetcode.cn/problems/combination-sum-iii/ 代码 class Solution {public:vector<vector<int

小航助学答题系统编程等级考试scratch一级真题2023年3月(含题库答题软件账号)

青少年编程等级考试scratch真题答题考试系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 1.下列说法不正确的是&#xff1f;&#xff08; &#xff09; A.可以从声音库中随机…