프로젝트 소개
2023년 2학기부터 2024년 1학기까지 진행한 2024-1 캡스톤디자인(CDP종합설계) 프로젝트이다.
2인 1팀으로 진행되며 3학년 2학기때는 설계 및 기초 작업들을 하고 4학년 1학기부터 본격적으로 구현 및 시연 발표를 한다.
3학년 2학기에 프로젝트를 설계할 때 PLC를 사용하여 부화장의 모니터링 시스템을 만들려고 초기 구현까지 한 상태였다.
최근 이슈들로 인해 이차전지와 전기차에 대한 많은 관심이 쏠리면서 내 전공을 배터리와 결합시키면 재미있을 것 같아서 4학년 학기 초에 교수님과 상의 끝에 주제를 바꾸게 되었다.
정보통신공학과 학생으로서 배터리와 관련된 주제는 전공과 달라서 도전적이었지만 어려운 주제인 만큼 얻어가는 것도 클 것이라 생각하고 진행하였다. 전기차와 이차전지에 대해서도 공부를 할 겸 도전해 보았다.
프로젝트 내용
목표
본 프로젝트는 라즈베리파이와 다양한 센서를 이용하여 전기차 배터리의 온도를 관리하고 충전 상태를 제어하는 시스템을 설계하고 구현하였다. 이를 통해 배터리의 성능을 향상하고 수명을 연장하며, 사용자의 안전을 보장하고자 한다.
주요 단계 및 일정
구성요소
핵심 모듈 | 모듈 설명 |
배터리 셀 및 충방전 회로 | 18650 리튬이온배터리 셀 3개 충전회로 : Photocoupler, Mosfet, 쇼트키 다이오드, LC 평활회로 방전회로 : ULN2003, LED, SPDT Relay, 승압 부스터, 시멘트저항(2.75ohm) 온도센서(ds18b20), 전류/전압센서(INA219), I2C 멀티플렉서(TCA9548A), PWM 팬, 12v SMPS 등으로 구성된 하드웨어 |
Raspberry Pi | Raspberry Pi에서 multi-thread를 사용하여 실시간으로 전류, 전압, 온도 센서값을 수집하고 수집한 데이터로 Soc연산, 온도에 따라 PWM을 사용한 충전속도 조절, 온도에 따른 FAN속도 조절, 양방향(full-duplex) 소켓 통신을 사용하여 서버로 가공한 데이터를 송신하고 Server로 부터 SPDT Relay제어 명령을 수신 |
Server(Node.js, MySQL) | Raspberry Pi에서 데이터를 받고 DB에 저장, 실시간으로 센서값을 Client로 전송, Client로 부터 SPDT Relay제어 명령을 수신 |
Client(React) | 사용자가 Server로 부터 데이터를 받아서 실시간으로 하드웨어 전체 상태를 모니터링하고 DB에 저장된 데이터를 조회 |
기능 블록도
하드웨어 전체
하드웨어는 배터리, 충전회로, 방전회로, SMPS, 라즈베리파이로 구성되어 있다.
배터리
18650 리튬이온배터리 사진 및 스펙이다.
충전회로
충전회로 : Photocoupler, Mosfet, 쇼트키 다이오드, LC 평활회로, 저항, 방열판 등으로 구성되어있다.
시중에 팔고 있는 아두이노 라즈베리파이 서보모터제어 MOSFET모듈은 +를 공통으로 사용하고 -로 출력 조절을 하는 방식이라서 회로를 구성할 수가 없었다.
그래서 직접 N-MOSFET의 Gate에 PWM신호를 넣고 Drain에 12V, Source에 출력을 연결하여 회로를 설계하였다.
처음 테스트를 할 때는 LC필터 부분이 없이 MOSFET 출력을 배터리에 연결하여 충전을 해보았으나 PWM파형이 깨지고 역으로 배터리에서 SMPS 쪽으로 역전류가 흐르는 것을 확인하였다.
왜 그런지 찾아보니 배터리에 필터링 회로를 사용하여 DC전압으로 변환하는 것이 필요하다고 해서 LC필터를 회로에 추가하여 배터리로 들어가는 전압을 안정화시켰고 역전류가 흐르지 못하도록 쇼트키 다이오드를 추가하였다.
일반 다이오드가 아닌 쇼트키 다이오드를 사용한 이유는 PWM제어가 동작 속도가 빠르기 때문이다. 그리고 과전압 또는 역전압 상황에서 소자를 보호하기 위해 사용하였다.
포토커플러는 동작 전원이 다른 두 회로를 사용하기 때문에 전원이 다른 두 회로를 완전히 분리시키기 위해 주로 사용하는데 나는 12V SMPS와 라즈베리파이의 5V 출력의 전압이 달라 절연시키고 노이즈에도 강하기 때문에 사용하였다.
방전회로
방전회로 : ULN2003(Transistor Array), LED, SPDT Relay, 승압 부스터, 시멘트저항(2.75 ohm)
12V의 SMPS의 전압을 컨버터를 통해 24V로 승압시켜서 ULN2003으로 들어가게 되고 라즈베리파이에서 GPIO로 HIGH, LOW 출력을 내서 ULN2003(Transistor Array)의 입력에 들어간다.(ULN2003은 24V의 릴레이를 작동시키기 위해 사용)
릴레이가 켜지면 방전모드 ON이라는 의미로 LED에 불이 들어온다.
그리고 릴레이가 충전회로에서 떨어지고 방전회로에 붙게 되어 방전저항 쪽으로 전류가 흐르게 되며 배터리를 시멘트 저항을 통해 방전하게 된다.
라즈베리파이
SDA, SCL : 전류센서 3개를 확장보드를 사용하여 연결
GPCLK0 : 온도센서 6개 병렬 연결(ds18b20 온도센서의 통신방식은 데이터 전용선 1개로 송/수신을 모두 수행하는 1 Wire로 작동)
GPIO 17, 27, 22 : 클라이언트에서 SPDT Relay를 제어하기 위해 SPDT Relay에 연결
라즈베리파이에는 하드웨어 PWM이 2개밖에 없어서 배터리를 2개만 사용하여 프로젝트를 진행하려고 했으나 3개를 사용하라고 하셔서 어쩔 수 없이 소프트웨어 PWM으로 통일하여 충전을 하였다.
라즈베리파이 주요 알고리즘(수도코드)
python으로 초기에 테스트 프로그램을 만들어서 센서들을 테스트하였고 최종 프로그램은 C++로 작성하였다.
1. 배터리 충전 상태 제어
function control_charging(sensor, temperature[], bat_data[])
tca_fd = setup I2C with TCA_ADDR
duty_cycle1, duty_cycle2, duty_cycle3 = 0, 0, 0
counter = 0
current1, voltage1, current2, voltage2, current3, voltage3 = [], [], [], [], [], []
SoC_array_1, SoC_array_2, SoC_array_3 = [], [], []
soc_1, soc_2, soc_3 = 0, 0, 0
loop forever
select TCA9548A channel 5
charge_mode = [STOP_CHARGING, STOP_CHARGING, STOP_CHARGING]
count = 0
if relay_state[RELAY_PIN1] == 1 then
charge_mode[0] = STOP_CHARGING
duty_cycle1 = 0
set PWM on BATTERY1_PWM_PIN to duty_cycle1
shuntVoltage = sensor.readShuntVoltage()
current1.append(sensor.readCurrent())
if len(current1) > 10 then current1.pop(0)
avg_current = sum([amph for amph in current1 if abs(amph) >= duty_cycle1]) / len(current1)
sleep for 100 milliseconds
voltage1.append(sensor.readBusVoltage())
if len(voltage1) > 10 then voltage1.pop(0)
avg_voltage = sum([volt for volt in voltage1 if volt >= 0.1]) / len(voltage1)
SoC_array_1.append(calculate_SoC(avg_voltage, relay_state[RELAY_PIN1]))
if len(SoC_array_1) > 5 then SoC_array_1.pop(0)
soc_1 = sum(SoC_array_1) / len(SoC_array_1)
print "Battery(1) Voltage:", avg_voltage, "V, Current:", avg_current, "mA, SoC:", soc_1, "%, Duty Cycle:", duty_cycle1, "%"
bat_data = [avg_voltage, avg_current, soc_1, duty_cycle1]
else
if temperature[0] > MAX_CRITICAL_TEMPERATURE or soc_1 == 100 then
charge_mode[0], duty_cycle1 = STOP_CHARGING, 0
else if temperature[0] > MAX_SAFE_TEMPERATURE then charge_mode[0] = STANDARD_CHARGING
else charge_mode[0] = FAST_CHARGING
bat_data[4] = charge_mode[0]
if charge_mode[0] != STOP_CHARGING then
target_current = 1000.0 if charge_mode[0] == FAST_CHARGING else 500.0
shuntVoltage = sensor.readShuntVoltage()
current1.append(sensor.readCurrent())
if len(current1) > 10 then current1.pop(0)
avg_current = sum([amph for amph in current1 if abs(amph) >= duty_cycle1]) / len(current1)
set PWM on BATTERY1_PWM_PIN to 0
sleep for 100 milliseconds
voltage1.append(sensor.readBusVoltage())
if len(voltage1) > 10 then voltage1.pop(0)
avg_voltage = sum([volt for volt in voltage1 if volt >= 0.1]) / len(voltage1)
SoC_array_1.append(calculate_SoC(avg_voltage, relay_state[RELAY_PIN1]))
if len(SoC_array_1) > 5 then SoC_array_1.pop(0)
soc_1 = sum(SoC_array_1) / len(SoC_array_1)
print "Battery(1) Voltage:", avg_voltage, "V, Current:", avg_current, "mA, SoC:", soc_1, "%, Duty Cycle:", duty_cycle1, "%"
bat_data = [avg_voltage, avg_current, soc_1, duty_cycle1]
set PWM on BATTERY1_PWM_PIN to duty_cycle1
if avg_voltage < TARGET_VOLTAGE then
if avg_current < target_current then duty_cycle1 += 1 + counter
else duty_cycle1 = max(0, duty_cycle1 - 1 - counter)
else duty_cycle1 = max(0, duty_cycle1 - 1 + counter)
counter = max(0, counter - 1)
set PWM on BATTERY1_PWM_PIN to duty_cycle1
end loop
end function
control_charging() 함수는 TCA9548A 멀티플렉서(확장보드)에 연결된 INA219 전류센서를 사용하여 여러 배터리의 충전 상태를 제어한다. 이 함수는 각 배터리의 전압, 전류 및 SoC를 측정하고, 온도 및 Soc 값에 따라 충전 모드를 조정한다. 무한 루프 안에서 주기적으로 각 배터리의 상태를 확인하고 충전 조건에 따라 충전 방식을 변경한다.
함수 초반에 I2C설정을 초기화하고 각 배터리별 변수를 초기화한다. 그 후 채널을 설정해서 INA219를 선택하고 전압과 전류를 측정한다. 프로젝트에 사용한 센서의 품질과 라즈베리파이의 PWM 주파수의 한계로 전류와 전압값의 약간의 오차가 발생하여 가장 최근 10개의 센서값의 평균을 전류/전압 값으로 설정하였다.
그렇게 측정된 전압값을 기반으로 SoC를 추정(caculat_SoC() 함수에서 전압값을 기반으로 SoC를 mapping 하여 사용)하고 배터리 온도를 통해 충전 모드를 설정한 후 CC-CV방식으로 배터리를 충전한다. 이때 PWM의 duty cycle을 조정하여 CC-CV충전을 실행하였다.
2. 팬 속도 조절
function control_fan_speed(temperature[], fan_pwm[])
loop forever
lock mutex
temperature[0] = readTemperature(BAT1_TEMP_ADDR)
temperature[1] = readTemperature(BAT2_TEMP_ADDR)
temperature[2] = readTemperature(BAT3_TEMP_ADDR)
temperature[3] = readTemperature(RESISTER1_TEMP_ADDR)
temperature[4] = readTemperature(RESISTER2_TEMP_ADDR)
temperature[5] = readTemperature(RESISTER3_TEMP_ADDR)
unlock mutex
fan_speed = 0
// Handle battery 1
if temperature[0] <= 20.0 then
fan_speed = 0
else if temperature[0] >= 40.0 then
fan_speed = 100
else
fan_speed = (temperature[0] - 20.0) / 20.0 * 100
end if
set BATTERY1_FAN_PIN to fan_speed
fan_pwm[0] = fan_speed
print "battery-1 Temperature: ", temperature[0], "C, Fan Speed: ", fan_speed
// Handle battery 2
if temperature[1] <= 20.0 then
fan_speed = 0
else if temperature[1] >= 40.0 then
fan_speed = 100
else
fan_speed = (temperature[1] - 20.0) / 20.0 * 100
end if
set BATTERY2_FAN_PIN to fan_speed
fan_pwm[1] = fan_speed
print "battery-2 Temperature: ", temperature[1], "C, Fan Speed: ", fan_speed
// Handle battery 3
if temperature[2] <= 20.0 then
fan_speed = 0
else if temperature[2] >= 40.0 then
fan_speed = 100
else
fan_speed = (temperature[2] - 20.0) / 20.0 * 100
end if
set BATTERY3_FAN_PIN to fan_speed
fan_pwm[2] = fan_speed
print "battery-3 Temperature: ", temperature[2], "C, Fan Speed: ", fan_speed
// Determine maximum temperature for resistors
max_temp = temperature[3]
if temperature[4] > max_temp then
max_temp = temperature[4]
end if
if temperature[5] > max_temp then
max_temp = temperature[5]
end if
// Handle resistors
if max_temp <= 20.0 then
fan_speed = 0
else if max_temp >= 50.0 then
fan_speed = 100
else
fan_speed = (max_temp - 20.0) / 30.0 * 100
end if
set RESISTER_FAN_PIN to fan_speed
fan_pwm[3] = fan_speed
print "Discharge Resistor Max Temperature: ", max_temp, "C, Fan Speed: ", fan_speed
// sleep for 1000 milliseconds (commented out)
// std::this_thread::sleep_for(std::chrono::milliseconds(1000))
end loop
end function
control_fan_speed() 함수는 온도 배열과 팬 속도 배열을 인자로 받아, 온도에 따라 팬의 속도를 조절하는 역할을 한다. 이 함수는 무한 루프 안에서 주기적으로 각 배터리 및 저항의 온도를 읽어온 후, 해당 온도에 따라 팬의 속도를 계산하여 설정한다.
팬 속도는 온도와 선형적으로 비례하며, 각 배터리 및 저항의 온도가 특정 임계값을 초과하면 팬 속도를 최댓값으로 설정한다.
클라이언트
라즈베리파이로부터 Socket통신을 사용하여 실시간으로 배터리 셀별 데이터를 받아서 시각화한 페이지이다.
배터리 위에 있는 FAN은 라즈베리파이의 소프트웨어 PWM으로 제어를 하여 해당하는 퍼센트를 화면에 보여준다.
그리고 각 배터리 셀별 SoC, 온도, 전압, 전류를 보여주고 아래에 충전 / 방전 / 완충 상태를 보여준다.
배터리 밑에 있는 충전기 모양은 초록색일 때는 충전 중이고 검은색은 완충상태, 빨간색일 때는 방전 중인 상태임을 나타내고 사용자가 누르게 되면 충전과 방전을 제어할 수 있다.
각 배터리 셀별 실시간으로 전류, 전압, 온도 PWM 총 4가지 데이터를 그래프로 시각화 한 페이지이다.
라즈베리파이로부터 실시간으로 받은 데이터를 1초마다 Node.js서버를 통해 MySQL에 저장한다.
MySQL에 저장된 데이터를 조회할 배터리 번호와 조회 시작시간, 끝시간을 설정하고 버튼을 누르면 해당하는 기간의 배터리에 대한 모든 정보들이 나온다.
전압, 전류, SoC, 충전온도, 방전온도, 충전 pwm, 충전회로 Fan, 방전회로 Fan에 대한 데이터들을 한눈에 볼 수 있다.
결론
배터리 충전 그래프
테스트 자료와 프로젝트 데이터의 비교
테스트 자료와 비슷하게 나온 것을 확인할 수 있다.
빨간 선이 전압, 초록선은 전류, 파란 선은 SoC이다. 테스트 자료의 전압과 프로젝트 데이터의 전압곡선을 비교하면 매우 흡사하다. 그리고 전류도 출렁이긴 하지만 일정하게 1.0A로 유지되는 모습이다.
PWM 입력과 배터리 양단 전압의 관계
핑크선이 충전회로의 MOSFET로 들어가는 PWM 퍼센트이다. 배터리의 전압이 상승함에 따라 라즈베리파이의 소프트웨어 PWM의 평균도 올라간다는 것을 볼 수 있다.
하지만 매우 심하게 출렁거리는데 그 이유는 소프트웨어 PWM이라서 100hz만 지원하기 때문에 정밀하게 제어하지 못하였고 시간이 부족해서 PID제어 프로그램을 만들지 못하였기 때문이다.
그래도 쇼트키 다이오드를 거치면서 전압이 튀는 피크들을 잡아주고 LC필터를 거쳐서 배터리로 들어갔기 때문에 전압 곡선을 보면 약간의 튐은 있지만 비교적 잘 나온 것 같다.
온도와 Fan 속도 제어
초록선이 MOSFET의 온도이다. 기존에는 배터리에 온도센서를 부착하여 팬 속도를 조절하려고 했으나 배터리에 열이 거의 발생하지 않아서 충전회로 쪽 MOSFET의 온도를 측정하고 팬을 가동했다.
충전 중에 온도가 최대 37도까지 상승함을 확인하였다. 기존에 팬을 달기 전에 시험해 봤을 때는 70도까지 상승하였지만 팬을 달고 난 후 절반가량으로 낮아졌다.
그리고 온도가 상승함에 따라 팬의 PWM 출력도 상승함을 알 수 있다. 즉 온도에 따른 팬 속도 제어가 잘 되고 있는 것으로 확인된다.
느낀 점 및 후기
라즈베리파이의 소프트웨어 PWM이 100hz라서 정밀한 제어를 하지 못했지만 다음에는 하드웨어 PWM을 사용하여 더욱 정밀하게 제어하고 싶다. 그리고 PWM C++ 코드도 급하게 만들어서 이상적으로 제어가 되지 않지만 이 부분도 PID제어를 사용한다면 부드럽게 제어될 것으로 예상된다.
처음에는 전공시간에 배웠던 지식들을 바탕으로 설계부터 구현까지 하려니 막막하였다. 특히 하드웨어는 수업시간에 조금밖에 안 배워서 도면 그리기, 충방전 회로 설계 및 납땜하기, 전류 전압 계산하기, 등 전부다 직접 하다 보니 주말 밤낮없이 계속 진행하였다.
그 과정에서 전자회로와 반도체 소자들에 대한 지식과 배터리와 전기차에 대해서 자세히 알 수 있게 되었고 임베디드 개발자가 되려면 소프트웨어만 할 줄 아는 게 아니라 전자회로나 자동차 등의 하드웨어 쪽 지식도 있어야 한다는 것을 알게 되었다.
기간 압박 속에서 작업하느라 매우 힘들었지만, 완성 후 무사히 시연까지 마치고 좋은 평가를 받아서 굉장히 보람찬 경험이었다.
참고 문헌
[1] X. Li, W. Han, "Battery State of Charge Estimation Based on Kalman Filter," Journal of Power Sources, vol. 10, no. 2, pp. 123-130, 2020.
[2] Y. Kim, "Advanced Battery Management Techniques for Electric Vehicles," IEEE Transactions on Industrial Electronics, vol. 67, no. 8, pp. 7004-7013, 2019.
[3] Z. Chen, "Thermal Management in Electric Vehicles," Automotive Engineering, vol. 45, no. 3, pp. 205-212, 2021.
[4] M. Gupta, R. Kumar, "Thermoelectric Cooling for Battery Temperature Management," Energy Conversion and Management, vol. 123, pp. 453-465, 2019.
[5] Beomjin Yoon, Seougyeol Yoo, Sangman Seong, “Compensation Method of EKF Based on LSTM for Estimating State of Charge of Li-polymer Battery” Transactions of KSAE, Vol. 27, No. 7, pp.501-507 (July, 2019)
[6] ICR18650-22f Lithium-ion Rechargeable Cell Samsung semiconductor datasheet 2016-07-01 [online] http://www.samsung.com/Products/Semiconductor
[7] ICR18650-22f Lithium-ion Rechargeable Cell Samsung semiconductor lygte-info [online] https:// lygte-info.dk/review/batteries2012/Samsung%20ICR18650-22F%202200mAh%20%28Green%29%20UK.html