第十七讲、Isaaclab中使用操作空间控制器

news2025/4/24 11:12:18

0 前言

官方教程:https://isaac-sim.github.io/IsaacLab/main/source/tutorials/05_controllers/run_osc.html
Isaacsim+Isaaclab安装:https://blog.csdn.net/m0_47719040/article/details/146389391?spm=1001.2014.3001.5502

有时候,仅使用differential IK控制器来控制机器人末端执行器的姿态是不够的。比如,我们可能希望在任务空间中强制执行非常特定的姿态跟踪动态误差,使用关节力/扭矩指令来驱动机器人,或者在特定方向上施加接触力,同时控制其他方向的运动(例如,用布擦拭桌面)。在这类任务中,可以使用空间控制器(OSC)。

本教程中,我们将学习如何使用一个OSC控制机器人。我们将使用controllers.OperationalSpaceController类施加一个垂直于倾斜墙面的恒定力,同时在所有其他方向上跟踪所需的末端执行器姿态。

教程对应的脚本为run_osc.pyscripts/tutorials/05_controllers目录下。

运行该程序:

  • 进入安装 isaac lab 时创建的conda虚拟环境
  • 在该环境下进入 isaac sim文件夹中运行source setup_conda_env.sh
  • 终端中输入./isaaclab.sh -p scripts/tutorials/05_controllers/run_osc.py --num_envs 128运行你的代码。

在这里插入图片描述

1 创建操作空间控制器

OperationalSpaceController类计算机器人在任务空间中同时进行运动和力控制的联合努力/扭矩。
官方文档:https://isaac-sim.github.io/IsaacLab/main/source/tutorials/05_controllers/run_osc.html

任务坐标系定义:
控制器的任务空间参考坐标系可自由指定(默认机器人基座坐标系)。但是,在某些情况下,定义相对于不同坐标系的目标坐标可能更容易。在这种情况下,应在set_command方法的current_task_frame_pose_b参数中提供此任务参考系相对于机器人基座坐标系的姿态。

场景示例:在墙面接触任务中,定义与墙面平行的坐标系(如Z轴垂直墙面),此时:

  • 力控制:只需在Z轴方向施加接触力。
  • 运动控制:在x/y平面调整位置,绕z轴旋转调整姿态。
# 假设已定义墙面坐标系到基座的变换矩阵 T_wall_to_base
controller.set_command(
    target_pose=target_in_wall_frame, 
    current_task_frame_pose_b=T_wall_to_base
)

控制类型与轴向选择:
1、目标类型(target_types)

  • “pose_abs”:绝对位姿控制(基座坐标系)
  • “pose_rel”:相对当前末端位姿的增量控制。
  • “wrench_abs”:绝对力/力矩控制。

还可以组合选择

# 同时进行位姿和力控制
cfg.target_types = ["pose_abs", "wrench_abs"]

2、控制轴向(motion_control_axes_task 和 force_control_axes_task)

  • 两个参数为6维二进制列表(0/1),分别对应位置(XYZ)和旋转(RxRyRz)。
  • 同一轴向不能同时激活运动和力控制(互补)。
# 在墙面坐标系中:
# - 运动控制XY平面位置和绕Z轴旋转(索引0,1,5)
# - 力控制Z轴方向(索引2)
cfg.motion_control_axes_task = [1, 1, 0, 0, 0, 1]  # X, Y, _, _, _, Rz
cfg.force_control_axes_task  = [0, 0, 1, 0, 0, 0]  # Z方向力控制

运动控制参数:
刚度与阻尼比(motion_control_stiffness 和 motion_damping_ratio_task)

  • 标量(所有轴向相同值)或6维列表(各轴向独立)。
  • 临界阻尼公式:kd = 2 * sqrt(kp)(当impedance_mode="variable_kp"时自动计算阻尼)。
cfg.impedance_mode = "variable_kp"  # 允许通过命令实时调整刚度
cfg.motion_stiffness_limits_task = (min_kp, max_kp)  # 刚度范围约束

力控制模式:
开环控制:直接设置目标力,无反馈

cfg.contact_wrench_stiffness_task = None

闭环控制:通过刚度参数调节力跟踪:

# 仅线性轴(XYZ)有效,旋转轴刚度被忽略
cfg.contact_wrench_stiffness_task = [100, 100, 100, 0, 0, 0]  # Z轴刚度100 N/m

