首先看一下星瞳科技的巡线方案(附上其开源源码):
THRESHOLD = (5, 70, -23, 15, -57, 0) # Grayscale threshold for dark things...
import sensor, image, time
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()
sensor.reset()
sensor.set_vflip(True)
sensor.set_hmirror(True)
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQQVGA) # 80x60 (4,800 pixels) - O(N^2) max = 2,3040,000.
#sensor.set_windowing([0,20,80,40])
sensor.skip_frames(time = 2000) # WARNING: If you use QQVGA it may take seconds
clock = time.clock() # to process a frame sometimes.
while(True):
clock.tick()
img = sensor.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.line(), color = 127)
print(rho_err,line.magnitude(),rho_err)
if line.magnitude()>8:
#if -40
直接从主函数看起:
img = sensor.snapshot().binary([THRESHOLD])
先是这样一行代码 ,星瞳科技对此函数的解释是:
根据像素是否在阈值列表
thresholds
中的阈值内,将图像中的所有像素设置为黑色或白色。
这样就可以理解为将图像按目标颜色和非目标颜色二值化为黑白两色,如果巡线的目标以及背景不是黑白两色,即可以采用这样的方法。
然后是很关键的:
line = img.get_regression([(100,100)], robust = True)
我们首先关注其thresholds参数,以下为星瞳科技的注解:
必须是元组列表。
[(lo, hi), (lo, hi), ..., (lo, hi)]
定义你想追踪的颜色范围。 对于灰度图像,每个元组需要包含两个值 - 最小灰度值和最大灰度值。 仅考虑落在这些阈值之间的像素区域。 对于RGB565图像,每个元组需要有六个值(l_lo,l_hi,a_lo,a_hi,b_lo,b_hi) - 分别是LAB L,A和B通道的最小值和最大值。 为方便使用,此功能将自动修复交换的最小值和最大值。 此外,如果元组大于六个值,则忽略其余值。相反,如果元组太短,则假定其余阈值处于最大范围。
并且将
robust = True
即 使用Theil-Sen线性回归算法。
接下来就可以利用得到 两个值 theta以及rho
星瞳科技在此处利用的是霍夫变换:
在直角坐标系上有一点B BB,经过这个点有一条直线,ρ \rho为原点到该直线的距离,垂足是点A ,这个线段与x轴的夹角为θ \theta。如下图1所示:
————————————————
版权声明:此处为CSDN博主「Jeff-Chow000」的原创文章
此处便不再更深讨论霍夫变换
星瞳科技利用这两个值的方式,让人有点难以理解 以及后面的效果也不太尽人意
于是我做了一些改动(附上源码):
THRESHOLD = (7, 255) # Grayscale threshold for dark things...
import sensor, image, time, math, pyb
from pyb import LED ,UART
import ustruct
LED(1).on()
LED(2).on()
LED(3).on()
sensor.reset()
sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.QQQVGA) # 80x60 (4,800 pixels) - O(N^2) max = 2,3040,000.
#sensor.set_windowing([0,20,80,40])
sensor.skip_frames(time = 2000) # WARNING: If you use QQVGA it may take seconds
clock = time.clock() # to process a frame sometimes.
uart = UART(3,115200) #定义串口3变量
uart.init(115200, bits=8, parity=None, stop=1)
theta_err = 0
rho_err = 0
coin = 0
x,y,r = 0,0,0
aa = 0
def sending_data(cx,cy,cw,ch):
global uart;
#frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];
#data = bytearray(frame)
data = ustruct.pack("= 2):
if(line.theta() > 90):
aa = -(line.rho()+40)
if(aa<-80):
aa=-80
else:
aa = (line.rho() - 40)*2
if(aa>80):
aa=80
rho_err = aa
theta_err =90- line.theta()
img.draw_line(line.line(), color = 127)
FH = bytearray([0x2C,0x12,int(rho_err),int(theta_err),coin,0,0x5B])
uart.write(FH)
首先由于我的目标是黑色的线白色的底,所以我直接采用的灰度图像也没有二值化,因为经过实验发现如果底不是纯白色,直接二值化后会有很多噪点,最后这些噪点可能就会对巡线造成一定影响。而灰度图像有0-255个像素值,目标为0那么其他偏黑的像素点也不会算作为目标点但是直接二值化为黑白图像那么一些偏黑的像素点可能就会成为黑点成为噪点。
并且我还限定了roi减少了镜头周围由于光线问题所带来的影响
还利用了一些限制条件:
if line and (line.magnitude() >= 2);
line.magnitude()为得到直线的模长,这个可以根据需要调整
根据霍夫变换得到的line.rho()不能够直接用作error来使用,因为他是一个“ 区域线性值 ”:
当line.theta() > 90时(左侧):
当 line.theta() > 90 && line.theta() < 135 时:
line.rho()从一个负数(大概为-40)变化到0;
当 line.theta() > 135 && line.theta() < 180 时:
line.rho()从0变化为一个正数(大概为40);
也就是说在 line.theta() =90 这条线的左侧,这个区域内,line.rho()的值其实是线性的,但是却不是同号:
那我们可不可以把左侧变成同一个符号的线性值且在 line.theta() = 90 时为0呢?
于是:
if(line.theta() > 90):
aa = -(line.rho()+40)
if(aa<-80):
aa=-80
经过多次测试, line.theta() = 90(左趋近90)时,line.rho() = -40,于是我们将零点右移(line.rho()+40);
这样左边就是从0一直线性变化为一个正数,我们再整体取反,就在 line.theta() > 90 && line.theta() < 180 这个范围内让 line.rho()从0到一个负数的线性变化。
当line.theta() < 90时(右侧):
当line.theta() = 90(右趋近) 时 line.rho() 这是又会变成 一个趋近40的数 且 在 line.theta() 向 0 变化这个过程中line.rho()不断线性的增长。
为了让 line.theta() 又趋近 90时的 line.rho() 值趋于零,同时保证线性性质,我们将右侧 line.rho()整体 减去 40,经过观察左侧与右侧相差一个二倍关系,所以我们将右侧再乘以2。
else:
aa = (line.rho() - 40)*2
if(aa>80):
aa=80
当然,左右两侧都有对应的限幅。
最后得到的 aa 值再赋值给变量 rho_err,即使由于限制条件我们没有更新得到 aa 值,串口传出去的值 rho_err 仍然保持上一次的误差值。
以上就是我根据星瞳科技openmv所改进的巡线方案。