【Python】SUMO + Python 联合仿真平台

SUMO(二)——与Python的基本联合 简单的实现SUMO和Python联合运用 阅读详情

第五部分:集成强化学习算法进行交通优化

强化学习的核心思想是让智能体 (Agent) 通过与环境 (Environment) 的交互来学习如何做出最优决策,以最大化累积奖励 (Cumulative Reward)。在交通场景中,智能体可以是交通信号控制器、自动驾驶车辆或交通管理中心,环境则是 SUMO 模拟的交通路网,奖励则与交通效率、安全、环保等目标相关。

5.1 强化学习与交通优化的契合点

交通系统本质上是动态的、随机的、高度复杂的系统,传统的基于规则或模型的优化方法在面对不断变化的需求和突发事件时,往往显得力不从心。强化学习的优势在于:

  1. 自适应性 (Adaptability):RL 智能体能够通过不断试错和学习,适应不同的交通状况和环境变化,而无需显式地对所有可能情况进行建模。
  2. 处理复杂性 (Handling Complexity):RL 可以处理高维度的状态空间和动作空间,适用于复杂的交通网络和多目标优化问题。
  3. 数据驱动决策 (Data-Driven Decisions):RL 从与环境的交互数据中学习策略,可以发现传统方法难以察觉的优化模式。
  4. 长期优化 (Long-term Optimization):RL 的目标是最大化长期累积奖励,能够学习具有远见的控制策略,而不仅仅是贪婪地优化当前状态。

在交通领域,强化学习已经被广泛应用于:

  • 自适应交通信号控制 (Adaptive Traffic Signal Control, ATSC):根据实时车流动态调整信号灯配时,减少延误,提高交叉口通行能力。
  • 匝道汇入控制 (Ramp Metering):控制车辆进入高速公路的速率,缓解主路拥堵。
  • 动态路径诱导 (Dynamic Route Guidance):为车辆提供最优路径建议,均衡路网负载。
  • 自动驾驶车辆决策 (Autonomous Vehicle Decision Making):例如换道、超车、路口通行等行为决策。
  • 车队协同控制 (Platoon Coordination):优化车队的行驶速度和间距,提高道路容量。
5.2 强化学习基础回顾

为了更好地理解后续内容,我们简要回顾一下强化学习的核心概念:

  • 智能体 (Agent):学习者和决策者。
  • 环境 (Environment):智能体所处的外部世界,与智能体交互。
  • 状态 (State, S):对环境在某一时刻的描述。例如,在交通信号控制中,状态可以是各个进口道的排队长度、当前信号相位等。
  • 动作 (Action, A):智能体可以执行的操作。例如,选择下一个绿灯相位,或者保持当前相位。
  • 奖励 (Reward, R):环境对智能体在某个状态下执行某个动作后反馈的标量信号,评价该动作的好坏。例如,负的车辆总延误、负的排队长度等。
  • 策略 (Policy, π):智能体从状态到动作的映射,即在给定状态下选择某个动作的规则或概率分布。π(a|s) = P[A_t = a | S_t = s]。
  • 价值函数 (Value Function):
    • 状态价值函数 (State-Value Function, Vπ(s)): 从状态 s 开始,遵循策略 π 能获得的期望累积奖励。
      Vπ(s) = Eπ[∑k=0∞ γk Rt+k+1 | St = s]
    • 动作价值函数 (Action-Value Function, Qπ(s, a)): 在状态 s 执行动作 a后,继续遵循策略 π 能获得的期望累积奖励。
      Qπ(s, a) = Eπ[∑k=0∞ γk Rt+k+1 | St = s, At = a]
      其中 γ (0 ≤ γ ≤ 1) 是折扣因子,表示未来奖励相对于当前奖励的重要性。
  • 马尔可夫决策过程 (Markov Decision Process, MDP):强化学习问题通常被建模为 MDP,其核心是状态具有马尔可夫性,即未来状态只依赖于当前状态和动作,与历史状态无关。
  • 探索与利用 (Exploration vs. Exploitation):
    • 利用 (Exploitation):选择当前已知的能够带来最大奖励的动作。
    • 探索 (Exploration):尝试新的动作,以发现可能更优的策略,即使这些动作当前看起来不是最优的。
      在学习过程中需要平衡这两者。
5.3 在 SUMO 中集成强化学习的通用框架

将强化学习算法应用于 SUMO 仿真进行交通优化,通常遵循以下框架:

  1. 定义强化学习问题:

    • 智能体 (Agent): 通常是 Python 脚本中实现的 RL 算法。
    • 环境 (Environment): SUMO 仿真实例。TraCI 作为智能体与 SUMO 环境交互的桥梁。
  2. 核心交互循环:

    • 步骤 1: 状态获取 (State Observation)
      • Python 脚本通过 TraCI 从 SUMO 中获取当前交通状态。
      • 这些状态信息需要被构造成 RL 智能体能够理解的形式(状态向量或张量)。
      • 例如:traci.lanearea.getJamLengthVehicle("detector_id") 获取检测器覆盖区域的排队车辆数,traci.trafficlight.getPhase("tls_id") 获取信号灯当前相位。
    • 步骤 2: 动作选择 (Action Selection)
      • RL 智能体根据当前观察到的状态和其学习到的策略,选择一个动作。
      • 对于初学者,可能会使用如 ε-greedy 策略来平衡探索和利用。
    • 步骤 3: 动作执行 (Action Execution)
      • Python 脚本通过 TraCI 将智能体选择的动作应用到 SUMO 环境中。
      • 例如:traci.trafficlight.setPhase("tls_id", phase_index) 设置信号灯的新相位。
    • 步骤 4: 环境演化与奖励计算 (Environment Step & Reward Calculation)
      • 调用 traci.simulationStep() 使 SUMO 仿真向前推进一个或多个时间步。
      • 在新的仿真步之后,计算由于上一个动作导致的奖励。奖励函数的设计至关重要,它直接引导智能体的学习方向。
      • 例如,奖励可以是负的累积车辆等待时间变化量,或者负的交叉口总排队长度。
    • 步骤 5: 智能体学习/更新 (Agent Learning/Update)
      • 智能体使用 (当前状态, 动作, 奖励, 下一状态) 这个经验元组来更新其内部模型或策略。
      • 例如,在 Q-learning 中,更新 Q 表;在 DQN 中,更新神经网络的权重。
    • 步骤 6: 循环/终止 (Loop/Termination)
      • 重复步骤 1-5,直到达到预设的训练轮次 (episodes) 或仿真时间,或者满足某个终止条件。
  3. 关键设计要素:

    • 状态表示 (State Representation):
      • 如何将 SUMO 中的原始交通数据转换为有意义且对 RL 算法有效的状态特征。
      • 需要包含足够的信息以做出明智决策,但也要避免维度灾难。
      • 常见的状态特征包括:各个进口道的排队长度、车辆密度、平均速度、当前信号相位、已持续时间等。
    • 动作空间 (Action Space):
      • 定义智能体可以执行的操作集合。
      • 对于交通信号控制:
        • 离散动作:选择下一个要切换到的信号相位。
        • 参数化动作:决定当前绿灯相位的持续时间。
      • 对于车辆控制:改变速度、换道、选择路径。
    • 奖励函数 (Reward Function Design):
      • 这是 RL 应用中最具挑战性也最关键的部分之一。
      • 奖励函数必须准确反映优化目标。
      • 例如:
        • 最小化延误:reward = -total_delay 或 reward = previous_total_delay - current_total_delay。
        • 最小化排队长度:reward = -total_queue_length。
        • 最大化吞吐量:reward = number_of_vehicles_passed。
        • 组合奖励:reward = w1 * (-delay) + w2 * (-queue_length) + w3 * (throughput)。
      • 需要仔细设计以避免智能体学到非预期的"捷径"行为。
