活动介绍

09:00:02 **** Incremental Build of configuration ble_app_uart_out for project BLE_APP_UART **** make -j4 all Building target: BLE_APP_UART.elf Invoking: GD RISC-V MCU Linker riscv-nuclei-elf-gcc -march=rv32imafcbp -mcmodel=medlow -msmall-data-limit=8 -msave-restore -mabi=ilp32f -Os -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -fno-common -fno-unroll-loops -Werror -Wunused -Wuninitialized -Wall -Wno-format -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable -g -Wl,-Map,"BLE_APP_UART.map" -T "..\..\..\..\gd32vw55x_example.lds" -nostartfiles -Xlinker --gc-sections -L"..\..\..\..\..\..\lib" -L"..\..\..\..\..\..\plf\riscv\NMSIS\Library\DSP\GCC" --specs=nano.specs -u_printf_float --specs=nosys.specs -Wl,--just-symbols=../../../../../../../ROM-EXPORT/symbol/rom_symbol_m.gcc -o "BLE_APP_UART.elf" ./util/aes_ecb.o ./util/crc.o ./util/cyclic_buffer.o ./util/debug_print.o ./util/dlist.o ./util/slist.o ./util/trace_ext.o ./util/user_setting.o ./util/ut

时间: 2025-08-06 18:11:27 浏览: 6
<think>我们正在处理用户关于RISC-VMCU项目BLE_APP_UART的构建和链接问题。用户的问题是关于使用riscv-nuclei-elf-gcc编译器时遇到的构建和链接问题。我们需要结合用户提供的引用内容(尽管引用内容主要涉及RISC-V交叉编译器的下载和GCC的基本介绍)以及常见构建和链接问题的解决方案来回答。用户提供的引用[1]和[2]主要给出了交叉编译器的下载链接以及GCC编译器的基本使用说明。引用[3]则是一个关于开发RISC-V操作系统的系列文章。考虑到用户的问题具体是构建和链接问题,我们应当集中讨论可能导致这些问题的常见原因及解决方案。构建和链接问题通常包括:编译器或链接器路径设置错误、缺失必要的库文件、链接脚本问题、启动文件问题、编译选项错误、目标设备不匹配等。以下是我们给用户的回答步骤:1.**确认编译器路径和版本**:首先需要确保使用的riscv-nuclei-elf-gcc编译器已正确安装且路径已加入系统环境变量。用户可以从引用[1]中提供的链接下载合适的编译器(尽管引用中提供的是riscv64-unknown-elf-gcc和riscv64-unknown-linux-gnu-gcc,而用户使用的是riscv-nuclei-elf-gcc,所以可能用户使用的是Nuclei提供的特定版本)。用户应检查编译器版本是否与项目要求一致。2.**检查编译选项**:根据引用[2]中提到的GCC编译步骤和常用选项,用户需要检查项目的编译选项是否正确。特别注意-march(指令集架构)和-mabi(应用二进制接口)选项是否与目标MCU匹配。例如,对于RISC-V核,需要指定正确的架构,如rv32imac等。3.**链接脚本**:链接问题常常源于链接脚本(通常是.ld文件)配置错误。需要确认链接脚本中的内存区域(MEMORY)定义(如FLASH和RAM的起始地址和大小)是否与目标MCU的内存映射一致。4.**启动文件**:确认启动文件(通常是一个.S汇编文件)是否正确,启动文件负责初始化堆栈指针、数据段等。确保启动文件与目标设备匹配,并且正确编译和链接。5.**库文件**:确保链接了必要的库文件。如果项目使用了标准库(如newlib)或特定的设备库,需要确认这些库的路径是否正确,并在链接选项中包含。6.**具体错误信息**:用户的问题中未给出具体的错误信息,因此建议用户根据具体的构建或链接错误信息进行排查。常见的错误包括未定义的引用(undefinedreference)、内存区域溢出、无法打开链接脚本等。7.**参考项目配置**:BLE_APP_UART项目可能是一个特定的示例项目(如基于Nuclei的某个开发板),建议参考该项目的官方文档或示例配置,确保构建环境设置正确。8.**调试信息**:根据引用[2]中提到,可以使用-g选项生成调试信息,这有助于调试链接问题,但注意这通常不会影响链接过程,而是影响生成的可调试性。由于用户的问题中没有给出具体的错误信息,我们只能提供一般性的解决步骤。同时,如果用户使用的是Nuclei提供的SDK,那么可能需要检查SDK的配置是否正确。根据引用[3],用户可能在开发一个操作系统,但具体到BLE_APP_UART项目,我们推测这更可能是一个蓝牙低功耗应用的示例项目,而非操作系统内核。因此,解决方案可能涉及外设库的链接。下面我们以结构化的方式回答用户的问题:</think> 针对RISC-V MCU BLE_APP_UART项目的构建和链接问题,以下是系统性解决方案: ### 一、常见问题原因分析 1. **编译器配置错误** - 需确认`riscv-nuclei-elf-gcc`路径在`PATH`环境变量中,可通过终端执行`which riscv-nuclei-elf-gcc`验证路径是否正确[^2] - 检查交叉编译器版本兼容性(如目标MCU为RV32IMAC架构时需匹配`-march=rv32imac`) 2. **链接脚本问题** - 链接脚本`.ld`文件需正确配置内存映射: ``` MEMORY { FLASH (rx) : ORIGIN = 0x20000000, LENGTH = 256K RAM (xrw) : ORIGIN = 0x80000000, LENGTH = 64K } ``` - 需确认`.text`/`.data`等段的起始地址与芯片手册一致 3. **缺失依赖项** - 关键启动文件缺失(如`startup_riscv.S`) - BLE协议栈库未链接(检查`-l`参数是否包含`ble_stack.a`等) ### 二、解决方案步骤 1. **基础环境检查** ```bash # 验证编译器 riscv-nuclei-elf-gcc --version # 输出应为 Nuclei RISC-V GCC 版本号 ``` - 若报错,需重新安装[Nuclei SDK](https://github.com/Nuclei-Software/nuclei-sdk)并配置环境变量[^1] 2. **构建命令修正** ```bash riscv-nuclei-elf-gcc \ -march=rv32imac -mabi=ilp32 \ -T nucleartos_linker.ld \ src/main.c drivers/ble_driver.c \ -lble_stack -lc -lnosys \ -o ble_app_uart.elf ``` - `-march`/`-mabi`须匹配目标芯片指令集 - `-T`指定链接脚本路径(建议用绝对路径) - `-l`链接BLE协议栈库 3. **内存冲突排查** 使用`riscv-nuclei-elf-size ble_app_uart.elf`检查内存占用: ``` text data bss dec hex filename 42352 1256 4096 47684 ba44 ble_app_uart.elf ``` - 若`text+data`超过FLASH大小需优化代码 - 若`bss`超过RAM大小需调整堆栈 4. **启动文件验证** 确保编译包含启动文件(示例Makefile): ```makefile STARTUP_FILE := nuclei-sdk/SoC/gd32vf103/Common/startup_gd32vf103.S OBJS += $(STARTUP_FILE) ``` ### 三、调试技巧 1. **反汇编分析** ```bash riscv-nuclei-elf-objdump -D ble_app_uart.elf > disasm.s ``` - 检查`_start`入口点是否指向正确的复位向量 - 确认中断向量表(Vector Table)地址对齐 2. **链接脚本关键项** ```ld ENTRY(_start) /* 必须指定入口函数 */ PROVIDE(__stack_init = .); /* 堆栈初始位置 */ .isr_vector : { *(.isr_vector) } > FLASH ``` ### 四、常见错误解决 | 错误信息 | 解决方案 | |-------------------------------|----------------------------| | `undefined reference to main` | 检查启动文件是否定义了`_start`→`main`调用链 | | `.RAM overflow by 512 bytes` | 增大链接脚本中RAM长度或优化全局变量 | | `cannot open -lble_stack` | 添加库搜索路径`-L/path/to/lib` | | `illegal instruction` | 检查`-march`是否包含MCU所有扩展(如`Zicsr`)|
阅读全文

相关推荐