动力学解耦与重力补偿:
惯性解耦(inertial_dynamics_decoupling):利用机器人惯性矩阵解耦任务空间加速度,提升动态响应精度。

cfg.inertial_dynamics_decoupling = True  # 启用完全惯性解耦
cfg.partial_inertial_dynamics_decoupling = False  # 不忽略平移-旋转耦合

重力补偿(gravity_compensation):若机器人模型已包含重力项(如仿真中启用重力),需关闭此选项:

cfg.gravity_compensation = False  # 假设仿真中机器人已处理重力

冗余机器人的零空间控制
冗余自由度机器人的关节运动不影响末端位姿(零空间)。

cfg.nullspace_control = "position"  # 吸引关节至中立位置
cfg.nullspace_stiffness = 10.0       # 零空间刚度
cfg.nullspace_damping_ratio = 0.7    # 阻尼比

本教程中的参数配置如下:

# 创建操作空间控制器配置
osc_cfg = OperationalSpaceControllerCfg(
    target_types=["pose_abs", "wrench_abs"],  # 绝对位姿 + 绝对力控制
    motion_control_axes_task=[1, 1, 0, 1, 1, 1],  # 控制XY位置和绕Z旋转(假设墙面Z轴)
    force_control_axes_task=[0, 0, 1, 0, 0, 0],   # Z轴力控制
    motion_control_stiffness=[200, 200, 0, 50, 50, 50],  # XY高刚度,旋转适中
    motion_damping_ratio_task=1.0,  # 临界阻尼(kd=2*sqrt(kp))
    impedance_mode="variable_kp",   # 允许动态调整刚度
    inertial_dynamics_decoupling=True,  # 启用惯性解耦
    partial_inertial_dynamics_decoupling=False,
    gravity_compensation=False,     # 仿真中已禁用重力
    nullspace_control="position",   # 零空间关节位置控制
    nullspace_stiffness=10.0,       # 零空间刚度
    nullspace_damping_ratio=0.7
)

2 更新机器人的状态

与前一节任务空间(task-space)控制类似,OSC 实现是一个仅用于计算的类。因此,需要提供机器人的必要信息,包括机器人的雅可比矩阵、质量/惯性矩阵、末端执行器位姿、速度、接触力(均在根坐标系中),以及关节位置和速度。此外,如果需要,用户还应提供重力补偿矢量和零空间关节位置目标。

