git clone this repo for template files
This assignment allows collaboration, online searching, generative AI. But please cite the origin of those content in your solution with comment.
◼️ Citing others' work will not affect the result.
❌ Using other sources without citing will cause punishment.
在开始开发前,请注意:在该模板中,你的所有修改应该在./SEML目录下进行,请仔细阅读关于SEML库的介绍。
在本次开发中,请关注以下几个文件及其对应的头文件:
Robo_Gimbal.c
Robo_AA.c
Robo_USB.c
Dji_Motor.c
你可以通过修改、补充或完成这些部分的代码来完成此次项目;或者,你也可以构建自己的框架,这取决于你的开发习惯。
在系统抽象的基础上,我们可以搭建了gimbal、USB和AA六个进程。其中gimbal进程负责云台控制,USB进程负责与上位机通信,AA进程负责自瞄。
每个进程都有一个Init函数和一个Task函数。在main.c中,Init函数在进程启动时执行一次,Task函数在进程运行时循环执行。代码内容我们提供了如下参考:
云台进程负责从全局变量中获取预期的云台朝向,然后利用电机抽象使用电流控制电机。
云台进程拥有一个句柄结构体"Task_Gimbal_t gimbal",其中包含了云台控制所需的所有参数。
流程如下:
- 初始化电机
- 初始化PID(yaw和pitch电机各有速度环、角度环双环PID)
Task函数流程类似OOP的类方法,首先获取数据,然后处理数据,最后输出数据。
流程如下:
- 从全局变量中获取预期云台朝向角度
- 使用角度环PID通过角度控制电机速度
- 使用速度环PID通过速度控制电机电流
- 发送电流
USB进程负责与上位机通信,接收上位机发送的数据包,然后根据数据包类型执行相应的操作。
在这里,你需要自行定义SendPacket和ReceivePacket两个结构题,用于装载收发数据包
- 初始化USB
- 初始化发送数据包
- 接收
- 发送
流程如下:
- 设定单次传输数据包长度(只要大于数据包长度即可)
- 调用USB_CDC抽象接收数据包
- 检查数据包头
- 检查CRC校验和
流程如下:
- 计算CRC校验和
- 调用USB_CDC抽象发送数据包
自瞄进程负责使用目标位置进行弹道解算,然后在全局变量中发布云台预期角度。
自定义
流程如下:
- 将IMU和自瞄目标位置传入弹道解算函数
- 在全局变量中发布云台预期角度
我们的系统提供了一些数据类型抽象接口,提供给大家使用
Event OS系统提供了全局变量抽象,可以在任何地方通过tag访问全局变量,并且不限制读写的变量类型。例如:
//在power.c中
int power = 100;
Robo_Push_Message_Cmd("power", power);这行代码将power变量推送到全局变量中,tag为"power"。然后在任何地方都可以通过tag访问这个变量。
//在main.c中
int power;
Robo_Get_Message_Cmd("power", power);这行代码将全局变量中tag为"power"的变量赋值给power。
/**
* @brief 电机句柄发送函数
* @param motor 电机句柄
* @param data 发送数据
*/
void Motor_Send_Data(Motor_t *motor, float *data)其中motor是电机句柄,data是发送的电流值,单位为毫安。例如:
Motor_Send_Data(motor, 10000);这行代码向motor发送了10000mA的电流。
从C板的IMU获取的所有数据均存储在AHRS结构体中,include "AHRS.h"即可使用。在云台跟随中,我们常常用它来获取云台姿态 例如:
current_yaw = AHRS.euler_angle.yaw;其中AHRS_t结构体在AHRS.h中定义如下:
/**
* @brief AHRS句柄
*/
typedef struct
{
Euler_Data_t euler_angle; /**< 欧拉角 */
Accel_Data_t accel; /**< 加速度 */
Accel_Data_t real_accel; /**< 惯性坐标系下的加速度 */
Gyro_Data_t gyro; /**< 角速度 */
Mag_Data_t mag; /**< 磁场 */
float quat[4]; /**< 姿态四元数 */
float g_norm; /**< 标定时的重力加速度 */
float sample_time; /**< 采样时间 */
Gyro_Data_t gyro_offset; /**< 角速度零偏校正值 */
AHRS_Calculate_fun_t AHRS_Calculate_fun; /**< AHRS解算算法 */
void *IMU_handle; /**< IMU句柄*/
Get_IMU_fun_t Get_IMU_fun; /**< 获取IMU数据函数 */
void *mag_handle; /**< 磁力计句柄*/
Get_Mag_fun_t Get_Mag_fun; /**< 获取磁力计数据函数 */
uint8_t count;
} AHRS_t;get_CRC16_check_sum方法定义在CRC8_CRC16.c中,用于计算CRC16校验和。
/**
* @brief 计算CRC16
* @param[in] pch_message: 数据
* @param[in] dw_length: 数据和校验的长度
* @param[in] wCRC:初始CRC16
* @retval 计算完的CRC16
*/
uint16_t get_CRC16_check_sum(uint8_t *pch_message,uint32_t dw_length,uint16_t wCRC)其中pch_message是数据,dw_length是数据长度,wCRC是初始CRC16值。例如:
checksum = get_CRC16_check_sum(msg, sizeof(msg), 0xffff);这行代码计算了msg的CRC16校验和。
/**
* @brief 均方根滤波器初始化
* @param[out] RMS_filter 均方根配置结构体指针
* @param[in] window_width 滑动窗口大小
* @param[in] init_value 初始值
* @param[in,out] buffer 队列缓存数组
*/
void RMS_filter_Init(RMS_filter_t *RMS_filter,const uint16_t window_width,const float init_value,float *buffer);例如:
RMS_filter_Init(&rms_filter, 10, 0, rms_buffer);这行代码初始化了一个滑动窗口大小为10的均方根滤波器,初始值为0。
其中RMS_filter_t结构体在math_filter.h中定义如下:
typedef struct
{
uint16_t window_width; /**< 窗口大小 */
float gain;
uint16_t count; /**< 计数值 */
float RMS_value; /**< rms值 */
float square_sum; /**< 平方和 */
s_queue data_queue; /**< 数据队列*/
} RMS_filter_t;/**
* @brief 均方根滤波器
* @param[in,out] RMS_filter 滤波器指针
* @param[in] data 输入数据
* @return 滤波后结果
*/
_FAST float RMS_filter(RMS_filter_t *RMS_filter,const float data);例如:
filtered_data = RMS_filter(&rms_filter, raw_data);这行代码对raw_data进行均方根滤波。
math_filter.c中还有滑动均值滤波器,使用方法类似。卡尔曼滤波器算法十分简单,但暂未封装。
math目录下还有一些其他算法,如矩阵运算、向量运算等。需要时可以查看。