09:02:49 **** Incremental Build of configuration Debug for project ov2640_AI **** make -j20 all arm-none-eabi-gcc "../Core/Src/ov2640_gpio.c" -mcpu=cortex-m3 -std=gnu11 -g3 -DDEBUG -DUSE_HAL_DRIVER -DSTM32F103xB -c -I../Core/Inc -I../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I../Drivers/STM32F1xx_HAL_Driver/Inc -I../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I../Drivers/CMSIS/Include -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Core/Src/ov2640_gpio.d" -MT"Core/Src/ov2640_gpio.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "Core/Src/ov2640_gpio.o" ../Core/Src/ov2640_gpio.c: In function 'OV2640_GPIO_Init': ../Core/Src/ov2640_gpio.c:125:7: warning: implicit declaration of function 'OV2640_LoadConfig' [-Wimplicit-function-declaration] 125 | if (OV2640_LoadConfig(hov, ov2640_init_regs) != HAL_OK) return HAL_ERROR; | ^~~~~~~~~~~~~~~~~ ../Core/Src/ov2640_gpio.c: At top level: ../Core/Src/ov2640_gpio.c:140:26: error: conflicting types for 'OV2640_LoadConfig'; have 'HAL_StatusTypeDef(OV2640_GPIO_HandleTypeDef *, const uint8_t (*)[2])' {aka 'HAL_StatusTypeDef(OV2640_GPIO_HandleTypeDef *, const unsigned char (*)[2])'} 140 | static HAL_StatusTypeDef OV2640_LoadConfig(OV2640_GPIO_HandleTypeDef *hov, const uint8_t (*regs)[2]) { | ^~~~~~~~~~~~~~~~~ ../Core/Src/ov2640_gpio.c:125:7: note: previous implicit declaration of 'OV2640_LoadConfig' with type 'int()' 125 | if (OV2640_LoadConfig(hov, ov2640_init_regs) != HAL_OK) return HAL_ERROR; | ^~~~~~~~~~~~~~~~~ make: *** [Core/Src/subdir.mk:40: Core/Src/ov2640_gpio.o] Error 1 "make -j20 all" terminated with exit code 2. Build might be incomplete. 09:02:49 Build Failed. 2 errors, 1 warnings. (took 216ms)

/****************************************************************************** ** Include Files ******************************************************************************/ #include "system_init.h" #include "Board_GpioCfg.h" /****************************************************************************** ** Private variables ******************************************************************************/ static volatile uint32_t TimingDelay; /******************************************************************************* ** Global Functions *******************************************************************************/ /** * @brief :Initialize the different GPIO ports * @param[in] None * @param[out] None * @retval :None */ // 配置 PORTB 为输出模式 GPIOB->DDR |= 0xFF; // 设置前8位为输出 GPIOB->DR &= ~0xFF; // 设置输出低电平 // 使能 GPIOA 和 GPIOB 的时钟 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN; void BoardGpioInit(void) { GPIO_Write_Mode_Bits(GPIOA_SFR, GPIO_PIN_MASK_0, GPIO_MODE_OUT); //LED1 GPIO_Write_Mode_Bits(GPIO_SFR, GPIO_PIN_MASK_11, GPIO_MODE_OUT); //LED2 } /** * @brief: Delay time * @param[in] nms * @param[out] None * @retval : None */ void delay_ms(volatile uint32_t nms) { volatile uint32_t i, j; for (i = 0; i < nms; i++) { j = 5000; while (j--) ; } } /******************************************************************************* ** Main Functions *******************************************************************************/ int main() { /* Initialize the system clock is 72MHz*/ SystemInit(72U); /* Setup SysTick Timer as delay function, and input frequency is 72MHz */ systick_delay_init(72U); /* Initialize the user IOs */ BoardGpioInit(); while (1) { //GPIO_Write_Mode_Bits(GPIOA_SFR, GPIO_PIN_MASK_3, GPIO_MODE_OUT); //LED1 //GPIO_Write_Mode_Bits(GPIOF_SFR, GPIO_PIN_MASK_11, GPIO_MODE_OUT); //LED2 //GPIO_Write_Mode_Bits(GPIOA_SFR, GPIO_PIN_MASK_3, GPIO_MODE_OUT); GPIO_Set_Output_Data_Bits(GPIOA_SFR, GPIO_PIN_MASK_3, Bit_SET);//LED1 H //GPIO_Write_Mode_Bits(GPIOF_SFR, GPIO_PIN_MASK_11, GPIO_MODE_OUT); GPIO_Set_Output_Data_Bits(GPIOF_SFR, GPIO_PIN_MASK_11, Bit_SET);//LED1 H systick_delay_ms(200); //GPIO_Write_Mode_Bits(GPIOA_SFR, GPIO_PIN_MASK_3, GPIO_MODE_OUT); GPIO_Set_Output_Data_Bits(GPIOA_SFR, GPIO_PIN_MASK_3, Bit_RESET);//LED1 L //GPIO_Write_Mode_Bits(GPIOF_SFR, GPIO_PIN_MASK_11, GPIO_MODE_OUT); GPIO_Set_Output_Data_Bits(GPIOF_SFR, GPIO_PIN_MASK_11, Bit_SET);//LED2 H systick_delay_ms(200); //GPIO_Write_Mode_Bits(GPIOA_SFR, GPIO_PIN_MASK_3, GPIO_MODE_OUT); GPIO_Set_Output_Data_Bits(GPIOA_SFR, GPIO_PIN_MASK_3, Bit_SET);//LED1 H //GPIO_Write_Mode_Bits(GPIOF_SFR, GPIO_PIN_MASK_11, GPIO_MODE_OUT); GPIO_Set_Output_Data_Bits(GPIOF_SFR, GPIO_PIN_MASK_11, Bit_RESET);//LED2 L systick_delay_ms(200); //GPIO_Write_Mode_Bits(GPIOA_SFR, GPIO_PIN_MASK_3, GPIO_MODE_OUT); GPIO_Set_Output_Data_Bits(GPIOA_SFR, GPIO_PIN_MASK_3, Bit_RESET);//LED1 L //GPIO_Write_Mode_Bits(GPIOF_SFR, GPIO_PIN_MASK_11, GPIO_MODE_OUT); GPIO_Set_Output_Data_Bits(GPIOF_SFR, GPIO_PIN_MASK_11, Bit_RESET);//LED2 L systick_delay_ms(200); } } /** * @brief : Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param[in] file pointer to the source file name * @param[in] line assert_param error line source number * @param[out] None * @retval :None */ void check_failed(uint8_t *File, uint32_t Line) { /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* Infinite loop */ while (1) { ; } }; 这段代码编译显示10:14:13 **** Incremental Build of configuration Debug for project GPIO_InputOutput **** gmake -j16 all Building file: ../main.c ../main.c:22:11: error: expected '=', ',', ';', 'asm' or '__attribute__' before '->' token ../main.c:23:11: error: expected '=', ',', ';', 'asm' or '__attribute__' before '->' token ../main.c:26:9: error: expected '=', ',', ';', 'asm' or '__attribute__' before '->' token ../main.c: In function 'BoardGpioInit': ../main.c:31:28: error: 'GPIO_SFR' undeclared (first use in this function) ../main.c:31:28: note: each undeclared identifier is reported only once for each function it appears in gmake: *** [subdir.mk:26: main.o] Error 1 "gmake -j16 all" terminated with exit code 2. Build might be incomplete. 10:14:14 Build Failed. 5 errors, 0 warnings. (took 642ms) 修改正确发给我

09:18:14 **** Incremental Build of configuration Debug for project ov2640_AI **** make -j20 all arm-none-eabi-gcc "../Core/Src/main.c" -mcpu=cortex-m3 -std=gnu11 -g3 -DDEBUG -DUSE_HAL_DRIVER -DSTM32F103xB -c -I../Core/Inc -I../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I../Drivers/STM32F1xx_HAL_Driver/Inc -I../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I../Drivers/CMSIS/Include -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Core/Src/main.d" -MT"Core/Src/main.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "Core/Src/main.o" arm-none-eabi-gcc -o "ov2640_AI.elf" @"objects.list" -mcpu=cortex-m3 -T"C:\Users\l\STM32CubeIDE\workspace_1.19.0\ov2640_AI\STM32F103C8TX_FLASH.ld" --specs=nosys.specs -Wl,-Map="ov2640_AI.map" -Wl,--gc-sections -static --specs=nano.specs -mfloat-abi=soft -mthumb -Wl,--start-group -lc -lm -Wl,--end-group C:/ST/STM32CubeIDE_1.19.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.13.3.rel1.win32_1.0.0.202411081344/tools/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/bin/ld.exe: ov2640_AI.elf section .bss' will not fit in region RAM' C:/ST/STM32CubeIDE_1.19.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.13.3.rel1.win32_1.0.0.202411081344/tools/bin/../lib/gcc/arm-none-eabi/13.3.1/../../../../arm-none-eabi/bin/ld.exe: region RAM' overflowed by 57984 bytes collect2.exe: error: ld returned 1 exit status make: *** [makefile:64: ov2640_AI.elf] Error 1 "make -j20 all" terminated with exit code 2. Build might be incomplete. 09:18:14 Build Failed. 3 errors, 0 warnings. (took 338ms)

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @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 "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include <stdio.h> #include <string.h> /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ #define UART_PRINTF(fmt, ...) do { \ char __buf[256]; \ sprintf(__buf, fmt, ##__VA_ARGS__); \ HAL_UART_Transmit(&huart2, (uint8_t*)__buf, strlen(__buf), HAL_MAX_DELAY); \ } while(0) /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ typedef struct { uint32_t LastValue; uint32_t NextValue; uint32_t Difference; }CountValue; /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ CountValue CountValue1; uint32_t now_CountValue=0; float TargetSpeed=30; float n=0; float PID_Speed=0; /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ float Incremental_KP=0.15; float Incremental_KI=0.2; float Incremental_KD=0; float myIncremental_PID(float Now,float Target) { static float Bias,LastBias,Last_LastBias,P_out,I_out,D_out,Total_out,Results; Bias=Target-Now; P_out=Incremental_KP*(Bias-LastBias); I_out=Incremental_KI*Bias; D_out=Incremental_KD*(Bias-2*LastBias+Last_LastBias); Total_out=P_out+I_out+D_out; Results+=Total_out; Last_LastBias=LastBias; LastBias=Bias; if(Results>80) Results=80; if(Results<30) Results=30; return Results; } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM2_Init(); MX_TIM3_Init(); MX_TIM4_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL);//开启编码器模式 HAL_TIM_Base_Start_IT(&htim2); //HAL_TIM_Base_Start(&htim2); HAL_TIM_Base_Start_IT(&htim4);//开启定时中断 //HAL_TIM_Base_Start(&htim4); HAL_TIM_Base_Start_IT(&htim3); // HAL_TIM_Base_Start(&htim3); HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);//开启pwm模式 while (1) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); PID_Speed=myIncremental_PID(n,120); __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,(int)PID_Speed); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //GET_NUM(); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ //float n=0;//转速,单位为:转/秒 int32_t encoder_counter = 0;//STM32编码器模式读取的总脉冲数 static int32_t encoder_last_value = 0; /** * @function: void GET_NUM(void) * @description: 使用STM32编码器模式,读取编码器产生的脉冲值 * @param {*} * @return {*} */ void GET_NUM(void) { // 获取当前32位计数值(处理溢出) uint32_t current_count = __HAL_TIM_GET_COUNTER(&htim2); // 计算增量(处理定时器溢出) if(current_count < encoder_last_value) { // 发生上溢(65535 → 0) encoder_counter = (65535 - encoder_last_value) + current_count; } else { encoder_counter = current_count - encoder_last_value; } encoder_last_value = current_count; // 更新上次值 } /** * @function:void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) * @description: 定时器中断回调函数,0.1S中断一次并计算转速,编码器产生的脉冲数 * @param {TIM_HandleTypeDef *htim} * @return {*} */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim==&htim4) { GET_NUM();//得到所记录的脉冲数 n=(float)encoder_counter/2400/0.1;//转速为n,r/s UART_PRINTF("%d\n",encoder_counter); } } /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ 我想要这串代码计算每分钟多少转

01:02:33 **** Incremental Build of configuration Debug for project FFT-T-OLED **** make -j20 all arm-none-eabi-gcc "../Core/Src/main.c" -mcpu=cortex-m3 -std=gnu11 -g3 -DDEBUG -DUSE_HAL_DRIVER -DSTM32F103xB -c -I../Core/Inc -I../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I../Drivers/STM32F1xx_HAL_Driver/Inc -I../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I../Drivers/CMSIS/Include -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Core/Src/main.d" -MT"Core/Src/main.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "Core/Src/main.o" ../Core/Src/main.c: In function 'main': ../Core/Src/main.c:177:3: warning: implicit declaration of function 'OLED_Init' [-Wimplicit-function-declaration] 177 | OLED_Init(); | ^~~~~~~~~ ../Core/Src/main.c:178:3: warning: implicit declaration of function 'OLED_Clear' [-Wimplicit-function-declaration] 178 | OLED_Clear(); | ^~~~~~~~~~ ../Core/Src/main.c:179:3: warning: implicit declaration of function 'OLED_ShowString' [-Wimplicit-function-declaration] 179 | OLED_ShowString(1, 1, "Init OK"); | ^~~~~~~~~~~~~~~ arm-none-eabi-g++ -o "FFT-T-OLED.elf" @"objects.list" -mcpu=cortex-m3 -T"D:\DianSai\FFT-T-OLED\STM32F103C8TX_FLASH.ld" -Wl,-Map="FFT-T-OLED.map" -Wl,--gc-sections -static --specs=nano.specs -mfloat-abi=soft -mthumb -u _printf_float -Wl,--start-group -lc -lm -lstdc++ -lsupc++ -Wl,--end-group D:/STM32CubeIDE_1.17.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.12.3.rel1.win32_1.1.0.202410251130/tools/bin/../lib/gcc/arm-none-eabi/12.3.1/../../../../arm-none-eabi/bin/ld.exe: ./Core/Src/main.o: in function FFT': D:/DianSai/FFT-T-OLED/Debug/../Core/Src/main.c:67: multiple definition of FFT'; ./Core/Src/FFT.o:D:/DianSai/FFT-T-OLED/Debug/../Core/Src/FFT.c:13: first defined here collect2.exe: error: ld returned 1 exit status make: *** [makefile:88: FFT-T-OLED.elf] Error 1 "make -j20 all" terminated with exit code 2. Build might be incomplete. 01:02:37 Build Failed. 2 errors, 3 warnings. (took 4s.239ms)

10:45:52 **** Incremental Build of configuration Debug for project test1 **** make -j12 all arm-none-eabi-gcc "../Core/Src/main.c" -mcpu=cortex-m3 -std=gnu11 -g3 -DDEBUG -DUSE_HAL_DRIVER -DSTM32F103xB -c -I../Core/Inc -I../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I../Drivers/STM32F1xx_HAL_Driver/Inc -I../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I../Drivers/CMSIS/Include -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Core/Src/main.d" -MT"Core/Src/main.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "Core/Src/main.o" arm-none-eabi-gcc "../Core/Src/n3310.c" -mcpu=cortex-m3 -std=gnu11 -g3 -DDEBUG -DUSE_HAL_DRIVER -DSTM32F103xB -c -I../Core/Inc -I../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy -I../Drivers/STM32F1xx_HAL_Driver/Inc -I../Drivers/CMSIS/Device/ST/STM32F1xx/Include -I../Drivers/CMSIS/Include -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Core/Src/n3310.d" -MT"Core/Src/n3310.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "Core/Src/n3310.o" ../Core/Src/n3310.c: In function 'LcdInit': ../Core/Src/n3310.c:25:27: error: 'GPIO_InitTypeDef' has no member named 'GPIO_Pin' 25 | GPIO_InitStructure.GPIO_Pin = (LCD_DC_PIN | LCD_CE_PIN | LCD_RST_PIN | LCD_SDIN_PIN | LCD_SCLK_PIN); | ^ ../Core/Src/n3310.c:26:27: error: 'GPIO_InitTypeDef' has no member named 'GPIO_Mode' 26 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; | ^ ../Core/Src/main.c: In function 'main': ../Core/Src/main.c:106:9: warning: implicit declaration of function 'GPIO_PinRemapConfig' [-Wimplicit-function-declaration] 106 | GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); | ^~~~~~~~~~~~~~~~~~~ ../Core/Src/n3310.c:26:40: error: 'GPIO_Mode_Out_PP' undeclared (first use in this function); did you mean 'GPIO_MODE_AF_PP'? 26 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

