基于opencv树莓派的寻灯灭灯小车--第六届全国光电设计大赛智能车组(下)

接着上一篇,进入while(1)循环

    while(1)
    {
        int k=0;
        cap>>frame;            //存图像于frame
        w.workthread[1]->red = digitalRead(27);
        if(w.workthread[1]->close==1)
        {
            w.workthread[1]->closer = digitalRead(26);
        }
        imshow("this",frame);
        cvtColor(frame, edges, CV_BGR2HSV);//彩色转换成灰度
        split(edges,channels);             //将图像按通道分离

判断语句中:

w.workthread[1]->close

为小车是否进入灭灯动作的标志位,等于0为进入灭灯动作,此时应该关闭车头的红外对管,不然红外对管会持续触发;

因此,只有在小车未进入灭灯动作的时候,才需要读取红外对管的数据,即:

w.workthread[1]->closer = digitalRead(26);

逻辑为:小车找到灯位置-->开启车头红外对管-->小车接近目标触发红外对管-->小车进入灭灯状态-->同时关闭红外对管-->灭灯完成-->继续开启红外对管

3.1、设置阈值并将图像二值化

        for(int i = 0;i(i);
          uchar*redData_1 = channels[2].ptr(i);
          for(int j = 0;j179)
                {
                    redData[j] = 255;
                    k+=1;
                }else if(redData[j]w.m_dowm&&redData_1[j]>w.I)
                {
                    redData[j] = 255;
                    k+=1;
                }else
                {
                    redData[j] = 0;
                }
           }

        }

这里只设置H通道和V通道的阈值,在效果较好的情况下保证速度

3.2、开操作,闭操作

        //morphologyEx(channels[0], channels[0], MORPH_OPEN, element);
        //morphologyEx(channels[0], channels[0], MORPH_CLOSE, element);

3.3、寻找小灯的位置

        cv::findContours(channels[0],contours,hierarchy,RETR_CCOMP,CHAIN_APPROX_SIMPLE);
        double maxArea = 0;
        vector maxContour;
        int second = 0;
        int max = 1;
        for(size_t i = 0;i = maxArea)
            {

                max = i;
                maxArea = area;
                maxContour = contours[i];
            }
        }
        Point2f vertex[4];

        Point s1,l,r,u,d;

找出图像中的最大连通域的位置

3.4、得到目标的坐标,并用pid算法控制舵机转向

        Point2f vertex[4];

        Point s1,l,r,u,d;
        if(k>0)        //如果图像上有信号,k大于0
        {
            RotatedRect box = minAreaRect(Mat(contours[max]));
            box.points(vertex);                               //得到矩形的坐标
            s1.x = (vertex[0].x+vertex[2].x)/2.0;             //分别得到矩形中心的x坐标与y坐标
            s1.y = (vertex[0].y+vertex[2].y)/2.0;
            l.x = s1.x-10;
            l.y = s1.y;
            r.x = s1.x+10;
            r.y = s1.y;
            u.x = s1.x;
            u.y = s1.y-10;
            d.x = s1.x;
            d.y = s1.y+10;
            line(channels[0],l,r,Scalar(100,0,0),2,LINE_AA);
            line(channels[0],u,d,Scalar(100,0,0),2,LINE_AA);     //以上为画一个十字表示中心坐标位置
            imshow("largest region",channels[0]);
                ThisError_x = s1.x - 160;
                ThisError_Y = s1.y - 120;
                if(LastError_x-ThisError_x>12)                    //防止噪点干扰的方法
                {
                    ThisError_x=LastError_x-12;
                }
                else if(LastError_x-ThisError_x<-12)
                {
                    ThisError_x=LastError_x+12;
                }
                pwm_x = ThisError_x*P + D*(ThisError_x-LastError_x);   //pid算法
                if(pwm_x>=4)
                {
                    speed=pwm_x;
                }else if(pwm_x<0)
                {
                    speed=-pwm_x;
                }

                if(LastError_Y-ThisError_Y>12)
                {
                    ThisError_Y=LastError_x-12;
                }
                else if(LastError_Y-ThisError_Y<-12)
                {
                    ThisError_Y=LastError_Y+12;
                }
                pwm_y = ThisError_Y*P_Y + D_Y*(ThisError_Y-LastError_Y);
                if(pwm_x<5&&pwm_x>-5&&back==1)        //控制舵机转向
                {
                    softPwmWrite(3,15+pwm_x);

                }else if(pwm_x<=-5&&back==1)
                {
                    softPwmWrite(3,10);
                    speed=5;

                }else if(pwm_x>=5&&back==1)
                {
                    softPwmWrite(3,20);
                    speed=5;

                }
                if((s1.y>=w.pwm_x_up&&w.workthread[1]->flag ==1))
                {

                    w.workthread[1]->flag = 0;

                    w.workthread[1]->start();        //进入线程2
                    w.timethread[1]->start();
                    w.timethread[1]->timer=0;
                    //m_timer=0;
                }else if(s1.y>=w.pwm_x_dowm&&s1.yflag ==1)
                {
                    w.I=170-(s1.y-55)/4;
                    softPwmWrite(2,0);
                    softPwmWrite(4,15);
                    //softPwmWrite(1,25);
                }
                else if(s1.yflag ==1)
                    {
                        softPwmWrite(2,0);
                        softPwmWrite(4,28);
                        //softPwmWrite(1,25);
                    }
                }
                LastError_x = ThisError_x;
                LastError_Y = ThisError_Y;
                //qDebug()<<"pwm_x:"<

