3.16. การควบคุมเซอร์โว

เซอร์โวงานอดิเรก (RC) คือมอเตอร์เกียร์ขนาดเล็กในกล่องปิดสนิทพร้อมการควบคุมตำแหน่งแบบวงปิดในตัว ภายในกล่องมี DC มอเตอร์ กล่องเกียร์ทด โพเทนชิโอมิเตอร์ต่อกับเพลาเอาต์พุต และบอร์ดไดรเวอร์ขนาดเล็กที่เปรียบเทียบค่าของโพเทนชิโอมิเตอร์กับ setpoint ที่ส่งเข้ามาจากภายนอก ไดรเวอร์เดินมอเตอร์ในทิศทางที่ลดค่าความผิดพลาด และหยุดเมื่อตำแหน่งตรงกัน จากฝั่งกล้องไม่มีสิ่งใดเหล่านี้มองเห็นได้ -- เพียงแค่บอกเซอร์โวว่าจะไปที่ไหน

3.16.1. สัญญาณ PWM

เซอร์โวรับ setpoint เป็นสัญญาณ PWM ที่อัตราเฟรมคงที่ 50 Hz โดย ความกว้างพัลส์ เลือกตำแหน่ง:

  • พัลส์ 1.0 ms ขับเพลาไปยังปลายด้านหนึ่งของการเดินทาง

  • พัลส์ 1.5 ms จอดเพลาที่ตำแหน่งกึ่งกลาง

  • พัลส์ 2.0 ms ขับเพลาไปยังปลายอีกด้านหนึ่ง

ค่าที่อยู่ระหว่างนั้นแมปกับตำแหน่งกลางๆ

Three rows of square-wave traces stacked vertically. Each row shows one 20 ms period of a 50 Hz PWM with a narrow high pulse at the start: 1.0 ms in the top row, 1.5 ms in the middle, 2.0 ms in the bottom.

เฟรม PWM ของเซอร์โวยาว 20 ms ความกว้างพัลส์ (1.0 -- 2.0 ms) เลือกตำแหน่ง

ต่างจาก LED และมอเตอร์ เซอร์โวไม่ได้ เฉลี่ย PWM ความกว้างพัลส์เองคือคำสั่ง ลอจิกภายในของเซอร์โววัดแต่ละพัลส์ กำหนดเป้าหมายตามนั้น และเดินมอเตอร์จนกว่าเอาต์พุตจะตรงกัน duty cycle เป็นเศษส่วน (ระหว่าง 5 % ถึง 10 % ตลอดช่วงทั้งหมด) เป็นเรื่องบังเอิญ -- ความกว้างพัลส์แบบสัมบูรณ์คือสิ่งที่สำคัญ ซึ่งเป็นสิ่งที่ซอฟต์แวร์ต้องควบคุม

3.16.2. การต่อวงจร

เซอร์โวงานอดิเรกใช้ขั้วต่อสามสาย:

  • Power (มักเป็นสีแดง): แหล่งจ่ายของเซอร์โวเอง โดยทั่วไป 4.8 V ถึง 6 V อย่าจ่ายไฟเซอร์โวจากราว 3.3 V ของกล้อง -- มันไม่สามารถจ่ายกระแส stall ได้ และราวจะตกแรงดัน

  • Ground (มักเป็นสีดำหรือน้ำตาล): เส้นทางกลับสำหรับไฟของเซอร์โว ต่อกับกราวด์ของกล้องเพื่อให้สัญญาณมีอ้างอิงร่วมกัน

  • Signal (มักเป็นสีขาว เหลือง หรือส้ม): สาย PWM จาก GPIO ของกล้อง

3.16.3. โค้ด

duty_u16() ใช้งานได้ แต่กำหนด duty เป็นเศษส่วนของรอบ -- ไม่สะดวกสำหรับสัญญาณที่ความกว้างพัลส์แบบสัมบูรณ์คือสิ่งสำคัญและรอบคงที่ duty_ns() กำหนดความกว้างพัลส์โดยตรงเป็นนาโนวินาที:

from machine import PWM, Pin

servo = PWM(Pin("P7"), freq=50, duty_ns=1_500_000)  # centre

คลื่นพาหะคือ 50 Hz (รอบ 20 ms) เวลาสูงในแต่ละรอบคือ 1500 µs พอดี ตัวช่วยเล็กๆ ทำให้การแมปตำแหน่งเป็นพัลส์ชัดเจน:

def set_position(angle):
    # angle: 0..180 degrees mapped to 1.0..2.0 ms
    pulse_us = 1000 + (angle * 1000) // 180
    servo.duty_ns(pulse_us * 1000)

set_position(0)      # full one way
set_position(90)     # centre
set_position(180)    # full the other way

การกวาดช้าๆ ตลอดช่วง:

import time

for angle in range(0, 181, 5):
    set_position(angle)
    time.sleep_ms(20)
for angle in range(180, -1, -5):
    set_position(angle)
    time.sleep_ms(20)

ช่วง 1.0 -- 2.0 ms คือมาตรฐาน แต่เซอร์โวหลายตัวรับช่วงที่กว้างกว่า (มักเป็น 500 µs ถึง 2500 µs) สำหรับการเดินทางเต็มที่ data sheet ของเซอร์โวระบุขีดจำกัดความกว้างพัลส์ที่แน่นอน ค่าที่อยู่นอกช่วงนั้นอาจฟาดมอเตอร์เข้ากับจุดหยุดทางกล

สำหรับเซอร์โวที่มีช่วงไม่ได้มาตรฐาน ให้ยกขีดจำกัดไปไว้ในค่าคงที่และกำหนดพารามิเตอร์การแมป:

PULSE_MIN_US = 500     # full one way (from the data sheet)
PULSE_MAX_US = 2500    # full the other way

def set_position(angle):
    span_us = PULSE_MAX_US - PULSE_MIN_US
    pulse_us = PULSE_MIN_US + (angle * span_us) // 180
    servo.duty_ns(pulse_us * 1000)