22电控任务
- 超抗(5月底)前,每人负责的兵种代码架构要了解个大概(默认都学会了FreeRTOS+双环PID
- 平时有空就折磨一下师兄,让他布置点pid任务给你们上手试试(工程之前是把抬升和小云台的PID任务给我玩了,虽然没上我的
- 几种滤波的学习与使用
- 目前我们21级电控对算法部分是有缺失的,只有张师兄了解系统辨识和lqr部分,有能力者尽快学习,对明年调参有种重大意义。可能会在暑假留校的时候布置这个任务
- 四元素解算(在步兵英雄代码里应该有写,ins_task,了解一下?这个控制解算算法挺有意思的)
- 缺资料或者没想法找我们(优质解答:我不知道
还有个是,调试软件的使用
- jlink+jscope+keil
- ozone+jlink(用ozone可以实现脱离keil,比如vscode+有个什么嵌入式插件的,或者clion)
- vofa++串口
工程抬升
题目
情景:你需要通过遥控器摇杆来控制工程的抬升平台,在没有摇杆输入量时,抬升平台需要保持之前的位置
实现方法很多,你可以脑洞大开,你可以自己写一个函数或者利用下面的来控制。
所有可用的外设结构体
typedef __packed struct
{
Motor_t Lift_Motor;
}Lift_UP_t;
电机封装
/*电机结构体*/
typedef __packed struct
{
uint8_t Pos_Lock; //位置锁
int32_t ExpSpeed; //期望速度
float ExpRadian; //期望角度
pid_parameter_t SPID; //速度环PID
pid_parameter_t PPID; //位置环PID
Encoder_t *Encoder; //码盘
MotorType_e MotorType;//电机种类
int16_t temp; //温度
int16_t anper; //当前电流值
float torque; //力矩
}Motor_t;
你该知道:3508能提供的数据只有 小圈位置、速度、温度、电流值
/*CAN数据处理-码盘处理*/
typedef __packed struct {
int32_t Encode_Record_Val;//累积码盘值(在小圈内)
int32_t Encode_Actual_Val;//真实码盘值(一大圈)
float Radian; //真实弧度
float Lock_Radian;
float User_Radian;
float Total_Radian; //记录的弧度
uint8_t Init_Flag;
int16_t Speed[2]; //0为旧速度,1为新速度
int16_t AccSpeed; //加速度
int16_t position; //未处理的Can原始码盘
int16_t last_position; //未处理的上次的Can原始码盘
int16_t lap_encoder; //编码器单圈码盘值(8192=12bit)
Encoder_Type_e Encoder_Type;//编码器种类
bool_t Block_Detect_Enable; //堵转检测开启否
STATE_e State; //电机状态
int16_t gear_Ratio; //减速比
int16_t Max_Block_Angle_Num;//堵转最大角度容忍值,可以调整这个来控制堵转灵敏度
int16_t Max_Block_Num; //堵转计数器最大值
int32_t Block_Count;
void (*User_Fun)(void); //用户自定义函数
} Encoder_t;
提示:Encode_Record_Val是3508小圈累加的位置数值,Speed[1]和position是CAN接收到的原生数据,所有encoder的计算将在CAN中断中完成,你无需关心怎么实现,到手即用。
PID
/*************************** Dongguan-University of Technology -ACE**************************
* @file pid.h
* @author 郑杰瀚
* @version V1.0.3
* @date 2022/11/19
* @brief
******************************************************************************
* @verbatim
* 已经把常用的pid模式都加进去了,还有滤波器一些没有实现
* 使用方法:
* 先创建一个pid结构体。
* 初始化:
* 先使用PidInit,注意模式是在这里设置,用 | 链接
* 然后使用PidInitMode将每个模式逐个赋值
* 使用:PidCalculate
* pid_clear
* User_Fun_Callback_Register
* demo:
* pid_parameter_t motor6020pid_s;//定义一个pid结构体
* PidInit(&motor6020pid_s,20,0,0,Output_Limit | Integral_Limit);//使用输出限幅和积分限幅模式
* PidInitMode(&motor6020pid_s,Integral_Limit,1000,0);//积分限幅模式设置
* PidInitMode(&motor6020pid_s,Output_Limit,30000,0);//输出限幅模式设置
* while(1)
* {
* ActualValue = sersor(); //获取数据
* setvalue = setclaculate(); //获取数据
* PidCalculate(&motor6020pid_s,setvalue,ActualValue); //计算
* }
*
* @attention
* 请确保User_Fun_Callback_Register注册了函数再使用结构体中的自定义函数
* 死区默认是使用的,如果不使用,不用理会就行,使用了会在死区内清除pid。
* @version
* v1.0.1 添加了步进式PID,所有功能待验证
* V1.0.2 添加了很多注释,Integral_Limit ,Output_Limit ,StepIn模式已验证,Derivative_On_Measurement似乎有点问题
* V1.0.3 添加了积分和输出滤波,待验证
************************** Dongguan-University of Technology -ACE***************************/
#ifndef __PID_H
#define __PID_H
#include "struct_typedef.h"
#include "stm32f4xx_hal.h"
typedef enum
{
NONE = 0X00, // 0000 0000 无
Deadzone = 0x01, // 0000 0001 死区
Integral_Limit = 0x02, // 0000 0010 积分限幅
Output_Limit = 0x04, // 0000 0100 输出限幅
Derivative_On_Measurement = 0x08, // 0000 1000 微分先行 TODO:
Separated_Integral = 0x10, // 0001 0000 积分分离
ChangingIntegrationRate = 0x20, // 0010 0000 变积分
OutputFilter = 0x40, // 0100 0000 输出滤波
DerivativeFilter = 0x80, // 1000 0000 微分滤波
StepIn = 0x0100, // 0000 0001 0000 0000 步进式
} PID_mode_e;
//一阶低通滤波参数
typedef __packed struct
{
fp32 input; //输入数据
fp32 last_input; //上次数据
fp32 out; //滤波输出的数据
fp32 num; //滤波参数
} first_order_filter_t;
typedef __packed struct Pid_parameter_t // pid结构体变量
{
fp32 Kp;
fp32 Ki;
fp32 Kd;
fp32 SetValue;
fp32 LastSetValue;
fp32 LastActualValue;
fp32 ActualValue;
fp32 Ierror;
fp32 Pout;
fp32 Iout;
fp32 Dout;
fp32 out;
fp32 Derror; //微分项
fp32 LastDerror;
fp32 LastLastDerror;
fp32 error; //误差项
fp32 LastError;
fp32 max_out; //最大输出
uint32_t mode; // pid模式
/* 积分限幅 */
fp32 max_Ierror; //最大积分输出
/* 误差死区 */
fp32 deadband;
/* 积分分离 */
fp32 threshold_max; //积分分离最大值
fp32 threshold_min; //积分分离最小值
/* 抗积分饱和 */
// fp32 maximum; //最大值
// fp32 minimum; //最小值
/* 变积分 */
fp32 errorabsmax; //偏差绝对值最大值
fp32 errorabsmin; //偏差绝对值最小值
/* 微分先行 */
fp32 gama; //微分先行滤波系数
/* 微分滤波 */
first_order_filter_t d_filter; //微分滤波结构体
/* 输出滤波 */
first_order_filter_t out_filter; //输出滤波结构体
/* 步进数 */
fp32 stepIn;
void (*User_Fun)(struct Pid_parameter_t *);
} pid_parameter_t;
void PidInit(pid_parameter_t *pid, fp32 kp, fp32 ki, fp32 kd, uint32_t mode);
void PidInitMode(pid_parameter_t *pid, uint32_t mode, fp32 num1, fp32 num2);
void pid_clear(pid_parameter_t *pid);
void User_Fun_Callback_Register(pid_parameter_t *pid, void (*User_Fun)(struct Pid_parameter_t *));
fp32 PidCalculate(pid_parameter_t *pid, fp32 SetValue, fp32 ActualValue);
上述为PID函数的结构体和函数,你可以在主函数里使用他
CAN
CAN接收中断、初始化已经写好,你无需关心。
抬升电机CAN Stdid=0x207
/************************** Dongguan-University of Technology -ACE**************************
* @file bsp_can.h
* @brief
* @author pansyhou侯文辉 ([email protected])
* @version 1.0
* @date 2022-07-24
*
*
* @history
* <table>
* Date Version Author Description
* 2022-07-24 1.0 侯文辉
* 2022-12-06 2.0 侯文辉
* @verbatim
* ==============================================================================
* 文件内容捏
* ==============================================================================
* @endverbatim
************************** Dongguan-University of Technology -ACE***************************/
#ifndef _ECF_BSP_CAN
#define _ECF_BSP_CAN
#include "fifo.h"
#include "can.h"
//CAN我方接收ID
typedef enum {
CAN_CHASSIS_ALL_ID = 0x200,
CAN_M3508_MOTOR1_ID = 0x201, //抬升电机
CAN_M3508_MOTOR2_ID = 0x202, //pitch
CAN_M3508_MOTOR3_ID = 0x203, //roll
CAN_M3508_MOTOR4_ID = 0x204,
CAN_M3508_MOTOR5_ID = 0x205,
CAN_M3508_MOTOR6_ID = 0x206,
CAN_M3508_MOTOR7_ID = 0x207,
CAN_M3508_MOTOR8_ID = 0x208,
CAN_GM6020_MOTOR1_ID = 0x205,
CAN_GM6020_MOTOR2_ID = 0x206,
CAN_GM6020_MOTOR3_ID = 0x207,
CAN_GM6020_MOTOR4_ID = 0x208,
CAN_GM6020_MOTOR5_ID = 0x209,
CAN_GM6020_MOTOR6_ID = 0x20A,
CAN_GM6020_MOTOR7_ID = 0x20B
} CAN_msg_e;
//CAN电调接收端id
typedef enum {
C620_OR_C610_1_TO_4_ID = 0x200,
C620_OR_C610_5_TO_8_ID = 0x1FF,
GM6020_1_TO_4_ID = 0x1FF,
GM6020_5_TO_7_ID = 0x2FF,
} ESC_Receive_Stdid;
typedef void (*can_stdmsg_rx_callback_t)(CAN_RxHeaderTypeDef *header, uint8_t *data);//数据处理指针
// typedef struct can_manage_obj *can_manage_obj_t;
//CAN管理结构体
typedef struct can_manage_obj
{
CAN_HandleTypeDef *hcan;//CAN句柄
fifo_t tx_fifo; //FIFO结构体
uint8_t *tx_fifo_buffer;//FIFO缓冲区
uint8_t is_sending; //CAN是否在发送
can_stdmsg_rx_callback_t can_rec_callback;//对应的CAN接收回调函数指针
}can_manage_obj;
//CAN数据包结构体
typedef struct can_std_msg
{
uint32_t std_id;
uint8_t dlc;
uint8_t data[8];
}can_std_msg;
extern struct can_manage_obj can1_manage;
extern struct can_manage_obj can2_manage;
extern void ECF_CAN_Init(void);
extern int32_t ECF_CAN_Rx_Callback_Register(can_manage_obj *m_obj, can_stdmsg_rx_callback_t fun);
extern uint32_t ECF_CAN_Send_Msg_FIFO(CAN_HandleTypeDef *hcan, uint16_t std_id, uint8_t *data, uint16_t len);
/**************堵塞式发送函数**************/
extern void ECF_CAN1_Send_Msg_Block(int16_t stdid, uint8_t *data, uint16_t len);
extern void ECF_CAN2_Send_Msg_Block(int16_t stdid, uint8_t *data, uint16_t len);
/**************普通可能会丢包的发送函数**************/
extern void ECF_CAN1_Send_Msg_NO_Block(int16_t stdid, uint8_t *data, uint16_t len);
extern void ECF_CAN2_Send_Msg_NO_Block(int16_t stdid, uint8_t *data, uint16_t len);
//高级封装发送函数,只为发送到电机 详细注释看函数体吧
extern void CAN1_C620_OR_C610_201_TO_204_SendMsg(int16_t ESC_201, int16_t ESC_202, int16_t ESC_203, int16_t ESC_204);
extern void CAN1_C620_OR_C610_205_TO_208_SendMsg(int16_t ESC_201, int16_t ESC_202, int16_t ESC_203, int16_t ESC_204);
extern void CAN1_GM6020_1_TO_4_SendMsg(int16_t ESC_201, int16_t ESC_202, int16_t ESC_203, int16_t ESC_204);
extern void CAN1_GM6020_5_TO_8_SendMsg(int16_t ESC_201, int16_t ESC_202, int16_t ESC_203, int16_t ESC_204);
extern void CAN_FIFO_Stress_Watch(void);
#endif
Bsp can API如下,你可以择其一个高级封装发送函数发送电机电流,可以改变初始化选择什么方式发送(如fifo等等,仅做了解,实际已经初始化了。
初始化函数
CAN 接收中断已经在main函数里初始化好了
//you can add some global variable right here
void Init(Lift_UP_t *L){
//your code add here
}
主函数
该函数你可以理解为跑在main函数的死循环里,入参的UP_IN为遥控器摇杆量,范围在-660至660
//you can add some global variable right here
void Lift_Up_Drive(Lift_UP_t *L,int32_t UP_IN){
//your code add here
}