add tutorial

This commit is contained in:
NeoZng
2022-11-12 18:03:18 +08:00
parent 0fb67070b8
commit 82a55d7eca
27 changed files with 341 additions and 114 deletions

View File

@@ -0,0 +1,32 @@
# algorithms
<p align='right'>neozng1@hnu.edu.cn</p>
> TODO:
>
> 1. 实现麦轮和全向轮的速度解算
> 2. 实现LQR
> 3. 实现一些通用的滤波器,如指数平均,窗平均,低通等
> 4. 实现系统辨识
## 总览和使用
module层的algorithm提供了一些供其他模块以及app的应用层使用的算法包括
1. PID控制器`controller.h`
2. crc8 crc16循环冗余校验
3. 卡尔曼滤波器`kalman_filter.h`,可以通过用户自定义函数配置为扩展卡尔曼滤波
4. `LQR.h`,线性二次型调节器
5. `QuaterninoEKF.h`,用于`ins_task`的四元数姿态解算和扩展卡尔曼滤波融合
6. `user_lib.h`,一些通用的函数,包括限幅、数据类型转换、角度弧度转换、快速符号判断以及优化开方等功能。多个模块都会使用的、不好区分的函数可以放置于此
## 代码结构
.c 为算法的实现,.h为算法对外接口的头文件
在编写应用的时候,你基本不会使用这里的函数,或是修改其实现。
若发现bug或需要增加功能联系组长讨论。

View File

