要点初见:用Python进行微观交通模型仿真——基于SUMO的伯克利开源项目Flow Project初探与拓展_sumo模拟元胞自动机交通流-程序员宅基地

技术标签: 交通仿真  Flow Project  SUMO  微观交通流模型  要点初见  Python  

       后续拓展:要点初见:安装教程与二度拓展——基于SUMO的Flow Project(附代码链接)

通俗来讲,微观交通模型仿真就是从车辆个体的视角(看动画)进行交通流仿真,对车辆个体随时间、空间的变化进行跟踪描述。常用的微观交通仿真方法包括在Matlab中建立元胞自动机模型、在Vissim中画车道调参数。但前者存在仿真系统太简易粗糙,不足以体现复杂的交通影响因素;后者又存在太依赖GUI界面,COM接口编程只支持高版本正版(这东西贼难搞)、C++编程,只能获得车辆通过设定的检测点的时空坐标而非各个时刻各车辆的坐标,让后续能进行的坐标分析操作变得很局限。

       但就博主在Github上寻找(现成的)微观交通模型元胞自动机代码找到快要绝望的时候,Flow Project冒了出来,这是一个由加州大学伯克利分校的移动传感实验室使用亚马逊AWS机器学习研究基金,基于SUMO开发出的用于交通微观模拟深度强化学习和控制实验的计算框架,不仅开源,开发语言还是当下火热的Python,依赖的操作系统还是Ubuntu。博主仔细研读了一下项目源码、Document,真是相见恨晚,让人忍不住想说:

       Talk is cheap,show me the code. 先放链接:

       Flow Project的Github链接(或在Github中搜“Flow SUMO”)是:https://github.com/flow-project/flow

       Flow Project的官网链接是:https://flow-project.github.io/

       Flow Project的Document链接是:https://flow.readthedocs.io/en/latest/flow_setup.html#installing-flow-and-sumo

       对Flow Project有任何疑惑可以在StackOverflow的专题链接中提问(带上flow-project的标签,回的贼快):https://stackoverflow.com/questions/tagged/flow-project

       本博文主要从对Flow Project的介绍、Flow Project的安装、用Python运行模型输出坐标三个部分进行介绍,附加博主改源码增加改变车身长度功能的方法介绍。

 

1  Flow Project的介绍

       Flow Project是基于开源框架SUMO的二次开发项目(其也可基于收费软件Aimsun、强化学习库RLlib,此文中暂不介绍),SUMO的全称Simulation of Urban Mobility,是一款欧洲开源的微观交通仿真框架,但开源的另一面也意味着放养、不更新……所以单论SUMO相比于Vissim不仅功能少而且还难用。

       SUMO有个知名(难用)的Python接口TraCI(Traffic Control Interface),这个久远的接口博主至今也还没研究懂,但从源码中可以看出Flow Project的开发者们恰恰是利用这个接口,对SUMO进行了二次封装,从而让我们能通过短短几十行可读性强的Python代码创建出功能齐全的微观异质交通流仿真模型。Flow Project的核心在于完美接入强化学习,但Flow Project在仿真有人车辆上也完全绰绰有余,还能加入强化学习的自动驾驶车辆,可拓展性非常强,博主未来也会继续研究该项目。

       在Ubuntu系统中运行Flow Project,仿真界面大概如下:

 

2  Flow Project的安装

       Flow Project的安装方法主要参考官方Document:https://flow.readthedocs.io/en/latest/flow_setup.html#testing-your-installation,简单说就是在Terminal中:

(1)下载Flow Project项目:

git clone https://github.com/flow-project/flow.git

cd flow

