//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // 구동 관련 파라메터 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // 추후에 연산 시간을 줄이기 위해 곱셈, 나눗셈 매크로 대신 상수로 대체할 것. // 모터 1회전당 펄스 수 #define MOTOR_PPR (12.0f) // 모터 1회전당 4채배 펄스 수 #define MOTOR_PPR_4X ((MOTOR_PPR) * 4.0f) // 기어 감속비 #define GEAR_RATIO (49.89f) // 바퀴 1회전당 4채배 펄스 수 : 48.0f * 49.89f = 2394.72f #define WHEEL_PPR_4X ((MOTOR_PPR_4X) * (GEAR_RATIO)) //#define WHEEL_PPR_4X (2394.72f) // 바퀴 외경 반지름: 값을 줄이면 더 가고, 값을 늘리면 덜 간다. #define WHEEL_RADIUS (35.0f) // 바퀴 외경 지름 : 68.0f #define WHEEL_DIAMETER ((WHEEL_RADIUS) * 2.0f) // 바퀴 원주 : 213.62830044410594021545975006301 #define WHEEL_CIRCLE_LEN ((WHEEL_DIAMETER) * (M_PI)) // 한 펄스당 진행 거리 : 0.10628273653935618916192027366319 (mm/pulse) #define MM_PER_PULSE ((WHEEL_CIRCLE_LEN) / (WHEEL_PPR_4X)) // 1mm 진행에 해당하는 펄스 수 : 9.4088657533738124969251871876104 (pulse/mm) #define PULSE_PER_MM ((WHEEL_PPR_4X) / (WHEEL_CIRCLE_LEN)) // 펄스 -> 거리 변환 #define PULSE2MM(p) ((p) * (MM_PER_PULSE)) // 거리 -> 펄스 변환 #define MM2PULSE(d) ((d) * (PULSE_PER_MM)) // 제어 주기 (sec) //#define WHEEL_CTRL_PERIOD (0.050f) #define WHEEL_CTRL_PERIOD (1.0f/TICKS_PER_SEC) // 제어 주기에 대한 주파수 (Hz) - PWM 주파수가 아니라 초당 바퀴 속도 제어 횟수임. #define WHEEL_CTRL_FREQ (1.0f / (WHEEL_CTRL_PERIOD)) // 펄스 -> 속도 변환 #define PULSE2VEL(p) (PULSE2MM(p) / WHEEL_CTRL_PERIOD) // 속도 -> 펄스 변환 #define VEL2PULSE(v) (MM2PULSE(v) * WHEEL_CTRL_PERIOD) // 바퀴의 최저 속도 (mm/sec) #define MIN_WHEEL_VEL (3.0f) // 바퀴의 최고 속도 (mm/sec) #define MAX_WHEEL_VEL (450.0f) #define MAX_PWM_DUTY (99) // 바퀴간 거리: 값을 줄이면 덜 회전하고, 키우면 더 회전한다. (odometry 각도 사용시) #define WHEEL_BASE (237.5f) //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Math //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #ifndef M_PI #define M_PI (3.1415926535897932384626433832795f) #endif // M_PI #define DEG2RAD(deg) (((M_PI)/180.0f)*(FLOAT)(deg)) #define RAD2DEG(rad) ((180.0f/(M_PI))*(FLOAT)(rad)) #define SQR(x) ((x)*(x)) #define MAX(a,b) (((a)>(b))?(a):(b)) #define MIN(a,b) (((a)<(b))?(a):(b)) #define SAT(x,l,h) MIN(MAX((x),(l)),(h)) /** * @fn PUBLIC inline S16 Math2CalEncOffset(U16 u16PrevEnc, U16 u16CurrEnc) * @brief 엔코더 이동량 계산 * @param u16PrevEnc [in] 이전 엔코더 카운트 값 * @param u16CurrEnc [in] 현재 엔코더 카운트 값 */ PUBLIC inline S16 Math2CalEncOffset(U16 u16PrevEnc, U16 u16CurrEnc) { U16 u16IntAbsOffset = 0, u16ExtAbsOffset = 0; S16 s16EncOffset = 0; // combination of Prev and Curr: // // (1) Prev Curr // ------------------------------+------+---- forward (Prev <= Curr) // // (2) Curr Prev // -----+-------------------------------+---- forward (Prev > Curr, overflow) // // (3) Curr Prev // ------------------------------+------+---- backward (Prev > Curr) // // (4) Prev Curr // -----+-------------------------------+---- backward (Prev <= Curr, underflow) // case of (1) and (4) if (u16PrevEnc <= u16CurrEnc ) { // internal offset u16IntAbsOffset = u16CurrEnc - u16PrevEnc; // external offset u16ExtAbsOffset = (0xffff - u16CurrEnc) + u16PrevEnc + 1; // encoder count offset s16EncOffset = ( u16IntAbsOffset <= u16ExtAbsOffset ) ? u16IntAbsOffset: // case of (1) forward -u16ExtAbsOffset; // case of (4) backward (underflow) } // case of (2) and (3) else { // internal offset u16IntAbsOffset = u16PrevEnc - u16CurrEnc; // external offset u16ExtAbsOffset = (0xffff - u16PrevEnc) + u16CurrEnc + 1; // encoder count offset s16EncOffset = ( u16IntAbsOffset > u16ExtAbsOffset ) ? u16ExtAbsOffset: // case of (2) forward (overflow) -u16IntAbsOffset; // case of (3) backward } return s16EncOffset; } /******************************************************************************* ** @fn PUBLIC inline void Math2WheelVel2LinAngVel(const float fLVel, const float fRVel, float* fLinVel, float* fAngVel) ** @brief convert linear/angular velocity into left/right wheel velocity ** @param fLVel [in] left wheel velocity ** @param fRVel [in] right wheel velocity ** @param fLinVel [out] linear velocity ** @param fAngVel [out] angular velocity ** @remark This function is called periodically every MOTION_DELTA_T by Prd. *******************************************************************************/ PUBLIC inline void Math2WheelVel2LinAngVel(float fLVel,float fRVel, float* fLinVel, float* fAngVel) { *fLinVel = (fLVel + fRVel) * 0.5f; *fAngVel = (fRVel - fLVel) / WHEEL_BASE; } /******************************************************************************* ** @fn PUBLIC inline void Math2LinAngVel2WheelVel(const float fLinVel, const float fAngVel, float* fLVel, float* fRVel) ** @brief convert left/right wheel velocity into linear/angular velocity ** @param fLinVel [out] linear velocity ** @param fAngVel [out] angular velocity ** @param fLVel [in] left wheel velocity ** @param fRVel [in] right wheel velocity ** @remark This function is called periodically every DELTA_T by Prd. *******************************************************************************/ PUBLIC inline void Math2LinAngVel2WheelVel(const float fLinVel, const float fAngVel, float* fLVel, float* fRVel) { *fLVel = fLinVel - 0.5f * WHEEL_BASE * fAngVel; *fRVel = fLinVel + 0.5f * WHEEL_BASE * fAngVel; } /******************************************************************************* ** PUBLIC inline void Math2NormalizeOri(FLOAT* fOri) ** This function normalize fOri with between -M_PI and +M_PI. ** @param fOri [in/out] orientation angle (radian) ** @return normalized orientation angle *******************************************************************************/ PUBLIC inline void Math2NormalizeOri(FLOAT* fOri) { while ( *fOri <= -M_PI ) *fOri += (M_PI + M_PI); while ( *fOri > +M_PI ) *fOri -= (M_PI + M_PI); } //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Quadrature Encoder //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Quadrature Encoder Truth Table // 전제조건: A상이 B상보다 90도 앞서는 것이 전진(CW,증가)이다. // ------+------------------------------------------------- // (A:B) | Curr. // Prev. | (0:0) (0:1) (1:0) (1:1) // ------+------------------------------------------------- // (0:0) | No_Change Backward Forward Error // (0:1) | Forward No_Change Error Backward // (1:0) | Backward Error No_Change Forward // (1:1) | Error Forward Backward No_Change // ------+-------------------------------------------------- // Quadrature Encoder PRIVATE U08 bPrevEncLeftPhaseA = FALSE; PRIVATE U08 bPrevEncLeftPhaseB = FALSE; PRIVATE U08 bPrevEncRightPhaseA = FALSE; PRIVATE U08 bPrevEncRightPhaseB = FALSE; PUBLIC U16 u16EncoderLeft = 0; PUBLIC U16 u16EncoderRight = 0; void __ISR(_INPUT_CAPTURE_1_VECTOR, ipl5) IC1_Interrupt_ISR(void) { mIC1ClearIntFlag(); if ((bPrevEncLeftPhaseA == bPrevEncLeftPhaseB) && (IN_ENC_LEFT_A != IN_ENC_LEFT_B)) { if (bPrevEncLeftPhaseA == IN_ENC_LEFT_A) u16EncoderLeft++; else u16EncoderLeft--; } else if ((bPrevEncLeftPhaseA != bPrevEncLeftPhaseB) && (IN_ENC_LEFT_A == IN_ENC_LEFT_B)) { if (bPrevEncLeftPhaseA == IN_ENC_LEFT_A) u16EncoderLeft--; else u16EncoderLeft++; } // update previous phase bPrevEncLeftPhaseA = IN_ENC_LEFT_A; bPrevEncLeftPhaseB = IN_ENC_LEFT_B; } void __ISR(_INPUT_CAPTURE_2_VECTOR, ipl5) IC2_Interrupt_ISR(void) { mIC2ClearIntFlag(); if ((bPrevEncLeftPhaseA == bPrevEncLeftPhaseB) && (IN_ENC_LEFT_A != IN_ENC_LEFT_B)) { if (bPrevEncLeftPhaseA == IN_ENC_LEFT_A) u16EncoderLeft++; else u16EncoderLeft--; } else if ((bPrevEncLeftPhaseA != bPrevEncLeftPhaseB) && (IN_ENC_LEFT_A == IN_ENC_LEFT_B)) { if (bPrevEncLeftPhaseA == IN_ENC_LEFT_A) u16EncoderLeft--; else u16EncoderLeft++; } // update previous phase bPrevEncLeftPhaseA = IN_ENC_LEFT_A; bPrevEncLeftPhaseB = IN_ENC_LEFT_B; } void __ISR(_INPUT_CAPTURE_3_VECTOR, ipl5) IC3_Interrupt_ISR(void) { mIC3ClearIntFlag(); if ((bPrevEncRightPhaseA == bPrevEncRightPhaseB) && (IN_ENC_RIGHT_A != IN_ENC_RIGHT_B)) { if (bPrevEncRightPhaseA == IN_ENC_RIGHT_A) u16EncoderRight--; else u16EncoderRight++; } else if ((bPrevEncRightPhaseA != bPrevEncRightPhaseB) && (IN_ENC_RIGHT_A == IN_ENC_RIGHT_B)) { if (bPrevEncRightPhaseA == IN_ENC_RIGHT_A) u16EncoderRight++; else u16EncoderRight--; } // update previous phase bPrevEncRightPhaseA = IN_ENC_RIGHT_A; bPrevEncRightPhaseB = IN_ENC_RIGHT_B; } void __ISR(_INPUT_CAPTURE_4_VECTOR, ipl5) IC4_Interrupt_ISR(void) { mIC4ClearIntFlag(); if ((bPrevEncRightPhaseA == bPrevEncRightPhaseB) && (IN_ENC_RIGHT_A != IN_ENC_RIGHT_B)) { if (bPrevEncRightPhaseA == IN_ENC_RIGHT_A) u16EncoderRight--; else u16EncoderRight++; } else if ((bPrevEncRightPhaseA != bPrevEncRightPhaseB) && (IN_ENC_RIGHT_A == IN_ENC_RIGHT_B)) { if (bPrevEncRightPhaseA == IN_ENC_RIGHT_A) u16EncoderRight++; else u16EncoderRight--; } // update previous phase bPrevEncRightPhaseA = IN_ENC_RIGHT_A; bPrevEncRightPhaseB = IN_ENC_RIGHT_B; } PUBLIC U16 EncGetCountLeftWheel(void) { return u16EncoderLeft; } PUBLIC U16 EncGetCountRightWheel(void) { return u16EncoderRight; } //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // PID 관련 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // 바퀴 정속 제어를 위한 P, I 게인값 #define K_P (0.1f) #define K_I (0.1f) // 가변 P 게인을 적용할 속도 구간 #define V_LSL (80.0f) #define V_USL (150.0f) // I 게인에 의한 엔코더 카운트의 최대 누적 오차 #define MAX_ERRSUM (3000) // PWM 비교값 가감 옵셋 #define PWM_OFFSET (700) // 새로운 지령 속도 로딩 여부 PRIVATE BOOL m_bNewCmd = FALSE; // 지령 속도 PRIVATE FLOAT m_fDesiredVl = 0.0f; PRIVATE FLOAT m_fDesiredVr = 0.0f; // 목표 속도 PRIVATE FLOAT m_fTargVl = 0.0f; PRIVATE FLOAT m_fTargVr = 0.0f; // 목표 속도에 준하는 엔코더 옵셋량 PRIVATE FLOAT m_fTargEncL = 0; PRIVATE FLOAT m_fTargEncR = 0; // 누적 엔코더 옵셋량 PRIVATE FLOAT m_fEncErrSumL = 0; PRIVATE FLOAT m_fEncErrSumR = 0; // PWM PUBLIC USHORT tmpPWMLeft = 0; PUBLIC USHORT tmpPWMRight = 0; PUBLIC UCHAR tmpPWMCount = 0; #define _PI 3.14159265 #define enc2cm 0.0313 #define cm2enc 31.889 #define angle2enc 8.0324 #define NORMAL_SPEED 90 // 90 => 30cm/s #define WF_SPEED 65 #define MAX_SPEED 98 #define MIN_SPEED 50 #define MAXDOCK_SPEED 85 #define DOCKING_SPEED 65 //65 #define HOMING_SPEED 65 //70 #define TURNING_SPEED 80 #define BACK_SPEED 80 #define SPIRAL_SPEED 65 #define STOP_SPEED 0 PUBLIC void DevWheelForwardDifferential(U08 lSpeed, U08 rSpeed) { U08 paramLeftWheel; U08 paramRightWheel; paramLeftWheel = lSpeed; paramRightWheel = rSpeed; OUT_LWHEEL_CONTROL_A1 = 0; OUT_RWHEEL_CONTROL_A0 = 0; OUT_LWHEEL_CONTROL_A0 = 1; OUT_RWHEEL_CONTROL_A1 = 1; HalOcEnable(OC_3,FALSE); HalOcEnable(OC_1,TRUE); HalOcEnable(OC_4,TRUE); OC1RS = (UINT16)(((PR2/100) * paramLeftWheel) + 1); OC4RS = (UINT16)(((PR2/100) * paramRightWheel ) + 1); HalTimerOn(TMR_2,TRUE); } PUBLIC void DevWheelTurnRightDifferential(U08 lSpeed, U08 rSpeed) { U08 paramLeftWheel; U08 paramRightWheel; paramLeftWheel = lSpeed; paramRightWheel = rSpeed; OUT_LWHEEL_CONTROL_A1 = 0; OUT_RWHEEL_CONTROL_A1 = 0; OUT_LWHEEL_CONTROL_A0 = 1; OUT_RWHEEL_CONTROL_A0 = 1; HalOcEnable(OC_3,FALSE); HalOcEnable(OC_1,TRUE); HalOcEnable(OC_4,TRUE); OC1RS = (UINT16)(((PR2/100) * paramLeftWheel) + 1); OC4RS = (UINT16)(((PR2/100) * paramRightWheel ) + 1); HalTimerOn(TMR_2,TRUE); } PUBLIC void DevWheelTurnRightRound(U08 lSpeed) { U08 paramLeftWheel; paramLeftWheel = lSpeed; HalOcEnable(OC_3,FALSE); HalOcEnable(OC_4,FALSE); OUT_RWHEEL_CONTROL_A0 = 0; OUT_LWHEEL_CONTROL_A1 = 0; OUT_RWHEEL_CONTROL_A1 = 0; OUT_LWHEEL_CONTROL_A0 = 1; HalOcEnable(OC_1,TRUE); OC1RS = (UINT16)(((PR2/100) * paramLeftWheel) + 1); HalTimerOn(TMR_2,TRUE); } PUBLIC void DevWheelTurnLeftDifferential(U08 lSpeed, U08 rSpeed) { U08 paramLeftWheel; U08 paramRightWheel; paramLeftWheel = lSpeed; paramRightWheel = rSpeed; OUT_LWHEEL_CONTROL_A0 = 0; OUT_RWHEEL_CONTROL_A0 = 0; OUT_LWHEEL_CONTROL_A1 = 1; OUT_RWHEEL_CONTROL_A1 = 1; HalOcEnable(OC_3,FALSE); HalOcEnable(OC_1,TRUE); HalOcEnable(OC_4,TRUE); OC1RS = (UINT16)(((PR2/100) * paramLeftWheel) + 1); OC4RS = (UINT16)(((PR2/100) * paramRightWheel ) + 1); HalTimerOn(TMR_2,TRUE); } PUBLIC void DevWheelBackwardDifferential(U08 lSpeed, U08 rSpeed) { U08 paramLeftWheel; U08 paramRightWheel; paramLeftWheel = lSpeed; paramRightWheel = rSpeed; OUT_LWHEEL_CONTROL_A0 = 0; OUT_RWHEEL_CONTROL_A1 = 0; OUT_LWHEEL_CONTROL_A1 = 1; OUT_RWHEEL_CONTROL_A0 = 1; HalOcEnable(OC_3,FALSE); HalOcEnable(OC_1,TRUE); HalOcEnable(OC_4,TRUE); OC1RS = (UINT16)(((PR2/100) * paramLeftWheel) + 1); OC4RS = (UINT16)(((PR2/100) * paramRightWheel ) + 1); HalTimerOn(TMR_2,TRUE); } PUBLIC void DevWheelTurnLeftBackRound(U08 lSpeed) { U08 paramLeftWheel; paramLeftWheel = lSpeed; HalOcEnable(OC_3,FALSE); HalOcEnable(OC_4,FALSE); OUT_LWHEEL_CONTROL_A0 = 0; OUT_RWHEEL_CONTROL_A0 = 0; OUT_RWHEEL_CONTROL_A1 = 0; OUT_LWHEEL_CONTROL_A1 = 1; HalOcEnable(OC_1,TRUE); OC1RS = (UINT16)(((PR2/100) * paramLeftWheel) + 1); HalTimerOn(TMR_2,TRUE); } PUBLIC void DevWheelTurnLeftRound(U08 rSpeed) { U08 paramRightWheel; paramRightWheel = rSpeed; HalOcEnable(OC_3,FALSE); HalOcEnable(OC_1,FALSE); OUT_LWHEEL_CONTROL_A1 = 0; OUT_LWHEEL_CONTROL_A0 = 0; OUT_RWHEEL_CONTROL_A0 = 0; OUT_RWHEEL_CONTROL_A1 = 1; HalOcEnable(OC_4,TRUE); OC4RS = (UINT16)(((PR2/100) * paramRightWheel ) + 1); HalTimerOn(TMR_2,TRUE); } PUBLIC void DevWheelTurnRightBackRound(U08 rSpeed) { U08 paramRightWheel; paramRightWheel = rSpeed; HalOcEnable(OC_3,FALSE); HalOcEnable(OC_1,FALSE); OUT_LWHEEL_CONTROL_A1 = 0; OUT_RWHEEL_CONTROL_A1 = 0; OUT_LWHEEL_CONTROL_A0 = 0; OUT_RWHEEL_CONTROL_A0 = 1; HalOcEnable(OC_4,TRUE); OC4RS = (UINT16)(((PR2/100) * paramRightWheel ) + 1); HalTimerOn(TMR_2,TRUE); } PUBLIC void DevWheelStop(void) { HalOcEnable(OC_1,FALSE); HalOcEnable(OC_4,FALSE); OC1RS = (UINT16)(STOP_SPEED); OC4RS = (UINT16)(STOP_SPEED); m_u8DevWheelState = WHEEL_STATE_STOP_STOP; OUT_LWHEEL_CONTROL_A0 = 0; OUT_LWHEEL_CONTROL_A1 = 0; OUT_RWHEEL_CONTROL_A0 = 0; OUT_RWHEEL_CONTROL_A1 = 0; } /** * @fn PUBLIC inline void WhlResetEncErrSum(void) * @brief 좌우 바퀴의 엔코더 누적 오차 리셋 */ PUBLIC inline void WhlResetEncErrSum(void) { m_fEncErrSumL = 0.0f; m_fEncErrSumR = 0.0f; // 2007.09.02 m_u16StsPrevEncL = EncGetCountLeftWheel(); m_u16StsPrevEncR = EncGetCountRightWheel(); } /* * @fn PUBLIC inline void WhlSetDirPwm(S16 s16CmpValL, S16 s16CmpValR) * @brief 좌우측 L298의 회전 방향 및 PWM 제어 * @param u16CmpValL [in] 좌측 바퀴의 PWM 비교값 * @param u16CmpValR [in] 우측 바퀴의 PWM 비교값 * @remark 부호는 회전 방향을 의미 (+: DIR_FORWARD, -: DIR_BACKWARD) */ PUBLIC inline void WhlSetDirPwm(S16 s16CmpValL, S16 s16CmpValR) { S08 s8CurrDirL = DIR_NON, s8CurrDirR = DIR_NON; // --------------------------------------------------------------- // 좌측 바퀴의 회전 방향 // 속도 명령이 양수인 경우 (전진) if (s16CmpValL > 0 ) { s8CurrDirL = DIR_FORWARD; } // 속도 명령이 음수인 경우 (후진) else if ( s16CmpValL < 0 ) { s8CurrDirL = DIR_BACKWARD; } else //정지 { s8CurrDirL = DIR_NON; } // 우측 바퀴의 회전 방향 // 속도 명령이 양수인 경우 (전진) if (s16CmpValR > 0 ) { s8CurrDirR = DIR_FORWARD; } // 속도 명령이 음수인 경우 (후진) else if ( s16CmpValR < 0 ) { s8CurrDirR = DIR_BACKWARD; } else //정지 { s8CurrDirR = DIR_NON; } // --------------------------------------------------------------- // PWM 비교값 설정 // 양수화하여 옵셋 더하기 s16CmpValL = abs(s16CmpValL);// + PWM_OFFSET; s16CmpValR = abs(s16CmpValR);// + PWM_OFFSET; // PWM 비교값의 상한값 제한 s16CmpValL = SAT(s16CmpValL, 0, MAX_PWM_DUTY); s16CmpValR = SAT(s16CmpValR, 0, MAX_PWM_DUTY); if (s8CurrDirL == DIR_FORWARD) { if (s8CurrDirR == DIR_FORWARD) { DevWheelForwardDifferential(s16CmpValL, s16CmpValR); } else if (s8CurrDirR == DIR_BACKWARD) { DevWheelTurnRightDifferential(s16CmpValL, s16CmpValR); } else { DevWheelTurnRightRound(s16CmpValL); } } else if (s8CurrDirL == DIR_BACKWARD) { if (s8CurrDirR == DIR_FORWARD) { DevWheelTurnLeftDifferential(s16CmpValL, s16CmpValR); } else if (s8CurrDirR == DIR_BACKWARD) { DevWheelBackwardDifferential(s16CmpValL, s16CmpValR); } else { DevWheelTurnLeftBackRound(s16CmpValL); } } else { if (s8CurrDirR == DIR_FORWARD) { DevWheelTurnLeftRound(s16CmpValR); } else if (s8CurrDirR == DIR_BACKWARD) { DevWheelTurnRightBackRound(s16CmpValR); } else { DevWheelStop(); WhlResetEncErrSum(); } } } PUBLIC inline void WhlProc(void) { FLOAT fPwmCmpValL = 0, fPwmCmpValR = 0; // PWM 비교값 FLOAT fEncErrL = 0.0f, fEncErrR = 0.0f; // 엔코더 카운트 오차 FLOAT fPwmDutyL = 0.0f, fPwmDutyR = 0.0f; // PWM duty (percentage) FLOAT fKp = 0.0f; // P 게인 // 새로운 지령 속도 설정시, 목표 속도 갱신 if (m_bNewCmd == TRUE ) { m_bNewCmd = FALSE; // 목표 속도 갱신 m_fTargVl = m_fDesiredVl; m_fTargVr = m_fDesiredVr; // 목표 속도에 준하는 엔코더 옵셋량 m_fTargEncL = VEL2PULSE(m_fTargVl); m_fTargEncR = VEL2PULSE(m_fTargVr); } // 엔코더 카운트 오차 fEncErrL = m_fTargEncL - (FLOAT)m_s16StsEncOffsetL; fEncErrR = m_fTargEncR - (FLOAT)m_s16StsEncOffsetR; // 엔코더 카운트의 누적 오차 m_fEncErrSumL += fEncErrL; m_fEncErrSumR += fEncErrR; // 엔코더 카운트의 누적 오차 상하한 제한 if (m_fEncErrSumL < -MAX_ERRSUM ) m_fEncErrSumL = -MAX_ERRSUM; if (m_fEncErrSumL > +MAX_ERRSUM ) m_fEncErrSumL = +MAX_ERRSUM; if (m_fEncErrSumR < -MAX_ERRSUM ) m_fEncErrSumR = -MAX_ERRSUM; if (m_fEncErrSumR > +MAX_ERRSUM ) m_fEncErrSumR = +MAX_ERRSUM; if (m_bMtnStop == TRUE) { // tmpPWMCount = 0; DevWheelStop(); WhlResetEncErrSum(); m_bMtnCtrl = FALSE; m_bMtnStop = FALSE; } // 바퀴 정속 제어를 할 경우 else if ( m_bMtnCtrl == TRUE ) { // 좌측 바퀴 - 저속인 경우 I 게인 제어 if (fabs(m_fTargVl) <= V_LSL ) { // tmpPWMCount = 1; fPwmCmpValL = (K_I * m_fEncErrSumL); } // 고속인 경우 PI 게인 제어 else if ( fabs(m_fTargVl) > V_USL ) { // tmpPWMCount = 3; fPwmCmpValL = (K_P * fEncErrL + K_I * m_fEncErrSumL); } // 중간 속도인 경우 가변 P, 고정 I 제어 else { // tmpPWMCount = 2; // V_LSL에서 fKp = 0, V_USL에서 fKp = K_P가 되도록 선형화 fKp = (K_P) * ( fabs(m_fTargVl) - V_LSL ); fKp /= (V_USL - V_LSL); fPwmCmpValL = (fKp * fEncErrL + K_I * m_fEncErrSumL); } // 우측 바퀴 - 저속인 경우 I 게인 제어 if (fabs(m_fTargVr) <= V_LSL ) { fPwmCmpValR = (K_I * m_fEncErrSumR); } // 고속인 경우 PI 게인 제어 else if ( fabs(m_fTargVr) > V_USL ) { fPwmCmpValR = (K_P * fEncErrR + K_I * m_fEncErrSumR); } // 중간 속도인 경우 가변 P, 고정 I 제어 else { // V_LSL에서 fKp = 0, V_USL에서 fKp = K_P가 되도록 선형화 fKp = (K_P) * ( fabs(m_fTargVr) - V_LSL ); fKp /= (V_USL - V_LSL); fPwmCmpValR = (fKp * fEncErrR + K_I * m_fEncErrSumR); } //2007.09.02 fPwmCmpValL = SAT(fPwmCmpValL, -MAX_PWM_DUTY, +MAX_PWM_DUTY); fPwmCmpValR = SAT(fPwmCmpValR, -MAX_PWM_DUTY, +MAX_PWM_DUTY); // tmpPWMLeft = fPwmCmpValL; // tmpPWMRight = fPwmCmpValR; // 회전 방향 및 PWM 비교값 설정 WhlSetDirPwm((S16)floor(fPwmCmpValL+0.5f), (S16)floor(fPwmCmpValR+0.5f)); ErrChkWheelLeft(fPwmCmpValL, m_s16StsEncOffsetL); ErrChkWheelRight(fPwmCmpValR, m_s16StsEncOffsetR); } // 바퀴 정속 제어를 하지 않을 경우 else { // tmpPWMCount = 4; WhlResetEncErrSum(); ErrChkWheelLeft(0, 0); ErrChkWheelRight(0, 0); } } PUBLIC void ErrChkWheelLeft(S16 s16PwmCmpValL, S16 s16EncOffsetL) { if (bIsNearlyDie || bOn.TestMode || gErrorMode != ERR_ID_NONE) return; #ifdef _ERROR_CHECK_WHEEL_ static S16 s16Count = 0; static S16 s16PwmCmpValMaxL = 0; static S16 s16EncOffset1sL = 0; static S16 s16ErrCntL = 0; // 1초 동안의 최대 PWM 비교값 s16PwmCmpValMaxL = MAX(abs(s16PwmCmpValMaxL), abs(s16PwmCmpValL)); // 1초 동안의 엔코더 이동량 s16EncOffset1sL += s16EncOffsetL; // 1초마다 검사 if (++s16Count < 20 ) return; // 지령 속도가 0인 경우, 에러 카운트 리셋 if (s16PwmCmpValL < 20 ) { s16PwmCmpValMaxL = 0; s16EncOffset1sL = 0; s16ErrCntL = 0; } // 좌측 바퀴 구속 감지 if (abs(s16PwmCmpValMaxL) > 95 ) // 95% { //if ( abs(s16EncOffset1sL) < 60 ) if (abs(s16EncOffset1sL) < 300 ) { // 일정 시간 이상 지속시 에러 처리 if (++s16ErrCntL >= 5 ) { s16ErrCntL = 0; SetErrMode(ERR_ID_LWHEEL); } } else { s16ErrCntL = 0; } } else { s16ErrCntL = 0; } // reset variables for the next time s16Count = 0; s16PwmCmpValMaxL = 0; s16EncOffset1sL = 0; #endif } PUBLIC void ErrChkWheelRight(S16 s16PwmCmpValR, S16 s16EncOffsetR) { if (bIsNearlyDie || bOn.TestMode || gErrorMode != ERR_ID_NONE) return; #ifdef _ERROR_CHECK_WHEEL_ static S16 s16Count = 0; static S16 s16PwmCmpValMaxR = 0; static S16 s16EncOffset1sR = 0; static S16 s16ErrCntR = 0; // 1초 동안의 최대 PWM 비교값 s16PwmCmpValMaxR = MAX(abs(s16PwmCmpValMaxR), abs(s16PwmCmpValR)); // 1초 동안의 엔코더 이동량 s16EncOffset1sR += s16EncOffsetR; // 1초마다 검사 if (++s16Count < 20 ) return; // 지령 속도가 0인 경우, 에러 카운트 리셋 if (s16PwmCmpValR < 20 ) { s16PwmCmpValMaxR = 0; s16EncOffset1sR = 0; s16ErrCntR = 0; } // 우측 바퀴 구속 감지 if (abs(s16PwmCmpValMaxR) > 95 ) { //if ( abs(s16EncOffset1sR) < 60 ) if (abs(s16EncOffset1sR) < 300 ) { if (++s16ErrCntR >= 5 ) { s16ErrCntR = 0; SetErrMode(ERR_ID_RWHEEL); } } else { s16ErrCntR = 0; } } else { s16ErrCntR = 0; } // reset variables for the next time s16Count = 0; s16PwmCmpValMaxR = 0; s16EncOffset1sR = 0; #endif } // 휠 제어 여부 PUBLIC BOOL m_bMtnCtrl = FALSE; // 휠 정지 여부 PUBLIC BOOL m_bMtnStop = FALSE; //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // 주행 관련 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // 엔코더 offset PUBLIC S16 m_s16StsEncOffsetL = 0; PUBLIC S16 m_s16StsEncOffsetR = 0; // 엔코더 카운트 옵셋 // 이전 엔코더 펄스 카운트 PUBLIC U16 m_u16StsPrevEncL = 0; PUBLIC U16 m_u16StsPrevEncR = 0; // current wheel velocity PUBLIC FLOAT m_fStsCurrLVel = 0.0f; /* current left wheel velocity */ PUBLIC FLOAT m_fStsCurrRVel = 0.0f; /* current right wheel velocity */ // current linear/angular velocity PUBLIC FLOAT m_fStsCurrLinVel = 0.0f; /* current linear velocity */ PUBLIC FLOAT m_fStsCurrAngVel = 0.0f; /* current angular velocity */ // Odometry, Gyro PUBLIC FLOAT m_fStsRobotX = 0.0f; PUBLIC FLOAT m_fStsRobotY = 0.0f; PUBLIC FLOAT m_fStsRobotQ = 0.0f; // This can be either odometry angle or gyro angle. PUBLIC FLOAT m_fStsDiffQ = 0.0f; PUBLIC FLOAT m_fStsDiffA = 0.0f; // Odometer PUBLIC FLOAT m_fStsOdometer = 0.0f; PUBLIC FLOAT m_fStsDiffOdo = 0.0f; PUBLIC void StsProc() { U16 u16CurrEncL = 0, u16CurrEncR = 0; // 현재 엔코더 카운트 FLOAT fDistWL, fDistWR; FLOAT fAngVelGyro = 0.0f; static FLOAT fOldQ = 0.0f; // 현재 엔코더 카운트 읽기 u16CurrEncL = EncGetCountLeftWheel(); u16CurrEncR = EncGetCountRightWheel(); // 엔코더 카운트 값 옵셋 계산 m_s16StsEncOffsetL = Math2CalEncOffset(m_u16StsPrevEncL, u16CurrEncL); m_s16StsEncOffsetR = Math2CalEncOffset(m_u16StsPrevEncR, u16CurrEncR); // 이전 엔코더 값 갱신 m_u16StsPrevEncL = u16CurrEncL; m_u16StsPrevEncR = u16CurrEncR; fDistWL = (float)(PULSE2MM(m_s16StsEncOffsetL)); fDistWR = (float)(PULSE2MM(m_s16StsEncOffsetR)); m_fStsDiffOdo = (fDistWL + fDistWR) / 2.0f; // 로봇 중심점의 이동 거리 fDistOffsetCP // Sts에 현재 속도 정보 저장 m_fStsCurrLVel = fDistWL/ WHEEL_CTRL_PERIOD; m_fStsCurrRVel = fDistWR/ WHEEL_CTRL_PERIOD; Math2WheelVel2LinAngVel(m_fStsCurrLVel, m_fStsCurrRVel, &m_fStsCurrLinVel, &m_fStsCurrAngVel); // 로봇 위치 계산 m_fStsOdometer = m_fStsOdometer + m_fStsDiffOdo; m_fStsRobotQ = -GyroGetRadAngle(); m_fStsRobotX += m_fStsDiffOdo * cosf(m_fStsRobotQ); m_fStsRobotY += m_fStsDiffOdo * sinf(m_fStsRobotQ); // 회전Goniometer m_fStsDiffA = (fDistWR - fDistWL) / WHEEL_BASE; m_fStsDiffQ = m_fStsRobotQ - fOldQ; Math2NormalizeOri(&m_fStsDiffQ); fAngVelGyro = m_fStsDiffQ/WHEEL_CTRL_PERIOD; fOldQ = m_fStsRobotQ; }