/* µ×ÅÌÄ£×é */ #include "cmsis_os2.h" #include #include "bsp/mm.h" #include "bsp/can.h" #include "component/ahrs.h" #include "device/motor_rm.h" #include "device/motor.h" #include "module/chassis.h" /** * @brief µ×ÅÌСÍÓÂÝģʽÏà¹Ø²ÎÊý */ #define CHASSIS_ROTOR_WZ_MIN 0.6f //СÍÓÂÝ×îСËÙ¶È #define CHASSIS_ROTOR_WZ_MAX 0.8f //СÍÓÂÝ×î´óËÙ¶È #define M_7OVER72PI (M_2PI * 7.0f / 72.0f) //½Ç¶ÈÆ«ÒÆÁ¿£¨ÓÃÔÚ¸úËæÔÆÌ¨35¡ã£© #define CHASSIS_ROTOR_OMEGA 0.001f //½ÇËٶȱ仯ƵÂÊ /** * @brief ÉèÖõ×ÅÌģʽ * @param c µ×Å̽ṹÌåÖ¸Õë * @param mode Ä¿±ê¿ØÖÆÄ£Ê½ * @param now µ±Ç°Ê±¼ä´Á(ms) * @return CHASSIS_OK:³É¹¦ CHASSIS_ERR_NULL:¿Õ */ static int8_t Chassis_SetMode(Chassis_t *c, Chassis_Mode_t mode, uint32_t now) { if (!c) return CHASSIS_ERR_NULL; if (mode == c->mode) return CHASSIS_OK; //Ëæ»úÖÖ×Ó£¬Ð¡ÍÓÂÝÄ£Ê½Ëæ»úÉèÖÃÐýת·½Ïò if (mode == CHASSIS_MODE_ROTOR && c->mode != CHASSIS_MODE_ROTOR) { srand(now); c->wz_multi = (rand() % 2) ? -1 : 1; } //ÖØÖÃPIDºÍÂ˲¨ for (uint8_t i = 0; i < c->num_wheel; i++) { PID_Reset(&c->pid.motor[i]); LowPassFilter2p_Reset(&c->filter.in[i], 0.0f); LowPassFilter2p_Reset(&c->filter.out[i], 0.0f); } c->mode = mode; return CHASSIS_OK; } /** * @brief СÍÓÂÝģʽ¶¯Ì¬½ÇËÙ¶È * @param min ×îСËÙ¶È * @param max ×î´óËÙ¶È * @param now µ±Ç°Ê±¼ä´Á(ms) * @return ½ÇËÙ¶ÈÖµ */ static float Chassis_CalcWz(const float min, const float max, uint32_t now) { float wz_vary = fabsf(0.2f * sinf(CHASSIS_ROTOR_OMEGA * (float)now)) + min; return (wz_vary > max) ? max : wz_vary; } /** * @brief µ×ÅÌģʽ³õʼ»¯ * @param c µ×Å̽ṹÌåÖ¸Õë * @param param µ×Å̲ÎÊý½á¹¹ÌåÖ¸Õë * @param mech_zero »úеÁãµãÅ·À­½Ç * @param target_freq ¿ØÖÆÆµÂÊ(Hz) * @return CHASSIS_OK:³É¹¦ CHASSIS_ERR_NULL:¿Õ CHASSIS_ERR_TYPE:²»Ö§³ÖµÄģʽ */ int8_t Chassis_Init(Chassis_t *c, const Chassis_Params_t *param, float target_freq) { if (!c) return CHASSIS_ERR_NULL; //³õʼ»¯CANͨÐÅ BSP_CAN_Init(); c->param = param; c->mode = CHASSIS_MODE_RELAX; //¸ù¾Ýµ×Å̲»Í¬ÉèÖÃģʽÂÖ×ÓÓë»ìºÏÆ÷ Mixer_Mode_t mixer_mode; switch (param->type) { case CHASSIS_TYPE_MECANUM://ÂóÂÖ c->num_wheel = 4; mixer_mode = MIXER_MECANUM; break; case CHASSIS_TYPE_PARLFIX4: c->num_wheel = 4; mixer_mode = MIXER_PARLFIX4; break; case CHASSIS_TYPE_PARLFIX2: c->num_wheel = 2; mixer_mode = MIXER_PARLFIX2; break; case CHASSIS_TYPE_OMNI_CROSS: c->num_wheel = 4; mixer_mode = MIXER_OMNICROSS; break; case CHASSIS_TYPE_OMNI_PLUS: //È«ÏòÂÖ£¨Àϲ½±øÀàÐÍ£© c->num_wheel = 4; mixer_mode = MIXER_OMNIPLUS; break; case CHASSIS_TYPE_SINGLE: c->num_wheel = 1; mixer_mode = MIXER_SINGLE; break; default: return CHASSIS_ERR_TYPE; } //³õʼ»¯Ê±¼ä´Á c->last_wakeup = 0; c->dt = 0.0f; //³õʼ»¯PIDºÍÂ˲¨ for (uint8_t i = 0; i < c->num_wheel; i++) { PID_Init(&c->pid.motor[i], KPID_MODE_NO_D, target_freq, ¶m->pid.motor_pid_param); LowPassFilter2p_Init(&c->filter.in[i], target_freq, param->low_pass_cutoff_freq.in); LowPassFilter2p_Init(&c->filter.out[i], target_freq, param->low_pass_cutoff_freq.out); //ÇåÁãµç»ú·´À¡ c->feedback.motor[i].rotor_speed = 0; c->feedback.motor[i].torque_current = 0; c->feedback.motor[i].rotor_abs_angle = 0; c->feedback.motor[i].temp = 0; } //³õʼ»¯PIDºÍ»ìºÏÆ÷ PID_Init(&c->pid.follow, KPID_MODE_NO_D, target_freq, ¶m->pid.follow_pid_param); Mixer_Init(&c->mixer, mixer_mode); //ÇåÁãÔ˶¯ÏòÁ¿ºÍÊä³ö c->move_vec.vx = c->move_vec.vy = c->move_vec.wz = 0.0f; for (uint8_t i = 0; i < c->num_wheel; i++) { c->out.motor[i] = 0.0f; } //×¢²á´ó½®µç»ú for (int i = 0; i < c->num_wheel; i++) { MOTOR_RM_Register(&(c->param->motor_param[i])); } return CHASSIS_OK; } /** * @brief ¸üеç»ú·´À¡(IMU+µç»ú״̬) * @param c µ×Å̽ṹÌåÖ¸Õë * @param feedback µ×ÅÌ·´À¡Ö¸Õë½á¹¹Ìå * @return CHASSIS_OK:³É¹¦ CHASSIS_ERR_NULL:¿Õ */ int8_t Chassis_UpdateFeedback(Chassis_t *c) { //¸üÐÂËùÓеç»ú·´À¡ for (uint8_t i = 0; i < c->num_wheel; i++) { MOTOR_RM_Update(&(c->param->motor_param[i])); MOTOR_RM_t *rm_motor = MOTOR_RM_GetMotor(&(c->param->motor_param[i])); c->motors[i] = rm_motor; MOTOR_RM_t *rm = c->motors[i]; if (rm_motor != NULL) { c->feedback.motor[i] = rm_motor->feedback; }else { return CHASSIS_ERR_NULL; } } return CHASSIS_OK; } /** * @brief µ×Å̵ç»ú¿ØÖÆ * @param c µ×Å̽ṹÌåÖ¸Õë * @param c_cmd ¿ØÖÆÃüÁî * @param now µ±Ç°Ê±¼ä´Á(ms) * @return CHASSIS_OK:³É¹¦ CHASSIS_ERR_NULL:¿Õ */ int8_t Chassis_Control(Chassis_t *c, const Chassis_CMD_t *c_cmd, uint32_t now) { if (!c || !c_cmd) return CHASSIS_ERR_NULL; //¼ÆËã¿ØÖÆÖÜÆÚ c->dt = (float)(now - c->last_wakeup) / 1000.0f; c->last_wakeup = now; if (!isfinite(c->dt) || c->dt <= 0.0f) { c->dt = 0.001f; } if (c->dt < 0.0005f) c->dt = 0.0005f; if (c->dt > 0.050f) c->dt = 0.050f; //ÉèÖÃģʽ Chassis_SetMode(c, c_cmd->mode, now); //²»Í¬Ä£Ê½Ï¶ÔÓ¦½âË㣨Ô˶¯ÏòÁ¿£© switch (c->mode) { case CHASSIS_MODE_BREAK: c->move_vec.vx = c->move_vec.vy = 0.0f; break; case CHASSIS_MODE_INDEPENDENT: c->move_vec.vx = c_cmd->ctrl_vec.vx; c->move_vec.vy = c_cmd->ctrl_vec.vy; break; default: { //Ò£¿ØÆ÷×ø±ê->»úÌå×ø±êϵ float beta = c->feedback.encoder_gimbalYawMotor - c->mech_zero; float cosb = cosf(beta); float sinb = sinf(beta); c->move_vec.vx = cosb * c_cmd->ctrl_vec.vx - sinb * c_cmd->ctrl_vec.vy; c->move_vec.vy = sinb * c_cmd->ctrl_vec.vx + cosb * c_cmd->ctrl_vec.vy; break; } } //¸ù¾Ýģʽ¼ÆËãµ×Å̽ÇËÙ¶È switch (c->mode) { case CHASSIS_MODE_RELAX: case CHASSIS_MODE_BREAK: case CHASSIS_MODE_INDEPENDENT: c->move_vec.wz = 0.0f; break; case CHASSIS_MODE_OPEN: c->move_vec.wz = c_cmd->ctrl_vec.wz; break; //ÔÆÌ¨¸úËæ case CHASSIS_MODE_FOLLOW_GIMBAL: c->move_vec.wz = PID_Calc(&c->pid.follow, c->mech_zero, c->feedback.encoder_gimbalYawMotor, 0.0f, c->dt); break; //ÔÆÌ¨¸úËæ£¨Æ«ÒÆ£© case CHASSIS_MODE_FOLLOW_GIMBAL_35: c->move_vec.wz = PID_Calc(&c->pid.follow,c->mech_zero +M_7OVER72PI, c->feedback.encoder_gimbalYawMotor, 0.0f, c->dt); break; //СÍÓÂÝ case CHASSIS_MODE_ROTOR: c->move_vec.wz = c->wz_multi * Chassis_CalcWz(CHASSIS_ROTOR_WZ_MIN,CHASSIS_ROTOR_WZ_MAX, now); break; } //Ô˶¯Ñ§Äæ½âË㣬Ô˶¯ÏòÁ¿·Ö½âΪµç»úתËÙ Mixer_Apply(&c->mixer, &c->move_vec, c->setpoint.motor_rpm, c->num_wheel, 500.0f); for (uint8_t i = 0; i < c->num_wheel; i++) { float rf = c->setpoint.motor_rpm[i];///Ä¿±êתËÙ float fb = LowPassFilter2p_Apply(&c->filter.in[i], (float)c->feedback.motor[i].rotor_speed); float out_current; switch (c->mode) { case CHASSIS_MODE_BREAK: case CHASSIS_MODE_FOLLOW_GIMBAL: case CHASSIS_MODE_FOLLOW_GIMBAL_35: case CHASSIS_MODE_ROTOR: case CHASSIS_MODE_INDEPENDENT: out_current = PID_Calc(&c->pid.motor[i], c->setpoint.motor_rpm[i], fb, 0.0f, c->dt); break; case CHASSIS_MODE_OPEN: out_current = c->setpoint.motor_rpm[i] / 7000.0f; break; case CHASSIS_MODE_RELAX: out_current = 0.0f; break; } //µÍͨÂ˲¨ºÍÏÞ·ù c->out.motor[i] = LowPassFilter2p_Apply(&c->filter.out[i], out_current); Clip(&c->out.motor[i], -c->param->limit.max_current, c->param->limit.max_current); } return CHASSIS_OK; } /** * @brief µç»úÊä³ö * @param c µ×Å̽ṹÌåÖ¸Õë */ void Chassis_Output(Chassis_t *c) { if (!c) return; //ÿ¸öµç»úÄ¿±êÊä³ö for (uint8_t i = 0; i < c->num_wheel; i++) { MOTOR_RM_t *rm = c->motors[i]; if (!rm) continue; MOTOR_RM_SetOutput(&rm->param, c->out.motor[i]); } //µ÷ÓÃctrl for (uint8_t i = 0; i < c->num_wheel; i++) { MOTOR_RM_t *rm = c->motors[i]; if (rm) { MOTOR_RM_Ctrl(&rm->param); } } } /** * @brief ÖØÖõ×ÅÌÊä³ö * @param c µ×Å̽ṹÌåÖ¸Õë */ void Chassis_ResetOutput(Chassis_t *c) { if (!c) return; for (uint8_t i = 0; i < c->num_wheel; i++) { MOTOR_RM_t *m = c->motors[i]; if (m) { MOTOR_RM_Relax(&(m->param)); } } }