@@ -2,7 +2,8 @@
******************************************************************************
* @file user_lib.c
* @author Wang Hongxi
* @version V1.0.0
* @author modified by neozng
* @version 0.2 beta
* @date 2021/2/18
* @brief
******************************************************************************
@@ -24,7 +25,7 @@
uint8_t GlobalDebugMode = 7;
//快速开方
// 快速开方
float Sqrt(float x)
{
float y;
@@ -51,29 +52,7 @@ float Sqrt(float x)
return y;
}
/**
* @brief 斜波函数计算,根据输入的值进行叠加, 输入单位为 /s 即一秒后增加输入的值
* @author RM
* @param[in] 斜波函数结构体
* @param[in] 输入值
* @retval 返回空
*/
float ramp_calc(ramp_function_source_t *ramp_source_type, float input)
{
ramp_source_type->input = input;
ramp_source_type->out += ramp_source_type->input * ramp_source_type->frame_period;
if (ramp_source_type->out > ramp_source_type->max_value)
{
ramp_source_type->out = ramp_source_type->max_value;
}
else if (ramp_source_type->out < ramp_source_type->min_value)
{
ramp_source_type->out = ramp_source_type->min_value;
}
return ramp_source_type->out;
}
//绝对值限制
// 绝对值限制
float abs_limit(float num, float Limit)
{
if (num > Limit)
@@ -87,7 +66,7 @@ float abs_limit(float num, float Limit)
return num;
}
//判断符号位
// 判断符号位
float sign(float value)
{
if (value >= 0.0f)
@@ -100,7 +79,7 @@ float sign(float value)
}
}
//浮点死区
// 浮点死区
float float_deadband(float Value, float minValue, float maxValue)
{
if (Value < maxValue && Value > minValue)
@@ -120,7 +99,7 @@ int16_t int16_deadline(int16_t Value, int16_t minValue, int16_t maxValue)
return Value;
}
//限幅函数
// 限幅函数
float float_constrain(float Value, float minValue, float maxValue)
{
if (Value < minValue)
@@ -131,7 +110,7 @@ float float_constrain(float Value, float minValue, float maxValue)
return Value;
}
//限幅函数
// 限幅函数
int16_t int16_constrain(int16_t Value, int16_t minValue, int16_t maxValue)
{
if (Value < minValue)
@@ -142,7 +121,7 @@ int16_t int16_constrain(int16_t Value, int16_t minValue, int16_t maxValue)
return Value;
}
//循环限幅函数
// 循环限幅函数
float loop_float_constrain(float Input, float minValue, float maxValue)
{
if (maxValue < minValue)
@@ -169,9 +148,9 @@ float loop_float_constrain(float Input, float minValue, float maxValue)
return Input;
}
//弧度格式化为-PI~PI
// 弧度格式化为-PI~PI
//角度格式化为-180~180
// 角度格式化为-180~180
float theta_format(float Ang)
{
return loop_float_constrain(Ang, -180.0f, 180.0f);

View File

@@ -87,39 +87,9 @@ extern uint8_t GlobalDebugMode;
#define VAL_MIN(a, b) ((a) < (b) ? (a) : (b))
#define VAL_MAX(a, b) ((a) > (b) ? (a) : (b))
typedef struct
{
float input; //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
float out; //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
float min_value; //<2F>޷<EFBFBD><DEB7><EFBFBD>Сֵ
float max_value; //<2F>޷<EFBFBD><DEB7><EFBFBD><EFBFBD>ֵ
float frame_period; //ʱ<><CAB1><EFBFBD><EFBFBD>
} ramp_function_source_t;
typedef struct
{
uint16_t Order;
uint32_t Count;
float *x;
float *y;
float k;
float b;
float StandardDeviation;
float t[4];
} Ordinary_Least_Squares_t;
//<2F><><EFBFBD>ٿ<EFBFBD><D9BF><EFBFBD>
float Sqrt(float x);
//б<><D0B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC>
void ramp_init(ramp_function_source_t *ramp_source_type, float frame_period, float max, float min);
//б<><D0B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
float ramp_calc(ramp_function_source_t *ramp_source_type, float input);
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
float abs_limit(float num, float Limit);
//<2F>жϷ<D0B6><CFB7><EFBFBD>λ

View File

View File

View File

@@ -0,0 +1,3 @@
can_comm
双板CAN通信

6
modules/led_light/led.md Normal file
View File

@@ -0,0 +1,6 @@
# led_task
<p align='right'>neozng1@hnu.edu.cn</p>
> TODO:
> 1. 预计添加不同错误标志,将错误类型和灯的闪烁频率或颜色等对应起来,方便调试

View File

@@ -12,11 +12,14 @@
/* use usart1 as vision communication*/
static Vision_Recv_s recv_data;
// @todo:由于后续需要进行IMU-Cam的硬件触发采集控制,因此可能需要将发送设置为定时任务,或由IMU采集完成产生的中断唤醒的任务,
// 使得时间戳对齐. 因此,在send_data中设定其他的标志位数据,让ins_task填充姿态值.
// static Vision_Send_s send_data;
static usart_instance vision_usart_instance;
/**
* @brief 接收解包回调函数,将在bsp_usart.c中被usart rx callback调用
* @todo 1.提高可读性,将get_protocol_info的第四个参数增加一个float buffer
* @todo 1.提高可读性,将get_protocol_info的第四个参数增加一个float类型buffer
* 2.添加标志位解码
*/
static void DecodeVision()
@@ -38,8 +41,10 @@ Vision_Recv_s* VisionInit(UART_HandleTypeDef *handle)
/**
* @brief 发送函数
* @todo 1.提高可读性,将get_protocol_info的第个参数增加一个float buffer
* @todo 1.提高可读性,将get_protocol_send_data的第6个参数增加一个float buffer
* 2.添加标志位解码
* 3.由于后续需要进行IMU-Cam的硬件触发采集控制,因此可能需要将发送设置为定时任务
* 或由IMU采集完成产生的中断唤醒的任务,使得时间戳对齐.
*
* @param send 待发送数据
*/
@@ -53,3 +58,9 @@ void VisionSend(Vision_Send_s *send)
get_protocol_send_data(0x02, flag_register, &send->yaw, 3, send_buff, &tx_len);
USARTSend(&vision_usart_instance, send_buff, tx_len);
}
Vision_Recv_s* VisionGetcmd()
{
return &recv_data;
}

View File

@@ -22,4 +22,11 @@ Vision_Recv_s *VisionInit(UART_HandleTypeDef *handle);
*/
void VisionSend(Vision_Send_s *send);
/**
* @brief 返回所需的上位机数据
*
* @return Vision_Recv_s* 数据结构体指针
*/
Vision_Recv_s* VisionGetcmd();
#endif // !MASTER_PROCESS_H

View File

