【计算机图形学】【代码复现】A-SDF中的数据集制作与数据生成

news2025/4/19 2:41:40

Follow A-SDF 的Data Generation部分:
We follow
(1) ANSCH to create URDF for shape2motion dataset
(1-2) URDF2OBJ(本人认为是1-2之间需要进行的重要的过渡部分)
(2) Manifold to create watertight meshes
(3) and modified mesh_to_sdf for generating sampled points and sdf values.

目录

  • 1. ANSCH to create URDF for shape2motion dataset
    • (1)克隆数据集,编辑自己的路径信息
    • (2)Shape2Motion数据集的解压和使用
    • (3)json2urdf
  • 1-2. URDF2OBJ
  • 2. Manifold to create watertight meshes
  • 3. modified mesh_to_sdf for generating sampled points and sdf values

1. ANSCH to create URDF for shape2motion dataset

follow这个github: ANSCH

(1)克隆数据集,编辑自己的路径信息

git clone https://github.com/dragonlong/articulated-pose.git # 克隆仓库
# global_info主要放路径信息,通过下行命令编辑路径信息
vim global_info.py

global_info.py中,主要修改192行的self.base_path路径,改成克隆下来文件夹的位置,我这里改成/mnt/d/sdc/liutong/codes/articulated-pose

(2)Shape2Motion数据集的解压和使用

因为这里需要使用到Shape2Motion数据集,所以下载一下,Shape2Motion项目主页:Shape2Motion,Shape2Motion下载链接:Shape2Motion下载链接

下载好Shape2Motion数据集后,在zip文件路径做如下操作

unzip 'Motion Dataset v0.zip' # 解压数据集
mv 'Motion Dataset v0' shape2motion # 重命名数据集
mv shape2motion /mnt/d/sdc/liutong/codes/articulated-pose/dataset/ # 将数据集移动到目标目录下

(3)json2urdf

follow github上的操作:

cd tools # 进入工具文件夹
python json2urdf.py	# 执行python2urdf

可能会出现以下报错,根据报错进行修改后重复运行python json2urdf.py

这时候报错1

Traceback (most recent call last):
  File "/mnt/d/sdc/liutong/codes/articulated-pose/tools/json2urdf.py", line 62, in <module>
    all_objs     = os.listdir( base_path  + dataset  + '/objects' )
FileNotFoundError: [Errno 2] No such file or directory: '/mnt/d/sdc/liutong/codes/articulated-pose/dataset/shape2motion/objects'
(gapartnet) root@szu-SYS-7048GR-TR:/mnt/d/sdc/liutong/codes/articulated-pose/tools# ls -l ../dadtaset/shape2motion

经检查,是Shape2Motion下没有objects文件夹,所以我将报错的62行进行更改如下,确保这部分可以正常运行:

# 从
all_objs     = os.listdir( base_path  + dataset  + '/objects' )
# 更改至
all_objs     = os.listdir( base_path  + dataset )

同理,73行中的路径代码因为也有objects,所以肯定也会出问题,同样进行修改:

# 从
instances_per_obj = sorted(glob.glob(base_path  + dataset  + '/objects/' + obj_name + '/*'))
# 更改至
instances_per_obj = sorted(glob.glob(base_path  + dataset + '/' + obj_name + '/*'))

接着会 报错2
Traceback (most recent call last):
  File "/mnt/d/sdc/liutong/codes/articulated-pose/tools/json2urdf.py", line 87, in <module>
    with open(json_name[0]) as json_file:
IndexError: list index out of range

这是因为前面运行的时候在dataset/shape2motion/下生成了一个urdf文件夹,这里面是生成的结果,所以会导致读取不到.json文件,所以只需要执行:

rm -rf dataset/shape2motion/urdf

即可。或者一劳永逸地改掉urdf的保存路径(前面的更改我没有把旧代码删掉,而注释旧代码,在下面添加新代码,所以我这里保存路径是在85行)。这里有个小小的担心是会不会后续条件下也要对urdf的路径进行修改

