Raspberry Pi로 원격 조종 자동차 만들기 (with MQTT)

GAON PARK·2023년 11월 23일
0
post-custom-banner

RC Car키트를 조립한 뒤 기본 코드를 작성했다. 원래 QT로 만든 UI에서 커맨드를 입력받아 명령을 수행하는 코드를 짰었는데, 여러가지 센서를 사용해보면 좋을 것 같아서 UI 를 과감히 제거(!)하고 "에어 컨트롤러"를 만들기로 했다.

오늘은 RC Car 프로젝트에서 라즈베리 파이와 관련된 기본적인 내용을 정리한다.

Motor

Motor HAT에 DC 모터와 Servo 모터를 연결한다. DC모터는 뒷바퀴에 연결해서 앞뒤로 차량이 오고 갈 수 있게 하고, Servo 모터는 앞바퀴에 연결해서 좌회전, 우회전이 가능하도록 한다.

Pi I2C 활성화

라즈베리파이에서 I2C 통신을 사용하기 위해 설정해준다.

# raspi-config 설정
sudo apt-get update
sudo raspi-config

# 3. Interface Options 선택
# I5 I2C 를 Enable 로 설정
# raspi-config 를 닫고

# I2C 유틸리티 설치
sudo apt-get install python3-smbus i2c-tools

# 연결된 I2C 디바이스를 검색하면
sudo i2cdetect -y 1

# 모터햇의 i2c 주소를 알 수 있다. 

모터 샘플 코드 설치

DC 모터와 Servo 모터의 테스트가 가능한 코드가 포함되어 있다.

wget http://wiki.geekworm.com/images/a/ac/Raspi-MotorHAT-python3.zip

환경 설정

라즈베리파이 자체를 브로커 서버로 사용할 경우 MQTT로 통신하기 위해 개발 환경을 설정한다.

# mqtt 설치
sudo apt install mosquitto -y

# 프로세스 확인 명령어로 확인하면, 설치하자마자 중계기가 실행중임을 알 수 있다
ps -ef | grep mosquitto

# borker server test 를 위해 app을 설치해서 테스트 해본다
sudo apt install mosquitto-clients –y
# 터미널 두 개로 확인 
# mosquitto_sub –t “ondol”
# mosquitto_pub –t “ondol” –m “HI”
# => 잘 나오면 설치 성공

# 인바운드 규칙 설정
sudo vi /etc/mosquitto/mosquitto.conf

# 파일 가장 아래에 다음 내용을 추가하고 저장한다
port 1883 
allow_anonymous true

# config 설정대로 MQTT broker 서버를 재실행한다
cd /etc/mosquitto
sudo /etc/init.d/mosquitto stop
sudo mosquitto –c mosquitto.conf –v

파이썬에서 mqtt를 사용하기 위해 관련 라이브러리를 설치한다.

sudo pip3 install paho-mqtt

RC Car Base Code

  • 여러가지 기능을 수행해야 하기에 thread로 구현했다.
  • 기본 스피드를 100으로 했다 (최고 속력은 255 정도 되는데 엄청 빠르다!)
  • 브로커 서버에 주소를 쓰는데, 이 프로그램을 실행할 라즈베리파이가 직접 브로커 서버가 될 수도 있다. 그런 경우에 broker_address 는 localhost (127.0.0.1) 로도 충분하다.
  • go, back, stop, left_min, left_max, right_min, right_max, mid 동작에 대해 각각을 메서드로 따로 구현했다.
  • left, right, mid 는 총 다섯개의 상태를 갖는다. 이건 mqtt 송신하는 쪽에서 범위로 값을 읽어 알아서 보내줄 예정.
    - 왼쪽으로 살짝 꺾기
    - 왼쪽으로 많이 꺾기
    - 오른쪽으로 살짝 꺾기
    - 오른쪽으로 많이 꺾기
    - 중간 상태 유지하기

cmd_thread.py

from Raspi_MotorHAT import Raspi_MotorHAT, Raspi_DCMotor  
from Raspi_PWM_Servo_Driver import PWM  
import threading  
import paho.mqtt.client as mqtt  
import socket  
  
  
class CmdThread(threading.Thread):  
    BROKER_ADDRESS = socket.gethostbyname(socket.gethostname())  
    speed_idx = 0  
    speed = [100, 150, 200, 255]  
    front_or_back = ""  # 현재 직진 중인가? 후진 중인가?  
  
    def __init__(self):  
        super().__init__()  
        self.client = mqtt.Client("cmd_sub")  
        self.client.connect(self.BROKER_ADDRESS)  
        self.client.subscribe("command")  
        self.client.on_message = self.on_command  
  
        self.mh = Raspi_MotorHAT(addr=0x6f)  
        self.myMotor = self.mh.getMotor(2)  
        self.pwm = PWM(0x6f)  
        self.pwm.setPWMFreq(60)  
  
    def on_command(self, client, userdata, message):  
        cmd = str(message.payload.decode("utf-8"))  
        if "go" == cmd:  
            self.front_or_back = "front"  
            self.go()  
        elif "back" == cmd:  
            self.front_or_back = "back"  
            self.back()  
        elif "stop" == cmd:  
            self.front_or_back = ""  
            self.stop()  
        elif "left_max" == cmd:  
            self.left_max()  
        elif "left_min" == cmd:  
            self.left_min()  
        elif "right_max" == cmd:  
            self.right_max()  
        elif "right_min" == cmd:  
            self.right_min()  
        elif "mid" == cmd:  
            self.mid()  
        elif "speed" in cmd:  
            idx = int(cmd.split("=")[1])  
            if idx <= 2:  
                self.speed_idx = 0  
            elif idx <= 4:  
                self.speed_idx = 1  
            elif idx <= 6:  
                self.speed_idx = 2  
            else:  
                self.speed_idx = 3  
            self.speed_changed()  
  
    def speed_changed(self):  
        if self.front_or_back == "front":  
            self.go()  
        elif self.front_or_back == "back":  
            self.back()  
  
    def run(self):  
        self.client.loop_forever()  
  
    def go(self):  
        self.myMotor.setSpeed(self.speed[self.speed_idx])  
        self.myMotor.run(Raspi_MotorHAT.BACKWARD)  
  
    def back(self):  
        self.myMotor.setSpeed(self.speed[self.speed_idx])  
        self.myMotor.run(Raspi_MotorHAT.FORWARD)  
  
    def stop(self):  
        self.myMotor.setSpeed(self.speed[self.speed_idx])  
        self.myMotor.run(Raspi_MotorHAT.RELEASE)  
  
    def left_max(self):  
        self.pwm.setPWM(0, 0, 300)  
  
    def left_min(self):  
        self.pwm.setPWM(0, 0, 340)  
  
    def mid(self):  
        self.pwm.setPWM(0, 0, 375)  
  
    def right_max(self):  
        self.pwm.setPWM(0, 0, 450)  
  
    def right_min(self):  
        self.pwm.setPWM(0, 0, 410)

main app.py

import cmd_thread


if __name__ == '__main__':
    cmd_th = cmd_thread.CmdThread()
    cmd_th.start()

그럼 이제 ESP32에 MQTT 통신을 하는 펌웨어를 만들어 자동차를 조종해보자

post-custom-banner

0개의 댓글