diff --git a/Src/freertos.c b/Src/freertos.c index 5d0b689..4ad8363 100644 --- a/Src/freertos.c +++ b/Src/freertos.c @@ -32,6 +32,7 @@ #include "master_process.h" #include "daemon.h" #include "robot.h" +#include "HT04.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -73,13 +74,13 @@ void StartROBOTTASK(void const *argument); void StartUITASK(void const *argument); /* USER CODE END FunctionPrototypes */ -void StartDefaultTask(void const * argument); +void StartDefaultTask(void const *argument); extern void MX_USB_DEVICE_Init(void); void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ /* GetIdleTaskMemory prototype (linked to static allocation support) */ -void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ); +void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize); /* USER CODE BEGIN GET_IDLE_TASK_MEMORY */ static StaticTask_t xIdleTaskTCBBuffer; @@ -95,11 +96,12 @@ void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackTyp /* USER CODE END GET_IDLE_TASK_MEMORY */ /** - * @brief FreeRTOS initialization - * @param None - * @retval None - */ -void MX_FREERTOS_Init(void) { + * @brief FreeRTOS initialization + * @param None + * @retval None + */ +void MX_FREERTOS_Init(void) +{ /* USER CODE BEGIN Init */ /* USER CODE END Init */ @@ -127,8 +129,9 @@ void MX_FREERTOS_Init(void) { /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ - osThreadDef(instask, StartINSTASK, osPriorityNormal, 0, 1024); - insTaskHandle = osThreadCreate(osThread(instask), NULL); + osThreadDef(instask, StartINSTASK, osPriorityAboveNormal, 0, 1024); + insTaskHandle = osThreadCreate(osThread(instask), NULL); // 由于是阻塞读取传感器,为姿态解算设置较高优先级,确保以1khz的频率执行 + // 后续修改为读取传感器数据准备好的中断处理, osThreadDef(motortask, StartMOTORTASK, osPriorityNormal, 0, 256); motorTaskHandle = osThreadCreate(osThread(motortask), NULL); @@ -143,7 +146,6 @@ void MX_FREERTOS_Init(void) { defaultTaskHandle = osThreadCreate(osThread(uitask), NULL); /* USER CODE END RTOS_THREADS */ - } /* USER CODE BEGIN Header_StartDefaultTask */ @@ -153,12 +155,12 @@ void MX_FREERTOS_Init(void) { * @retval None */ /* USER CODE END Header_StartDefaultTask */ -void StartDefaultTask(void const * argument) +void StartDefaultTask(void const *argument) { /* init code for USB_DEVICE */ MX_USB_DEVICE_Init(); /* USER CODE BEGIN StartDefaultTask */ - vTaskDelete( NULL ); + vTaskDelete(NULL); /* USER CODE END StartDefaultTask */ } @@ -170,13 +172,15 @@ void StartINSTASK(void const *argument) { // 1kHz INS_Task(); - VisionSend(); // 解算完成后发送视觉数? + VisionSend(); // 解算完成后发送视觉数�? osDelay(1); } } void StartMOTORTASK(void const *argument) { + // 若使用HT电机则取消本行注释 + HTMotorControlInit(); while (1) { // 500Hz diff --git a/bsp/dwt/bsp_dwt.c b/bsp/dwt/bsp_dwt.c index 58ab213..ce2e4fa 100644 --- a/bsp/dwt/bsp_dwt.c +++ b/bsp/dwt/bsp_dwt.c @@ -21,11 +21,14 @@ static osMutexId DWT_MUTEX; /** * @brief 私有函数,用于检查DWT CYCCNT寄存器是否溢出,并更新CYCCNT_RountCount * @attention 此函数假设两次调用之间的时间间隔不超过一次溢出 + * + * @todo 更好的方案是为dwt的时间更新单独设置一个任务 + * 不过,使用dwt的初衷是定时不被中断/任务等因素影响,因此该实现仍然有其存在的意义 * */ static void DWT_CNT_Update(void) { - if (__get_CONTROL()) // 不在中断中,使用互斥锁;在中断则直接执行即可 + if (__get_CONTROL()) // 不在中断中,使用互斥锁;在中断则直接执行即可,本框架将所有中断优先级设置为相同,故不会被其他中断重入 if (osOK != osMutexWait(DWT_MUTEX, 0)) return; diff --git a/modules/algorithm/user_lib.c b/modules/algorithm/user_lib.c index f3806c5..eb3b021 100644 --- a/modules/algorithm/user_lib.c +++ b/modules/algorithm/user_lib.c @@ -192,4 +192,18 @@ void Cross3d(float *v1, float *v2, float *res) float Dot3d(float *v1, float *v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; -} \ No newline at end of file +} + +// 均值滤波,删除buffer中的最后一个元素,填入新的元素并求平均值 +float AverageFilter(float new_data, float *buf, uint8_t len) +{ + float sum = 0; + for (uint8_t i = 0; i < len - 1; i++) + { + buf[i] = buf[i + 1]; + sum += buf[i]; + } + buf[len - 1] = new_data; + sum += new_data; + return sum / len; +} diff --git a/modules/algorithm/user_lib.h b/modules/algorithm/user_lib.h index 3028f46..4404c46 100644 --- a/modules/algorithm/user_lib.h +++ b/modules/algorithm/user_lib.h @@ -12,20 +12,16 @@ */ #ifndef _USER_LIB_H #define _USER_LIB_H + + #include "stdint.h" #include "main.h" #include "cmsis_os.h" -enum -{ - CHASSIS_DEBUG = 1, - GIMBAL_DEBUG, - INS_DEBUG, - RC_DEBUG, - IMU_HEAT_DEBUG, - SHOOT_DEBUG, - AIMASSIST_DEBUG, -}; + +#define msin(x) (arm_sin_f32(x)) +#define mcos(x) (arm_cos_f32(x)) + extern uint8_t GlobalDebugMode; @@ -89,40 +85,41 @@ extern uint8_t GlobalDebugMode; /** * @brief 返回一块干净的内�?,不过仍然需要强制转�?为你需要的类型 - * + * * @param size 分配大小 - * @return void* + * @return void* */ -void* zero_malloc(size_t size); +void *zero_malloc(size_t size); -//���ٿ��� +// ���ٿ��� float Sqrt(float x); -//�������� +// �������� float abs_limit(float num, float Limit); -//�жϷ���λ +// �жϷ���λ float sign(float value); -//�������� +// �������� float float_deadband(float Value, float minValue, float maxValue); -//�޷����� +// �޷����� float float_constrain(float Value, float minValue, float maxValue); -//�޷����� +// �޷����� int16_t int16_constrain(int16_t Value, int16_t minValue, int16_t maxValue); -//ѭ���޷����� +// ѭ���޷����� float loop_float_constrain(float Input, float minValue, float maxValue); -//�Ƕ� ���޷� 180 ~ -180 +// �Ƕ� ���޷� 180 ~ -180 float theta_format(float Ang); int float_rounding(float raw); -float* Norm3d(float* v); +float *Norm3d(float *v); -float NormOf3d(float* v); +float NormOf3d(float *v); -void Cross3d(float* v1, float* v2, float* res); +void Cross3d(float *v1, float *v2, float *res); -float Dot3d(float* v1, float* v2); +float Dot3d(float *v1, float *v2); + +float AverageFilter(float new_data, float *buf, uint8_t len); -//���ȸ�ʽ��Ϊ-PI~PI #define rad_format(Ang) loop_float_constrain((Ang), -PI, PI) #endif diff --git a/modules/imu/ins_task.c b/modules/imu/ins_task.c index c9ef358..4c05f06 100644 --- a/modules/imu/ins_task.c +++ b/modules/imu/ins_task.c @@ -99,7 +99,7 @@ attitude_t *INS_Init(void) // noise of accel is relatively big and of high freq,thus lpf is used INS.AccelLPF = 0.0085; - DWT_GetDeltaT64(&INS_DWT_Count); + DWT_GetDeltaT(&INS_DWT_Count); return (attitude_t *)&INS.Gyro; // @todo: 这里偷懒了,不要这样做! 修改INT_t结构体可能会导致异常,待修复. } diff --git a/modules/led/led.h b/modules/led/led.h index 0be2506..0741315 100644 --- a/modules/led/led.h +++ b/modules/led/led.h @@ -12,7 +12,7 @@ typedef struct uint8_t led_alpha; // 透明度,通过pwm频率改变 uint8_t led_brightness; // 亮度,通过电压改变(如果可以,使用dac) uint8_t led_color; // rgb value,0-255 - uint8_t led_switch // 开关,on1 off0 + uint8_t led_switch; // 开关,on1 off0 // void (*action_callback)(void); // led动作回调函数 } LEDInstance; diff --git a/modules/motor/DJImotor/dji_motor.c b/modules/motor/DJImotor/dji_motor.c index 370f215..0100ff9 100644 --- a/modules/motor/DJImotor/dji_motor.c +++ b/modules/motor/DJImotor/dji_motor.c @@ -134,7 +134,7 @@ static void DecodeDJIMotor(CANInstance *_instance) RPM_2_ANGLE_PER_SEC * SPEED_SMOOTH_COEF * (float)((int16_t)(rxbuff[2] << 8 | rxbuff[3])); measure->real_current = (1.0f - CURRENT_SMOOTH_COEF) * measure->real_current + CURRENT_SMOOTH_COEF * (float)((int16_t)(rxbuff[4] << 8 | rxbuff[5])); - measure->temperate = rxbuff[6]; + measure->temperature = rxbuff[6]; // 多圈角度计算,前提是假设两次采样间电机转过的角度小于180°,自己画个图就清楚计算过程了 if (measure->ecd - measure->last_ecd > 4096) @@ -238,6 +238,7 @@ void DJIMotorControl() pid_ref = motor_controller->pid_ref; // 保存设定值,防止motor_controller->pid_ref在计算过程中被修改 if (motor_setting->motor_reverse_flag == MOTOR_DIRECTION_REVERSE) pid_ref *= -1; // 设置反转 + // pid_ref会顺次通过被启用的闭环充当数据的载体 // 计算位置环,只有启用位置环且外层闭环为位置时会计算速度环输出 if ((motor_setting->close_loop_type & ANGLE_LOOP) && motor_setting->outer_loop_type == ANGLE_LOOP) @@ -272,6 +273,9 @@ void DJIMotorControl() pid_ref = PIDCalculate(&motor_controller->current_PID, measure->real_current, pid_ref); } + if (motor_setting->feedback_reverse_flag == FEEDBACK_DIRECTION_REVERSE) + pid_ref *= -1; + // 获取最终输出 set = (int16_t)pid_ref; diff --git a/modules/motor/DJImotor/dji_motor.h b/modules/motor/DJImotor/dji_motor.h index c2477f2..d83ba95 100644 --- a/modules/motor/DJImotor/dji_motor.h +++ b/modules/motor/DJImotor/dji_motor.h @@ -35,7 +35,7 @@ typedef struct float angle_single_round; // 单圈角度 float speed_aps; // 角速度,单位为:度/秒 int16_t real_current; // 实际电流 - uint8_t temperate; // 温度 Celsius + uint8_t temperature; // 温度 Celsius float total_angle; // 总角度,注意方向 int32_t total_round; // 总圈数,注意方向 @@ -48,7 +48,7 @@ typedef struct typedef struct { - DJI_Motor_Measure_s measure; // 电机测量值 + DJI_Motor_Measure_s measure; // 电机测量值 Motor_Control_Setting_s motor_settings; // 电机设置 Motor_Controller_s motor_controller; // 电机控制器 diff --git a/modules/motor/HTmotor/HT04.c b/modules/motor/HTmotor/HT04.c index f9f9b1b..4057290 100644 --- a/modules/motor/HTmotor/HT04.c +++ b/modules/motor/HTmotor/HT04.c @@ -1,10 +1,16 @@ #include "HT04.h" #include "memory.h" #include "general_def.h" -#include "bsp_dwt.h" +#include "user_lib.h" +#include "cmsis_os.h" +#include "string.h" +#include "daemon.h" +#include "stdlib.h" static uint8_t idx; -HTMotorInstance *ht_motor_instance[HT_MOTOR_CNT]; +static HTMotorInstance *ht_motor_instance[HT_MOTOR_CNT]; +static osThreadId ht_task_handle[HT_MOTOR_CNT]; +static uint8_t zero_buff[6] = {0}; /** * @brief 设置电机模式,报文内容[0xff,0xff,0xff,0xff,0xff,0xff,0xff,cmd] @@ -17,9 +23,8 @@ static void HTMotorSetMode(HTMotor_Mode_t cmd, HTMotorInstance *motor) memset(motor->motor_can_instace->tx_buff, 0xff, 7); // 发送电机指令的时候前面7bytes都是0xff motor->motor_can_instace->tx_buff[7] = (uint8_t)cmd; // 最后一位是命令id CANTransmit(motor->motor_can_instace, 1); - memset(motor->motor_can_instace->tx_buff, 0, 6); // 发送控制指令的时候前面6bytes都是0 + memcpy(motor->motor_can_instace->tx_buff, zero_buff, 6); } - /* 两个用于将uint值和float值进行映射的函数,在设定发送值和解析反馈值时使用 */ static uint16_t float_to_uint(float x, float x_min, float x_max, uint8_t bits) { @@ -43,28 +48,60 @@ static void HTMotorDecode(CANInstance *motor_can) { uint16_t tmp; // 用于暂存解析值,稍后转换成float数据,避免多次创建临时变量 uint8_t *rxbuff = motor_can->rx_buff; - HTMotor_Measure_t *measure = &((HTMotorInstance *)motor_can->id)->measure; // 将can实例中保存的id转换成电机实例的指针 - measure->feedback_dt = DWT_GetDeltaT(&measure->count); + HTMotorInstance *motor = (HTMotorInstance *)motor_can->id; + HTMotor_Measure_t *measure = &(motor->measure); // 将can实例中保存的id转换成电机实例的指针 + + DaemonReload(motor->motor_daemon); + measure->feed_dt = DWT_GetDeltaT(&measure->feed_cnt); measure->last_angle = measure->total_angle; tmp = (uint16_t)((rxbuff[1] << 8) | rxbuff[2]); - measure->total_angle = uint_to_float(tmp, P_MIN, P_MAX, 16) - measure->angle_bias; + measure->total_angle = uint_to_float(tmp, P_MIN, P_MAX, 16); - measure->speed_rads = // SPEED_SMOOTH_COEF * measure->speed_rads + (1 - SPEED_SMOOTH_COEF) * - (measure->total_angle - measure->last_angle) / measure->feedback_dt; + tmp = (uint16_t)(rxbuff[3] << 4) | (rxbuff[4] >> 4); + measure->speed_rads = AverageFilter((uint_to_float(tmp, V_MIN, V_MAX, 12) - HT_SPEED_BIAS), measure->speed_buff, SPEED_BUFFER_SIZE); - measure->real_current = uint_to_float(((uint16_t)(rxbuff[3] << 4) | (uint16_t)(rxbuff[4] >> 4)), V_MIN, V_MAX, 12); + tmp = (uint16_t)(((rxbuff[4] & 0x0f) << 8) | rxbuff[5]); + measure->real_current = CURRENT_SMOOTH_COEF * uint_to_float(tmp, T_MIN, T_MAX, 12) + + (1 - CURRENT_SMOOTH_COEF) * measure->real_current; + + +} - if (!measure->first_feedback_flag) // 初始化的时候设置偏置 - { - measure->first_feedback_flag = 1; - measure->angle_bias = measure->total_angle; - measure->total_angle = 0; - measure->speed_rads = 0; - DWT_GetDeltaT(&measure->count); - } +static void HTMotorLostCallback(void *motor_ptr) +{ + HTMotorInstance *motor = (HTMotorInstance *)motor_ptr; + if (motor->stop_flag == MOTOR_STOP) + return; + if (++motor->lost_cnt % 10 != 0) + HTMotorSetMode(CMD_MOTOR_MODE, motor); // 若不在停止模式,尝试重新让电机进入控制模式 +} - // measure->real_current = uint_to_float((rxbuff[3] << 8) | rxbuff[4], I_MIN, I_MAX, 16); +/* 海泰电机一生黑,什么垃圾协议! */ +void HTMotorCalibEncoder(HTMotorInstance *motor) +{ + uint16_t p, v, kp, kd, t; + p = float_to_uint(0, P_MIN, P_MAX, 16); + v = float_to_uint(0, V_MIN, V_MAX, 12); + kp = float_to_uint(0, KP_MIN, KP_MAX, 12); + kd = float_to_uint(0, KD_MIN, KD_MAX, 12); + t = float_to_uint(0, T_MIN, T_MAX, 12); + + uint8_t *buf = motor->motor_can_instace->tx_buff; + buf[0] = p >> 8; + buf[1] = p & 0xFF; + buf[2] = v >> 4; + buf[3] = ((v & 0xF) << 4) | (kp >> 8); + buf[4] = kp & 0xFF; + buf[5] = kd >> 4; + buf[6] = ((kd & 0xF) << 4) | (t >> 8); + buf[7] = t & 0xff; + memcpy(zero_buff, buf, 6); // 初始化的时候至少调用一次,故将其他指令为0时发送的报文保存一下,详见ht04电机说明 + CANTransmit(motor->motor_can_instace, 1); + DWT_Delay(0.005); + HTMotorSetMode(CMD_ZERO_POSITION, motor); + DWT_Delay(0.005); + // HTMotorSetMode(CMD_MOTOR_MODE, motor); } HTMotorInstance *HTMotorInit(Motor_Init_Config_s *config) @@ -83,34 +120,43 @@ HTMotorInstance *HTMotorInit(Motor_Init_Config_s *config) config->can_init_config.id = motor; motor->motor_can_instace = CANRegister(&config->can_init_config); + Daemon_Init_Config_s conf = { + .callback = HTMotorLostCallback, + .owner_id = motor, + .reload_count = 5, // 20ms + }; + motor->motor_daemon = DaemonRegister(&conf); + HTMotorEnable(motor); + HTMotorSetMode(CMD_MOTOR_MODE, motor); // 确保电机已经上电并执行电机模式 + DWT_Delay(0.05); + HTMotorCalibEncoder(motor); // 将当前编码器位置作为零位 + DWT_Delay(0.05); // 保证下一个电机发送时CAN是空闲的,注意应用在初始化模块的时候不应该进入中断 ht_motor_instance[idx++] = motor; return motor; } void HTMotorSetRef(HTMotorInstance *motor, float ref) { - motor->pid_ref = ref * 0.285f; + motor->pid_ref = ref; } -void HTMotorControl() +/** + * @brief 为了避免总线堵塞,为每个电机创建一个发送任务 + * @param argument 传入的电机指针 + */ +void HTMotorTask(void const *argument) { float set, pid_measure, pid_ref; + HTMotorInstance *motor = (HTMotorInstance *)argument; + HTMotor_Measure_t *measure = &motor->measure; + Motor_Control_Setting_s *setting = &motor->motor_settings; + CANInstance *motor_can = motor->motor_can_instace; uint16_t tmp; - HTMotorInstance *motor; - HTMotor_Measure_t *measure; - Motor_Control_Setting_s *setting; - CANInstance *motor_can; - // 遍历所有电机实例,计算PID - for (size_t i = 0; i < idx; i++) - { // 先获取地址避免反复寻址 - motor = ht_motor_instance[i]; - measure = &motor->measure; - setting = &motor->motor_settings; - motor_can = motor->motor_can_instace; + while (1) + { pid_ref = motor->pid_ref; - if ((setting->close_loop_type & ANGLE_LOOP) && setting->outer_loop_type == ANGLE_LOOP) { if (setting->angle_feedback_source == OTHER_FEED) @@ -145,36 +191,47 @@ void HTMotorControl() if (setting->motor_reverse_flag == MOTOR_DIRECTION_REVERSE) set *= -1; - LIMIT_MIN_MAX(set, T_MIN, T_MAX); // 限幅,实际上这似乎和pid输出限幅重复了 - tmp = float_to_uint(set, T_MIN, T_MAX, 12); // 数值最后在 -12~+12之间 + LIMIT_MIN_MAX(set, T_MIN, T_MAX); + tmp = float_to_uint(set, T_MIN, T_MAX, 12); if (motor->stop_flag == MOTOR_STOP) tmp = float_to_uint(0, T_MIN, T_MAX, 12); motor_can->tx_buff[6] = (tmp >> 8); motor_can->tx_buff[7] = tmp & 0xff; - CANTransmit(motor_can, 1); + CANTransmit(motor_can, 0.5); + + osDelay(1); + } +} + +void HTMotorControlInit() +{ + char ht_task_name[5] = "ht"; + // 遍历所有电机实例,创建任务 + if (!idx) + return; + for (size_t i = 0; i < idx; i++) + { + char ht_id_buff[2] = {0}; + __itoa(i, ht_id_buff, 10); + strcat(ht_task_name, ht_id_buff); // 似乎没什么吊用,osthreaddef会把第一个变量当作宏字符串传入,作为任务名 + // todo 还需要一个更优雅的方案来区分不同的电机任务 + osThreadDef(ht_task_name, HTMotorTask, osPriorityNormal, 0, 128); + ht_task_handle[i] = osThreadCreate(osThread(ht_task_name), ht_motor_instance[i]); } } void HTMotorStop(HTMotorInstance *motor) { - if (motor->stop_flag == MOTOR_STOP) - return; - HTMotorSetMode(CMD_RESET_MODE, motor); - HTMotorSetMode(CMD_RESET_MODE, motor); // 发两次,确保电机停止 motor->stop_flag = MOTOR_STOP; } void HTMotorEnable(HTMotorInstance *motor) { - if (motor->stop_flag == MOTOR_ENALBED) - return; - HTMotorSetMode(CMD_MOTOR_MODE, motor); - HTMotorSetMode(CMD_MOTOR_MODE, motor); motor->stop_flag = MOTOR_ENALBED; } -// void HTMotorCalibEncoder(HTMotorInstance *motor) -// { -// HTMotorSetMode(CMD_ZERO_POSITION, motor); -// } +void HTMotorOuterLoop(HTMotorInstance *motor, Closeloop_Type_e type) +{ + motor->motor_settings.outer_loop_type = type; +} diff --git a/modules/motor/HTmotor/HT04.h b/modules/motor/HTmotor/HT04.h index fdd8d7b..1ad61f5 100644 --- a/modules/motor/HTmotor/HT04.h +++ b/modules/motor/HTmotor/HT04.h @@ -5,10 +5,12 @@ #include "bsp_can.h" #include "controller.h" #include "motor_def.h" +#include "daemon.h" #define HT_MOTOR_CNT 4 #define CURRENT_SMOOTH_COEF 0.9f -#define SPEED_SMOOTH_COEF 0.85f +#define SPEED_BUFFER_SIZE 5 +#define HT_SPEED_BIAS -0.0109901428f // 电机速度偏差,单位rad/s #define P_MIN -95.5f // Radians #define P_MAX 95.5f @@ -16,18 +18,23 @@ #define V_MAX 45.0f #define T_MIN -18.0f // N·m #define T_MAX 18.0f +#define KP_MIN 0.0f // N-m/rad +#define KP_MAX 500.0f +#define KD_MIN 0.0f // N-m/rad/s +#define KD_MAX 5.0f typedef struct // HT04 { float total_angle; // 角度为多圈角度,范围是-95.5~95.5,单位为rad float last_angle; - float speed_rads; - float real_current; - float angle_bias; - uint8_t first_feedback_flag; - float feedback_dt; - uint32_t count; + float speed_rads; + float speed_buff[SPEED_BUFFER_SIZE]; + + float real_current; + + float feed_dt; + uint32_t feed_cnt; } HTMotor_Measure_t; /* HT电机类型定义*/ @@ -49,6 +56,9 @@ typedef struct Motor_Working_Type_e stop_flag; // 启停标志 CANInstance *motor_can_instace; + + DaemonInstance *motor_daemon; + uint32_t lost_cnt; } HTMotorInstance; /* HT电机模式,初始化时自动进入CMD_MOTOR_MODE*/ @@ -76,10 +86,10 @@ HTMotorInstance *HTMotorInit(Motor_Init_Config_s *config); void HTMotorSetRef(HTMotorInstance *motor, float ref); /** - * @brief 给所有的HT电机发送控制指令 + * @brief 初始化电机任务,若要使用,需要在motortask的死循环前调用 * */ -void HTMotorControl(); +void HTMotorControlInit(); /** * @brief 停止电机,之后电机不会响应HTMotorSetRef设定的值 @@ -96,7 +106,15 @@ void HTMotorStop(HTMotorInstance *motor); void HTMotorEnable(HTMotorInstance *motor); /** - * @brief 校准电机编码器,设置的当前位置为0 + * @brief 修改电机闭环对象 + * + * @param motor + * @param type + */ +void HTMotorOuterLoop(HTMotorInstance *motor, Closeloop_Type_e type); + +/** + * @brief 将当前编码器位置置零 * * @param motor */ diff --git a/modules/motor/HTmotor/HT04.md b/modules/motor/HTmotor/HT04.md index 7a7f4f8..ab6af23 100644 --- a/modules/motor/HTmotor/HT04.md +++ b/modules/motor/HTmotor/HT04.md @@ -9,8 +9,34 @@ 请注意发送和反馈数据的**单位**。 +> ~~HT的电机协议做的真不行,纯纯直接抄mit还抄不明白,之后全部换成LK!~~ +另一种上电时校准编码器的方法: +```c +static void HTMotorDecode(CANInstance *motor_can) +{ + uint16_t tmp; // 用于暂存解析值,稍后转换成float数据,避免多次创建临时变量 + uint8_t *rxbuff = motor_can->rx_buff; + HTMotor_Measure_t *measure = &((HTMotorInstance *)motor_can->id)->measure; // 将can实例中保存的id转换成电机实例的指针 + measure->last_angle = measure->total_angle; + tmp = (uint16_t)((rxbuff[1] << 8) | rxbuff[2]); + measure->total_angle = uint_to_float(tmp, P_MIN, P_MAX, 16) - measure->angle_bias; -> ~~HT的电机协议做的真不行,纯纯直接抄mit还抄不明白,之后全部换成LK!~~ \ No newline at end of file + tmp = (uint16_t)(rxbuff[3] << 4) | (rxbuff[4] >> 4); + measure->speed_rads = AverageFilter((uint_to_float(tmp, V_MIN, V_MAX, 12) - HT_SPEED_BIAS), measure->speed_buff, SPEED_BUFFER_SIZE); + + tmp = (uint16_t)(((rxbuff[4] & 0x0f) << 8) | rxbuff[5]); + measure->real_current = CURRENT_SMOOTH_COEF * uint_to_float(tmp, T_MIN, T_MAX, 12) + + (1 - CURRENT_SMOOTH_COEF) * measure->real_current; + + if (!measure->first_feedback_flag) // 初始化的时候设置偏置 + { + measure->first_feedback_flag = 1; + measure->angle_bias = measure->total_angle; + measure->total_angle = 0; + measure->speed_rads = 0; + } +} +``` diff --git a/modules/motor/LKmotor/LK9025.c b/modules/motor/LKmotor/LK9025.c index 6a4143a..2323734 100644 --- a/modules/motor/LKmotor/LK9025.c +++ b/modules/motor/LKmotor/LK9025.c @@ -1,6 +1,8 @@ #include "LK9025.h" #include "stdlib.h" #include "general_def.h" +#include "daemon.h" +#include "bsp_dwt.h" static uint8_t idx; static LKMotorInstance *lkmotor_instance[LK_MOTOR_MX_CNT] = {NULL}; @@ -14,10 +16,12 @@ static CANInstance *sender_instance; // 多电机发送时使用的caninstance( */ static void LKMotorDecode(CANInstance *_instance) { - LKMotor_Measure_t *measure; - uint8_t *rx_buff; - rx_buff = _instance->rx_buff; - measure = &(((LKMotorInstance *)_instance->id)->measure); // 通过caninstance保存的id获取对应的motorinstance + LKMotorInstance *motor = (LKMotorInstance *)_instance->id; // 通过caninstance保存的father id获取对应的motorinstance + LKMotor_Measure_t *measure = &motor->measure; + uint8_t *rx_buff = _instance->rx_buff; + + DaemonReload(motor->daemon); // 喂狗 + measure->feed_dt = DWT_GetDeltaT(&measure->feed_dwt_cnt); measure->last_ecd = measure->ecd; measure->ecd = (uint16_t)((rx_buff[7] << 8) | rx_buff[6]); @@ -30,12 +34,11 @@ static void LKMotorDecode(CANInstance *_instance) measure->real_current = (1 - CURRENT_SMOOTH_COEF) * measure->real_current + CURRENT_SMOOTH_COEF * (float)((int16_t)(rx_buff[3] << 8 | rx_buff[2])); - measure->temperate = rx_buff[1]; + measure->temperature = rx_buff[1]; - // 计算多圈角度 - if (measure->ecd - measure->last_ecd > 32678) + if (measure->ecd - measure->last_ecd > 32768) measure->total_round--; - else if (measure->ecd - measure->last_ecd < -32678) + else if (measure->ecd - measure->last_ecd < -32768) measure->total_round++; measure->total_angle = measure->total_round * 360 + measure->angle_single_round; } @@ -66,7 +69,16 @@ LKMotorInstance *LKMotorInit(Motor_Init_Config_s *config) } LKMotorEnable(motor); + DWT_GetDeltaT(&motor->measure.feed_dwt_cnt); lkmotor_instance[idx++] = motor; + + Daemon_Init_Config_s daemon_config = { + .callback = NULL, + .owner_id = motor, + .reload_count = 5, // 0.05秒 + }; + motor->daemon = DaemonRegister(&daemon_config); + return motor; } @@ -91,7 +103,7 @@ void LKMotorControl() if (setting->angle_feedback_source == OTHER_FEED) pid_measure = *motor->other_angle_feedback_ptr; else - pid_measure = measure->total_angle; + pid_measure = measure->real_current; pid_ref = PIDCalculate(&motor->angle_PID, pid_measure, pid_ref); if (setting->feedforward_flag & SPEED_FEEDFORWARD) pid_ref += *motor->speed_feedforward_ptr; @@ -104,10 +116,10 @@ void LKMotorControl() else pid_measure = measure->speed_rads; pid_ref = PIDCalculate(&motor->angle_PID, pid_measure, pid_ref); + if (setting->feedforward_flag & CURRENT_FEEDFORWARD) + pid_ref += *motor->current_feedforward_ptr; } - if (setting->feedforward_flag & CURRENT_FEEDFORWARD) - pid_ref += *motor->current_feedforward_ptr; if (setting->close_loop_type & CURRENT_LOOP) { pid_ref = PIDCalculate(&motor->current_PID, measure->real_current, pid_ref); @@ -126,9 +138,7 @@ void LKMotorControl() } if (idx) // 如果有电机注册了 - { - CANTransmit(sender_instance, 1); - } + CANTransmit(sender_instance, 0.2); } void LKMotorStop(LKMotorInstance *motor) @@ -145,3 +155,8 @@ void LKMotorSetRef(LKMotorInstance *motor, float ref) { motor->pid_ref = ref; } + +uint8_t LKMotorIsOnline(LKMotorInstance *motor) +{ + return DaemonIsOnline(motor->daemon); +} diff --git a/modules/motor/LKmotor/LK9025.h b/modules/motor/LKmotor/LK9025.h index c8ac2d3..a5cef5a 100644 --- a/modules/motor/LKmotor/LK9025.h +++ b/modules/motor/LKmotor/LK9025.h @@ -5,6 +5,7 @@ #include "bsp_can.h" #include "controller.h" #include "motor_def.h" +#include "daemon.h" #define LK_MOTOR_MX_CNT 4 // 最多允许4个LK电机使用多电机指令,挂载在一条总线上 @@ -23,11 +24,13 @@ typedef struct // 9025 float angle_single_round; // 单圈角度 float speed_rads; // speed rad/s int16_t real_current; // 实际电流 - uint8_t temperate; // 温度,C° + uint8_t temperature; // 温度,C° float total_angle; // 总角度 int32_t total_round; // 总圈数 + float feed_dt; + uint32_t feed_dwt_cnt; } LKMotor_Measure_t; typedef struct @@ -49,6 +52,8 @@ typedef struct CANInstance *motor_can_ins; + DaemonInstance *daemon; + } LKMotorInstance; /** @@ -88,4 +93,6 @@ void LKMotorStop(LKMotorInstance *motor); */ void LKMotorEnable(LKMotorInstance *motor); +uint8_t LKMotorIsOnline(LKMotorInstance *motor); + #endif // LK9025_H diff --git a/modules/motor/motor_task.c b/modules/motor/motor_task.c index 91f0034..47ff232 100644 --- a/modules/motor/motor_task.c +++ b/modules/motor/motor_task.c @@ -5,10 +5,9 @@ #include "step_motor.h" #include "servo_motor.h" - void MotorControlTask() { - // static uint8_t cnt = 0; 设定任务频率 + // static uint8_t cnt = 0; 设定不同电机的任务频率 // if(cnt%5==0) //200hz // if(cnt%10==0) //100hz DJIMotorControl(); @@ -16,10 +15,13 @@ void MotorControlTask() /* 如果有对应的电机则取消注释,可以加入条件编译或者register对应的idx判断是否注册了电机 */ LKMotorControl(); - HTMotorControl(); + // legacy support + // 由于ht04电机的反馈方式为接收到一帧消息后立刻回传,以此方式连续发送可能导致总线拥塞 + // 为了保证高频率控制,HTMotor中提供了以任务方式启动控制的接口,可通过宏定义切换 + // HTMotorControl(); + // 将所有的CAN设备集中在一处发送,最高反馈频率仅能达到500Hz,为了更好的控制效果,应使用新的HTMotorControlInit()接口 ServeoMotorControl(); - //StepMotorControl(); + // StepMotorControl(); } -