25/1/6 算法笔记<强化学习> 初玩V-REP

news2025/1/8 14:00:01

我们安装V-REP之后,使用的是下面Git克隆的项目。

git clone https://github.com/deep-reinforcement-learning_book/Chapter16-Robot-Learning-in-Simulation.git

项目中直接组装好了一个机械臂。

我们先来分析下它的对象树

DefaultCamera:摄像机,用于捕捉场景的视图

Dummy:虚拟对象,作为参照或者用于定位其他对象

target:抓住任务中的目标物体。机器人需要抓取的物体

Vision_sensor:视觉传感器,用于捕捉场景图像,通常用于视觉反馈或识别目标

diningTable:场景中的桌子,通常作为抓取任务的基础

Sawyer:机器人手臂,执行抓取任务

Sawyer_jointX:机器人手臂的关节,用于控制各个链接之间的运动

Sawyer_tip:机器人的末端执行器,用于抓取物体

BaxterGripper:夹具部分,抓取物体的执行器

BaxterGripper_attachPoint:夹具的附加点,链接夹具和机器人手臂

BaxterGripper_attachProxSensor:接近传感器,用于检测夹具是否接近目标物体

BaxterGripper_leftJoint和BaxterGripper_rightJoint:夹具的左右关节,控制夹具的打开和关闭

BaxterGripper_leftPad和BaxterGripper_rightPad:夹具的抓取垫,实际接触目标物体的部分

在V-REP中设置好环境之后,我们需要用PyRep软件包写一个定义环境中动力学过程和奖励函数的控制脚本。我们仓库提供了定义环境的代码,我们来看下代码。

导入库和模块

from os.path import dirname, join, abspath
from pyrep import PyRep   #用与物理仿真环境交互的库
from pyrep.robots.arms.sawyer import Sawyer #机器人手臂和夹具的类
from pyrep.robots.end_effectors.baxter_gripper import BaxterGripper #夹具的类
from pyrep.objects.proximity_sensor import ProximitySensor #用于检测物体的传感器类
from pyrep.objects.vision_sensor import VisionSensor
from pyrep.objects.shape import Shape #用于场景中的物体和虚拟目标
from pyrep.objects.dummy import Dummy
from pyrep.const import JointType, JointMode #常量类,用于定义关节类型和控制模式
import numpy as np
import matplotlib.pyplot as plt
import math

定义有效位置范围

POS_MIN, POS_MAX = [0.1, -0.3, 1.], [0.45, 0.3, 1.]  # valid position range of target object 

创建GraspEnv类

class GraspEnv(object):
    ''' Sawyer robot grasping a cuboid '''

设置公共变量

self.headless = headless #设置可视化
self.reward_offset = 10.0  #成功抓取目标物体时的奖励值
self.reward_range = self.reward_offset#奖励范围,用于注册Gym环境
self.penalty_offset = 1.    #不良情况的惩罚值
self.fall_down_offset = 0.1 #判断目标物体是否掉落的距离阈值
self.metadata = []          #存储与Gym环境相关的元数据
self.control_mode = control_mode #设置控制模式

启动和设置场景

self.pr = PyRep()   #创建一个PyRep实例,用于与仿真环境交互
if control_mode == 'end_position':  #根据控制模式选择不同的场景文件
    SCENE_FILE = join(dirname(abspath(__file__)), './scenes/sawyer_reacher_rl_new_ik.ttt')
elif control_mode == 'joint_velocity':
    SCENE_FILE = join(dirname(abspath(__file__)), './scenes/sawyer_reacher_rl_new.ttt')
self.pr.launch(SCENE_FILE, headless=headless)
self.pr.start()

根据控制模式选择不同的场景文件,end_position 模式使用逆向运动学场景,而 joint_velocity 使用正向运动学场景。

获取机器人和夹具

self.agent = Sawyer() #为场景中Sawyer机器人手臂实例
self.gripper = BaxterGripper() #为场景中的Baxter Gripper夹具实例

设置传感器和物体

self.gripper_left_pad = Shape('BaxterGripper_leftPad')  #夹具的左垫,左侧接触面
self.proximity_sensor = ProximitySensor('BaxterGripper_attachProxSensor')#接近传感器
self.vision_sensor = VisionSensor('Vision_sensor') #视觉传感器
self.table  = Shape('diningTable') #桌子,在场景中用于碰撞检测的物体

