Compare commits
10 Commits
f3a3953652
...
276fee9352
Author | SHA1 | Date |
---|---|---|
NeoZng | 276fee9352 | |
NeoZng | d764e0f940 | |
NeoZng | 50479fc16c | |
NeoZng | 145e886618 | |
NeoZng | 3139ab11b6 | |
NeoZng | 903698251e | |
NeoZng | 8a476e6ee1 | |
NeoZng | ce90b3852c | |
NeoZng | cb3aeb4063 | |
NeoZng | 668848a1e8 |
17
.Doc/TODO.md
17
.Doc/TODO.md
|
@ -48,6 +48,8 @@
|
|||
|
||||
### 待完成
|
||||
|
||||
Unicom
|
||||
|
||||
- [ ] 为键鼠/遥控器/ps手柄/视觉上位机等各种控制器提供一套统一的接口,把发来数据转化为标准的控制数据,包括底盘速度云台角度等等
|
||||
|
||||
- [ ] 给每个模块增加调试的条件编译,并增加bsp log的输出。或直接在运行时添加log等级,输出不同的信息。
|
||||
|
@ -57,11 +59,14 @@
|
|||
舵机模块,需要预先定义90/180/360连续旋转的电机类型,并且能够设定max和min位置。
|
||||
|
||||
- [x] 编写舵机模块(待测试和优化)
|
||||
- [ ] 可能需要串口舵机的支持?
|
||||
|
||||
#### imu
|
||||
|
||||
- [ ] 完善bmi088模块和算法的交互,添加异步量测更新的SO3上的IEKF
|
||||
|
||||
> 继续使用四元数(S3)也许是一个更好的选择,改动较小且运算开销也较小。后续考虑修改一套ESKF_INS并移植到框架中。
|
||||
|
||||
#### ==master_machine==
|
||||
|
||||
- [ ] 增加IMU数据的时间戳
|
||||
|
@ -69,6 +74,8 @@
|
|||
- [ ] 重构seasky protocol的接口
|
||||
- [ ] 增加数据未更新的处理
|
||||
|
||||
需要一个更简单的协议以加快速度。若有必要,可能需要重新编写一个简单的调试上位机UI。
|
||||
|
||||
|
||||
### 待优化
|
||||
|
||||
|
@ -78,10 +85,20 @@
|
|||
|
||||
当前实现为buzzer是单独的module,若需要蜂鸣器警报的module可以自行包含buzzer.h以创建不同情况下的警报,如电机离线、堵转、遥控器离线等。
|
||||
|
||||
也许还有其他方式提醒离线和异常。
|
||||
|
||||
目前急需一个无线遥控继电器,防止机器人的急停模式失效。
|
||||
|
||||
#### BMI088
|
||||
|
||||
- [ ] 完善和SO3 IEKF的交互,增加异步任务的唤醒和数据传递(IMU中断唤起任务)
|
||||
|
||||
根据BMI088的datasheet在初始标定完成后将gyro和acc都设置为中断触发,当数据准备好时传感器会在对应的引脚输出跳变,通过EXTI捕获跳变并触发中断,在回调函数中启动SPI DMA传输。陀螺仪数据来到时进行姿态的预测即propagation,加速度计数据到来时进行量测更新(correct)。
|
||||
|
||||
也许需要找到一种更好的方式构建INS任务以方便和其他模块、应用的交互。
|
||||
|
||||
另外,若要进一步提升自瞄效果,在姿态得到更新时(陀螺仪和imu的数据到来时),需要额外的引脚连接到相机上完成硬触发采集,以获得更好的时间对齐效果,防止视觉得到的姿态数据发生漂移。目前视觉端假设姿态更新的频率是1khz(当前每次完成姿态解算都会向上位机发送当前的姿态)
|
||||
|
||||
#### remote_control
|
||||
|
||||
- [ ] 增加长按/短按检测 (是否有必要?)
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
|
||||
[TOC]
|
||||
|
||||
> TODO:
|
||||
>
|
||||
> 1. 添加一键编译+启用ozone调试脚本,使得整个进一步流程自动化
|
||||
> 2. 增加更多的背景知识介绍
|
||||
|
||||
## 前言
|
||||
|
||||
了解过嵌入式开发的你一定接触过Keil,这款20世纪风格UI的IDE伴随很多人度过了学习单片机的岁月。然而由于其缺少代码补全、高亮和静态检查的支持,以及为人诟病的一系列逆天的设置、极慢的编译速度(特别是在开发HAL库时),很多开发者开始转向其他IDE。
|
||||
|
@ -42,31 +37,15 @@ CubeMX进行初始化 --> VSCode编写代/进行编译/简单调试 --> Ozone变
|
|||
|
||||
3. C语言基础:[程序设计入门——C语言](https://www.icourse163.org/course/ZJU-199001?from=searchPage&outVendor=zw_mooc_pcssjg_)
|
||||
|
||||
***务必学完以上课程再开始本教程的学习。***
|
||||
***务必学完以上课程再开始本教程的学习,以及后续的开发。***
|
||||
|
||||
万丈高楼不可平地而起,地基不牢只会导致递归学习。
|
||||
|
||||
> 如果有可能,还应该学习:[哈佛大学公开课:计算机科学cs50](https://open.163.com/newview/movie/courseintro?newurl=%2Fspecial%2Fopencourse%2Fcs50.html)。你将会对单片机和计算机有不同的理解。
|
||||
|
||||
## 预备知识
|
||||
|
||||
1. 软件安装(队伍NAS和资料硬盘内提供了所有必要的依赖,安装包和插件,目录是`/EC/VSCode+Ozone环境配置`),请以公共账号登陆网盘,ip地址为`49.123.113.2:5212`,账号`public@rm.cloud`,密码`public`。
|
||||
|
||||
所有安装包也可以在此百度网盘链接下获得:[archive.zip](https://pan.baidu.com/s/1sO_EI4cToyIAcScOQx-JSg?pwd=6666)
|
||||
|
||||
```shell
|
||||
# 网盘中的文件:
|
||||
basic_framework.zip # 本仓库文件,注意,可能不为最新,建议从仓库clone并定时pull
|
||||
daplink_register_license.rar # daplink license注册机
|
||||
gcc-arm-none-eabi-10.3-2021.10-win32.zip # arm-gnu-toolchain
|
||||
JLinkARM.dll # 修改过的jlink运行链接库
|
||||
JLink_Windows_V722b.exe # JLink软件包
|
||||
mingw-get-setup.exe # mingw工具链
|
||||
OpenOCD.zip # OpenOCD
|
||||
Ozone_doc.pdf # Ozone使用手册
|
||||
Ozone_Windows_V324_x86.exe # Ozone安装包
|
||||
VSCodeUserSetup-x64-1.73.1.exe # VSCode安装包
|
||||
```
|
||||
|
||||
2. C语言从源代码到.bin和.hex等机器代码的编译和链接过程
|
||||
1. C语言从源代码到.bin和.hex等机器代码的编译和链接过程
|
||||
|
||||
3. C语言的内存模型
|
||||
|
||||
|
@ -178,6 +157,28 @@ typedef struct
|
|||
|
||||
> ***所有需要编辑的配置文件都已经在basic_framework的仓库中提供,如果不会写,照猫画虎。***
|
||||
|
||||
- **软件安装**
|
||||
|
||||
队伍NAS和资料硬盘内提供了所有必要的依赖,安装包和插件,目录是`/EC/VSCode+Ozone环境配置`,请以公共账号登陆网盘,ip地址为`49.123.113.2:5212`,账号`public@rm.cloud`,密码`public`。
|
||||
|
||||
对于非队内的开发者,我们提供了网盘下载方式。所有安装包也可以在此百度网盘链接下获得:[archive.zip](https://pan.baidu.com/s/1sO_EI4cToyIAcScOQx-JSg?pwd=6666)
|
||||
|
||||
```shell
|
||||
# 网盘中的文件:
|
||||
basic_framework.zip # 本仓库文件,注意为了保证最新,建议从仓库clone并定时pull(或自动fetch)
|
||||
daplink_register_license.rar # daplink license注册机
|
||||
gcc-arm-none-eabi-10.3-2021.10-win32.zip # arm-gnu-toolchain,注意,这个版本太老,编译最新的框架可能出现一些编译参数不支持的情况。请通过Msys2直接安装或到arm官网下载最新的12.x版本。
|
||||
JLinkARM.dll # 修改过的jlink运行链接库
|
||||
JLink_Windows_V722b.exe # JLink软件包
|
||||
mingw-get-setup.exe # mingw工具链(更推荐的方式是使用msys2安装)
|
||||
OpenOCD.zip # OpenOCD
|
||||
Ozone_doc.pdf # Ozone使用手册
|
||||
Ozone_Windows_V324_x86.exe # Ozone安装包
|
||||
VSCodeUserSetup-x64-1.73.1.exe # VSCode安装包
|
||||
# 最佳实践是下载msys2并在mingw64中安装软件包!!!
|
||||
# 如果你喜欢clang,可以使用clang下的arm工具链。
|
||||
```
|
||||
|
||||
> 2022-12-01更新:
|
||||
>
|
||||
> **VSCode上线了一款新的插件:**
|
||||
|
@ -197,8 +198,8 @@ typedef struct
|
|||
- **C/C++**:提供C/C++的调试和代码高亮支持
|
||||
- **Better C++ Syntax**:提供更丰富的代码高亮和智能提示
|
||||
- **C/C++ Snippets**:提供代码块(关键字)补全
|
||||
- **Cortex-Debug**,**Cortex-Debug: Device Support Pack - STM32F4**:提供调试支持。cortex debug还会自动帮助你安装一些调试相关的插件。
|
||||
- **IntelliCode**,**Makfile Tools**:提供代码高亮支持
|
||||
- **Cortex-Debug**,**Cortex-Debug: Device Support Pack - STM32F4**:提供调试支持。cortex debug还会自动帮助你安装一些调试相关的插件,包括RTOS支持和内存查看等。
|
||||
- **IntelliCode**,**Makfile Tools**:提供代码高亮支持。喜欢clang的同学可以使用clangd。
|
||||
|
||||
![image-20221112172157533](../.assets/image-20221112172157533.png)
|
||||
|
||||
|
@ -232,7 +233,9 @@ typedef struct
|
|||
|
||||
打开命令行(win+R,cmd,回车),输入`gcc -v`,如果没有报错,并输出了一堆路径和参数说明安装成功。
|
||||
|
||||
安装完之后,建议将ming的bin文件夹下的ming32-make.exe复制一份,并将copy更名为make.exe
|
||||
安装完之后,建议将ming的bin文件夹下的mingw32-make.exe复制一份,并将copy更名为make.exe
|
||||
|
||||
> 当然,更推荐的方式是将MinGW终端集成到VSCode中,防止类linux环境和Win的环境冲突,特别是你的电脑中安装了其他工具链的时候,如MSVC、LLVM等。
|
||||
|
||||
- 配置gcc-arm-none-eabi环境变量,**把压缩包解压以后放在某个地方**,然后同上,将工具链的bin添加到PATH:(will be deprecated soon,请注意这种方法将会在主分支发布正式版的时候删除)
|
||||
|
||||
|
|
|
@ -4,5 +4,29 @@ Ozone的实时变量可视化监测(示波器)功能可以很好地帮助我们
|
|||
|
||||
## 调试顺序
|
||||
|
||||
先内环,后外环。若有已知的外部扰动如阻力、重力等可以在**保持kp不变**的情况下添加积分环节,并查看达到稳态时积分的输出,该输出值可以作为**前馈**作用通过feedforward_ptr一同送入下一个串级控制器。
|
||||
先内环,后外环。若有已知的外部扰动如阻力、重力等可以在**保持kp不变**的情况下添加积分环节,并查看达到稳态时积分的输出,该输出值可以作为**前馈**作用通过feedforward_ptr一同送入下一个串级控制器。在没有模型的时候,计算前馈一般通过**数据驱动**的方法,如稳态误差-参考值关系拟合。
|
||||
|
||||
经典的经验式整定方法有简单的Ziegler-Nicols法和Cohen-Coon法,还有需要一定的系统辨识的Chien-Hrones-Reswick、Tyreus-Luyben和Skogestad法。这些方法在网络上或提出方法的论文中都有详尽的说明,这里不再赘述。
|
||||
|
||||
我们可以使用上述方法确定一套参数的初值,再根据时域表现进行精细的参数调整。
|
||||
|
||||
以笔者个人的纯调参无模型云台调试经验,可以进行如下步骤:
|
||||
|
||||
1. 首先整定P参数,使用二分法确定大致范围。将电机参考速度和实际速度在Ozone示波器同一窗口中打开以观察时域表现。把pid的ref值添加到变量观测,方便修改(只要修改ref值,每次就可以触发阶跃信号输入)。当P达到欠阻尼且波动只有一个周期(只有超调一个峰)时,开始添加D参数。
|
||||
2. 添加微分参数。务必打开微分滤波器,并根据你的闭环带宽(速度环要跟随的频率)设置滤波系数。目前使用的是一阶低通滤波,后续考虑增加高阶滤波器和特殊的带宽滤波器(切比雪夫、巴特沃斯等)。调节D参数至P进入临界阻尼或过阻尼的状态(没有超调)。
|
||||
3. 交替增大比例系数和微分系数,直到出现不可控的微分抖动,然后减小两者的值直到出现一个合适的平衡(调个大概即可,没有模型的情况下很难达到均衡)
|
||||
4. **固定PD参数**,查看不同目标值的**静差**。如果没有对云台进行建模,则拟合目标值与静差的关系(线性、多项式、指数、对数),并将该值作为前馈加入系统,以补偿PD无法影响的扰动项。
|
||||
5. 增加额外的积分控制以消除前馈模型不准确带来的影响。务必使用积分分离和变速积分。参数不要求精细,一般来说大致即可。
|
||||
|
||||
## model-based控制
|
||||
|
||||
无模型的控制显然存在其劣势和效果上限,为了提高性能,我们需要对云台进行建模。
|
||||
|
||||
建模的方式包括动力学方程推导和数据驱动的系统辨识。
|
||||
|
||||
动力学方程的推导即分析系统的输入(电机的扭矩)和输出(速度/角度)的关系,得到系统动态的微分方程,然后使输入等于微分方程中除输出外的项,从而让输出的动态变为简单的一阶线性常微分方程,以最快的方式到达期望位置。此时再额外增加PID控制器,用于补偿建模得到的系统和实际不匹配造成的误差。建模考虑的因素越全面,建模的效果可能会越好。
|
||||
|
||||
系统辨识的经典方法是伯德图法即频域分析。通过给定一系列的正弦输入,观察系统的输出表现得到系统的相频曲线和幅频特性曲线,并选择一个特定结构的系统对曲线进行拟合(最小二乘或极大似然估计),对得到的系统应用超前-滞后补偿控制器以确定合适的PID参数,在裕度和带宽之间权衡,使得系统的频率响应满足设计要求。
|
||||
|
||||
Ozone可以非常方便的获取系统的频率响应数据。
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
本框架使用stm32cubemx生成,基于makefile编译系统(后期拟修改为cmake+nijna+makefile以提高编译速度,对于目前的版本您可以考虑自行安装ccache以提高编译速度),使用arm gnu工具链开发,利用arm-none-eabi-gcc编译(make命令,命令行为mingw32-make)。
|
||||
|
||||
> 目前已经支持CMakeLists构建。
|
||||
|
||||
> ***==!deprecated==***:若需使用keil5开发,请在stm32cubemx的`project manager`标签页下将工具链改为MDK,然后在keil中自行添加所需包含的.c文件和头文件。关于如何在keil下添加dsplib,请参考文档。在vscode中也有**KEIL assistant**和**Embedded IDE**插件可供使用。
|
||||
>
|
||||
> ***强烈推荐使用VSCode进行开发,Ozone进行调试。***
|
||||
|
@ -110,6 +112,23 @@
|
|||
} CANInstance;
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## 其他IDE支持
|
||||
|
||||
你还可以尝试VSC的EIDE插件+ozone的方式开发ac5/6工具链的程序。ac5/6工具链由于为arm出品的商业闭源软件,对mcu硬件架构的针对性优化更多,编译出来的程序优化效果往往更好、体积更小,执行速度快过arm-gnu工具链。
|
||||
|
||||
另外的可能是Visual Studio + VisualGDB,根据VisualGDB的官网进行配置即可,可以利用VS提供的各种测试调试性能分析功能。对于熟悉VS丰富的功能支持的开发者,可以选择此方式。
|
||||
|
||||
还可以尝试VSC的插件KEIL assistant,通过此法你需要在根目录打开cubeide配置文件,在project manager标签页选择将工具链改为MDK,**我们非常不推荐这种方式**。若要使用CubeIDE,也在相同的地方修改工具链。
|
||||
|
||||
***不过开发本框架的最佳实践仍然是VSCode+Ozone。***
|
||||
|
||||
> 只要你学习了[VSCode+Ozone使用方法](./VSCode+Ozone使用方法.md)文档内的*预备知识* ,掌握了工具链的用途和原理之后,不论换用什么IDE什么编译器,你都可以切换自如。
|
||||
|
||||
|
||||
|
||||
## BSP层(Board Sopport Package)
|
||||
|
||||
- 主要功能:实现对STM HAL的封装功能,进一步抽象硬件。
|
||||
|
@ -190,6 +209,8 @@ ROOT:.
|
|||
│ debug_ozone.jdebug # ozone debug调试配置和缓存文件
|
||||
│ LICENSE # 开源协议文件
|
||||
│ Makefile # 编译管理文件,为make(mingw32-make)命令的目标
|
||||
| Makefile.upgrade # 编译管理文件的升级版, 资深用户使用
|
||||
| CMakeLists.txt # cmake构建规则, 资深用户使用
|
||||
│ openocd_dap.cfg # 用于OpenOCD调试使用的配置文件,dap用
|
||||
│ openocd_jlink.cfg # 用于OpenOCD调试使用的配置文件,jlink用
|
||||
│ README.md # 本说明文档
|
||||
|
@ -198,11 +219,6 @@ ROOT:.
|
|||
│ STM32F407.svd # F407外设地址映射文件,用于调试
|
||||
│ STM32F407IGHx_FLASH.ld # F407IGH(C板MCU)目标FLASH地址和链接规则,用于编译(作为链接阶段的链接器)
|
||||
│ task.ps1 # powershell脚本,一键编译并进入ozone调试/reset开发板用
|
||||
│ TODO.md # 项目待完成的任务
|
||||
│ VSCode+Ozone使用方法.md # 开发环境配置和前置知识介绍
|
||||
│ 修改HAL配置时文件目录的更改.md # 重新配置CubeMX时的步骤和注意事项
|
||||
│ 必须做&禁止做.md # 开发必看,规范和要求
|
||||
│ 如何定位bug.md # 开发必看,快速定位bug并进行修复.还提供了一些debug典例
|
||||
│
|
||||
├─.vscode
|
||||
│ launch.json # 调试的配置文件
|
||||
|
@ -210,14 +226,17 @@ ROOT:.
|
|||
│ tasks.json # 任务配置文件,包括一键编译下载调试等
|
||||
│
|
||||
├─.assets # 说明文档的图片
|
||||
├─.Doc # 本框架的各类说明文档
|
||||
├─.github # github action CI支持文件
|
||||
│
|
||||
├─application # 应用层
|
||||
├─bsp # 板级支持包
|
||||
├─modules # 模块层
|
||||
│
|
||||
├─Src #hal生成的外设初始化源文件
|
||||
├─Inc #hal生成的外设初始化头文件
|
||||
├─Drivers #hal driver和cmsis drivers
|
||||
└─Middlewares # STusb ext , rtos , segger rtt等
|
||||
├─Src # hal生成的外设初始化源文件
|
||||
├─Inc # hal生成的外设初始化头文件
|
||||
├─Drivers # hal driver和cmsis drivers
|
||||
└─Middlewares # STusb ext, rtos, segger rtt等
|
||||
```
|
||||
|
||||
## BSP/Module/Application介绍
|
||||
|
|
|
@ -6,29 +6,72 @@ VSCode的各种配置如快捷键、高亮颜色、主题、界面形状和位
|
|||
|
||||
## 快捷键
|
||||
|
||||
不管使用什么软件,基本的快捷键一定可以帮助你提高效率,更专注于当前正在做的事——减少双手离开主键盘区的频率和时间。
|
||||
|
||||
常用的快捷键已经在[VSCode的基础操作](VSCode+Ozone使用方法.md)中介绍,这里推荐一些shortcuts的组合,可以直接`ctrl+k&s`打开快捷键设置或进入`setting.json`手动修改。当然,你不一定要和我们的建议完全一致,**根据自己的习惯定制才是最好的!**
|
||||
|
||||
- `ctrl+;`设置为移动到行尾,同时保留`end`,这样在输完函数参数之后不用按`→`或使用鼠标
|
||||
- `alt+j/k`设置为向左向右移动,这样在修改的时候不用按方向键;可以设置组合键达到”移动到符号末尾”的功能实现按符号或单词移动
|
||||
- `tab`改为移动到下一个建议(智能提示),`enter`设置为接受当前建议。如果只有一个建议,`tab`直接接受;`alt+tab`移动到上一个建议。
|
||||
- 设置一个组合键用于选中当前单词
|
||||
- 记住当前文件查找和全局查找的组合,最好再学习一下**正则表达式**。还有”选中当前所有出现“,可以快速定位当前文件有哪些地方用了这个变量/函数。
|
||||
- 熟练使用`ctrl+tab`切换当前已经打开的文件
|
||||
- 可以在`setting.json`中将`task.json`中的任务绑定成你需要的快捷键,比如默认`ctrl+shift+b`是构建任务。把一些常用的命令行操作写成task,通过快捷键瞬间触发。
|
||||
- 善用右键菜单中的peek xxx功能、展开调用层级和头文件/源文件跳转,以及git查看文件差异的功能,这在理清代码结构、查看之前的更改的时候非常有用。当然,不是让你按右键,是使用快捷键。如果你的键盘上有一个像文件一样的图标,当你聚焦在编辑区的时候可以按下它来替代右键(展开菜单)。
|
||||
- more...
|
||||
|
||||
|
||||
|
||||
|
||||
## 提高效率
|
||||
|
||||
|
||||
*若你使用触控板和笔记本电脑,也可以利用触控板实现一些实在找不到的快捷键功能。*一开始你可能会决定快捷键难记又难按,当你熟练之后,却会在无形之中极大提升你的效率。
|
||||
|
||||
|
||||
|
||||
## 代码高亮
|
||||
|
||||
虽然VSCode默认的语法高亮已经吊打KEIL和CubeIDE之流,但你是否发现变量全是浅蓝色,字符串、数值字面量内部也无法区分?无出其右,我们可以自定义各种entity的颜色!
|
||||
|
||||
建议让copilot协助你完成,打开`setting.json`开始设置吧:
|
||||
|
||||
- global、private member、public member、local设置为不同的颜色
|
||||
- 私有函数和公开函数设置为不同的颜色
|
||||
- static函数设置特殊的颜色?
|
||||
- 函数参数变量设置不同的颜色?
|
||||
|
||||
|
||||
|
||||
## 终端工具
|
||||
|
||||
你至少应该学习bash(msys2中会集成一套类linux的bash工具)的基础指令,如果有可能,也学习一下powershell。在chatGPT或者Copilot的帮助下,这易如反掌。当它们无能为力时请务必查阅**官方文档**,而不是在CSDN中💩里淘🪙。
|
||||
|
||||
为你的终端配置好看的颜色、字体和背景,但不要花太多时间。一定要让你的终端具有补全和历史记录记忆功能。
|
||||
|
||||
学会在命令行中使用一些常用的git操作,虽然vscode中已经提供了强大的图形化集成。
|
||||
|
||||
务必学习vi/vim/nano的基础操作。
|
||||
|
||||
熟悉在终端中创建/追加/移动/复制/删除文件和文件夹。
|
||||
|
||||
学习gnu工具链的简单指令,协助你与编译报错信息一起定位bug和错误(常常是undefined reference或未定义函数、未定义就使用)。
|
||||
|
||||
也许,了解一下shell的工作原理,或者更多,现代操作系统的运行架构?
|
||||
|
||||
> 为basic_framework通过串口/SWD建立一套简单的cmd?有很多开源代码的实现,期待你的PR!
|
||||
|
||||
|
||||
|
||||
## 进一步提高效率
|
||||
|
||||
首先,快捷键一定要按的顺手,最好能让双手处于标准指法的起始状态就可以轻松触发。一定不要超过三个键,否则按起来手掌大开大合。
|
||||
|
||||
建议打开函数的自动括号补全(一般在对应语言的插件设置中寻找),同时习惯snippets的使用,并根据自己的需要自定义一些方便的snippets。
|
||||
|
||||
如果你认为vim很酷,那它确实很酷。vscode中有fake-vim插件,喜欢折腾的话,vim会让你在双手始终保持在键盘区,劈里啪啦地编码,只不过学习成本较高,曲线复杂。
|
||||
|
||||
你最好升级一下默认的shell,windows下有好看的posh,linux则有广为流传的zsh。具体如何配置,自行查阅**官方文档**。
|
||||
|
||||
记住,最好将不同软件开发使用的环境隔离开,所以你需要把msys2集成到vscode中!(macOS和linux用户忽略)
|
||||
|
||||
把开发中常用但又繁琐的流程自动化,积累一套你自己的脚本库。
|
||||
|
||||
....
|
||||
|
||||
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 8.1 MiB |
Binary file not shown.
After Width: | Height: | Size: 8.7 MiB |
|
@ -17,4 +17,9 @@ jobs:
|
|||
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: make -j12
|
||||
run: make -j12
|
||||
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
- name: ninja
|
||||
run: cd build && cmake -G Ninja .. && ninja
|
||||
|
|
@ -51,5 +51,4 @@ build
|
|||
./idea
|
||||
.vscode/.cortex-debug.peripherals.state.json
|
||||
.vscode/.cortex-debug.registers.state.json
|
||||
*.jdebug*
|
||||
settings.json
|
||||
*.jdebug*
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"robot_def.h": "c",
|
||||
"bsp_dwt.h": "c",
|
||||
"dji_motor.h": "c",
|
||||
"message_center.h": "c",
|
||||
"super_cap.h": "c",
|
||||
"can_comm.h": "c",
|
||||
"lqr.h": "c",
|
||||
"math.h": "c",
|
||||
"stdint.h": "c",
|
||||
"general_def.h": "c",
|
||||
"lk9025.h": "c",
|
||||
"arm_math.h": "c",
|
||||
"bmi088driver.h": "c",
|
||||
"bmi088middleware.h": "c",
|
||||
"bmi088_regndef.h": "c",
|
||||
"bmi088reg.h": "c",
|
||||
"balance.h": "c",
|
||||
"stdlib.h": "c",
|
||||
"memory.h": "c",
|
||||
"bsp_usart.h": "c",
|
||||
"compare": "c",
|
||||
"limits": "c",
|
||||
"*.tcc": "c",
|
||||
"type_traits": "c",
|
||||
"bsp_log.h": "c",
|
||||
"segger_rtt.h": "c",
|
||||
"referee.h": "c",
|
||||
"referee_communication.h": "c",
|
||||
"vmc_project.h": "c",
|
||||
"user_lib.h": "c",
|
||||
"quaternionekf.h": "c",
|
||||
"bsp_usb.h": "c",
|
||||
"robot.h": "c",
|
||||
"rm_referee.h": "c",
|
||||
"stdio.h": "c",
|
||||
"crc.h": "c",
|
||||
"bmi088.h": "c",
|
||||
"cmath": "c",
|
||||
"ht04.h": "c",
|
||||
"gain_table.h": "c",
|
||||
"referee_task.h": "c",
|
||||
"task.h": "c",
|
||||
"robot_task.h": "c",
|
||||
"motor_task.h": "c",
|
||||
"bsp_flash.h": "c",
|
||||
"bsp_iic.h": "c",
|
||||
"usbd_cdc_if.h": "c",
|
||||
"kf.h": "c",
|
||||
"none.h": "c",
|
||||
"buzzer.h": "c",
|
||||
"bsp_pwm.h": "c",
|
||||
"main.h": "c",
|
||||
"stm32f4xx_hal_conf.h": "c",
|
||||
"master_process.h": "c",
|
||||
"bsp_can.h": "c",
|
||||
"can.h": "c"
|
||||
},
|
||||
"cortex-debug.variableUseNaturalFormat": true,
|
||||
"C_Cpp.default.configurationProvider": "ms-vscode.makefile-tools",
|
||||
// "C_Cpp.default.compilerPath": "D:\\Msys2\\mingw64\\bin\\arm-none-eabi-gcc.exe"
|
||||
"makefile.compileCommandsPath": "build/compile_commands.json"
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
# 指定编译平台/架构与语言标准, 推荐指定Ninja为构建工具,可以加快编译速度(相比make)
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
||||
# 指定工具链
|
||||
set(CMAKE_C_COMPILER_FORCED TRUE) # skip compiler test
|
||||
set(CMAKE_CXX_COMPILER_FORCED TRUE)
|
||||
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
|
||||
set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
|
||||
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
|
||||
set(CMAKE_OBJDUMP arm-none-eabi-objdump)
|
||||
set(SIZE arm-none-eabi-size)
|
||||
set(CMAKE_AR arm-none-eabi-ar)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# 指定工程名称和语言类型
|
||||
project(basic_framework C ASM)
|
||||
|
||||
# 选择构建类型
|
||||
set(CMAKE_BUILD_TYPE Debug) # Debug Release RelWithDebInfo MinSizeRel
|
||||
|
||||
# board specific settings, arch/fpu/instruction
|
||||
set(MCU_FLAGS -mcpu=cortex-m4 -mthumb -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16)
|
||||
set(LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/STM32F407IGHx_FLASH.ld") # 指定链接脚本
|
||||
set(DSP_NAME "libCMSISDSP.a") # 指定DSP库名称
|
||||
link_directories(${CMAKE_SOURCE_DIR}/Middlewares/ST/ARM/DSP/Lib)
|
||||
|
||||
# Generic compiler settings for optimization and basic link lib
|
||||
add_compile_options(-pipe ${MCU_FLAGS} -Wall -Werror -fmessage-length=0 # basic options
|
||||
-ffunction-sections -fdata-sections -fno-common # optimize options
|
||||
)
|
||||
add_link_options(-pipe ${MCU_FLAGS} -T${LINKER_SCRIPT} -Wl,--no-warn-rwx-segments # close RWX warning
|
||||
-lm -lc -lnosys # lib options
|
||||
-Wl,--gc-sections -flto -specs=nano.specs -specs=nosys.specs # optimize options
|
||||
-Wl,-Map=${PROJECT_BINARY_DIR}/${PROJECT_NAME}.map -Wl,--cref -Wl,--print-memory-usage # map options
|
||||
) # if your executable is too large , try option '-s' to strip symbols
|
||||
|
||||
# add_compile_definitions() works for compile stage
|
||||
# while add_definitions() works for both compile and link stage
|
||||
add_definitions(
|
||||
-DUSE_HAL_DRIVER
|
||||
-DSTM32F407xx
|
||||
-DARM_MATH_CM4
|
||||
) # need -D<macro> to define macro
|
||||
|
||||
# add inc
|
||||
# 递归包含头文件的函数
|
||||
function(include_sub_directories_recursively root_dir)
|
||||
if (IS_DIRECTORY ${root_dir}) # 当前路径是一个目录吗,是的话就加入到包含目录
|
||||
message("include dir: " ${root_dir})
|
||||
include_directories(${root_dir})
|
||||
endif()
|
||||
|
||||
file(GLOB ALL_SUB RELATIVE ${root_dir} ${root_dir}/*) # 获得当前目录下的所有文件,让如ALL_SUB列表中
|
||||
foreach(sub ${ALL_SUB})
|
||||
if (IS_DIRECTORY ${root_dir}/${sub})
|
||||
include_sub_directories_recursively(${root_dir}/${sub}) # 对子目录递归调用,包含
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
include_sub_directories_recursively(${CMAKE_SOURCE_DIR}/Drivers)
|
||||
include_sub_directories_recursively(${CMAKE_SOURCE_DIR}/Middlewares)
|
||||
include_sub_directories_recursively(${CMAKE_SOURCE_DIR}/bsp)
|
||||
include_sub_directories_recursively(${CMAKE_SOURCE_DIR}/modules)
|
||||
include_sub_directories_recursively(${CMAKE_SOURCE_DIR}/application)
|
||||
include_sub_directories_recursively(${CMAKE_SOURCE_DIR}/Inc)
|
||||
|
||||
# add source, only surfix .c
|
||||
file(GLOB_RECURSE SOURCES
|
||||
"Drivers/*.c"
|
||||
"Src/*.c"
|
||||
"Middlewares/*.c"
|
||||
"bsp/*.c"
|
||||
"modules/*.c"
|
||||
"application/*.c"
|
||||
)
|
||||
|
||||
# 汇编文件路径
|
||||
# ENABLE_LANGUAGE(ASM)
|
||||
set(ASM_SOURCES
|
||||
startup_stm32f407xx.s
|
||||
Middlewares/Third_Party/SEGGER/RTT/SEGGER_RTT_ASM_ARMv7M.s
|
||||
)
|
||||
set_source_files_properties(${ASM_SOURCES} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp")
|
||||
|
||||
# Build types
|
||||
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
|
||||
message(STATUS "Maximum optimization for speed")
|
||||
add_compile_options(-Ofast)
|
||||
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
|
||||
message(STATUS "Maximum optimization for speed, debug info included")
|
||||
add_compile_options(-Ofast -g)
|
||||
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel")
|
||||
message(STATUS "Maximum optimization for size")
|
||||
add_compile_options(-Os)
|
||||
else ()
|
||||
message(STATUS "Minimal optimization, debug info included")
|
||||
add_compile_options(-Og -g -gdwarf-2)
|
||||
add_definitions(-DESC_DEBUG) # ESC Debug
|
||||
endif ()
|
||||
|
||||
# build binary and hex file
|
||||
add_executable(${PROJECT_NAME}.elf ${SOURCES} ${ASM_SOURCES} ${LINKER_SCRIPT})
|
||||
target_link_libraries(${PROJECT_NAME}.elf ${DSP_NAME} m) # link DSP lib and math lib
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${PROJECT_NAME}.elf POST_BUILD
|
||||
COMMAND ${CMAKE_OBJCOPY} -Oihex $<TARGET_FILE:${PROJECT_NAME}.elf> ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.hex
|
||||
COMMAND ${CMAKE_OBJCOPY} -Obinary $<TARGET_FILE:${PROJECT_NAME}.elf> ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.bin
|
||||
COMMENT "Building hex & bin file..."
|
||||
COMMENT "EXCUTABLE SIZE:"
|
||||
COMMAND ${SIZE} ${PROJECT_NAME}.elf
|
||||
)
|
136
README.md
136
README.md
|
@ -7,12 +7,17 @@
|
|||
<p>
|
||||
<img src="https://img.shields.io/badge/version-beta-blue"/>
|
||||
<img src="https://img.shields.io/badge/license-MIT-green"/>
|
||||
<img src="https://github.com/HNUYueLuRM/basic_framework/actions/workflows/c-cpp.yml/badge.svg"/>
|
||||
</p>
|
||||
<p>
|
||||
<img src="https://gitee.com/HNUYueLuRM/basic_framework/badge/star.svg"/>
|
||||
<img src="https://gitee.com/HNUYueLuRM/basic_framework/badge/fork.svg"/>
|
||||
</p>
|
||||
<h4><p><font face="仿宋">最棒的嵌入式电控开源?!</p></h>
|
||||
<h5><p><font face="consolas">Best RoboMaster embedded EC open-source code ever?</p></h>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
> ***也许不是最好的?但一定是最完整最详细最适合上手的电控开源!***
|
||||
|
||||
[TOC]
|
||||
|
@ -32,11 +37,23 @@
|
|||
|
||||
## 功能介绍和展示
|
||||
|
||||
这是湖南大学RoboMaster跃鹿战队电控组2022-2023赛季的通用嵌入式控制框架,可用于**机器人主控**、自研模组(imu/测距等各种传感器)、超级电容控制器等。该框架旨在打造上手简单、易于移植、高代码复用、层级分明、结构清晰的一套SDK,供队内使用&RM参赛队伍&嵌入式开发者交流学习。
|
||||
### 起源
|
||||
|
||||
通过精心设计的bsp和module支持以及成熟的app示例,该框架可以在短时间内为不同类型和结构的机器人轻松构建电控程序,可扩展性和可维护性相较目前的大部分开源代码和本队的老代码提升巨大。
|
||||
这是湖南大学RoboMaster跃鹿战队电控组2022-2023赛季的通用嵌入式控制框架,可用于**机器人主控**、自研模组(imu/测距等各种传感器)、超级电容控制器等。
|
||||
|
||||
同时,相较于传统的基于KEIL MDK开发的工作流,我们提供了基于arm-gnu工具链+VSCode/Clion+Ozone&Systemviewer/FreeMaster的现代化开发工作流,你再也不用面对上个世纪的UI忍受没有补全和高亮的代码了!在配套现代化开发工具支持下,将极大提高电控开发效率和调试效率。硬件模块测试和整车联调从未如此简单!
|
||||
从目前的RoboMaster开源社区来看,大部分队伍都没有一套规则统一,符合较大规模软件开发原则的框架,有些学校连不同兵种代码都相去甚远,甚至连队伍用于传承的代码注释都寥寥无几,全靠师傅带徒弟言传身教。当然,不乏有广东工业大学DynamicX开源的rm_control这样规范且先进的系统,但基于Linux、ROS、C++的这套软件栈对于新人来说还是过于复杂(但我们很推荐熟悉基本工具的同学使用!)。
|
||||
|
||||
### 优势
|
||||
|
||||
为此,basic_framework应运而生。该框架旨在打造上手简单、易于移植、高代码复用、层级分明、结构清晰的一套SDK,供队内使用&RM参赛队伍&嵌入式开发者交流学习。通过精心设计的bsp和module支持以及成熟的app示例,该框架可以在短时间内为不同类型和结构的机器人**轻松构建**电控程序,**可扩展性**和**可维护性**相较目前的大部分开源代码和本队的老代码提升巨大。
|
||||
|
||||
同时,相较于传统的基于KEIL MDK开发的工作流,我们推出了基于arm-gnu工具链+VSCode/Clion+Ozone&Systemviewer/FreeMaster的**现代化开发工作流**和非**常完善配套教程**,你再也不用面对上个世纪的UI,忍受没有补全和高亮的代码了!在现代化开发工具支持下,将**极大提高电控开发效率和调试效率**。硬件模块测试和整车联调从未**如此简单方便**!
|
||||
|
||||
> 用软件开发的思想设计嵌入式系统是一种降维打击
|
||||
>
|
||||
> <p align=right>—— 沃兹基·烁德</p>
|
||||
|
||||
### 效果展示
|
||||
|
||||
![](.assets/allrobot.jpg)
|
||||
|
||||
|
@ -51,8 +68,58 @@
|
|||
|
||||
<center>展示中的视觉识别与预测算法是基于rm_vision打造的</center>
|
||||
|
||||
3. 工程机器人流程化抓取矿石/兑换矿石/救援’
|
||||
|
||||
![engineering](.assets/engineering.gif)
|
||||
|
||||
4. 平衡步兵机器人
|
||||
|
||||
![balance](.assets/balance.gif)
|
||||
|
||||
这些机器人的程序均基于basic_framework打造,已在我们的仓库中提供:[HNUYueLuRM](https://gitee.com/hnuyuelurm)
|
||||
|
||||
> 更多测试视频可以关注我们的bilibili账号:[湖南大学跃鹿战队](https://space.bilibili.com/522795884),或在bilibili搜索跃鹿战队,观看我们的比赛视频。
|
||||
|
||||
### 可用功能
|
||||
|
||||
你可以基于这些良好抽象的功能打造自己的模块或应用。
|
||||
|
||||
#### bsp封装
|
||||
|
||||
| 功能类别 | 模块 |
|
||||
| ---------- | ----------------------- |
|
||||
| 通信类外设 | usart spi i2c can usb |
|
||||
| 日志 | log flash |
|
||||
| 功能型 | gpio(exti) pwm adc |
|
||||
| 辅助 | dwt |
|
||||
|
||||
#### 模块封装
|
||||
|
||||
| 功能类别 | 模块 |
|
||||
| -------- | ------------------------------------------------------------ |
|
||||
| 电机 | DJI、HT海泰04、瓴控LK、步进电机、舵机 |
|
||||
| 通信 | 多板通信(基于CAN)、seasky协议上位机通信、裁判系统数据/UI/多机、vofa协议、DT7-DR16遥控器 |
|
||||
| 功能模块 | 蜂鸣器、oled、bmi088、ist8310、超级电容、TFminiPlus、 |
|
||||
| 应用支持 | 常用算法库、守护线程、消息中心 |
|
||||
|
||||
#### 应用封装
|
||||
|
||||
作为命令发布主体的robot_cmd
|
||||
|
||||
用于步兵、英雄、哨兵、无人机的gimbal
|
||||
|
||||
麦克纳姆轮/全向轮底盘的chassis
|
||||
|
||||
平衡步兵的底盘balance_chassis
|
||||
|
||||
装配了发射机构的机器人的shoot
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 架构
|
||||
|
||||
总览。
|
||||
|
@ -65,31 +132,41 @@
|
|||
|
||||
### 设计思想
|
||||
|
||||
框架在结构上分为三层:bsp/module/app。整体使用的设计模式是**结构层级模式**,即每个“类”包含需要使用的底层“类”,通过组装不同的基础模实现更强大的功能。而最顶层的app之间则通过**pub-sub消息机制**进行解耦,使得在编写代码时不会出现相互包含的情况。
|
||||
1. ***首先,总览框架的设计模式。***
|
||||
|
||||
**pub-sub机制的体现**:以本仓库的app层为例,包含了chassis,gimbal,shoot,cmd四个应用,每个应用都对应了机器人上的不同模组。cmd应用负责从机器人控制信号来源(遥控器/上位机/环境传感器)处获取信息并解析成各个**执行单元的实际动作**(电机/舵机/气缸/阀门等的扭矩/速度/位置/角度/开度等),并将此信息**发布**出去。chassis、gimbal、shoot等包含了执行单元的应用则**订阅**这些消息,并通过自己包含的子模块,调用它们的接口实现动作。
|
||||
框架在结构上分为三层:bsp/module/app。整体使用的设计模式是**结构层级模式**,即每个“类”包含需要使用的底层“类”,通过组装不同的基础模实现更强大的功能。而最顶层的app之间则通过**pub-sub消息机制**进行解耦,使得在编写代码时不会出现相互包含的情况。
|
||||
|
||||
**结构层级模式的体现**:以chassis应用为例,chassis中包含了4个底盘电机模块。当chassis收到cmd应用的信息,希望让底盘以1m/s的速度前进。chassis首先根据底盘的类型(舵轮/麦克纳姆轮/全向轮/平衡底盘)以及对应的动力学/运动学解算函数,计算得到每个电机的输目标输入值,此时chassis将输入通过电机模块(motor module)的接口将设定值告知电机。而每个电机模块又有各自的PID计算模块和自身电流&速度&角度传感器的信息,可以计算出最终需要的电流设定值。假设该电机使用CAN协议与电调通信,则电机通过自身包含的CANInstance(bsp_can提供)用于和实际硬件交互,电机模块将设定值电流值或其他指令按照通信协议组织在CAN报文中,通过CANInstance提供的接口,把最终控制数据发送给电调,实现控制闭环。可以看到,**包含关系为chassis∈motor∈bspcan**。
|
||||
我们希望通过bsp对硬件的抽象使得module的编写更为轻松,不需要考虑底层的硬件具体是如何运作的;再通过module的外接模块的抽象,使得app的编写可以通过完全硬件无关的方式考虑,达到*”只阅读module的说明文档就能迅速开发应用 ”*的程度。bsp和module的设计愿景,就是成为人们常说的***中间件**。*
|
||||
|
||||
有了上面的大体认知,我们分别介绍框架的三层结构。
|
||||
**pub-sub机制的体现**:以本仓库的app层为例,包含了chassis,gimbal,shoot,cmd四个应用,每个应用都对应了机器人上的不同模组。cmd应用负责从机器人控制信号来源(遥控器/上位机/环境传感器)处获取信息并解析成各个**执行单元的实际动作**(电机/舵机/气缸/阀门等的扭矩/速度/位置/角度/开度等),并将此信息**发布**出去。chassis、gimbal、shoot等包含了执行单元的应用则**订阅**这些消息,并通过自己包含的子模块,调用它们的接口实现动作。
|
||||
|
||||
- **bsp**即板级支持包,提供对开发板外设的软件抽象,让module层能使用和硬件无关的接口(由bsp提供)进行数据处理与交互。bsp层和ST的HAL为强耦合,与硬件直接绑定。若要向其他的ST芯片移植,基本不需要修改bsp层;若是其他单片机则建议保留**接口设计**,对接口调用进行重现实现。每一种外设的头文件中都定义了一个**XXXInstance**(xxx为外设名),其中包含了使用该外设所需要的所有数据,如发送/接收的数据,长度,id(如果有),父指针(指向module实例的指针,用于回调)等。由于C没有`class`,因此所有bsp的接口都需要传入一个额外的参数:XXXInstance*,用于实现c++的`this`指针以区分具体是哪一个实例调用了接口。
|
||||
**结构层级模式的体现**:以chassis应用为例,chassis中包含了4个底盘电机模块。当chassis收到cmd应用的信息,希望让底盘以1m/s的速度前进。chassis首先根据底盘的类型(舵轮/麦克纳姆轮/全向轮/平衡底盘)以及对应的动力学/运动学解算函数,计算得到每个电机的输目标输入值,此时chassis将输入通过电机模块(motor module)的接口将设定值告知电机。而每个电机模块又有各自的PID计算模块和自身电流&速度&角度传感器的信息,可以计算出最终需要的电流设定值。假设该电机使用CAN协议与电调通信,则电机通过自身包含的CANInstance(bsp_can提供)用于和实际硬件交互,电机模块将设定值电流值或其他指令按照通信协议组织在CAN报文中,通过CANInstance提供的接口,把最终控制数据发送给电调,实现控制闭环。从调用来看,三个层级的**包含关系为chassis∈motor∈bspcan**。
|
||||
|
||||
- **module**即模块层,包括了需要开发板硬件外设支持的(一般用于通信)真实**硬件模组**如电机、舵机、imu、测距传感器,和通过软件实现的**算法**如PID、滤波器、状态观测器;还有用于兼容不同控制信息模块(遥控器/ps手柄/图传链路/上位机)的统一接口模块,以及为app层提供数据交互的message center。
|
||||
2. ***有了上面的大体认知,我们分别介绍框架的三层结构。***
|
||||
|
||||
module层仍然是基于实例的,一个app会包含多个module的instance。当app便可以用硬件无关的接口使用module,如要求电机以一定速度运动、关闭气阀、给超级电容或上位机发送一些反馈数据等。在有了方便的bsp之后,只需要在你构建的module中包含必须的bsp,然后为app提供合理的接口即可。
|
||||
- **bsp**即板级支持包,提供对开发板外设的软件抽象,让module层能使用和硬件无关的接口(由bsp提供)进行数据处理与交互。
|
||||
|
||||
- **app**是框架层级中最高的部分。目前的框架设计里,会有多个app任务运行在freertos中,当然你也可以根据需要启动一些事件驱动的任务,所有的任务安排都放在`app/robot_task`中。当前的app层仅是一个机器人开发的示例,有了封装程度极高的module,你可以在app完成任何事情。
|
||||
bsp层和ST的HAL为强耦合,与硬件直接绑定。若要向其他的ST芯片移植,基本不需要修改bsp层;若是其他单片机则建议保留**接口设计**,对接口调用进行重现实现。每一种外设的头文件中都定义了一个**XXXInstance**(xxx为外设名),其中包含了使用该外设所需要的所有数据,如发送/接收的数据,长度,id(如果有),父指针(指向module实例的指针,用于回调)等。由于C没有`class`,因此所有bsp的接口都需要传入一个额外的参数:XXXInstance*,用于实现c++的`this`指针以区分具体是哪一个实例调用了接口。
|
||||
|
||||
目前的app设计里,可以兼容多块开发板的情况,通过**条件编译**切换开发板的位置。如步兵机器人可以将主控MCU放在云台上,而超级电容控制板同时作为底盘板。使用CAN/SPI/UART将两者连接,便可以通过**`app/robot_def.h`**中的宏完成设置。可以根据需要,设置更多的开发板(双云台哨兵、工程机器人)。
|
||||
- **module**即模块层,包括了需要开发板硬件外设支持的(一般用于通信)真实**硬件模组**如电机、舵机、imu、测距传感器,和通过软件实现的**算法**如PID、滤波器、状态观测器;还有用于兼容不同控制信息模块(遥控器/ps手柄/图传链路/上位机)的统一接口模块,以及为app层提供数据交互的message center。
|
||||
|
||||
这套框架可以轻松扩展到所有机器人上,在我们的仓库中,有步兵机器人、平衡步兵机器人、哨兵机器人、英雄机器人、工程机器人以及空中机器人的代码实例,皆按照本框架中的三层结构开发。若设计了新的机器人,只需要在robot_def.h中修改传感器的位置、底盘轮距轴距、拨弹盘容量、弹舱载弹量等参数便可以**立刻实现部署**。
|
||||
module层仍然是基于实例的,一个app会包含多个module的instance。当app便可以用硬件无关的接口使用module,如要求电机以一定速度运动、关闭气阀、给超级电容或上位机发送一些反馈数据等。在有了方便的bsp之后,只需要在你构建的module中包含必须的bsp,然后为app提供合理易用的接口即可。
|
||||
|
||||
至于bsp和module中每个instance的设计,我们采用了**面向对象**的C风格代码,整个框架也统一了变量和函数命名方式,调用层级和数据流十分清晰(下一个章节也有插图阐述)。
|
||||
- **app**是框架层级中最高的部分。目前的框架设计里,会有多个app任务运行在freertos中,当然你也可以根据需要启动一些事件驱动的任务,所有的任务安排都放在`app/robot_task`中。当前的app层仅是一个机器人开发的示例,有了封装程度极高的module,你可以在app完成任何事情。
|
||||
|
||||
为了避免出现”底层代码包含上层头文件“的情况,我们让bsp层instance在注册时要求module提供数据发送/接收的回调函数指针,从而在发生对应中断或事件时完成对module函数的”反向调用“。事实上,你也可以进一步将这套思想放入app的设计中,当某个事件发生时触发app的任务,而不是将app的任务定时运行(这可以提高运行效率,降低cpu占用)。
|
||||
目前的app设计里,可以兼容多块开发板的情况,通过**条件编译**切换开发板的位置。如步兵机器人可以将主控MCU放在云台上,而超级电容控制板同时作为底盘板。使用CAN/SPI/UART将两者连接,便可以通过**`app/robot_def.h`**中的宏完成设置。可以根据需要,设置更多的开发板(双云台哨兵、工程机器人)。
|
||||
|
||||
bsp和module的instance在初始化时接口皆为**`XXXInstance* XXXRegister(XXX_Init_Config_s* conf)`**,传入该实例所需的config参数,返回一个实例指针(看作this指针),之后要调用模块的功能,传入该指针即可。我们还提供了守护线程,以供module选用,当异常情况发生时在LOG中发送warning、触发蜂鸣器或LED进行声光报警以及错误/离线回调函数,保证系统的鲁棒性和安全性。
|
||||
这套框架可以轻松扩展到所有机器人上,在我们的仓库中,有步兵机器人、平衡步兵机器人、哨兵机器人、英雄机器人、工程机器人以及空中机器人的代码实例,皆按照本框架中的三层结构开发。若设计了新的机器人,只需要在robot_def.h中修改传感器的位置、底盘轮距轴距、拨弹盘容量、弹舱载弹量等参数便可以**立刻实现部署**。
|
||||
|
||||
3. ***知道了每个层级的结构之后,我们再谈谈如何进行每层的开发。***
|
||||
|
||||
对于bsp和module中每个instance的设计,我们采用了**面向对象**的C风格代码,整个框架也统一了变量和函数命名方式,调用层级和数据流十分清晰(下一个章节也有插图阐述)。
|
||||
|
||||
为了避免出现”底层代码包含上层头文件“的情况,我们让bsp层instance在注册时要求module提供数据发送/接收的回调函数指针,从而在发生对应中断或事件时完成对module函数的”反向调用“。事实上,你也可以进一步将这套思想放入app的设计中,当某个事件发生时触发app的任务,而不是将app的任务定时运行(这可以提高运行效率,降低cpu占用)。
|
||||
|
||||
bsp和module的instance在初始化时接口皆为**`XXXInstance* XXXRegister(XXX_Init_Config_s* conf)`**,传入该实例所需的config参数,返回一个实例指针(看作this指针),之后要调用模块的功能,传入该指针即可。我们还提供了守护线程,以供module选用,当异常情况发生时在LOG中发送warning、触发蜂鸣器或LED进行声光报警以及错误/离线回调函数,保证系统的鲁棒性和安全性。
|
||||
|
||||
而对于app的开发,由于底层接口已经设计的较为完善,不同的机器人可以直接**`fork`** basic_framework的代码,开发app层。当bsp和module有功能更新时,只需要通过git的cherry-pick-commit功能将更新拉取到自己的仓库,**获得动态的热更新而无需手动合并分支!**
|
||||
|
||||
|
||||
|
||||
|
@ -175,9 +252,11 @@ app、module和bsp都有相应的rtos任务。其中bsp为创建任务提供了
|
|||
|
||||
随后,通过调试器将开发板连接至你的电脑,点击上方tab页的终端(terminal)->运行任务(run task),选择download_dap or download_jlink(或你自己编写的stlink/ulink/...),便会开始下载,终端或jFlash中会提示擦除、下载、验证的进度。
|
||||
|
||||
想要调试,在左侧tab页选择合适的调试选项,按F5或图形界面的绿色小三角形按钮,开始调试。
|
||||
想要调试,在左侧tab页选择合适的调试选项,按F5或图形界面的绿色小三角形按钮,开始调试。当然,调试器的设置也请参考配置文档,主要是将可执行文件路劲加入环境变量的PATH。
|
||||
|
||||
**更详细的开发流程,请参照`.Doc/VSCode+Ozone使用方法.md`**
|
||||
**更详细的开发流程和本仓库工作流的最佳实践,请参照`.Doc/VSCode+Ozone使用方法.md`**,里面介绍了开发所需的前置知识、环境配置,以及工具链原理、使用方法等。
|
||||
|
||||
要对本仓库进行开发,务必先阅读`.Doc/架构介绍与开发指南.md`,内含本仓库组织结构的**文件树**。若你希望使用其他工具链或IDE,里面也有相关说明。
|
||||
|
||||
### 基本文档
|
||||
|
||||
|
@ -187,7 +266,7 @@ app、module和bsp都有相应的rtos任务。其中bsp为创建任务提供了
|
|||
|
||||
- [Bug_Report](.Doc/Bug_Report.md) :提供了一些提交issues的模板范例,若在使用中出现问题请按照模板提供信息。
|
||||
- [TODO](.Doc/TODO.md) :框架后续开发计划和维护说明
|
||||
- **[VSCode+Ozone使用方法.md](VSCode+Ozone使用方法.md)** :**重要**,上手必看。介绍了当前开发工作流和传统KEIL开发的不同,先讲解一些与工具链有关的基础知识,然后说明了如何配置开发环境,安装必要的软件和一些”操作“。还涉及了VSCode编辑调试和Ozone示波器&trace功能的使用指南。
|
||||
- **[VSCode+Ozone使用方法](.Doc/VSCode+Ozone使用方法.md)** :**重要**,上手必看。介绍了当前开发工作流和传统KEIL开发的不同,先讲解一些与工具链有关的基础知识,然后说明了如何配置开发环境,安装必要的软件和一些”操作“。还涉及了VSCode编辑调试和Ozone示波器&trace功能的使用指南。
|
||||
- [合理地进行PID参数整定](.Doc/合理地进行PID参数整定.md) :介绍了如何为PID控制器进行参数整定,包括简单的经验准则和基于模型的前馈控制、扰动消除等方法。
|
||||
- [如何定位bug](.Doc/如何定位bug.md) :当嵌入式开发出现bug时,以更高效地方法进行错误定位和复现。简单的调试器使用技巧。
|
||||
- [必须做&禁止做](.Doc/必须做&禁止做.md) :字如其名
|
||||
|
@ -198,7 +277,20 @@ app、module和bsp都有相应的rtos任务。其中bsp为创建任务提供了
|
|||
|
||||
框架中的三层结构都有详尽的注释帮助阅读和二次开发,三个抽象层都有各自总览的说明文档,而每个bsp/module/app都配有对应的个性化说明文档,提供了接口说明和改进或进一步开发的建议。
|
||||
|
||||
建议以自上而下的方式阅读代码,app-》module-》bsp,我们还提供了框架的说明视频,分别讲解每个抽象层和总体的设计思路,还有一些杂碎的开发相关知识:[basic_framework教程]()
|
||||
建议以自上而下的方式阅读代码,app-》module-》bsp,为此我们还提供了框架的说明视频,分别讲解每个抽象层和总体的设计思路,介绍了bsp的依赖HAL,还有一些杂碎的开发相关知识:[basic_framework教程](https://space.bilibili.com/522795884/channel/collectiondetail)
|
||||
|
||||
### 运行单个bsp/module测试
|
||||
|
||||
**每个bsp和module的文件夹中都有各自的说明文档**,测试用例和使用方法都在文档中给出,非常方便。
|
||||
|
||||
想要进行硬件查错、连线检查或测试,如测试开发板功能(外设)是否完好,电机电调能否正常使用,或给新人提供教学,只要:
|
||||
|
||||
1. 将main.c中的`RoboInit()`函数删除,然后包含`bsp_init.h`头文件,在原`RoboInit()`的位置添加一个`BSPInit()`
|
||||
2. 包含用于测试的bsp或module头文件,根据说明文档的用例初始化对应的支持包或模块即可。
|
||||
3. 同时可以将实时系统的初始化注释或删除,在`main.c`的`while(1)`主循环中进行测试,也可以使用`bsp_tim.h`提供的定时任务。
|
||||
4. 编译,下载,运行,调试。
|
||||
|
||||
我们为机械和视觉的同学能方便测试硬件模块的好坏,设计了一套通过串口和遥控器控制的**硬件功能测试程序**,使得其他技术组成员可以操作这个”黑箱“,在没有电控组成员的时候也不会卡住其他队员的进度。
|
||||
|
||||
### VSCode集成工具
|
||||
|
||||
|
@ -245,6 +337,6 @@ ST官方现在将HAL放入github维护。想要获取最新的支持,可以自
|
|||
|
||||
## 致谢
|
||||
|
||||
本框架设计参考了哈尔滨工业大学(深圳)南工骁鹰🦅战队的EC_framework以及RoboMaster官方的RoboRTS-firmware🤖。姿态解算改进自哈尔滨工程大学创盟之翼🛩️的四元数EKF姿态解算。裁判系统数据解析移植了深圳大学RoboPilot2021年电控英雄开源代码。
|
||||
本框架设计参考了哈尔滨工业大学(深圳)南工骁鹰🦅战队的EC_framework以及RoboMaster官方的RoboRTS-firmware🤖。姿态解算改进自哈尔滨工程大学创梦之翼🛩️的四元数EKF姿态解算。裁判系统数据解析移植了深圳大学RoboPilot2021年电控英雄开源代码。
|
||||
|
||||
感谢2022-2023赛季跃鹿战队电控组参与新框架测试和开发的队员们,包括设计出机器人平台的机械组队员,还有一起联调的视觉组队员,以及负责拍摄、记录、宣传的运营组成员。
|
||||
|
|
|
@ -5,8 +5,8 @@ BSP
|
|||
> TODO:
|
||||
> 1. 增加硬件和软件CRC支持,统一不同crc顺序和计算码的调用方式
|
||||
|
||||
bsp的功能是提供对片上外设的封装,即单片机芯片内部拥有的功能的封装。在开发板pcb上集成的模块应该放在module层而不是这里。
|
||||
bsp的功能是提供对片上(MCU中)外设的封装,即单片机芯片内部拥有的功能的封装。**在开发板pcb上集成的模块(蜂鸣器,加热电阻,wifi等)应该放在module层而不是这里**。
|
||||
|
||||
bsp应该提供几种接口。包括初始化接口,一般命名为`XXXRegister()`(对于只有一个instance的可以叫`XXXInit()`,但建议统一风格都叫register);调用此模块实现的必要功能,如通信型外设(CubeMX下的connectivity)提供接收和发送的接口,以及接收完成的数据回调函数。
|
||||
bsp应该提供几种接口。包括初始化接口,一般命名为`XXXRegister()`(对于只有一个instance的可以叫`XXXInit()`,但建议统一风格都叫register);调用此模块实现的必要功能,如通信型外设(CubeMX下的connectivity)提供接收和发送的接口,以及接收完成、发送完成(若有必要,或有发送队列需求)的数据回调函数。
|
||||
|
||||
bsp_tools.h中提供了将bsp数据接收回调函数设置为任务的接口,通过这种方式,可以进一步提高整个系统的实时性,同时保证高优先级的任务一定按时执行。
|
||||
- bsp_tools.h中提供了将bsp数据接收回调函数设置为任务的接口,通过这种方式,可以进一步提高整个系统的实时性,同时保证高优先级的任务一定按时执行。
|
|
@ -106,7 +106,7 @@ CANInstance *CANRegister(CAN_Init_Config_s *config)
|
|||
uint8_t CANTransmit(CANInstance *_instance, float timeout)
|
||||
{
|
||||
static uint32_t busy_count;
|
||||
static float wait_time;
|
||||
static volatile float wait_time __attribute__((unused)); // for cancel warning
|
||||
float dwt_start = DWT_GetTimeline_ms();
|
||||
while (HAL_CAN_GetTxMailboxesFreeLevel(_instance->can_handle) == 0) // 等待邮箱空闲
|
||||
{
|
||||
|
|
|
@ -188,31 +188,31 @@ static uint8_t BMI088GyroInit(BMI088Instance *bmi088)
|
|||
*/
|
||||
static void BMI088AccSPIFinishCallback(SPIInstance *spi)
|
||||
{
|
||||
static BMI088Instance *bmi088;
|
||||
bmi088 = (BMI088Instance *)(spi->id);
|
||||
// static BMI088Instance *bmi088;
|
||||
// bmi088 = (BMI088Instance *)(spi->id);
|
||||
// 若第一次读取加速度,则在这里启动温度读取
|
||||
// 如果使用异步姿态更新,此处唤醒量测更新的任务
|
||||
}
|
||||
|
||||
static void BMI088GyroSPIFinishCallback(SPIInstance *spi)
|
||||
{
|
||||
static BMI088Instance *bmi088;
|
||||
bmi088 = (BMI088Instance *)(spi->id);
|
||||
// static BMI088Instance *bmi088;
|
||||
// bmi088 = (BMI088Instance *)(spi->id);
|
||||
// 若不是异步,啥也不做;否则启动姿态的预测步(propagation)
|
||||
}
|
||||
|
||||
static void BMI088AccINTCallback(GPIOInstance *gpio)
|
||||
{
|
||||
static BMI088Instance *bmi088;
|
||||
bmi088 = (BMI088Instance *)(gpio->id);
|
||||
// static BMI088Instance *bmi088;
|
||||
// bmi088 = (BMI088Instance *)(gpio->id);
|
||||
// 启动加速度计数据读取(和温度读取,如果有必要),并转换为实际值
|
||||
// 读取完毕会调用BMI088AccSPIFinishCallback
|
||||
}
|
||||
|
||||
static void BMI088GyroINTCallback(GPIOInstance *gpio)
|
||||
{
|
||||
static BMI088Instance *bmi088;
|
||||
bmi088 = (BMI088Instance *)(gpio->id);
|
||||
// static BMI088Instance *bmi088;
|
||||
// bmi088 = (BMI088Instance *)(gpio->id);
|
||||
// 启动陀螺仪数据读取,并转换为实际值
|
||||
// 读取完毕会调用BMI088GyroSPIFinishCallback
|
||||
}
|
||||
|
@ -258,6 +258,7 @@ uint8_t BMI088Acquire(BMI088Instance *bmi088, BMI088_Data_t *data_store)
|
|||
// 如果数据还没准备好,则返回空数据?或者返回上一次的数据?或者返回错误码? @todo
|
||||
if (bmi088->update_flag.imu_ready == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* pre calibrate parameter to go here */
|
||||
|
@ -286,7 +287,6 @@ void BMI088CalibrateIMU(BMI088Instance *_bmi088)
|
|||
// 一次性参数用完就丢,不用static
|
||||
float startTime; // 开始标定时间,用于确定是否超时
|
||||
uint16_t CaliTimes = 6000; // 标定次数(6s)
|
||||
uint8_t buf[6] = {0}; // buffer
|
||||
float gyroMax[3], gyroMin[3]; // 保存标定过程中读取到的数据最大值判断是否满足标定环境
|
||||
float gNormTemp, gNormMax, gNormMin; // 同上,计算矢量范数(模长)
|
||||
float gyroDiff[3], gNormDiff; // 每个轴的最大角速度跨度及其模长
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "string.h"
|
||||
|
||||
static PWMInstance *buzzer;
|
||||
static uint8_t idx;
|
||||
// static uint8_t idx;
|
||||
static BuzzzerInstance *buzzer_list[BUZZER_DEVICE_CNT] = {0};
|
||||
|
||||
/**
|
||||
|
@ -84,6 +84,8 @@ void BuzzerTask()
|
|||
case OCTAVE_7:
|
||||
PWMSetPeriod(buzzer, (float)1 / SiFreq);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ CANCommInstance *CANCommInit(CANComm_Init_Config_s *comm_config)
|
|||
ins->can_ins = CANRegister(&comm_config->can_config);
|
||||
|
||||
Daemon_Init_Config_s daemon_config = {
|
||||
.callback = NULL,
|
||||
.callback = CANCommLostCallback,
|
||||
.owner_id = (void *)ins,
|
||||
.reload_count = comm_config->daemon_count,
|
||||
};
|
||||
|
|
|
@ -18,8 +18,6 @@ float gyroDiff[3], gNormDiff;
|
|||
uint8_t caliOffset = 1;
|
||||
int16_t caliCount = 0;
|
||||
|
||||
static uint32_t offset_cal_DWT_Count = 0;
|
||||
|
||||
IMU_Data_t BMI088;
|
||||
|
||||
#if defined(BMI088_USE_SPI)
|
||||
|
@ -333,17 +331,7 @@ void BMI088_Read(IMU_Data_t *bmi088)
|
|||
{
|
||||
static uint8_t buf[8] = {0};
|
||||
static int16_t bmi088_raw_temp;
|
||||
static float dt = 1.0;
|
||||
static uint8_t first_read_flag = 0;
|
||||
if (!first_read_flag)
|
||||
{
|
||||
first_read_flag = 1;
|
||||
DWT_GetDeltaT(&offset_cal_DWT_Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
dt = DWT_GetDeltaT(&offset_cal_DWT_Count) * 1000.0;
|
||||
}
|
||||
|
||||
BMI088_accel_read_muli_reg(BMI088_ACCEL_XOUT_L, buf, 6);
|
||||
|
||||
bmi088_raw_temp = (int16_t)((buf[1]) << 8) | buf[0];
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "bsp_dwt.h"
|
||||
#include "ist8310.h"
|
||||
#include "memory.h"
|
||||
#include "stdlib.h"
|
||||
#include "bsp_log.h"
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// 一般这个模块只有一个实例,所以直接保存在这里,实际上不保存也可以,application可以自己保存
|
||||
static IST8310Instance *ist8310_instance = NULL; // 用于存储IST8310实例的指针
|
||||
|
@ -91,7 +92,7 @@ IST8310Instance *IST8310Init(IST8310_Init_Config_s *config)
|
|||
IICAccessMem(ist->iic, ist8310_write_reg_data_error[i][0], &check_who_i_am, 1, IIC_READ_MEM, 1); // 读回自身id
|
||||
if (check_who_i_am != ist8310_write_reg_data_error[i][1])
|
||||
while (1)
|
||||
ist8310_write_reg_data_error[i][2]; // 掉线/写入失败/未知错误,会返回对应的错误码
|
||||
LOGERROR("[ist8310] init error, code %d", ist8310_write_reg_data_error[i][2]); // 掉线/写入失败/未知错误,会返回对应的错误码
|
||||
}
|
||||
|
||||
ist8310_instance = ist; // 保存ist8310实例的指针
|
||||
|
|
|
@ -36,11 +36,11 @@ void LEDSwitch(LEDInstance *_led, uint8_t led_switch)
|
|||
|
||||
void LEDShow(uint32_t aRGB)
|
||||
{
|
||||
static uint8_t alpha;
|
||||
static uint16_t red, green, blue;
|
||||
// static uint8_t alpha;
|
||||
// static uint16_t red, green, blue;
|
||||
|
||||
alpha = (aRGB & 0xFF000000) >> 24;
|
||||
red = ((aRGB & 0x00FF0000) >> 16) * alpha;
|
||||
green = ((aRGB & 0x0000FF00) >> 8) * alpha;
|
||||
blue = ((aRGB & 0x000000FF) >> 0) * alpha;
|
||||
// alpha = (aRGB & 0xFF000000) >> 24;
|
||||
// red = ((aRGB & 0x00FF0000) >> 16) * alpha;
|
||||
// green = ((aRGB & 0x0000FF00) >> 8) * alpha;
|
||||
// blue = ((aRGB & 0x000000FF) >> 0) * alpha;
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ LKMotorInstance *LKMotorInit(Motor_Init_Config_s *config)
|
|||
lkmotor_instance[idx++] = motor;
|
||||
|
||||
Daemon_Init_Config_s daemon_config = {
|
||||
.callback = NULL,
|
||||
.callback = LKMotorLostCallback,
|
||||
.owner_id = motor,
|
||||
.reload_count = 5, // 50ms
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue