BASIC4MCU | 질문게시판 | ATmegq128 PI 제어기 추가
페이지 정보
작성자 바밤 작성일2024-10-08 16:30 조회115회 댓글1건본문
PI 제어기로 일정한 속도 11 rad/s로 유지하는 코드를 짜는 중입니다
모터에 바퀴를 달거나 달지 않았을 때도 일정한 속도로 유지할 수 있게 소스코드를 짜봤는데
어디서 문제인지 잘 모르겠어요. 하이퍼터미널로 각속도를 재보면 80rad/s에서 유지하는데 속도 낮추는 법을 잘 모르겠어요
코드는 상관 없고 Kp, Ki를 계속 튜닝해야 하나요?
아님 코드가 잘못된 건가요..?
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
volatile int num = 0; // 펄스 수
volatile unsigned int angular_velocity = 0; // 각속도
int target_velocity = 11; // 목표 각속도 11 rad/s
volatile float We = 0; // 각속도 오차
volatile float We_int = 0; // 적분 오차
volatile float u = 0; // 제어 신호 (PWM 듀티 사이클)
volatile float temp = 0; // 이전 적분 값
float Kp = 1.0; // 비례 이득
float Ki = 0.5; // 적분 이득
volatile uint16_t pressure_value = 0; // 압력 센서 값
// 적분 오차 제한 값
float We_int_max = 100.0; // 적분 오차 최대값
float We_int_min = -100.0; // 적분 오차 최소값
// 포트 초기화
void port_Init() {
DDRA = 0xFF; // 포트 A를 출력으로 설정
DDRB = 0xFF; // 포트 B를 출력으로 설정 (PWM 신호 출력)
DDRD = 0x00; // 포트 D를 입력으로 설정 (인터럽트 입력) encoder 신호
DDRC = 0xFF; // 포트 C를 출력으로 설정 (모터 제어용)
PORTA = 0x01; // 포트 A 초기화
}
// 인터럽트 초기화
void int_Init() {
EICRA = 0x03; // INT0 인터럽트를 상승 엣지에서 트리거
EIMSK = 0x01; // INT0 인터럽트 활성화
}
// 타이머 1 초기화 (PWM 설정)
void timer1_Init() {
TCCR1A = 0x81; // 고속 PWM 모드, 비반전 모드
TCCR1B = 0x03; // 분주비 64 (PWM 신호 적절한 주파수로 설정)
TCNT1 = 0; // 타이머1 카운터 초기화
}
// 타이머 3 초기화 (1초 간격)
void timer3_Init() {
TCCR3A = 0x00; // 비교 매치 모드 비활성화 (Normal 모드)
TCCR3B = 0x05; // 분주비 1024
TCNT3 = 49911; // 1초 후 오버플로우 발생하도록 설정
ETIMSK = 0x04; // 타이머 3 오버플로우 인터럽트 활성화
}
// UART 초기화
void usart_Init() {
UBRR0H = 0; // 상위 바이트
UBRR0L = 103; // 하위 바이트
UCSR0B = 0x18; // 송신 가능, 수신 가능 설정
UCSR0C = 0x06; // 데이터 비트 8비트, 패리티 없음, 1 스톱 비트 설정
}
// UART로 송신 함수
void usart_Transmit(unsigned char data) {
while (!(UCSR0A & (1 << UDRE0))); // 송신 버퍼가 비어질 때까지 대기
UDR0 = data; // 데이터를 버퍼에 저장
}
// 문자열을 UART로 전송
void usart_Print(char* str) {
while (*str) {
usart_Transmit(*str++);
}
}
// 외부 인터럽트 0 서비스 루틴
ISR(INT0_vect) {
num++; // 펄스 수 증가
if (num == 1) {
timer3_Init(); // 첫 번째 펄스가 들어오면 타이머 3 시작
}
}
// 타이머 3 오버플로우 인터럽트 서비스 루틴
ISR(TIMER3_OVF_vect) {
char buffer[100];
// 각속도 계산 (7.48은 펄스 수를 rad/s로 변환하기 위한 상수)
angular_velocity = (7.48 * num);
// PI 제어기 적용
We = target_velocity - angular_velocity; // 각속도 오차 계산
We_int = temp + We; // 적분 오차 계산
// 적분 오차 제한
if (We_int > We_int_max) {
We_int = We_int_max;
} else if (We_int < We_int_min) {
We_int = We_int_min;
}
u = Kp * We + Ki * We_int; // 제어 신호 계산
temp = We_int; // 적분 값 업데이트
// 제어 신호에 따라 PWM 듀티 사이클 업데이트 (최대/최소값 제한)
if (u > 255) {
u = 255;
} else if (u < 0) {
u = 0;
}
OCR1A = (uint16_t)u; // 제어 신호를 PWM 값으로 변환
// 각속도 및 PWM 출력 확인용 메시지
snprintf(buffer, sizeof(buffer), "angular_velocity: %d.%d%d rad/s, PWM Output: %d\r\n",
angular_velocity / 100, angular_velocity % 100 / 10, angular_velocity % 10, (uint16_t)u);
usart_Print(buffer);
// 펄스 수 초기화 및 타이머 리셋
num = 0;
TCNT3 = 49911;
}
// 메인 함수
int main(void) {
port_Init(); // 포트 초기화
int_Init(); // 인터럽트 초기화
timer1_Init(); // 타이머 1 초기화
usart_Init(); // UART 초기화
sei(); // 글로벌 인터럽트 활성화
while (1) {
// 메인 루프에서 PWM 신호가 정상적으로 출력되는지 확인 가능
}
return 0;
}
댓글 1
조회수 115master님의 댓글
master 작성일
시리얼 통신에 많은 데이터를 전송하는 것 같은데요
9600bps는 1바이트에 약 1ms가 걸립니다.
이 시간을 줄이는 것이 좋습니다.
115200bps로 늘리세요