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

基于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;//画第一条匝道边线的画家的退回距离return
    const 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,出取1
          double  dir,        //场景方向初始化为0
          double  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()
{
    //删除原来可能存在的的Lanes
    int 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);
    }
    //匝道相邻道包含两条lane
    double 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道路ParaLane
    for(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道路ParaLane
    for(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();
        }
    }
}
  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值