본문 바로가기

카테고리 없음

Raspberry Pi 5로 서보 모터 제어하기 (with PCA9685)

우분투 터미널 창을 열고 다음 코드를 입력해서 PCA9685 라이브러리 설치한다.

 

sudo pip install adafruit-circuitpython-pca9685 --break-system-packages
sudo pip install adafruit-circuitpython-servokit --break-system-packages

 

import time  # Import time module to introduce delays in the program
import busio  # Import busio module to set up I2C communication
from adafruit_motor import servo  # Import servo module to control the servo motor
from adafruit_pca9685 import PCA9685  # Import PCA9685 module to interface with the PCA9685 PWM controller
from board import SCL, SDA  # Import specific pins SCL and SDA for I2C communication
# Initialize I2C communication on the Raspberry Pi using the SCL and SDA pins.
# I2C 통신 활성화, 핀 설정 코드
i2c = busio.I2C(SCL, SDA)


# Create an instance of the PCA9685 class using the I2C communication.
# This instance will be used to control PWM signals, which are typically used for controlling servos.
# PCA9685 보드 I2C 주소 설정 코드 (변경방법은 아래 설명 참고)
pca = PCA9685(i2c)

# Set the PWM frequency to 50Hz, which is the standard frequency for controlling servo motors.
pca.frequency = 50

# Create a ContinuousServo object on channel 0 of the PCA9685.
# A continuous servo can rotate indefinitely in either direction based on the throttle value.
# PCA9685 보드에서 몇번째 핀에 연결된 서보모터를 제어할 것인지.
# 0 ~ 15 번까지 있다.
servo0 = servo.ContinuousServo(pca.channels[0])

# Set the throttle to full speed forward (1.0).
# Throttle value can range from -1.0 (full speed reverse) to 1.0 (full speed forward).
servo0.throttle = 1.0

# Keep the servo running at full speed for 3 seconds.
time.sleep(3)

# Stop the servo by setting the throttle to 0.0 (no movement).
servo0.throttle = 0.0

# Briefly sleep for 0.1 seconds to ensure the stop command is fully processed.
time.sleep(0.1)

# Deinitialize the PCA9685 to free up resources and reset the hardware.
# PCA9685 동작 종료 코드
pca.deinit()

 

PCA9685의 I2C 주소를 변경하고자 한다면 다음과 같이 변경한다.

pca = PCA9685(i2c) 
# 이 원본 코드를 아래와 같이 수정한다.

pca = PCA9685(i2c, address=0x41)
#address = 에 이어서 보드에 설정한 값을 입력하면 된다.

 

다수의 서보모터를 사용할 경우 다음과 같이 코드를 변경한다.

servo0 = servo.ContinuousServo(pca.channels[0])
# 0번핀 (첫번째 핀)에 연결된 서보모터 0.

servo1 = servo.ContinuousServo(pca.channels[1])
# 1번핀 (두번째 핀)에 연결된 서보모터 1.
servo0 = servo.ContinuousServo(pca.channels[2])
# 2번핀 (세번째 핀)에 연결된 서보모터 2.

# ....

servo15 = servo.ContinuousServo(pca.channels[15])
# 15번핀 (열다섯번째 핀)에 연결된 서보모터 15.

# PCA9685에서 사용가능한 서보모터는 총 16개로 0번부터 15번까지 있다.

 

 

docs-circuitpython-org-pca9685-en-3.3.7.pdf
0.15MB

PCA 9685 메뉴얼

 

사용할 서보모터 규격.

 

 

20250302

 

서보모터를 각도로 제어하기 위해서 angle 함수를 사용하였다.

 

 

import time
import busio 
from adafruit_motor import servo  
from adafruit_pca9685 import PCA9685 
from board import SCL, SDA  

i2c = busio.I2C(SCL, SDA)



pca = PCA9685(i2c)


pca.frequency = 50

servo0 = servo.Servo(pca.channels[15])
servo0.set_pulse_width_range(330,2450)
servo0.actuation_range = 180

servo0.angle = 0
time.sleep(3)
servo0.angle = 90
time.sleep(3)
servo0.angle = 180
time.sleep(3)


pca.deinit()

 

서보모터를 anlge 함수로 제어하기 위해서는 서보모터의 최소 펄스와 최대 펄스를 설정해주어야 한다.
실험결과 내가 구입한 서보모터는 최소펄스 330일때 0도. 최대펄스 2450일때 180도로 움직인다.

actuation_range로 최대 각도를 변경해주었다. (기본값은 135도)

 

-----------

import time
import busio 
from adafruit_motor import servo  
from adafruit_pca9685 import PCA9685 
from board import SCL, SDA  


import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
 


