سيارة تتبع الخط

الدرس المرئي 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.

مخطط توضيحي لنتيجة التشغيل:

results matching ""

    No results matching ""