#include "balance.h" #include "TrackModule.h" int Time_count=0; //Time variable //计时变量 u8 Lidar_Detect = Lidar_Detect_ON; //电磁巡线模式雷达检测障碍物,默认开启 int RC_Lidar_Avoid_FLAG = RC_Avoid_OFF; //遥控模式雷达避障,默认关闭 u8 Mode; float RC_Velocity_CCD=350,RC_Velocity_ELE=350; float PS2_Velocity,PS2_Turn_Velocity; //遥控控制的速度 Encoder OriginalEncoder; //Encoder raw data //编码器原始数据 u8 Lidar_Success_Receive_flag=0,Lidar_flag_count=0;//雷达在线标志位,雷达掉线计数器 //粗延时函数,微秒 void delay1_us(u16 time) { u16 i=0; while(time--) { i=10; //自己定义 while(i--) ; } } //毫秒级的延时 void delay1_ms(u16 time) { u16 i=0; while(time--) { i=12000; //自己定义 while(i--) ; } } /************************************************************************** Function: The inverse kinematics solution is used to calculate the target speed of each wheel according to the target speed of three axes Input : X and Y, Z axis direction of the target movement speed Output : none 函数功能:运动学逆解,根据三轴目标速度计算各车轮目标转速 入口参数:X和Y、Z轴方向的目标运动速度 返回 值:无 **************************************************************************/ void Drive_Motor(float Vx,float Vy,float Vz) { float amplitude=3.5; //Wheel target speed limit //车轮目标速度限幅 // if((Mode == PS2_Control_Mode||Mode == APP_Control_Mode)&&RC_Lidar_Avoid_FLAG&&Lidar_Success_Receive_flag)//app以及ps2控制下,判断是否开启遥控避障 // { // RC_Lidar_Aviod(&Vx,&Vy,&Vz); // } // //Speed smoothing is enabled when moving the omnidirectional trolley // //全向移动小车才开启速度平滑处理 // if(Car_Mode==Mec_Car||Car_Mode==Omni_Car) // { // Smooth_control(Vx,Vy,Vz); //Smoothing the input speed //对输入速度进行平滑处理 // // //Get the smoothed data // //获取平滑处理后的数据 // Vx=smooth_control.VX; // Vy=smooth_control.VY; // Vz=smooth_control.VZ; // } // // //Mecanum wheel car // //麦克纳姆轮小车 // if (Car_Mode==Mec_Car) // { // //Inverse kinematics //运动学逆解 // MOTOR_A.Target = +Vy+Vx-Vz*(Axle_spacing+Wheel_spacing); // MOTOR_B.Target = -Vy+Vx-Vz*(Axle_spacing+Wheel_spacing); // MOTOR_C.Target = +Vy+Vx+Vz*(Axle_spacing+Wheel_spacing); // MOTOR_D.Target = -Vy+Vx+Vz*(Axle_spacing+Wheel_spacing); // // //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 // MOTOR_A.Target=target_limit_float(MOTOR_A.Target,-amplitude,amplitude); // MOTOR_B.Target=target_limit_float(MOTOR_B.Target,-amplitude,amplitude); // MOTOR_C.Target=target_limit_float(MOTOR_C.Target,-amplitude,amplitude); // MOTOR_D.Target=target_limit_float(MOTOR_D.Target,-amplitude,amplitude); // } // // //Omni car // //全向轮小车 // else if (Car_Mode==Omni_Car) // { // //Inverse kinematics //运动学逆解 // MOTOR_A.Target = Vy + Omni_turn_radiaus*Vz; // MOTOR_B.Target = -X_PARAMETER*Vx - Y_PARAMETER*Vy + Omni_turn_radiaus*Vz; // MOTOR_C.Target = +X_PARAMETER*Vx - Y_PARAMETER*Vy + Omni_turn_radiaus*Vz; // // //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 // MOTOR_A.Target=target_limit_float(MOTOR_A.Target,-amplitude,amplitude); // MOTOR_B.Target=target_limit_float(MOTOR_B.Target,-amplitude,amplitude); // MOTOR_C.Target=target_limit_float(MOTOR_C.Target,-amplitude,amplitude); // MOTOR_D.Target=0; //Out of use //没有使用到 // } // // //Ackermann structure car // //阿克曼小车 // else if (Car_Mode==Akm_Car) // { // //Ackerman car specific related variables //阿克曼小车专用相关变量 // float R, Ratio=636.56, AngleR, Angle_Servo; // // // For Ackerman small car, Vz represents the front wheel steering Angle // //对于阿克曼小车Vz代表右前轮转向角度 // AngleR=Vz; // R=Axle_spacing/tan(AngleR)-0.5f*Wheel_spacing; // // // Front wheel steering Angle limit (front wheel steering Angle controlled by steering engine), unit: rad // //前轮转向角度限幅(舵机控制前轮转向角度),单位:rad // AngleR=target_limit_float(AngleR,-0.49f,0.32f); // // //Inverse kinematics //运动学逆解 // if(AngleR!=0) // { // MOTOR_A.Target = Vx*(R-0.5f*Wheel_spacing)/R; // MOTOR_B.Target = Vx*(R+0.5f*Wheel_spacing)/R; // } // else // { // MOTOR_A.Target = Vx; // MOTOR_B.Target = Vx; // } // // The PWM value of the servo controls the steering Angle of the front wheel // //舵机PWM值,舵机控制前轮转向角度 // //Angle_Servo = -0.628f*pow(AngleR, 3) + 1.269f*pow(AngleR, 2) - 1.772f*AngleR + 1.573f; // Angle_Servo = -0.628f*pow(AngleR, 3) + 1.269f*pow(AngleR, 2) - 1.772f*AngleR + 1.755f; // Servo=SERVO_INIT + (Angle_Servo - 1.755f)*Ratio; // // //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 // MOTOR_A.Target=target_limit_float(MOTOR_A.Target,-amplitude,amplitude); // MOTOR_B.Target=target_limit_float(MOTOR_B.Target,-amplitude,amplitude); // MOTOR_C.Target=0; //Out of use //没有使用到 // MOTOR_D.Target=0; //Out of use //没有使用到 // Servo=target_limit_int(Servo,800,2200); //Servo PWM value limit //舵机PWM值限幅 // } // // //Differential car // //差速小车 // else if (Car_Mode==Diff_Car) // { //Inverse kinematics //运动学逆解 MOTOR_A.Target = Vx - Vz * Wheel_spacing / 2.0f; //计算出左轮的目标速度 MOTOR_B.Target = Vx + Vz * Wheel_spacing / 2.0f; //计算出右轮的目标速度 //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 MOTOR_A.Target=target_limit_float( MOTOR_A.Target,-amplitude,amplitude); MOTOR_B.Target=target_limit_float( MOTOR_B.Target,-amplitude,amplitude); MOTOR_C.Target=0; //Out of use //没有使用到 MOTOR_D.Target=0; //Out of use //没有使用到 // } // // //FourWheel car // //四驱车 // else if(Car_Mode==FourWheel_Car) // { // //Inverse kinematics //运动学逆解 // MOTOR_A.Target = Vx - Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //计算出左轮的目标速度 // MOTOR_B.Target = Vx - Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //计算出左轮的目标速度 // MOTOR_C.Target = Vx + Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //计算出右轮的目标速度 // MOTOR_D.Target = Vx + Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //计算出右轮的目标速度 // // //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 // MOTOR_A.Target=target_limit_float( MOTOR_A.Target,-amplitude,amplitude); // MOTOR_B.Target=target_limit_float( MOTOR_B.Target,-amplitude,amplitude); // MOTOR_C.Target=target_limit_float( MOTOR_C.Target,-amplitude,amplitude); // MOTOR_D.Target=target_limit_float( MOTOR_D.Target,-amplitude,amplitude); // } // // //Tank Car // //履带车 // else if (Car_Mode==Tank_Car) // { // //Inverse kinematics //运动学逆解 // MOTOR_A.Target = Vx - Vz * (Wheel_spacing) / 2.0f; //计算出左轮的目标速度 // MOTOR_B.Target = Vx + Vz * (Wheel_spacing) / 2.0f; //计算出右轮的目标速度 // // //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 // MOTOR_A.Target=target_limit_float( MOTOR_A.Target,-amplitude,amplitude); // MOTOR_B.Target=target_limit_float( MOTOR_B.Target,-amplitude,amplitude); // MOTOR_C.Target=0; //Out of use //没有使用到 // MOTOR_D.Target=0; //Out of use //没有使用到 // } } /************************************************************************** Function: FreerTOS task, core motion control task Input : none Output : none 函数功能:FreeRTOS任务,核心运动控制任务 入口参数:无 返回 值:无 **************************************************************************/ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static u8 Count_CCD = 0; //调节CCD控制频率 static u8 last_mode = 0; if(htim->Instance==TIM12) { // //TIM12->SR &= ~(0x01); // //Time count is no longer needed after 30 seconds // //时间计数,30秒后不再需要 // if(Time_count<3000)Time_count++; // //Get the encoder data, that is, the real time wheel speed, // //and convert to transposition international units // //获取编码器数据,即车轮实时速度,并转换位国际单位 // Get_Velocity_Form_Encoder(); //// if(++Lidar_flag_count==10) Lidar_Success_Receive_flag=0,Lidar_flag_count=0,RC_Lidar_Avoid_FLAG=RC_Avoid_OFF; //100ms清零雷达接收到数据标志位 //// switch(User_Key_Scan()) //// { //// case 1: //单击用来切换模式 //// Mode++; //// if(Mode == ELE_Line_Patrol_Mode) //// { //// ELE_ADC_Init(); //初始化电磁巡线模式 //// } ////// //// else if(Mode == CCD_Line_Patrol_Mode) //CCD巡线模式 //// { //// CCD_Init(); //CCD初始化,CCD模块和电磁巡线模块共用一个接口,两个不能同时使用 //// } //// else if(Mode > 6) //5种模式循环切换 //// { //// Mode = 0; //// } //// break; //// case 2: //电磁巡线状态时,双击可以打开/关闭雷达检测障碍物,默认打开 //// if(Mode == ELE_Line_Patrol_Mode) //电磁巡线控制模式下 //// { //// Lidar_Detect = !Lidar_Detect; //// if(Lidar_Detect == Lidar_Detect_OFF) //// memset(Dataprocess,0, sizeof(PointDataProcessDef)*225); //用于雷达检测障碍物的数组清零 //// }else if(Mode == APP_Control_Mode||Mode ==PS2_Control_Mode){ //// RC_Lidar_Avoid_FLAG=!RC_Lidar_Avoid_FLAG; //// if(RC_Lidar_Avoid_FLAG == RC_Avoid_OFF) //// memset(Dataprocess,0, sizeof(PointDataProcessDef)*225); //用于雷达检测障碍物的数组清零 //// } //// break; //// } //// if(last_mode != Mode) //每次切换模式需要刷新屏幕 //// { //// last_mode++; //// OLED_Clear(); //// if(last_mode>6) //// last_mode = 0; //// } //// //// if(Mode != ELE_Line_Patrol_Mode) //// Buzzer_Alarm(0); //// if(Mode == APP_Control_Mode) Get_RC(); //Handle the APP remote commands //处理APP遥控命令 //// else if(Mode == PS2_Control_Mode) PS2_Control(); //Handle PS2 controller commands //处理PS2手柄控制命令 //// else if(Mode == Lidar_Avoid_Mode) Lidar_Avoid(); //Avoid Mode //避障模式 //// else if(Mode == Lidar_Follow_Mode) Lidar_Follow(); //Follow Mode //跟随模式 //// else if(Mode == Lidar_Along_Mode) Lidar_along_wall(); //Along Mode //走直线模式 //// else if(Mode == ELE_Line_Patrol_Mode) //// { //// //// //// Get_RC_ELE(); //ELE模式 ////// delay1_ms(2); //// } //// else //CCD模式 //// { //// if(++Count_CCD == 2) //调节控制频率,4*5 = 20ms控制一次 //// { //// Count_CCD = 0; //// //// Get_RC_CCD(); //// } //// else if(Count_CCD>2) //// Count_CCD = 0; //// } // // // IRDM_line_inspection_V2(); // Drive_Motor(Move_X,Move_Y,Move_Z); // // // //If there is no abnormity in the battery voltage, and the enable switch is in the ON position, // //and the software failure flag is 0 // //如果电池电压不存在异常,而且使能开关在ON档位,而且软件失能标志位为0 // if(Turn_Off(Voltage)==0) // { // //Speed closed-loop control to calculate the PWM value of each motor, // //PWM represents the actual wheel speed // //速度闭环控制计算各电机PWM值,PWM代表车轮实际转速 // MOTOR_A.Motor_Pwm=Incremental_PI_A(MOTOR_A.Encoder, MOTOR_A.Target); // MOTOR_B.Motor_Pwm=Incremental_PI_B(MOTOR_B.Encoder, MOTOR_B.Target); // MOTOR_C.Motor_Pwm=Incremental_PI_C(MOTOR_C.Encoder, MOTOR_C.Target); // MOTOR_D.Motor_Pwm=Incremental_PI_D(MOTOR_D.Encoder, MOTOR_D.Target); // Limit_Pwm(16500) ; // //Set different PWM control polarity according to different car models // //根据不同小车型号设置不同的PWM控制极性 // switch(Car_Mode) // { // case Mec_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, -MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Mecanum wheel car //麦克纳姆轮小车 // case Omni_Car: Set_Pwm( MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Omni car //全向轮小车 // case Akm_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, Servo); break; //Ackermann structure car //阿克曼小车 // case Diff_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Differential car //两轮差速小车 // case FourWheel_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, -MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //FourWheel car //四驱车 // case Tank_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Tank Car //履带车 // } // } // //If Turn_Off(Voltage) returns to 1, the car is not allowed to move, and the PWM value is set to 0 // //如果Turn_Off(Voltage)返回值为1,不允许控制小车进行运动,PWM值设置为0 // else Set_Pwm(0,0,0,0,0); //----------------云台移植测试-------------------// Xianfu_Pwm(); Velocity1=Position_PID_1(Position1,Target1); //舵机PID控制,可以根据目标位置进行速度调整,离目标位置越远速度越快 Velocity2=Position_PID_2(Position2,Target2); //舵机PID控制,可以根据目标位置进行速度调整,离目标位置越远速度越快 Xianfu_Velocity(); Set_Pwm_PTZ(Velocity1,Velocity2); //赋值给PWM寄存器 } } /************************************************************************** Function: Assign a value to the PWM register to control wheel speed and direction Input : PWM Output : none 函数功能:赋值给PWM寄存器,控制车轮转速与方向 入口参数:PWM 返回 值:无 **************************************************************************/ void Set_Pwm(int motor_a,int motor_b,int motor_c,int motor_d,int servo) { if(motor_a<0) PWMA2=16800,PWMA1=16800+motor_a; else PWMA1=16800,PWMA2=16800-motor_a; if(motor_b<0) PWMB1=16800,PWMB2=16800+motor_b; else PWMB2=16800,PWMB1=16800-motor_b; if(motor_c<0) PWMC1=16800,PWMC2=16800+motor_c; else PWMC2=16800,PWMC1=16800-motor_c; if(motor_d<0) PWMD2=16800,PWMD1=16800+motor_d; else PWMD1=16800,PWMD2=16800-motor_d; //Servo control //舵机控制 Servo_PWM = servo; } /************************************************************************** Function: Limit PWM value Input : Value Output : none 函数功能:限制PWM值 入口参数:幅值 返回 值:无 **************************************************************************/ void Limit_Pwm(int amplitude) { MOTOR_A.Motor_Pwm=target_limit_float(MOTOR_A.Motor_Pwm,-amplitude,amplitude); MOTOR_B.Motor_Pwm=target_limit_float(MOTOR_B.Motor_Pwm,-amplitude,amplitude); MOTOR_C.Motor_Pwm=target_limit_float(MOTOR_C.Motor_Pwm,-amplitude,amplitude); MOTOR_D.Motor_Pwm=target_limit_float(MOTOR_D.Motor_Pwm,-amplitude,amplitude); } /************************************************************************** Function: Limiting function Input : Value Output : none 函数功能:限幅函数 入口参数:幅值 返回 值:无 **************************************************************************/ float target_limit_float(float insert,float low,float high) { if (insert < low) return low; else if (insert > high) return high; else return insert; } int target_limit_int(int insert,int low,int high) { if (insert < low) return low; else if (insert > high) return high; else return insert; } /************************************************************************** Function: Check the battery voltage, enable switch status, software failure flag status Input : Voltage Output : Whether control is allowed, 1: not allowed, 0 allowed 函数功能:检查电池电压、使能开关状态、软件失能标志位状态 入口参数:电压 返回 值:是否允许控制,1:不允许,0允许 **************************************************************************/ u8 Turn_Off( int voltage) { u8 temp; if(voltage<10||EN==0||Flag_Stop==1) { temp=1; } else temp=0; return temp; } /************************************************************************** Function: Calculate absolute value Input : long int Output : unsigned int 函数功能:求绝对值 入口参数:long int 返回 值:unsigned int **************************************************************************/ u32 myabs(long int a) { u32 temp; if(a<0) temp=-a; else temp=a; return temp; } /************************************************************************** Function: Incremental PI controller Input : Encoder measured value (actual speed), target speed Output : Motor PWM According to the incremental discrete PID formula pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)] e(k) represents the current deviation e(k-1) is the last deviation and so on PWM stands for incremental output In our speed control closed loop system, only PI control is used pwm+=Kp[e(k)-e(k-1)]+Ki*e(k) 函数功能:增量式PI控制器 入口参数:编码器测量值(实际速度),目标速度 返回 值:电机PWM 根据增量式离散PID公式 pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)] e(k)代表本次偏差 e(k-1)代表上一次的偏差 以此类推 pwm代表增量输出 在我们的速度控制闭环系统里面,只使用PI控制 pwm+=Kp[e(k)-e(k-1)]+Ki*e(k) **************************************************************************/ int Incremental_PI_A (float Encoder,float Target) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //计算偏差 Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias; if(Pwm>16800)Pwm=16800; if(Pwm<-16800)Pwm=-16800; Last_bias=Bias; //Save the last deviation //保存上一次偏差 return Pwm; } int Incremental_PI_B (float Encoder,float Target) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //计算偏差 Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias; if(Pwm>16800)Pwm=16800; if(Pwm<-16800)Pwm=-16800; Last_bias=Bias; //Save the last deviation //保存上一次偏差 return Pwm; } int Incremental_PI_C (float Encoder,float Target) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //计算偏差 Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias; if(Pwm>16800)Pwm=16800; if(Pwm<-16800)Pwm=-16800; Last_bias=Bias; //Save the last deviation //保存上一次偏差 return Pwm; } int Incremental_PI_D (float Encoder,float Target) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //计算偏差 Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias; if(Pwm>16800)Pwm=16800; if(Pwm<-16800)Pwm=-16800; Last_bias=Bias; //Save the last deviation //保存上一次偏差 return Pwm; } /************************************************************************** Function: Processes the command sent by APP through usart 2 Input : none Output : none 函数功能:对APP通过串口2发送过来的命令进行处理 入口参数:无 返回 值:无 **************************************************************************/ void Get_RC(void) { u8 Flag_Move=1; if(Car_Mode==Mec_Car||Car_Mode==Omni_Car) //The omnidirectional wheel moving trolley can move laterally //全向轮运动小车可以进行横向移动 { switch(Flag_Direction) //Handle direction control commands //处理方向控制命令 { case 1: Move_X=RC_Velocity; Move_Y=0; Flag_Move=1; break; case 2: Move_X=RC_Velocity; Move_Y=-RC_Velocity; Flag_Move=1; break; case 3: Move_X=0; Move_Y=-RC_Velocity; Flag_Move=1; break; case 4: Move_X=-RC_Velocity; Move_Y=-RC_Velocity; Flag_Move=1; break; case 5: Move_X=-RC_Velocity; Move_Y=0; Flag_Move=1; break; case 6: Move_X=-RC_Velocity; Move_Y=RC_Velocity; Flag_Move=1; break; case 7: Move_X=0; Move_Y=RC_Velocity; Flag_Move=1; break; case 8: Move_X=RC_Velocity; Move_Y=RC_Velocity; Flag_Move=1; break; default: Move_X=0; Move_Y=0; Flag_Move=0; break; } if(Flag_Move==0) { //If no direction control instruction is available, check the steering control status //如果无方向控制指令,检查转向控制状态 if (Flag_Left ==1) Move_Z= PI/2*(RC_Velocity/500); //left rotation //左自转 else if(Flag_Right==1) Move_Z=-PI/2*(RC_Velocity/500); //right rotation //右自转 else Move_Z=0; //stop //停止 } } else //Non-omnidirectional moving trolley //非全向移动小车 { switch(Flag_Direction) //Handle direction control commands //处理方向控制命令 { case 1: Move_X=+RC_Velocity; Move_Z=0; break; case 2: Move_X=+RC_Velocity; Move_Z=-PI/2; break; case 3: Move_X=0; Move_Z=-PI/2; break; case 4: Move_X=-RC_Velocity; Move_Z=-PI/2; break; case 5: Move_X=-RC_Velocity; Move_Z=0; break; case 6: Move_X=-RC_Velocity; Move_Z=+PI/2; break; case 7: Move_X=0; Move_Z=+PI/2; break; case 8: Move_X=+RC_Velocity; Move_Z=+PI/2; break; default: Move_X=0; Move_Z=0; break; } if (Flag_Left ==1) Move_Z= PI/2; //left rotation //左自转 else if(Flag_Right==1) Move_Z=-PI/2; //right rotation //右自转 } //Z-axis data conversion //Z轴数据转化 if(Car_Mode==Akm_Car) { //Ackermann structure car is converted to the front wheel steering Angle system target value, and kinematics analysis is pearformed //阿克曼结构小车转换为前轮转向角度 Move_Z=Move_Z*2/9; } else if(Car_Mode==Diff_Car||Car_Mode==Tank_Car||Car_Mode==FourWheel_Car) { if(Move_X<0) Move_Z=-Move_Z; //The differential control principle series requires this treatment //差速控制原理系列需要此处理 Move_Z=Move_Z*RC_Velocity/500; } //Unit conversion, mm/s -> m/s //单位转换,mm/s -> m/s Move_X=Move_X/1000; Move_Y=Move_Y/1000; Move_Z=Move_Z; //Control target value is obtained and kinematics analysis is performed //得到控制目标值,进行运动学分析 Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** Function: Read the encoder value and calculate the wheel speed, unit m/s Input : none Output : none 函数功能:读取编码器数值并计算车轮速度,单位m/s 入口参数:无 返回 值:无 **************************************************************************/ void Get_Velocity_Form_Encoder(void) { //Retrieves the original data of the encoder //获取编码器的原始数据 float Encoder_A_pr,Encoder_B_pr,Encoder_C_pr,Encoder_D_pr; OriginalEncoder.A=Read_Encoder(2); OriginalEncoder.B=Read_Encoder(3); OriginalEncoder.C=Read_Encoder(4); OriginalEncoder.D=Read_Encoder(5); //Decide the encoder numerical polarity according to different car models //根据不同小车型号决定编码器数值极性 switch(Car_Mode) { case Mec_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=OriginalEncoder.B; Encoder_C_pr= -OriginalEncoder.C; Encoder_D_pr= -OriginalEncoder.D; break; case Omni_Car: Encoder_A_pr=-OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B; Encoder_C_pr= -OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; case Akm_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B; Encoder_C_pr= OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; case Diff_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B; Encoder_C_pr= OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; case FourWheel_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=OriginalEncoder.B; Encoder_C_pr= -OriginalEncoder.C; Encoder_D_pr= -OriginalEncoder.D; break; case Tank_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr= -OriginalEncoder.B; Encoder_C_pr= OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; } //The encoder converts the raw data to wheel speed in m/s //编码器原始数据转换为车轮速度,单位m/s MOTOR_A.Encoder= Encoder_A_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_B.Encoder= Encoder_B_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_C.Encoder= Encoder_C_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_D.Encoder= Encoder_D_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; } /************************************************************************** Function: Smoothing the three axis target velocity Input : Three-axis target velocity Output : none 函数功能:对三轴目标速度做平滑处理 入口参数:三轴目标速度 返回 值:无 **************************************************************************/ void Smooth_control(float vx,float vy,float vz) { float step=0.05; if (vx>0) smooth_control.VX+=step; else if(vx<0) smooth_control.VX-=step; else if(vx==0) smooth_control.VX=smooth_control.VX*0.9f; if (vy>0) smooth_control.VY+=step; else if(vy<0) smooth_control.VY-=step; else if(vy==0) smooth_control.VY=smooth_control.VY*0.9f; if (vz>0) smooth_control.VZ+=step; else if(vz<0) smooth_control.VZ-=step; else if(vz==0) smooth_control.VZ=smooth_control.VZ*0.9f; smooth_control.VX=target_limit_float(smooth_control.VX,-float_abs(vx),float_abs(vx)); smooth_control.VY=target_limit_float(smooth_control.VY,-float_abs(vy),float_abs(vy)); smooth_control.VZ=target_limit_float(smooth_control.VZ,-float_abs(vz),float_abs(vz)); } /************************************************************************** Function: Floating-point data calculates the absolute value Input : float Output : The absolute value of the input number 函数功能:浮点型数据计算绝对值 入口参数:浮点数 返回 值:输入数的绝对值 **************************************************************************/ float float_abs(float insert) { if(insert>=0) return insert; else return -insert; } /************************************************************************** Function: PS2_Control Input : none Output : none 函数功能:PS2手柄控制 入口参数: 无 返回 值:无 **************************************************************************/ void PS2_Control(void) { int LY,RX,LX; //手柄ADC的值 int Threshold=20; //阈值,忽略摇杆小幅度动作 static float Key1_Count = 0,Key2_Count = 0; //用于控制读取摇杆的速度 //转化为128到-128的数值 LY=-(PS2_LY-128);//左边Y轴控制前进后退 RX=-(PS2_RX-128);//右边X轴控制转向 LX=-(PS2_LX-128);//左边X轴控制转向,麦轮和全向小车专用 if(LY>-Threshold&&LY<Threshold) LY=0; if(RX>-Threshold&&RX<Threshold) RX=0; //忽略摇杆小幅度动作 if(LX>-Threshold&&LX<Threshold) LX=0; if(Strat) //按下start键才可以控制小车 { if (PS2_KEY == PSB_L1) //按下左1键加速(按键在顶上) { if((++Key1_Count) == 20) //调节按键反应速度 { PS2_KEY = 0; Key1_Count = 0; if((PS2_Velocity += X_Step)>MAX_RC_Velocity) //前进最大速度1230 PS2_Velocity = MAX_RC_Velocity; if(Car_Mode != Akm_Car) //非阿克曼车可调节转向速度 { if((PS2_Turn_Velocity += Z_Step)>MAX_RC_Turn_Bias) //转向最大速度325 PS2_Turn_Velocity = MAX_RC_Turn_Bias; } } } else if(PS2_KEY == PSB_R1) //按下右1键减速 { if((++Key2_Count) == 15) { PS2_KEY = 0; Key2_Count = 0; if((PS2_Velocity -= X_Step)<MINI_RC_Velocity) //前后最小速度110 PS2_Velocity = MINI_RC_Velocity; if(Car_Mode != Akm_Car) //非阿克曼车可调节转向速度 { if((PS2_Turn_Velocity -= Z_Step)<MINI_RC_Turn_Velocity)//转向最小速度45 PS2_Turn_Velocity = MINI_RC_Turn_Velocity; } } } else Key2_Count = 0,Key2_Count = 0; //读取到其他按键重新计数 Move_X = (PS2_Velocity/128)*LY; //速度控制,力度表示速度大小 if(Car_Mode == Mec_Car || Car_Mode == Omni_Car) { Move_Y = LX*PS2_Velocity/128; } else { Move_Y = 0; } if(Car_Mode == Akm_Car) //阿克曼车转向控制,力度表示转向角度 Move_Z = RX*(PI/2)/128*2/9; else //其他车型转向控制 { //if(Move_X>=0) Move_Z = (PS2_Turn_Velocity/128)*RX; //转向控制,力度表示转向速度 // else // Move_Z = -(PS2_Turn_Velocity/128)*RX; } } else { Move_X = 0; Move_Y = 0; Move_Z = 0; } Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** 函数功能:CCD巡线,采集3个电感的数据并提取中线 入口参数:无 返回 值:无 **************************************************************************/ void Get_RC_CCD(void) { static float Bias,Last_Bias; float move_z=0; Move_X=RC_Velocity_CCD; //CCD巡线模式线速度 Bias=CCD_Median-64; //提取偏差,64为巡线的中心点 if(Car_Mode == Omni_Car) move_z=-Bias*Omni_Car_CCD_KP*0.1f-(Bias-Last_Bias)*Omni_Car_CCD_KI*0.1f; //PD控制,原理就是使得小车保持靠近巡线的中心点 else if(Car_Mode == Tank_Car) move_z=-Bias*Tank_Car_CCD_KP*0.1f-(Bias-Last_Bias)*Tank_Car_CCD_KI*0.1f; else move_z=-Bias*CCD_KP*0.1f-(Bias-Last_Bias)*CCD_KI*0.1f; Last_Bias=Bias; //保存上一次的偏差 if(Car_Mode==Mec_Car) { Move_Z=move_z*RC_Velocity_CCD/50000; //差速控制原理需要经过此处处理 } else if(Car_Mode==Omni_Car) { Move_Z=move_z*RC_Velocity_CCD/21000; //差速控制原理需要经过此处处理 } else if(Car_Mode==Akm_Car) { Move_Z=move_z/450; //差速控制原理需要经过此处处理 } else if(Car_Mode==Diff_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_CCD/67000; //差速控制原理需要经过此处处理 } else if(Car_Mode==Tank_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_CCD/50000; //差速控制原理需要经过此处处理 } else if(Car_Mode==FourWheel_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_CCD/20100; //差速控制原理需要经过此处处理 } //Z-axis data conversion //Z轴数据转化 //Unit conversion, mm/s -> m/s //单位转换,mm/s -> m/s Move_X=Move_X/1000; Move_Z=Move_Z; Move_Y=0; //Control target value is obtained and kinematics analysis is performed //得到控制目标值,进行运动学分析 Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** 函数功能:电磁巡线,采集3个电感的数据并提取中线 入口参数:无 返回 值:无 **************************************************************************/ void Get_RC_ELE(void) { static float Bias,Last_Bias; float move_z=0; if(Detect_Barrier() == No_Barrier) { Move_X=RC_Velocity_ELE; //电磁巡线模式的速度 Bias=100-Sensor; //提取偏差 if(Car_Mode == Omni_Car) move_z=-Bias* Omni_Car_ELE_KP*0.08f-(Bias-Last_Bias)* Omni_Car_ELE_KI*0.05f; else if(Car_Mode == Tank_Car) move_z=-Bias*Tank_Car_ELE_KP*0.1f-(Bias-Last_Bias)*Tank_Car_ELE_KI*0.1f; else move_z=-Bias* ELE_KP*0.08f-(Bias-Last_Bias)* ELE_KI*0.05f; Last_Bias=Bias; Buzzer_Alarm(0); if(Car_Mode==Mec_Car) { Move_Z=move_z*RC_Velocity_ELE/50000; //差速控制原理需要经过此处处理 } else if(Car_Mode==Omni_Car) { Move_Z=move_z*RC_Velocity_ELE/10800; //差速控制原理需要经过此处处理 } else if(Car_Mode==Diff_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_ELE/45000; //差速控制原理需要经过此处处理 } else if(Car_Mode==Tank_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_ELE/28000; //差速控制原理需要经过此处处理 } else if(Car_Mode==FourWheel_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_ELE/20100; //差速控制原理需要经过此处处理 } else if(Car_Mode==Akm_Car) { Move_Z=move_z/450; //差速控制原理需要经过此处处理 } } else //有障碍物 { Buzzer_Alarm(100); //当电机使能的时候,有障碍物则蜂鸣器报警 Move_X = 0; Move_Z = 0; Move_Y = 0; } //Z-axis data conversion //Z轴数据转化 //Unit conversion, mm/s -> m/s //单位转换,mm/s -> m/s Move_X=Move_X/1000; Move_Z=Move_Z; Move_Y=0; //Control target value is obtained and kinematics analysis is performed //得到控制目标值,进行运动学分析 Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** 函数功能:检测前方是否有障碍物 入口参数:无 返回 值:无 **************************************************************************/ u8 Detect_Barrier(void) { u16 i; u16 point_count = 0; if(Lidar_Detect == Lidar_Detect_ON) { for(i=0;i<1152;i++) //检测是否有障碍物 { if((Dataprocess[i].angle>300)||(Dataprocess[i].angle<60)) { if(0<Dataprocess[i].distance&&Dataprocess[i].distance<700)//700mm内是否有障碍物 point_count++; } } if(point_count > 0)//有障碍物 return Barrier_Detected; else return No_Barrier; } else return No_Barrier; } /************************************************************************** 函数功能:小车避障模式 入口参数:无 返回 值:无 **************************************************************************/ void Lidar_Avoid(void) { u16 i = 0; u8 calculation_angle_cnt = 0; //用于判断225个点中需要做避障的点 float angle_sum = 0; //粗略计算障碍物位于左或者右 u8 distance_count = 0; //距离小于某值的计数 for(i=0;i<1152;i++) //遍历120度范围内的距离数据,共120个点左右的数据 { if((Dataprocess[i].angle>300)||(Dataprocess[i].angle<60)) //避障角度在300-60之间 { if((0<Dataprocess[i].distance)&&(Dataprocess[i].distance<Avoid_Distance)) //距离小于450mm需要避障,只需要120度范围内点 { calculation_angle_cnt++; //计算距离小于避障距离的点个数 if(Dataprocess[i].angle<60) angle_sum += Dataprocess[i].angle; else if(Dataprocess[i].angle>300) angle_sum += (Dataprocess[i].angle-360); //300度到60度转化为-60度到60度 if(Dataprocess[i].distance<Avoid_Min_Distance) //记录小于200mm的点的计数 distance_count++; } } } Move_X = forward_velocity; if(calculation_angle_cnt == 0)//不需要避障 { Move_Z = 0; } else //当距离小于200mm,小车往后退 { if(distance_count>8) { Move_X = -forward_velocity; Move_Z = 0; } else { Move_X = 0; if(angle_sum > 0)//障碍物偏右 { if(Car_Mode == Mec_Car) //麦轮转弯需要把前进速度降低 Move_X = 0; else //其他车型保持原有车速 Move_X = forward_velocity; if(Car_Mode == Akm_Car) Move_Z = PI/4; else if(Car_Mode == Omni_Car) Move_Z=corner_velocity; else Move_Z=other_corner_velocity;//左转 } else //偏左 { if(Car_Mode == Mec_Car) Move_X = 0; else Move_X = forward_velocity; if(Car_Mode == Akm_Car) Move_Z = -PI/4; else if(Car_Mode == Omni_Car) Move_Z=-corner_velocity;//右转 else Move_Z=-other_corner_velocity; } } } Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** 函数功能:小车跟随模式 入口参数:无 返回 值:无 **************************************************************************/ void Lidar_Follow(void) { static u16 cnt = 0; int i; int calculation_angle_cnt = 0; static float angle = 0; //避障的角度 static float last_angle = 0; // u16 mini_distance = 65535; static u8 data_count = 0; //用于滤除一写噪点的计数变量 //需要找出跟随的那个点的角度 for(i = 0; i < 1152; i++) { if((0<Dataprocess[i].distance)&&(Dataprocess[i].distance<Follow_Distance)) { calculation_angle_cnt++; if(Dataprocess[i].distance<mini_distance) { mini_distance = Dataprocess[i].distance; angle = Dataprocess[i].angle; } } } if(angle > 180) //0--360度转换成0--180;-180--0(顺时针) angle -= 360; if((angle-last_angle > 10)||(angle-last_angle < -10)) //做一定消抖,波动大于10度的需要做判断 { if(++data_count > 30) //连续30次采集到的值(300ms后)和上次的比大于10度,此时才是认为是有效值 { data_count = 0; last_angle = angle; } } else //波动小于10度的可以直接认为是有效值 { if(++data_count > 10) //连续10次采集到的值(100ms后),此时才是认为是有效值 { data_count = 0; last_angle = angle; } } if(calculation_angle_cnt < 8) //跟随距离小于8且当cnt>40的时候,认为在1600内没有跟随目标 { if(cnt < 40) cnt++; if(cnt >= 40) { Move_X = 0; Move_Z = 0; } } else { cnt = 0; if(Move_X > 0.06f || Move_X < -0.06f) //当Move_X有速度时,转向PID开始调整 { if(mini_distance < 700 && (last_angle > 60 || last_angle < -60)) { Move_Z = -0.0098f*last_angle; //当距离偏小且角度差距过大直接快速转向 } else { Move_Z = -Follow_Turn_PID(last_angle,0); //转向PID,车头永远对着跟随物品 } } else { Move_Z = 0; } if(angle>150 || angle<-150) //如果小车在后方60°需要反方向运动以及快速转弯 { Move_X = -Distance_Adjust_PID(mini_distance, Keep_Follow_Distance); Move_Z = -0.0098f*last_angle; } else { Move_X = Distance_Adjust_PID(mini_distance, Keep_Follow_Distance); //保持距离保持在500mm } Move_X = target_limit_float(Move_X,-amplitude_limiting,amplitude_limiting); //对前进速度限幅 } Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** 函数功能:小车走直线模式 入口参数:无 返回 值:无 **************************************************************************/ void Lidar_along_wall(void) { static int determine; static u32 target_distance=0; u32 distance; u8 data_count = 0; //用于滤除一写噪点的计数变量 Move_X = forward_velocity; //初始速度 for(int j=0;j<1152;j++) //225 { if(Dataprocess[j].angle>268 && Dataprocess[j].angle<272) //取雷达的4度的点 { if(determine300||Dataprocess[i].angle<60) {//车前避障角度 300°-60° if(0<Dataprocess[i].distance&&Dataprocess[i].distance<avoid_distance) { avoid_point_count++;//需要避障的点数 if(Dataprocess[i].angle<60) { right_angle_sum+=Dataprocess[i].angle; } else if(Dataprocess[i].angle>300) { left_angle_sum+=(Dataprocess[i].angle-360);//300°到359°转化为-60°到-1° } if(Dataprocess[i].distance<Avoid_Min_Distance) {//小于最小避障距离 mini_distance_count++; } } } } if(*Vx>0&&avoid_point_count>8){//需要避障 if(mini_distance_count>=8)//距离小于最小转弯距离要求 { *Vx=*Vx*-0.75f;//后退 *Vz=0; } else { if(right_angle_sum+left_angle_sum>0) {//往左避让 *Vx=*Vx*0.5f;//避障的X轴速度是前进速度的一半 if(Car_Mode==Akm_Car){ *Vz=(PI/2)*4/9; }else{ *Vz=1; } }else{//往右避让 *Vx=*Vx*0.5f;//避障的X轴速度是前进速度的一半 if(Car_Mode==Akm_Car){ *Vz=(-PI/2)*4/9; }else{ *Vz=-1; } } } } } /************************************************************************** 函数功能:限制PWM赋值 入口参数:无 返回 值:无 **************************************************************************/ void Xianfu_Pwm(void) { int Amplitude_H1=1200, Amplitude_L1=300; //舵机脉宽极限值,即限制舵机转角13.5°-256.5°中心位置为750,运动范围为±450,(750-250)/90=5.5,5.5*80=450 int Amplitude_H2=1200, Amplitude_L2=300; //舵机脉宽极限值,即限制舵机转角9°-171°,中心位置为750,运动范围为±450,(750-250)/90=5.5,5.5*80=450 if(Target1<Amplitude_L1) Target1=Amplitude_L1; if(Target1>Amplitude_H1) Target1=Amplitude_H1; if(Target2<Amplitude_L2) Target2=Amplitude_L2; if(Target2>Amplitude_H2) Target2=Amplitude_H2; } /************************************************************************* 函数功能:位置式PID控制器 入口参数:编码器测量位置信息,目标位置 返回 值:电机PWM增量 **************************************************************************/ float Position_PID_1(float Position,float Target) { //增量输出 static float Bias,Pwm,Integral_bias,Last_Bias; Bias=Target-Position; //计算偏差 Integral_bias+=Bias; //求出偏差的积分 Pwm=Position_KP*Bias/100+Position_KI*Integral_bias/100+Position_KD*(Bias-Last_Bias)/100; //位置式PID控制器 Last_Bias=Bias; //保存上一次偏差 return Pwm; } /************************************************************************* 函数功能:位置式PID控制器 入口参数:编码器测量位置信息,目标位置 返回 值:电机PWM增量 **************************************************************************/ float Position_PID_2(float Position,float Target) { //增量输出 static float Bias,Pwm,Integral_bias,Last_Bias; Bias=Target-Position; //计算偏差 Integral_bias+=Bias; //求出偏差的积分 Pwm=Position_KP*Bias/100+Position_KI*Integral_bias/100+Position_KD*(Bias-Last_Bias)/100; //位置式PID控制器 Last_Bias=Bias; //保存上一次偏差 return Pwm; } /************************************************************************** 函数功能:限制速度 入口参数:无 返回 值:无 **************************************************************************/ void Xianfu_Velocity(void) { int Amplitude_H=Speed, Amplitude_L=-Speed; if(Velocity1<Amplitude_L) Velocity1=Amplitude_L; if(Velocity1>Amplitude_H) Velocity1=Amplitude_H; if(Velocity2<Amplitude_L) Velocity2=Amplitude_L; if(Velocity2>Amplitude_H) Velocity2=Amplitude_H; } /************************************************************************** 函数功能:赋值给PWM寄存器,并且判断转向 入口参数:左轮PWM、右轮PWM 返回 值:无 **************************************************************************/ void Set_Pwm_PTZ(float velocity1,float velocity2) { Position1+=velocity1; //速度的积分,得到舵机的位置 Position2+=velocity2; //赋值给STM32的寄存器 // TIM12->CCR1=Position1; //GPIOA8,云台舵机 // TIM12->CCR2=Position2; //GPIOA11,外臂舵机 TIM8->CCR1=Position1; //GPIOC6,云台舵机 TIM8->CCR2=Position2; //GPIOC7,外臂舵机 }看不懂啊,逐行解释

