基于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 场景模块设置参数(以匝道为例)
- 右键弹出选项条目(地图与道路不同)
- 在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()));
- 绘制参数设置界面
- 手动布局ui界面,注意修改控件的名称(可与变量名一致便于调用)和参数(最大值、小数点)
- 传递场景模块参数
- 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();
}
}
}