控制模式设置

if control_mode == 'end_position': #根据控制模式启用或禁用控制循环。
    self.agent.set_control_loop_enabled(True) #根据控制模式定义动作空间的维度
    self.action_space = np.zeros(4)
elif control_mode == 'joint_velocity':
    self.agent.set_control_loop_enabled(False)
    self.action_space = np.zeros(7)
else:
    raise NotImplementedError

观察空间设置

self.observation_space = np.zeros(17)

包含7个关节的位置和速度以及目标的位置,总共17维。

锁定电机

self.agent.set_motor_locked_at_zero_velocity(True)

防止电机在初始化时移动。

获取目标和末端执行器

self.target = Shape('target') #目标物体
self.agent_ee_tip = self.agent.get_tip()#末端执行器
self.tip_target = Dummy('Sawyer_target')#目标点
self.tip_pos = self.agent_ee_tip.get_position()#获取末端执行器的当前位置

末端执行器(End Effector)是机器人或自动化系统中用于与环境进行交互的部件。它通常位于机器人手臂的末端,负责执行特定的任务,如抓取、搬运、切割、焊接等。

末端执行器的类型:夹具,吸盘,工具,手,喷嘴

设置初始姿态

if control_mode == 'end_position':  #设置末端执行器的初始位置和朝向
    initial_pos = [0.3, 0.1, 0.9]  #关节速度控制,设置各个关节的初始位置
    self.tip_target.set_position(initial_pos)
    self.tip_target.set_orientation([0, np.pi, np.pi/2], reset_dynamics=True)
elif control_mode == 'joint_velocity':
    self.initial_joint_positions = [0.001815199851989746, -1.4224984645843506, 0.704303503036499, 2.54307222366333, 2.972468852996826, -0.4989511966705322, 4.105560302734375]
    self.agent.set_joint_positions(self.initial_joint_positions)

记录初始位置

self.pr.step()   #更新仿真,进行一步仿真以更新状态
self.initial_tip_positions = self.agent_ee_tip.get_position()#获取末端执行器和目标物体的初始位置
self.initial_target_positions = self.target.get_position()
 

其他函数设置

_get_state:返回机器人的状态,包括关节位置,速度和目标位置
def _get_state(self):
    return np.array(self.agent.get_joint_positions()+
            self.agent.get_joint_velocities()+
            self.target.get_position())
_is_holding:检查夹具是否抓住物体
def _is_holding(self):
    pad_collide_object = self.gripper_left_pad.check_collision(self.target)#检查碰撞
    if pad_collide_object and self.proximity_sensor.is_detected(self.target)==True:
        return True  #检测目标
    else:
        return False
_move:根据给定的动作移动机器人末端执行器

函数_move可以在有效范围内通过反向运动学模式操控移动机械臂末端执行器,PuRep中可以谈股票在机械臂末端放置一个部件来实现以反向运动学控制末端执行器,具体做法是设置这个末端不见的位置和旋转角,如果调用pr.step()函数,你们在PyRep中机械臂关节的反向运动控制可以自动求解。由于单个较大步长的控制可能是不精确的,这里我们将整个动作产生的位移运动分解为一系列小步长运动,并采用一个有u自大迭代次数和最大容错值的反馈控制闭环来执行这些小步长动作。

def _move(self,action,bounding_offset=0.15,step_factor=0.2,max_itr=20,
    max_error=0.05,rotarion_norm=5.):
    pos=self.gripper.get_position()
    if pos[0]+action[0]>POS_MIN[0]-bounding_offset and pos[0]+action[0]<POS_MAX[0]+bounding_offset \
        and pos[1]+action[1]>POS_MIN[1]-bounding_offset and pos[1]+action[1]<POS_MAX[1]+2*bounding_offset \
        and pos[2]+action[2] > POS_MIN[2]-2*bounding_offset:  
        
        ori_z=-self.agent_ee_tip.get_orientation()[2] 
        target_pos = np.array(self.agent_ee_tip.get_position())+np.array(action[:3])
        diff=1 
        itr=0
        while np.sum(np.abs(diff))>max_error and itr<max_itr:
            itr+=1
            cur_pos = self.agent_ee_tip.get_position()
            diff=target_pos-cur_pos  #计算当前和目标位置之间的差
            pos = cur_pos+step_factor*diff   #根据当前差值和步长因子计算新位置
            self.tip_target.set_position(pos.tolist())
            self.pr.step()     #每次设置目标位置后,调用仿真步骤以更新状态
        ori_z+=rotation_norm*action[3]  
        self.tip_target.set_orientation([0, np.pi, ori_z])  
        self.pr.step()

    else:
        print("Potential Movement Out of the Bounding Box!")
        pass 

