سيارة تتبع الخط
الدرس المرئي 20 - سيارة تتبع الخط: https://singtown.com/learn/50037/
يوضح هذا المثال استخدام دالة get_regression() في كاميرا OpenMV للحصول على الانحدار الخطي (linear regression) لمنطقة الاهتمام (ROI). باستخدام هذه الطريقة، يسهل جعل الروبوت يتتبع جميع الخطوط التي تشير إلى نفس الاتجاه العام.
يمكن استخدام هذا الروتين لتتبع الخط بواسطة الروبوت، والنتيجة جيدة جدًا.
سيارة تطارد الكرات
الدرس المرئي 9 - السيارة التي تطارد الكرة: https://singtown.com/learn/49239/
تحضير المواد
لوحة دارة OpenMV × 1:\

قاعدة سيارة مطبوعة بتقنية الطباعة ثلاثية الأبعاد:

- بطارية ليثيوم 3.7 فولت × 1:

- لوحة تحكم بمحرك TB6612 × 1:

- عجلة كروية (Bull's eye wheel) × 2:

- محرك تيار مستمر N20 × 2 (يشمل القاعدة الثابتة والإطار):

- صامولة برغي M3*20 × 2:

- برغي ذاتي التنصيت M2*4 × 2

توصيل الدارة واختبار المحرك

كتابة وحدة السيارة (module)
بدايةً، لماذا نحتاج إلى كتابة وحدة (module)؟ ليس من الصعب التحكم في المحرك مباشرة - لأن هذا يمنحنا أفضل قابلية لإعادة استخدام الكود، ويجعل منطق التحكم مستقلًا عن بنية السيارة. بالنسبة للسيارات المختلفة، يكفي تغيير وحدة السيارة.
car.py
from pyb import Pin, Timer
inverse_left=False #غيّرها إلى True لعكس اتجاه العجلة اليسرى
inverse_right=False #غيّرها إلى True لعكس اتجاه العجلة اليمنى
ain1 = Pin('P0', Pin.OUT_PP)
ain2 = Pin('P1', Pin.OUT_PP)
bin1 = Pin('P2', Pin.OUT_PP)
bin2 = Pin('P3', Pin.OUT_PP)
ain1.low()
ain2.low()
bin1.low()
bin2.low()
pwma = Pin('P7')
pwmb = Pin('P8')
tim = Timer(4, freq=1000)
ch1 = tim.channel(1, Timer.PWM, pin=pwma)
ch2 = tim.channel(2, Timer.PWM, pin=pwmb)
ch1.pulse_width_percent(0)
ch2.pulse_width_percent(0)
def run(left_speed, right_speed):
if inverse_left==True:
left_speed=(-left_speed)
if inverse_right==True:
right_speed=(-right_speed)
if left_speed < 0:
ain1.low()
ain2.high()
else:
ain1.high()
ain2.low()
ch1.pulse_width_percent(int(abs(left_speed)))
if right_speed < 0:
bin1.low()
bin2.high()
else:
bin1.high()
bin2.low()
ch2.pulse_width_percent(int(abs(right_speed)))
احفظ الملف أعلاه باسم car.py، واحفظ car.py على OpenMV وفقًا لـ كيفية استخدام الوحدات (modules).
اختبر الكود في بيئة التطوير (IDE):\ main.py
import car
while True:
car.run(100,100)
تحقق من أن السيارة تتحرك للأمام. إذا لم تتحرك، غيّر قيمتَي inverse_left و inverse_right في السطرين الثاني والثالث لعكس اتجاه العجلة اليسرى أو اليمنى للتأكد من أن السيارة تتحرك إلى الأمام.
تنفيذ خوارزمية PID
خوارزمية PID خوارزمية شائعة جدًا تُستخدم في التحكم، وهناك الكثير من الشروحات النظرية عنها على الإنترنت.\ https://zh.wikipedia.org/wiki/PID控制器\ http://baike.baidu.com/link?url=-obQq78Ur4bTeqA10bIniO6y0euQFcWL9WW18vq2hA3fyHN3rt32o79F2WPE7cK0Di9M6904rlHD9ttvVTySIK\ الكود بسيط جدًا. لقد نسخت مباشرةً الكود المصدري لوحدة تحكم طيران:\ https://github.com/wagnerc4/flight_controller/blob/master/pid.py\ وهو بدوره نسخة من ArduPilot\ https://github.com/ArduPilot/ardupilot
pid.py
from pyb import millis
from math import pi, isnan
class PID:
_kp = _ki = _kd = _integrator = _imax = 0
_last_error = _last_derivative = _last_t = 0
_RC = 1/(2 * pi * 20)
def __init__(self, p=0, i=0, d=0, imax=0):
self._kp = float(p)
self._ki = float(i)
self._kd = float(d)
self._imax = abs(imax)
self._last_derivative = float('nan')
def get_pid(self, error, scaler):
tnow = millis()
dt = tnow - self._last_t
output = 0
if self._last_t == 0 or dt > 1000:
dt = 0
self.reset_I()
self._last_t = tnow
delta_time = float(dt) / float(1000)
output += error * self._kp
if abs(self._kd) > 0 and dt > 0:
if isnan(self._last_derivative):
derivative = 0
self._last_derivative = 0
else:
derivative = (error - self._last_error) / delta_time
derivative = self._last_derivative + \
((delta_time / (self._RC + delta_time)) * \
(derivative - self._last_derivative))
self._last_error = error
self._last_derivative = derivative
output += self._kd * derivative
output *= scaler
if abs(self._ki) > 0 and dt > 0:
self._integrator += (error * self._ki) * scaler * delta_time
if self._integrator < -self._imax: self._integrator = -self._imax
elif self._integrator > self._imax: self._integrator = self._imax
output += self._integrator
return output
def reset_I(self):
self._integrator = 0
self._last_derivative = float('nan')
احفظ pid.py أيضًا على OpenMV وفقًا لـ كيفية استخدام الوحدات (modules).
ضبط المعلمات لتحقيق التتبع
الشيء الأساسي هو ضبط معلمتَي PI، http://blog.csdn.net/zyboy2000/article/details/9418257
THRESHOLD = (5, 70, -23, 15, -57, 0) # عتبة التدرج الرمادي للأشياء الداكنة...
import csi, image, time
csi0 = csi.CSI()
from pyb import LED
import car
from pid import PID
rho_pid = PID(p=0.4, i=0)
theta_pid = PID(p=0.001, i=0)
LED(1).on()
LED(2).on()
LED(3).on()
csi0.reset()
csi0.vflip(True)
csi0.hmirror(True)
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QQQVGA) # 80x60 (4800 بكسل) - الحد الأقصى O(N^2) = 2,3040,000.
#csi0.window([0,20,80,40])
csi0.snapshot(time = 2000) # تحذير: إذا استخدمت QQVGA فقد يستغرق الأمر ثوانٍ
clock = time.clock() # لمعالجة الإطار أحيانًا.
while(True):
clock.tick()
img = csi0.snapshot().binary([THRESHOLD])
line = img.get_regression([(100,100)], robust = True)
if (line):
rho_err = abs(line.rho)-img.width()/2
if line.theta>90:
theta_err = line.theta-180
else:
theta_err = line.theta
img.draw_line(line, color = 127)
print(rho_err,line.magnitude,rho_err)
if line.magnitude>8:
#if -40<b_err<40 and -30<t_err<30:
rho_output = rho_pid.get_pid(rho_err,1)
theta_output = theta_pid.get_pid(theta_err,1)
output = rho_output+theta_output
car.run(50+output, 50-output)
else:
car.run(0,0)
else:
car.run(50,-50)
pass
#print(clock.fps())
إذا أردت صنع سيارة تتبع خط، يكفي استخدام قيمتَي theta وrho المُعادتين من كائن الخط الذي يحصل عليه هذا البرنامج (theta تمثل زاوية قطعة الخط المُعادة، وrho تمثل مسافة الإزاحة)، واستخدام theta وrho للتحكم في زاوية السيارة.
قيمة rho هي الأهم. إذا لم ترغب في استخدام theta، يمكنك الاكتفاء باستخدام rho.
مخطط توضيحي لنتيجة التشغيل:
