有限条件下编队协同驾驶的仿真开发(一)——Plexe-Sumo的初步介绍与Demo解析

写在前面

本人系某985高校23级智能交通系统工程博士新生,但硕士期间主要研究方向为基于数据驱动的过程控制系统故障诊断,也因此算是一个彻头彻尾的交通小白。
然而时间不等人,进组的第一个项目就必须使用SUMO软件来进行交通流建模,可没接触过科班教学的我在各类论坛上均没有找到SUMO的详细教程,也不想且没时间去啃SUMO的指导手册!
趁此机会!想将学习过程中的一些基础内容与大家分享,也算是用网络见证一下自己的进步!
如果大家有什么相关的教程推荐!请速速种草!
当然,如有错误,也欢迎各位批评指正!

Introduction

目前车联网和车路协同 (V2X) 是自动驾驶和智能交通领域研究的核心问题之一。简单来说,V2X 就是为车辆提供了上帝视角,可以获取更大范围、更准确的信息,从而优化驾驶决策。

在各种智能交通场景中,platooning (车辆编队) 是一个很热门的话题。借助 V2X, platooning 可以减小车辆之间的安全距离。较小的安全距离可以增加道路的吞吐量,对于在高速公路上行驶的卡车编队,还可以减小风阻,降低能耗。由于各方面的限制,很难在实车、实路上测试智能交通算法的性能。在这种情况下,仿真测试可以在一定程度上验证算法的有效性。

V2X 仿真主要包括两个方面,即交通场景和网络通讯,其中SUMO 可以实现交通场景的仿真,而网络通讯方面的仿真软件有OMNeT++, NS3 等。在此基础上,相关研究人员开发了相关框架来进行仿真:

  1. Veins (vehicles in network simulation) = SUMO + OMNeT++,是针对车联网的仿真软件。

  2. Plexe (platooning extension for veins) = Plexe-SUMO + Plexe-Veins,是在Veins得基础上Plexe 是在 Veins 中加入了 platooning 的元素。

在做对网络通讯方面的真实度要求不高的 V2X 仿真时,即不需要模拟实际联网中的延迟、丢包等情况时,通常采用 Plexe-SUMO,来为代码撰写提供便利。因此,Plexe-Sumo也成为了该种条件下的编队协同驾驶模拟的优异选择。

Plexe-Sumo的Github下载链接:https://github.com/michele-segata/plexe-pyapi

在这里插入图片描述

安装Plexe-Sumo的python API流程如下:

# sumo安装
cd

git clone --recursive https://github.com/eclipse/sumo

sudo apt-get install cmake python libxerces-c-dev libfox-1.6-dev libgl1-mesa-dev libglu1-mesa-dev libgdal-dev libproj-dev

# sumo的系统环境变量设置
cd ~/sumo/build/cmake_modules

cmake ../..

make -j8

echo "export SUMO_HOME=~/sumo" >> ~/.bashrc 

echo "export PATH=$SUMO_HOME/bin:$SUMO_HOME/tools:$PATH" >> ~/.bashrc 

# plexe-pyapi安装
cd 

git clone https://github.com/michele-segata/plexe-pyapi.git

cd plexe-pyapi

pip install --user .

Framework

Core File - utils.py

plexe库中的核心模块,其中包含多个函数,每个函数的参数及其含义通过表格进行阐述。

add_vehicle()使用traci.vehicle.add模块 根据sumo版本加入车辆
plexe类接口
vid设置车辆id
position车辆起始位置
lane道路
speed车辆起始速度
vtype车辆类别
add_platooning_vehicle()在add_vehicle的基础上加入一辆车至编队中
plexe类接口
vid设置的车辆ID
position车辆起始位置
lane道路
speed车辆起始速度
cacc_spacingCACC状态下的车辆间距
real_engine = False真实车辆动力学模型或一阶滞后模型
type = “vtypeauto”rou文件类型设置
communicate()按拓扑结构中的设置与领航车和前车进行交互,支撑CACC与Fake CACC的应用
plexe类接口
topology拓扑结构
get_distance()计算v1与v2两车的欧式距离
plexe类接口
v1车辆1
v2车辆2
start_sumo()使用指定的配置文件启动或重新启动Sumo仿真,
config_fileSumo配置文件
already_runningTrue: 仅加载给定的配置文件;False: 重新启动Sumo
gui开始可视化界面
running()使用指定的配置文件启动或重新启动Sumo仿真,
config_fileSumo配置文件
already_runningTrue: 仅加载给定的配置文件;False: 重新启动Sumo
gui是否启动可视化界面

Configuration File

freeway.gui.xml: Sumo中的界面配置文件;

freeway.net.xml: Sumo中的路网配置文件,提供路网的初步构造;

freeway.rou.xml: Sumo中的车辆配置文件,对路网中各个道路边缘的长度与相关参数进行配置(vType id),(通过vehicle id指定车辆类型并规定器路径,plexe中并未通过该方式及进行配置);