这段代码首先获取夹具当前的位置,

然后检查移动是否在有效范围内,如果新位置超出边界,则不执行移动。

获取当前末端执行器的朝向,特别是 Z 轴的旋转角度,并进行反转(因为在设置时方向可能存在差异)。

计算目标位置并进行移动,并通过一个循环逐步移动末端执行器到目标位置。计算当前位置与目标位置之间的差值 diff,并根据步长因子更新当前位置。

更新末端执行器的朝向,

边界检测失败时的处理。

reinit:重新初始化环境
def reset(self,random_target = False):
    self.shutdown()   #关闭原环境
    self.__init__(self.headless)#重新初始化环境,确保环境处于一个干净的状态
reset:重置环境,设置目标位置
def reset(self, random_target=False):
    '''
    在一个立方体内获取随机位置并设置目标位置。
    '''
    # 设置目标物体
    if random_target:  # 随机化
        pos = list(np.random.uniform(POS_MIN, POS_MAX))  # 从有效范围内均匀抽样
        self.target.set_position(pos)  # 随机位置
    else:  # 非随机化
        self.target.set_position(self.initial_target_positions)  # 固定位置
    self.target.set_orientation([0,0,0])  # 设置目标物体的朝向为零
    self.pr.step()  # 进行一步仿真

    # 设置末端位置以进行初始化
    if self.control_mode == 'end_position':  # 关节模式为逆运动学
        self.agent.set_control_loop_enabled(True)  # 启用逆运动学模式
        self.tip_target.set_position(self.initial_tip_positions)  # 由于在逆运动学模式下不能直接设置关节位置,因此设置末端目标位置
        self.pr.step()  # 进行一步仿真
        # 防止卡住的情况。由于使用逆运动学进行移动,卡住可能导致逆运动学无法求解,因此在目标位置未达到时采取一些随机动作。
        itr = 0
        max_itr = 10
        while np.sum(np.abs(np.array(self.agent_ee_tip.get_position() - np.array(self.initial_tip_positions)))) > 0.1 and itr < max_itr:
            itr += 1
            self.step(np.random.uniform(-0.2, 0.2, 4))  # 采取随机动作以防止卡住
            self.pr.step()  # 进行一步仿真

    elif self.control_mode == 'joint_velocity':  # 关节模式为前向运动学
        self.agent.set_joint_positions(self.initial_joint_positions)  # 设置初始关节位置
        self.pr.step()  # 进行一步仿真

    # 设置可碰撞,以便进行碰撞检测
    self.gripper_left_pad.set_collidable(True)  # 设置夹具的左垫为可碰撞,以便检查碰撞
    self.target.set_collidable(True)  # 设置目标物体为可碰撞
    # 如果夹具没有完全打开,则打开夹具
    if np.sum(self.gripper.get_open_amount()) < 1.5:
        self.gripper.actuate(1, velocity=0.5)  # 以一定速度打开夹具
        self.pr.step()  # 进行一步仿真

    return self._get_state()  # 返回当前环境状态
