Orbit 使用指南 07 | 创建强化学习环境 | Isaac Sim | Omniverse |

news2024/12/23 17:55:19

如是我闻 在谈论了如何创建基础环境后,我们现在将探索如何为强化学习创建任务环境。

基础环境被设计为一个感知-行动环境(sense-act environment),代理(agent)可以向环境发送命令并从环境接收观测。这种最小接口对于许多应用来说已经足够,例如传统的运动规划和控制。然而,许多应用需要一个任务规范,这通常作为代理的学习目标。例如,在导航任务中,代理可能需要到达一个目标位置。为此,我们使用envs.RLTaskEnv类,它扩展了基础环境以包括任务规范。

与Orbit中的其他组件类似,使用者还是不要直接修改基类RLTaskEnv,而是简单地为他们的任务环境实现一个配置RLTaskEnvCfg。这种做法允许我们将任务规范与环境实现分离,使得更容易重用同一环境的组件用于不同的任务。

在这个教程中,我们将使用RLTaskEnvCfg配置车杆环境,以创建保持杆直立的任务。我们将学习如何使用奖励项、终止条件、课程和命令来指定任务。

还是那个问题,大家都是来这里训练机器人,你好歹给个机械臂的例子也行阿,一直说车杆我也用不到我的例子上去阿,啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊

在本指南中,我们使用在omni.isaac.orbit_tasks.classic.cartpole模块中定义的车杆环境。

# Copyright (c) 2022-2024, The ORBIT Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

import math

import omni.isaac.orbit.sim as sim_utils
from omni.isaac.orbit.assets import ArticulationCfg, AssetBaseCfg
from omni.isaac.orbit.envs import RLTaskEnvCfg
from omni.isaac.orbit.managers import EventTermCfg as EventTerm
from omni.isaac.orbit.managers import ObservationGroupCfg as ObsGroup
from omni.isaac.orbit.managers import ObservationTermCfg as ObsTerm
from omni.isaac.orbit.managers import RewardTermCfg as RewTerm
from omni.isaac.orbit.managers import SceneEntityCfg
from omni.isaac.orbit.managers import TerminationTermCfg as DoneTerm
from omni.isaac.orbit.scene import InteractiveSceneCfg
from omni.isaac.orbit.utils import configclass

import omni.isaac.orbit_tasks.classic.cartpole.mdp as mdp

##
# Pre-defined configs
##
from omni.isaac.orbit_assets.cartpole import CARTPOLE_CFG  # isort:skip


##
# Scene definition
##


@configclass
class CartpoleSceneCfg(InteractiveSceneCfg):
    """Configuration for a cart-pole scene."""

    # ground plane
    ground = AssetBaseCfg(
        prim_path="/World/ground",
        spawn=sim_utils.GroundPlaneCfg(size=(100.0, 100.0)),
    )

    # cartpole
    robot: ArticulationCfg = CARTPOLE_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")

    # lights
    dome_light = AssetBaseCfg(
        prim_path="/World/DomeLight",
        spawn=sim_utils.DomeLightCfg(color=(0.9, 0.9, 0.9), intensity=500.0),
    )
    distant_light = AssetBaseCfg(
        prim_path="/World/DistantLight",
        spawn=sim_utils.DistantLightCfg(color=(0.9, 0.9, 0.9), intensity=2500.0),
        init_state=AssetBaseCfg.InitialStateCfg(rot=(0.738, 0.477, 0.477, 0.0)),
    )


##
# MDP settings
##


@configclass
class CommandsCfg:
    """Command terms for the MDP."""

    # no commands for this MDP
    null = mdp.NullCommandCfg()


@configclass
class ActionsCfg:
    """Action specifications for the MDP."""

    joint_effort = mdp.JointEffortActionCfg(asset_name="robot", joint_names=["slider_to_cart"], scale=100.0)


