【Qt】超声波扫描雷达上位机

该文介绍了一个使用STM32微控制器,结合超声波传感器HC-SR04和舵机SG90构建的扫描雷达项目。STM32控制舵机旋转并测量距离,通过串口将数据发送到基于Qt的上位机,上位机实时显示扫描角度和障碍物距离,具有图形化界面和余晖效果。遇到的问题包括舵机角度数据偶尔跳动导致上位机崩溃。
摘要由CSDN通过智能技术生成

前言

超声波传感器HC-SR04,在单片机练手项目中很常见,比如说避障小车这种项目。舵机SG90,也是在当初学习51单片机的时候,用到过的外设。
在这个项目中,我使用了野火的stm32指南者开发板,搭配超声波传感器和舵机这两种外设,通过串口,向基于Qt的扫描雷达上位机发送角度和距离的数据。
上位机利用了Qt的画图功能,实时显示当前舵机的角度和障碍物的距离。

1硬件连接

在这里插入图片描述选择野火的指南者开发板作,这块板子把很多IO都就外接出来了,用来做项目很方便。
舵机信号线连接PA4。
超声波传感器Trig连接PB6,Echo连接PB7。

2单片机程序

单片机程序主要实现的功能:
1舵机半圆形的来回旋转,带着上面的超声波传感器旋转。
2超声波传感器探测距离。
3通过串口把角度和距离的数据发送到电脑上。

int main(void)
{	
	int i;
	int angle = 0;
	
	SysTick_Configure();
	
	Uart1_Configuration();
	Led_Configuration();
	printf("111111111");
	TIM4_Init(2000,9);		//蜂鸣器,初始化定时器4,预分频9,计数频率8M
	TIM_Cmd(TIM4, DISABLE);
	
	TIM3_Count_Configuration();
	HCSR04_Configuration();
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	TIM2_PWM_Configuration();	  //舵机
	
	TIM_SetCompare2(TIM2, 500);  //回到初始位?
	Delay_us(1000000);  //等待舵机复位



		while(1)
		{
			for(i = 500; i<= 2300; i += 2)
			{
				TIM_SetCompare1(TIM2, i);
				Delay_us(5000);
				angle = (i - 500) / 10;
				printf("%c%d %d\n", '+',angle, CalculateDistance());//角度和距离
				//Uart1_SendString(string);
				//USART_SendData(USART1, '\n');
			}
			for(i = 2300; i>= 500; i -= 2)
			{
				TIM_SetCompare1(TIM2, i);
				Delay_us(5000);
				angle = (i - 500) / 10;
				printf("%c%d %d\n", '-',angle, CalculateDistance());
			}
		}
}

这是main函数内容,主要就是旋转舵机,然后通过重定义的串口printf,把角度和数据发送出去。

其实单片机的硬件和软件不是最重要的,他主要的作用就是发送串口数据了,也可以用模拟的数据来代替,并不一定要真实的。

3上位机设计

在这里插入图片描述

这是最终要实现的效果图。
1 一个扇面的形状,表示超声波传感器扫过的角度。
2 20-50cm的距离能够探测,探测到是物品时红色,没有是绿色。
3 扫描线是随着舵机的转动而随着转动的
4 带有余晖效果,也就是过去扫描的角度的内容,能滞留一段时间显示在软件上。

4上位机软件

这是widger.c中的内容,主要代码都放在这里。

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QtMath>
#include <QTimer>
#include <QDebug>

int flag=0;

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    resize(1600,900);
    setStyleSheet("background-color:balck");//黑色背景
//    QTimer *time1=new QTimer(this);
//    time1->start(20);
//    connect(time1,&QTimer::timeout,this,&Widget::timer_timeout);
    serial=new QSerialPort(this);

    QString PortString;//创建容器

    foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())//foreach关键字,遍历列表,放到info中
    {
        PortString=info.portName();//将可用串口的名字放到字符串容器中

        qDebug()<<PortString;
    }

    serial->setPortName(PortString);
    serial->setBaudRate(9600);
    serial->setDataBits(QSerialPort::Data8);
    serial->setParity(QSerialPort::NoParity);//没有检验
    serial->setStopBits(QSerialPort::OneStop);

    serial->setFlowControl(QSerialPort::NoFlowControl);
    connect(serial,QSerialPort::readyRead,this,&Widget::serial_port);//当串口有数据时,转到槽
    serial->open(QIODevice::ReadWrite);//打开串口
}