# 函数功能:更新机器人状态信息,为操作空间控制器(OSC)提供必要的输入数据
def update_states(
    sim: sim_utils.SimulationContext,
    scene: InteractiveScene,
    robot: Articulation,
    ee_frame_idx: int,
    arm_joint_ids: list[int],
    contact_forces,
):
    """Update the robot states.

    Args:
        sim: (SimulationContext) 仿真上下文对象,包含时间步长等物理参数
        scene: (InteractiveScene) 交互场景对象,管理多环境
        robot: (Articulation) 机器人刚体关节树对象
        ee_frame_idx: (int) 末端执行器在body_state_w中的索引
        arm_joint_ids: (list[int]) 机械臂关节的物理引擎索引列表
        contact_forces: (ContactSensor) 接触力传感器对象

    Returns:
        返回OSC控制器所需的11个关键参数,所有张量均为batch格式(shape: [num_envs, ...])
    """
    # ====================== 动力学参数获取 ======================
    # 获取雅可比矩阵(世界坐标系)
    # 注意:对于固定基座机器人,物理引擎返回的雅可比矩阵索引需要-1(排除根身体)
    ee_jacobi_idx = ee_frame_idx - 1  # 计算物理引擎中的雅可比索引
    # 形状:[num_envs, 6, num_arm_joints] (6=3平移+3旋转)
    jacobian_w = robot.root_physx_view.get_jacobians()[:, ee_jacobi_idx, :, arm_joint_ids]
    
    # 获取质量矩阵(仅机械臂关节部分)
    # 形状:[num_envs, num_arm_joints, num_arm_joints]
    mass_matrix = robot.root_physx_view.get_generalized_mass_matrices()[:, arm_joint_ids, :][:, :, arm_joint_ids]
    
    # 获取重力补偿力矩(已考虑机器人质量分布)
    # 形状:[num_envs, num_arm_joints]
    gravity = robot.root_physx_view.get_gravity_compensation_forces()[:, arm_joint_ids]

    # ================= 雅可比矩阵坐标系转换(世界→基座) =================
    jacobian_b = jacobian_w.clone()  # 创建副本避免修改原始数据
    # 计算基座旋转矩阵的逆(将世界坐标系转换到基座坐标系)
    root_rot_matrix = matrix_from_quat(quat_inv(robot.data.root_quat_w))  # shape: [num_envs, 3, 3]
    # 转换平移部分雅可比:J_lin_base = R_base^T * J_lin_world
    jacobian_b[:, :3, :] = torch.bmm(root_rot_matrix, jacobian_b[:, :3, :])
    # 转换旋转部分雅可比:J_rot_base = R_base^T * J_rot_world
    jacobian_b[:, 3:, :] = torch.bmm(root_rot_matrix, jacobian_b[:, 3:, :])

    # ===================== 末端执行器位姿计算 =====================
    # 世界坐标系下的基座位姿
    root_pos_w = robot.data.root_pos_w  # 位置 [num_envs, 3]
    root_quat_w = robot.data.root_quat_w  # 四元数 [num_envs, 4]
    # 世界坐标系下的末端位姿
    ee_pos_w = robot.data.body_pos_w[:, ee_frame_idx]  # 末端位置
    ee_quat_w = robot.data.body_quat_w[:, ee_frame_idx]  # 末端姿态
    # 将末端位姿转换到基座坐标系
    ee_pos_b, ee_quat_b = subtract_frame_transforms(root_pos_w, root_quat_w, ee_pos_w, ee_quat_w)
    # 拼接完整位姿张量
    root_pose_w = torch.cat([root_pos_w, root_quat_w], dim=-1)  # [num_envs, 7]
    ee_pose_w = torch.cat([ee_pos_w, ee_quat_w], dim=-1)        # [num_envs, 7]
    ee_pose_b = torch.cat([ee_pos_b, ee_quat_b], dim=-1)        # [num_envs, 7]

    # ===================== 末端执行器速度计算 =====================
    # 世界坐标系下的末端线速度+角速度 [num_envs, 6]
    ee_vel_w = robot.data.body_vel_w[:, ee_frame_idx, :]  
    # 基座速度(线速度 + 角速度)[num_envs, 6]
    root_vel_w = robot.data.root_vel_w  
    # 计算末端相对于基座的速度(世界坐标系)
    relative_vel_w = ee_vel_w - root_vel_w  
    # 转换到基座坐标系:线速度
    ee_lin_vel_b = quat_rotate_inverse(robot.data.root_quat_w, relative_vel_w[:, 0:3])  # [num_envs, 3]
    # 转换到基座坐标系:角速度
    ee_ang_vel_b = quat_rotate_inverse(robot.data.root_quat_w, relative_vel_w[:, 3:6])  # [num_envs, 3]
    # 拼接完整速度张量 [num_envs, 6]
    ee_vel_b = torch.cat([ee_lin_vel_b, ee_ang_vel_b], dim=-1)

    # ====================== 接触力处理 ======================
    ee_force_w = torch.zeros(scene.num_envs, 3, device=sim.device)  # 初始化接触力
    sim_dt = sim.get_physics_dt()  # 获取物理时间步长
    contact_forces.update(sim_dt)  # 更新接触传感器数据
    # 数据平滑处理:取最近4步的平均值,再取三个接触面中的最大值(假设仅有一个有效接触)
    # net_forces_w_history形状:[num_envs, history_len=4, num_surfaces=3, 3]
    ee_force_w, _ = torch.max(torch.mean(contact_forces.data.net_forces_w_history, dim=1), dim=1)
    # 简化假设:直接使用世界坐标系的力(实际应根据任务坐标系转换)
    ee_force_b = ee_force_w  # 此处应优化为基座坐标系转换

    # ===================== 关节状态获取 =====================
    # 仅提取机械臂关节(排除夹爪等)
    joint_pos = robot.data.joint_pos[:, arm_joint_ids]  # [num_envs, num_arm_joints]
    joint_vel = robot.data.joint_vel[:, arm_joint_ids]  # [num_envs, num_arm_joints]

    # 返回所有必要参数(用于OSC控制器计算)
    return (
        jacobian_b,        # 基座坐标系雅可比矩阵 [num_envs, 6, num_arm_joints]
        mass_matrix,       # 质量矩阵 [num_envs, num_arm_joints, num_arm_joints]
        gravity,           # 重力补偿力矩 [num_envs, num_arm_joints]
        ee_pose_b,         # 基座坐标系末端位姿 [num_envs, 7]
        ee_vel_b,          # 基座坐标系末端速度 [num_envs, 6]
        root_pose_w,       # 世界坐标系基座位姿 [num_envs, 7]
        ee_pose_w,         # 世界坐标系末端位姿 [num_envs, 7]
        ee_force_b,        # 基座坐标系接触力(当前简化处理)[num_envs, 3]
        joint_pos,         # 关节角度 [num_envs, num_arm_joints]
        joint_vel,         # 关节速度 [num_envs, num_arm_joints]
    )

