基于unity交通仿真软件开发

基于unity交通仿真软件开发

摘要:基于Unity实现交通系统仿真功能,开发一电脑端与安卓手机端软件,设计了一个包含道路、车辆和交通信号灯等元素的场景,确保车辆按照规定的交通规则行驶,实现了最基本、最核心的固定周期信号灯变换和车量运行逻辑功能。对该软件的架构设计、技术细节、应用前景进行了讨论,并提出了一些改进和扩展的方向,以进一步提升系统的功能和性能。

关键词:信号灯、车辆运行、UI、数据保存

1背景

交通仿真是一种基于计算机模拟的方法,通过建立交通系统的数学模型和仿真环境,模拟和分析交通流量、车辆行为、道路网络等因素对交通系统运行的影响。通过对不同交通策略和控制方法的仿真实验,可以评估其对交通系统性能的影响,并提供优化方案。

交通仿真背后的核心理论包括车辆行为模型、交通流模型和道路网络模型等。车辆行为模型描述了车辆在不同情况下的加速、减速、换道等行为规则。交通流模型则描述了车辆在道路上的流动规律,包括车辆密度、速度和流量等指标。道路网络模型则描述了道路网络的拓扑结构和连接关系。

通过交通仿真,我们可以模拟不同交通场景下的交通流量、交通拥堵、交通事故等情况,并评估不同交通策略的效果。这些信息可以帮助交通规划者和决策者做出科学合理的决策,优化交通系统的运行效率,提高交通安全性。

2 制作过程

2.1 素材准备  

2.1.1 需要资源

 网格(fbx),材质(material),贴图(textyre),预制体(prefab),粒子特效(particle),特定插件从UnityStore下载和使用专业软件进行处理。

2.1.2 完整开发流程使用软件

用PS 处理图片,提供贴图。

用Blender或3Dmax等建模软件,使用自带着色器算法对贴图和颜色进行处理,为模型添加材质,设置合适的模型原点坐标,将方向设为Y轴向上后,导出为fbx格式到unity。导入过程出现贴图丢失属于正常现象,unity不支持内嵌贴图,先将fbx导入,再将对应texture导入同一文件夹下。

Vistual Studio与unity关联,用于更改插件,编程。

用unity编辑粒子特效,发布exe,打包apk。

2.2 建立模型

为了节约时间成本,降低电脑GPU渲染压力,提升程序运行性能。本研究信号灯等模型采用unity自带原生物体(圆柱、正方体、球体……)简单缩放旋转后建模,然后添加材质,其它均从Unitystore下载。未严格按照实际模型比例模型精确到0.01m并在专业工业建模软件UG、solidworks建模,未从blender,3dmax等建模软件建模。

采用非写实建模风格。

2.2.1信号灯

整体结构:

 默认情况下Gleftturn不显示。

左转:

采用4个长方体和1个圆柱组成来表示左转信号。用该物体显示与隐藏表示亮灭,不采用点光源阵列来表示是为了节约性能。

  

2.2.2道路

在unity中为平面(qued),添加贴图,设置图片拉伸效果tiling。

2.2.3 车辆、建筑、花草树木、路灯……

从Unitystore导入。

2.2.4 组合

y轴基准高度均设成0。

              

2.3基本和核心功能

2.3.1十字路口路口信号灯

交通规则:1绿灯时间+黄灯时间+红灯时间=周期。

          2同一个红绿灯直行与左转的周期相同。

2.3.1.1 直行信号灯

思路:为每一个方向的信号灯赋予给一个数字,设置参数Whichlight,x-、x+、z-、z+方向信号灯挂载脚本设置0、1、2、3,进行区分,如果不添加存储功能次参数无效。设置延迟时间,用Invoke函数嵌套进行循环,周期性改变参数i,i的值意义0为绿,1为黄,2为红,改变颜色模拟灯的亮灭。一个路口4个方向信号灯不存在关联,独立运行。

2.3.1.2 左转信号灯

