#include "balance.h" int Time_count=0; //Time variable //��ʱ���� // Robot mode is wrong to detect flag bits //������ģʽ�Ƿ��������־λ int robot_mode_check_flag=0; short test_num; Encoder OriginalEncoder; //Encoder raw data //������ԭʼ���� u8 command_lost_count=0; //���ڡ�CAN�������ʧʱ���������ʧ1���ֹͣ���� /* Calibrated Ackermann steering model (manual push-test, motors disabled). Maps signed path curvature kappa = 1/R [1/m, R at the rear-axle center] to servo PWM. Sign convention: kappa > 0 -> right turn, kappa < 0 -> left turn. Quadratic fit of measured (servo, 1/R) points, max residual ~12 PWM: servo = AKM_C0 + AKM_C1*kappa + AKM_C2*kappa*kappa NOTE: kappa is in 1/m. The raw calibration table listed 1/R in 1/cm; these coefficients were fit after converting R from cm to m, so they must be fed SI curvature (kappa = wz/Vx, both SI). Straight-ahead lands near 1656 PWM. */ #define AKM_SERVO_C0 1656.373f #define AKM_SERVO_C1 140.548f #define AKM_SERVO_C2 (-7.654f) /* Steering range from the calibration table: PWM 1100 (left) .. 2000 (right). */ #define AKM_SERVO_MIN 1100 #define AKM_SERVO_MAX 2000 /* Largest curvature the car can actually track: R_min ~= 0.30 m -> 3.33 /m. */ #define AKM_KAPPA_MAX 3.331f /* Remote CH1 neutral pulse width [us]. The servo straight-ahead neutral is SERVO_INIT (motor.h). The two differ, so the CH1 passthrough is shifted by (SERVO_INIT - AKM_REMOTER_CH1_MID) to keep stick-center = wheels-straight. */ #define AKM_REMOTER_CH1_MID 1500 /* Linear steering map that honors the servo's real straight-ahead neutral (SERVO_INIT) instead of the arithmetic midpoint of [MIN, MAX]. norm > 0 = left -> toward AKM_SERVO_MIN; norm < 0 = right -> toward AKM_SERVO_MAX; norm == 0 -> SERVO_INIT. Each side is scaled to its own end stop so the full mechanical travel is used even though the neutral is off-center. */ #if AKM_DIRECT_MAP || AKM_YAW_ASSIST static int Akm_Norm_To_Servo(float norm) { float span, pwm; norm = target_limit_float(norm, -1.0f, 1.0f); span = (norm >= 0.0f) ? (float)(SERVO_INIT - AKM_SERVO_MIN) : (float)(AKM_SERVO_MAX - SERVO_INIT); pwm = (float)SERVO_INIT - norm * span; return (int)(pwm + (pwm >= 0.0f ? 0.5f : -0.5f)); } #endif /* Needed by Mode 0 (calibrated Ackermann) and Mode 2 (yaw-rate assist), i.e. whenever direct-map is off and CH1 is not overriding the servo. Direct-map (Mode 1) and the CH1 debug override never call it. */ #if !AKM_DIRECT_MAP && !AKM_SERVO_DEBUG_REMOTE_CH1 static int Akm_Curvature_To_Servo(float kappa) { float pwm; kappa = target_limit_float(kappa, -AKM_KAPPA_MAX, AKM_KAPPA_MAX); pwm = AKM_SERVO_C0 + AKM_SERVO_C1 * kappa + AKM_SERVO_C2 * kappa * kappa; return (int)(pwm + (pwm >= 0.0f ? 0.5f : -0.5f)); } #endif /************************************************************************** Function: The inverse kinematics solution is used to calculate the target speed of each wheel according to the target speed of three axes Input : X and Y, Z axis direction of the target movement speed Output : none �������ܣ��˶�ѧ��⣬��������Ŀ���ٶȼ��������Ŀ��ת�� ��ڲ�����X��Y��Z�᷽���Ŀ���˶��ٶ� ���� ֵ���� **************************************************************************/ void Drive_Motor(float Vx,float Vy,float Vz) { float amplitude=3.5; //Wheel target speed limit //����Ŀ���ٶ��޷� //Speed smoothing is enabled when moving the omnidirectional trolley //ȫ���ƶ�С���ſ����ٶ�ƽ������ if(Car_Mode==Mec_Car||Car_Mode==Omni_Car) { Smooth_control(Vx,Vy,Vz); //Smoothing the input speed //�������ٶȽ���ƽ������ //Get the smoothed data //��ȡƽ������������� Vx=smooth_control.VX; Vy=smooth_control.VY; Vz=smooth_control.VZ; } //Mecanum wheel car //�����ķ��С�� if (Car_Mode==Mec_Car) { //Inverse kinematics //�˶�ѧ��� MOTOR_A.Target = +Vy+Vx-Vz*(Axle_spacing+Wheel_spacing); MOTOR_B.Target = -Vy+Vx-Vz*(Axle_spacing+Wheel_spacing); MOTOR_C.Target = +Vy+Vx+Vz*(Axle_spacing+Wheel_spacing); MOTOR_D.Target = -Vy+Vx+Vz*(Axle_spacing+Wheel_spacing); //Wheel (motor) target speed limit //����(���)Ŀ���ٶ��޷� MOTOR_A.Target=target_limit_float(MOTOR_A.Target,-amplitude,amplitude); MOTOR_B.Target=target_limit_float(MOTOR_B.Target,-amplitude,amplitude); MOTOR_C.Target=target_limit_float(MOTOR_C.Target,-amplitude,amplitude); MOTOR_D.Target=target_limit_float(MOTOR_D.Target,-amplitude,amplitude); } //Omni car //ȫ����С�� else if (Car_Mode==Omni_Car) { //Inverse kinematics //�˶�ѧ��� MOTOR_A.Target = Vy + Omni_turn_radiaus*Vz; MOTOR_B.Target = -X_PARAMETER*Vx - Y_PARAMETER*Vy + Omni_turn_radiaus*Vz; MOTOR_C.Target = +X_PARAMETER*Vx - Y_PARAMETER*Vy + Omni_turn_radiaus*Vz; //Wheel (motor) target speed limit //����(���)Ŀ���ٶ��޷� MOTOR_A.Target=target_limit_float(MOTOR_A.Target,-amplitude,amplitude); MOTOR_B.Target=target_limit_float(MOTOR_B.Target,-amplitude,amplitude); MOTOR_C.Target=target_limit_float(MOTOR_C.Target,-amplitude,amplitude); MOTOR_D.Target=0; //Out of use //û��ʹ�õ� } //Ackermann structure car //������С�� else if (Car_Mode==Akm_Car) { #if AKM_DIRECT_MAP // Direct passthrough mode (tuning/debug, not physically Ackermann): // - Vz is linearly mapped across the full servo travel, independent // of speed. Vz > 0 = left (ROS) -> AKM_SERVO_MIN (left end). // - Vx is sent to both drive wheels unchanged (no differential). // The AKM_SERVO_DEBUG_REMOTE_CH1 override below still takes priority // over this servo value. // vz_norm > 0 (left) -> AKM_SERVO_MIN; 0 -> SERVO_INIT (straight). float vz_norm = target_limit_float(Vz / AKM_DIRECT_VZ_FULL, -1.0f, 1.0f); Servo = Akm_Norm_To_Servo(vz_norm); MOTOR_A.Target = Vx; MOTOR_B.Target = Vx; MOTOR_C.Target = 0; MOTOR_D.Target = 0; #elif AKM_YAW_ASSIST // Mode 2: DECOUPLED steering + IMU yaw-rate differential assist // (simplified torque vectoring). This deliberately breaks the Ackermann // w-v coupling that shrinks the steering angle at speed: // // * Steering (w): the servo is a DIRECT map of Vz across the full // curvature range, INDEPENDENT of Vx (like Mode 1). So a large Vz // always yields a large front-wheel angle, even at high Vx. // * Drive (v): Vx sets the base wheel speed independently. // * Ackermann is used only as the FEEDFORWARD reference for the rear // differential; an IMU yaw-rate PI loop trims on top. // // Sign bookkeeping (fit domain, kappa>0 = right; ROS Vz>0 = left = CCW): // vz_norm = clamp(Vz/AKM_DIRECT_VZ_FULL, +-1) // Servo = linear FULL-TRAVEL map of vz_norm (SAME as Mode 1 / // direct map), NOT the calibration fit -- this is what // gives full steering authority at any speed. // kappa_cmd = -vz_norm * AKM_KAPPA_MAX is used only to build the // Ackermann feedforward reference for the rear diff. // The yaw loop works in the ROS/geometric frame (r>0 = left). static float yaw_integral = 0.0f; // PI integrator state [m/s] static float r_filt = 0.0f; // low-pass filtered yaw rate [rad/s] float vz_norm, kappa_cmd; float r_ref, r_meas, e_r, dv_ff, dv_fb, dv, dv_max; // Decoupled steering command: Vz -> normalized steering, NOT via Vx. vz_norm = target_limit_float(Vz / AKM_DIRECT_VZ_FULL, -1.0f, 1.0f); // Servo = linear full-travel map, identical to Mode 1 (direct map), // centered on SERVO_INIT. vz_norm > 0 (left) -> AKM_SERVO_MIN. Servo = Akm_Norm_To_Servo(vz_norm); // Ackermann feedforward reference yaw rate for the commanded steering. // kappa_cmd maps full stick to the car's max trackable curvature. // Back in the ROS/geometric frame: r_ref > 0 = left turn. kappa_cmd = -vz_norm * AKM_KAPPA_MAX; r_ref = -kappa_cmd * Vx; // Measured yaw rate from the gyro, de-biased LSB -> rad/s, with an // optional sign flip and a light first-order low-pass. r_meas = AKM_GYRO_Z_SIGN * (float)gyro[2] / AKM_GYRO_Z_TO_RADPS; r_filt += AKM_YAW_IMU_LPF * (r_meas - r_filt); if(float_abs(Vx) < AKM_YAW_MIN_SPEED) { // Too slow for a meaningful yaw rate: freeze the loop, no assist. yaw_integral = 0.0f; dv = 0.0f; } else { e_r = r_ref - r_filt; // Feedforward: alpha=1 reproduces the Mode-0 geometric differential // exactly (dv = 0.5*track*|kappa|*Vx expressed via r_ref). dv_ff = AKM_YAW_FF_ALPHA * 0.5f * Wheel_spacing * r_ref; // PI feedback with rectangular integration at the fixed control // period (Drive_Motor runs at CONTROL_FREQUENCY Hz). yaw_integral += e_r * (1.0f / (float)CONTROL_FREQUENCY); dv_fb = AKM_YAW_KP * e_r + AKM_YAW_KI * yaw_integral; dv = dv_ff + dv_fb; // Clamp the differential to a fraction of Vx and anti-windup: if the // PI part alone saturates, roll the integrator back. dv_max = AKM_YAW_MAX_DIFF_RATIO * float_abs(Vx); if(dv > dv_max) { if(AKM_YAW_KI > 0.0f) yaw_integral -= (dv - dv_max) / AKM_YAW_KI; dv = dv_max; } else if(dv < -dv_max) { if(AKM_YAW_KI > 0.0f) yaw_integral -= (dv + dv_max) / AKM_YAW_KI; dv = -dv_max; } } // r_ref > 0 (left) means the left wheel is inner (slower). This matches // Mode 0's MOTOR_A = Vx*(1 + 0.5*track*kappa_fit) once dv_ff is expanded, // because kappa_fit = -r_ref/Vx. MOTOR_A.Target = Vx - dv; // left MOTOR_B.Target = Vx + dv; // right MOTOR_C.Target = 0; MOTOR_D.Target = 0; #else // Inputs: Vx = rear-axle-center linear speed [m/s], // Vz = rotation speed wz about the turn center [rad/s]. // Both refer to the rear-axle center -- exactly the point the servo // calibration measured R against -- so the geometric curvature is // kappa_geom = wz / v = Vz / Vx = 1/R_rear [1/m] // with the ROS sign convention: Vz > 0 = CCW = left turn. // The calibration table / servo fit use the opposite sign (kappa > 0 // = right turn), so we negate to get the fit-domain curvature: // kappa_fit = -kappa_geom -> Vz > 0 gives kappa_fit < 0 = left. float kappa_geom, kappa_fit; if(float_abs(Vx) > 0.001f) /* Vx sign is drive direction, not steering direction. */ kappa_geom = Vz / float_abs(Vx); else kappa_geom = 0.0f; // Ackermann geometry cannot steer without forward motion. kappa_fit = target_limit_float(-kappa_geom, -AKM_KAPPA_MAX, AKM_KAPPA_MAX); // Rear-wheel differential about the rear-axle center, expressed in the // fit-domain curvature. On a right turn (kappa_fit > 0) the turn center // is to the right, so the right wheel is inner (slower) and the left // wheel is outer (faster): // MOTOR_A (left) = Vx*(1 + 0.5*track*kappa_fit) // MOTOR_B (right) = Vx*(1 - 0.5*track*kappa_fit) MOTOR_A.Target = Vx * (1.0f + 0.5f * Wheel_spacing * kappa_fit); MOTOR_B.Target = Vx * (1.0f - 0.5f * Wheel_spacing * kappa_fit); // The PWM value of the servo controls the steering Angle of the front wheel //���PWMֵ���������ǰ��ת��Ƕ� Servo=Akm_Curvature_To_Servo(kappa_fit); #endif /* AKM_DIRECT_MAP */ // Servo source override: when enabled, the remote CH1 drives the servo // directly, taking priority over BOTH control laws above. The remote // neutral (AKM_REMOTER_CH1_MID) is shifted onto the servo straight-ahead // neutral (SERVO_INIT) so stick-center = wheels-straight. #if AKM_SERVO_DEBUG_REMOTE_CH1 Servo = Remoter_Ch1 + (SERVO_INIT - AKM_REMOTER_CH1_MID); #endif //Wheel (motor) target speed limit //����(���)Ŀ���ٶ��޷� MOTOR_A.Target=target_limit_float(MOTOR_A.Target,-amplitude,amplitude); MOTOR_B.Target=target_limit_float(MOTOR_B.Target,-amplitude,amplitude); MOTOR_C.Target=0; //Out of use //û��ʹ�õ� MOTOR_D.Target=0; //Out of use //û��ʹ�õ� Servo=target_limit_int(Servo,AKM_SERVO_MIN,AKM_SERVO_MAX); //Servo PWM value limit //PWMֵ޷ } //Differential car //����С�� else if (Car_Mode==Diff_Car) { //Inverse kinematics //�˶�ѧ��� MOTOR_A.Target = Vx - Vz * Wheel_spacing / 2.0f; //��������ֵ�Ŀ���ٶ� MOTOR_B.Target = Vx + Vz * Wheel_spacing / 2.0f; //��������ֵ�Ŀ���ٶ� //Wheel (motor) target speed limit //����(���)Ŀ���ٶ��޷� MOTOR_A.Target=target_limit_float( MOTOR_A.Target,-amplitude,amplitude); MOTOR_B.Target=target_limit_float( MOTOR_B.Target,-amplitude,amplitude); MOTOR_C.Target=0; //Out of use //û��ʹ�õ� MOTOR_D.Target=0; //Out of use //û��ʹ�õ� } //FourWheel car //������ else if(Car_Mode==FourWheel_Car) { //Inverse kinematics //�˶�ѧ��� MOTOR_A.Target = Vx - Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //��������ֵ�Ŀ���ٶ� MOTOR_B.Target = Vx - Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //��������ֵ�Ŀ���ٶ� MOTOR_C.Target = Vx + Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //��������ֵ�Ŀ���ٶ� MOTOR_D.Target = Vx + Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //��������ֵ�Ŀ���ٶ� //Wheel (motor) target speed limit //����(���)Ŀ���ٶ��޷� MOTOR_A.Target=target_limit_float( MOTOR_A.Target,-amplitude,amplitude); MOTOR_B.Target=target_limit_float( MOTOR_B.Target,-amplitude,amplitude); MOTOR_C.Target=target_limit_float( MOTOR_C.Target,-amplitude,amplitude); MOTOR_D.Target=target_limit_float( MOTOR_D.Target,-amplitude,amplitude); } //Tank Car //�Ĵ��� else if (Car_Mode==Tank_Car) { //Inverse kinematics //�˶�ѧ��� MOTOR_A.Target = Vx - Vz * (Wheel_spacing) / 2.0f; //��������ֵ�Ŀ���ٶ� MOTOR_B.Target = Vx + Vz * (Wheel_spacing) / 2.0f; //��������ֵ�Ŀ���ٶ� //Wheel (motor) target speed limit //����(���)Ŀ���ٶ��޷� MOTOR_A.Target=target_limit_float( MOTOR_A.Target,-amplitude,amplitude); MOTOR_B.Target=target_limit_float( MOTOR_B.Target,-amplitude,amplitude); MOTOR_C.Target=0; //Out of use //û��ʹ�õ� MOTOR_D.Target=0; //Out of use //û��ʹ�õ� } } /************************************************************************** Function: FreerTOS task, core motion control task Input : none Output : none �������ܣ�FreeRTOS���񣬺����˶��������� ��ڲ������� ���� ֵ���� **************************************************************************/ void Balance_task(void *pvParameters) { u32 lastWakeTime = getSysTickCnt(); u32 lastControlTime = lastWakeTime; while(1) { u32 currentControlTime; float controlDt; // Run the wheel-speed control loop at 200 Hz. vTaskDelayUntil(&lastWakeTime, F2T(RATE_200_HZ)); currentControlTime = getSysTickCnt(); controlDt = (float)(currentControlTime - lastControlTime) / (float)configTICK_RATE_HZ; lastControlTime = currentControlTime; // Avoid a large integral jump if the task is stalled while debugging. controlDt = target_limit_float(controlDt, 0.001f, 0.050f); //Time count is no longer needed after 30 seconds //ʱ�������30�������Ҫ if(Time_count<(30*CONTROL_FREQUENCY))Time_count++; //Get the encoder data, that is, the real time wheel speed, //and convert to transposition international units //��ȡ���������ݣ�������ʵʱ�ٶȣ���ת��λ���ʵ�λ Get_Velocity_Form_Encoder(); if(Check==0) //If self-check mode is not enabled //���û�������Լ�ģʽ { // command_lost_count++; //���ڡ�CAN�������ʧʱ���������ʧ1���ֹͣ���� // if(command_lost_count>RATE_100_HZ && APP_ON_Flag==0 && Remote_ON_Flag==0 && PS2_ON_Flag==0) //����APP��PS2����ģң��ģʽ������CAN������1������3����ģʽ // Move_X=0, Move_Y=0, Move_Z=0; if (APP_ON_Flag) Get_RC(); //Handle the APP remote commands //����APPң������ else if (Remote_ON_Flag) Remote_Control(); //Handle model aircraft remote commands //������ģң������ else if (PS2_ON_Flag) PS2_control(); //Handle PS2 controller commands //����PS2�ֱ��������� //CAN, Usart 1, Usart 3, Uart5 control can directly get the three axis target speed, //without additional processing //CAN������1������3(ROS)������5����ֱ�ӵõ�����Ŀ���ٶȣ�������⴦�� else Drive_Motor(Move_X, Move_Y, Move_Z); //Click the user button to update the gyroscope zero //�����û������������������ Key(); //If there is no abnormity in the battery voltage, and the enable switch is in the ON position, //and the software failure flag is 0 //�����ص�ѹ�������쳣������ʹ�ܿ�����ON��λ����������ʧ�ܱ�־λΪ0 if(Turn_Off(Voltage)==0) { //Speed closed-loop control to calculate the PWM value of each motor, //PWM represents the actual wheel speed //�ٶȱջ����Ƽ�������PWMֵ��PWM��������ʵ��ת�� MOTOR_A.Motor_Pwm=Incremental_PI_A(MOTOR_A.Encoder, MOTOR_A.Target, controlDt); MOTOR_B.Motor_Pwm=Incremental_PI_B(MOTOR_B.Encoder, MOTOR_B.Target, controlDt); MOTOR_C.Motor_Pwm=Incremental_PI_C(MOTOR_C.Encoder, MOTOR_C.Target, controlDt); MOTOR_D.Motor_Pwm=Incremental_PI_D(MOTOR_D.Encoder, MOTOR_D.Target, controlDt); Limit_Pwm(16700); //Set different PWM control polarity according to different car models //���ݲ�ͬС���ͺ����ò�ͬ��PWM���Ƽ��� switch(Car_Mode) { case Mec_Car: Set_Pwm( MOTOR_A.Motor_Pwm, -MOTOR_B.Motor_Pwm, -MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Mecanum wheel car //�����ķ��С�� case Omni_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, -MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Omni car //ȫ����С�� case Akm_Car: Set_Pwm( MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, 16799,-16799 , Servo); break; //Ackermann structure car //������С�� case Diff_Car: Set_Pwm( MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Differential car //���ֲ���С�� case FourWheel_Car: Set_Pwm( MOTOR_A.Motor_Pwm, -MOTOR_B.Motor_Pwm, -MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //FourWheel car //������ case Tank_Car: Set_Pwm( MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Tank Car //�Ĵ��� } } //If Turn_Off(Voltage) returns to 1, the car is not allowed to move, and the PWM value is set to 0 //���Turn_Off(Voltage)����ֵΪ1������������С�������˶���PWMֵ����Ϊ0 else Set_Pwm(0,0,0,0,(Car_Mode == Akm_Car) ? SERVO_INIT : 0); } } } /************************************************************************** Function: Assign a value to the PWM register to control wheel speed and direction Input : PWM Output : none �������ܣ���ֵ��PWM�Ĵ��������Ƴ���ת���뷽�� ��ڲ�����PWM ���� ֵ���� **************************************************************************/ void Set_Pwm(int motor_a,int motor_b,int motor_c,int motor_d,int servo) { //Forward and reverse control of motor //�������ת���� if(motor_a<0) PWMA1=16799,PWMA2=16799+motor_a; else PWMA2=16799,PWMA1=16799-motor_a; //Forward and reverse control of motor //�������ת���� if(motor_b<0) PWMB1=16799,PWMB2=16799+motor_b; else PWMB2=16799,PWMB1=16799-motor_b; // PWMB1=10000,PWMB2=5000; //Forward and reverse control of motor //�������ת���� if(motor_c<0) PWMC1=16799,PWMC2=16799+motor_c; else PWMC2=16799,PWMC1=16799-motor_c; //Forward and reverse control of motor //�������ת���� if(motor_d<0) PWMD1=16799,PWMD2=16799+motor_d; else PWMD2=16799,PWMD1=16799-motor_d; //Servo control //������� Servo_PWM =servo; } /************************************************************************** Function: Limit PWM value Input : Value Output : none �������ܣ�����PWMֵ ��ڲ�������ֵ ���� ֵ���� **************************************************************************/ void Limit_Pwm(int amplitude) { MOTOR_A.Motor_Pwm=target_limit_float(MOTOR_A.Motor_Pwm,-amplitude,amplitude); MOTOR_B.Motor_Pwm=target_limit_float(MOTOR_B.Motor_Pwm,-amplitude,amplitude); MOTOR_C.Motor_Pwm=target_limit_float(MOTOR_C.Motor_Pwm,-amplitude,amplitude); MOTOR_D.Motor_Pwm=target_limit_float(MOTOR_D.Motor_Pwm,-amplitude,amplitude); } /************************************************************************** Function: Limiting function Input : Value Output : none �������ܣ��޷����� ��ڲ�������ֵ ���� ֵ���� **************************************************************************/ float target_limit_float(float insert,float low,float high) { if (insert < low) return low; else if (insert > high) return high; else return insert; } int target_limit_int(int insert,int low,int high) { if (insert < low) return low; else if (insert > high) return high; else return insert; } /************************************************************************** Function: Check the battery voltage, enable switch status, software failure flag status Input : Voltage Output : Whether control is allowed, 1: not allowed, 0 allowed �������ܣ�����ص�ѹ��ʹ�ܿ���״̬������ʧ�ܱ�־λ״̬ ��ڲ�������ѹ ���� ֵ���Ƿ��������ƣ�1����������0���� **************************************************************************/ u8 Turn_Off( int voltage) { u8 temp; if(voltage<10||EN==0||Flag_Stop==1) { temp=1; PWMA1=0;PWMA2=0; PWMB1=0;PWMB2=0; PWMC1=0;PWMC1=0; PWMD1=0;PWMD2=0; } else temp=0; return temp; } /************************************************************************** Function: Calculate absolute value Input : long int Output : unsigned int �������ܣ������ֵ ��ڲ�����long int ���� ֵ��unsigned int **************************************************************************/ u32 myabs(long int a) { u32 temp; if(a<0) temp=-a; else temp=a; return temp; } /************************************************************************** Function: Incremental PI controller Input : Encoder measured value (actual speed), target speed Output : Motor PWM According to the incremental discrete PID formula pwm+=Kp[e��k��-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)] e(k) represents the current deviation e(k-1) is the last deviation and so on PWM stands for incremental output In our speed control closed loop system, only PI control is used pwm+=Kp[e��k��-e(k-1)]+Ki*e(k)*dt �������ܣ�����ʽPI������ ��ڲ���������������ֵ(ʵ���ٶ�)��Ŀ���ٶ� ���� ֵ�����PWM ��������ʽ��ɢPID��ʽ pwm+=Kp[e��k��-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)] e(k)��������ƫ�� e(k-1)������һ�ε�ƫ�� �Դ����� pwm����������� �����ǵ��ٶȿ��Ʊջ�ϵͳ���棬ֻʹ��PI���� pwm+=Kp[e��k��-e(k-1)]+Ki*e(k)*dt **************************************************************************/ int Incremental_PI_A (float Encoder,float Target,float dt) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //����ƫ�� Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias*dt; if(Pwm>16700)Pwm=16700; if(Pwm<-16700)Pwm=-16700; Last_bias=Bias; //Save the last deviation //������һ��ƫ�� return Pwm; } int Incremental_PI_B (float Encoder,float Target,float dt) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //����ƫ�� Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias*dt; if(Pwm>16700)Pwm=16700; if(Pwm<-16700)Pwm=-16700; Last_bias=Bias; //Save the last deviation //������һ��ƫ�� return Pwm; } int Incremental_PI_C (float Encoder,float Target,float dt) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //����ƫ�� Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias*dt; if(Pwm>16700)Pwm=16700; if(Pwm<-16700)Pwm=-16700; Last_bias=Bias; //Save the last deviation //������һ��ƫ�� return Pwm; } int Incremental_PI_D (float Encoder,float Target,float dt) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //����ƫ�� Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias*dt; if(Pwm>16700)Pwm=16700; if(Pwm<-16700)Pwm=-16700; Last_bias=Bias; //Save the last deviation //������һ��ƫ�� return Pwm; } /************************************************************************** Function: Processes the command sent by APP through usart 2 Input : none Output : none �������ܣ���APPͨ������2���͹�����������д��� ��ڲ������� ���� ֵ���� **************************************************************************/ void Get_RC(void) { u8 Flag_Move=1; if(Car_Mode==Mec_Car||Car_Mode==Omni_Car) //The omnidirectional wheel moving trolley can move laterally //ȫ�����˶�С�����Խ��к����ƶ� { switch(Flag_Direction) //Handle direction control commands //��������������� { case 1: Move_X=RC_Velocity; Move_Y=0; Flag_Move=1; break; case 2: Move_X=RC_Velocity; Move_Y=-RC_Velocity; Flag_Move=1; break; case 3: Move_X=0; Move_Y=-RC_Velocity; Flag_Move=1; break; case 4: Move_X=-RC_Velocity; Move_Y=-RC_Velocity; Flag_Move=1; break; case 5: Move_X=-RC_Velocity; Move_Y=0; Flag_Move=1; break; case 6: Move_X=-RC_Velocity; Move_Y=RC_Velocity; Flag_Move=1; break; case 7: Move_X=0; Move_Y=RC_Velocity; Flag_Move=1; break; case 8: Move_X=RC_Velocity; Move_Y=RC_Velocity; Flag_Move=1; break; default: Move_X=0; Move_Y=0; Flag_Move=0; break; } if(Flag_Move==0) { //If no direction control instruction is available, check the steering control status //����޷������ָ����ת�����״̬ if (Flag_Left ==1) Move_Z= PI/2*(RC_Velocity/500); //left rotation //����ת else if(Flag_Right==1) Move_Z=-PI/2*(RC_Velocity/500); //right rotation //����ת else Move_Z=0; //stop //ֹͣ } } else //Non-omnidirectional moving trolley //��ȫ���ƶ�С�� { switch(Flag_Direction) //Handle direction control commands //��������������� { case 1: Move_X=+RC_Velocity; Move_Z=0; break; case 2: Move_X=+RC_Velocity; Move_Z=-PI/2; break; case 3: Move_X=0; Move_Z=-PI/2; break; case 4: Move_X=-RC_Velocity; Move_Z=-PI/2; break; case 5: Move_X=-RC_Velocity; Move_Z=0; break; case 6: Move_X=-RC_Velocity; Move_Z=+PI/2; break; case 7: Move_X=0; Move_Z=+PI/2; break; case 8: Move_X=+RC_Velocity; Move_Z=+PI/2; break; default: Move_X=0; Move_Z=0; break; } if (Flag_Left ==1) Move_Z= PI/2; //left rotation //����ת else if(Flag_Right==1) Move_Z=-PI/2; //right rotation //����ת } //Z-axis data conversion //Z������ת�� if(Car_Mode==Akm_Car) { //Ackermann structure car is converted to the front wheel steering Angle system target value, and kinematics analysis is pearformed //�������ṹС��ת��Ϊǰ��ת��Ƕ� Move_Z=Move_Z*2/9; } else if(Car_Mode==Diff_Car||Car_Mode==Tank_Car||Car_Mode==FourWheel_Car) { if(Move_X<0) Move_Z=-Move_Z; //The differential control principle series requires this treatment //���ٿ���ԭ��ϵ����Ҫ�˴��� Move_Z=Move_Z*RC_Velocity/500; } //Unit conversion, mm/s -> m/s //��λת����mm/s -> m/s Move_X=Move_X/1000; Move_Y=Move_Y/1000; Move_Z=Move_Z; //Control target value is obtained and kinematics analysis is performed //�õ�����Ŀ��ֵ�������˶�ѧ���� Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** Function: Handle PS2 controller control commands Input : none Output : none �������ܣ���PS2�ֱ�����������д��� ��ڲ������� ���� ֵ���� **************************************************************************/ void PS2_control(void) { int LX,LY,RY; int Threshold=20; //Threshold to ignore small movements of the joystick //��ֵ������ҡ��С���ȶ��� //128 is the median.The definition of X and Y in the PS2 coordinate system is different from that in the ROS coordinate system //128Ϊ��ֵ��PS2����ϵ��ROS����ϵ��X��Y�Ķ��岻һ�� LY=-(PS2_LX-128); LX=-(PS2_LY-128); RY=-(PS2_RX-128); //Ignore small movements of the joystick //����ҡ��С���ȶ��� if(LX>-Threshold&&LX-Threshold&&LY-Threshold&&RY m/s //��λת����mm/s -> m/s Move_X=Move_X/1000; Move_Y=Move_Y/1000; Move_Z=Move_Z; //Control target value is obtained and kinematics analysis is performed //�õ�����Ŀ��ֵ�������˶�ѧ���� Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** Function: The remote control command of model aircraft is processed Input : none Output : none �������ܣ��Ժ�ģң�ؿ���������д��� ��ڲ������� ���� ֵ���� **************************************************************************/ void Remote_Control(void) { //Data within 1 second after entering the model control mode will not be processed //�Խ��뺽ģ����ģʽ��1���ڵ����ݲ����� static u8 thrice=CONTROL_FREQUENCY; int Threshold=100; //Threshold to ignore small movements of the joystick //��ֵ������ҡ��С���ȶ��� //limiter //�޷� int LX,LY,RY,RX,Remote_RCvelocity; Remoter_Ch1=target_limit_int(Remoter_Ch1,1000,2000); Remoter_Ch2=target_limit_int(Remoter_Ch2,1000,2000); Remoter_Ch3=target_limit_int(Remoter_Ch3,1000,2000); Remoter_Ch4=target_limit_int(Remoter_Ch4,1000,2000); // Front and back direction of left rocker. Control forward and backward. //��ҡ��ǰ���򡣿���ǰ�����ˡ� LX=Remoter_Ch2-1500; //Left joystick left and right.Control left and right movement. Only the wheelie omnidirectional wheelie will use the channel. //Ackerman trolleys use this channel as a PWM output to control the steering gear //��ҡ�����ҷ��򡣿��������ƶ�������ȫ���ֲŻ�ʹ�õ���ͨ����������С��ʹ�ø�ͨ����ΪPWM������ƶ�� LY=Remoter_Ch4-1500; //Front and back direction of right rocker. Throttle/acceleration/deceleration. //��ҡ��ǰ��������/�Ӽ��١� RX=Remoter_Ch3-1500; //Right stick left and right. To control the rotation. //��ҡ�����ҷ��򡣿�����ת�� RY=Remoter_Ch1-1500; if(LX>-Threshold&&LX-Threshold&&LY-Threshold&&RX-Threshold&&RY m/s //��λת����mm/s -> m/s Move_X=Move_X/1000; Move_Y=Move_Y/1000; Move_Z=Move_Z; //Data within 1 second after entering the model control mode will not be processed //�Խ��뺽ģ����ģʽ��1���ڵ����ݲ����� if(thrice>0) Move_X=0,Move_Z=0,thrice--; //Control target value is obtained and kinematics analysis is performed //�õ�����Ŀ��ֵ�������˶�ѧ���� Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** Function: Click the user button to update gyroscope zero Input : none Output : none �������ܣ������û������������������ ��ڲ������� ���� ֵ���� **************************************************************************/ void Key(void) { u8 tmp; tmp=click_N_Double_MPU6050(50); if(tmp==2)memcpy(Deviation_gyro,Original_gyro,sizeof(gyro)),memcpy(Deviation_accel,Original_accel,sizeof(accel)); } /************************************************************************** Function: Read the encoder value and calculate the wheel speed, unit m/s Input : none Output : none �������ܣ���ȡ��������ֵ�����㳵���ٶȣ���λm/s ��ڲ������� ���� ֵ���� **************************************************************************/ void Get_Velocity_Form_Encoder(void) { //Retrieves the original data of the encoder //��ȡ��������ԭʼ���� float Encoder_A_pr,Encoder_B_pr,Encoder_C_pr,Encoder_D_pr; //Stamp the moment the encoder counters are latched, as close to the //real sample instant as possible. //�ڶ�ȡ��������������˲���ʱ��������������ʵ�ɼ�ʱ�̡� g_speed_sample_time_us = mcu_time_us(); OriginalEncoder.A=Read_Encoder(2); OriginalEncoder.B=Read_Encoder(3); OriginalEncoder.C=Read_Encoder(4); OriginalEncoder.D=Read_Encoder(5); //test_num=OriginalEncoder.B; //Decide the encoder numerical polarity according to different car models //���ݲ�ͬС���ͺž�����������ֵ���� switch(Car_Mode) { case Mec_Car: Encoder_A_pr= OriginalEncoder.A; Encoder_B_pr= OriginalEncoder.B; Encoder_C_pr=-OriginalEncoder.C; Encoder_D_pr=-OriginalEncoder.D; break; case Omni_Car: Encoder_A_pr=-OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B; Encoder_C_pr=-OriginalEncoder.C; Encoder_D_pr=-OriginalEncoder.D; break; case Akm_Car: Encoder_A_pr= OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B; Encoder_C_pr= OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; case Diff_Car: Encoder_A_pr= OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B; Encoder_C_pr= OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; case FourWheel_Car: Encoder_A_pr= OriginalEncoder.A; Encoder_B_pr= OriginalEncoder.B; Encoder_C_pr=-OriginalEncoder.C; Encoder_D_pr=-OriginalEncoder.D; break; case Tank_Car: Encoder_A_pr= OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B; Encoder_C_pr= OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; } //The encoder converts the raw data to wheel speed in m/s //������ԭʼ����ת��Ϊ�����ٶȣ���λm/s MOTOR_A.Encoder= Encoder_A_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_B.Encoder= Encoder_B_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_C.Encoder= Encoder_C_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_D.Encoder= Encoder_D_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; } /************************************************************************** Function: Smoothing the three axis target velocity Input : Three-axis target velocity Output : none �������ܣ�������Ŀ���ٶ���ƽ������ ��ڲ���������Ŀ���ٶ� ���� ֵ���� **************************************************************************/ void Smooth_control(float vx,float vy,float vz) { float step=0.01; if (vx>0) smooth_control.VX+=step; else if(vx<0) smooth_control.VX-=step; else if(vx==0) smooth_control.VX=smooth_control.VX*0.9f; if (vy>0) smooth_control.VY+=step; else if(vy<0) smooth_control.VY-=step; else if(vy==0) smooth_control.VY=smooth_control.VY*0.9f; if (vz>0) smooth_control.VZ+=step; else if(vz<0) smooth_control.VZ-=step; else if(vz==0) smooth_control.VZ=smooth_control.VZ*0.9f; smooth_control.VX=target_limit_float(smooth_control.VX,-float_abs(vx),float_abs(vx)); smooth_control.VY=target_limit_float(smooth_control.VY,-float_abs(vy),float_abs(vy)); smooth_control.VZ=target_limit_float(smooth_control.VZ,-float_abs(vz),float_abs(vz)); } /************************************************************************** Function: Floating-point data calculates the absolute value Input : float Output : The absolute value of the input number �������ܣ����������ݼ������ֵ ��ڲ����������� ���� ֵ���������ľ���ֵ **************************************************************************/ float float_abs(float insert) { if(insert>=0) return insert; else return -insert; } /************************************************************************** Function: Prevent the potentiometer to choose the wrong mode, resulting in initialization error caused by the motor spinning.Out of service Input : none Output : none �������ܣ���ֹ��λ��ѡ��ģʽ�����³�ʼ���������������ת����ֹͣʹ�� ��ڲ������� ���� ֵ���� **************************************************************************/ void robot_mode_check(void) { static u8 error=0; if(abs(MOTOR_A.Motor_Pwm)>2500||abs(MOTOR_B.Motor_Pwm)>2500||abs(MOTOR_C.Motor_Pwm)>2500||abs(MOTOR_D.Motor_Pwm)>2500) error++; //If the output is close to full amplitude for 6 times in a row, it is judged that the motor rotates wildly and makes the motor incapacitated //�������6�νӽ�����������ж�Ϊ�����ת���õ��ʧ�� if(error>6) EN=0,Flag_Stop=1,robot_mode_check_flag=1; }