《TF2.x强化学习手册》P59-P65-SARSA-Q-learning

文章目录

    • 实现SARSA算法和对应的强化学习智能体
      • 前期准备
      • 实现步骤
      • 工作原理
        • 初始化
        • 算法流程
    • 构建基于Q学习的智能体
      • 前期准备
      • 实现步骤
      • 工作原理
      • SARSA 算法的收敛性:
      • SARSA 适合在线学习和真实系统:
      • Q 学习算法的适用性:

实现SARSA算法和对应的强化学习智能体

  • SARSA(State-Action-Reward-State-Action)是一种强化学习算法,主要用于估计给定策略下的最优状态-动作价值函数(QQ 函数)。它是一种在轨策略(on-policy)算法,意味着它使用当前策略来选择动作。

如何实现SARSA算法(状态-行动-奖励-状态-行动)

以及如何使用SARSA开发和训练智能体

SARSA可以应用与无模型种子问题,并对未知马尔科夫决策过程MDP的价值函数进行优化。

前期准备

import numpy as np
import random

实现步骤

使用一个函数实现SARSA学习算法的更新

并使用 ϵ \epsilon ϵ-greedy的探索策略。

两者结合就可以得到一个在给定的强化学习环境中工作的完整智能体。

  1. 定义一个实现SARSA算法的函数,并用0初始化状态-动作价值

    def sarsa(env, max_episodes):grid_action_values = np.zeros((len(env.distinct_states), env.action_space.n))
    
  2. 根据环境的配置更新目标状态和炸弹状态的价值

    grid_action_values[env.goal_state] = 1
    grid_action_values[env.bomb_state] = -1
    
  3. 定义折扣因子gamma和学习率超参数 α \alpha α 。同样创建一个别名q表示grid_action_values

    gamma = 0.99  # discounting factor
    alpha = 0.01  # learning rate
    # q: state-action-value function
    q = grid_action_values
    
  4. 实现外部循环

    for episode in range(max_episodes):step_num = 1done = Falsestate = env.reset()action = greedy_policy(q[state], 1)
    
  5. 实现内循环,其中包括SARSA学习的更新步骤

    while not done:next_state, reward, done = env.step(action)step_num += 1decayed_epsilon = gamma ** step_num  # Doesn't have to be gammanext_action = greedy_policy(q[next_state], decayed_epsilon)q[state][action] += alpha * (reward + gamma * q[next_state][next_action] - q[state][action])state = next_stateaction = next_action
    
    1. 初始化:选择一个初始状态 S S S 和在该状态下执行的动作 A A A
    2. 循环直到结束
      • 执行动作 A A A,观察奖励 R R R 和下一个状态 S ′ S' S
      • 使用策略选择在状态 S ′ S' S 下要执行的动作 A ′ A' A
        在数学语言中,这些步骤可以表示为:
        S ′ , R , done = env.step ( A ) step_num + = 1 ϵ = γ step_num A ′ = greedy_policy ( Q ( S ′ ) , ϵ ) S', R, \text{done} = \text{env.step}(A) \\ \text{step\_num} += 1 \\ \epsilon = \gamma^{\text{step\_num}} \\ A' = \text{greedy\_policy}(Q(S'), \epsilon) S,R,done=env.step(A)step_num+=1ϵ=γstep_numA=greedy_policy(Q(S),ϵ)
    3. 更新 Q 值
      • 更新 Q 值 Q ( S , A ) Q(S, A) Q(S,A) 使用以下公式:
        Q ( S , A ) ← Q ( S , A ) + α [ R + γ Q ( S ′ , A ′ ) − Q ( S , A ) ] Q(S, A) \leftarrow Q(S, A) + \alpha \left[ R + \gamma Q(S', A') - Q(S, A) \right] Q(S,A)Q(S,A)+α[R+γQ(S,A)Q(S,A)]
        其中:
      • α \alpha α 是学习率。
      • γ \gamma γ 是折扣因子。
      • Q ( S ′ , A ′ ) Q(S', A') Q(S,A) 是在状态 S ′ S' S 下执行动作 A ′ A' A 的 Q 值。
    4. 转移到下一个状态和动作
      • 将当前状态 S S S 更新为下一个状态 S ′ S' S
      • 将当前动作 A A A 更新为下一个动作 A ′ A' A
        在数学语言中,这些步骤可以表示为:
        S = S ′ A = A ′ S = S' \\ A = A' S=SA=A
    5. 重复步骤 2-4,直到达到终止状态(done 为 True)。
  6. 在SARSA的最后一步,可视化状态-动作价值函数

    visualize_grid_action_values(grid_action_values)
    
  7. 实现智能体要使用的 ϵ \epsilon ϵ-greedy策略 greedy_policy()

    def greedy_policy(q_values, epsilon):"""Epsilon-greedy policy """if random.random() >= epsilon:return np.argmax(q_values)else:return random.randint(0, 3)
    
    • if random.random() >= epsilon::这里,random.random() 生成一个0到1之间的随机浮点数。如果这个随机数大于或等于 epsilon,那么执行 if 分支中的代码。
      • return np.argmax(q_values):如果条件为真,函数返回 q_values 数组中最大值的索引。这个索引代表了具有最高Q值的动作,也就是我们基于当前知识认为的最佳动作。
    • else::如果 random.random() 生成的随机数小于 epsilon,则执行 else 分支中的代码。
      • return random.randint(0, 3):在这种情况下,函数返回一个从0到3(包含0和3)的随机整数,代表随机选择的动作。这表明在 epsilon-greedy 策略中,有一定概率(由 epsilon 决定)我们会选择一个随机动作而不是最佳动作,以鼓励探索环境。
  8. 实现__main__函数并运行SARSA算法

    if __name__ == "__main__":max_episodes = 4000env = GridworldV2Env(step_cost=-0.1, max_ep_length=30)sarsa(env, max_episodes)
    

    在这里插入图片描述

工作原理

SARSA是一种基于时序差分学习的在轨策略(on-policy)控制算法。

本节使用SARSA算法估计最优状态-动作价值。SARSA算法与Q学习非常相似。

在每次迭代中,SARSA算法都会根据当前的状态和动作来更新价值表,目标是找到每个状态下的最优策略。通过这种方式,算法可以学习如何在给定的环境中取得最佳的行动策略。

总结一下,SARSA 算法的核心在于它在更新 Q 值时考虑了下一个状态 S ′ S' S 和下一个动作 A ′ A' A,而不是像 Q-Learning 那样考虑在下一个状态 S ′ S' S 下所有可能动作的最大 Q 值。这使得 SARSA 是一种同策略(on-policy)方法,因为它使用当前策略来选择下一个动作 A ′ A' A

Initialize Q ( s , a ) , ∀ s ∈ S , a ∈ A ( s ) Q(s,a),\forall s\in\mathcal{S},a\in\mathcal{A}(s) Q(s,a),sS,aA(s), arbitrarily, and Q ( t e r m i n a l − s t a t e , ⋅ ) = 0 Q(terminal-state,\cdot)=0 Q(terminalstate,)=0
Repeat (for each episode):
Initialize ⁡ S \operatorname{Initialize}S InitializeS
Choose A A A from S S S using policy derived from Q Q Q (e.g., ϵ \epsilon ϵ-greedy)
Repeat (for each step of episode):
T a k e a c t i o n A , o b s e r v e R , S ′ {\mathrm{Take~action~}A,{\mathrm{~observe~}}R,S^{\prime}} Take action A, observe R,S
Choose A ′ A^\prime A from S ′ S^\prime S using policy derived from Q Q Q (e.g., ϵ \epsilon ϵ-greedy)
Q ( S , A ) ← Q ( S , A ) + α [ R + γ Q ( S ′ , A ′ ) − Q ( S , A ) ] Q(S,A)\leftarrow Q(S,A)+\alpha\left[R+\gamma Q(S^{\prime},A^{\prime})-Q(S,A)\right] Q(S,A)Q(S,A)+α[R+γQ(S,A)Q(S,A)]
S ˙ ← S ′ ; \dot{S} \leftarrow S^{\prime }; S˙S; A ← A ′ ; A\leftarrow A^{\prime }; AA; until S S S is terminal

初始化
  • Q ( s , a ) Q(s,a) Q(s,a) 初始化:对于所有状态 s s s 和在状态 s s s 下可采取的动作 a a a Q Q Q 函数被任意初始化。 Q Q Q 函数表示在特定状态 s s s 下采取动作 a a a 的期望回报。
  • 终端状态 Q Q Q:对于终端状态,所有动作的 Q Q Q 值设为0,因为终端状态之后没有进一步的奖励。
算法流程
  1. 重复(每个剧集)

    • 对于每个剧集(episode),算法会进行以下步骤。
    • 初始化状态 S S S:选择一个初始状态 S S S
  2. 选择动作 A A A

    • 根据当前的 Q Q Q 函数(例如使用 ϵ \epsilon ϵ-贪婪策略)从状态 S S S 中选择一个动作 A A A
  3. 重复(每个剧集的每一步)

    • 采取动作并观察:执行动作 A A A,观察获得的奖励 R R R 和下一个状态 S ′ S' S
    • 选择下一个动作 A ′ A' A:在新的状态 S ′ S' S 下,使用基于 Q Q Q 函数的策略(如 ϵ \epsilon ϵ-贪婪)选择下一个动作 A ′ A' A
  4. 更新 Q Q Q 函数

    • 使用以下公式更新 Q Q Q 函数:
      Q ( S , A ) ← Q ( S , A ) + α [ R + γ Q ( S ′ , A ′ ) − Q ( S , A ) ] Q(S,A) \leftarrow Q(S,A) + \alpha \left[ R + \gamma Q(S',A') - Q(S,A) \right] Q(S,A)Q(S,A)+α[R+γQ(S,A)Q(S,A)]

    • 其中, α \alpha α 是学习率, γ \gamma γ 是折扣因子。

  5. 更新状态和动作

    • S ′ S' S 更新为新的当前状态 S S S
    • A ′ A' A 更新为新的当前动作 A A A
    • 这个过程一直重复,直到 S S S 成为终端状态。

构建基于Q学习的智能体

Q 学习可以应用于无模型的强化学习问题。它支持离轨(off-policy)学习,为使用其他策略或其他智能体收集经验的问题提供了实用的解决方案。

本节将构造一个可工作的强化学习智能体,使用Q学习算法生成状态-价值函数

前期准备

impoet numpy as np
import random

实现步骤

用一个函数实现Q学习算法,并使用 ϵ \epsilon ϵ-greedy的探索策略。

  1. 定义一个实现Q学习算法的函数,并用0初始化状态-动作价值

    def q_learning(env, max_episodes):grid_action_values = np.zeros((len(env.distinct_states), env.action_space.n))
    
  2. 根据环境的配置更新目标状态和炸弹状态的值

    grid_action_values[env.goal_state] = 1
    grid_action_values[env.bomb_state] = -1
    
  3. 定义折扣因子gamma 和学习率超参数 alpha 。同样创建一个别名q表示grid_action_values

    gamma = 0.99  # discounting factor
    alpha = 0.01  # learning rate
    # q: state-action-value function
    q = grid_action_values
    
  4. 实现外部循环

    for episode in range(max_episodes):step_num = 1done = Falsestate = env.reset()
    
  5. 实现内循环,其中包括Q学习的更新。此外对 ϵ \epsilon ϵ-greedy策略使用 ϵ \epsilon ϵ进行衰减

    while not done:decayed_epsilon = 1 * gamma ** step_num  # Doesn't have to be gammaaction = greedy_policy(q[state], decayed_epsilon)next_state, reward, done = env.step(action)# Q-Learning updategrid_action_values[state][action] += alpha * (reward + gamma * max(q[next_state]) - q[state][action])step_num += 1state = next_state
    

    在 Q-Learning 算法中,Q 值的更新可以用以下数学公式表示:
    Q ( s , a ) Q(s, a) Q(s,a) 表示在状态 s s s 下执行动作 a a a 的 Q 值。在时间步 t t t 时,智能体观察到状态 S t S_t St,执行动作 A t A_t At,然后到达新状态 S t + 1 S_{t+1} St+1 并获得奖励 R t + 1 R_{t+1} Rt+1。Q 值的更新公式为:
    Q ( S t , A t ) ← Q ( S t , A t ) + α [ R t + 1 + γ max ⁡ a ′ Q ( S t + 1 , a ′ ) − Q ( S t , A t ) ] Q(S_t, A_t) \leftarrow Q(S_t, A_t) + \alpha [R_{t+1} + \gamma \max_{a'} Q(S_{t+1}, a') - Q(S_t, A_t)] Q(St,At)Q(St,At)+α[Rt+1+γmaxaQ(St+1,a)Q(St,At)]
    其中:

    • α \alpha α 是学习率,取值在 0 到 1 之间,决定了新信息对旧信息的覆盖程度。
    • γ \gamma γ 是折扣因子,取值在 0 到 1 之间,用于平衡即时奖励和未来奖励。
    • max ⁡ a ′ Q ( S t + 1 , a ′ ) \max_{a'} Q(S_{t+1}, a') maxaQ(St+1,a) 表示在状态 S t + 1 S_{t+1} St+1 下所有可能动作的 Q 值中的最大值。
    • R t + 1 R_{t+1} Rt+1 是在时间步 t + 1 t+1 t+1 收到的奖励。
      这个公式表示,Q 值的更新等于当前 Q 值加上一个学习率 α \alpha α 乘以奖励 R t + 1 R_{t+1} Rt+1 和折扣后的未来最大 Q 值 γ max ⁡ a ′ Q ( S t + 1 , a ′ ) \gamma \max_{a'} Q(S_{t+1}, a') γmaxaQ(St+1,a) 与当前 Q 值 Q ( S t , A t ) Q(S_t, A_t) Q(St,At) 之差。这个差值称为 TD 误差(Temporal-Difference error),它衡量了基于当前估计的 Q 值和基于实际经验的新估计之间的差异。
  6. 最后一步,对状态-动作价值函数进行可视化

    visualize_grid_action_values(grid_action_values)
    
  7. 实现智能体要使用的 ϵ \epsilon ϵ-greedy策略

    def greedy_policy(q_values, epsilon):"""Epsilon-greedy policy """if random.random() >= epsilon:return np.argmax(q_values)else:return random.randint(0, 3)
    
  8. 实现__main__函数,并运行Q学习算法

    if __name__ == "__main__":max_episodes = 4000env = GridworldV2Env(step_cost=-0.1, max_ep_length=30)q_learning(env, max_episodes)
    

在这里插入图片描述

工作原理

Q学习算法中涉及Q值的更新,总结为: Q [ s , a ] = Q [ s , a ] + λ ∗ ( r + γ ∗ m a x a ′ ( Q [ s ′ , a ′ ] ) − Q [ s , a ] ) Q[s,a]=Q[s,a]+\lambda*(r+\gamma*max_{a^{\prime}}(Q[s^{\prime},a^{\prime}])-Q[s,a]) Q[s,a]=Q[s,a]+λ(r+γmaxa(Q[s,a])Q[s,a])

Q [ s , a ] Q[s,a] Q[s,a] 是当前状态s和动作q的Q函数值

m a x a ′ ( Q [ s ′ , a ′ ] ) max_{a^{\prime}}(Q[s^{\prime},a^{\prime}]) maxa(Q[s,a]) 用于从下一步可能的Q值中选择最大值

两者的主要区别

  • 策略依赖性:SARSA 使用当前策略来选择下一个动作,而 Q 学习不依赖当前策略来更新 Q 值。
  • 探索与利用:SARSA 的探索和利用是同步进行的,而 Q 学习在更新时完全依赖于最大 Q 值,可能会更倾向于探索。
  • 稳定性:SARSA 更稳定,因为它直接依赖于当前策略。Q 学习可能会因为其贪婪性质而在某些情况下导致不稳定的学习过程。

SARSA算法比Q学习算法具有更好的收敛性,因此它更适合在线学习或再真实系统上学习。

Q学习更适合于模拟环境或真实系统上资源(如时间/金钱)不太昂贵的情况下训练。

SARSA 算法的收敛性:

  • 同策略更新:SARSA 使用当前策略来选择下一个动作,这意味着它的更新是基于当前策略的动作后果。因此,它的学习过程与实际策略紧密相关,导致学习过程更加稳定。
  • 逐步学习:SARSA 在每个时间步都进行学习,而不是只依赖于最终的奖励。这种逐步的学习方式有助于避免由于过分依赖未来奖励估计而产生的潜在不稳定性和偏差。
  • 避免过估计:由于 SARSA 在更新 Q 值时考虑了下一个动作的 Q 值,而不是最大 Q 值,它不太可能产生过估计(overestimation)问题,这在 Q 学习中是一个常见问题。

SARSA 适合在线学习和真实系统:

  • 在线学习:在线学习要求算法能够从实时数据中快速且稳定地学习。SARSA 的稳定收敛性使其更适合于这种场景,因为它能够确保策略的持续改进而不会出现剧烈波动。
  • 真实系统:在真实系统中,错误的决策可能带来严重的后果。SARSA 的稳定性有助于减少这种风险,因为它不太可能因为过度探索而导致灾难性的决策。

Q 学习算法的适用性:

  • 探索性:Q 学习算法通过在每个时间步选择最大化未来奖励的动作来进行学习,这可能导致算法进行更多的探索。这种探索性在资源充足的情况下是有益的,因为它有助于快速发现最优策略。
  • 模拟环境:在模拟环境中,错误的决策不会带来实际损失,因此 Q 学习的探索性可以被充分利用,以快速找到最优解。
  • 资源成本:Q 学习可能需要更多的迭代和探索来达到收敛,这在时间和计算资源上可能比较昂贵。在资源受限的情况下,Q 学习可能不是最佳选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/46302.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HDC使用常见命令

HDC(HarmonyOS Device Connector)是为开发人员提供的用于调试的命令行工具,通过该工具可以在windows/linux/mac系统上与真实设备进行交互。 使用HDC前,需要配置相关环境变量: 在此电脑 > 属性 > 高级系统设置 &g…

Git常用命令以及使用IDEA集成Gitee

目录 一、设置用户签名 二、初始化本地库 三、查看本地库状态 四、添加文件到暂存区 五、提交本地库 六、修改文件 七、版本穿梭 八、Git分支 九、分支的操作 9.1、查看分支 9.2、创建分支 9.3、切换分支 9.4、合并分支 十、团队协作 十一、Idea集成Git 11.1、配…

Github 2024-07-15 开源项目周报 Top15

根据Github Trendings的统计,本周(2024-07-15统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目5非开发语言项目4JavaScript项目3TypeScript项目2Go项目1Solidity项目1Java项目1Rust项目1免费编程学习平台:freeCodeCamp.org 创建…

3.1-RNN存在的问题以及LSTM的结构

文章目录 1 RNN存在的问题1.1梯度消失问题1.2梯度爆炸问题1.3梯度爆炸的对策 2梯度消失的对策——LSTM2.1输出门2.2遗忘门2.3输入门2.4总结2.5 LSTM梯度的流动 1 RNN存在的问题 RNN存在梯度消失和梯度爆炸的问题。 书上以下图的这句话为例,进行说明;为了…

前瞻断言与后瞻断言:JavaScript 正则表达式的秘密武器

JavaScript 中的前瞻断言(lookahead)和后瞻断言(lookbehind)相信用过的小伙伴就知道它的威力了,在一些特定的需求场景下,可以做到四两拨千斤的作用,今天让我们来盘点一下在 JavaScript 正则表达…

昇思25天学习打卡营第14天|munger85

基于MindNLPMusicGen生成自己的个性化音乐 这个所谓的个性化的音乐就是指你输入一段文字它会根据这个文字输出一段音乐这个音乐是贴近于那段文字的所以叫做文生成音乐, 如果网络正常的话就可以直接从下载这个模型。 那么音乐生成的有两种方式呢有两种方式&#xff…

【C++初阶】C/C++内存管理

【C初阶】C/C内存管理 🥕个人主页:开敲🍉 🔥所属专栏:C🥭 🌼文章目录🌼 1. C/C内存分布 2. C语言中动态内存管理方式:malloc/calloc/realloc/free 3. C内存管理方式 3…

拉格朗日乘子法和KKT条件

拉格朗日乘子法(Lagrange Multiplier) 和 KKT(Karush-Kuhn-Tucker) 条件是求解约束优化问题的重要方法,在有等式约束时使用拉格朗日乘子法,在有不等约束时使用 KKT 条件。当然,这两个方法求得的结果只是必要条件,只有当目标函数…

ssrf复习(及ctfshow351-360)

1. SSRF 概述 服务器会根据用户提交的URL发送一个HTTP请求。使用用户指定的URL,Web应用可以获取图片或者文件资源等。典型的例子是百度识图功能。 如果没有对用户提交URL和远端服务器所返回的信息做合适的验证或过滤,就有可能存在“请求伪造"的缺陷…

FPGA学习笔记(一) FPGA最小系统

文章目录 前言一、FPGA最小系统总结 前言 今天学习下FPGA的最小系统一、FPGA最小系统 FPGA最小系统与STM32最小系统类似,由供电电源,时钟电路晶振,复位和调试接口JTAG以及FLASH配置芯片组成,其与STM32最大的不同之处就是必须要有…

链表面试练习习题集(Java)

1. 思路&#xff1a; 因为杨辉三角是由二维数组构成&#xff0c;所以要先创建一个二维数组&#xff0c;如何用顺序表表示二维数组&#xff0c;可以通过List<List<Interger>>来表示一个二维数组&#xff0c;可以这样理解&#xff1a;先创建一个一维数组List&#x…

modbus slave 设备通过 网关thingsboard-gateway 将数据上传到thingsboard云平台

搭建thingsboard物联网云平台花了大量时间&#xff0c;从小白到最后搭建成功&#xff0c;折磨了好几天&#xff0c;也感谢网友的帮助&#xff0c;提供了思路最终成功搞定&#xff0c;特此记录。 一、thingsboard环境搭建&#xff08;Ubuntu20.04LTS&#xff09; 参考官方文档&a…

java之 junit单元测试案例【经典版】

一 junit单元测试 1.1 单元测试作用 单元测试要满足AIR原则&#xff0c;即 A&#xff1a; automatic 自动化&#xff1b; I: Independent 独立性&#xff1b; R&#xff1a;Repeatable 可重复&#xff1b; 2.单元测试必须使用assert来验证 1.2 案例1 常规单元测试 1.…

PSINS工具箱函数介绍——r2d

介绍工具箱里面r2d这个小函数的作用。 程序源码 function deg r2d(rad) % Convert angle unit from radian to degree % % Prototype: deg r2d(rad) % Input: rad - angle in radian(s) % Output: deg - angle in degree(s) % % See also r2dm, r2dms, d2r, dm2r, dms2r% …

多种方式实现 元素高度丝滑的从0-1显示出来

选择合适的方式&#xff0c;给用户更好的体验&#xff0c;多种方式实现 元素高度丝滑的从0-1显示出来。 能用 CSS 实现的动画&#xff0c;就不要采用 JS 去实现。 1、浏览器可以对CSS动画进行优化&#xff0c;其优化原理类似于requestAnimationFrame&#xff0c;会把每一帧的…

appium2.0 执行脚本遇到的问题

遇到的问题&#xff1a; appium 上的日志信息&#xff1a; 配置信息 方法一 之前用1.0的时候 地址默认加的 /wd/hub 在appium2.0上&#xff0c; 服务器默认路径是 / 如果要用/wd/hub 需要通过启动服务时设置基本路径 appium --base-path/wd/hub 这样就能正常执行了 方法二…

mysql的索引事务和存储引擎

一、索引 1、索引 索引的概念 &#xff1a;索引是一个排序的列表&#xff0c;在列表当中存储索引的值以及索引值对应数据所在的物理行。 索引的引用&#xff1a; 使用索引之后&#xff0c;就不需要扫描全表来定位某行的数据。 加快数据库的查询速度。 索引可以是表中的一…

在 K8s 上使用 KubeBlocks 提供的 MySQL operator 部署高可用 WordPress 站点

引言 WordPress WordPress 是全球最流行的内容管理系统&#xff08;CMS&#xff09;&#xff0c;自 2003 年发布以来&#xff0c;已成为网站建设的首选工具。其广泛的插件和主题生态系统使用户能够轻松扩展功能和美化外观。活跃的社区提供丰富的资源和支持&#xff0c;进一步…

[RK3588-Android12] 关于如何取消usb-typec的pd充电功能

问题描述 RK3588取消usb-typec的pd充电功能 解决方案&#xff1a; 在dts中fusb302节点下usb_con: connector子节点下添加如下熟悉&#xff1a; 打上如下2个补丁 diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index c8a4e57c9f9b..173f8cb7…

使用OpenCV寻找图像中的轮廓

引言 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库。它提供了大量的视觉处理功能&#xff0c;包括图像和视频捕获、特征检测与匹配、图像变换、图像分割、颜色空间转换等。在图像处理中&#xff0c;寻找图像中的…