前言
超声波传感器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
这个程序还有些问题。
可能是因为舵机放的时间太久了,有时候角度数据会胡乱跳动,跳动的厉害的时候,上位机程序会崩溃。猜测可能是因为某些极端值导致的栈溢出崩溃。