5.4 案例:基于 Q-learning 的自适应交通信号控制

Q-learning 是一种经典的、基于价值的、离策略 (off-policy) 的强化学习算法。它学习一个动作价值函数 Q(s, a),表示在状态 s 下执行动作 a 后能够获得的期望累积回报。

5.4.1 问题定义

  • 目标: 控制单个交叉口的交通信号灯,以最小化车辆的平均等待时间或总排队长度。
  • 环境: SUMO 模拟的单个交叉口。
  • 智能体: Q-learning 算法。

5.4.2 状态表示 (State Representation)

为了简化,我们采用离散化的状态表示。假设一个四路交叉口,每个进口道有一个主要的直行/左转相位组合。

  • 例如,可以将每个进口道的排队长度离散化为几个等级(如:0-5辆车为等级0,6-10辆车为等级1,11-15辆车为等级2,>15辆车为等级3)。
  • 当前有效的信号相位也可以作为状态的一部分。

假设有四个进口道(北N, 南S, 东E, 西W),每个进口道排队长度有 L 个等级。
如果只考虑排队长度,状态可以是 (q_N, q_S, q_E, q_W),其中 q_i 是第 i 个进口道的排队长度等级。
状态空间大小为 L4。如果再加上当前信号相位(假设有 P 个相位),则状态空间大小为 L4 * P。

示例:简化状态
为了代码演示,我们可以将状态简化为更易于管理的形式。例如,对于一个简单的十字路口,我们可以定义状态为代表各主要方向拥堵情况的元组。

假设交叉口 tls_id = "J1"。
我们将定义4个相位(例如):

  • Phase 0: 南北直行绿灯 (NS_Green)
  • Phase 1: 南北左转绿灯 (NSL_Green) (如果需要)
  • Phase 2: 东西直行绿灯 (EW_Green)
  • Phase 3: 东西左转绿灯 (EWL_Green) (如果需要)

为了简化,假设我们只控制两个主相位:NS 向绿灯,EW 向绿灯。

  • 状态 (State):我们可以将每个方向(N, S, E, W)进入交叉口前一定距离内的车辆数作为状态的一部分。
    例如,状态可以定义为 (num_veh_N, num_veh_S, num_veh_E, num_veh_W, current_phase_index)。
    为了使 Q 表可管理,这些车辆数需要被离散化。
    例如,0-5 辆车 -> 0, 6-10 辆车 -> 1, >10 辆车 -> 2。
    如果 current_phase_index 有2个值 (0 for NS_Green, 1 for EW_Green)。
    那么状态空间大小是 3 * 3 * 3 * 3 * 2 = 162 个状态。这对于 Q-table 来说是可行的。

5.4.3 动作空间 (Action Space)

  • 动作: 智能体在每个决策时刻选择下一个要激活的信号相位。
  • 对于我们的双相位示例:
    • 动作 0: 切换到/保持 NS_Green 相位。
    • 动作 1: 切换到/保持 EW_Green 相位。

5.4.4 奖励函数 (Reward Function)

  • 目标: 减少排队。
  • 奖励: 可以是负的当前交叉口总排队长度。
    reward = - (total_queue_length_N + total_queue_length_S + total_queue_length_E + total_queue_length_W)
    或者,奖励可以是切换前后排队长度的变化:
    reward = previous_total_queue - current_total_queue

5.4.5 Q-learning 算法

  • Q 表: 一个表格,存储每个 (状态, 动作) 对的 Q 值,Q[state][action]。
  • 学习率 (Learning Rate, α): 控制新学习到的信息在多大程度上覆盖旧信息。
  • 折扣因子 (Discount Factor, γ):衡量未来奖励的重要性。
  • 探索率 (Exploration Rate, ε): 在 ε-greedy 策略中,以 ε 的概率随机选择动作(探索),以 1-ε 的概率选择当前 Q 值最大的动作(利用)。

Q 值更新规则:
当智能体从状态 s 执行动作 a,得到奖励 r,并转移到新状态 s' 时,Q 值的更新如下:
Q(s, a) ← Q(s, a) + α * [r + γ * max<sub>a'</sub>(Q(s', a')) - Q(s, a)]

5.4.6 Python + TraCI 实现 Q-learning 交通信号控制 (概念性代码)

假设我们已经配置好了 SUMO 场景 (.sumocfg 文件),其中包含一个名为 "J1" 的交叉口,以及相应的检测器来获取排队长度。

import traci
import sumolib # 用于解析网络等
import numpy as np
import random
import os
import sys

# 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'")

# Q-learning 参数
ALPHA = 0.1  # 学习率
GAMMA = 0.9  # 折扣因子
EPSILON_START = 1.0 # 初始探索率
EPSILON_END = 0.05 # 最终探索率
EPSILON_DECAY = 0.995 # 探索率衰减因子
NUM_EPISODES = 1000 # 训练的总轮次数
SIMULATION_STEPS_PER_EPISODE = 3600 # 每轮仿真的步数
ACTION_DURATION = 10 # 每个选择的相位至少持续10个仿真步

# 交叉口和相位信息
TLS_ID = "J1" # 你的交通灯ID
# 假设我们只有两个主要相位: 0 (NS绿灯), 1 (EW绿灯)
# 实际中需要根据你的 .net.xml 文件中的 <tlLogic> 定义来确定
# 例如,相位0可能对应 traci.trafficlight.getCompleteRedYellowGreenDefinition(TLS_ID)[0].phases[0]
# 这里简化为相位索引
PHASE_NS_GREEN_INDEX = 0 # 假设南北绿灯是程序中的第0个相位
PHASE_EW_GREEN_INDEX = 2 # 假设东西绿灯是程序中的第2个相位 (跳过黄灯)
# 确保这些索引与你的信号灯定义匹配,或者你可以定义一个更详细的相位列表
# 例如 programID = traci.trafficlight.getProgram(TLS_ID)
# phases = traci.trafficlight.getCompleteRedYellowGreenDefinition(TLS_ID)[0].phases
# green_phases_indices = [i for i, p in enumerate(phases) if 'G' in p.state or 'g' in p.state]
# 为了简单,我们直接指定动作就是切换到这两个核心相位
ACTIONS = [PHASE_NS_GREEN_INDEX, PHASE_EW_GREEN_INDEX] # 可选动作:激活NS绿灯或EW绿灯
NUM_ACTIONS = len(ACTIONS)

# 状态离散化
# 假设有4条入口道,每条道的检测器ID
# 例如: "det_N_J1_0", "det_S_J1_0", "det_E_J1_0", "det_W_J1_0"
INCOMING_LANES_DETECTORS = {
   
   
    "N": ["lane_N_in_0"], # 假设北向进口道上的检测器对应车道 lane_N_in_0
    "S": ["lane_S_in_0"], # 南向
    "E": ["lane_E_in_0"], # 东向
    "W": ["lane_W_in_0"]  # 西向
}
# 实际应用中,检测器通常是 e1Detector 或 e2Detector (lane area detector)
# 这里用 getLaneIDList 获取车道,再用 getLastStepVehicleNumber 获取车道上的车辆数作为简化队列
# 或者使用 traci.lanearea.getJamLengthVehicle() 如果定义了 laneAreaDetector

MAX_CARS_PER_LANE_STATE = 3 # 离散化等级: 0 (0-5), 1 (6-10), 2 (>10)

# Q 表初始化
# 状态: (q_N, q_S, q_E, q_W, current_phase_logic_index)
# current_phase_logic_index: 0 for NS, 1 for EW (代表当前哪个方向是绿灯)
# q_N, q_S, q_E, q_W 的取值范围是 0, 1, 2
# current_phase_logic_index 的取值范围是 0, 1
q_table = np.zeros((MAX_CARS_PER_LANE_STATE, MAX_CARS_PER_LANE_STATE,
                    MAX_CARS_PER_LANE_STATE, MAX_CARS_PER_LANE_STATE,
                    len(ACTIONS), # 代表当前是哪个主要流向的绿灯
                    NUM_ACTIONS)) # Q(s,a)

def get_sumo_executable(gui=False):
    """获取SUMO或SUMO-GUI的可执行文件路径"""
    if gui:
        return "sumo-gui" # 返回SUMO GUI的可执行文件名
    return "sumo" # 返回SUMO命令行版本的可执行文件名

def start_simulation(sumocfg_file, gui=False):
    """启动SUMO仿真"""
    sumo_cmd = [get_sumo_executable(gui), "-c", sumocfg_file] # 构建SUMO启动命令
    sumo_cmd.extend(["--step-length", "1"]) # 设置仿真步长为1秒
    sumo_cmd.extend(["--remote-port", "8813"]) # 指定TraCI端口
    sumo_cmd.extend(["--waiting-time-memory", "1000"]) # 增加车辆等待时间记忆长度,用于获取准确的累计等待时间
    # sumo_cmd.extend(["--time-to-teleport", "-1"]) # 防止车辆瞬移,让拥堵更真实
    traci.start(sumo_cmd, port=8813) # 启动SUMO并通过TraCI连接
    print(f"SUMO started with command: {
     
     ' '.join(sumo_cmd)}") # 打印启动命令

def get_lane_queue_length(lane_id):
    """获取指定车道的排队车辆数 (简化版,实际中用检测器更佳)"""
    # 这是一个非常简化的版本,实际中应该用检测器
    # 例如 traci.lanearea.getJamLengthVehicle(detector_id) 或 traci.edge.getLastStepHaltingNumber(edge_id)
    # 这里我们用 traci.lane.getLastStepHaltingNumber(lane_id)
    try:
        return traci.lane.getLastStepHaltingNumber(lane_id) # 获取车道上一步的停止车辆数
    except traci.TraCIException:
        print(f"Warning: Could not get halting number for lane {
     
     lane_id}") # 打印警告信息
        return 0 # 返回0,如果获取失败

def discretize_queue(queue_length):
    """将排队长度离散化"""
    if queue_length <= 5: # 如果排队长度小于等于5
        return 0 # 返回离散状态0
    elif queue_length <= 10: # 如果排队长度小于等于10
        return 1 # 返回离散状态1
    else: # 其他情况
        return 2 # 返回离散状态2

def get_state():
    """从SUMO获取当前状态并离散化"""
    queues = [] # 初始化队列列表
    for direction_lanes in INCOMING_LANES_DETECTORS.values(): # 遍历所有方向的车道
        direction_queue = 0 # 初始化方向队列长度
        for lane_id in direction_lanes: # 遍历该方向的所有车道
            direction_queue += get_lane_queue_length(lane_id) # 累加车道队列长度
        queues.append(discretize_queue(direction_queue)) # 将离散化后的队列长度添加到列表

    # 获取当前信号灯相位逻辑 (哪个主要方向是绿灯)
    # 我们需要将实际的相位索引映射到我们定义的简化逻辑索引 (0 for NS, 1 for EW)
    current_sumo_phase = traci.trafficlight.getPhase(TLS_ID) # 获取当前SUMO相位索引
    
    # 这个映射逻辑非常重要,需要根据你的信号灯定义来确定
    # 假设 PHASE_NS_GREEN_INDEX (e.g., 0) 对应我们的逻辑状态 0
    # 假设 PHASE_EW_GREEN_INDEX (e.g., 2) 对应我们的逻辑状态 1
    # 其他黄灯、红灯相位,我们需要判断它们之前是哪个绿灯相位
    # 这是一个简化逻辑,实际中可能需要更复杂的判断或记录上一个绿灯相位
    # 为了简单,我们直接判断当前相位是否是我们定义的两个主绿灯相位之一
    current_phase_logic_index = 0 # 默认为NS方向绿灯的逻辑索引
    if current_sumo_phase == PHASE_EW_GREEN_INDEX: # 如果当前SUMO相位是东西绿灯
        current_phase_logic_index = 1 # 设置逻辑索引为1 (EW)
    elif current_sumo_phase == PHASE_NS_GREEN_INDEX: # 如果当前SUMO相位是南北绿灯
        current_phase_logic_index = 0 # 设置逻辑索引为0 (NS)
    else:
        # 如果是黄灯或全红,我们可能需要知道它从哪个绿灯转换而来
        # 为了简化,如果不是明确的NS或EW绿灯,我们可能基于上一个动作或一个默认值
        # 这里我们先假设它保持上一个逻辑状态,或者需要一个全局变量来跟踪
        # 在这个例子中,我们期望动作总是切换到明确的NS绿灯或EW绿灯之一
        # 如果是黄灯,它通常属于前一个绿灯阶段的延续。
        # 更鲁棒的方法是记录上一个 chosen_action (代表的逻辑相位)
        # 这里我们尝试从 traci.trafficlight.getProgram(TLS_ID) 和 traci.trafficlight.getPhase(TLS_ID)
        # 来推断当前是哪个方向的绿灯亮起。
        # 对于Q-learning,状态定义需要稳定。
        # 假设我们的动作直接设定到 NS_GREEN 或 EW_GREEN, 黄灯是过渡
        # 那么 current_phase_logic_index 可以反映当前哪个方向 *应该* 是绿灯
        # 或者我们可以更简单地,将当前实际的SUMO相位索引作为状态的一部分,
        # 但这会增加状态空间的维度。
        # 此处的简化:如果不是我们关心的两个主绿灯,就看上一次的动作是哪个。
        # 为了Q-table的索引,这个current_phase_logic_index必须是0或1.
        # 我们假设动作执行后,相位会变成我们期望的主绿灯相位之一
        # 所以,在get_state()时,这个值反映了*当前*哪个主要方向是绿灯。
        # 这个逻辑需要根据实际的信号灯配置来精确化。
        # 例如,如果你的相位0是NS绿,相位1是NS黄,相位2是EW绿,相位3是EW黄
        # 当 current_sumo_phase 是 0 或 1 时,逻辑上是NS绿。
        # 当 current_sumo_phase 是 2 或 3 时,逻辑上是EW绿。
        # 在这个示例中,我们之前定义了ACTIONS = [PHASE_NS_GREEN_INDEX, PHASE_EW_GREEN_INDEX]
        # 所以,如果当前相位是 PHASE_NS_GREEN_INDEX, 我们的逻辑相位就是0
        # 如果当前相位是 PHASE_EW_GREEN_INDEX, 我们的逻辑相位就是1
        # 如果是黄灯,它属于前一个绿灯。
        # traci.trafficlight.getPhase(TLS_ID) 返回的是程序中的绝对相位索引
        program_logic = traci.trafficlight.getCompleteRedYellowGreenDefinition(TLS_ID)[0] # 获取当前程序逻辑
        current_phase_definition = program_logic.phases[current_sumo_phase].state # 获取当前相位的灯色字符串
        
        # 一个更可靠的方法是检查当前相位定义中哪个方向是绿灯
        # 例如,如果 'G' 或 'g' 出现在代表南北向的灯位上
        # 但这依赖于灯色字符串的复杂解析。
        # 简化:如果当前是南北绿,phase_logic_index = 0, 如果是东西绿,则为 1
        # 黄灯阶段通常意味着前一个绿灯阶段的结束。
        # 为了Q-table索引,我们需要一个明确的逻辑状态。
        # 假设我们总是从一个绿灯相位切换到另一个绿灯相位(中间有黄灯)
        # state 中的 current_phase_logic_index 代表“哪个方向刚刚或正在经历绿灯”
        # 如果 traci.trafficlight.getPhase(TLS_ID) 是 PHASE_NS_GREEN_INDEX -> 0
        # 如果 traci.trafficlight.getPhase(TLS_ID) 是 PHASE_EW_GREEN_INDEX -> 1
        # 如果是黄灯,它紧随绿灯之后。
        # 这个state的 current_phase_logic_index 代表上一个或当前的主要绿灯方向。
        # 更好的做法可能是,这个状态元素代表“即将为哪个方向开绿灯的决策”已做出,
        # 或者直接使用上一个动作作为状态的一部分。

        # 为了Q-table的稳定,我们假设 current_phase_logic_index 代表当前 *主要* 的绿灯方向
        # 如果当前是黄灯,它通常指示着从哪个绿灯相位转换而来。
        # e.g. if current_sumo_phase is yellow after NS_GREEN, logic is still NS (0)
        # 为了简单,我们假设动作执行后,相位会变成我们期望的主绿灯相位之一
        # 然后我们才获取状态。所以这里应该反映的是已设置的绿灯。
        # 实际中,RL的状态需要精确定义。
        # 让我们假设一个全局变量 `last_chosen_logic_phase` 来记录上一个选择的逻辑相位
        # 并在动作执行后更新它。
        # state_tuple = tuple(queues + [last_chosen_logic_phase]) # 拼接成元组作为状态
        # 另一种方式:直接从当前相位判断。
        # 假设 phase 0, 1 是 NS (绿,黄),phase 2, 3 是 EW (绿,黄)
        # if current_sumo_phase in [0, 1]: current_phase_logic_index = 0
        # elif current_sumo_phase in [2, 3]: current_phase_logic_index = 1
        # 这个需要根据你的 `TLS_ID` 的具体相位定义来确定。
        # 对于本例,我们直接使用我们定义的 `ACTIONS` 列表中的索引。
        # 如果当前相位是ACTIONS[0] (PHASE_NS_GREEN_INDEX),则逻辑相位为0.
        # 如果当前相位是ACTIONS[1] (PHASE_EW_GREEN_INDEX),则逻辑相位为1.
        # 黄灯阶段较为复杂,它通常与前一个绿灯相位关联。
        # 为了Q表查找,这个逻辑索引必须是确定的。
        # 我们假设状态是在“绿灯”期间或即将进入绿灯时评估的。
        # 这里的 current_phase_logic_index 指的是当前哪个 *逻辑* 方向是绿灯。
        # 0 代表南北,1 代表东西。
        active_program_id = traci.trafficlight.getProgram(TLS_ID) # 获取当前活动的程序ID
        all_logics = traci.trafficlight.getAllProgramLogics() # 获取所有程序逻辑
        current_logic = None # 初始化当前逻辑
        for logic in all_logics: # 遍历所有逻辑
            if logic.programID == active_program_id: # 如果找到当前活动的程序ID
                current_logic = logic # 设置为当前逻辑
                break # 跳出循环
        
        if current_logic: # 如果找到了当前逻辑
            # 假设 PHASE_NS_GREEN_INDEX 是南北绿灯相位在当前 program 中的索引
            # 假设 PHASE_EW_GREEN_INDEX 是东西绿灯相位在当前 program 中的索引
            # 我们需要判断当前相位 `current_sumo_phase` 属于哪个逻辑方向。
            # 例如,如果 `current_sumo_phase` 等于我们定义的南北绿灯相位索引,
            # 或者与南北绿灯相关联的黄灯相位索引。
            # 这是一个困难点,因为SUMO的相位索引是绝对的,而我们的逻辑相位是抽象的。
            # 简化:我们假设 `last_action_logic_index` 是一个全局变量,在执行动作后更新。
            # `state = tuple(queues + [last_action_logic_index])`
            # 为避免引入全局变量,我们尝试从当前相位推断。
            # 如果当前相位是`PHASE_NS_GREEN_INDEX`,则逻辑相位为0。
            # 如果当前相位是`PHASE_EW_GREEN_INDEX`,则逻辑相位为1。
            # 对于黄灯,它属于前一个绿灯。
            # 让我们用一个更简单的状态,只基于队列。
            # 并且,当前相位信息由Q-table的结构隐式处理,或者我们让动作决定下一个相位,
            # 而不是将当前相位作为状态的一部分。
            # 如果动作是“切换到NS”或“切换到EW”,那么当前相位信息可能不是那么重要了,
            # 因为Q值是 Q(s, a),其中a已经是决策。
            # 让我们暂时移除 current_phase_logic_index from state for simplification of Q-table,
            # and make the Q-table Q[q_N][q_S][q_E][q_W][action_index]
            # q_table = np.zeros((MAX_CARS_PER_LANE_STATE, ..., NUM_ACTIONS))
            # 但是标准的Q-learning是 Q(s,a),所以状态中包含当前相位通常是必要的。
            
            # 重新考虑:state 中包含当前哪个 *主要方向* 是绿灯。
            # 0 for NS, 1 for EW.
            # 这个映射需要你根据自己的 .net.xml 中的 <tlLogic> 定义。
            # e.g. if phases 0,1,2 are for NS (G, y, G_ped), and 3,4,5 for EW (G,y,G_ped)
            # then if current_sumo_phase in [0,1,2], current_phase_logic_index = 0
            # else current_phase_logic_index = 1
            # 为了本代码的通用性,我们假设一个函数 `get_current_logic_phase_index(current_sumo_phase)`
            # 在这里我们硬编码一个简单的映射,假设PHASE_NS_GREEN_INDEX和相关的黄灯属于逻辑0,
            # PHASE_EW_GREEN_INDEX和相关的黄灯属于逻辑1。
            # 假设黄灯相位紧随绿灯相位。
            # if current_sumo_phase == PHASE_NS_GREEN_INDEX or current_sumo_phase == PHASE_NS_GREEN_INDEX + 1:
            # current_phase_logic_index = 0
            # elif current_sumo_phase == PHASE_EW_GREEN_INDEX or current_sumo_phase == PHASE_EW_GREEN_INDEX + 1:
            # current_phase_logic_index = 1
            # 这个逻辑需要用户根据自己的信号灯定义来适配。
            # 我们将假设 `current_phase_logic_index` 来源于上一次执行的动作的意图。
            # 或者,更简单,我们直接用当前SUMO相位模除一个基数(如果相位有规律)
            # 为了这个例子,我们将使用一个全局变量来跟踪上一个选择的逻辑相位。
            global last_selected_logic_phase_index # 声明使用全局变量
            state_tuple = tuple(queues + [last_selected_logic_phase_index]) # 组成状态元组
            return state_tuple # 返回状态元组
    return tuple([0]*len(INCOMING_LANES_DETECTORS) + [0]) # 如果无法获取逻辑,返回默认状态


def get_reward(current_queues_raw):
    """计算奖励,负的当前总排队长度"""
    # current_queues_raw 是一个包含各方向原始车辆数的列表或元组
    return -sum(current_queues_raw) # 返回负的总排队长度

def choose_action(state, epsilon):
    """使用epsilon-greedy策略选择动作"""
    if random.random() < epsilon: # 如果随机数小于epsilon
        return random.choice(range(NUM_ACTIONS)) # 随机选择一个动作 (探索)
    else: # 否则
        # state是一个元组 (qN, qS, qE, qW, current_phase_logic_idx)
        # q_table[state] 会因为 state 是元组而直接索引
        return np.argmax(q_table[state]) # 选择Q值最大的动作 (利用)

def run_episode(episode_num, current_epsilon, sumocfg_file, gui=False):
    """运行一个训练轮次"""
    global last_selected_logic_phase_index # 声明使用全局变量
    start_simulation(sumocfg_file, gui=gui) # 启动仿真
    
    # 初始设置一个默认的逻辑相位,例如南北绿灯
    last_selected_logic_phase_index = 0 # 假设初始是南北绿灯逻辑
    traci.trafficlight.setPhase(TLS_ID, ACTIONS[last_selected_logic_phase_index]) # 设置初始相位
    
    # 为了获取初始状态,先运行几步让交通流稳定一下,并应用初始相位
    for _ in range(5): # 运行5个仿真步
        traci.simulationStep() # 执行一步仿真

    current_state = get_state() # 获取初始状态
    total_reward_this_episode = 0 # 初始化本轮总奖励

    # 为了计算奖励,我们需要未离散化的队列长度
    raw_queues_for_reward = [] # 初始化原始队列长度列表
    for direction_lanes in INCOMING_LANES_DETECTORS.values(): # 遍历所有方向的车道
        direction_queue = 0 # 初始化方向队列长度
        for lane_id in direction_lanes: # 遍历该方向所有车道
            direction_queue += get_lane_queue_length(lane_id) # 累加车道队列长度
        raw_queues_for_reward.append(direction_queue) # 添加到原始队列列表

    simulation_time = 0 # 初始化仿真时间
    while simulation_time < SIMULATION_STEPS_PER_EPISODE: # 当仿真时间小于每轮最大步数
        action_logic_index = choose_action(current_state, current_epsilon) # 选择动作(逻辑索引 0 或 1)
        chosen_sumo_phase = ACTIONS[action_logic_index] # 将逻辑动作索引映射到SUMO相位索引

        traci.trafficlight.setPhase(TLS_ID, chosen_sumo_phase) # 应用选择的SUMO相位
        last_selected_logic_phase_index = action_logic_index # 更新上一个选择的逻辑相位索引

        # 让选定的相位持续一段时间
        for _ in range(ACTION_DURATION): # 循环相位持续时间
            if simulation_time >= SIMULATION_STEPS_PER_EPISODE: # 如果达到最大步数
                break # 跳出循环
            traci.simulationStep() # 执行一步仿真
            simulation_time += 1 # 仿真时间加1
        
        if simulation_time >= SIMULATION_STEPS_PER_EPISODE: # 再次检查是否达到最大步数
             # 在结束前获取最后的状态和奖励
            next_raw_queues_for_reward = [] # 初始化下一个原始队列长度列表
            for direction_lanes in INCOMING_LANES_DETECTORS.values(): # 遍历所有方向的车道
                direction_queue = 0 # 初始化方向队列长度
                for lane_id in direction_lanes: # 遍历该方向所有车道
                    direction_queue += get_lane_queue_length(lane_id) # 累加车道队列长度
                next_raw_queues_for_reward.append(direction_queue) # 添加到下一个原始队列列表
            
            reward = get_reward(next_raw_queues_for_reward) # 计算奖励
            next_state = get_state() # 获取下一个状态(虽然可能不会用于更新,因为是episode末尾)
            break # 结束循环

        next_raw_queues_for_reward = [] # 初始化下一个原始队列长度列表
        for direction_lanes in INCOMING_LANES_DETECTORS.values(): # 遍历所有方向的车道
            direction_queue = 0 # 初始化方向队列长度
            for lane_id in direction_lanes: # 遍历该方向所有车道
                direction_queue += get_lane_queue_length(lane_id) # 累加车道队列长度
            next_raw_queues_for_reward.append(direction_queue) # 添加到下一个原始队列列表
        
        reward = get_reward(next_raw_queues_for_reward) # 计算奖励
        next_state = get_state() # 获取下一个状态

        # Q-table 更新
        # current_state 是一个元组,可以直接用作q_table的索引
        # action_logic_index 是选择的动作索引 (0 或 1)
        best_next_action = np.argmax(q_table[next_state]) # 找到下一个状态下Q值最大的动作
        td_target = reward + GAMMA * q_table[next_state][best_next_action] # 计算TD目标值
        td_delta = td_target - q_table[current_state][action_logic_index] # 计算TD误差
        q_table[current_state][action_logic_index] += ALPHA * td_delta # 更新Q值

        current_state = next_state # 更新当前状态
        raw_queues_for_reward = next_raw_queues_for_reward # 更新原始队列长度
        total_reward_this_episode += reward # 累加奖励

    traci.close() # 关闭TraCI连接
    print(f"Episode {
     
     episode_num + 1}: Total Reward: {
     
     total_reward_this_episode}, Epsilon: {
     
     current_epsilon:.3f}") # 打印轮次信息
    return total_reward_this_episode # 返回本轮总奖励

# ---- 全局变量,用于 get_state ----
# 这个全局变量的引入是为了简化状态表示中“当前逻辑相位”的获取。
# 在更复杂的实现中,这部分状态可以从历史动作或者对SUMO相位的详细解析中得到。
last_selected_logic_phase_index = 0 # 初始化为0 (例如,代表NS绿灯逻辑)

# 主训练循环
if __name__ == '__main__':
    sumocfg_file_path = "your_scenario.sumocfg" # 指定你的SUMO配置文件路径
    # 请确保 "your_scenario.sumocfg" 文件存在,并且包含名为 "J1" 的交通灯
    # 以及在 INCOMING_LANES_DETECTORS 中定义的车道
    # 并且交通灯 "J1" 的程序中,相位 ACTIONS[0] 和 ACTIONS[1] 是有效的绿灯相位。

    # 示例:创建一个简单的SUMO场景文件(如果它们不存在)
    # 为了能运行,你需要一个最基本的 .net.xml, .rou.xml, .sumocfg
    # network_file = "rl_intersection.net.xml"
    # route_file = "rl_routes.rou.xml"
    # config_file = "rl_config.sumocfg"
    #
    # if not os.path.exists(network_file):
    #     with open(network_file, "w") as f:
    #         f.write("""<network>
    # <node id="J1" x="0.0" y="0.0" type="traffic_light"/>
    # <node id="N1" x="0.0" y="100.0" type="priority"/>
    # <node id="S1" x="0.0" y="-100.0" type="priority"/>
    # <node id="E1" x="100.0" y="0.0" type="priority"/>
    # <node id="W1" x="-100.0" y="0.0" type="priority"/>
    #
    # <edge id="N_in" from="N1" to="J1" priority="1">
    #    <lane id="lane_N_in_0" index="0" speed="13.89" length="100.0" shape="0.0,95.0 0.0,5.0"/>
    # </edge>
    # <edge id="S_in" from="S1" to="J1" priority="1">
    #    <lane id="lane_S_in_0" index="0" speed="13.89" length="100.0" shape="0.0,-95.0 0.0,-5.0"/>
    # </edge>
    # <edge id="E_in" from="E1" to="J1" priority="1">
    #    <lane id="lane_E_in_0" index="0" speed="13.89" length="100.0" shape="95.0,0.0 5.0,0.0"/>
    # </edge>
    # <edge id="W_in" from="W1" to="J1" priority="1">
    #    <lane id="lane_W_in_0" index="0" speed="13.89" length="100.0" shape="-95.0,0.0 -5.0,0.0"/>
    # </edge>
    #
    # <edge id="out_N" from="J1" to="N1" priority="1"/>
    # <edge id="out_S" from="J1" to="S1" priority="1"/>
    # <edge id="out_E" from="J1" to="E1" priority="1"/>
    # <edge id="out_W" from="J1" to="W1" priority="1"/>
    #
    # <tlLogic id="J1" type="static" programID="0" offset="0">
    #    <phase duration="30" state="GrGr"/> <!-- NS Green (Incorrect, should be e.g. GGrrGGrr for 4 approaches) -->
    #    <phase duration="5"  state="yryr"/> <!-- NS Yellow -->
    #    <phase duration="30" state="rGrG"/> <!-- EW Green -->
    #    <phase duration="5"  state="ryry"/> <!-- EW Yellow -->
    # </tlLogic>
    # <!-- A more correct phase definition for a 4-arm intersection:
    # J1 has incoming edges: E_in, N_in, S_in, W_in (order matters for 'state' string)
    # Connections:
    # E_in -> out_W, out_N, out_S
    # N_in -> out_S, out_E, out_W
    # S_in -> out_N, out_E, out_W
    # W_in -> out_E, out_N, out_S
    # Assuming standard 12-signal group (3 per approach: Left, Through, Right)
    # State string: E_L, E_T, E_R, N_L, N_T, N_R, S_L, S_T, S_R, W_L, W_T, W_R
    # Example state string for NS Green (N-S through, N-S Right, S-N Right):
    # Phase 0 (NS Green): "rrgGgrrgGgrr" (assuming E,N,S,W order for approaches in TL definition)
    # Phase 1 (NS Yellow): "rryygrryygrr"
    # Phase 2 (EW Green): "GgrrGgrrggGG"
    # Phase 3 (EW Yellow): "yyrryyrrggYY"
    # The PHASE_NS_GREEN_INDEX and PHASE_EW_GREEN_INDEX must match these.
    # For this example code, let's assume a simplified phase definition:
    # <tlLogic id="J1" type="static" programID="0" offset="0">
    #    <phase duration="30" state="GGggrrrrGGgg"/> <!-- NS Green (index 0) -->
    #    <phase duration="4"  state="yyyyrrrryyyy"/> <!-- NS Yellow (index 1) -->
    #    <phase duration="30" state="rrrrGGggrrrr"/> <!-- EW Green (index 2) -->
    #    <phase duration="4"  state="rrrryyyyrrrr"/> <!-- EW Yellow (index 3) -->
    # </tlLogic>
    # Then PHASE_NS_GREEN_INDEX = 0, PHASE_EW_GREEN_INDEX = 2
    # The `state` attribute in <phase> refers to the signal states for all links controlled by the TLS.
    # The order of characters in `state` corresponds to the order of <connection> elements for that TLS in the .net.xml file
    # OR, if `tlType="static"` and no connections specified, it's based on incoming lane order.
    # It's CRUCIAL that ACTIONS[0] and ACTIONS[1] correctly set the desired green p
SUMO与python联合仿真(跟驰与换道) 这个对于我来说,是一个非常像解决的问题,比如我想要让其在仿真时间为20s时开始减速,这时应该当step=19时设置这一函数,其次是车辆类型depart参数时,depart表示进入仿真的时间,这个时间如果设置为0时,则出现在仿真场景中的仿真时间是1s。因为我的一个场景是需要均匀减速的,但是我用这个函数时出了问题,他是比如我设置了-2的减速度,他不会立即以这个减速度减速,而是减速度慢慢减至-2,如我设置在仿真20s时减速。要注意的是,他这个代码是关闭自动换道和跟驰的,因此在设置强制换道时,该操作不受影响。 阅读详情

相关推荐

使用TraCI在SUMO模拟器中应用强化学习的方法

这个模拟是为了演示车辆如何通过选择正确的路径来避免交通拥堵,来学习到达目的地。它还展示了车辆如何根据收到的奖励优化其路径选择策略。 一、创建路线文件,用于构建仿真环境 需要准备一个包含道路信息的网络文件和一个包含车辆路线信息的路线文件,下图显示了由 .net文件和.rou文件生成的模拟道路网络。 绿色小长方形是阻塞道路(也就是Bottleneck),左侧的红色小三角是模拟车辆,其目的地位于网络的右侧。 二、图像的转换(flag)...

xielang13的博客 2546

【Python】 SUMO + Python 联合仿真平台

好的,我理解您对“SUMO + Python 联合仿真平台”深度剖析的极高要求,期望获得前所未有的信息量、详尽的代码示例以及覆盖从基础到进阶的全面知识。生成一篇达到10万字乃至更高数量级的文章,在单次交互中是难以企及的,但我将竭尽所能,为您提供一个极其详尽和深入的开端,并在此基础上持续为您生成内容,力求逐步满足您的需求。我们将从最基础的概念和机制开始,逐步深入到高级应用、复杂场景和运维精髓。请注意,由于内容的庞大和深入,这会是一个循序渐进的过程。现在,让我们开始第一部分:SUMO 与 Python 联合仿真

宅男很神经 3758

自动驾驶变道博弈实战:用Python+SUMO模拟混合交通中的纳什均衡

本文探讨了自动驾驶变道博弈的实战应用,通过Python+SUMO模拟混合交通中的纳什均衡。文章详细介绍了博弈论在自动驾驶变道决策中的应用,包括安全收益、效率收益和舒适度成本的计算,以及如何构建博弈矩阵并求解纳什均衡。通过SUMO-Python联合仿真平台,展示了自动驾驶车辆与人类驾驶车辆的交互决策过程,并提供了优化模型和效果验证的方法。

w3x4y的博客 648

【仿真】CARLA实战避坑指南:从SUMO联调到Docker部署的典型问题解析

本文详细解析了CARLA仿真平台在SUMO联调、Traffic Manager使用和Docker部署中的典型问题及解决方案。从车辆ID映射错误到同步机制配置,再到Docker环境下的日志录制失效和显卡驱动兼容性问题,提供了实战避坑指南,帮助开发者高效搭建自动驾驶仿真环境。

weixin_26782929的博客 380

SUMO与Python联合仿真实战:从环境配置到交通流优化

本文详细介绍了SUMO与Python联合仿真的实战指南,从环境配置到交通流优化。通过SUMO-Python联合仿真平台,用户可以灵活控制交通仿真过程,集成机器学习算法,实现高效的交通网络模拟。文章涵盖了环境配置、交通场景构建、Python控制仿真及进阶优化技巧,适合交通工程和自动驾驶领域的研究人员。

weixin_29051193的博客 448

从零搭建CARLA-SUMO联合仿真环境:详细配置与避坑指南

本文详细介绍了如何从零搭建CARLA-SUMO联合仿真环境,涵盖Windows和Linux双平台的基础配置、CARLA与SUMO的安装与配置、联合仿真实战步骤以及常见问题解决方案。通过具体命令和调试技巧,帮助开发者快速掌握联合仿真技术,避免常见错误,提升开发效率。

weixin_29094539的博客 595

SUMO与CARLA联合仿真:构建宏观可控、微观可感的车路协同验证体系

交通仿真系统是智能网联汽车研发的核心基础设施,其本质是在数字空间中复现真实道路环境下的车辆交互与动态演化。基于离散事件驱动的SUMO擅长建模百万级交通流、信号配时与路网级拥堵传播,而基于物理引擎的CARLA则提供高保真感知数据、车辆动力学响应与决策执行能力。二者联合并非简单接口对接,而是通过时间步长协商、进程级解耦与数据契约设计,实现宏观交通状态到微观车辆行为的可信映射。该技术已广泛应用于L4自动驾驶算法回归测试、车路协同场景生成及数字孪生交通推演等工程实践,显著提升测试复现性与场景覆盖度。

davy57345的博客 365

CARLA与SUMO联合仿真:构建高保真自动驾驶测试环境

自动驾驶仿真技术是算法开发和验证的关键环节,它通过构建虚拟环境来模拟真实世界的复杂交通场景。其核心原理在于将交通流逻辑与高保真渲染解耦,利用协同仿真架构实现数据实时同步。这种技术能极大提升测试效率与安全性,为感知、规划和控制算法的迭代提供近乎无限的测试用例。在工程实践中,CARLA与SUMO的联合仿真方案尤为典型:CARLA提供逼真的传感器模型和物理引擎,负责环境渲染与主角车交互;SUMO则作为微观交通流仿真的权威,生成符合交通工程理论的动态背景车流。通过TraCI协议进行数据桥接,二者优势互补,共同构建出

weixin_30340617的博客 364

保姆级教程:在Windows上搞定CARLA 0.9.14与SUMO的联合仿真(含DirectX报错解决)

本文提供了一份详细的Windows系统下CARLA 0.9.14与SUMO联合仿真的保姆级教程,涵盖从环境准备到实战运行的全过程。特别针对DirectX报错等常见问题提供了解决方案,帮助开发者快速搭建联合仿真环境,实现CARLA与SUMO的无缝协同工作。

weixin_30736301的博客 334

sumo-rl:一个简单的界面,用于通过SUMO实例化强化学习环境,以进行交通信号控制。 与OpenAI的Gym Env和RLlib的MultiAgentEnv兼容

相扑 SUMO-RL通过用于交通信号控制的提供了一个简单的界面来实例化强化学习环境。 主类继承了的 。 如果使用参数'single-agent = True'实例化,则其行为类似于来自的常规 。 负责使用 API检索信息并在交通信号灯上。 该存储库的目标: 提供一个简单的界面,以与使用SUMO的交通信号控制强化学习一起使用 支持Multiagent RL 与gym.Env和流行的RL库(例如和 易于定制:状态和奖励定义易于修改 安装 安装最新版本的SUMO: sudo add-apt-repository ppa:sumo/stable sudo apt-get update sudo apt-get install sumo sumo-tools sumo-doc 不要忘记设置SUMO_HOME变量(默认的sumo安装路径为/ usr / share / sumo) ec

2017阿里内推笔试题--算法工程师(运筹优化)

2017阿里内推笔试题–算法工程师(运筹优化)题目沐哲是一个菜鸟仓库的一个拣货员,但他有非常个怪异的习惯。每次拣货的重量都要比之前拣的一个轻,每次拣到货后都可以得到1块钱,沐哲想知道这样最多能赚多少钱 32 34 7 33 21 2 13 12 3 11 26 36 16 30 22 1 24 14 20 23 25 5 19 29 27 15 9 17 31 4 6 18 8 10 3

shawpan的专栏 1万+

Raspberry Pi 4B 蓝牙串口(SPP)配置与使用

Linux、Android、Windows 等操作系统带有在引导期间启动的蓝牙守护进程/蓝牙服务。 对于与监视器连接并使用 GUI(如 GTK 等)运行的设备,只需多次单击即可轻松启用并连接到附近的设备。 但是在无头设备的情况下,我们必须依赖于需要在命令行上运行的不同命令。 这篇博客我们将讨论如何在无头树莓派上通过蓝牙设备连接附近。

视觉与物联智能 2730

史上最牛逼的导航网站(很全很详细)

今天,给大家推荐47个聚合型的导航网站 每一个都收录上百个网站,1个能顶100个,丝毫没有夸张的成分。 (推荐收藏) 设计师导航网站 01 优设导航 https://hao.uisdc.com/ 设计师必备的导航网站,包含设计工具、素材资源、字体设计、配色方案、酷站模板等。 界面清晰无广告,收录网站超过520个。 02 设计导航 http://h...

孙叫兽的博客 7万+

EEMD算法原理及应用

本博客主要概括EEMD算法的背景、原理以及应用,利用EEEMD对信号进行分解,然后选择有用的IMF分量进行叠加以完成信号的重构,从而实现对信号降噪。本文配套的源代码见文中链接,可供大家免费下载。

总结论文中的算法并给出论文的复现代码 4万+

WPF 上位机可视化天花板:HandyControl 搭建 SCADA 系统

HandyControl 凭借「丰富的工业级控件、灵活的主题定制、优异的性能」,成为 WPF 开发 SCADA 系统的首选 UI 库。本文搭建的 SCADA 系统涵盖实时监控、设备控制、报警管理、趋势分析四大核心功能,通过 HandyControl 实现了工业级可视化效果,开发效率提升 60% 以上。控件开箱即用:无需自定义仪表盘、趋势图、指示灯等 SCADA 核心控件;可视化效果专业:工业风主题、动态颜色切换、清晰的状态呈现,契合工业场景需求;

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1114

【100分】【等和子数组最小和】

逆序排序,这样能够使得在分组时优先选取较大的元素,以便将元素分配到较小的子集中,从而使得每个子集的和相对均衡。给定一个数组nums,将元素分为若干个组,使得每组和相等,求出满足条件的所有分组中,组内元素和的最小值。分成若干个组,使得每组的和相等,并求出满足条件的所有分组中,组内元素和的最小值。在递归的过程中,我们需要注意一个优化点,即避免将相同和的桶重复计算。4 个子集(5),(1,4),(2,3),(2,3)2 个子集(5, 1, 4),(2,3, 2,3)个子集,则返回子集和的最小值,如果不能,则将。

weixin_54707168的博客 1161

Google Earth Engine(GEE)——将sentinel - 2影像所有波段重采样到10m分辨率 (resample 和 projection的使用)

影像重投影是将一个影像从一个坐标系投影到另一个坐标系的过程。这通常涉及到将像素从一个平面坐标系变换到另一个平面坐标系。这可以用于许多应用中,例如建立数字高程模型(DEM)和地图制图。影像重投影通常需要进行数学计算,包括转换矩阵和插值算法,以确保像素值的准确性和精度。这次我们将对哨兵2好数据中20m分辨率的波段,重采样到10m分辨率中,然后同一参与计算。

此星光明博客 3102

【C2000系列DSP的双向DC-DC电源】基于TMS320F28034的DC-DC项目开发流程

理解DC-DC 双向电源(基于 TMS320F28034)的开发流程,需要从功能拆解、开发步骤、验证 / 测试项

昔时扬尘处 315

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

上一篇: 【Python】 SUMO + Python 联合仿真平台
下一篇: 【Python】 U盘CHK文件及误删文件恢复深度探索
宅男很神经
博客等级 码龄5年 7949粉丝 1249原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宅男很神经

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值