/* * 云台模组 模式自行选择:遥控器改变或自行初始化 delta获取从遥控器自行获取 example: Gimbal_t gimbal; Gimbal_IMU_t gimbal_imu; Gimbal_CMD_t gimbal_cmd; Gimbal_Init(&gimbal,&Config_GetRobotParam()->gimbal_param,GIMBAL_CTRL_FREQ); while(1){ //这里写遥控器获取gimbal_cmd数据 if(osMessageQueueGet(task_runtime.msgq.gimbal.imu, &gimbal_imu, NULL, 0)) Gimbal_UpdateIMU(&gimbal, &gimbal_imu); Gimbal_UpdateFeedback(&gimbal); Gimbal_Control(&gimbal,&gimbal_cmd); Gimbal_Output(&gimbal); } */ /* Includes ----------------------------------------------------------------- */ #include "gimbal.h" #include "bsp/can.h" #include "bsp/time.h" #include "component/filter.h" #include "component/pid.h" #include "device/motor_rm.h" #include "device/motor_dm.h" /* Private typedef ---------------------------------------------------------- */ /* Private define ----------------------------------------------------------- */ /* Private macro ------------------------------------------------------------ */ /* Private variables -------------------------------------------------------- */ /* Private function -------------------------------------------------------- */ static double poly(double x) { return 0.420757*pow(x,3) + -2.27122*pow(x,2) + 4.13016*x + -1.98984; } /** * \brief 计算角度偏差 * * \param * \param * * \return 函数运行结果 */ static float motor_imu_offset(float* motor, float* imu){ float motor_imu_offset = motor - imu; /* 处理跨越±π的情况 */ if (motor_imu_offset > M_PI) motor_imu_offset -= M_2PI; if (motor_imu_offset < -M_PI) motor_imu_offset += M_2PI; return motor_imu_offset; } /** * \brief 设置云台模式 * * \param c 包含云台数据的结构体 * \param mode 要设置的模式 * * \return 函数运行结果 */ static int8_t Gimbal_SetMode(Gimbal_t *g, Gimbal_Mode_t mode) { if (g == NULL) return -1; if (mode == g->mode) return GIMBAL_OK; PID_Reset(&g->pid.major_yaw_angle); PID_Reset(&g->pid.major_yaw_omega); PID_Reset(&g->pid.yaw_angle); PID_Reset(&g->pid.yaw_omega); PID_Reset(&g->pid.pit_angle); PID_Reset(&g->pid.pit_omega); LowPassFilter2p_Reset(&g->filter_out.major_yaw, 0.0f); LowPassFilter2p_Reset(&g->filter_out.yaw, 0.0f); LowPassFilter2p_Reset(&g->filter_out.pit, 0.0f); AHRS_ResetEulr(&(g->setpoint.eulr)); /* 切换模式后重置设定值 */ g->setpoint.eulr.pit = g->feedback.imu.eulr.pit; g->setpoint.eulr.yaw = g->feedback.imu.eulr.yaw; g->mode = mode; return 0; } /* Exported functions ------------------------------------------------------- */ /** * \brief 初始化云台 * * \param g 包含云台数据的结构体 * \param param 包含云台参数的结构体指针 * \param target_freq 任务预期的运行频率 * * \return 函数运行结果 */ int8_t Gimbal_Init(Gimbal_t *g,Gimbal_Params_t *param, float target_freq) { if (g == NULL) return -1; g->param = param; g->mode = GIMBAL_MODE_RELAX; /* 设置默认模式 */ /* 初始化云台电机控制PID和LPF */ PID_Init(&(g->pid.major_yaw_angle), KPID_MODE_CALC_D, target_freq, &(g->param->pid.major_yaw_angle)); PID_Init(&(g->pid.major_yaw_omega), KPID_MODE_CALC_D, target_freq, &(g->param->pid.major_yaw_omega)); PID_Init(&(g->pid.yaw_angle), KPID_MODE_CALC_D, target_freq, &(g->param->pid.yaw_angle)); PID_Init(&(g->pid.yaw_omega), KPID_MODE_CALC_D, target_freq, &(g->param->pid.yaw_omega)); PID_Init(&(g->pid.pit_angle), KPID_MODE_CALC_D, target_freq, &(g->param->pid.pit_angle)); PID_Init(&(g->pid.pit_omega), KPID_MODE_CALC_D, target_freq, &(g->param->pid.pit_omega)); g->limit.yaw.max = g->param->Limit_t.zero.yaw_encoder+g->param->Limit_t.zero.travel.yaw; g->limit.yaw.min = g->param->Limit_t.zero.yaw_encoder; LowPassFilter2p_Init(&g->filter_out.major_yaw, target_freq, g->param->low_pass_cutoff_freq.out); LowPassFilter2p_Init(&g->filter_out.yaw, target_freq, g->param->low_pass_cutoff_freq.out); LowPassFilter2p_Init(&g->filter_out.pit, target_freq, g->param->low_pass_cutoff_freq.out); BSP_CAN_Init(); /* 大yaw电机注册 */ if(g->param->motor.major_yaw==RM) MOTOR_RM_Register(&(g->param->motor.major_yaw_rm_motor)); if(g->param->motor.major_yaw==DM){ MOTOR_DM_Register(&(g->param->motor.major_yaw_dm_motor)); MOTOR_DM_Enable(&(g->param->motor.major_yaw_dm_motor)); } /*大疆电机注册*/ if(g->param->motor.yaw==RM) MOTOR_RM_Register(&(g->param->motor.yaw_rm_motor)); if(g->param->motor.pit==RM) MOTOR_RM_Register(&(g->param->motor.pit_rm_motor)); /*达妙电机注册*/ if(g->param->motor.yaw==DM){ MOTOR_DM_Register(&(g->param->motor.yaw_dm_motor)); MOTOR_DM_Enable(&(g->param->motor.yaw_dm_motor)); } if(g->param->motor.pit==DM){ MOTOR_DM_Register(&(g->param->motor.pit_dm_motor)); MOTOR_DM_Enable(&(g->param->motor.pit_dm_motor)); } return 0; } static int8_t Gimbal_Direction(Gimbal_t *g){ if (g == NULL) { return -1; } /* 欧拉角方向选择 */ switch(g->param->Direction.Eulr.pit){ case Pit: g->direction.Eulr.pit=g->feedback.imu.eulr.pit; break; case Yaw: g->direction.Eulr.pit=g->feedback.imu.eulr.yaw; break; case Rol: g->direction.Eulr.pit=g->feedback.imu.eulr.rol; break; } switch(g->param->Direction.Eulr.yaw){ case Pit: g->direction.Eulr.yaw=g->feedback.imu.eulr.pit; break; case Yaw: g->direction.Eulr.yaw=g->feedback.imu.eulr.yaw; break; case Rol: g->direction.Eulr.yaw=g->feedback.imu.eulr.rol; break; } /* 角速度方向选择 */ switch(g->param->Direction.Gyro.pit){ case Gyro_x: g->direction.Gyro.pit=g->feedback.imu.gyro.x; break; case Gyro_y: g->direction.Gyro.pit=g->feedback.imu.gyro.y; break; case Gyro_z: g->direction.Gyro.pit=g->feedback.imu.gyro.z; break; } switch(g->param->Direction.Gyro.yaw){ case Gyro_x: g->direction.Gyro.yaw=g->feedback.imu.gyro.x; break; case Gyro_y: g->direction.Gyro.yaw=g->feedback.imu.gyro.y; break; case Gyro_z: g->direction.Gyro.yaw=g->feedback.imu.gyro.z; break; } g->direction.Quaternion_t.q0=g->feedback.imu.quat.q0; g->direction.Quaternion_t.q1=g->feedback.imu.quat.q1; g->direction.Quaternion_t.q2=g->feedback.imu.quat.q2; g->direction.Quaternion_t.q3=g->feedback.imu.quat.q3; return 0; } /** * \brief 通过CAN设备更新云台反馈信息 * * \param gimbal 云台 * \param can CAN设备 * * \return 函数运行结果 */ int8_t Gimbal_UpdateFeedback(Gimbal_t *gimbal) { if (gimbal == NULL) return -1; /* 更新大yaw电机反馈数据(RM和DM) */ if(gimbal->param->motor.major_yaw==RM){ MOTOR_RM_Update(&(gimbal->param->motor.major_yaw_rm_motor)); MOTOR_RM_t *rm_motor_yaw = MOTOR_RM_GetMotor(&(gimbal->param->motor.major_yaw_rm_motor)); if(rm_motor_yaw != NULL) gimbal->feedback.motor.major_yaw = rm_motor_yaw->feedback; } if(gimbal->param->motor.major_yaw==DM){ MOTOR_DM_Update(&(gimbal->param->motor.major_yaw_dm_motor)); MOTOR_DM_t *dm_motor = MOTOR_DM_GetMotor(&(gimbal->param->motor.major_yaw_dm_motor)); if (dm_motor != NULL) { gimbal->feedback.motor.major_yaw = dm_motor->motor.feedback; } } /* 更新RM电机反馈数据 */ if(gimbal->param->motor.yaw==RM){ MOTOR_RM_Update(&(gimbal->param->motor.yaw_rm_motor)); MOTOR_RM_t *rm_motor_yaw = MOTOR_RM_GetMotor(&(gimbal->param->motor.yaw_rm_motor)); if(rm_motor_yaw != NULL) gimbal->feedback.motor.yaw = rm_motor_yaw->feedback; } if(gimbal->param->motor.pit==RM){ MOTOR_RM_Update(&(gimbal->param->motor.pit_rm_motor)); MOTOR_RM_t *rm_motor_pit = MOTOR_RM_GetMotor(&(gimbal->param->motor.pit_rm_motor)); if (rm_motor_pit != NULL) gimbal->feedback.motor.pit = rm_motor_pit->feedback; } /* 更新DM电机反馈数据 */ if(gimbal->param->motor.yaw==DM){ MOTOR_DM_Update(&(gimbal->param->motor.yaw_dm_motor)); MOTOR_DM_t *dm_motor = MOTOR_DM_GetMotor(&(gimbal->param->motor.yaw_dm_motor)); if (dm_motor != NULL) { gimbal->feedback.motor.yaw = dm_motor->motor.feedback; } } if(gimbal->param->motor.pit==DM){ MOTOR_DM_Update(&(gimbal->param->motor.pit_dm_motor)); MOTOR_DM_t *dm_motor = MOTOR_DM_GetMotor(&(gimbal->param->motor.pit_dm_motor)); if (dm_motor != NULL) { gimbal->feedback.motor.pit = dm_motor->motor.feedback; } } return 0; } int8_t Gimbal_UpdateIMU(Gimbal_t *gimbal, const Gimbal_IMU_t *imu){ if (gimbal == NULL) { return -1; } gimbal->feedback.imu.gyro = imu->gyro; gimbal->feedback.imu.eulr = imu->eulr; gimbal->feedback.imu.accl = imu->accl; gimbal->feedback.imu.quat = imu->quat; Gimbal_Direction(gimbal); } /** * \brief 运行大yaw控制逻辑 * * \param g 包含云台数据的结构体 * \param * * \return 函数运行结果 */ int8_t major_yaw_Control(Gimbal_t *g){ /*处理大yaw控制命令,软件限位 - 使用电机绝对角度*/ /*获得小YAW的中点位置,如果小YAW大于中点的一定范围,大YAW开始运动, 跟随小YAW向相同方向运动,将遥控数据进行分配*/ /*小YAW中点*/ float small_yaw_center_offset = g->param->Limit_t.zero.yaw_encoder + g->param->Limit_t.zero.travel.yaw* 0.5f; g->setpoint.major_yaw=small_yaw_center_offset; // /* 小yaw是否开启 */ // float err = g->feedback.motor.yaw.rotor_abs_angle - small_yaw_center_offset; // float threshold = (g->param->Limit_t.zero.travel.yaw * 0.35f); // /* 超出阈值范围,让大yaw跟随 */ // if (fabsf(err) > threshold) { // /* 此时让大yaw动起来,跟随err的符号 */ //// g->setpoint.major_yaw = g->feedback.motor.major_yaw.rotor_abs_angle - err; // g->setpoint.major_yaw = small_yaw_center_offset;; // } else { // /* 阈值以内,大yaw不动*/ // g->setpoint.major_yaw = g->feedback.motor.major_yaw.rotor_abs_angle; //当前值,不动 // } return 0; } int8_t Gimbal_Control_mode(Gimbal_t *g,Gimbal_CMD_t *g_cmd){ if (g == NULL || g_cmd == NULL) { return -1; } switch(g_cmd->ctrl_mode){ case GIMBAL_MODE_REMOTE: g->setpoint.eulr.yaw+=(g_cmd->delta_yaw*g->dt); g->setpoint.eulr.pit+=(g_cmd->delta_pit*g->dt); break; case GIMBAL_MODE_AI: g->setpoint.eulr.yaw=g_cmd->set_yaw; g->setpoint.eulr.pit=g_cmd->set_pit; break; case GIMBAL_MODE_NUC: /* 可能用不上,还在想 */ break; } return GIMBAL_OK; }; /** * \brief 运行云台控制逻辑 * * \param g 包含云台数据的结构体 * \param g_cmd 云台控制指令 * * \return 函数运行结果 */ int8_t Gimbal_Control(Gimbal_t *g, Gimbal_CMD_t *g_cmd) { if (g == NULL || g_cmd == NULL) { return -1; } g->dt = (BSP_TIME_Get_us() - g->lask_wakeup) / 1000000.0f; g->lask_wakeup = BSP_TIME_Get_us(); Gimbal_SetMode(g, g_cmd->mode); /* 双yaw的大yaw,setpoint从这设立 */ major_yaw_Control(g); /* 欧拉角控制相关逻辑 */ float yaw_omega_set_point, pit_omega_set_point,major_yaw_omega_set_point; float delta_yaw = g_cmd->delta_yaw*g->dt; float delta_pit = g_cmd->delta_pit*g->dt; float yaw_motor_imu_offset=motor_imu_offset(&g->feedback.motor.yaw.rotor_abs_angle, &g->feedback.imu.eulr.yaw); float delta_max = CircleError(g->limit.yaw.max, (g->setpoint.eulr.yaw + yaw_motor_imu_offset + delta_yaw), M_2PI); float delta_min = CircleError(g->limit.yaw.min, (g->setpoint.eulr.yaw + yaw_motor_imu_offset + delta_yaw), M_2PI); switch (g->mode) { case GIMBAL_MODE_RELAX:/*放松模式*/ g->out.major_yaw = 0.0f; g->out.yaw = 0.0f; g->out.pit = 0.0f; break; case GIMBAL_MODE_ABSOLUTE:/*绝对模式*/ /* 手动和自瞄 */ Gimbal_Control_mode(g, g_cmd); if (g->param->motor.limit_yaw==true) Clip(&(g->setpoint.eulr.yaw),delta_min,delta_max); else{ /*限制在-3.14~3.14*/ if (g->setpoint.eulr.yaw > M_PI) g->setpoint.eulr.yaw -= M_2PI; if (g->setpoint.eulr.yaw < -M_PI) g->setpoint.eulr.yaw += M_2PI; } /* 处理pitch控制命令,软件限位 - 使用电机绝对角度 */ /* 限制pit控制命令 */ if (g->param->motor.limit_pit == true) Clip(&(g->setpoint.eulr.pit),g->param->Limit_t.pit_min,g->param->Limit_t.pit_max); /*4310大YAW控制 这里是单环控制的,有需要加双环 */ major_yaw_omega_set_point =PID_Calc(&g->pid.major_yaw_angle, g->setpoint.major_yaw, g->feedback.motor.yaw.rotor_abs_angle, g->feedback.motor.yaw.rotor_speed,g->dt); // g->out.major_yaw = PID_Calc(&g->pid.major_yaw_omega,major_yaw_omega_set_point, // g->feedback.motor.yaw.rotor_speed,0.0f,g->dt); g->out.major_yaw = major_yaw_omega_set_point; // 直接输出速度环目标值作为电机输出 // g->ai.v_err=g->ai.v - g->setpoint.eulr.yaw; // g->ai.p_err=g->ai.p - g->direction.Gyro.yaw; // g->out.yaw=g->ai.accl+g->ai.p_err*g->param->acclctrl.k+g->param->acclctrl.p*g->ai.v_err; yaw_omega_set_point = PID_Calc(&(g->pid.yaw_angle), g->setpoint.eulr.yaw, g->direction.Eulr.yaw, 0.0f, g->dt); g->out.yaw = PID_Calc(&(g->pid.yaw_omega), yaw_omega_set_point, g->direction.Gyro.yaw, 0.f, g->dt); pit_omega_set_point = PID_Calc(&(g->pid.pit_angle), g->setpoint.eulr.pit, g->direction.Eulr.pit, 0.0f, g->dt); // g->out.pit = PID_Calc(&(g->pid.pit_omega), pit_omega_set_point, // g->feedback.imu.gyro.y, 0.f, g->dt); g->out.pit = pit_omega_set_point+poly(g->feedback.motor.pit.rotor_abs_angle); /*前馈添加*/ if(g->param->feedforward.imu.yaw==true) g->out.yaw+=g->param->feedforward.imu.K_yaw*(yaw_omega_set_point - g->feedback.imu.gyro.z); if(g->param->feedforward.imu.pit==true) g->out.pit+=g->param->feedforward.imu.K_pit*(pit_omega_set_point - g->feedback.imu.gyro.x); break; case GIMBAL_MODE_RELATIVE: /*计算零点*/ CircleAdd(&(g->relative_angle.yaw), delta_yaw, M_2PI); /* -PI~PI 归一化 */ if(g->relative_angle.yaw > M_PI) g->relative_angle.yaw -= M_2PI; if(g->relative_angle.yaw < -M_PI) g->relative_angle.yaw += M_2PI; /* 计算底盘当前在世界坐标系的方向 (坐标系转换) */ /* 底盘Yaw(世界) = 云台Yaw(世界) - 云台电机Yaw(机械) */ float current_chassis_yaw_world=-motor_imu_offset(&g->feedback.motor.yaw.rotor_abs_angle,&g->feedback.imu.eulr.yaw); /* 归一化底盘角度 */ if(current_chassis_yaw_world > M_PI) current_chassis_yaw_world -= M_2PI; if(current_chassis_yaw_world < -M_PI) current_chassis_yaw_world += M_2PI; /* 最终的欧拉角控制目标 (Setpoint) */ /* 目标(世界) = 底盘(世界) + 期望相对角(相对) */ g->setpoint.eulr.yaw = current_chassis_yaw_world + g->relative_angle.yaw; /* 再次归一化 Setpoint,防止数值溢出 */ if(g->setpoint.eulr.yaw > M_PI) g->setpoint.eulr.yaw -= M_2PI; if(g->setpoint.eulr.yaw < -M_PI) g->setpoint.eulr.yaw += M_2PI; /* Pitch 轴处理 ( Pitch 保持绝对水平,不随底盘颠簸) */ g->setpoint.eulr.pit += delta_pit; /* Pitch 限位 */ if (g->param->motor.limit_pit == true) Clip(&(g->setpoint.eulr.pit), g->param->Limit_t.pit_min, g->param->Limit_t.pit_max); /*4310大YAW控制 这里是单环控制的,有需要加双环 */ major_yaw_omega_set_point =PID_Calc(&g->pid.major_yaw_angle, g->setpoint.major_yaw, g->feedback.motor.major_yaw.rotor_abs_angle, g->feedback.motor.major_yaw.rotor_speed,g->dt); // g->out.yaw_4310 = PID_Calc(&g->pid.yaw_4310_omega,yaw_omega_set_point, // g->feedback.imu.gyro.z,0.0f,g->dt); g->out.major_yaw = major_yaw_omega_set_point; // 直接输出速度环目标值作为电机输出 yaw_omega_set_point = PID_Calc(&(g->pid.yaw_angle), g->setpoint.eulr.yaw, g->direction.Eulr.yaw, 0.0f, g->dt); g->out.yaw = PID_Calc(&(g->pid.yaw_omega), yaw_omega_set_point, g->direction.Gyro.yaw, 0.f, g->dt); pit_omega_set_point = PID_Calc(&(g->pid.pit_angle), g->setpoint.eulr.pit, g->direction.Eulr.pit, 0.0f, g->dt); g->out.pit = PID_Calc(&(g->pid.pit_omega), pit_omega_set_point, g->direction.Gyro.pit, 0.f, g->dt); /*前馈添加*/ if(g->param->feedforward.imu.yaw==true) g->out.yaw+=g->param->feedforward.imu.K_yaw*(yaw_omega_set_point - g->feedback.imu.gyro.z); if(g->param->feedforward.imu.pit==true) g->out.pit+=g->param->feedforward.imu.K_pit*(pit_omega_set_point - g->feedback.imu.gyro.x); break; } /* 输出滤波 */ g->out.major_yaw = LowPassFilter2p_Apply(&g->filter_out.major_yaw, g->out.major_yaw); g->out.yaw = LowPassFilter2p_Apply(&g->filter_out.yaw, g->out.yaw); g->out.pit = LowPassFilter2p_Apply(&g->filter_out.pit, g->out.pit); return 0; } /** * \brief 云台输出 * * \param s 包含云台数据的结构体 * \param out CAN设备云台输出结构体 */ void Gimbal_Output(Gimbal_t *g){ /*大yaw电机输出*/ if(g->param->motor.major_yaw==RM){ MOTOR_RM_Ctrl(&g->param->motor.major_yaw_rm_motor); MOTOR_RM_SetOutput(&g->param->motor.major_yaw_rm_motor, g->out.major_yaw); } if(g->param->motor.major_yaw==DM){ g->out.major_yaw_dm.angle=g->param->dm_Params_t.major_yaw_dm.angle; g->out.major_yaw_dm.kd=g->param->dm_Params_t.major_yaw_dm.kd; g->out.major_yaw_dm.kp=g->param->dm_Params_t.major_yaw_dm.kp; g->out.major_yaw_dm.velocity=g->param->dm_Params_t.major_yaw_dm.velocity; g->out.major_yaw_dm.torque= g->out.major_yaw * g->param->dm_Params_t.major_yaw_dm_Reduction_ratio; // 乘以减速比 MOTOR_DM_MITCtrl(&g->param->motor.major_yaw_dm_motor,&(g->out.major_yaw_dm)); } /*大疆电机输出*/ if(g->param->motor.yaw==RM){ MOTOR_RM_Ctrl(&g->param->motor.yaw_rm_motor); MOTOR_RM_SetOutput(&g->param->motor.yaw_rm_motor, g->out.yaw); } if(g->param->motor.pit==RM){ MOTOR_RM_Ctrl(&g->param->motor.pit_rm_motor); MOTOR_RM_SetOutput(&g->param->motor.pit_rm_motor, g->out.pit);// } /*达妙电机输出*/ if(g->param->motor.pit==DM){ g->out.pit_dm.angle=g->param->dm_Params_t.pit_dm.angle; g->out.pit_dm.kd=g->param->dm_Params_t.pit_dm.kd; g->out.pit_dm.kp=g->param->dm_Params_t.pit_dm.kp; g->out.pit_dm.velocity=g->param->dm_Params_t.pit_dm.velocity; g->out.pit_dm.torque= g->out.pit * g->param->dm_Params_t.pit_dm_Reduction_ratio; // 乘以减速比 MOTOR_DM_MITCtrl(&g->param->motor.pit_dm_motor,&(g->out.pit_dm)); } if(g->param->motor.yaw==DM){ g->out.yaw_dm.angle=g->param->dm_Params_t.yaw_dm.angle; g->out.yaw_dm.kd=g->param->dm_Params_t.yaw_dm.kd; g->out.yaw_dm.kp=g->param->dm_Params_t.yaw_dm.kp; g->out.yaw_dm.velocity=g->param->dm_Params_t.yaw_dm.velocity; g->out.yaw_dm.torque= g->out.yaw * g->param->dm_Params_t.yaw_dm_Reduction_ratio; // 乘以减速比 MOTOR_DM_MITCtrl(&g->param->motor.yaw_dm_motor,&(g->out.yaw_dm)); } }