(2)安装anaconda2(参考anaconda官网https://www.anaconda.com/),之后在flow目录下创建专属运行环境并安装依赖:

conda env create -f environment.yml

source activate flow

(3)安装Flow Project和SUMO:

pip install -e .

./scripts/setup_sumo_ubuntu1404.sh(对于Ubuntu 14.04)

./scripts/setup_sumo_ubuntu1604.sh(对于Ubuntu 16.04)

./scripts/setup_sumo_ubuntu1804.sh(对于Ubuntu 18.04)

./scripts/setup_sumo_osx.sh(对于Mac)

       检查SUMO是否安装成功:

which sumo

sumo --version

       启动SUMO图形界面:

sumo-gui

       如果系统显示未找到相关命令,则运行source ~/.bashrc或打开一个新的Terminal来更新环境变量。

(4)检查flow是否安装成功:

source activate flow

python examples/sumo/sugiyama.py

       这步大概率是不成功的,或是在重启后再运行不成功,会发生没法激活flow的问题亦或Python版本导致的报错,因为博主系统默认的python版本是/usr/bin下的Python2.7,但Flow Project又是依赖anaconda目录下的python3.5,而随便改系统的环境变量又容易把很多框架的依赖弄崩。博主找到的解决方法是:在每次开机第一次运行Flow Project的脚本前运行如下三句话(第三行的UBUNTU改成你的home目录名):

export PATH=/usr/local/anaconda2/bin:$PATH

source activate flow

export PYTHONPATH="/home/UBUNTU/.conda/envs/flow/lib/python3.5/site-packages:$PYTHONPATH"

       这之后在flow目录下的Terminal中输入python examples/sumo/sugiyama.py或examples/sumo/下的其他脚本即可成功运行仿真,这也意味着你的flow安装好了,欢迎来到用Python进行交通流仿真的时代~

 

3  用Python运行模型、输出坐标

       从官方新手教程中可以看到,仿真的核心在于examples/sumo/目录下的脚本,笔者就以该目录下的单向N车道高速公路仿真创建脚本highway.py的更改、拓展为例,介绍如何用Python运行仿真并输出各时刻各车辆在道路上的坐标。

       Flow Project中的道路形态有限,新道路的创建应该是基于SUMO底层进行定制,还有待未来研究。就在官方给出的几种道路形态中,N车道高速公路是较简单的一个模型,原脚本的运行效果如上文中仿真图像介绍部分。但博主因为论文研究需要,对该脚本进行了爆改:

(1)改源码增加改变车身长度的功能

       Flow Project的Python脚本里不能直接改变每辆输入车辆的车身长度,需要更改源码。参考博主在StackOverflow中提出的相关问题https://stackoverflow.com/questions/56891526/how-to-change-sizes-of-vehicle-in-flow-simulation,需要更改flow/flow/core目录下的params.py中的class VehicleParams中的def add()函数,增加加粗的这几行:

    def add(self,
            veh_id,
            length=5,
            acceleration_controller=(SimCarFollowingController, {}),
            lane_change_controller=(SimLaneChangeController, {}),
            routing_controller=None,
            initial_speed=0,
            num_vehicles=1,
            car_following_params=None,
            lane_change_params=None):
        """Add a sequence of vehicles to the list of vehicles in the network.

        Parameters
        ----------
        veh_id : str
            base vehicle ID for the vehicles (will be appended by a number)
        acceleration_controller : tup, optional
            1st element: flow-specified acceleration controller
            2nd element: controller parameters (may be set to None to maintain
            default parameters)
        lane_change_controller : tup, optional
            1st element: flow-specified lane-changer controller
            2nd element: controller parameters (may be set to None to maintain
            default parameters)
        routing_controller : tup, optional
            1st element: flow-specified routing controller
            2nd element: controller parameters (may be set to None to maintain
            default parameters)
        initial_speed : float, optional
            initial speed of the vehicles being added (in m/s)
        num_vehicles : int, optional
            number of vehicles of this type to be added to the network
        car_following_params : flow.core.params.SumoCarFollowingParams
            Params object specifying attributes for Sumo car following model.
        lane_change_params : flow.core.params.SumoLaneChangeParams
            Params object specifying attributes for Sumo lane changing model.
        """
        if car_following_params is None:
            # FIXME: depends on simulator
            car_following_params = SumoCarFollowingParams()

        if lane_change_params is None:
            # FIXME: depends on simulator
            lane_change_params = SumoLaneChangeParams()

        type_params = {}
        type_params.update(car_following_params.controller_params)
        type_params.update(lane_change_params.controller_params)

        # If a vehicle is not sumo or RL, let the minGap be zero so that it
        # does not tamper with the dynamics of the controller
        if acceleration_controller[0] != SimCarFollowingController \
                and acceleration_controller[0] != RLController:
            type_params["minGap"] = 0.0

        type_params['length'] = length

        # This dict will be used when trying to introduce new vehicles into
        # the network via a Flow. It is passed to the vehicle kernel object
        # during environment instantiation.
        self.type_parameters[veh_id] = \
            { "length": length,
             "acceleration_controller": acceleration_controller,
             "lane_change_controller": lane_change_controller,
             "routing_controller": routing_controller,
             "initial_speed": initial_speed,
             "car_following_params": car_following_params,
             "lane_change_params": lane_change_params}

        # TODO: delete?
        self.initial.append({
            "veh_id":
                veh_id,
            "length":
                length,

            "acceleration_controller":
                acceleration_controller,
            "lane_change_controller":
                lane_change_controller,
            "routing_controller":
                routing_controller,
            "initial_speed":
                initial_speed,
            "num_vehicles":
                num_vehicles,
            "car_following_params":
                car_following_params,
            "lane_change_params":
                lane_change_params
        })

        # This is used to return the actual headways from the vehicles class.
        # It is passed to the vehicle kernel class during environment
        # instantiation.
        self.minGap[veh_id] = type_params["minGap"]

        for i in range(num_vehicles):
            v_id = veh_id + '_%d' % i

            # add the vehicle to the list of vehicle ids
            self.ids.append(v_id)

            self.__vehicles[v_id] = dict()

            # specify the type
            self.__vehicles[v_id]["type"] = veh_id

            # update the number of vehicles
            self.num_vehicles += 1
            if acceleration_controller[0] == RLController:
                self.num_rl_vehicles += 1

        # increase the number of unique types of vehicles in the network, and
        # add the type to the list of types
        self.num_types += 1
        self.types.append({"veh_id": veh_id, "type_params": type_params})

       这样就可以在Python文件中改变输入车辆的长度了。

(2)让我们来更改Python脚本吧:

       先看一下highway.py改后的全貌:

"""Example of an open multi-lane network with human-driven vehicles."""

from flow.controllers import IDMController, SimLaneChangeController, ContinuousRouter, RLController
from flow.core.experiment import Experiment
from flow.core.params import SumoParams, EnvParams, \
    NetParams, InitialConfig, InFlows, SumoLaneChangeParams, SumoCarFollowingParams
from flow.core.params import VehicleParams
from flow.envs.loop.lane_changing import LaneChangeAccelEnv, \
    ADDITIONAL_ENV_PARAMS
from flow.scenarios.highway import HighwayScenario, ADDITIONAL_NET_PARAMS

#from flow.core.params import SimParams


def highway_example(render=None):
    """
    Perform a simulation of vehicles on a highway.

    Parameters
    ----------
    render : bool, optional
        specifies whether to use the gui during execution

    Returns
    -------
    exp: flow.core.experiment.Experiment
        A non-rl experiment demonstrating the performance of human-driven
        vehicles on a figure eight.
    """
    sim_params = SumoParams(restart_instance=True, sim_step=0.1, emission_path="./data/",render=True, sight_radius=30, pxpm=3, show_radius=True)

    if render is not None:
        sim_params.render = render

    vehicles = VehicleParams()
    
    vehicles.add(
        veh_id="rlcar",# Lincoln MKC 4552*1864*1654
        length = 4.552,
        acceleration_controller=(RLController, {}),
        car_following_params=SumoCarFollowingParams(
            speed_mode="obey_safe_speed",
        ),
        initial_speed=0,
        num_vehicles=1)
    
    vehicles.add(
        veh_id="humancar",# Volkswagen LAVIDA 4670*1806*1474 max:120km/h
        length = 4.67,
        #v0 : desirable velocity, in m/s (default: 30) in flow/flow/controllers/car_following_models.py 352
        acceleration_controller=(IDMController,{'v0':32}),# 115km/h
        lane_change_controller=(SimLaneChangeController, {}),
        lane_change_params=SumoLaneChangeParams(
            lane_change_mode="strategic",# Human cars make lane changes in accordance with SUMO to provide speed boosts
        ),
        num_vehicles=1)
    
    vehicles.add(
        veh_id="humanbus_lane2",# YUTONG ZK6826BEV 8245*2500*3240 max:100km/h
        length = 8.245,
        acceleration_controller=(IDMController, {'v0':26.4}),# 95km/h
        #lane_change_controller=(SimLaneChangeController, {}),
        #lane_change_params=SumoLaneChangeParams(
        #    lane_change_mode="strategic",# Human cars make lane changes in accordance with SUMO to provide speed boosts
        #),
        num_vehicles=1)
    vehicles.add(
        veh_id="humanbus_lane1",# YUTONG ZK6826BEV 8245*2500*3240 max:100km/h
        length = 8.245,
        acceleration_controller=(IDMController, {'v0':26.4}),# 95km/h
        num_vehicles=1)
    vehicles.add(
        veh_id="humanbus_lane0",# YUTONG ZK6826BEV 8245*2500*3240 max:100km/h
        length = 8.245,
        acceleration_controller=(IDMController, {'v0':26.4}),# 95km/h
        num_vehicles=1)
        
    vehicles.add(
        veh_id="humantruck_lane2",# FOTON BJ5319XXY-AB 12000*2550*3950 max:100km/h
        length = 12,
        acceleration_controller=(IDMController, {'v0':25}),# 90km/h
        #lane_change_controller=(SimLaneChangeController, {}),
        #lane_change_params=SumoLaneChangeParams(
        #    lane_change_mode="strategic",# Human cars make lane changes in accordance with SUMO to provide speed boosts
        #),
        num_vehicles=1)
    vehicles.add(
        veh_id="humantruck_lane1",# FOTON BJ5319XXY-AB 12000*2550*3950 max:100km/h
        length = 12,
        acceleration_controller=(IDMController, {'v0':25}),# 90km/h
        num_vehicles=1)
    vehicles.add(
        veh_id="humantruck_lane0",# FOTON BJ5319XXY-AB 12000*2550*3950 max:100km/h
        length = 12,
        acceleration_controller=(IDMController, {'v0':25}),# 90km/h
        num_vehicles=1)

    env_params = EnvParams(additional_params=ADDITIONAL_ENV_PARAMS)

    inflow = InFlows()
    
    inflow.add(
        veh_type="rlcar",
        edge="highway_0",
        #probability=0.025,# 0.25 probability for emitting a vehicle each second (not together with vehsPerHour or period)
        vehs_per_hour=250,
        departLane=3,# the index of the lane, starting with rightmost=0
        departSpeed=30)
    
    inflow.add(
        veh_type="humancar",
        edge="highway_0",
        #probability=0.85,# 0.25 probability for emitting a vehicle each second (not together with vehsPerHour or period)
        vehs_per_hour=15000,
        departLane="random",#free random allowed best first
        departSpeed=30)
    
    inflow.add(
        veh_type="humanbus_lane2",
        edge="highway_0",
        #probability=0.1,
        vehs_per_hour=486,
        departLane=2,
        departSpeed=26.4)
    inflow.add(
        veh_type="humanbus_lane1",
        edge="highway_0",
        #probability=0.1,
        vehs_per_hour=486,
        departLane=1,
        departSpeed=26.4)
    inflow.add(
        veh_type="humanbus_lane0",
        edge="highway_0",
        #probability=0.1,
        vehs_per_hour=486,
        departLane=0,
        departSpeed=26.4)
    
    inflow.add(
        veh_type="humantruck_lane2",
        edge="highway_0",
        #probability=0.05,
        vehs_per_hour=486,
        departLane=2,
        departSpeed=25)
    inflow.add(
        veh_type="humantruck_lane1",
        edge="highway_0",
        #probability=0.05,
        vehs_per_hour=486,
        departLane=1,
        departSpeed=25)
    inflow.add(
        veh_type="humantruck_lane0",
        edge="highway_0",
        #probability=0.05,
        vehs_per_hour=486,
        departLane=0,
        departSpeed=25)

    initial_config = InitialConfig(spacing="uniform", shuffle=True)# initial position in road

    scenario = HighwayScenario(#3:110-120 2:90-120 3:90-120 4:60-120 [G1503 2019.5 daily car:180000 bus/truck:70000]
        name="highway",
        vehicles=vehicles,
        net_params=NetParams(
                inflows=inflow,
                additional_params={
                    'length': 6000,
                    'lanes': 4,
                    'speed_limit': 33.3,
                    'num_edges': 1
                }),
        initial_config=initial_config)
    

    env = LaneChangeAccelEnv(env_params, sim_params, scenario)

    return Experiment(env)


if __name__ == "__main__":
    # import the experiment variable
    exp = highway_example()

    # run for a set number of rollouts / time steps
    #exp.run(1, 1000, convert_to_csv = False)
    exp.run(1, 5000, convert_to_csv = True)

       分解到单步,用Flow Project创建微观交通流主要分为设定全局参数、添加既有车辆、添加输入流、设定道路场景、开始仿真5个步骤:

a.设定全局参数

    sim_params = SumoParams(restart_instance=True, sim_step=0.1, emission_path="./data/",render=True, sight_radius=30, pxpm=3, show_radius=True)

    if render is not None:
        sim_params.render = render

       此处restart_instance的作用是避免先前有未关闭的SUMO窗口,设定为True后每次运行脚本时会在点击仿真界面播放键后关闭窗口再重新开一个。

       sim_step是记录坐标的间隔时间,此处为0.1s记录一次所有道路上车辆的当前坐标;

       emission_path决定了坐标文件的保存位置,运行自然结束后(车辆跑完还会有一段较长的记录时间)会在flow/data/目录下找到一个csv和一个xml文件。

       后续的参数是对网络仿真功能生效的参数,会在网络仿真中在每辆车周身显示一圈“探测范围圈”,在此仿真中未涉及,保持上述参数即可。

b.添加既有车辆

    vehicles = VehicleParams()
    
    vehicles.add(
        veh_id="rlcar",# Lincoln MKC 4552*1864*1654
        length = 4.552,
        acceleration_controller=(RLController, {}),
        car_following_params=SumoCarFollowingParams(
            speed_mode="obey_safe_speed",
        ),
        initial_speed=0,
        num_vehicles=1)
    
    vehicles.add(
        veh_id="humancar",# Volkswagen LAVIDA 4670*1806*1474 max:120km/h
        length = 4.67,
        #v0 : desirable velocity, in m/s (default: 30) in flow/flow/controllers/car_following_models.py 352
        acceleration_controller=(IDMController,{'v0':32}),# 115km/h
        lane_change_controller=(SimLaneChangeController, {}),
        lane_change_params=SumoLaneChangeParams(
            lane_change_mode="strategic",# Human cars make lane changes in accordance with SUMO to provide speed boosts
        ),
        num_vehicles=1)

       此段的作用是在刚开始仿真时于既有道路上添加车辆,同时此处设定的该种类车辆的长度也会作用于后续添加的flow中。车辆将会以相同的间隔在道路上均匀分布,避免刚开始仿真时道路上无车的情况(让车流充分混合)。

       vehicles即为创建的对象,add里的参数veh_id代表车辆的类型,需和后方创建flow时对应车辆的类型名相同,在最后生成的坐标文件中该类型名也是搜索遍历的关键变量。

       length是车辆的长度,单位是m。

       acceleration_controller是该种车辆的加速控制逻辑,可以选择RL、IDM等加速控制逻辑,并在各加速控制逻辑函数中用字典的形式设置初始车速(单位为m/s)等变量(具体变量详见flow/flow/core目录下的params.py中的VehicleParams源码)。

       car_following_params设定了该种车辆的跟车逻辑,其中speed_mode有"right_of_way"(默认)、"obey_safe_speed"、"no_collide"、"aggressive"、"all_checks"几种模式,具体解释同样见flow/flow/core目录下的params.py中的VehicleParams源码。

       lane_change_controller设定了该种车辆的换道逻辑,具体的换道参数在lane_change_params中设定,lane_change_mode同样包括"no_lat_collide"(默认)、"strategic"、"aggressive"这几种选择,具体见params.py。

       initial_speed设定了这些在道路上的既有车辆的初始速度,和后续在flow中添加的车流的出发速度无关。num_vehicles则是添加既有车辆的数量,因博主的仿真不依赖初始的车辆,故随便设了个1。

       因为Flow Project暂时还没有设定各条车道车速上下限的功能,只能同时设定四条车道的最大车速,故博主选择在各条车道上建立不同初速度、期望速度的不同种类车流,在c中将有所体现。

c.添加输入流

    inflow = InFlows()
    
    inflow.add(
        veh_type="rlcar",
        edge="highway_0",
        #probability=0.025,# 0.25 probability for emitting a vehicle each second (not together with vehsPerHour or period)
        vehs_per_hour=250,
        departLane=3,# the index of the lane, starting with rightmost=0
        departSpeed=30)

       仿真模型不能只有道上车辆而没有输入,故Inflow()函数就是为在道路上建立输入车流而准备的。

       inflow作为创建的对象,add函数中veh_type对应上文中VehicleParams()创建的车流,必须使用上文中创建过的车辆种类。

       edge是选择车辆输入的道路端,_0一般是左边,另一边自然就是_1啦。

       关于输入车流的车流量,这里存在probability和vehs_per_hour两种方式进行车流量设定。probabilty是一种基于概率的车流生成方式,代表每秒生成车流的概率,最大为1,也只能为1了……所以这种生成方法存在着一个生成上限的问题(然而一秒一辆的路还能开车?)为了解决这种不太自由的限制,Flow Project还借助TraCI给出了vehs_per_hour的参数,就是喜闻乐见的每小时输入车辆数。

       departLane则是选择该种车流的初始车道,它有着“free”、“ random”、“ allowed”、“ best”和“first”这几种选择(具体见http://sumo.dlr.de/wiki/Definition_of_Vehicles,_Vehicle_Types,_and_Routes),但这里还可以选择车道数字,譬如四车道就是从左到右0到3,这样这辆车就会不停地在这条车道上生成。

       departSpeed是该种车流的出发速度,单位为m/s。

d.设定道路场景

    initial_config = InitialConfig(spacing="uniform", shuffle=True)# initial position in road

    scenario = HighwayScenario(#3:110-120 2:90-120 3:90-120 4:60-120 [G1503 2019.5 daily car:180000 bus/truck:70000]
        name="highway",
        vehicles=vehicles,
        net_params=NetParams(
                inflows=inflow,
                additional_params={
                    'length': 6000,
                    'lanes': 4,
                    'speed_limit': 33.3,
                    'num_edges': 1
                }),
        initial_config=initial_config)
    

    env = LaneChangeAccelEnv(env_params, sim_params, scenario)

       这里是Flow Project最强大,也最不自由的地方,通过这么一小段话就能构建出一个系统中既有的复杂场景。emmm……系统中既有之外的不行。

       HighwayScenario()可以看出这是专门为单向四车道高速公路场景设立的函数。因为Flow Project本意是用来训练简单场景下的强化学习车辆(复杂场景请考虑GTA等等游戏),因此没有太复杂的车辆网络(官方:有待诸位改源码扩展)。

       其中inflow输入先前创建的InFlow()对象即可,length指的是道路长度(单位:m,千万别太长,否则运行后的数据记录会中断),lanes是车道数,可以增减,speed_limit是这整条道路每条车道上车辆不会超过的最大车速(单位:m/s),num_edges指的是道路的端口数(没尝试增加过,或许可以变成双向?)。

e.开始仿真

exp.run(1, 5000, convert_to_csv = True)

       主函数中的这句话代表着仿真开始,其中1指的是仿真的次数(不是仿真的开始时刻!!),5000指的是仿真的秒数(太长的话数据记录会中断),convert_to_csv指是否把仿真产生的xml文件自动转为csv文件。如果仿真网络一定要大的话,就不要选择自动转换了,会崩而且极其占内存,可以考虑自己写程序将仿真产生的xml文件转成csv(博主没试过……)。

 

       后续博主还会在后续博客中给出对Flow Project产生的csv文件进行处理的Python脚本,关于Flow Project或有什么其他更方便的微观交通流仿真模型代码创建方法,欢迎交流讨论!Flow Project好像比较小众,Github上就300+Stars,加州伯克利他们组是不是快要放弃这个项目了呢……

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_37857300/article/details/100076886

智能推荐

JWT(Json Web Token)实现无状态登录_无状态token登录-程序员宅基地

文章浏览阅读685次。1.1.什么是有状态?有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。例如登录:用户登录后,我们把登录者的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session。然后下次请求,用户携带cookie值来,我们就能识别到对应session,从而找到用户的信息。缺点是什么?服务端保存大量数据,增加服务端压力 服务端保存用户状态,无法进行水平扩展 客户端请求依赖服务.._无状态token登录

SDUT OJ逆置正整数-程序员宅基地

文章浏览阅读293次。SDUT OnlineJudge#include<iostream>using namespace std;int main(){int a,b,c,d;cin>>a;b=a%10;c=a/10%10;d=a/100%10;int key[3];key[0]=b;key[1]=c;key[2]=d;for(int i = 0;i<3;i++){ if(key[i]!=0) { cout<<key[i.

年终奖盲区_年终奖盲区表-程序员宅基地

文章浏览阅读2.2k次。年终奖采用的平均每月的收入来评定缴税级数的,速算扣除数也按照月份计算出来,但是最终减去的也是一个月的速算扣除数。为什么这么做呢,这样的收的税更多啊,年终也是一个月的收入,凭什么减去12*速算扣除数了?这个霸道(不要脸)的说法,我们只能合理避免的这些跨级的区域了,那具体是那些区域呢?可以参考下面的表格:年终奖一列标红的一对便是盲区的上下线,发放年终奖的数额一定一定要避免这个区域,不然公司多花了钱..._年终奖盲区表

matlab 提取struct结构体中某个字段所有变量的值_matlab读取struct类型数据中的值-程序员宅基地

文章浏览阅读7.5k次,点赞5次,收藏19次。matlab结构体struct字段变量值提取_matlab读取struct类型数据中的值

Android fragment的用法_android reader fragment-程序员宅基地

文章浏览阅读4.8k次。1,什么情况下使用fragment通常用来作为一个activity的用户界面的一部分例如, 一个新闻应用可以在屏幕左侧使用一个fragment来展示一个文章的列表,然后在屏幕右侧使用另一个fragment来展示一篇文章 – 2个fragment并排显示在相同的一个activity中,并且每一个fragment拥有它自己的一套生命周期回调方法,并且处理它们自己的用户输_android reader fragment

FFT of waveIn audio signals-程序员宅基地

文章浏览阅读2.8k次。FFT of waveIn audio signalsBy Aqiruse An article on using the Fast Fourier Transform on audio signals. IntroductionThe Fast Fourier Transform (FFT) allows users to view the spectrum content of _fft of wavein audio signals

随便推点

Awesome Mac:收集的非常全面好用的Mac应用程序、软件以及工具_awesomemac-程序员宅基地

文章浏览阅读5.9k次。https://jaywcjlove.github.io/awesome-mac/ 这个仓库主要是收集非常好用的Mac应用程序、软件以及工具,主要面向开发者和设计师。有这个想法是因为我最近发了一篇较为火爆的涨粉儿微信公众号文章《工具武装的前端开发工程师》,于是建了这么一个仓库,持续更新作为补充,搜集更多好用的软件工具。请Star、Pull Request或者使劲搓它 issu_awesomemac

java前端技术---jquery基础详解_简介java中jquery技术-程序员宅基地

文章浏览阅读616次。一.jquery简介 jQuery是一个快速的,简洁的javaScript库,使用户能更方便地处理HTML documents、events、实现动画效果,并且方便地为网站提供AJAX交互 jQuery 的功能概括1、html 的元素选取2、html的元素操作3、html dom遍历和修改4、js特效和动画效果5、css操作6、html事件操作7、ajax_简介java中jquery技术

Ant Design Table换滚动条的样式_ant design ::-webkit-scrollbar-corner-程序员宅基地

文章浏览阅读1.6w次,点赞5次,收藏19次。我修改的是表格的固定列滚动而产生的滚动条引用Table的组件的css文件中加入下面的样式:.ant-table-body{ &amp;amp;::-webkit-scrollbar { height: 5px; } &amp;amp;::-webkit-scrollbar-thumb { border-radius: 5px; -webkit-box..._ant design ::-webkit-scrollbar-corner

javaWeb毕设分享 健身俱乐部会员管理系统【源码+论文】-程序员宅基地

文章浏览阅读269次。基于JSP的健身俱乐部会员管理系统项目分享:见文末!

论文开题报告怎么写?_开题报告研究难点-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏15次。同学们,是不是又到了一年一度写开题报告的时候呀?是不是还在为不知道论文的开题报告怎么写而苦恼?Take it easy!我带着倾尽我所有开题报告写作经验总结出来的最强保姆级开题报告解说来啦,一定让你脱胎换骨,顺利拿下开题报告这个高塔,你确定还不赶快点赞收藏学起来吗?_开题报告研究难点

原生JS 与 VUE获取父级、子级、兄弟节点的方法 及一些DOM对象的获取_获取子节点的路径 vue-程序员宅基地

文章浏览阅读6k次,点赞4次,收藏17次。原生先获取对象var a = document.getElementById("dom");vue先添加ref <div class="" ref="divBox">获取对象let a = this.$refs.divBox获取父、子、兄弟节点方法var b = a.childNodes; 获取a的全部子节点 var c = a.parentNode; 获取a的父节点var d = a.nextSbiling; 获取a的下一个兄弟节点 var e = a.previ_获取子节点的路径 vue

推荐文章

热门文章

相关标签