基于QT的多场景机动车防碰撞算法仿真测试平台

  • 大创项目日志,仅供参考

1.道路模块的搭建与拼接

  • 主界面如图所示

  • 头文件 源文件 UI界面文件
    mainwindow.h/.cpp/.ui 主界面
    mapcanvas.h/.cpp 地图画布
    文件夹Scenes包含:
    a. scene.h/.cpp 操作场景模块
    b. interface.h/.cpp 场景模块间的接口
    c. straight ramp cross curve.h/.cpp 绘制场景模块
    d. straightsetdialog rampsetdialog crosetdialog cursetdialog.h/.cpp/.ui
    绘制参数设置界面,传递场景模块参数

  • MapCanvas类是一个窗口类,即地图画布。每新建一个地图就新建了一个地图画布,关于道路场景绘制的各种操作都在这里进行。将来还会加入车辆仿真功能,车辆可视化的工作应该也会在这里进行。
    目前地图画布的功能有:
    1.创建新场景。
    2.拖拽移动单个场景或同时框选多个场景拖拽移动。
    3.拖拽后如果有临近道路接口则自动吸附连接,批量移动时多个场景同时移动吸附拼接。
    4.在地图空白处双击后拖拽可移动地图,地图为无限大。
    5.滚动鼠标滚轮缩放地图。
    6.右键点击地图空白处可清空地图、粘贴、撤销、重做。

难点1 匝道的绘制

  • 需要考虑草地、水泥路面、车道线的绘制顺序(逐步覆盖)
    全绿-直道灰-直道上边线-匝道灰-匝道边线-直道虚线中心线-直道下边线
  • drawRect() drawLine() 绘制路面和车道线
  • traslate(x,y) 移动painter的位置,改变绘制起点
  • rotate(angle) 改变painter的转向
  • save() restore() 保存或恢复painter的位置与转向
void Ramp::paintRoad(QWidget *canvas, double scale, QPointF mapOrigin)
{//开始绘制QPainter painter(canvas);//参数准备Scale = scale;      //更新比例尺double wid_lane = Wid_lane/Scale;   //米->像素double len_lane = Len_lane/Scale;   //米->像素double len_Ramp = Len_Ramp/Scale;   //米->像素QPointF origin = GlobalPos/scale + mapOrigin;double angle_bug; //始终表示匝道与直道的锐角夹角double L;//匝道衔接处长度double del;//匝道路长差值double Lon_Ramp,Sho_Ramp;//匝道长短边线double Len1,Len2;//分别代表第一次和第二次画的匝道边线double ret;//画第一条匝道边线的画家的退回距离returnconst float pi = 3.1415926535879;//定义常量pi//直道int Num_lanes = Num_laneL + Num_laneR;//总车道数double Wid_lanes = wid_lane*Num_lanes;//道路总宽double X = -len_lane/2;double Y = -Num_lanes*wid_lane/2;//匝道L = wid_lane/sin(Angle*pi/180);// 角度*pi/180 = 弧度制del = L*cos(Angle*pi/180);Lon_Ramp = len_Ramp+del/2;Sho_Ramp = len_Ramp-del/2;if(FlagOut == 0){     //匝道入angle_bug = Angle;ret = 0;Len1 = Lon_Ramp;Len2 = Sho_Ramp;}else{             //匝道出angle_bug = 180-Angle;ret = del;Len1 = Sho_Ramp;Len2 = Lon_Ramp;}//准备画布painter.setRenderHint(QPainter::Antialiasing);//反锯齿,更清晰painter.resetTransform();//先重置参数painter.translate(origin.x(), origin.y());//把相对原点设置为坐标系原点painter.rotate(-Dir);//从目标角度逆时针旋转到0度//绘制直道背景草地painter.setPen(QColor(165,194,146));painter.setBrush(QColor(165,194,146));painter.drawRect(X, Y-1/scale,len_lane, Wid_lanes+2/scale);//绘制匝道背景草地painter.save();painter.translate(L/2,wid_lane*Num_lanes/2);painter.rotate(180-angle_bug);painter.setPen(QColor(165,194,146));painter.setBrush(QColor(165,194,146));painter.drawRect(-ret, -1/scale, Len1+ret, wid_lane+2/scale);//绘制直道柏油路面painter.restore();painter.setPen(QColor(155,144,144));painter.setBrush(QColor(155,144,144));painter.drawRect(X, Y-0.4/scale, len_lane, Wid_lanes+0.8/scale);//绘制直道道路上边线painter.setPen(QPen(Qt::white, 0.2/scale));painter.drawLine(QPointF(X,Y),QPointF(X+len_lane,Y));//painter.drawLine(QPoint(X,Y+Wid_lanes),QPoint(X+Len_lane,Y+Wid_lanes));//绘制匝道柏油路面painter.save();painter.translate(L/2,wid_lane*Num_lanes/2);painter.rotate(180-angle_bug);painter.setPen(QColor(155,144,144));painter.setBrush(QColor(155,144,144));painter.drawRect(-ret, -0.4/scale, Len1+ret, wid_lane+0.8/scale);//绘制匝道道路边线和直道下边线//painter.setPen(QPen(Qt::RoundJoin));painter.setPen(QPen(Qt::white, 0.2/scale));painter.drawLine(QPointF(0,0),QPointF(Len1,0));//匝道 长/短 边线(入/出)painter.rotate(angle_bug-180);painter.drawLine(QPointF(0,0),QPointF((len_lane-L)/2,0));//直道下边线右段painter.restore();painter.save();painter.translate(-L/2,wid_lane*Num_lanes/2);painter.rotate(180-angle_bug);painter.drawLine(QPointF(0,0),QPointF(Len2,0));//匝道 短/长 边线(入/出)painter.rotate(angle_bug);painter.drawLine(QPointF(0,0),QPointF((len_lane-L)/2,0));//直道下边线左段//绘制直道车道虚线painter.restore();painter.setPen(QPen(Qt::white, 0.2/scale, Qt::DashLine));double yLine = Y;for(int i = 1; i<Num_lanes; ++i){yLine += wid_lane;if(i==Num_laneL) continue; // 跳过道路中央实线,防止异常错位显示painter.drawLine(QPointF(X,yLine),QPointF(X+len_lane,yLine));}//绘制直道道路中央实线painter.setPen(QPen(QColor(255,204,0), 0.2/scale));if(Num_laneL!=0||Num_laneR!=0){  //单行道不画中央黄实线painter.drawLine(QPointF(X,Y+wid_lane*Num_laneL),QPointF(X+len_lane,Y+wid_lane*Num_laneL));}painter.resetTransform();//重置参数,恢复画布
}

难点2 匝道边线红框(选中区域)的设定

  • 匝道与直道衔接处的边界点位置需要较为复杂的数学推导来确定
  • QVector <QPointF> ClickAreaPoints 创建容器
  • ClickAreaPoints.append(QPointF(x,y)) 在容器中添加边界点
  • ClickArea = QPolygonF(ClickAreaPoints) 根据多个边界点确定框选区域
  • drawPolygon(ClickArea) 根据框选区域绘制多边形