@@ -6,7 +6,7 @@
#define PROTOCOL_CMD_ID 0XA5
#define OFFSET_BYTE 8 //出数据段外,其他部分所占字节数
#define OFFSET_BYTE 8 // 出数据段外,其他部分所占字节数
typedef enum
{
@@ -77,7 +77,7 @@ typedef struct
float pitch;
float roll;
// uint32_t time_stamp;
// uint32_t time_stamp; // @todo 用于和相机的时间戳对齐
} Vision_Send_s;
typedef struct
@@ -86,23 +86,23 @@ typedef struct
{
uint8_t sof;
uint16_t data_length;
uint8_t crc_check; //帧头CRC校验
} header; //数据帧头
uint16_t cmd_id; //数据ID
uint16_t frame_tail; //帧尾CRC校验
uint8_t crc_check; // 帧头CRC校验
} header; // 数据帧头
uint16_t cmd_id; // 数据ID
uint16_t frame_tail; // 帧尾CRC校验
} protocol_rm_struct;
/*更新发送数据帧,并计算发送数据帧长度*/
void get_protocol_send_data(uint16_t send_id, //信号id
void get_protocol_send_data(uint16_t send_id, // 信号id
uint16_t flags_register, // 16位寄存器
float *tx_data, //待发送的float数据
float *tx_data, // 待发送的float数据
uint8_t float_length, // float的数据长度
uint8_t *tx_buf, //待发送的数据帧
uint16_t *tx_buf_len); //待发送的数据帧长度
uint8_t *tx_buf, // 待发送的数据帧
uint16_t *tx_buf_len); // 待发送的数据帧长度
/*接收数据处理*/
uint16_t get_protocol_info(uint8_t *rx_buf, //接收到的原始数据
uint16_t *flags_register, //接收数据的16位寄存器地址
float *rx_data); //接收的float数据存储地址
uint16_t get_protocol_info(uint8_t *rx_buf, // 接收到的原始数据
uint16_t *flags_register, // 接收数据的16位寄存器地址
float *rx_data); // 接收的float数据存储地址
#endif

View File

@@ -2,13 +2,11 @@
* @file dji_motor.h
* @author neozng
* @brief DJI智能电机头文件
* @version 0.1
* @version 0.2
* @date 2022-11-01
*
* @todo 1. 给不同的电机设置不同的低通滤波器惯性系数而不是统一使用宏
2. 当前电机初始化函数`DJIMotorInit()`稍显凌乱,
应设置一个`dji_motor_init_config_s`结构体用于电机初始化,使得风格统一,提高可读性
3. 为M2006和M3508增加开环的零位校准函数
2. 为M2006和M3508增加开环的零位校准函数
* @copyright Copyright (c) 2022 HNU YueLu EC all rights reserved
*
@@ -28,13 +26,13 @@
/* DJI电机CAN反馈信息*/
typedef struct
{
uint16_t ecd;
uint16_t ecd; // 0-8192
uint16_t last_ecd;
int16_t speed_rpm;
int16_t given_current;
int16_t speed_rpm; // rounds per minute
int16_t given_current; // 实际电流
uint8_t temperate;
int16_t total_round;
int32_t total_angle;
int16_t total_round; // 总圈数,注意方向
int32_t total_angle; // 总角度,注意方向
} dji_motor_measure;
/**
@@ -66,19 +64,18 @@ typedef struct
/**
* @brief 调用此函数注册一个DJI智能电机,需要传递较多的初始化参数,请在application初始化的时候调用此函数
* 推荐传参时像标准库一样构造initStructure然后传入此函数.
* recommend: type xxxinitStructure = {
* .member1=xx,
* .member2=xx,
* ....};
* recommend: type xxxinitStructure = {.member1=xx,
* .member2=xx,
* ....};
* 请注意不要在一条总线上挂载过多的电机(超过6个),若一定要这么做,请降低每个电机的反馈频率(设为500Hz),
* 并减小DJIMotorControl()任务的运行频率.
*
* @attention M3508和M2006的反馈报文都是0x200+id,而GM6020的反馈是0x204+id,请注意前两者和后者的id不要冲突.
* 如果产生冲突,在初始化电机的时候会进入IDcrash_Handler(),可以通过debug来判断是否出现冲突.
*
*
* @param config 电机初始化结构体,包含了电机控制设置,电机PID参数设置,电机类型以及电机挂载的CAN设置
*
* @return dji_motor_instance*
*
* @return dji_motor_instance*
*/
dji_motor_instance *DJIMotorInit(Motor_Init_Config_s config);

View File

@@ -5,8 +5,7 @@
> TODO:
>
> 1. 给不同的电机设置不同的低通滤波器惯性系数而不是统一使用宏
> 2. 当前电机初始化函数`DJIMotorInit()`稍显凌乱,应设置一个`dji_motor_init_config_s`结构体用于电机初始化,使得风格统一,提高可读性
> 3. 为M2006和M3508增加开环的零位校准函数
> 2. 为M2006和M3508增加开环的零位校准函数

View File

View File

View File

0
modules/remote/remote.md Normal file
View File

View File