# 从
save_dir     = base_path + '/' + dataset  + '/urdf/{}/{}'.format(obj_name, instance_name) # todo
# 更改至
save_dir     = base_path + 'urdf/{}/{}'.format(obj_name, instance_name)

接着会 报错3
Traceback (most recent call last):
  File "/mnt/d/sdc/liutong/codes/articulated-pose/tools/json2urdf.py", line 224, in <module>
    f.write('{}/{}\t'.format(obj_j['revolute'], obj_j['prismatic']))
KeyError: 'revolute'

发现是因为有的obj_j为空造成的,故更改如下:

# 从
with open(base_path + '/' + dataset + '/statistics.txt', "a+") as f:
	for obj_j in object_joints:
		f.write('{}/{}\t'.format(obj_j['revolute'], obj_j['prismatic']))
    f.write('\n')
# 更改至
with open(base_path + '/' + dataset + '/statistics.txt', "a+") as f:
	for obj_j in object_joints:
		if not len(obj_j) == 0:
			f.write('{}/{}\t'.format(obj_j['revolute'], obj_j['prismatic']))
		else:
			f.write('NAN/NAN\t')
    f.write('\n')

虽然ANSCH后续还有一些渲染数据的操作,但是这里我们只需要拿到URDF就足够了

1-2. URDF2OBJ

在这一步,我follow了一个已经写好的github代码:urdf_to_obj
由于我们得到的文件形式是这样的:
在这里插入图片描述
在这里插入图片描述
我们要取的是这下边的syn.urdf来进行最终结果的合成,我根据这个文件格式改了一下我上面提及的github的代码,更改后的代码urdf_to_obj如下,参数释义:
--urdf_file_dir:urdf文件夹的路径
--output_dir:输出文件夹的路径
--angle:用于给输出obj文件命名时候用的,你生成的是多少度就写多少度就好了(并不是在这里指定角度就能够实现生成)

from urdfpy import URDF
import numpy as np
import trimesh
import argparse
import meshes_extracted
import os

def merge_meshes(mesh_list):
    """Merge a list of meshes into a single mesh
    
    Parameters:
    - mesh_list (list): list of trimesh.Trimesh objects
    
    Returns:
    - mesh (trimesh.Trimesh): merged mesh"""

    # Get vertices and faces
    verts_list = [mesh.vertices for mesh in mesh_list]
    faces_list = [mesh.faces for mesh in mesh_list]

    # Num of faces per mesh
    faces_offset = np.cumsum([v.shape[0] for v in verts_list], dtype=np.float32) 

    # Compute offset for faces, otherwise they all start from 0
    faces_offset = np.insert(faces_offset, 0, 0)[:-1]            

    verts = np.vstack(verts_list)
    faces = np.vstack([face + offset for face, offset in zip(faces_list, faces_offset)])

    # Create single mesh
    mesh = trimesh.Trimesh(verts, faces)

    return mesh