最新推荐

recommend-type

开发界面语义化:声控 + 画图协同生成代码.doc

开发界面语义化:声控 + 画图协同生成代码.doc
recommend-type

Python程序TXLWizard生成TXL文件及转换工具介绍

### 知识点详细说明: #### 1. 图形旋转与TXL向导 图形旋转是图形学领域的一个基本操作,用于改变图形的方向。在本上下文中,TXL向导(TXLWizard)是由Esteban Marin编写的Python程序,它实现了特定的图形旋转功能,主要用于电子束光刻掩模的生成。光刻掩模是半导体制造过程中非常关键的一个环节,它确定了在硅片上沉积材料的精确位置。TXL向导通过生成特定格式的TXL文件来辅助这一过程。 #### 2. TXL文件格式与用途 TXL文件格式是一种基于文本的文件格式,它设计得易于使用,并且可以通过各种脚本语言如Python和Matlab生成。这种格式通常用于电子束光刻中,因为它的文本形式使得它可以通过编程快速创建复杂的掩模设计。TXL文件格式支持引用对象和复制对象数组(如SREF和AREF),这些特性可以用于优化电子束光刻设备的性能。 #### 3. TXLWizard的特性与优势 - **结构化的Python脚本:** TXLWizard 使用结构良好的脚本来创建遮罩,这有助于开发者创建清晰、易于维护的代码。 - **灵活的Python脚本:** 作为Python程序,TXLWizard 可以利用Python语言的灵活性和强大的库集合来编写复杂的掩模生成逻辑。 - **可读性和可重用性:** 生成的掩码代码易于阅读,开发者可以轻松地重用和修改以适应不同的需求。 - **自动标签生成:** TXLWizard 还包括自动为图形对象生成标签的功能,这在管理复杂图形时非常有用。 #### 4. TXL转换器的功能 - **查看.TXL文件:** TXL转换器(TXLConverter)允许用户将TXL文件转换成HTML或SVG格式,这样用户就可以使用任何现代浏览器或矢量图形应用程序来查看文件。 - **缩放和平移:** 转换后的文件支持缩放和平移功能,这使得用户在图形界面中更容易查看细节和整体结构。 - **快速转换:** TXL转换器还提供快速的文件转换功能,以实现有效的蒙版开发工作流程。 #### 5. 应用场景与技术参考 TXLWizard的应用场景主要集中在电子束光刻技术中,特别是用于设计和制作半导体器件时所需的掩模。TXLWizard作为一个向导,不仅提供了生成TXL文件的基础框架,还提供了一种方式来优化掩模设计,提高光刻过程的效率和精度。对于需要进行光刻掩模设计的工程师和研究人员来说,TXLWizard提供了一种有效的方法来实现他们的设计目标。 #### 6. 系统开源特性 标签“系统开源”表明TXLWizard遵循开放源代码的原则,这意味着源代码对所有人开放,允许用户自由地查看、修改和分发软件。开源项目通常拥有活跃的社区,社区成员可以合作改进软件,添加新功能,或帮助解决遇到的问题。这种开放性促进了技术创新,并允许用户根据自己的需求定制软件。 #### 7. 压缩包子文件的文件名称列表 文件名称列表中的“txlwizard-master”可能指的是TXLWizard项目的主版本库或主分支。这个名称表明了这是项目源代码的中心点,其他开发者会从这个主分支拉取代码进行合作开发或部署。以“-master”结尾通常是版本控制系统中表示主要开发线路的常见约定,例如Git中的master(现在更常被称为main)分支。 通过这些知识点的详细解释,我们可以看到TXLWizard不仅是一个用于生成TXL文件的工具,它还整合了一系列的功能,使得电子束光刻掩模的设计工作更为高效和直观。同时,作为一个开源项目,它能够借助社区的力量不断进步,为用户带来更多的便利和创新。
recommend-type