step:根据动作更新环境状态,计算奖励和完成状态
def step(self, action):
    '''
    根据给定的动作移动机器人手臂。
    如果控制模式为 'joint_velocity',则动作是 7 维的关节速度值 + 1 维夹具的旋转值;
    如果控制模式为 'end_position',则动作是 3 维的末端(手臂末端)位置值 + 1 维夹具的旋转值。
    '''
    # 初始化
    done = False  # 表示回合是否结束
    reward = 0  # 奖励初始化
    hold_flag = False  # 是否抓住物体的标志

    if self.control_mode == 'end_position':
        if action is None or action.shape[0] != 4:  # 检查动作是否有效
            print('没有动作或动作维度错误!')
            action = list(np.random.uniform(-0.1, 0.1, 4))  # 生成随机动作
        self._move(action)  # 根据动作移动末端执行器

    elif self.control_mode == 'joint_velocity':
        if action is None or action.shape[0] != 7:  # 检查动作是否有效
            print('没有动作或动作维度错误!')
            action = list(np.random.uniform(-0.1, 0.1, 7))  # 生成随机动作
        self.agent.set_joint_target_velocities(action)  # 在手臂上执行动作
        self.pr.step()  # 进行一步仿真

    else:
        raise NotImplementedError  # 如果控制模式不支持,抛出异常

    ax, ay, az = self.gripper.get_position()  # 获取夹具的当前位置
    if math.isnan(ax):  # 捕捉夹具位置为 NaN 的情况
        print('夹具位置为 NaN。')
        self.reinit()  # 重新初始化环境
        done = True  # 标记为结束

    tx, ty, tz = self.target.get_position()  # 获取目标物体的位置
    offset = 0.08  # 增强奖励:目标位置在目标物体上方的偏移量
    sqr_distance = (ax - tx) ** 2 + (ay - ty) ** 2 + (az - (tz + offset)) ** 2  # 夹具与目标物体之间的平方距离

    # ''' 仅用于基于视觉的控制,时间消耗大! '''
    # current_vision = self.vision_sensor.capture_rgb()  # 使用视觉传感器捕捉视图的截图
    # plt.imshow(current_vision)  # 显示捕捉到的图像
    # plt.savefig('./img/vision.png')  # 保存图像

    # 如果夹具与物体足够接近,并且接近传感器检测到目标,则关闭夹具
    if sqr_distance < 0.1 and self.proximity_sensor.is_detected(self.target) == True:
        # 确保在抓取之前夹具是打开的
        self.gripper.actuate(1, velocity=0.5)  # 打开夹具
        self.pr.step()  # 进行一步仿真
        self.gripper.actuate(0, velocity=0.5)  # 关闭夹具,0 表示关闭,1 表示打开;速度 0.5 确保夹具在一帧内关闭
        self.pr.step()  # 进行一步仿真

        if self._is_holding():  # 检查是否抓住物体
            reward += self.reward_offset  # 抓取物体时的额外奖励
            done = True  # 标记为结束
            hold_flag = True  # 设置抓住物体的标志
        else:
            self.gripper.actuate(1, velocity=0.5)  # 如果没有抓住物体,确保夹具是打开的
            self.pr.step()  # 进行一步仿真
    elif np.sum(self.gripper.get_open_amount()) < 1.5:  # 如果夹具关闭(未完全打开),则打开夹具
        self.gripper.actuate(1, velocity=0.5)  # 打开夹具
        self.pr.step()  # 进行一步仿真
    else:
        pass  # 如果夹具状态正常,什么都不做

    # 基础奖励是与目标的负距离
    reward -= np.sqrt(sqr_distance)  # 根据夹具与目标的距离调整奖励

    # 检查物体是否掉落
    if tz < self.initial_target_positions[2] - self.fall_down_offset:  
        done = True  # 标记为结束
        reward = -self.reward_offset  # 物体掉落的惩罚奖励

    # 增强奖励:如果夹具的朝向与目标物体垂直,则抓取姿势更好
    desired_orientation = np.concatenate(([np.pi, 0], [self.target.get_orientation()[2]]))  # 夹具在 z 轴上垂直于目标并朝下
    rotation_penalty = -np.sum(np.abs(np.array(self.agent_ee_tip.get_orientation()) - desired_orientation))  # 计算朝向惩罚
    rotation_norm = 0.02  # 旋转惩罚的归一化因子
    reward += rotation_norm * rotation_penalty  # 将旋转惩罚添加到奖励中

    # 碰撞惩罚
    if self.gripper_left_pad.check_collision(self.table):  # 检查夹具是否与桌子碰撞
        reward -= self.penalty_offset  # 如果发生碰撞,减少奖励
        # print('惩罚与桌子的碰撞。')

    if math.isnan(reward):  # 捕捉数值问题的情况
        reward = 0.  # 如果奖励为 NaN,重置为 0

    return self._get_state(), reward, done, {'finished': hold_flag}  # 返回当前状态、奖励、是否结束和抓取标志