def main(args):
    """Extract meshes from a URDF file and save them as .obj files"""

    # Load urdf file dir
    urdf_file_dir = args.urdf_file_dir

    output_dir = args.output_dir

    # Load other parameters
    object_angle = args.angle

    # Get the sub_dir under urdf_file_dir, and then 0001/0002/0003/0004/0005
    urdf_root = sorted(os.listdir(urdf_file_dir))

    for urdf_dir in urdf_root:
        # urdf_file_path be like 0001/syn.urdf, 0002/syn.urdf, 0003/syn.urdf, etc
        urdf_file_path = os.path.join(urdf_file_dir, urdf_dir, 'syn.urdf')
        output_path = os.path.join(output_dir, f'{urdf_dir}art{object_angle}.obj')

        # Load urdf file
        robot = URDF.load(urdf_file_path)

        meshes = robot.visual_trimesh_fk()

        mesh_list = []

        for idx, mesh in enumerate(meshes):

            pose = meshes[mesh]   # 4 x 4 : rotation + translation

            translation = pose[:3, 3][:, None]
            
            # Add a column of zeros to the vertices
            verts = np.array(mesh.vertices)
            zeros = np.zeros((verts.shape[0], 1))
            new_verts = np.hstack((verts, zeros))

            # Apply pose to the vertices
            verts_pose = pose @ new_verts.transpose(1, 0) 
            verts_pose = verts_pose[:3, :] + translation   
            verts_pose = verts_pose.transpose(1, 0)
                
            mesh_extracted = trimesh.Trimesh(verts_pose, mesh.faces)

            mesh_list.append(mesh_extracted)

        # Merge meshes
        mesh_merged = merge_meshes(mesh_list)

        # Save merged meshes
        print('save ' + str(output_path))
        trimesh.exchange.export.export_mesh(mesh_merged, output_path, file_type='obj')


if __name__=='__main__':
    parser = argparse.ArgumentParser()

    parser.add_argument(
        "--urdf_file_dir", default='', type=str, help="Path to the .urdf file", required=True
    )
    parser.add_argument(
        "--output_dir", default='', type=str, help="Path to the output dir", required=True
    )
    parser.add_argument(
        "--angle", default='', type=str, help="Angle of objects, be used to name the file", required=True
    )
    args = parser.parse_args()

    main(args)

运行示例:

 python urdf_to_obj_group.py --urdf_file_dir /mnt/d/sdc/liutong/codes/articulated-pose/dataset/shape2motion/urdf/door --output_dir obj_tmp --angle 90

报错如下:

Traceback (most recent call last):
  File "urdf_to_obj_group.py", line 104, in <module>
    main(args)
  File "urdf_to_obj_group.py", line 56, in main
    robot = URDF.load(urdf_file_path)
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 3729, in load
    return URDF._from_xml(node, path)
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 3926, in _from_xml
    kwargs = cls._parse(node, path)
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 161, in _parse
    kwargs.update(cls._parse_simple_elements(node, path))
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 137, in _parse_simple_elements
    v = [t._from_xml(n, path) for n in vs]
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 137, in <listcomp>
    v = [t._from_xml(n, path) for n in vs]
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 181, in _from_xml
    return cls(**cls._parse(node, path))
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 161, in _parse
    kwargs.update(cls._parse_simple_elements(node, path))
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 137, in _parse_simple_elements
    v = [t._from_xml(n, path) for n in vs]
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 137, in <listcomp>
    v = [t._from_xml(n, path) for n in vs]
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 1146, in _from_xml
    kwargs = cls._parse(node, path)
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 161, in _parse
    kwargs.update(cls._parse_simple_elements(node, path))
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 127, in _parse_simple_elements
    v = t._from_xml(v, path)
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 181, in _from_xml
    return cls(**cls._parse(node, path))
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 161, in _parse
    kwargs.update(cls._parse_simple_elements(node, path))
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 127, in _parse_simple_elements
    v = t._from_xml(v, path)
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/urdf.py", line 581, in _from_xml
    meshes = load_meshes(fn)
  File "/home/liutong/anaconda3/envs/gapartnet/lib/python3.8/site-packages/urdfpy/utils.py", line 235, in load_meshes
    raise ValueError('At least one mesh must be pmeshesent in file')
ValueError: At least one mesh must be pmeshesent in file

其实使用原始github连接中的python urdf_to_obj.py --urdf_path relative/path/to/urdf来进行试验的话,会发现0001的syn.urdf文件会报这样的错误,而0002的syn.urdf文件就不会报这样的错误。
经过仔细地检查发现,实际上是因为0001的syn.urdf中,mesh - none_motion.obj的vertices数量为空所导致的,但是因为这个错误是包里的错误,所以我也不知道应该怎么进行修改比较合适。
最后我是直接删掉会产生这个异常的文件夹,最后不会产生异常的文件夹应该只有51个。