【创新图生成:扣子平台的技术前沿与创新思维】:引领图像生成技术的新潮流

# 1. 扣子平台的图像生成技术概述 在当今数字化时代,图像生成技术正成为推动技术创新和社会变革的关键力量。扣子平台正是这一技术革新的代表。本章将对扣子平台的图像生成技术进行一个全面概述,探究它如何将理论技术转化为实际应用,并带来显著的社会影响。 扣子平台运用了先进的图像生成技术,将现实世界和数字空间无缝融合,打造了沉浸式的视觉体验。其技术的核心是通过高度复杂的算法模型,快速生成高质
recommend-type

海康威视机器视觉工程师考核

<think>好的,我现在需要帮助用户了解海康威视机器视觉工程师的考核内容和要求。根据用户提供的引用资料,特别是引用[1]和[2],里面提到了考核素材包分为初级和中级,涵盖理论、算法、应用案例等。首先,我要整理这些信息,确保结构清晰,符合用户要求的格式。 接下来,我需要确认素材包的具体内容,比如初级和中级的不同点。引用[2]提到初级包含基础理论、算法实现和实际案例,中级则增加复杂算法和项目分析。这部分需要分点说明,方便用户理解层次。 另外,用户可能想知道如何准备考核,比如下载素材、学习顺序、模拟考核等,引用[2]中有使用说明和注意事项,这部分也要涵盖进去。同时要注意提醒用户考核窗口已关闭,
recommend-type