3 计算机器人指令

  • 命令坐标系转换
    • 位姿控制 (“pose_abs”):通过 subtract_frame_transforms 将基座坐标系下的目标位姿转换到任务帧坐标系。例如,若任务帧是墙面坐标系,则目标位置将表示为相对于墙面的坐标。
    • 力控制 (“wrench_abs”):假设力命令已在任务帧中定义(如Z轴垂直墙面),无需转换。
  • OSC命令设置
    • 任务帧位姿传递:current_task_frame_pose_b 告知OSC任务帧相对于基座的位置,用于内部计算误差。
    • 当前末端位姿:提供实时反馈,用于计算位姿误差和速度误差。
  • 力矩计算参数
    • 质量矩阵与惯性解耦:启用 inertial_dynamics_decoupling 时,质量矩阵用于解耦任务空间加速度,提升动态响应。
    • 零空间控制:nullspace_joint_pos_target 吸引关节至中立位置(如 joint_centers),避免极限位置。
  • 力控制实现
    • 开环 vs 闭环:若 contact_wrench_stiffness_task 未设置,为开环力控;若设置刚度值,则闭环调整力误差。
    • 接触力测量:ee_force_b 来自接触传感器,需平滑处理(如示例中的四步平均)。
# 将目标命令从基座坐标系转换到任务帧(如墙面坐标系)
def convert_to_task_frame(
    osc: OperationalSpaceController, 
    command: torch.tensor, 
    ee_target_pose_b: torch.tensor
):
    """将基座坐标系下的命令转换为任务帧坐标系下的命令
    
    Args:
        osc: 操作空间控制器实例,包含配置信息(如目标类型)
        command: 原始命令张量,形状为 [num_envs, command_dim]
        ee_target_pose_b: 末端在基座坐标系下的目标位姿(用于定义任务帧)

    Returns:
        command_task: 转换到任务帧后的命令张量
        task_frame_pose_b: 任务帧在基座坐标系下的位姿(用于后续计算)
    """
    command_task = command.clone()  # 避免修改原始命令
    task_frame_pose_b = ee_target_pose_b.clone()  # 任务帧位姿(例如墙面坐标系)

    cmd_idx = 0  # 命令张量的当前处理位置索引
    # 遍历所有目标类型(如 ["pose_abs", "wrench_abs"])
    for target_type in osc.cfg.target_types:
        if target_type == "pose_abs":
            # 提取当前位姿命令部分(位置+四元数姿态)
            target_pos_b = command_task[:, cmd_idx : cmd_idx+3]  # [num_envs, 3]
            target_quat_b = command_task[:, cmd_idx+3 : cmd_idx+7]  # [num_envs, 4]
            # 将位姿从基座坐标系转换到任务帧坐标系
            # 数学等效:T_task^base * T_target^task = T_target^base → 求T_target^task
            target_pos_task, target_quat_task = subtract_frame_transforms(
                task_frame_pose_b[:, :3],   # 任务帧位置(基座坐标系)
                task_frame_pose_b[:, 3:7],  # 任务帧姿态(基座坐标系)
                target_pos_b, 
                target_quat_b
            )
            # 更新命令张量中的位姿部分
            command_task[:, cmd_idx : cmd_idx+3] = target_pos_task
            command_task[:, cmd_idx+3 : cmd_idx+7] = target_quat_task
            cmd_idx += 7  # 移动索引到下个命令部分
        elif target_type == "wrench_abs":
            # 力/力矩命令已在任务帧中定义,无需转换
            cmd_idx += 6  # 力控制为6维(3力 + 3力矩)
        else:
            raise ValueError(f"未定义的目标类型:{target_type}")

    return command_task, task_frame_pose_b

# ====================== OSC命令设置与力矩计算 ======================
# 重置控制器内部状态(如积分项、历史误差)
osc.reset()

# 将目标命令转换到任务帧坐标系
command_task, task_frame_pose_b = convert_to_task_frame(
    osc, 
    command=command_raw,  # 原始命令(基座坐标系)
    ee_target_pose_b=ee_target_pose_b  # 任务帧在基座下的位姿(如墙面坐标系)
)