QMatrix matrix;         //变换矩阵matrix.rotate(-Dir);    //顺时针旋转-Dir° QVector<QPointF> ClickAreaPoints;   //更新点击识别区if(FlagOut == 0){     //匝道入ClickAreaPoints.append(QPointF(origin+QPointF(X, Y-1/scale)*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(-X, Y-1/scale)*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(-X,-(Y-1/scale))*matrix));double l1x,l1y,l2x,l2y,l3x,l3y,l4x,l4y;//长匝道草地边线交点l1x = L/2-Len1*cos(angle_bug*pi/180);l1y = -Y+Len1*sin(angle_bug*pi/180);l2x = l1x+1*sin(angle_bug*pi/180)/scale;l2y = l1y+1*cos(angle_bug*pi/180)/scale;l3x = l2x;l3y = -(Y-1/scale);l4x = l3x+(l2y-l3y)/tan(angle_bug*pi/180);l4y = l3y;//ClickAreaPoints.append(QPointF(Origin+QPoint(L/2,-(Y-1/scale))*matrix));//ClickAreaPoints.append(QPointF(Origin+QPoint(L/2-Len1*cos(angle_bug*pi/180),-Y+Len1*sin(angle_bug*pi/180))*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(l4x,l4y)*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(l2x,l2y)*matrix));double s1x,s1y,s2x,s2y,s3x,s3y,s4x,s4y;//短匝道草地边线交点s1x = -L/2-Len2*cos(angle_bug*pi/180);s1y = -Y+Len2*sin(angle_bug*pi/180);s2x = s1x-1*sin(angle_bug*pi/180)/scale;s2y = s1y-1*cos(angle_bug*pi/180)/scale;s3x = s2x;s3y = -(Y-1/scale);s4x = s3x+(s2y-s3y)/tan(angle_bug*pi/180);s4y = s3y;//ClickAreaPoints.append(QPointF(Origin+QPoint(-L/2-Len2*cos(angle_bug*pi/180),-Y+Len2*sin(angle_bug*pi/180))*matrix));//ClickAreaPoints.append(QPointF(Origin+QPoint(-L/2,-(Y-1/scale))*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(s2x,s2y)*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(s4x,s4y)*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(X,-(Y-1/scale))*matrix));}else{           //匝道出ClickAreaPoints.append(QPointF(origin+QPointF(X, Y-1/scale)*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(-X, Y-1/scale)*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(-X,-(Y-1/scale))*matrix));double s1x,s1y,s2x,s2y,s3x,s3y,s4x,s4y;//短匝道草地边线交点s1x = L/2+Len1*cos((180-angle_bug)*pi/180);s1y = -Y+Len1*sin((180-angle_bug)*pi/180);s2x = s1x+1*sin((180-angle_bug)*pi/180)/scale;s2y = s1y-1*cos((180-angle_bug)*pi/180)/scale;s3x = s2x;s3y = -(Y-1/scale);s4x = s3x-(s2y-s3y)/tan((180-angle_bug)*pi/180);s4y = s3y;//ClickAreaPoints.append(QPointF(Origin+QPoint(L/2,-Y)*matrix));//ClickAreaPoints.append(QPointF(Origin+QPoint(L/2+Len1*cos((180-angle_bug)*pi/180),-Y+Len1*sin((180-angle_bug)*pi/180))*matrix));ClickAreaPoints.append(QPointF(origin+QPoint(s4x,s4y)*matrix));ClickAreaPoints.append(QPointF(origin+QPoint(s2x,s2y)*matrix));double l1x,l1y,l2x,l2y,l3x,l3y,l4x,l4y;//长匝道草地边线交点l1x = -L/2+Len2*cos((180-angle_bug)*pi/180);l1y = -Y+Len2*sin((180-angle_bug)*pi/180);l2x = l1x-1*sin((180-angle_bug)*pi/180)/scale;l2y = l1y+1*cos((180-angle_bug)*pi/180)/scale;l3x = l2x;l3y = -(Y-1/scale);l4x = l3x-(l2y-l3y)/tan((180-angle_bug)*pi/180);l4y = l3y;//ClickAreaPoints.append(QPointF(Origin+QPoint(-L/2+Len2*cos((180-angle_bug)*pi/180),-Y+Len2*sin((180-angle_bug)*pi/180))*matrix));//ClickAreaPoints.append(QPointF(Origin+QPoint(-L/2,-Y)*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(l2x,l2y)*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(l4x,l4y)*matrix));ClickAreaPoints.append(QPointF(origin+QPointF(X,-(Y-1/scale))*matrix));}ClickArea = QPolygonF(ClickAreaPoints);if(FlagChosen){       //场景模块painter.setRenderHint(QPainter:: Antialiasing);//抗锯齿painter.setPen(QPen(Qt::red, 1.5, Qt::DashLine));painter.setBrush(QColor(0,0,0,0));//painter.drawRect(scenesMap.last()->HighLightRect);painter.drawPolygon(ClickArea);}

难点3 场景模块设置参数(以匝道为例)

  1. 右键弹出选项条目(地图与道路不同)
  • 在mapcanvas的构造函数中新建QMenu和QAction的对象,将动作添加到选项列表;利用connect将动作(signal)与执行动作的函数(slot)连接

//地图设置M_contextMenu   = new QMenu;SetMapAction    = new QAction("设置比例尺", this);ClrMapAction    = new QAction("清空地图", this);PasteAction     = new QAction("粘贴",this);UndoAction      = new QAction("撤销",this);RedoAction      = new QAction("重做",this);M_contextMenu->addAction(SetMapAction);M_contextMenu->addAction(ClrMapAction);M_contextMenu->addAction(PasteAction);M_contextMenu->addAction(UndoAction);M_contextMenu->addAction(RedoAction);connect(ClrMapAction, &QAction::triggered, [=](){ListScenes.clear();saveTrace();update();});connect(SetMapAction, &QAction::triggered, [=](){ ;});connect(PasteAction, &QAction::triggered, [=](){ emit Signal_Paste(); }); //???connect(UndoAction, SIGNAL(triggered()),this,SLOT(Slot_Undo()));connect(RedoAction, SIGNAL(triggered()),this,SLOT(Slot_Redo()));//场景设置R_contextMenu   = new QMenu;SetSceneAction  = new QAction("设置参数",this);DelSceneAction  = new QAction("删除",this);CopySceneAction = new QAction("复制",this);R_contextMenu->addAction(SetSceneAction);R_contextMenu->addAction(DelSceneAction);R_contextMenu->addAction(CopySceneAction);connect(SetSceneAction, SIGNAL(triggered()),this,SLOT(Slot_SetScene()));connect(DelSceneAction, SIGNAL(triggered()),this,SLOT(Slot_DelScene()));connect(CopySceneAction, SIGNAL(triggered()),this,SLOT(Slot_CopyScene()));
  1. 绘制参数设置界面
  • 手动布局ui界面,注意修改控件的名称(可与变量名一致便于调用)和参数(最大值、小数点)

  1. 传递场景模块参数
  • rampsetdialog负责处理本模块控件变量,需要注意两点:1.控件初始化时应显示模块初始参数;2.点击OK后,将控件里的值传给rampsetdialog的public变量。