@configclass
class ObservationsCfg:
    """Observation specifications for the MDP."""

    @configclass
    class PolicyCfg(ObsGroup):
        """Observations for policy group."""

        # observation terms (order preserved)
        joint_pos_rel = ObsTerm(func=mdp.joint_pos_rel)
        joint_vel_rel = ObsTerm(func=mdp.joint_vel_rel)

        def __post_init__(self) -> None:
            self.enable_corruption = False
            self.concatenate_terms = True

    # observation groups
    policy: PolicyCfg = PolicyCfg()


@configclass
class EventCfg:
    """Configuration for events."""

    # reset
    reset_cart_position = EventTerm(
        func=mdp.reset_joints_by_offset,
        mode="reset",
        params={
            "asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]),
            "position_range": (-1.0, 1.0),
            "velocity_range": (-0.5, 0.5),
        },
    )

    reset_pole_position = EventTerm(
        func=mdp.reset_joints_by_offset,
        mode="reset",
        params={
            "asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"]),
            "position_range": (-0.25 * math.pi, 0.25 * math.pi),
            "velocity_range": (-0.25 * math.pi, 0.25 * math.pi),
        },
    )


@configclass
class RewardsCfg:
    """Reward terms for the MDP."""

    # (1) Constant running reward
    alive = RewTerm(func=mdp.is_alive, weight=1.0)
    # (2) Failure penalty
    terminating = RewTerm(func=mdp.is_terminated, weight=-2.0)
    # (3) Primary task: keep pole upright
    pole_pos = RewTerm(
        func=mdp.joint_pos_target_l2,
        weight=-1.0,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"]), "target": 0.0},
    )
    # (4) Shaping tasks: lower cart velocity
    cart_vel = RewTerm(
        func=mdp.joint_vel_l1,
        weight=-0.01,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"])},
    )
    # (5) Shaping tasks: lower pole angular velocity
    pole_vel = RewTerm(
        func=mdp.joint_vel_l1,
        weight=-0.005,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"])},
    )


@configclass
class TerminationsCfg:
    """Termination terms for the MDP."""

    # (1) Time out
    time_out = DoneTerm(func=mdp.time_out, time_out=True)
    # (2) Cart out of bounds
    cart_out_of_bounds = DoneTerm(
        func=mdp.joint_pos_manual_limit,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]), "bounds": (-3.0, 3.0)},
    )


@configclass
class CurriculumCfg:
    """Configuration for the curriculum."""

    pass


##
# Environment configuration
##


@configclass
class CartpoleEnvCfg(RLTaskEnvCfg):
    """Configuration for the locomotion velocity-tracking environment."""

    # Scene settings
    scene: CartpoleSceneCfg = CartpoleSceneCfg(num_envs=4096, env_spacing=4.0)
    # Basic settings
    observations: ObservationsCfg = ObservationsCfg()
    actions: ActionsCfg = ActionsCfg()
    events: EventCfg = EventCfg()
    # MDP settings
    curriculum: CurriculumCfg = CurriculumCfg()
    rewards: RewardsCfg = RewardsCfg()
    terminations: TerminationsCfg = TerminationsCfg()
    # No command generator
    commands: CommandsCfg = CommandsCfg()

    # Post initialization
    def __post_init__(self) -> None:
        """Post initialization."""
        # general settings
        self.decimation = 2
        self.episode_length_s = 5
        # viewer settings
        self.viewer.eye = (8.0, 0.0, 5.0)
        # simulation settings
        self.sim.dt = 1 / 120

运行环境的脚本run_cartpole_rl_env.py位于orbit/source/standalone/tutorials/03_envs目录中。这个脚本与前一个教程中的cartpole_base_env.py脚本类似,不同之处在于它使用envs.RLTaskEnv而不是envs.BaseEnv。让我们搂一眼他的代码。