同理,用隐藏显示模拟灯达到亮灭。

2.3.1.3 参数表

见源程序

2.3.2  T字路口信号灯

同理十字路口,适当删减,改变参数。

2.3.3 车辆

最核心的功能,每辆车相当于小的智能体。

2.3.3.1 直行车辆生成功能

在道路上选择0-n个点作为生成点位,采用Instantitate函数生成预制体,同时初始化车辆参数。

在生成点位旁加基于碰撞检测的范围检测,在有车占用范围内时段不生成。

2.3.3.2 直行车辆遇到红灯停车

方法1:车辆获取最近路口对应方向信号灯参数i判断信号状态。如果无人驾驶普及,路口可以只放一个信号发生器,不需要灯,让车辆智能系统判断。

方法2:判断该方向最近信号灯的颜色,本研究采用此方法。

车辆通过查询标签(tag)方式寻找最近路口。

2.3.3.3直行车辆避让前方直行车辆减速

采用射线检测返回前车距离,小于临界值进行减速直到速度为0。

2.3.3.4 直行车辆加速

周期性采用射线检测返回前方车距离,距离减少进行加速直至速度为最大值。

2.3.3.5 直行车辆避让左转车辆停车

采用射线检测返回前车距离,小于临界值进行减速直到速度为0。可以加一定偏移量,本研究为了方便调试,偏移量设为0。

2.3.3.6 直行车辆销毁

判断与最近路口的距离,超出边界范围后销毁。

2.3.3.6 左转车辆红灯停车

左转车辆设为循环动画,停车原理同直行车辆。

2.3.3.7 左转车辆避让直行车辆

基于碰撞检测的范围检测,左转车辆在左转过程中,范围内存在直行车辆时停止运动。

2.4绿波带仿真

拼接多个路口,每个路口设置成相同周期,设置不同的延迟时间,错开的时差用路口距离除车速度计算。

2.5 UI设计

以上所有仿真功能在unity中均可正常运行。为了方便用户体验获得直观感受,增加添加部分模型功能,调整部分参数功能。UI的设计原则是不同界面之间不干涉,基于项目自身功能进行设置,具有不可借鉴性。UI有固定程序、界面模板,根据实际需求改变内容与程序框架。

2.6 数据保存

     类中声明属性,将各个参数转为列表。采用序列化,反序列化的方对列表进行读写。电脑端用Newtonsoft.json插件将列表数据保存到路径Streaming Asset中json文件下。安卓手机端使用ios插件保存到路径Application.persistentDataPath + "/SaveData.json"

2.7 Camera设置

SRcamare插件导入设置参数。

3 改进思路

曲线道路的建模,车辆沿着曲线道路行驶。实际往往只存在连续3-4个直线十字路口。现实中没有笔直的的道路,不会都是是井子形状设计。

车辆变道功能实现,没有任何一辆车在起始点与终点间不转弯,不会只在直行左转车道上。

4 发展前景

     如果有验证信号灯算法的需求,本程序可以导入信号灯算法,调整算法参数,更改车流量,用最短时间,最直观的方式,多种测试来展现结果。比实际路口连续多天实验时间不断调整参数时间成本短。

