毕业设计思路篇(五)之交通流量模拟

前面几篇都是在搭台,这篇才是车真正在路上跑起来。

思路篇(四) 把车放进各条路的 m_queVehicle 之后,仿真进入 runSimulation(Graph &G)。外层按道路遍历,内层按该路上的车辆队列逐个处理;只有 it.time < SYSTEM_TIME 的车才参与本步计算,相当于用离散时间片驱动。车速会扣道路拥堵度,车开到路尽头要进路口,问 思路篇(二) 里初始化好的 TrafficLight 能不能过——这套逻辑是整份毕业设计里最绕、也最容易出 bug 的一段。


runSimulation(Graph &G)

1. 遍历每条道路

每一轮仿真扫一遍 G.m_Road_v。对单条路,先把 m_queVehicle 拷到临时队列 src,处理完再写回 obj,避免边遍历边 push_back 搞乱迭代器。路内没有车时循环自然跳过。

2. 遍历该道路的车辆

src 队首弹出一辆车,根据时间戳决定是否更新状态;处理完的车要么回到本路 obj,要么换路到别条路的队列,要么在路口等灯后塞回 obj

a. 计算特定时间间隔后的位置

本步先算速度 fSpec(100 - road.get_Congestion() - 20) / 3.6,再算十秒(代码里时间步长与 * 10 配套)后的位移 dist = it.dDistance + it.fSpec * 10,同时 it.time++。拥堵越高速度越低,是我当时简化的宏观模型,没做跟驰微观仿真。

b. 若应行驶至其他道路

dist >= road.m_dLength 表示本时间片内车已经开到路尽头,要尝试进入下一段路。先拷贝 it.queRoutepop() 掉已走完的当前路 id;若队列空了说明到终点,演示代码里直接 exit(0);否则 next = route.front() 是下一条道路 id。

此时车逻辑上在路口,对应路口下标存在 it.m_nSiteRoadID(与道路终点路口相关)。先对该路口灯 clock(SYSTEM_TIME) 推进相位,再 getStatus(it.m_nSiteRoadID, next)——注意这里 from 是当前路 id、to 是下一条路 id,和 思路篇(二)roadID 约定一致。

若能通行,则填至目标道路

若不能,则继续停留在路口缓冲区

绿灯:queRoute 更新为 pop 后的队列,距离清零,m_nSiteRoadID 改成 next,车 push_backG.m_Road_v[next].m_queVehicle。红灯:把 dDistance 设为当前路长度(表示堵在路末端等),车放回本路 obj,下一时间片再试。

c. 若仍停留在原道路

dist 没到路长,说明还在本段路上跑,更新 it.dDistance = dist 后放回 obj。时间戳尚未到的车原样 push_backobj,等全局时间追上来再动。

  for (auto &road:G.m_Road_v) {

        auto src = road.m_queVehicle;

        decltype(road.m_queVehicle) obj;

        //路内车的遍历

        while (!src.empty()) {

            //弹出一辆车

            auto it = src.front();

            src.pop_front();

            // 当车的时间戳小于实际时间时,才模拟运行

            if (it.time < SYSTEM_TIME) {

                it.fSpec = (100 - road.get_Congestion() - 20) / 3.6;

                dist = it.dDistance + it.fSpec * 10;

                it.time++;



                it.showself();

                //如果车十秒后不在此路

                if (dist >= road.m_dLength) {

                    //路径擦除

                    auto route = it.queRoute;

                    int site = it.m_nSiteRoadID;

                    route.pop();

                    //如果抵达终点

                    if (route.empty()) {

                        cout << "it is be shutdown" << endl;

                        exit(0);

                        // 否侧没有抵达终点

                    } else {

                        //获取所在路和下一条路的ID

                        int next = route.front();

                        //判断红绿灯情况

                        cout << site << endl;

                        G.m_CrossRoad_v[site].m_CTrafficLight_Light.clock(SYSTEM_TIME);

                        //如果可以通行

                        if (G.m_CrossRoad_v[site].m_CTrafficLight_Light.getStatus(it.m_nSiteRoadID, next)) {

                            cout << GREEN << "绿灯通行:" << endl;

                            it.queRoute = route;

                            it.dDistance = 0;

                            it.m_nSiteRoadID = next;

                            auto *site_road = &G.m_Road_v[next].m_queVehicle;

                            site_road->push_back(it);

                            //如果不能通行

                        } else {

                            //将距离置为道路长度,表示正在等候红灯

                            it.dDistance = G.m_Road_v[it.m_nSiteRoadID].m_dLength;

                            cout << YELLOW << "等待红灯" << endl;

                            //车辆塞回去

                            obj.push_back(it);

                        }

                    }

                    //否则,当车十秒后还在此路时

                } else {

                    it.dDistance = dist;

                    obj.push_back(it);

                }

                //否则直接填入

            } else {

                obj.push_back(it);

            }

        }

        road.m_queVehicle = obj;

    }

调试时我主要靠 showself 和彩色 cout 看每步是绿灯还是等红灯。常见坑:换路后车已经进 m_Road_v[next].m_queVehicle,但本轮外层还在遍历原 road,所以必须用 src/obj 双队列,不能把正在换路的车和本路剩余车搅在一起。另一个坑是 m_nSiteRoadID 和路口下标混用——getStatus 第一个参数实际是道路 id,命名当时写得有点误导,对照 思路篇(二)roadID 表查才对上。

系列主流程到这里闭环。路线从哪来、图怎么建,分别回看 番外篇汇总篇

版权声明: 本文首发于 指尖魔法屋-毕业设计思路篇(五)之交通流量模拟https://blog.thinkmoon.cn/post/145-graduation-design-traffic-notes-cplusplus/) 转载或引用必须申明原指尖魔法屋来源及源地址!