Widget::~Widget()
{
    delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
//    QPainter paint(this);
//    QPicture img;
//    img.load("E://cc.fff");
//    paint.drawPicture(0,0,img);//加载图片

    QPainter paint(this);
    paint.setRenderHint(QPainter::Antialiasing);
    QPen pen;
    pen.setColor(Qt::green);
    paint.setPen(pen);

    paint.drawArc(100,100,1400,1400,0*16,180*16);//左上角地址,右下角地址,起始角度和结束角度
    paint.drawArc(240,240,1120,1120,0*16,180*16);//左上角地址,右下角地址,起始角度和结束角度
    paint.drawArc(380,380,840,840,0*16,180*16);//左上角地址,右下角地址,起始角度和结束角度
    paint.drawArc(520,520,560,560,0*16,180*16);//左上角地址,右下角地址,起始角度和结束角度
    paint.drawArc(660,660,280,280,0*16,180*16);//左上角地址,右下角地址,起始角度和结束角度

    paint.drawLine(0,800,1600,800);//左上角是0,0点

    paint.save();
    paint.translate(800,800);
    for(int i=0; i<5;i++)
    {
        paint.rotate(-30);//旋转30度

        paint.drawLine(0,0,720,0);
    }
    paint.restore();//返回保存坐标

    QFont font("仿宋",30,QFont::Bold,true);
    paint.setFont(font);

    paint.save();
    paint.translate(800,800);
    /*************显示角度信息*************/
    for(int i=0;i<7;i++)
    {
        int r=i*30;

        QString t= QString::number(r)+"°";

        paint.drawText(720*qCos(qDegreesToRadians(30.0*i)),-720*qSin(qDegreesToRadians(30.0*i)),t);
    }

    /*************显示距离*************/
    for(int i=1;i<6;i++)
    {
        int temp_distance=i*10;
        QString distance=QString::number(temp_distance)+"cm";
        paint.drawText(140*i,30,distance);
    }


    paint.restore();


    pen.setWidth(15);
    paint.setPen(pen);
    paint.save();
    paint.translate(800,800);


//    paint.drawLine(0,0,700*qCos(qDegreesToRadians(angle)),-700*qSin(qDegreesToRadians(angle)));//绘制扫描线

/****************用渐变线实现绘制辉光余晖效果****************/
    double temp_angle=angle;
    if(flag==0)
    {
        if(distance>=50.0)
        {
            pen.setColor(Qt::green);
            paint.setPen(pen);
            int x=700*qCos(qDegreesToRadians(temp_angle));
            int y=-700*qSin(qDegreesToRadians(temp_angle));
            paint.drawLine(0,0,x,y);//绘制扫描线
        }
        else
        {
            pen.setColor(Qt::green);
            paint.setPen(pen);
            int x=distance*14*qCos(qDegreesToRadians(temp_angle));
            int y=-distance*14*qSin(qDegreesToRadians(temp_angle));
            paint.drawLine(0,0,x,y);//绘制扫描线

            pen.setColor(Qt::red);
            paint.setPen(pen);
            int x1=700*qCos(qDegreesToRadians(temp_angle));
            int y1=-700*qSin(qDegreesToRadians(temp_angle));
            paint.drawLine(x,y,x1,y1);//绘制扫描线
        }
        for(int i=1;i<=temp_angle;i++)
        {
            paint.setOpacity(1-i*1.0/((temp_angle>30)?30:temp_angle));
            if(arr[int(temp_angle-i)]>=50.0)
            {
                pen.setColor(Qt::green);
                paint.setPen(pen);
                int x=700*qCos(qDegreesToRadians(temp_angle-i));
                int y=-700*qSin(qDegreesToRadians(temp_angle-i));
                paint.drawLine(0,0,x,y);//绘制扫描线
            }
            else
            {
                pen.setColor(Qt::green);
                paint.setPen(pen);
                int x=arr[int(temp_angle-i)]*14*qCos(qDegreesToRadians(temp_angle-i));
                int y=-arr[int(temp_angle-i)]*14*qSin(qDegreesToRadians(temp_angle-i));
                paint.drawLine(0,0,x,y);//绘制扫描线\

                pen.setColor(Qt::red);
                paint.setPen(pen);
                int x1=700*qCos(qDegreesToRadians(temp_angle-i));
                int y1=-700*qSin(qDegreesToRadians(temp_angle-i));
                paint.drawLine(x,y,x1,y1);//绘制扫描线
                if(i==30)
                {
                    break;
                }
            }

        }

    }



    else if(flag==1)
    {
        if(distance>=50.0)
        {
            pen.setColor(Qt::green);
            paint.setPen(pen);
            int x=700*qCos(qDegreesToRadians(temp_angle));
            int y=-700*qSin(qDegreesToRadians(temp_angle));
            paint.drawLine(0,0,x,y);//绘制扫描线
        }
        else
        {
            pen.setColor(Qt::green);
            paint.setPen(pen);
            int x=distance*14*qCos(qDegreesToRadians(temp_angle));
            int y=-distance*14*qSin(qDegreesToRadians(temp_angle));
            paint.drawLine(0,0,x,y);//绘制扫描线

            pen.setColor(Qt::red);
            paint.setPen(pen);
            int x1=700*qCos(qDegreesToRadians(temp_angle));
            int y1=-700*qSin(qDegreesToRadians(temp_angle));
            paint.drawLine(x,y,x1,y1);//绘制扫描线
        }
        for(int i=1;i<=(180-temp_angle);i++)
        {
            paint.setOpacity(1-i*1.0/(((180-temp_angle)>30)?30:(180-temp_angle)));
            if(arr[int(temp_angle+i)]>=50.0)
            {
                pen.setColor(Qt::green);
                paint.setPen(pen);
                int x=700*qCos(qDegreesToRadians(temp_angle+i));
                int y=-700*qSin(qDegreesToRadians(temp_angle+i));
                paint.drawLine(0,0,x,y);//绘制扫描线
            }
            else
            {
                pen.setColor(Qt::green);
                paint.setPen(pen);
                int x=arr[int(temp_angle+i)]*14*qCos(qDegreesToRadians(temp_angle+i));
                int y=-arr[int(temp_angle+i)]*14*qSin(qDegreesToRadians(temp_angle+i));
                paint.drawLine(0,0,x,y);//绘制扫描线\

                pen.setColor(Qt::red);
                paint.setPen(pen);
                int x1=700*qCos(qDegreesToRadians(temp_angle+i));
                int y1=-700*qSin(qDegreesToRadians(temp_angle+i));
                paint.drawLine(x,y,x1,y1);//绘制扫描线
                if(i==30)
                {
                    break;
                }
            }

        }
//            paint.drawLine(0,0,700*qCos(qDegreesToRadians(temp_angle+i)),-700*qSin(qDegreesToRadians(temp_angle+i)));
//            if(i==30)
//            {
//                break;
//            }
//        }
    }
    paint.restore();
}