Linux环境下Docker Hub公共容器映像检测工具集

在给出的知识点中,我们需要详细解释有关Docker Hub、公共容器映像、容器编排器以及如何与这些工具交互的详细信息。同时,我们会涵盖Linux系统下的相关操作和工具使用,以及如何在ECS和Kubernetes等容器编排工具中运用这些检测工具。 ### Docker Hub 和公共容器映像 Docker Hub是Docker公司提供的一项服务,它允许用户存储、管理以及分享Docker镜像。Docker镜像可以视为应用程序或服务的“快照”,包含了运行特定软件所需的所有必要文件和配置。公共容器映像指的是那些被标记为公开可见的Docker镜像,任何用户都可以拉取并使用这些镜像。 ### 静态和动态标识工具 静态和动态标识工具在Docker Hub上用于识别和分析公共容器映像。静态标识通常指的是在不运行镜像的情况下分析镜像的元数据和内容,例如检查Dockerfile中的指令、环境变量、端口映射等。动态标识则需要在容器运行时对容器的行为和性能进行监控和分析,如资源使用率、网络通信等。 ### 容器编排器与Docker映像 容器编排器是用于自动化容器部署、管理和扩展的工具。在Docker环境中,容器编排器能够自动化地启动、停止以及管理容器的生命周期。常见的容器编排器包括ECS和Kubernetes。 - **ECS (Elastic Container Service)**:是由亚马逊提供的容器编排服务,支持Docker容器,并提供了一种简单的方式来运行、停止以及管理容器化应用程序。 - **Kubernetes**:是一个开源平台,用于自动化容器化应用程序的部署、扩展和操作。它已经成为容器编排领域的事实标准。 ### 如何使用静态和动态标识工具 要使用这些静态和动态标识工具,首先需要获取并安装它们。从给定信息中了解到,可以通过克隆仓库或下载压缩包并解压到本地系统中。之后,根据需要针对不同的容器编排环境(如Dockerfile、ECS、Kubernetes)编写配置,以集成和使用这些检测工具。 ### Dockerfile中的工具使用 在Dockerfile中使用工具意味着将检测工具的指令嵌入到构建过程中。这可能包括安装检测工具的命令、运行容器扫描的步骤,以及将扫描结果集成到镜像构建流程中,确保只有通过安全和合规检查的容器镜像才能被构建和部署。 ### ECS与Kubernetes中的工具集成 在ECS或Kubernetes环境中,工具的集成可能涉及到创建特定的配置文件、定义服务和部署策略,以及编写脚本或控制器来自动执行检测任务。这样可以在容器编排的过程中实现实时监控,确保容器编排器只使用符合预期的、安全的容器镜像。 ### Linux系统下的操作 在Linux系统下操作这些工具,用户可能需要具备一定的系统管理和配置能力。这包括使用Linux命令行工具、管理文件系统权限、配置网络以及安装和配置软件包等。 ### 总结 综上所述,Docker Hub上的静态和动态标识工具提供了一种方法来检测和分析公共容器映像,确保这些镜像的安全性和可靠性。这些工具在Linux开发环境中尤为重要,因为它们帮助开发人员和运维人员确保他们的容器映像满足安全要求。通过在Dockerfile、ECS和Kubernetes中正确使用这些工具,可以提高应用程序的安全性,减少由于使用不安全的容器镜像带来的风险。此外,掌握Linux系统下的操作技能,可以更好地管理和维护这些工具,确保它们能够有效地发挥作用。
recommend-type