# Copyright (c) 2022-2024, The ORBIT Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""
This script demonstrates how to run the RL environment for the cartpole balancing task.
"""

from __future__ import annotations

"""Launch Isaac Sim Simulator first."""


import argparse

from omni.isaac.orbit.app import AppLauncher

# add argparse arguments
parser = argparse.ArgumentParser(description="Tutorial on running the cartpole RL environment.")
parser.add_argument("--num_envs", type=int, default=16, help="Number of environments to spawn.")

# append AppLauncher cli args
AppLauncher.add_app_launcher_args(parser)
# parse the arguments
args_cli = parser.parse_args()

# launch omniverse app
app_launcher = AppLauncher(args_cli)
simulation_app = app_launcher.app

"""Rest everything follows."""

import torch

from omni.isaac.orbit.envs import RLTaskEnv

from omni.isaac.orbit_tasks.classic.cartpole.cartpole_env_cfg import CartpoleEnvCfg


def main():
    """Main function."""
    # create environment configuration
    env_cfg = CartpoleEnvCfg()
    env_cfg.scene.num_envs = args_cli.num_envs
    # setup RL environment
    env = RLTaskEnv(cfg=env_cfg)

    # simulate physics
    count = 0
    while simulation_app.is_running():
        with torch.inference_mode():
            # reset
            if count % 300 == 0:
                count = 0
                env.reset()
                print("-" * 80)
                print("[INFO]: Resetting environment...")
            # sample random actions
            joint_efforts = torch.randn_like(env.action_manager.action)
            # step the environment
            obs, rew, terminated, truncated, info = env.step(joint_efforts)
            # print current orientation of pole
            print("[Env 0]: Pole joint: ", obs["policy"][0][1].item())
            # update counter
            count += 1

    # close the environment
    env.close()


if __name__ == "__main__":
    # run the main function
    main()
    # close sim app
    simulation_app.close()

代码解析

我们已经在创建基础环境中讲解了上述内容的一部分,了解了如何指定场景 (scene)、观察 (observations)、行为 (actions) 和事件 (events)。因此,在这个教程中,我们将只关注环境的RL(强化学习)组件。

在Orbit中,提供了envs.mdp模块中不同项的各种实现。我们将在这个教程中使用其中的一些项,但用户也可以自定义他们自己的项。这些通常放在他们的任务特定子包中(例如,在omni.isaac.orbit_tasks.classic.cartpole.mdp中)。

定义奖励

managers.RewardManager用于计算代理的奖励项。与其他管理器类似,我们使用managers.RewardTermCfg类来配置它的像。managers.RewardTermCfg类指定计算奖励的函数或可调用类以及与之关联的权重。它还接受一个参数字典"params",这些参数在调用奖励函数时传递给它。

对于车杆任务,我们将使用以下奖励项:

  • 存活奖励:鼓励代理尽可能长时间地存活。
  • 终止奖励:同样地,对代理终止行为进行惩罚。
  • 杆角度奖励:鼓励代理保持杆在期望的直立位置。
  • 小车速度奖励:鼓励代理尽可能保持小车速度较小。
  • 杆速度奖励:鼓励代理尽可能保持杆速度较小。
@configclass
class RewardsCfg:
    """Reward terms for the MDP."""

    # (1) Constant running reward
    alive = RewTerm(func=mdp.is_alive, weight=1.0)
    # (2) Failure penalty
    terminating = RewTerm(func=mdp.is_terminated, weight=-2.0)
    # (3) Primary task: keep pole upright
    pole_pos = RewTerm(
        func=mdp.joint_pos_target_l2,
        weight=-1.0,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"]), "target": 0.0},
    )
    # (4) Shaping tasks: lower cart velocity
    cart_vel = RewTerm(
        func=mdp.joint_vel_l1,
        weight=-0.01,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"])},
    )
    # (5) Shaping tasks: lower pole angular velocity
    pole_vel = RewTerm(
        func=mdp.joint_vel_l1,
        weight=-0.005,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"])},
    )

定义终止条件

大多数学习任务发生在我们称之为一个周期(episode)的有限步骤数内。例如,在车杆任务中,我们希望代理尽可能长时间地平衡杆。然而,如果代理达到不稳定或不安全的状态,我们想要终止周期。另一方面,如果代理能够长时间平衡杆,我们希望终止周期并开始一个新的,以便代理可以从不同的起始配置学习平衡杆。

managers.TerminationsCfg配置了什么构成周期的终止。在这个示例中,我们希望任务在满足以下任一条件时终止:

  • 周期长度 大于定义的max_episode_length
  • 小车超出界限 小车超出了界限[-3, 3]

managers.TerminationsCfg.time_out指定了项是否为超时(截断)项或终止项。超时项或截断项是描述的两种终止类型,详细请见Gymnasium’s documentation.。

@configclass
class TerminationsCfg:
    """Termination terms for the MDP."""

    # (1) Time out
    time_out = DoneTerm(func=mdp.time_out, time_out=True)
    # (2) Cart out of bounds
    cart_out_of_bounds = DoneTerm(
        func=mdp.joint_pos_manual_limit,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]), "bounds": (-3.0, 3.0)},
    )

定义命令

对于各种条件目标任务(goal-conditioned tasks),指定代理的目标或命令是很有用的。这些通过managers.CommandManager处理。命令管理器处理在每一步重采样和更新命令。它也可以用来将命令作为观察提供给代理。

对于这个简单任务,我们不使用任何命令。这是通过使用带有envs.mdp.NullCommandCfg配置的命令项来指定的。我们也可以在运动或操纵任务中看到命令定义的示例。

@configclass
class CommandsCfg:
    """Command terms for the MDP."""

    # no commands for this MDP
    null = mdp.NullCommandCfg()

定义课程(curriculum)

在训练学习代理时,从一个简单的任务开始并随着代理训练的进展逐渐增加任务的难度往往是有帮助的。这就是课程学习背后的思想(curriculum learning)。在Orbit中,我们提供了一个managers.CurriculumManager类,可以用来为环境定义一个课程。

在本指南中,为了简单起见,我们没有实现一个课程,但我们可以在其他运动或操纵任务中看到课程定义的示例。我们使用一个简单的直通课程来定义一个不修改环境的课程管理器。

@configclass
class CurriculumCfg:
    """Configuration for the curriculum."""

    pass

将所有内容整合在一起

定义了上述所有组件后,我们现在可以为车杆环境创建RLTaskEnvCfg配置。这类似于在创建基础环境中定义的BaseEnvCfg,只是增加了上述部分中解释的RL组件。

@configclass
class CartpoleEnvCfg(RLTaskEnvCfg):
    """Configuration for the locomotion velocity-tracking environment."""

    # Scene settings
    scene: CartpoleSceneCfg = CartpoleSceneCfg(num_envs=4096, env_spacing=4.0)
    # Basic settings
    observations: ObservationsCfg = ObservationsCfg()
    actions: ActionsCfg = ActionsCfg()
    events: EventCfg = EventCfg()
    # MDP settings
    curriculum: CurriculumCfg = CurriculumCfg()
    rewards: RewardsCfg = RewardsCfg()
    terminations: TerminationsCfg = TerminationsCfg()
    # No command generator
    commands: CommandsCfg = CommandsCfg()

    # Post initialization
    def __post_init__(self) -> None:
        """Post initialization."""
        # general settings
        self.decimation = 2
        self.episode_length_s = 5
        # viewer settings
        self.viewer.eye = (8.0, 0.0, 5.0)
        # simulation settings
        self.sim.dt = 1 / 120

运行模拟循环

回到run_cartpole_rl_env.py脚本,模拟循环与之前的教程类似。唯一的区别是我们创建了一个envs.RLTaskEnv的实例,而不是envs.BaseEnv。因此,现在envs.RLTaskEnv.step()方法返回额外的信号,如奖励和终止状态。信息字典也保持了如各个项的奖励贡献、每个项的终止状态、周期长度等数量的记录。

def main():
    """Main function."""
    # create environment configuration
    env_cfg = CartpoleEnvCfg()
    env_cfg.scene.num_envs = args_cli.num_envs
    # setup RL environment
    env = RLTaskEnv(cfg=env_cfg)

    # simulate physics
    count = 0
    while simulation_app.is_running():
        with torch.inference_mode():
            # reset
            if count % 300 == 0:
                count = 0
                env.reset()
                print("-" * 80)
                print("[INFO]: Resetting environment...")
            # sample random actions
            joint_efforts = torch.randn_like(env.action_manager.action)
            # step the environment
            obs, rew, terminated, truncated, info = env.step(joint_efforts)
            # print current orientation of pole
            print("[Env 0]: Pole joint: ", obs["policy"][0][1].item())
            # update counter
            count += 1

    # close the environment
    env.close()

运行代码

类似于之前的指南,我们可以通过执行run_cartpole_rl_env.py脚本来运行环境。

./orbit.sh -p source/standalone/tutorials/03_envs/run_cartpole_rl_env.py --num_envs 32

这应该会打开一个与之前教程中相似的模拟。然而,这一次,环境返回更多指定奖励和终止状态的信号。此外,当基于配置中指定的终止标准终止时,各个环境会自行重置。

在这里插入图片描述

要停止模拟,可以关闭窗口,或在启动模拟的终端中按Ctrl+C。

在本指南中,我们看到了如何为强化学习创建一个任务环境。我们通过扩展基础环境来包含奖励、终止、命令和课程条款来实现这一点。我们还浏览了如何使用envs.RLTaskEnv类运行环境并从中接收各种信号。

虽然可以手动为期望的任务创建envs.RLTaskEnv类的实例,但这不具有可扩展性,因为它需要每个任务的专门脚本。因此,我们利用gymnasium.make()函数来用gym接口创建环境。我们将在下一个指南中讨论如何做到这一点。

愿本文除一切机器人模拟器苦

以上

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

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

相关文章

Meta 推出SceneScript,一种全新的3D场景重建方式

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

高项-案例分析练习(范围管理)

案例一 公司在2014年初承接了一个医疗信息系统项目,要求2014年底完成该项目研发任务并进行试运行,2015年负责项目全年的运行维护,运行稳定后甲方验收合格项目才能结束。由于张工具有多年的医疗系统开发管理经验,公司领导任命他为项…

用BSP优化3D渲染

3D渲染引擎设计者面临的最大问题之一是可见性计算:只必须绘制可见的墙壁和物体,并且必须以正确的顺序绘制它们(应该在远处的墙壁前面绘制近墙) 。 更重要的是,对于游戏等应用程序来说,开发能够快速渲染场景…

Flutter动画(一)Ticker、Animate 原理

在任何系统的UI框架中,动画原理都是类似的,即:在一段时间内,快速地多次改变UI外观;由于人眼会产生视觉暂留,所以最终看到的就是一个“连续”的动画。 Flutter中对动画进行了抽象,主要涉及 Anim…

2.6、媒体查询(mediaquery)

概述 媒体查询作为响应式设计的核心,在移动设备上应用十分广泛。媒体查询可根据不同设备类型或同设备不同状态修改应用的样式。媒体查询常用于下面两种场景: 针对设备和应用的属性信息(比如显示区域、深浅色、分辨率),设计出相匹配的布局。当屏幕发生动态改变时(比如分屏…

2016年认证杯SPSSPRO杯数学建模B题(第二阶段)多帧图像的复原与融合全过程文档及程序

2016年认证杯SPSSPRO杯数学建模 B题 多帧图像的复原与融合 原题再现: 数码摄像技术被广泛使用于多种场合中。有时由于客观条件的限制,拍摄设备只能在较低的分辨率下成像。为简单起见,我们只考虑单色成像。假设成像的分辨率为 32 64&#x…

QT作业。。

1.使用手动连接,将登录框中的取消按钮使用t4版本的连接到自定义的槽函数中,在自定义的槽函数中调用关闭函数将登录按钮使用t5版本的连接到自定义的槽函数中,在槽函数中判断u界面上输入的账号是否为"admin",密码是否为&q…

(done) 机器学习中的方差 variance 和 偏差 bias 怎么理解?

来源:https://blog.csdn.net/weixin_41479678/article/details/116230631 情况1属于:低 bias,高 variance (和 human performance 相近,但和 验证集dev set 相远) 通常意味着模型训练轮数太多 情况2属于:高 bias&#…

基于NetCoreServer的WebSocket客户端实现群播(学习笔记)

一、NetCoreServer介绍 超快速、低延迟的异步套接字服务器和客户端 C# .NET Core 库,支持 TCP、SSL、UDP、HTTP、HTTPS、WebSocket 协议和 10K 连接问题解决方案。 开源地址:https://github.com/chronoxor/NetCoreServer 支持: Example: TC…

分享Pandas 数据分析实战课程

分享Pandas 数据分析实战课程,3 小时掌握数据分析核心技能。 链接:https://pan.baidu.com/s/1Ikk3I1dfoFO0id3EBZJdGg?pwd4y83 提取码:4y83 链接:https://pan.quark.cn/s/fa2acd7513f4 提取码:yWu7

【3DsMax】展UV记录

目录 一、概念 二、边的颜色 三、UV的连续性 四、合理的划分UV接缝 五、总结 一、概念 展uv的概念可以理解为把三维的模型铺平展成一个平面,然后在这个平面上去绘制图案。 二、边的颜色 我们先创建一个长方体,然后在修改器列表中添加“UVW展开”…

数据格式化方法

首先你需要一个可以展示代码的组件; 我使用的是tech-ui(内部组件库); 你如果没有类似的组件,可以参考以下链接替代: react-monaco-editor -- 代码编辑器(适用Umi)_umi monaco editor-CSDN博客 Codemirror -- 代码编辑器(react…

schweizer-electronic 公司 safedat2 操作使用说明

schweizer-electronic 公司 safedat2 操作使用说明

优质的短效HTTP代理具备什么优点?

随着网络时代的蓬勃发展,数据的获取与处理成为了企业决策和市场竞争的关键。在这场数据的角逐中,优质的短效HTTP代理脱颖而出,备受业界瞩目。优质的短效HTTP代理,提供了稳定的网络连接和匿名性,更为数据采集提供了关键…

css设置div的2个span一个在最左边,一个在最右边

界面&#xff1a; 代码&#xff1a; <html><style>.top span {display: block;position: absolute;margin: 0 20px; /* 添加边距以避免太靠近边缘 */ }.top span:nth-child(1) {left: 5px; /* 调整左侧位置 */ }.top span:nth-child(2) {right: 5px; /* 调整右侧位…

1.6 学Python能干什么,Python的应用领域有哪些

Python能干什么&#xff0c;Python的应用领域 Python 作为一种功能强大的编程语言&#xff0c;因其简单易学而受到很多开发者的青睐。那么&#xff0c;Python 的应用领域有哪些呢&#xff1f; Python 有着非广泛的应用&#xff0c;几乎所有大中型互联网公司都在使用 Python&a…

推荐一款很不错的vscode高亮插件

用过很多款高亮插件&#xff0c;总感觉大部分显示都很乱&#xff0c;但是其中有一款用起来很清晰明了&#xff0c;很喜欢&#xff01; 插件名字&#xff1a;select-highlight-cochineal-color 使用效果&#xff1a; 底色高亮让人感觉很清晰&#xff0c;一个好的高亮插件能让你…

鸿蒙一次开发,多端部署(九)应用市场首页

本小节将以应用市场首页为例&#xff0c;介绍如何使用自适应布局能力和响应式布局能力适配不同尺寸窗口。 页面设计 一个典型的应用市场首页的UX设计如下所示。 观察应用市场首页的页面设计&#xff0c;不同断点下的页面设计有较多相似的地方。 据此&#xff0c;我们可以将页…

2024蓝桥杯每日一题(单调队列)

备战2024年蓝桥杯 -- 每日一题 Python大学A组 试题一&#xff1a;单调栈 试题二&#xff1a;滑动窗口 试题三&#xff1a;子矩阵 试题四&#xff1a;最大子序和 试题一&#xff1a;单调栈 【题目描述】 给定一个长度为 N 的整数数列&#xff0c;输出每…

怿星科技Neptune CHT-S测试系统,让智能座舱测试更加高效便捷

随着汽车“智能化”浪潮的推进&#xff0c;汽车的智能化水平正在持续刷新行业认知。在这股智能化潮流中&#xff0c;智能座舱作为客户体验最为直观的部分&#xff0c;其重要性不言而喻。倘若座舱设备出现死机、黑屏、卡顿等现象&#xff0c;都将对客户的使用体验产生非常大的影…