5其他说明

   上传的源程序中脚本GYRlight1,TurnLeftLight1中lighttime[0]、Turnlefttime[0]没有减3。上传不易,哎嘿,特此声明一下。

   如果设置自适应算法,根据车流量改变信号灯分配时长。参考该代码,把源码中GYRlight1更新成这个代码,设置一下碰撞体的范围就可以了。不设置范围没有影响,意味着没有开启调节算法。一个路口只设置红绿灯杆x0中的直行就可以,不需要4个方向都设置。算法可以根据需要自行调整。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class GYRlight1 : MonoBehaviour
{
    //传值x00x11
    public int whichlight;

    [Tooltip("延迟开始")]
    public float delaytime;

    [Tooltip("第几个灯")]
    public int i;

    [Tooltip("灯的秒数的数组")]
    public int[] Lighttime = new int[] {4,1,5};


    //[Tooltip("判断是否为X方向")]
    //public bool x=false;

    public static GYRlight1 instance;

    //[Tooltip("汽车点位")]
    //public Transform[] carCreatpoints;

    [Tooltip("灯的颜色初始化设置")]
    public int  initialsetting;

    
    public int T,gt,rt;
    [Tooltip("碰撞检测范围")]
    public Vector3 offset, CheckCarNumsize ;

    [Tooltip("参数改变的脚本引用")]
    public GYRlight1 x0, x1, z0, z1;
    public TurnLeftLight1 leftx0, leftx1, leftz0, leftz1;

    RYBpoleBase rYBpoleBase;
    private RYBpole RYBpole;
    private void Awake()
    {
        instance = this;
    }

    void Start()
    {
        if (transform.parent.parent.GetComponent<RYBpoleBase>() != null)
        {
            rYBpoleBase = transform.parent.parent.GetComponent<RYBpoleBase>();
            //放在start里面初始化值awake太早调用其他脚本
            RYBpole = rYBpoleBase.RYBpole;
            if (RYBpole != null)
            {
                print(transform.parent.parent.name + "绿灯是" + RYBpole.LightStraight_gt[whichlight]);
                T = RYBpole.LightCycle;
                gt =Lighttime[0] = RYBpole.LightStraight_gt[whichlight];
                rt=Lighttime[2] = T - gt-3;
                i = RYBpole.i[whichlight];
                delaytime = RYBpole.dt[whichlight];
                initialsetting = RYBpole.sc[whichlight];
            }
        }
        else
        {
            gt = Lighttime[0];
            rt = Lighttime[2];
        }
        if (initialsetting == 2)
        {
            transform.GetChild(2).GetComponent<MeshRenderer>().material.color = Color.red;
        }
        if (initialsetting == 1)
        {
            transform.GetChild(1).GetComponent<MeshRenderer>().material.color = Color.yellow;
        }
        if (initialsetting == 0)
        {
            transform.GetChild(0).GetComponent<MeshRenderer>().material.color = Color.green;
        }
        //第一周期加速
        //StartCoroutine(SkipTime());
        Invoke(nameof(Changecolor), delaytime);
    }

    
    private void Update()
    {
        if(Input.GetKeyDown(KeyCode.Alpha3))
        {
            Time.timeScale = 20;
        }
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            Time.timeScale = 2;
        }
        if(Input.GetKeyDown(KeyCode.Alpha1))
        {
            Time.timeScale = 1;
        }
    }

    /// <summary>
    /// 跳过第一周期
    /// </summary>
    private IEnumerator SkipTime()
    {
        Time.timeScale = 20; // 设置时间缩放为2倍

        yield return new WaitForSecondsRealtime((gt+rt+3)/20-1); // 延迟10秒

        Time.timeScale = 1; // 恢复时间缩放为正常速度
    }

    void Changecolor()
    {
        
        //Debug.Log(i);
        //根据i的值决定颜色
        //绿灯
        if (i == 0)
        {
            transform.GetChild(0).GetComponent<MeshRenderer>().material.color = Color.green;
        }
        else
        {
            transform.GetChild(0).GetComponent<MeshRenderer>().material.color = Color.white;
        }

        //黄灯
        if (i == 1)
        {
            transform.GetChild(1).GetComponent<MeshRenderer>().material.color = Color.yellow;
        }
        else
        {
            transform.GetChild(1).GetComponent<MeshRenderer>().material.color = Color.white;
        }
        //红灯
        if (i == 2)
        {
            transform.GetChild(2).GetComponent<MeshRenderer>().material.color = Color.red;
        }
        else
        {
            transform.GetChild(2).GetComponent<MeshRenderer>().material.color = Color.white;
        }
        //改变i的大小
        //绿灯
        if (i == 0)
        {
            //绿灯前检测
            //if (whichlight == 0)
            //{
            //    CheckCarNum();
            //}
            Invoke(nameof(Changecolor), Lighttime[0]);
        }
        //黄灯
        else if (i == 1)
        {
            Invoke(nameof(Changecolor), Lighttime[1]);
            //黄灯时y检测
            if (whichlight == 0)
            {
                CheckCarNum();
            }
        }
        //红灯
        else if (i == 2)
        {
            Invoke(nameof(Changecolor), Lighttime[2]);
            红灯时检测
            //if (whichlight == 0)
            //{
            //    CheckCarNum();
            //}
        }    
        i++;
        if (i == 3) i = 0;
    }

    /// <summary>
    /// 改变周期、绿灯、红灯参数
    /// </summary>
    void CheckCarNum()
    {
        //归零
        x0.Lighttime[0] = x0.gt;
        x0.Lighttime[2] = x0.rt;
        x1.Lighttime[0] = x1.gt;
        x1.Lighttime[2] = x1.rt;
        z0.Lighttime[0] = z0.gt;
        z0.Lighttime[2] = z0.rt;
        z1.Lighttime[0] = z1.gt;
        z1.Lighttime[2] = z1.rt;


        //函数

        Collider[] hitcolliders = Physics.OverlapBox(transform.parent.parent.position + offset, CheckCarNumsize, Quaternion.identity, LayerMask.GetMask("ground"));
        int targercount = hitcolliders.Length;
        
        if (targercount>=4)
        {
            print("触发调节");
            x0.Lighttime[0] += 3;
            x0.Lighttime[2] -= 3;
            //leftx0.Turnlefttime[2] += 3;
            //leftx0.delaytime+=3;

            x1.Lighttime[0] += 3;
            x1.Lighttime[2] -= 3;
            //leftx1.Turnlefttime[2] += 3;

            //leftx1.delaytime += 3;

            z0.Lighttime[0] -= 3;
            z0.Lighttime[2] += 3;

            //z0.delaytime += 3;
            //leftz0.delaytime += 3;

            z1.Lighttime[0] -= 3;
            z1.Lighttime[2] += 3;


            //z1.delaytime += 3;
            //leftz1.delaytime += 3;
        }
    }
    private void OnDrawGizmos()
    {
        if (whichlight == 0)
        {
            Gizmos.color = UnityEngine.Color.green;
            Gizmos.DrawWireCube(transform.parent.parent.position + offset, CheckCarNumsize);
        }
        
    }

}