shutdown:关闭仿真
def shutdown(self):
        self.pr.stop()
        self.pr.shutdown()

主程序

if __name__ == '__main__':
#设置控制模式
    CONTROL_MODE='joint_velocity'  # 'end_position' or 'joint_velocity'
    env=GraspEnv(headless=False, control_mode=CONTROL_MODE) #初始化抓取环境
    for eps in range(30):
        env.reset() #每个回合的开始,重置环境
        for step in range(30):
            if CONTROL_MODE=='end_position': #根据控制模式生成动作
                action=np.random.uniform(-0.2,0.2,4)
            elif CONTROL_MODE=='joint_velocity':
                action=np.random.uniform(-2.,2.,7)
            else:
                raise NotImplementedError
            try: 
                env.step(action)   #执行生成的动作并更新环境       
            except KeyboardInterrupt:
                print('Shut Down!')
                env.shutdown()

    env.shutdown()

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

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

相关文章

Linux驱动开发:深入理解I2C时序(二)

在Linux驱动开发中,I2C时序的理解和正确处理是保证I2C设备正常工作和通信的关键。I2C协议的时序特性决定了数据的有效传输和设备间的协作。因此,掌握I2C的时序细节,以及如何在Linux内核中进行时序处理,能够让开发者更好地处理设备通信问题。 本文将继续深入探讨I2C通信协议…

Java100道面试题

1.JVM内存结构 1. 方法区&#xff08;Method Area&#xff09; 方法区是JVM内存结构的一部分&#xff0c;用于存放类的相关信息&#xff0c;包括&#xff1a; 类的结构&#xff08;字段、方法、常量池等&#xff09;。字段和方法的描述&#xff0c;如名称、类型、访问修饰符…

《Python游戏编程入门》注-第9章8

2 游戏信息的显示 在游戏窗口的上部会显示游戏分数、游戏关卡、剩余砖块数以及剩余小球数等信息,如图12所示。 图12 游戏信息显示 使用如图13所示的代码实现以上功能。 图13 显示游戏信息的代码 其中,print_text()函数MyLibrary.

idea插件之 translation翻译插件

文章目录 1. translation翻译插件2. 效果图3. 延伸&#xff08;默认自动配置微软翻译&#xff09; 1. translation翻译插件 Settings 》Plugins 》Translation PS&#xff1a;安装后需要重启idea。 2. 效果图 右键选择插件&#xff0c;或者ctrlshifty 直接翻译代码。 3. 延伸…

Infineon PSoC 4 CapSense ModusToolbox IDE - 系统生态篇

本文档说明了 ModusToolbox 软体环境的 4 个层面&#xff0c;该环境为 CapSense 设备和生态系统提供支援。本文是 Infineon PSoC 4 CapSense ModusToolbox IDE-系统介绍的延伸篇 (Infineon PSoC 4 CapSense ModusToolbox IDE -系统介绍篇 - 大大通(简体站))。 什么是ModusToolb…

PyCharm+RobotFramework框架实现UDS自动化测试——(一)python-can 库的安装与环境配置

从0开始学习CANoe使用 从0开始学习车载测试 相信时间的力量 星光不负赶路者&#xff0c;时光不负有心人。 文章目录 1. 概述2.安装 python-can 库—基于pycharm在对应的工程下3. 在任意盘中安装环境4. 导入 can 模块语法5. 配置 CAN 接口6.CANoe设备连接语法 1. 概述 本专栏主…

springCloud实战

一、Feign的实战 1、使用 1.1步骤 ①引入feign依赖 ②在启动类上加上EnableFeignClients注解&#xff0c;开启Feign客户端 ③编写FeignClient接口 1.2开启feign调用日志 只需在yml配置文件中开启配置即可 feign:client:default:loggerLevel: FULL #feign接口被调用时的…

DINO-X环境搭建推理测试

引子 开放世界检测&#xff0c;前文也写OV-DINO&#xff08;感兴趣的童鞋&#xff0c;请移步OV-DINO开放词检测环境安装与推理-CSDN博客&#xff09;。这不&#xff0c;DINO系列又更新了。OK&#xff0c;那就让我们开始吧。 一、模型介绍 IDEA 开发了一个通用物体提示来支持无…