i2c = busio.I2C(SCL, SDA)
pca = PCA9685(i2c)
pca.frequency = 50

ads = ADS.ADS1115(address=0x48, i2c=i2c)
channel = AnalogIn(ads, ADS.P0)


angleList = []





servo0 = servo.Servo(pca.channels[15])
servo0.set_pulse_width_range(330,2450)
servo0.actuation_range = 180

for deg in range(180) :
    print(deg)
    servo0.angle = deg
    
    # 저장하고 싶은 타입을 사용
    # angleList.append(channel.voltage)
    # angleList.append(channel.value)
    
    
    time.sleep(0.5)
print(angleList)
pca.deinit()

 0 ~180도까지의 서보모터 저항값 리스트 생성 코드

 

-----------------------

가변저항의 전압값을 측정하여 리스트를 작성하였으나 실제로 동작시켰을때 정확하게 일치하는 경우는 적었다.

이에 측정된 전압값이 리스트의 값중에 가장 근접한 값의 인덱스를 반환하는 함수가 필요했다.

(아두이노에서는 map 함수를 사용하면 되서 편리해보였다.)

def findNearNum(exList, values):
    answer = [0 for _ in range(2)] # answer 리스트 0으로 초기화

    minValue = min(exList, key=lambda x:abs(x-values))
    minIndex = exList.index(minValue)
    answer[0] = minIndex
    answer[1] = minValue
    
    return answer

exList = [4, 10, 8, 32, 95]
values = 20.4213

returnList = findNearNum(exList, values)
print(returnList)

 

 

출처 : https://blog.naver.com/zzang9ha/222010163074

 

 

------

20도씩 회전시키면서 전압값으로 계산된 각도값과 비교하는 프로그램

import time
import busio 
from adafruit_motor import servo  
from adafruit_pca9685 import PCA9685 
from board import SCL, SDA  


import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
 

i2c = busio.I2C(SCL, SDA)
pca = PCA9685(i2c)
pca.frequency = 50

ads = ADS.ADS1115(address=0x48, i2c=i2c)
channel = AnalogIn(ads, ADS.P0)

servo0 = servo.Servo(pca.channels[15])
servo0.set_pulse_width_range(330,2450)
servo0.actuation_range = 180

angleList = [21416, 21416, 21417, 21493, 21446, 21370, 21269, 21205, 21104, 21038, 20974, 20852, 20793, 20736, 20628, 20569, 20521, 20409, 20354, 20285, 20173, 20105, 19980, 19938, 19879, 19745, 19747, 19643, 19541, 19479, 19396, 19310, 19227, 19168, 19069, 18998, 18890, 18852, 18796, 18666, 18623, 18552, 18437, 18378, 18351, 18205, 18176, 18086, 17983, 17931, 17814, 17752, 17697, 17570, 17513, 17451, 17349, 17292, 17238, 17129, 17066, 17002, 16880, 16811, 16719, 16668, 16593, 16495, 16441, 16350, 16261, 16189, 16120, 16004, 15943, 15870, 15771, 15778, 15623, 15576, 15481, 15382, 15329, 15261, 15153, 15089, 15019, 14937, 14863, 14800, 14697, 14627, 14512, 14446, 14391, 14257, 14259, 14152, 14061, 13991, 13930, 13839, 13757, 13716, 13594, 13522, 13429, 13376, 13292, 13199, 13113, 13072, 12961, 12902, 12796, 12702, 12645, 12576, 12469, 12423, 12342, 12266, 12170, 12087, 12026, 11945, 11830, 11793, 11720, 11621, 11554, 11494, 11380, 11322, 11254, 11154, 11082, 10964, 10973, 10844, 10757, 10686, 10636, 10528, 10463, 10398, 10291, 10221, 10178, 10054, 10005, 9893, 9834, 9763, 9663, 9591, 9516, 9438, 9359, 9296, 9194, 9120, 9073, 8973, 8901, 8811, 8745, 8660, 8580, 8508, 8454, 8348, 8297, 8206, 8118, 8056, 7970, 7880, 7800, 7709]

def findNearNum(exList, values):
    answer = [0 for _ in range(2)] # answer 리스트 0으로 초기화

    minValue = min(exList, key=lambda x:abs(x-values))
    minIndex = exList.index(minValue)
    answer = minIndex
    
    return answer
servo0.angle = 0
time.sleep(5)
for i in range(0,200,20) :
    servo0.angle = i
    time.sleep(1)
    print(channel.value)
    print(findNearNum(angleList,channel.value))

 

 

20250305

서보모터를 손으로 동작시키기 위해서는 모터의 홀딩이 풀려야 되는데 모르겠다... 어떻게 해야 힘을 뺄수 있을까..