2. Manifold to create watertight meshes

这一步实际上就是缩减网格数量,因为原本的网格太密集了
follow这个github: Manifold
follow里面的Install进行安装就可以了
然后我根据它的命令,用python写了一个自动执行减少网格数量的程序,其中要用到的参数如下:
--obj_file_dir:放置了URDF2OBJ的那些obj文件的文件夹
--target_dir:输出文件夹路径

import os
import argparse

# res = os.popen('ls').read()
# print(res)

def main(args):
    # Load obj file dir
    obj_file_dir = args.obj_file_dir
    target_dir = args.target_dir

    obj_files = sorted(os.listdir(obj_file_dir))

    for obj in obj_files:
        source_obj = os.path.join(obj_file_dir, obj)
        obj = "manifold_" + obj
        target_obj = os.path.join(target_dir, obj)
        
        res = os.popen('./manifold ' + source_obj + ' ' + target_obj).read()
        print(res)
        

if __name__=='__main__':
    parser = argparse.ArgumentParser()

    parser.add_argument(
        "--obj_file_dir", default='', type=str, help="Path to the .obj file dir", required=True
    )
    parser.add_argument(
        "--target_dir", default='', type=str, help="Path to the target dir", required=True
    )
    args = parser.parse_args()


    main(args)

这是得到的结果:
在这里插入图片描述

3. modified mesh_to_sdf for generating sampled points and sdf values

follow 这个github: mesh_to_sdf
按照里面的步骤安装好
这一步实际上就是做sdf采样了,像A-SDF里的话采样好像是保存成一些.npy还是.npz文件的,但是我这里根据我自己的需要,需要采样并保存到.mat文件中,下面提供一个保存到.mat文件中的代码:
--manifold_obj_dir: 上一步manifold减少网格数量后的结果
--sdf_dir: .mat文件的保存路径

from mesh_to_sdf import sample_sdf_near_surface

import trimesh
import numpy as np
import os
import argparse
from scipy.io import savemat
os.environ['PYOPENGL_PLATFORM'] = 'egl'

def main(args):
    # Load obj file dir
    manifold_obj_dir = args.manifold_obj_dir
    sdf_dir = args.sdf_dir

    manifold_objs = sorted(os.listdir(manifold_obj_dir))

    for obj in manifold_objs:
        # Load mesh
        mesh = trimesh.load(os.path.join(manifold_obj_dir, obj))

        # sample points
        print("Now processing " + str(os.path.join(manifold_obj_dir, obj)))
        points, sdf, gradients = sample_sdf_near_surface(mesh, number_of_points=500000, return_gradients=True)

        # construct save path
        target_file = os.path.join(sdf_dir, obj.split('.')[0].split('_')[1] + '.mat')
		
		# if only points and sdf
        # sdf = sdf[:, np.newaxis]
        # result = np.hstack((points, sdf))
        
        result = np.hstack((points, gradients))

        print("Saving to " + str(os.path.join(sdf_dir, obj.split('.')[0].split('_')[1] + '.mat')) + ", shape:" + str(result.shape))

        data = {}
        # data.update({'p_sdf':result})
        data.update({'p':result})

        savemat(target_file, data)


if __name__=='__main__':
    parser = argparse.ArgumentParser()

    parser.add_argument(
        "--manifold_obj_dir", default='', type=str, help="Path to the .obj file dir", required=True
    )
    parser.add_argument(
        "--sdf_dir", default='', type=str, help="Path to the .mat dir", required=True
    )
    args = parser.parse_args()

    main(args)

得到的结果如下:
在这里插入图片描述
最终得到的这些SDF对文件,只需要放到目标路径文件夹下进行使用就好了

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

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

相关文章

CPLEX Studio 集成开发环境 (IDE) 介绍