其中的以下代码:

                if((s1.y>=w.pwm_x_up&&w.workthread[1]->flag ==1))
                {

                    w.workthread[1]->flag = 0;

                    w.workthread[1]->start();        //进入线程2
                    w.timethread[1]->start();
                    w.timethread[1]->timer=0;
                    //m_timer=0;
                }

 

有必要解释一下,当目标的y轴坐标大于一定值时,也就是说小车接近目标一定距离,并且此时小车不在预灭灯状态时,小车进入workthread线程,同时也开启timethread线程,先看workthread线程内部的代码:

 

void WorkThread::run()
{
    softPwmWrite(2,30);
    softPwmWrite(4,0);      //以上两行表示刹车
    delay(200);
    while(flag==0)          //刹车后进入预灭灯状态循环
    {
            softPwmWrite(2,0);
            if(speed>=4)           //speed为舵机的转动角度
            {
                softPwmWrite(4,11);
            }else if(speed<4)
            {
                softPwmWrite(4,8);
            }
        //qDebug()<<"start!!";
        if(closer==0)                //如果closer=0,判断50ms后是否还为0,这是为了避免误判
        {

            delay(50);               
            if(closer==0)           //进入真正灭灯状态,closer的值为车头红外对管的值
            {
                close=0;
                m_timer=0;
                qDebug()<<"stop!!";
                softPwmWrite(2,30);
                softPwmWrite(4,0);
                softPwmWrite(1,5);    //挡板展开
                delay(100);
                softPwmWrite(2,0);
                softPwmWrite(4,0);
                softPwmWrite(3,15);   //11向左转,19向右转
                back=0;
                delay(1000);
                softPwmWrite(1,22);    //挡板合上
                softPwmWrite(2,60);     //后退
                softPwmWrite(4,0);
                delay(1000);
                softPwmWrite(3,12);
                softPwmWrite(2,30);     //后退
                softPwmWrite(4,0);
                delay(400);
                back=1;
                flag =1;                 //flag等于1后,将会跳出预灭灯状态循环
                close=1;
            }
        }else if(m_timer==1)             //timethread的标志为1后同样会跳出循环
        {
            flag = 1;
        }
    }
}

基本思路为,小车刹车后 进入预灭灯状态

此时小车的车速根据舵机的值变化,也就是说舵机转角越大,车速相应越快,这是为了防止由于转角过大造成的卡死停车

如果红外对管触发,则进入实际灭灯状态,灭灯完成后退出预灭灯状态,退出线程;

如果进入预灭灯状态超过3秒。也就是timethread的标志位m_timer=1,小车认为之前的目标为误判目标(因为比赛场地的反光问题,小车在一定的角度下,有可能把地面的反射光线错误识别为目标)此时同样退出预灭灯状态,退出线程;

如果以上情况都没有发生,小车会持续在预灭灯状态,直到以上一种情况发生;

下面时timethread线程内部代码:

void TimeThread::run()
{
    while(timer==0)
    {
        m_timer=0;
        delay(1000);
        delay(1000);
        delay(1000);
        m_timer=1;
        timer=1;
    }
}

3.5、如果这一帧图像没有找到目标位置,小车保持右转,在原地转圈

        else
        {
            if(w.workthread[1]->flag ==1)digitalWrite(28,0);   //0表示前进
            w.Tento2(70);     //马达速度
            if(w.workthread[1]->flag ==1)
            {
                softPwmWrite(1,22);
                softPwmWrite(2,0);
                softPwmWrite(4,45);
                softPwmWrite(3,20);   //11向左转,19向右转
            }

            //softPwmWrite(2,0);
            //softPwmWrite(4,50);
        }

 

 

至此,while(1)循环内的代码已经完成,整车工程及源代码将在下一篇中分享,由于这个比赛小车是仓促制作,小车车模不大,在对抗赛中的第二轮被大车淘汰,代码也写的很匆忙,很渣渣,而且不应该用qt进行开发的,直接用python会省下很多时间,在io口的控制上也应该使用stm32等单片机才对,树莓派的io口做这类比赛太吃力了,总之写的不好,请见谅,谢谢观看!

你可能感兴趣的:(opencv)