List ---- 模拟实现LIST功能的发现

目录 listlist概念 list 中的迭代器list迭代器知识const迭代器写法list访问自定义类型 附录代码 list list概念 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是双向链表结构&#xff0c;双向链表中每个元素…

STM32-笔记37-吸烟室管控系统项目

一、项目需求 1. 使用 mq-2 获取环境烟雾值&#xff0c;并显示在 LCD1602 上&#xff1b; 2. 按键修改阈值&#xff0c;并显示在 LCD1602 上&#xff1b; 3. 烟雾值超过阈值时&#xff0c;蜂鸣器长响&#xff0c;风扇打开&#xff1b;烟雾值小于阈值时&#xff0c;蜂鸣器不响…

VUE3配置后端地址,实现前后端分离及开发、正式环境分离

新建.env.development及.env.production .env.development 指定开发环境地址.env.production 指定生产环境地址 格式如下 VITE_APP_BASE_APIhttp://localhost:8070只需要在对应文件写入对应的后端地址即可 修改env.d.ts /// <reference types"vite/client" /…

win32汇编环境,在窗口程序中画五边形与六边形

;运行效果 ;win32汇编环境,在窗口程序中画五边形与六边形 ;展示五边形与六边形的画法 ;将代码复制进radasm软件里,直接编译可运行.重要部分加备注。 ;下面为asm文件 ;>>>>>>>>>>>>>>>>>>>>>>>>>…

Java Web开发进阶——Spring Boot基础

Spring Boot是基于Spring框架的新一代开发框架&#xff0c;旨在通过自动化配置和简化的开发方式提升生产效率。它将复杂的配置抽象化&#xff0c;让开发者专注于业务逻辑实现&#xff0c;而无需关注繁琐的基础配置。 1. Spring Boot简介与优势 Spring Boot 是 Spring 家族中的…

【Linux】文件系统命令

目录 文件系统命令 Linux文件系统 文件操作相关命令 文件系统命令 磁盘文件系统&#xff1a;指本地主机中实际可以访问到的文件系统&#xff0c;包括硬盘、CD-ROM、DVD、USB存储器、磁盘阵列等。常见文件系统格式有&#xff1a;autofs、coda、Ext&#xff08;Extended File…

关于变电站及线路接线情况展示的一些想法

以前总项目的时候总习惯于给变电站画个轮廓和接线点&#xff0c;要不就是给变电站3D建模。费时、费力效果还不一定好!其实&#xff0c;像上图一样线路搭配高清影像效果是不是会更好&#xff1f;尤其变电站区域可以使用航飞0.2米左右的数据&#xff0c;基本上站内设备都能看清了…

【OceanBase】使用 Superset 连接 OceanBase 数据库并进行数据可视化分析

文章目录 前言一、前提条件二、操作步骤2.1 准备云主机实例2.2 安装docker-compose2.3 使用docker-compose安装Superset2.3.1 克隆 Superset 的 GitHub 存储库2.3.2 通过 Docker Compose 启动 Superset 2.4 开通 OB Cloud 云数据库2.5 获取连接串2.6 使用 Superset 连接 OceanB…

开源平台Kubernetes的优势是什么?

Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;方便进行声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态系统&#xff0c;其服务、支持和工具的使用范围广泛。 Kubernetes 这个名字源于希腊语&#xff0c;意…

“大数据+职业本科”:VR虚拟仿真实训室的发展前景

在新时代背景下&#xff0c;随着科技的飞速进步和产业结构的不断升级&#xff0c;职业教育正迎来前所未有的变革。“大数据职业本科”的新型教育模式&#xff0c;结合VR&#xff08;虚拟现实&#xff09;技术的广泛应用&#xff0c;为实训教学开辟了崭新的道路&#xff0c;尤其…

flask实现国外大学生志愿者管理服务系统【英文】

完整源码项目包获取→点击文章末尾名片&#xff01;

lambda用法及其原理

目录 lambda形式lambda用法1.sort降序2.swap3.捕捉列表 习题解题 lambda形式 [capture-list](parameters)->return type{function boby}[capture-list]&#xff1a;[捕捉列表]用于捕捉函数外的参数&#xff0c;可以为空&#xff0c;但不能省略&#xff1b;(parameters) &am…