# 设置OSC命令(任务帧坐标系下的命令 + 当前末端位姿 + 任务帧位姿)
osc.set_command(
    command=command_task,  # 转换后的命令(任务帧)
    current_ee_pose_b=ee_pose_b,  # 当前末端在基座下的位姿(来自update_states)
    current_task_frame_pose_b=task_frame_pose_b  # 任务帧在基座下的位姿
)

# 计算关节力矩(核心运算)
joint_efforts = osc.compute(
    jacobian_b=jacobian_b,            # 基座坐标系下的雅可比矩阵
    current_ee_pose_b=ee_pose_b,      # 当前末端基座位姿
    current_ee_vel_b=ee_vel_b,        # 当前末端基座速度
    current_ee_force_b=ee_force_b,    # 当前末端基座接触力(简化处理)
    mass_matrix=mass_matrix,          # 质量矩阵(仅机械臂关节)
    gravity=gravity,                  # 重力补偿力矩
    current_joint_pos=joint_pos,      # 当前关节角度
    current_joint_vel=joint_vel,      # 当前关节速度
    nullspace_joint_pos_target=joint_centers  # 零空间目标关节位置(如限位中点)
)

# 将计算的关节力矩应用到机器人
robot.set_joint_effort_target(
    joint_efforts,          # 计算得到的力矩 [num_envs, num_arm_joints]
    joint_ids=arm_joint_ids  # 仅控制机械臂关节(排除夹爪)
)
# 将数据写入仿真环境,准备下一步物理计算
robot.write_data_to_sim()

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

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

相关文章

基于SpringBoot的校园二手商品在线交易系统+含项目运行说明文档

基于SpringBoot的校园二手商品在线交易系统含项目运行说明文档 专注校园二手交易平台是一个基于Java的在线市场,专为学生设计,便于买卖二手商品。平台提供全面的用户管理功能,包括学生、管理员和二手商品卖家账户管理。商品管理功能允许用户…

详解springcloud gateway工作原理、断言、filter、uri、id、全局跨域、globalfilter等以及关键源码实现

1.gateway概念 网关就是当前微服务项目的"统一入口"程序中的网关就是当前微服务项目对外界开放的统一入口所有外界的请求都需要先经过网关才能访问到我们的程序提供了统一入口之后,方便对所有请求进行统一的检查和管理 2. 网关的主要功能 将所有请求统一经过网关网…

C++面向对象特性之继承篇

C语音是面向过程的语言,而C在其之上多了面向对象的特性,面向对象三大特性:封装性、继承性、多态性。今天主包来讲讲自己学到的关于C继承特性的知识。 一、继承是什么 继承是提高代码复用的一种重要手段。正如C的模版、泛型编程等等都是为了实现代码复用…

【AI News | 20250423】每日AI进展

AI Repos 1、suna Suna是一款完全开源的AI助手,旨在通过自然对话帮助用户轻松完成现实世界的任务。它作为您的数字伙伴,提供研究、数据分析和日常问题解决等功能,并结合强大的能力与直观的界面,理解您的需求并交付成果。Suna的工…

【学习准备】算法和开发知识大纲

1 缘起 今年(2025年)的职业升级结果:不通过。没办法升职加薪了。 需要开始完善学习,以应对不同的发展趋势,为了督促自己学习,梳理出相关学习大纲。 分为算法和开发两部分。 算法,包括基础算法和…

第七篇:linux之基本权限、进程管理、系统服务

第七篇:linux之基本权限、进程管理、系统服务 文章目录 第七篇:linux之基本权限、进程管理、系统服务一、基本权限1、什么是权限?2、为什么要有权限?3、权限与用户之间的关系?4、权限对应的数字含义5、使用chmod设定权…

爬虫案例-爬取某企数据

文章目录 1、准备要爬取企业名称数据表2、爬取代码3、查看效果 1、准备要爬取企业名称数据表 企业名称绍兴市袍江王新国家庭农场绍兴市郑杜粮油专业合作社绍兴市越城区兴华家庭农场绍兴市越城区锐意家庭农场绍兴市越城区青甸畈家庭农场绍兴市袍江王新国家庭农场绍兴市袍江月明…

学习笔记—C++—string(一)

目录 string 为什么学习string的类 string类的常用接口 string类对象的常见构造 string类对象的访问及遍历操作 operator[] 迭代器 范围for auto 迭代器(二) string类对象的容量操作 size,length,max_size,capacity,clear基本用法 reserve 提…