【扣子平台图像艺术探究:理论与实践的完美结合】:深入学习图像生成的艺术

# 1. 图像艺术的理论基础 艺术领域的每一个流派和技巧都有其理论基础。在图像艺术中,理论基础不仅是对艺术表现形式的认知,也是掌握艺术创作内在逻辑的关键。深入理解图像艺术的理论基础,能够帮助艺术家们在创作过程中更加明确地表达自己的艺术意图,以及更好地与观众沟通。 图像艺术的理论
recommend-type

增广路定理的证明

<think>嗯,用户想查找增广路定理的证明,关于图论和网络流算法的。我得先理解增广路定理是什么,然后找到相关的证明方法。根据之前的引用,尤其是引用5提到最大流最小割定理,里面有三个等价条件,其中第二个是残余网络中没有增广路径时,流就是最大流。这可能和增广路定理相关,也就是当残余网络中没有增广路时,当前流就是最大流,这可能就是增广路定理的内容。 首先,我需要明确增广路定理的陈述。根据引用5,增广路定理可能指的是:一个流是最大流当且仅当残余网络中不存在增广路径。这个定理的证明需要用到最大流最小割定理,也就是第三个条件,即最大流的流量等于最小割的容量。 证明的步骤可能需要分为两个方向:必要性(
recommend-type

Pulse:基于SwiftUI的Apple平台高效日志记录与网络监控

从给定文件信息中,我们可以提取出以下IT知识点进行详细阐述: **Pulse概览:** Pulse是一个专门针对Apple平台(如iOS、iPadOS、macOS等)的功能强大的日志记录系统。其设计目的是为了简化开发者在这些平台上调试网络请求和应用日志的过程。Pulse的核心特色是它使用SwiftUI来构建,这有助于开发者利用现代Swift语言的声明式UI优势来快速开发和维护。 **SwiftUI框架:** SwiftUI是一种声明式框架,由苹果公司推出,用于构建用户界面。与传统的UIKit相比,SwiftUI使用更加简洁的代码来描述界面和界面元素,它允许开发者以声明的方式定义视图和界面布局。SwiftUI支持跨平台,这意味着同一套代码可以在不同的Apple设备上运行,大大提高了开发效率和复用性。Pulse选择使用SwiftUI构建,显示了其对现代化、高效率开发的支持。 **Network Inspector功能:** Pulse具备Network Inspector功能,这个功能使得开发者能够在开发iOS应用时,直接从应用内记录和检查网络请求和日志。这种内嵌式的网络诊断能力非常有助于快速定位网络请求中的问题,如不正确的URL、不返回预期响应等。与传统的需要外部工具来抓包和分析的方式相比,这样的内嵌式工具大大减少了调试的复杂性。 **日志记录和隐私保护:** Pulse强调日志是本地记录的,并保证不会离开设备。这种做法对隐私保护至关重要,尤其是考虑到当前数据保护法规如GDPR等的严格要求。因此,Pulse的设计在帮助开发者进行问题诊断的同时,也确保了用户数据的安全性。 **集成和框架支持:** Pulse不仅仅是一个工具,它更是一个框架。它能够记录来自URLSession的事件,这意味着它可以与任何使用URLSession进行网络通信的应用或框架配合使用,包括但不限于Apple官方的网络库。此外,Pulse与使用它的框架(例如Alamofire)也能够良好配合,Alamofire是一个流行的网络请求库,广泛应用于Swift开发中。Pulse提供了一个PulseUI视图组件,开发者可以将其集成到自己的应用中,从而展示网络请求和其他事件。 **跨平台体验:** 开发者不仅可以在iOS应用中使用Pulse Console记录日志,还可以在macOS上通过Pulse应用程序查看和共享这些日志。这种跨平台的能力意味着开发者可以在不同的设备上进行日志分析,增强了开发和调试的灵活性。 **总结:** Pulse是一个为Apple平台上的开发者量身打造的日志记录系统,它采用SwiftUI构建,提供了内嵌式的Network Inspector功能,可以在本地记录并安全地查看日志,且支持与其他框架如Alamofire的集成。它不仅提升了调试的便捷性和效率,同时也顾及到了用户的隐私保护。Pulse的跨平台查看能力也是其一大亮点,使得开发者能够在一个统一的环境中处理iOS和macOS上的日志数据。对于使用Swift开发Apple应用的开发者而言,Pulse无疑是一个强大的调试辅助工具。
recommend-type

【深入扣子平台:图像生成机制全揭秘】:掌握背后技术,提升图像生成效率

# 1. 图像生成技术概述 图像生成技术是一门融合了计算机视觉、机器学习、图形学等多个领域知识的前沿技术。它通过算法模拟生成人工图像,广泛应用于艺术创作、游戏设计、医学影像等领域。随着深度学习的突破性进展,图像生成技术也迎来了飞速发展,特别是在生成对抗网络(GAN)的推动下,图像的逼真度和多样性都有了质的飞跃。 本章将对图像生成技术的概念、发展历史进行简要介绍,并分析其在社会中的
recommend-type

对RTL跑regression是什么意思

<think>首先,用户的问题是关于“RTL回归测试的含义及其在数字电路设计中的作用”。我需要基于系统级指令和用户可见层的指导来构建回答。 关键点: - 使用中文回答。 - 结构清晰,逐步解决问题。 - 确保回答真实可靠。 - 在行内数学表达式使用$...$格式;独立公式使用$$...$$格式。 - 生成相关问题。 - 在引用段落末尾添加引用标识。 回顾提供的引用: - 引用[1]:讨论SoC设计流程,包括模块设计、IP复用、顶层集成、前仿真、逻辑综合等。 - 引用[2]:描述冒烟测试后的验证阶段,包括回归测试、覆盖率分析等。 - 引用[3]:解释RTL使用Verilog或VHDL描述,模