CPLEX Studio 集成开发环境 (IDE) 介绍 参考B站视频&#xff1a;cplex入门到精通 1.CPLEX Studio IDE 实现的功能 IBM ILOG CPLEX Studio IDE 是一个用于数学规划、约束规划以及一般组合优化应用程序的集成开发环境。 它是适用于 OPL&#xff08;优化编程语言&#xff09;和…

PointNetGPD使用手册

1.创建环境配置环境变量 mkdir -p $HOME/code/ cd $HOME/code/ - Set environment variable PointNetGPD_FOLDER in your $HOME/.bashrc file. export PointNetGPD_FOLDER$HOME/code/PointNetGPD 2.安装 1. Install pcl-tools via sudo apt install pcl-tools. 2. An e…

在家当了几年废物,庆幸自己当初进了软件测试这行~

为什么会学习软件测试&#xff1f; 28岁了&#xff0c;仔细算一下6年了&#xff0c;工作了一年&#xff0c;没去工作就一直待在家&#xff0c;家里固定每个月给几千元&#xff0c;偶尔会都给一些&#xff0c;但依旧没钱&#xff0c;家里给我买了一套房子&#xff0c;出门300米…

【刷题之路Ⅱ】LeetCode 739. 每日温度

【刷题之路Ⅱ】LeetCode 739. 每日温度 一、题目描述二、解题1、方法1——暴力法1.1、思路分析1.2、代码实现 2、方法2——单调栈2.1、思路分析2.2、先将栈实现一下2.3、代码实现 一、题目描述 原题连接&#xff1a; 739. 每日温度 题目描述&#xff1a; 给定一个整数数组 tem…

ChatGPT 使用 拓展资料:吴恩达大咖 基于LangChain的LLM应用程序开发-1