//void Widget::timer_timeout()
//{
//    //qDebug()<<"ddd";
//    //static i=0;
//    //i++;
//    if(flag==0)
//    {
//        angle++;
//        if(angle==180)
//        {
//            flag=1;
//        }

//    }
//    else
//    {
//        angle--;
//        if(angle==0)
//        {
//            flag=0;
//        }
//    }

//    update();//重新绘制
//}

void Widget::serial_port()
{

    QString angle_data,angle_str,distance_str;
    rx_data=serial->readAll();//从串口接收数据
    rx_len=rx_data.length();//调用qstring中的方法
    rx_data=rx_data.toLatin1();//转为ascii字符串的方法

    //qDebug()<<rx_data;

    if(rx_data[0]=='+')
    {
        flag=0;
    }
    else
    {
        flag=1;
    }

    if(rx_data[0] == '+')
        flag = 0;
    else
        flag = 1;
    for(int i = 1; i < rx_len - 1; i++)
        angle_data += rx_data[i];

    int index = angle_data.indexOf(' ');
    for(int i = 0; i < index; i++)
        angle_str += angle_data[i];
    angle = angle_str.toInt();

    for(int i = index + 1; i < sizeof(angle_data); i++)
        distance_str += angle_data[i];
    distance =distance_str.toInt();

    arr[int(angle)] = distance;

    qDebug() << angle << " " << distance;

    update();
}

其中最难得部分,就是余晖效果的实现了。
使用的是方法,是在扫描下后面添加30根从绿色渐变到透明的线。

5 总结

上位机部分放在git仓库:https://gitee.com/wangyongwei111/qt.git

这个程序还有些问题。
可能是因为舵机放的时间太久了,有时候角度数据会胡乱跳动,跳动的厉害的时候,上位机程序会崩溃。猜测可能是因为某些极端值导致的栈溢出崩溃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值