GPLT-2025年第十届团体程序设计天梯赛总决赛题解(共计266分)

今天偶然发现天梯赛的代码还保存着,于是决定写下这篇题解,也算是复盘一下了 L1本来是打算写的稳妥点,最后在L1-6又想省时间,又忘记了insert,replace这些方法怎么用,也不想花时间写一个文件测试&#xff0c…

MySQL数据库精研之旅第十期:打造高效联合查询的实战宝典(一)

专栏:MySQL数据库成长记 个人主页:手握风云 目录 一、简介 1.1. 为什么要使用联合查询 1.2. 多表联合查询时的计算 1.3. 示例 二、内连接 2.1. 语法 2.2. 示例 三、外连接 4.1. 语法 4.2. 示例 一、简介 1.1. 为什么要使用联合查询 一次查询需…

15.FineReport动态展示需要的列

1.首先连接自带的sqlite数据库,具体方法参考下面的链接 点击查看连接sqlite数据库 2.文件 – 新建普通报表 3.新建数据库查询 4.查询自带的销售明细表 5.把数据添加到格子中,并设置边框颜色等格式 6.查询新的数据集:column 7.点笔 8.全部添…

Windows云主机远程连接提示“出现了内部错误”

今天有人反馈说有个服务器突然连不上了,让我看下什么问题,我根据他给的账号密码试了下发现提示“出现了内部错误”,然后就是一通排查 先是查看安全组,没发现特别的问题,因为也没有调过这块的配置 然后通过控制台登录进…

最新扣子(Coze)案例教程:Excel数据生成统计图表,自动清洗数据+转换可视化图表+零代码,完全免费教程

大家好,我是斜杠君。 知识星球群有同学和我说每天的工作涉及很多数据表的重复操作,想学习Excel数据表通过大模型自动转数据图片的功能。 今天斜杠君就带大家一起搭建一个智能体,以一个销售行业数据为例,可以快速实现自动清洗Exc…

如何安装Visio(win10)

首先下载下面这些文件 HomeStudent2021Retail.img officedeploymenttool_17531-20046.exe office中文语言包.exe 确保这些文件都在一个文件夹内(我已经上传这些资源,这些资源都是官网下载的) 官网资源下载教程 1.下载Office镜像&#xff0…

建筑安全员 A 证与 C 证:差异决定职业方向

在建筑行业的职业发展道路上,安全员 A 证和 C 证就像两条不同的岔路,它们之间的差异,在很大程度上决定了从业者的职业方向。 从证书性质和用途来看,A 证是从业资格证书,更像是一把开启安全管理高层岗位的 “金钥匙”。…

(19)VTK C++开发示例 --- 分隔文本读取器

文章目录 1. 概述2. CMake链接VTK3. main.cpp文件4. 演示效果 更多精彩内容👉内容导航 👈👉VTK开发 👈 1. 概述 本例采用坐标和法线(x y z nx ny nz)的纯文本文件,并将它们读入vtkPolyData并显示…

Redis从入门到实战先导篇

前言:本节内容包括虚拟机VMware的安装,Linux系统的配置,FinalShell的下载与配置,Redis与其桌面客户端的安装指导,便于后续黑马Redis从入门到实战的课程学习 目录 主要内容 0.相关资源 1.VMware安装 2.Linux与CentOS安装 3.Fi…

JavaScript 防抖和节流

方法一:使用lodash库的debounce方法 方法二:手写防抖函数 function debounce(fn,t){// 1.声明一个定时器变量 因为需要多次赋值 使用let声明let timer // 返回一个匿名函数return function(){if(timer){// 如果定时器存在清除之前的定时器 clearTimeout(…

Spring Boot 启动时 `converting PropertySource ... to ...` 日志详解

Spring Boot 启动时 converting PropertySource ... to ... 日志详解 1. 日志背景 在 Spring Boot 应用启动过程中,会加载并处理多种 配置源(如 application.properties、系统环境变量、命令行参数等)。这些配置源会被封装为 PropertySource…

分割数据集中.json格式标签转化成伪彩图图像

一、前言 图像分割任务中,分割数据集的转换和表示方式对于模型训练至关重要。目前主要有两种常见的分割结果表示方法: 1. 转化为TXT文件 这种方式通常使用一系列的点(坐标)来表示图像中每个像素的类别标签。每个点通常包含像素…