ChatGPT 使用 拓展资料:吴恩达大咖 基于LangChain的LLM应用程序开发 基于LangChain的LLM应用程序开发 LangChain for LLM Application Development [https://www.deeplearning.ai/short-courses/langchain-for-llm-application-development/] 基于LangChain的LLM应用程序开发…

干货|SpringCloud之注册中心如何选用

SpringCloud的框架并不陌生了&#xff0c;在业内微服务领域的扛把子。今天来看一看如何根据业务需要&#xff0c;来选择合适的注册中心&#xff1f; 注册中心是微服务管理节点通信、核心配置的关键组件&#xff0c;从分布式多节点的前提下最主要要解决是就是分布式下的一致性问…

教你领取免费的亚马逊云服务服务器并搭建服务器环境的方法教程

本篇文章主要讲解&#xff0c;亚马逊新用户注册领取亚马逊免费服务器的详细操作流程方法,以及如何规避免费服务器到期后自动续费的问题解决办法。 作者&#xff1a;任聪聪 日期&#xff1a;2023年6月2日 前提材料准备 1.需要先准备好你的亚马逊账号注册所需的手机号、邮箱、vi…

18-Vue3中一些新的组件

目录 1、Fragment2、Teleport3、Suspense 1、Fragment 在Vue2中: 组件必须有一个根标签在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中好处: 减少标签层级, 减小内存占用 2、Teleport 什么是Teleport&#xff1f;—— Teleport 是一种能够将我…

一个开源的基于golang开发的企业级物联网平台

SagooIOT是一个基于golang开发的开源的企业级物联网基础开发平台。负责设备管理和协议数据管理&#xff0c;支持跨平台的物联网接入及管理方案&#xff0c;平台实现了物联网开发相关的基础功能&#xff0c;基于该功能可以快速的搭建起一整套的IOT相关的业务系统。旨在通过可复用…

测试4年,费时8个月,入职阿里,涨薪14K,可算是熬出头了····

前言 你的努力&#xff0c;终将成就无可替代的自己 本科毕业后就一直从事测试的工作&#xff0c;和多数人一样&#xff0c;最开始从事功能测试&#xff08;所谓的点点点&#xff09;的工作&#xff0c;看着自己的同学一步一步往上走&#xff0c;自己还是在原地踏步&#xff0c;…

java打jar包并包装成exe解压即用

1首先找到要加载的main方法类 public static void main(String[] args) { //创建该对象则调用构造方法&#xff0c;对象实现ActionListener则自动调用actionPerformed&#xff08;&#xff09;方法new PicdealMain();}2.点击 idea&#xff1a;File->Project Struce…(快捷键…

MyBatis深入学习总结(1.0)

MyBatis总结 MyBatis入门操作 简介 原始jdbc操作&#xff08;查询数据&#xff09; 原始jdbc操作&#xff08;插入数据&#xff09; 原始jdbc操作的分析 原始jdbbc开发存在的问题如下&#xff1a; 数据库连接创建、释放频繁造成系统资源的浪费从而影响系统性能sql语句在代…

3年经验,面试测试开发岗25K都拿不到了吗?这么坑?

最近后台读者说自己最近在疯狂投简历&#xff0c;有的石沉大海&#xff0c;但还好不是全军覆没。前两天好不容易熬到了阿里的四面&#xff0c;跟我聊了一下&#xff0c;面试官拿哪些题为难了他&#xff1f; 前面几题还好&#xff0c;问的是有关JVM的一些问题&#xff0c;比如说…

一道Java经典面试题 99%都有可能做错

前言 最近在面试中遇到一个关于位运算的题目 如下图 请问这个aString打印值是多少 学过位运算我们都知道 9<<4位 用2进制表示就是0000 1001 如果按照我之前的算法就是 0000 10001 向左位运算4 得到 1001 0000 这个时候我们得到的值就是 12816 144 拿到144这个值我们再…

session.upload_progress文件包含漏洞

session.upload_progress文件包含漏洞 前言 之前学习了该漏洞&#xff0c;但是没有做笔记&#xff0c;导致容易遗忘。在此用一个题目来理解session.upload_progress漏洞 基础知识 session存储 我们在phpinfo可以看到session的存储路径&#xff1a; 以下是一些session在lin…

【Python文本处理】基于运动路线记录GPX的文件解析,GPX转SRT字幕文件(不需要安装三方库)

【Python文本处理】基于运动路线记录GPX的文件解析&#xff0c;GPX转SRT字幕文件&#xff08;不需要安装三方库&#xff09; 解析和转换 GPX文件格式 GPX文件本身其实就是坐标、海拔、时间、心率等综合性的xml文件 如图&#xff1a; 海拔&#xff1a;ele 时间&#xff1a;t…

【GTest】使用CMakeLitsts.txt构建Windows和Linux的跨平台GoogleTest项目(非常详细+亲测有效)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、…

00): Can‘t connect to MySQL server on ‘localhost:3306‘ (10061)

好久没有使用数据库&#xff0c; 连接数据库报上面的错误&#xff0c;尝试了网上的方法还是没有成功&#xff0c;思索之后想起之前手动关闭了mysql的服务&#xff0c;Windows启动时mysql服务不会自动启动&#xff0c;成功启动mysql服务后再次连接数据库&#xff0c;正常连接。 …

keil 使用问题总结

1. 编译报错 1.1 …\USER\stm32f10x.h(428): error: #67: expected a “}” ADC1_2_IRQn 18, /*!< ADC1 and ADC2 global Interrupt */*** Using Compiler V5.06 update 4 (build 422), folder: D:\keil_v537\install\ARM\ARM…

滴滴和华为5年,分享一下真实的划水经验....

先简单交代一下背景吧&#xff0c;某不知名 985 的本硕&#xff0c;17 年毕业加入华为&#xff0c;之后跳槽到了滴滴&#xff0c;一直从事软件测试的工作。之前没有实习经历&#xff0c;算是5年的工作经验吧。 这5年之间完成了一次晋升&#xff0c;换了一家公司&#xff0c;有…