freeway.sumo.cfg: Sumo中的运行配置文件,提供输入的路网(net)配置与车辆(rou)配置文件、运行时长与步长、过程等…

vehicles.xml: 车辆动力学配置文件,提供车辆的变速箱比率、齿轮箱、引擎(换挡规则)等车辆参数,目前包括Alfa Romeo 147 1.6 Twin Spark、Bugatti Veyron、Audi R8三项。

Demo

最新版本种官方共提供了7项Demo,分别为

  • autofeeddemo.py:
  • brakedemo.py: platoon: 编队协同刹车
  • dashboard-demo.py:
  • enginedemo.py: 三车启动性能对比(车辆动力学)
  • joindemo.py: 从中间加入 platoon
  • overtake-and-keep-right-test.py: 单辆车超车
  • platoon-lane-change-test.py: platoon 协同换道超车

joindemo.py

该Demo文件实现了车辆编队过程中单一车辆车加入编队的过程。

  1. 变量的初始化定义:
# 导入相关函数包
import os
import sys
import random
import traci # sumo内置函数包

# 导入plexe--sumo中的函数
from utils import add_platooning_vehicle, communicate, get_distance, start_sumo, running
# 导入相关参数
from plexe import Plexe, ACC, CACC, FAKED_CACC, RPM, GEAR, ACCELERATION, SPEED

# 确认Sumo的环境变量是否声明,若声明则确认Sumo中工具包位置
if 'SUMO_HOME' in os.environ:
    tools = os.path.join(os.environ['SUMO_HOME'], 'tools')
    sys.path.append(tools)
else:
    sys.exit("please declare environment variable 'SUMO_HOME'")
    
# 该Demo中的参数初始化
LENGTH = 4 # 单车长度
DISTANCE = 5 # 车头间距
JOIN_DISTANCE = DISTANCE * 2 # 加入车队时需保留间距
SPEED = 120 / 3.6 # 巡航速度

# 行动状态(0为人工驾驶,1为ACC, 2为CACC):
GOING_TO_POSITION = 0	# 前往
OPENING_GAP = 1			# 打开间隙
COMPLETED = 2			# 完成加入编组

# 行动参与者 (仅考虑一个编队,车辆编号依次为v.0,v.1,...)
LEADER = "v.0"		# 领航(0开始)
JOIN_POSITION = 3	# 加入位置
FRONT_JOIN = "v.%d" % (JOIN_POSITION - 1) 	# 前车编号
BEHIND_JOIN = "v.%d" % JOIN_POSITION	  	# 后车编号
N_VEHICLES = 8								# 车队中车辆总数+1
JOINER = "v.%d" % N_VEHICLES				# 加入车辆编号
  1. 该Demo中的函数模块定义:

add_vehicles() 初始车辆定义模块

在仿真中加入一个包含n辆车的车队与一个距离较远希望加入车队的车辆。

def add_vehicles(plexe, n, real_engine=False):
    # 初始化车队拓扑结构
    topology = {}
    # 编队车辆遍历
    # 调用utils中的模块,实现编队设置
    for i in range(n):
      	# 定义车辆id与初始状态
        vid = "v.%d" % i
        add_platooning_vehicle(plexe, vid, (n - i + 1) * (DISTANCE + LENGTH)+50, 0, SPEED, DISTANCE, real_engine)
        # 定义车辆所在车道,并标记该车道为不安全
        plexe.set_fixed_lane(vid, 0, safe=False)
        # 定义车辆的运动模式
        traci.vehicle.setSpeedMode(vid, 0)
        # 定义领航车和跟随车辆分别为ACC和CACC控制模式
        if i == 0:
            plexe.set_active_controller(vid, ACC)
        else:
            plexe.set_active_controller(vid, CACC)
        # 定义每辆车的前车与领航车的拓扑结构
        if i > 0:
            topology[vid] = {"front": "v.%d" % (i - 1), "leader": LEADER}
    
    # 添加一辆希望加入编队的车辆
    vid = "v.%d" % n
    add_platooning_vehicle(plexe, vid, 10, 1, SPEED, DISTANCE, real_engine)
    plexe.set_fixed_lane(vid, 1, safe=False)
    traci.vehicle.setSpeedMode(vid, 0)
    plexe.set_active_controller(vid, ACC)
    
    # 定义CACC的相关参数,此处定义CACC中加入编队时的车辆间距
    plexe.set_path_cacc_parameters(vid, distance=JOIN_DISTANCE)
    
    return topology

get_in__position() 加入编队模块

通过更改拓扑结构和为加入车辆提供领航车辆与前车,使加入车辆靠近加入位置

此时,加入车辆加速,与前车和领航车之间通过使用GPS位置的CACC来进行控制。

def get_in_position(plexe, jid, fid, topology):
    # 定义加入车辆的拓扑结构
    topology[jid] = {"leader": LEADER, "front": fid}
    
    # 提升加入车辆的巡航速度,设置其控制方式为基于GPS位置的CACC
    plexe.set_cc_desired_speed(jid, SPEED + 15)
    plexe.set_active_controller(jid, FAKED_CACC)
    
    return topology