RampSetDialog::RampSetDialog(QWidget *parent,int num_laneR  ,int num_laneL  ,double len_lane,double wid_lane,double len_Ramp,double angle   ,bool flagOut   ,double dir     ) :QDialog(parent),ui(new Ui::RampSetDialog)
{ui->setupUi(this);this->setWindowTitle("参数设置");//初始化参数ui->num_laneR->setValue(num_laneR);ui->num_laneL->setValue(num_laneL);ui->len_lane->setValue(len_lane);ui->wid_lane->setValue(wid_lane);ui->len_Ramp->setValue(len_Ramp);ui->angle->setValue(angle);ui->dir->setValue(dir);ui->ramp_in->setChecked(!flagOut);ui->ramp_out->setChecked(flagOut);//点击OK,改变道路参数,关闭对话框connect(ui->OK,&QPushButton::clicked,parent,[=](){Set();emit OK_clicked();  //???this->reject();this->deleteLater();});//点击Cancel,关闭对话框connect(ui->Cancel,&QPushButton::clicked,[=](){this->reject();this->deleteLater();});}void RampSetDialog::Set() //匝道设置函数,将控件里的值传给rampsetdialog的public变量
{num_laneR   = ui->num_laneR->value();num_laneL   = ui->num_laneL->value();len_lane    = ui->len_lane->value();wid_lane    = ui->wid_lane->value();len_Ramp    = ui->len_Ramp->value();angle       = ui->angle->value();dir         = ui->dir->value();QButtonGroup rampdir;//给RadioButton分组标序号rampdir.addButton(ui->ramp_in,0);rampdir.addButton(ui->ramp_out,1);int a = rampdir.checkedId();//查看选中按钮的序号switch (a){case(0):flagOut = 0; break;//选中匝道入case(1):flagOut = 1; break;//选中匝道出}
}
  • ramp负责本模块的绘制。值传递与地址传递的两个Ramp构造函数???,SetScene函数可以将rampsetdialog的public变量值传递给ramp的public变量从而进行绘制。
Ramp::Ramp(QPointF globalPos,  //原点int     num_laneR,  //右侧车道数int     num_laneL,  //左侧车道数double  len_lane,   //车道长度double  wid_lane,   //车道宽度double  len_Ramp,   //匝道长度double  angle,      //匝道角度,不允许超出90度bool    flagOut,    //匝道入或出,入取0,出取1double  dir,        //场景方向初始化为0double  scale)      //比例尺
{SceneType = Scene::Ramp;Scale = scale;Dir = dir;GlobalPos = globalPos;Num_laneR = num_laneR;Num_laneL = num_laneL;Len_lane = len_lane;Wid_lane = wid_lane;Len_Ramp = len_Ramp;Angle = angle;FlagOut = flagOut;
}Ramp::Ramp(Ramp* ramp){SceneType = Scene::Ramp;Scale = ramp->Scale;Dir = ramp->Dir;GlobalPos = ramp->GlobalPos;Num_laneR = ramp->Num_laneR;Num_laneL = ramp->Num_laneL;Len_lane = ramp->Len_lane;Wid_lane = ramp->Wid_lane;Len_Ramp = ramp->Len_Ramp;Angle = ramp->Angle;FlagOut = ramp->FlagOut;
}void Ramp::SetScene(int num_laneR,int num_laneL,double len_lane,double wid_lane,double len_Ramp,double angle,bool flagOut,double dir)
{Num_laneR = num_laneR;Num_laneL = num_laneL;Len_lane = len_lane;Wid_lane = wid_lane;Len_Ramp = len_Ramp;Angle = angle;FlagOut = flagOut,Dir = dir;
}
  • mapcanvas负责贯穿整个设置参数流程:首先根据ListScenes中最后选中的模块,建立相应模块的指针和对话框指针。随后先调用StraightSetDialog构造函数生成对话框,再利用connect使对话框OK键触发时调用SetScene函数传递修改后的模块参数至ramp进行绘制。
void MapCanvas::Slot_SetScene(){Scene* sceneLast = ListScenes.last();switch (sceneLast->SceneType){case Scene::Straight:{Straight* Scene = (Straight*)sceneLast;//创建对话框对象并传入初始化数据StraightSetDialog* SetDlg = new StraightSetDialog (this,Scene->Num_laneR,Scene->Num_laneL,Scene->Len_lane,Scene->Wid_lane,Scene->Dir);SetDlg->setModal(false);        //设置窗口为非模态SetDlg->move(MouseScreenPos.toPoint());   //设置窗口出现位置为鼠标当前位置connect(SetDlg,&StraightSetDialog::OK_clicked,this,[=](){Scene->SetScene( SetDlg->num_laneR,SetDlg->num_laneL,SetDlg->len_lane,SetDlg->wid_lane,SetDlg->dir);});SetDlg->show();break;}case Scene::Cross:{Cross* Scene = (Cross*)sceneLast;//创建对话框对象并传入初始化数据CroSetDialog* SetDlg = new CroSetDialog (this,Scene->Num_laneR1,Scene->Num_laneL1,Scene->Num_laneR2,Scene->Num_laneL2,Scene->Len_lane1,Scene->Len_lane2,Scene->Wid_lane,Scene->Dir);SetDlg->setModal(false);        //设置窗口为非模态SetDlg->move(MouseScreenPos.toPoint());   //设置窗口出现位置为鼠标当前位置connect(SetDlg,&CroSetDialog::OK_clicked,this,[=](){Scene->SetScene( SetDlg->num_laneR1,SetDlg->num_laneL1,SetDlg->num_laneR2,SetDlg->num_laneL2,SetDlg->len_lane1,SetDlg->len_lane2,SetDlg->wid_lane,SetDlg->dir);});SetDlg->show();break;}case Scene::Ramp:{Ramp* Scene = (Ramp*)sceneLast;//创建对话框对象并传入初始化数据RampSetDialog* SetDlg = new RampSetDialog (this,Scene->Num_laneR,Scene->Num_laneL,Scene->Len_lane,Scene->Wid_lane,Scene->Len_Ramp,Scene->Angle,Scene->FlagOut,Scene->Dir);SetDlg->setModal(false);        //设置窗口为非模态SetDlg->move(MouseScreenPos.toPoint());   //设置窗口出现位置为鼠标当前位置connect(SetDlg,&RampSetDialog::OK_clicked,this,[=](){Scene->SetScene( SetDlg->num_laneR,SetDlg->num_laneL,SetDlg->len_lane,SetDlg->wid_lane,SetDlg->len_Ramp,SetDlg->angle,SetDlg->flagOut,SetDlg->dir);});SetDlg->show();break;}case Scene::Curve:{Curve* Scene = (Curve*)sceneLast;//创建对话框对象并传入初始化数据CurSetDialog* SetDlg = new CurSetDialog (this,Scene->LeanNum_laneR,Scene->LeanNum_laneL,Scene->LeanLen_lane,Scene->LeanWid_lane,Scene->VerticalLen_lane,Scene->Seti,Scene->R,Scene->Dir);SetDlg->setModal(false);        //设置窗口为非模态SetDlg->move(MouseScreenPos.toPoint());   //设置窗口出现位置为鼠标当前位置connect(SetDlg,&CurSetDialog::OK_clicked,this,[=](){Scene->SetScene( SetDlg->leanNum_laneR1,SetDlg->leanNum_laneL1,SetDlg->leanLen_lane1,SetDlg->leanWid_lane1,SetDlg->verticalLen_lane1,SetDlg->seti1,SetDlg->r1,SetDlg->dir1);});SetDlg->show();break;}}saveTrace();
}

难点4 场景模块之间自动吸附与逻辑连接

1.自动吸附

  • mapcanvas自动吸附函数:被选中的场景寻找接口进行移动。
void MapCanvas::autoSplice(){if(!ListScenes.empty()){qDebug()<<"尝试吸附";QPointF movement = ListScenes.last()->searchInterface(ListScenes);  if(movement.x() != -99){for(int i=0; i<ListScenes.size(); ++i){if(ListScenes.at(i)->FlagChosen){ListScenes.at(i)->move(movement);}}}}
}
  • scene两个重载的寻找接口函数(返回移动量):searchInterface(Interface*
    p)判断接口匹配情况;searchInterface(QList<Scene*> scenesMap)遍历各个场景寻找接口。
QPointF Scene::searchInterface(Interface* p)
{for(int i = 0; i < Interfaces.size(); ++i){if((Interfaces.at(i)->FlagPoint - p->FlagPoint).manhattanLength() < 3 &&(Interfaces.at(i)->FlagPoint - p->FlagPoint).manhattanLength() > 1e-10 &&Interfaces.at(i)->Num_lanesGo   == p->Num_lanesBack &&Interfaces.at(i)->Num_lanesBack == p->Num_lanesGo   &&abs(Interfaces.at(i)->Dir - p->Dir) == 180){return Interfaces.at(i)->FlagPoint - p->FlagPoint;}}return QPoint(-99,-99);
}QPointF Scene::searchInterface(QList<Scene*> ListScenes)
{//第一层:我自己的每个接口for(int i = 0; i < Interfaces.size(); ++i){//第二层:每一个场景是否存在和我自己的这个接口相邻的接口for(int j = 0; j < ListScenes.size()-1; ++j){QPointF movement = ListScenes.at(j)->searchInterface(Interfaces.at(i));if(movement.x() != -99){//qDebug()<<"接口"<<i+1<<"和场景"<<j+1<<"相邻。需要移动"<<movement;return movement;}}}return QPoint(-99,-99);
}

2.逻辑连接

  • scene寻找目标接口函数(返回目标接口指针)与道路逻辑连接函数
/****************拼接完成后进行道路连接时寻找目标接口****************/
Interface* Scene::searchTargetInterface(QList<Scene*> scenesMap, Interface* p, int n)
{//遍历场景模块if(n < scenesMap.size()-1){for(int i = n+1; i < scenesMap.size(); ++i){//遍历特定模块的全部接口for(int j = 0; j < scenesMap.at(i)->Interfaces.size(); ++j){//循环接口与目标接口距离接近if( ((scenesMap.at(i)->Interfaces.at(j)->FlagPoint - p->FlagPoint).manhattanLength() < 1)&&(scenesMap.at(i)->Interfaces.at(j) != p)){p->FlagJoined = true;scenesMap.at(i)->Interfaces.at(j)->FlagJoined = true;return scenesMap.at(i)->Interfaces.at(j);}}}}return p; //目标接口不存在则返回循环接口
}/*************拼接完成后将各场景模块的道路建立连接并存入链表NextLane***********/
void Scene::joinLane(QList<Scene*> scenesMap)
{for(int i = 0; i < scenesMap.size()-1; ++i){for(int j = 0; j < scenesMap.at(i)->Interfaces.size(); ++j){scenesMap.at(i)->Interfaces.at(j)->FlagJoined = false;}}//遍历场景模块for(int i = 0; i < scenesMap.size()-1; ++i){//qDebug()<<"开始检索场景"<<i<<"的各接口!";//遍历特定模块的全部接口for(int j = 0; j < scenesMap.at(i)->Interfaces.size(); ++j){//qDebug()<<"  开始检索场景"<<i<<"的接口"<<j<<"!";Interface* CirculateInterface = scenesMap.at(i)->Interfaces.at(j);//循环接口Interface* TargetInterface = searchTargetInterface(scenesMap, CirculateInterface, i);//目标接口//目标接口不存在则跳出本次循环继续遍历其他接口if(CirculateInterface == TargetInterface){continue;}int sum = TargetInterface->Num_lanesGo+TargetInterface->Num_lanesBack;//总车道数//根据接口Num_lanesGo、Num_lanesBack属性连接道路for(int m = 0; m < TargetInterface->Num_lanesGo; ++m){CirculateInterface->Lanes.at(m)->NextLane << TargetInterface->Lanes.at(sum-1-m);}for(int n = TargetInterface->Num_lanesGo; n < sum; ++n){TargetInterface->Lanes.at(sum-1-n)->NextLane << CirculateInterface->Lanes.at(n);}}}
}

难点5 场景模块内部平行道与下一道关系

  • 构造一个connectLanes函数用于初始化接口和道路,并确定内部道路关系(相邻道、下一道)
void Ramp::connectLanes()
{//删除原来可能存在的的Lanesint i;for(i=0; i<Interfaces.size(); ++i){Interfaces.at(i)->Lanes.clear();}/*****初始化Lanes*****/QPointF startPoint;QPointF endPoint;QPointF formerstartPoint;QPointF formerendPoint;QPointF latterstartPoint;QPointF latterendPoint;QPointF rampstartPoint;QPointF rampendPoint;int num_lanes = Num_laneL+Num_laneR; //道路总数QMatrix matrix;         //变换矩阵matrix.rotate(-Dir);    //顺时针旋转-Dir°//接口0对应道路double x0 = -Len_lane/2;double y0 = -(Wid_lane*num_lanes/2 - Wid_lane/2);for(int i=0; i<Num_laneL; ++i){startPoint = QPointF(GlobalPos+QPointF(x0+Len_lane,y0+i*Wid_lane)*matrix);endPoint = QPointF(GlobalPos+QPointF(x0,y0+i*Wid_lane)*matrix);Interfaces.at(0)->Lanes<<new Lane(startPoint,endPoint);}double y = y0 + Wid_lane*Num_laneL;for(int i=0; i<Num_laneR-1; ++i){startPoint = QPointF(GlobalPos+QPointF(x0,y+i*Wid_lane)*matrix);endPoint = QPointF(GlobalPos+QPointF(x0+Len_lane,y+i*Wid_lane)*matrix);Interfaces.at(0)->Lanes<<new Lane(startPoint,endPoint);}//匝道相邻道包含两条lanedouble L = Wid_lane/sin(Angle*M_PI/180); //接口部分长度formerstartPoint = QPointF(GlobalPos+QPointF(x0,y+(Num_laneR-1)*Wid_lane)*matrix);formerendPoint = QPointF(GlobalPos+QPointF(-L/2,y+(Num_laneR-1)*Wid_lane)*matrix);latterstartPoint = QPointF(GlobalPos+QPointF(L/2,y+(Num_laneR-1)*Wid_lane)*matrix);latterendPoint = QPointF(GlobalPos+QPointF(x0+Len_lane,y+(Num_laneR-1)*Wid_lane)*matrix);Interfaces.at(0)->Lanes<<new Lane(formerstartPoint,formerendPoint); //接口0对应前半条道路//接口1对应道路Interfaces.at(1)->Lanes<<new Lane(latterstartPoint,latterendPoint); //接口1对应后半条道路for(int i=Interfaces.at(0)->Lanes.size()-2; i>=0; --i){Interfaces.at(1)->Lanes<<Interfaces.at(0)->Lanes.at(i);}//接口2对应道路if(FlagOut == 0){rampstartPoint = QPointF(GlobalPos+QPointF(-Len_Ramp*cos(Angle*M_PI/180),Len_Ramp*sin(Angle*M_PI/180)+(num_lanes)*Wid_lane/2)*matrix);rampendPoint = QPointF(GlobalPos+QPointF(0,(num_lanes)*Wid_lane/2)*matrix);Interfaces.at(2)->Lanes<<new Lane(rampstartPoint,rampendPoint);}else{rampstartPoint = QPointF(GlobalPos+QPointF(0,(num_lanes)*Wid_lane/2)*matrix);rampendPoint = QPointF(GlobalPos+QPointF(Len_Ramp*cos(Angle*M_PI/180),Len_Ramp*sin(Angle*M_PI/180)+(num_lanes)*Wid_lane/2)*matrix);Interfaces.at(2)->Lanes<<new Lane(rampstartPoint,rampendPoint);}/*****相邻道路关系ParaLane*****///接口0道路ParaLanefor(int i=0; i<num_lanes; ++i){if(i==0){ //最上层直道Interfaces.at(0)->Lanes.at(i)->ParaLane<<Interfaces.at(0)->Lanes.at(i+1);}else if(i==(num_lanes-1)){ //最下层直道Interfaces.at(0)->Lanes.at(i)->ParaLane<<Interfaces.at(0)->Lanes.at(i-1);}else if(i==(num_lanes-2)){ //倒数第二层直道Interfaces.at(0)->Lanes.at(i)->ParaLane<<Interfaces.at(0)->Lanes.at(i-1)<<Interfaces.at(0)->Lanes.at(i+1)<<Interfaces.at(1)->Lanes.at(0);}else{ //其余直道Interfaces.at(0)->Lanes.at(i)->ParaLane<<Interfaces.at(0)->Lanes.at(i-1)<<Interfaces.at(0)->Lanes.at(i+1);}}//接口1道路ParaLanefor(int i=0; i<num_lanes; ++i){if(i==(num_lanes-1)){ //最上层直道Interfaces.at(1)->Lanes.at(i)->ParaLane<<Interfaces.at(1)->Lanes.at(i-1);}else if(i==0){ //最下层直道Interfaces.at(1)->Lanes.at(i)->ParaLane<<Interfaces.at(1)->Lanes.at(i+1);}else if(i==1){ //倒数第二层直道Interfaces.at(1)->Lanes.at(i)->ParaLane<<Interfaces.at(1)->Lanes.at(i+1)<<Interfaces.at(1)->Lanes.at(i-1)<<Interfaces.at(0)->Lanes.at(num_lanes-1);}else{ //其余直道Interfaces.at(0)->Lanes.at(i)->ParaLane<<Interfaces.at(0)->Lanes.at(i+1)<<Interfaces.at(0)->Lanes.at(i-1);}}/*****匝道内部连接关系NextLane*****/if(FlagOut == 0){Interfaces.at(0)->Lanes.at(num_lanes-1)->NextLane<<Interfaces.at(1)->Lanes.at(0);Interfaces.at(2)->Lanes.at(0)->NextLane<<Interfaces.at(1)->Lanes.at(0);}else {Interfaces.at(0)->Lanes.at(num_lanes-1)->NextLane<<Interfaces.at(1)->Lanes.at(0)<<Interfaces.at(2)->Lanes.at(0);}
}
  • paintRoad函数中刷新接口点与每条车道中线位置(已完善,无需此部分代码)
/*****刷新接口点*****/if(FlagOut == 0){Interfaces.at(0)->refreshFlagPos(QPointF(GlobalPos+QPointF(-Len_lane/2,0)*matrix));Interfaces.at(1)->refreshFlagPos(QPointF(GlobalPos+QPointF(Len_lane/2,0)*matrix));Interfaces.at(2)->refreshFlagPos(QPointF(GlobalPos+QPointF(-Len_Ramp*cos(Angle*pi/180),Len_Ramp*sin(Angle*pi/180)+(Num_lanes)*Wid_lane/2)*matrix));}else {Interfaces.at(0)->refreshFlagPos(QPointF(GlobalPos+QPointF(-Len_lane/2,0)*matrix));Interfaces.at(1)->refreshFlagPos(QPointF(GlobalPos+QPointF(Len_lane/2,0)*matrix));Interfaces.at(2)->refreshFlagPos(QPointF(GlobalPos+QPointF(Len_Ramp*cos(Angle*pi/180),Len_Ramp*sin(Angle*pi/180)+(Num_lanes)*Wid_lane/2)*matrix));}/*****刷新车道中线对象*****/QPointF interfacePoint0 = Interfaces.at(0)->FlagPoint;QPointF interfacePoint1 = Interfaces.at(1)->FlagPoint;QPointF interfacePoint2 = Interfaces.at(2)->FlagPoint;QPointF startPoint;QPointF endPoint;QPointF movement;QPointF dMovement = QPointF(0,Wid_lane);QPointF formerstartPoint;QPointF formerendPoint;QPointF latterstartPoint;QPointF latterendPoint;QPointF rampstartPoint;QPointF rampendPoint;//直道部分if(Num_lanes % 2 == 1){movement = QPointF(0,-(Num_lanes-1)/2*Wid_lane);//关键1***********for(int i=0; i<Num_laneL; ++i){startPoint = (interfacePoint1 + movement*matrix);endPoint = (interfacePoint0 + movement*matrix);Interfaces.at(0)->Lanes.at(i)->refreshPoints(startPoint,endPoint);movement += dMovement;}for(int i=Num_laneL; i<Num_lanes-1; ++i){startPoint = (interfacePoint0 + movement*matrix);endPoint = (interfacePoint1 + movement*matrix);Interfaces.at(0)->Lanes.at(i)->refreshPoints(startPoint,endPoint);movement += dMovement;}double L = Wid_lane/sin(Angle*pi/180); //接口部分长度formerstartPoint = (interfacePoint0 + movement*matrix);formerendPoint = (interfacePoint0 + (movement+QPointF((Len_lane-L)/2,0))*matrix);Interfaces.at(0)->Lanes.at(Num_lanes-1)->refreshPoints(formerstartPoint,formerendPoint);latterstartPoint = (interfacePoint1 + (movement+QPointF(-(Len_lane-L)/2,0))*matrix);latterendPoint = (interfacePoint1 + movement*matrix);Interfaces.at(1)->Lanes.at(0)->refreshPoints(latterstartPoint,latterendPoint);}else{movement = QPointF(0,-(Num_lanes/2-0.5)*Wid_lane);//关键2***********for(int i=0; i<Num_laneL; ++i){startPoint = (interfacePoint1 + movement*matrix);endPoint = (interfacePoint0 + movement*matrix);Interfaces.at(0)->Lanes.at(i)->refreshPoints(startPoint,endPoint);movement += dMovement;}for(int i=Num_laneL; i<Num_lanes-1; ++i){startPoint = (interfacePoint0 + movement*matrix);endPoint = (interfacePoint1 + movement*matrix);Interfaces.at(0)->Lanes.at(i)->refreshPoints(startPoint,endPoint);movement += dMovement;}double L = Wid_lane/sin(Angle*pi/180); //接口部分长度formerstartPoint = (interfacePoint0 + movement*matrix);formerendPoint = (interfacePoint0 + (movement+QPointF((Len_lane-L)/2,0))*matrix);Interfaces.at(0)->Lanes.at(Num_lanes-1)->refreshPoints(formerstartPoint,formerendPoint);latterstartPoint = (interfacePoint1 + (movement+QPointF(-(Len_lane-L)/2,0))*matrix);latterendPoint = (interfacePoint1 + movement*matrix);Interfaces.at(1)->Lanes.at(0)->refreshPoints(latterstartPoint,latterendPoint);}//匝道部分if(FlagOut == 0){rampstartPoint = interfacePoint2;rampendPoint = (interfacePoint2 + QPointF(Len_Ramp*cos(Angle*pi/180),-Len_Ramp*sin(Angle*pi/180)));Interfaces.at(2)->Lanes.at(0)->refreshPoints(rampstartPoint,rampendPoint);}else {rampstartPoint = (interfacePoint2 + QPointF(-Len_Ramp*cos(Angle*pi/180),-Len_Ramp*sin(Angle*pi/180)));rampendPoint = interfacePoint2;Interfaces.at(2)->Lanes.at(0)->refreshPoints(rampstartPoint,rampendPoint);}
  • 绘制车道中心线进行验证
//绘制车道中心线(红点蓝线验证)for(int i=0; i<Interfaces.at(0)->Lanes.size()-1; ++i){painter.setPen(QPen(Qt::blue, 0.1/scale));startPoint = toWidget(Interfaces.at(0)->Lanes.at(i)->StartPoint,mapOrigin);endPoint = toWidget(Interfaces.at(0)->Lanes.at(i)->EndPoint,mapOrigin);painter.drawLine(startPoint,endPoint);painter.setPen(QPen(Qt::red, 1/scale));painter.drawPoint(startPoint);}painter.setPen(QPen(Qt::blue, 0.1/scale));formerstartPoint = toWidget(Interfaces.at(0)->Lanes.at(Num_lanes-1)->StartPoint,mapOrigin);formerendPoint = toWidget(Interfaces.at(0)->Lanes.at(Num_lanes-1)->EndPoint,mapOrigin);painter.drawLine(formerstartPoint,formerendPoint);latterstartPoint = toWidget(Interfaces.at(1)->Lanes.at(0)->StartPoint,mapOrigin);latterendPoint = toWidget(Interfaces.at(1)->Lanes.at(0)->EndPoint,mapOrigin);painter.drawLine(latterstartPoint,latterendPoint);rampstartPoint = toWidget(Interfaces.at(2)->Lanes.at(0)->StartPoint,mapOrigin);rampendPoint = toWidget(Interfaces.at(2)->Lanes.at(0)->EndPoint,mapOrigin);painter.drawLine(rampstartPoint,rampendPoint);painter.setPen(QPen(Qt::red, 1/scale));painter.drawPoint(formerstartPoint);painter.drawPoint(latterstartPoint);painter.drawPoint(rampstartPoint);

完善:无需将刷新接口点与刷新车道中线对象重复编写,构造一个refreshInterfacePoint函数用于刷新接口点,其中利用refreshInterface函数刷新接口的FlagPoint和Dir参数。

void Ramp::refreshInterfacePoint()
{//刷新接口点QMatrix matrix;         //变换矩阵matrix.rotate(-Dir);    //顺时针旋转-Dir°int Num_lanes = Num_laneL + Num_laneR;//总车道数const float pi = 3.1415926535879;//定义常量pi/*****刷新接口点*****/if(FlagOut == 0){Interfaces.at(0)->refreshInterface(QPointF(GlobalPos+QPointF(-Len_lane/2,0)*matrix),Dir);Interfaces.at(1)->refreshInterface(QPointF(GlobalPos+QPointF(Len_lane/2,0)*matrix),Dir+180);Interfaces.at(2)->refreshInterface(QPointF(GlobalPos+QPointF(-Len_Ramp*cos(Angle*pi/180),Len_Ramp*sin(Angle*pi/180)+(Num_lanes)*Wid_lane/2)*matrix),Dir+Angle);}else {Interfaces.at(0)->refreshInterface(QPointF(GlobalPos+QPointF(-Len_lane/2,0)*matrix), Dir);Interfaces.at(1)->refreshInterface(QPointF(GlobalPos+QPointF(Len_lane/2,0)*matrix), Dir+180);Interfaces.at(2)->refreshInterface(QPointF(GlobalPos+QPointF(Len_Ramp*cos(Angle*pi/180),Len_Ramp*sin(Angle*pi/180)+(Num_lanes)*Wid_lane/2)*matrix),Dir+180-Angle);}
}

2.车辆行为的操控与监测

本仿真平台的车辆类型有两种:主车与环境车。
1.主车:定义主车运动轨迹时,由用户设置起始点、下一点和结束点,根据用户所标点生成轨迹路线,主车按照此轨迹行驶,用户也可以实时控制<油门><刹车>来实现加减速。
2.环境车:地图拼接完成后,需要确定整体场景道路的逻辑连接。在每个接口处初始化车道中线的对象,定义行驶路线的起点和终点,并建立场景内部与之间道路的连接关系,环境车即可依据车道中线行驶。需要注意到:匝道衔接处的轨迹应根据车辆运动参数生成;弯道中直线与弧线相接部分从属于同一条车道中线;十字路口处的下一道有多种选择,具体行车轨迹可由车辆周围情况确定。
同时,为了模拟出真实车流场景,通过随机指令函数控制车辆的行驶状态。车辆可以继续沿已生成的车道中线行驶,也可以选择变动到相邻道、特定位置调转方向、并入或并出匝道、弯道弧线处超车、十字路口多方位汇入等。其中各种行驶状态应赋予合适的触发条件和概率,以最大限度地还原仿真。
由随机指令函数控制的行驶状态都涉及到行驶道路的变化,需要统一的规则加以约束。根据当前所在车道、目标车道和车辆的运动参数、周围环境,寻找形成最佳轨迹多对应的变道终点。

难点1 地图与车辆的“编译”(仿真模式前期准备)
1.地图编译:创建车道并建立场景内部车道连接,建立场景间车道连接。
直接调用各场景中的 connectLanes() 和 scene 中的 joinLane() 即可。
2.车辆编译:ListVehicle ListThread 用于存储每辆车的对象和线程
a.主车:创建主车需要用户绘制的道路 ListPath 作为参数,随后建立主车发车连接 mainVehicle() 与结束连接 Signal_StatuesChange() 。
b.环境车:创建环境车需要车辆ID numPoints+1 作为参数,numPoints代表车道被选过的次数。
其中环境车发车位置的选取十分关键,需要根据总可选车道数 num_Lanes 和某一车道是否被选过 Num_ChoisedPoints 进行分散性的发车,同时还要利用 random() 随机选取发车点在某一车道上的位置,从而达到良好的视觉效果。随后利用场景、接口、道路建立的临时指针建立环境车发车连接 viceVehicle() 与结束连接/开启新一辆环境车 startNewViceVehicle() 。

void MapCanvas::Slot_Compile(){if(FlagAmICurrentPage && !ListPath.empty()){/****************************地图部分编译**************************/// 创建车道并建立场景内部车道连接for(int i=0; i<ListScenes.size(); ++i){Scene* scene0 = ListScenes.at(i);switch (scene0->SceneType){case Scene::Straight:{Straight* scene = (Straight*)scene0;scene->connectLanes();break;}case Scene::Cross:{Cross* scene = (Cross*)scene0;scene->connectLanes();break;}case Scene::Curve:{Curve* scene = (Curve*)scene0;scene->connectLanes();break;}case Scene::Ramp:{Ramp* scene = (Ramp*)scene0;scene->connectLanes();break;}}}//建立场景间车道连接Scene::joinLane(ListScenes);/**************************地图部分编译完成*************************//****************************车辆部分编译**************************/// 开始创建车辆ListVehicle.clear();ListThread.clear();// 创建主车ListVehicle << new Vehicle(ListPath.at(0));ListThread << new QThread;ListVehicle.at(0)->moveToThread(ListThread.at(0));// 建立主车发车连接connect(this, SIGNAL(startSimulation()), ListVehicle.at(0), SLOT(mainVehicle()));// 建立主车结束连接connect(ListVehicle.at(0), &Vehicle::simulationDone, this, [=](){ListThread.at(0)->exit();ListThread.at(0)->wait();for(int i=0; i<ListVehicle.size(); ++i){ListVehicle.at(i)->FlagSimulating = false;}timer->stop();FlagVehicleGoing = false;FlagSimulating = false;FlagCompiled = false;emit Signal_StatuesChange();qDebug()<<"仿真完成";update();});// 创建环境车if(!ListScenes.empty()){qDebug()<<"开始放置环境车";Scene* sceneRandom;Interface* interfaceRandom;Lane* laneRandom;QPointF pointRandom;int i;int j;int n;int num_Lanes = 0;// 可发车车道计数for(i=0; i<ListScenes.size(); ++i){ //第i个场景for(j=0; j<ListScenes.at(i)->Interfaces.size(); ++j){   //第i个场景的第j个接口for(n=0; n<ListScenes.at(i)->Interfaces.at(j)->Lanes.size(); ++n){   //第i个场景的第j个接口的第n个if(!ListScenes.at(i)->Interfaces.at(j)->Lanes.at(n)->FlagCounted){ListScenes.at(i)->Interfaces.at(j)->Lanes.at(n)->FlagCounted = true;ListScenes.at(i)->Interfaces.at(j)->Lanes.at(n)->Num_ChoisedPoints = 0;num_Lanes ++;}}}}//qDebug()<<"完成车道计数!!";// 恢复计数标记for(i=0; i<ListScenes.size(); ++i){ //第i个场景for(j=0; j<ListScenes.at(i)->Interfaces.size(); ++j){   //第i个场景的第j个接口for(n=0; n<ListScenes.at(i)->Interfaces.at(j)->Lanes.size(); ++n){   //第i个场景的第j个接口的第n个ListScenes.at(i)->Interfaces.at(j)->Lanes.at(n)->FlagCounted = false;}}}//qDebug()<<"完成车道计数恢复!";//*******!!!!!!!!!!!!此处似乎有Bug!!!!!!!!!!!!!*******qDebug()<<"场景总可发车车道数:"<<num_Lanes;int numPoints = 0;while(numPoints<numOfViceVehicle) {sceneRandom = ListScenes.at(random(ListScenes.size()));interfaceRandom = sceneRandom->Interfaces.at(random(sceneRandom->Interfaces.size()));laneRandom = interfaceRandom->Lanes.at(random(interfaceRandom->Lanes.size()));if(numPoints/num_Lanes == laneRandom->Num_ChoisedPoints){qDebug()<<"尝试新建环境车:"<<numPoints+1;pointRandom = laneRandom->StartPoint + (laneRandom->EndPoint-laneRandom->StartPoint)*random(101)/100.0;ListVehicle<<new Vehicle(numPoints+1);ListThread<<new QThread;ListVehicle.at(numPoints+1)->moveToThread(ListThread.at(numPoints+1));// 建立环境车发车连接connect(this, &MapCanvas::startSimulation, ListVehicle.at(numPoints+1), [=](){ListVehicle.at(numPoints+1)->viceVehicle(laneRandom, pointRandom);});// 建立环境车结束连接connect(ListVehicle.at(numPoints+1), &Vehicle::viceVehicleFinish, this, [=](){// 关闭已离开的环境车的线程qDebug()<<"环境车"<<numPoints+1<<"离开地图";ListThread.at(numPoints+1)->exit();ListThread.at(numPoints+1)->wait();startNewViceVehicle();});connect(ListVehicle.at(numPoints+1), &Vehicle::simulationDone, this, [=](){ListThread.at(numPoints+1)->exit();ListThread.at(numPoints+1)->wait();});qDebug()<<"新建环境车:"<<numPoints+1<<"成功!"<<ListVehicle.size();laneRandom->Num_ChoisedPoints++;numPoints++;}}}/**************************车辆部分编译完成*************************/FlagCompiled = true;  //已编译成功标记emit Signal_CompileSuccess();}
}

为了使地图上的环境车数量保持动态平衡,需要一辆车消失后生成一辆新的环境车。

void MapCanvas::startNewViceVehicle()
{// 建立新环境车和新线程Vehicle* tempVehicle = new Vehicle(ListVehicle.size()+1);QThread* tempThread = new QThread;tempVehicle->moveToThread(tempThread);ListVehicle << tempVehicle;ListThread << tempThread;// 选定新车发车位置Scene* sceneRandom;Interface* interfaceRandom;Lane* laneRandom;sceneRandom = ListScenes.at(random(ListScenes.size()));interfaceRandom = sceneRandom->Interfaces.at(random(sceneRandom->Interfaces.size()));while(interfaceRandom->FlagJoined){sceneRandom = ListScenes.at(random(ListScenes.size()));interfaceRandom = sceneRandom->Interfaces.at(random(sceneRandom->Interfaces.size()));}int numLanesBack = interfaceRandom->Num_lanesBack;laneRandom = interfaceRandom->Lanes.at(numLanesBack+random(interfaceRandom->Lanes.size()-numLanesBack));// 建立新车发车连接connect(this, &MapCanvas::startViceVehicle, tempVehicle, [=](int id){if(id == tempVehicle->vehicleID){qDebug()<<"环境车"<<tempVehicle->vehicleID<<"接替发车";tempVehicle->viceVehicle(laneRandom, laneRandom->StartPoint);}});// 建立新车结束连接connect(tempVehicle, &Vehicle::viceVehicleFinish, this, [=](){tempThread->exit();tempThread->wait();qDebug()<<"环境车"<<tempVehicle->vehicleID<<"离开地图";startNewViceVehicle();});connect(tempVehicle, &Vehicle::simulationDone, this, [=](){tempThread->exit();tempThread->wait();});emit startViceVehicle(ListVehicle.size());ListThread.last()->start();}void MapCanvas::Slot_StartSimulate()
{if(FlagAmICurrentPage && FlagCompiled){if(!ListThread.at(0)->isRunning()){qDebug()<<"开始仿真";emit startSimulation();FlagSimulating = true;FlagVehicleGoing = true;for(int i=0; i<ListVehicle.size(); ++i){ListVehicle.at(i)->FlagSimulating = true;ListThread.at(i)->start();}timer->start();emit Signal_StatuesChange();}else if(FlagSimulating){qDebug()<<"继续仿真";timer->start();for(int i=0; i<ListVehicle.size(); ++i){ListVehicle.at(i)->FlagSuspend = false;}FlagVehicleGoing = true;emit Signal_StatuesChange();}}
}

基于QT的多场景机动车防碰撞算法仿真测试平台相关推荐

  1. matlab的svpwm逆变器,基于matlab的SVPWM逆变器死区补偿算法仿真研究

    SURVEY LE 即巧年10月一第7期一.一.一一 基于madab的SVPWM逆变器死区补偿算法仿真研究 长春工业大学于海芳柏博旭刘龙高胜昌 摘要:为解决爳M逆变器死区效应问题,本文提出了冖种基于坐 ...

  2. 移相全桥 matlab,基于Matlab的移相全桥变换器仿真实验平台设计

    基于Matlab的移相全桥变换器仿真实验平台设计 所属分类:matlab例程 开发工具:Others 文件大小:1206KB 下载次数:14 上传日期:2019-04-21 14:18:51 上 传 ...

  3. 基于Qt框架实现的 C/S架构的外卖平台软件系统

    资源下载地址:https://download.csdn.net/download/sheziqiong/85628215 基于Qt框架实现的 C_S架构的外卖平台软件系统 演示视频 C/S 架构的外 ...

  4. 基于MATLAB的路面裂缝检测识别算法仿真

    目录 1.算法仿真效果 2.MATLAB源码 3.算法概述 4.部分参考文献 1.算法仿真效果 matlab2022a仿真结果如下: 2.MATLAB源码 %********************

  5. 基于QT和Node.js的八叉树算法提取图片主题色

    资源下载地址:https://download.csdn.net/download/sheziqiong/85883609 资源下载地址:https://download.csdn.net/downl ...

  6. 【RFID】基于MATLAB的RFID 系统的空中接口过程以及防碰撞算法仿真

    1.软件版本 matlab2013b 2.本算法理论知识 RFID的接口过程满足如下的结构框图: 3.核心代码 clc; %清屏 close all; %关闭所有窗口 clear all; %清除所有 ...

  7. 基于MATLAB的leach协议能量检测算法仿真

    目录 1.算法概述 2.仿真效果 3.MATLAB仿真源码 1.算法概述 LEACH协议,全称是"低功耗自适应集簇分层型协议" (Low Energy Adaptive Clust ...

  8. 基于matlab的低秩结构重构算法仿真实现,对比ALM,IT,APG,ADMM

    目录 一.理论基础 二.核心程序 三.测试结果 一.理论基础 从数学上讲, 矩阵的秩反应了矩阵的固有属性, 矩阵的低秩性是指矩阵的秩相对于矩阵的行数和列数而言很小.低秩矩阵稀疏分解模型是将已知矩阵M ...

  9. 基于MATLAB图像harris角点提取算法仿真

    figure; ori_im2 = imread('ucsb1.jpg');     % 读取图像 ori_im2=rgb2gray(ori_im2);  fx = [6 0 -6;8 0 -8;6 ...

最新文章

  1. html流程图可新增删除,CSS / HTML流程图
  2. Google开源OCR项目Tesseract安装版在Windows下的使用测试记录
  3. mysql错误:file xxx conflicts with file from package mysql-libs-5.5.68-1.el7.x86_64
  4. SwiftUI之深入解析如何创建和组合视图
  5. npm run 脚本背后的事情
  6. Hexo 博客添加 README.md 以及部署到 GitHub 丢失/显示不正常解决方法
  7. 论文阅读 - AUTOVC: Zero-Shot Voice Style Transfer with Only Autoencoder Loss
  8. 机器学习回归算法—性能评估欠拟合与过拟合
  9. 这几个冷门却实用的 Python 库,我爱了!
  10. android学习一---搭建开发环境
  11. mysql ssh .net_c# – 使用SSH.NET库与MySQL建立SSH连接
  12. 服务器如何关闭登录日志文件,linux云服务器登录日志文件
  13. seci-log 开源日志分析软件发布了
  14. google ble 语音spec(voiceverBLERemotecontrol)
  15. 双目视觉三维重构(一)————简介
  16. 机器学习-*-K均值聚类及代码实现
  17. HDU 2094 产生冠军
  18. 频响测试低12dB问题
  19. 机器学习-贝叶斯分类
  20. k8s之PV以及PVC

热门文章

  1. Opencv 6种模板匹配
  2. 36条人情世故 +8式大学法则+10招助=成功推进器-助你三十而立
  3. Android 绘制产生重影(重叠)
  4. vue项目调用百度地图api 学习总结
  5. 两小时快速搭建微人大成绩单监测系统
  6. 酒店数字化转型,就从这4步开始
  7. 三星e210l android5.0,[合作版]MIUI V5_三星S3 E210L_4.8.8
  8. redis模糊删除Key
  9. 三相半波可控整流电路
  10. 淘宝运营:26天营业额翻15倍!从3000/天到45000/天