当然!以下是一个基于Unity开发的三维设计件的简单案例: 案例名称:3D模型编辑器 功能: 1. 导入和导出模型文件,支持常见的格式如OBJ、FBX等。 2. 创建、编辑和删除三维模型,包括移动、旋转和缩放等操作。 3. 材质编辑:添加纹理、调整颜色、透明度等。 4. 光照设置:调整场景光照、阴影和反射效果。 5. 物理模拟:模拟物理效果,如重力、碰撞等。 6. 动画编辑:创建和编辑模型的动画序列。 7. 场景编辑:创建和编辑场景,包括地形、天空盒、摄像机等。 8. 用户界面:设计直观的用户界面,包括工具栏、菜单和操作面板等。 实现方法: 1. 利用Unity提供的建模工具或者导入第三方建模软件创建模型,并通过编写脚本实现模型的编辑功能。 2. 利用Unity的材质系统和着色器来实现材质编辑功能。 3. 利用Unity的光照系统来调整光照效果。 4. 利用Unity的物理引擎来实现物理模拟功能。 5. 利用Unity的动画系统来创建和编辑动画序列。 6. 利用Unity的场景编辑器来创建和编辑场景。 7. 利用Unity的UI系统来设计用户界面,并通过编写脚本实现交互功能。 以上仅为一个简单的案例示例,实际开发过程中可能涉及更多功能和复杂性的设计。使用Unity作为开发工具,你可以通过脚本编程、图形界面设计、资源管理等功能,实现一个强大的三维设计软件。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值