opengap() 车队打开插入空隙模块

通过创建临时车队,将后方车辆打开空隙,即后方所有车辆组成以后方车辆为领航者的临时编队

def open_gap(plexe, vid, jid, topology, n):
    # 确认后方第一台车辆的id
    index = int(vid.split(".")[1])
    
    # 对后方所有车辆进行遍历,将其领航车辆定义为后方第一台车,即演化为一临时车队
    for i in range(index + 1, n):
        topology["v.%d" % i]["leader"] = vid
        
    # 将后方编队的领航车辆的前方车辆定义为加入车辆
    topology[vid]["front"] = jid
    
    #设置加入车辆的控制方式为基于GPS位置的CACC,并定义车间距
    plexe.set_active_controller(vid, FAKED_CACC)
    plexe.set_path_cacc_parameters(vid, distance=JOIN_DISTANCE)
    
    return topology

reset__leader() 后方车队的领航车辆重置

def reset_leader(vid, topology, n):
	# 确认后方第一台车辆的id
    index = int(vid.split(".")[1])
    
    for i in range(index + 1, n):
        # 重新定义后方车队的领航车辆为前方车队的领航车
        topology["v.%d" % i]["leader"] = LEADER
        
    return topology
  1. 该Demo中的主程序模块定义:

main() 主程序

def main(demo_mode, real_engine, setter=None):
    # 定义随机种子以使得车辆颜色随机
    random.seed(1)
    
    # 启动Sumo仿真 
    start_sumo("cfg/freeway.sumo.cfg", False)
    
    # 初始化Plexe类
    plexe = Plexe()
    
    # 建立traci与plexe的步骤监听器
    traci.addStepListener(plexe)
    
    # 定义开始时间和模态
    step = 0
    state = GOING_TO_POSITION # 即'0'模态
    
    # 从step开始至6000结束,按步长运行,'demo_mode'是是否采用演示模式的参数
    while running(demo_mode, step, 6000):

        # 当处于演示模式时,单次仿真结束后进行重置
        if demo_mode and step == 6000:
            start_sumo("cfg/freeway.sumo.cfg", True)
            step = 0
            state = GOING_TO_POSITION
            random.seed(1)
            
		# traci模块控制仿真前进一个步长
        traci.simulationStep()

        if step == 0:
            # 仿真开始时,创建一个车队
            topology = add_vehicles(plexe, N_VEHICLES, real_engine)
            # 将追踪车辆'Joiner'的视图标识符定义为'"View #0"'
            traci.gui.trackVehicle("View #0", JOINER)
            # 设置追踪视图的缩放级别,数值越大缩放越小
            traci.gui.setZoom("View #0", 20000)
        if step % 10 == 1:
            # 定义车辆间每100ms根据拓扑结构与领航车辆和前车交互
            # 支撑CACC和FakeCACC的应用
            communicate(plexe, topology)
        if step == 100:
            # 仿真开始1s后,加入车辆开始靠近车队
            topology = get_in_position(plexe, JOINER, FRONT_JOIN, topology)
        if state == GOING_TO_POSITION and step > 0:
            # 当加入车辆与前车距离足够接近时,后方车辆打开间隙
            if get_distance(plexe, JOINER, FRONT_JOIN) < JOIN_DISTANCE + 1:
                state = OPENING_GAP
                topology = open_gap(plexe, BEHIND_JOIN, JOINER, topology, N_VEHICLES)
        if state == OPENING_GAP:
            # 当间隙足够大时,完成加入车辆的操作
            if get_distance(plexe, BEHIND_JOIN, FRONT_JOIN) > 2 * JOIN_DISTANCE + 2:
                state = COMPLETED
                # 换道
                plexe.set_fixed_lane(JOINER, 0, safe=False)
                
                # 重新定义加入车辆的控制方式及相关参数
                plexe.set_active_controller(JOINER, CACC)
                plexe.set_path_cacc_parameters(JOINER, distance=DISTANCE)
                plexe.set_active_controller(BEHIND_JOIN, CACC)
                plexe.set_path_cacc_parameters(BEHIND_JOIN, distance=DISTANCE)
                
                # 重新设置后方车辆的领航车辆为第一辆车
                topology = reset_leader(BEHIND_JOIN, topology, N_VEHICLES)
                
        if real_engine and setter is not None:
            # 如果使用仪表板模式,则更新面板上的值
            tracked_id = traci.gui.getTrackedVehicle("View #0")
            if tracked_id != "":
                ed = plexe.get_engine_data(tracked_id)
                vd = plexe.get_vehicle_data(tracked_id)
                setter(ed[RPM], ed[GEAR], vd.speed, vd.acceleration)

        step += 1

    traci.close()

欢迎各位批评指正!
下一章将针对SUMO的底层和初步使用方法进行介绍,敬请期待!

Ref:

[1] 基于 Plexe-SUMO 的 V2X 仿真 - 简书 (jianshu.com)

Tool:

[1] ChatGPT

  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值