手动控制逻辑代码
智能小车控制逻辑入口
“main.py”是小车控制代码的主入口,定义了小车控制模式及对应模式的后续调用实现。
首先导入一系列与参数有关的模块,以及启动摄像头的关键模块。
import os
from argparse import ArgumentParser
from multiprocessing import Process, Queue
from src.scenes import Manual, scene_initiator
from src.utils import getkey, log, CameraBroadcaster, CAMERA_INFO
定义出可选参数更改小车的控制模式,可选命令行cmd控制,手动控制,声音控制(正在规划中)以及测试等。
def parse_args():
parser = ArgumentParser()
parser.add_argument('--mode', type=str, required=False, default='manual',
choices=['cmd', 'voice', 'manual', 'test'])
return parser.parse_args()
遵循简单性的原则,在程序的主入口需判断模式的设定后,进入到对应模式的实现中,包含摄像头端口的调用及行驶过程中各类消息队列的处理。
if __name__ == '__main__':
args = parse_args()
log.info('start')
msg_queue = Queue(maxsize=1)
camera = CameraBroadcaster(CAMERA_INFO)
shared_memory_name = camera.memory_name
camera_process = Process(target=camera.run)
camera_process.start()
if args.mode == 'manual':
task = Manual(shared_memory_name, CAMERA_INFO, msg_queue)
process = Process(target=task.loop)
process.start()
try:
while True:
key = getkey()
if key == 'esc':
process.join()
camera.stop_sign.value = True
camera_process.join()
break
else:
msg_queue.put(key)
except (KeyboardInterrupt, SystemExit):
camera.stop_sign.value = True
camera_process.join()
os.system('stty sane')
log.info('stopping.')
elif args.mode == 'cmd':
process_list = []
record_map = {}
try:
log.info(f'start reading cmd')
while True:
command = input().strip()
if command == 'stop':
for p in process_list:
p.kill()
log.info(f'start put stop sign')
camera.stop_sign.value = True
camera_process.join()
break
elif command == 'clear':
for p in process_list:
p.kill()
process_list.clear()
log.info(f'clear succ')
continue
elif command == 'Manual':
log.error(f'Does not support switching from cmd mode to manual mode')
continue
log.info(f'building scene {command}')
scene = scene_initiator(command)
log.info(f'{scene}')
if scene is not None:
scene_obj = scene(shared_memory_name, CAMERA_INFO, msg_queue)
process = Process(target=scene_obj.loop)
process.start()
process_list.append(process)
except (KeyboardInterrupt, SystemExit):
camera.stop_sign.value = True
camera_process.join()
for process in process_list:
process.kill()
log.info('stopping.')
elif args.mode == 'voice':
raise NotImplementedError('voice control is not currently supported.')
elif args.mode == 'test':
test_list = []
test1 = Manual(shared_memory_name, CAMERA_INFO, msg_queue)
test_list.append(Process(target=test1.loop))
test2 = scene_initiator('Tracking')(shared_memory_name, CAMERA_INFO, msg_queue)
test_list.append(Process(target=test2.loop))
test3 = scene_initiator('Tracking')(shared_memory_name, CAMERA_INFO, msg_queue)
test_list.append(Process(target=test3.loop))
for process in test_list:
process.start()
try:
while True:
key = getkey()
if key == 'esc':
for process in test_list:
process.kill()
camera.stop_sign.value = True
camera_process.join()
break
else:
msg_queue.put(key)
except (KeyboardInterrupt, SystemExit):
camera.stop_sign.value = True
camera_process.join()
os.system('stty sane')
log.info('stopping.')
父主题: 代码实现
智能小车运动控制代码
“python/src/actions”文件夹中包含的是与烧录到ESP32控制端对应的小车基础和复杂运动的python代码文件以及其中的函数说明
-
小车动作函数定义。
import timefrom abc import ABC, abstractmethodclass BaseAction(ABC):"""基础动作的基类,所有基本动作均继承于该类"""def __init__(self, *args, **kwds) -> None:"""基础动作类的初始化方法,通过args与kwds控制输入参数:param args::param kwds:"""# 抽象的速度信息self.speed = kwds.get('speed', -1)# 电机角度self.servo_angle = kwds.get('servo', [-1, -1])# 根据电机的实际情况修改下发到电机的速度self.motor_rating = [1.45, 1, 1, 1]# 确定是否需要在运行时根据前动作更新电机角度及电机速度self.update_speed = Falseself.update_servo = Falseif self.speed == -1:self.update_speed = Trueif self.servo_angle[0] == -1 and self.servo_angle[1] == -1:self.update_servo = True# 由速度生成方法将抽象的总体速度计算为4个电机的速度并输出为listself.speed_setting = self.generate_speed_setting(self.speed)self.fix_speed()def fix_speed(self):self.speed_setting = [int(speed * ratio) for speed, ratio in zip(self.speed_setting, self.motor_rating)]@staticmethod@abstractmethoddef generate_speed_setting(speed, degree=0):"""生成4个电机的速度,并输出为列表抽象类,需要根据具体情况进行设置:param speed: 抽象的速度。 当前动作初始化时设置 或 控制器根据前一动作速度进行设置:param degree: 如需转弯,速度计算需要的角度信息:return:"""passdef __call__(self, speed, servo_angle):"""call魔法函数,两个输入参数由控制器输入当init方法设置了相关信息,则忽略控制器输入的参数当init方法没有设置相关信息,相关信息的将由控制器输入的参数进行更新:param speed: 抽象速度:param servo_angle: 舵机的角度:return: 长度为6的列表,前4位为4个电机的速度,后2位为舵机的两个角度"""if self.update_servo:self.servo_angle = servo_angleif self.update_speed:degree = 0if hasattr(self, 'degree'):degree = self.degreeself.speed_setting = self.generate_speed_setting(speed, degree)self.fix_speed()return self.speed_setting + self.servo_angleclass Advance(BaseAction):"""小车前进"""@staticmethoddef generate_speed_setting(speed, degree=0):return [-speed, -speed, speed, speed]class BackUp(BaseAction):"""小车后退"""@staticmethoddef generate_speed_setting(speed, degree=0):return [speed, speed, -speed, -speed]class CustomAction(BaseAction):"""自定义动作"""def __init__(self, *args, **kwds):super().__init__(*args, **kwds)self.speed_setting = kwds.get('motor_setting', [0, 0, 0, 0])self.update_controller_speed = Falseself.update_speed = False@staticmethoddef generate_speed_setting(speed, degree=0):return [0, 0, 0, 0]class Stop(BaseAction):"""小车停止"""def __init__(self, *args, **kwds):super().__init__(*args, **kwds)self.speed = 0@staticmethoddef generate_speed_setting(speed, degree=0):return [0, 0, 0, 0]class TurnLeft(BaseAction):"""小车左转"""def __init__(self, *args, **kwds):super().__init__(*args, **kwds)self.degree = kwds.get('degree', 0)self.speed_setting = self.generate_speed_setting(speed=self.speed, degree=self.degree)@staticmethoddef generate_speed_setting(speed, degree=0):return [-speed, -speed, int(speed * (1 + degree)), int(speed * (1 + degree))]class TurnRight(BaseAction):"""小车右转"""def __init__(self, *args, **kwds):super().__init__(*args, **kwds)self.degree = kwds.get('degree', 0)self.speed_setting = self.generate_speed_setting(speed=self.speed, degree=self.degree)@staticmethoddef generate_speed_setting(speed, degree=0):return [-int(speed * (1 + degree)), -int(speed * (1 + degree)), speed, speed]class ShiftLeft(BaseAction):"""向左平移"""def __init__(self, *args, **kwds):super().__init__(*args, **kwds)self.motor_rating = [1.5, 1.3, 1.25, 1.25]self.fix_speed()@staticmethoddef generate_speed_setting(speed, degree=0):return [speed, -speed, -speed, speed]class ShiftRight(BaseAction):"""向右平移"""@staticmethoddef generate_speed_setting(speed, degree=0):return [-speed, speed, speed, -speed]class LeftOblique(BaseAction):"""斜向左前方"""@staticmethoddef generate_speed_setting(speed, degree=0):return [0, -speed, 0, speed]class RightOblique(BaseAction):"""斜向右前方"""@staticmethoddef generate_speed_setting(speed, degree=0):return [-speed, 0, speed, 0]class SpinClockwise(BaseAction):"""顺时针旋转"""@staticmethoddef generate_speed_setting(speed, degree=0):return [-speed] * 4class SpinAntiClockwise(BaseAction):"""逆时针旋转"""@staticmethoddef generate_speed_setting(speed, degree=0):return [speed] * 4class SetServo(BaseAction):"""舵机转动"""def __init__(self, *args, **kwds):super().__init__(*args, **kwds)self.speed = 0@staticmethoddef generate_speed_setting(speed, degree=0):return [0, 0, 0, 0]class Sleep(BaseAction):"""Sleep(1)等同于time.sleep(1)可加入至动作序列进行使用"""def __init__(self, *args, **kwds):super().__init__(*args, **kwds)self.sleep_time = args[0]@staticmethoddef generate_speed_setting(speed, degree=0):return []def __call__(self, speed, servo_angle):time.sleep(self.sleep_time)return None -
以逆时针旋转的操作为例,方法首先继承于运动的基类,实现运动动作的方式是修改不同位置电机的旋转方向和转速。
class SpinAntiClockwise(BaseAction):"""逆时针旋转"""@staticmethoddef generate_speed_setting(speed, degree=0):return [speed] * 4 -
复杂动作则以掉头行驶为例,是多种不同的简单动作的组合,首先要导入所有的简单动作。
from abc import ABCfrom src.actions.base_action import Advance, Sleep, SpinAntiClockwise, Stop, SpinClockwise, CustomAction -
再利用简单动作形成复杂行驶动作。
class TurnAround(ComplexAction):def __init__(self):super().__init__()self.action_seq = [Stop(),Sleep(0.5),Advance(speed=30),Sleep(0.35),Stop(),Sleep(0.3),SpinAntiClockwise(speed=50),Sleep(0.55),Advance(speed=30),Sleep(1.2),SpinAntiClockwise(speed=50),Sleep(0.55),Stop(),# Advance(speed=35),]
父主题: 代码实现
手动控制逻辑代码
“python/src/scenes/manual.py”为手动控制小车的核心部分。
-
首先导入所有必须模块和基础运动模块。
import datetimeimport osimport cv2import numpy as npfrom src.actions import Advance, Stop, SetServo, TurnLeft, TurnRight, SpinClockwise, SpinAntiClockwise, BackUp, \ShiftLeft, ShiftRight, CustomActionfrom src.actions.complex_actions import ComplexAction, TurnAroundfrom src.scenes.base_scene import BaseScenefrom src.utils import log -
基于场景的基类构建手动控制小车场景的手动类,初始化后,进入到loop循环的函数中,不断等待键盘输入的键值,再执行键值对应的指令
def loop(self):ret = self.init_state() # 执行初始化if ret:log.error(f'{self.__class__.__name__} init failed.')returnframe = np.ndarray((self.height, self.width, 3), dtype=np.uint8, buffer=self.broadcaster.buf) # 拉取共享内存中的图片log.info(f'{self.__class__.__name__} loop start')last_action = SetServo(servo=[90, 65]) # 设置舵机角度while True:try:if not self.msg_queue.empty():key = self.msg_queue.get()else:continueexcept KeyboardInterrupt:self.ctrl.execute(Stop()) #捕获SIGINT之后停止小车breakdegree = 0if key == 'up':self.speed = min(self.speed + 1, 60) #加速elif key == 'down':self.speed = max(self.speed - 1, 25) #减速elif key == 'left':last_action = ShiftLeft() #左平移elif key == 'right':last_action = ShiftRight() #右平移elif key == 'w':last_action = Advance() #前进elif key == 'a':last_action = TurnLeft() #左转degree = 1.1elif key == 's':last_action = BackUp() #后退elif key == 'd':last_action = TurnRight() #右转degree = 1.1elif key == 'q':last_action = SpinAntiClockwise() #逆时针旋转elif key == 'e':last_action = SpinClockwise() #顺时针旋转elif key == 'space':last_action = Stop() #停车elif key == 'esc':self.ctrl.execute(Stop()) #退出循环前停下小车breakelif key == 'c':save_img = frame.copy()cv2.imwrite(os.path.join(self.save_dir, f'{datetime.datetime.now()}.jpg'), save_img) #保存当前摄像头中的画面log.info(f'image saved.')elif key == 't':last_action = CustomAction(motor_setting=[-62, 50, 50, -50])elif key == 'r':last_action = CustomAction(motor_setting=[55, -50, -50, 50])elif key == 'z':last_action = TurnAround() #掉头else:continueif not isinstance(last_action, ComplexAction) and not isinstance(last_action, CustomAction):last_action.update_speed = Falselast_action.speed_setting = last_action.generate_speed_setting(speed=self.speed, degree=degree)last_action.fix_speed()self.ctrl.execute(last_action)
父主题: 代码实现
目标检测模型代码
“python/src/models/yolov5.py”为yolov5模型的定义代码,为小车的基础运行提供核心的智能目标识别与检测功能。
-
示例代码定义了如何重塑图片的尺寸,并计算需要零值填充大小的功能。
def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=False, scaleFill=False, scaleup=True):# Resize image to a 32-pixel-multiple rectangle https://github.com/ultralytics/yolov3/issues/232shape = img.shape[:2] # current shape [height, width]if isinstance(new_shape, int):new_shape = (new_shape, new_shape)# Scale ratio (new / old)r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])if not scaleup: # only scale down, do not scale up (for better test mAP)r = min(r, 1.0)# Compute paddingratio = r, r # width, height ratiosnew_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh paddingif auto: # minimum rectangledw, dh = np.mod(dw, 64), np.mod(dh, 64) # wh paddingelif scaleFill: # stretchdw, dh = 0.0, 0.0new_unpad = (new_shape[1], new_shape[0])ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratiosdw /= 2 # divide padding into 2 sidesdh /= 2if shape[::-1] != new_unpad: # resizeimg = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))left, right = int(round(dw - 0.1)), int(round(dw + 0.1))img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add borderreturn img, ratio, (dw, dh) -
Yolov5的模型定义,以及推理的实现过程,形成最终的推理结果目标框和对应的类别名称。
class YoloV5(Model):def __init__(self, model_path, acl_init=True):super().__init__(model_path, acl_init)self.neth = 640self.netw = 640self.conf_threshold = 0.1dic = {0: 'left',1: 'right',2: 'stop',3: 'turnaround'}self.names = ['person', 'sports_ball', 'bicycle', 'motorcycle', 'car', 'bus', 'truck'] * 12self.object_list = ['person', 'sports_ball', 'bicycle', 'motorcycle', 'car', 'bus', 'truck']self.names = list(dic.values())self.object_list = list(dic.values())def infer(self, img_bgr):imgh, imgw = img_bgr.shape[0], img_bgr.shape[1]imginfo = np.array([self.neth, self.netw, imgh, imgw], dtype=np.float16)img_padding = letterbox(img_bgr, new_shape=(self.neth, self.netw))[0] # padding resize bgrimg = []img.append(img_padding)img = np.stack(img, axis=0)img = img[..., ::-1].transpose(0, 3, 1, 2) # BGR tp RGBimage_np = np.array(img, dtype=np.float32)image_np_expanded = image_np / 255.0img = np.ascontiguousarray(image_np_expanded).astype(np.float16) #将tensor的内存连续排列result = self.execute([img, imginfo]) #调用推理接口batch_boxout, boxnum = resultpred_boxes = []idx = 0num_det = int(boxnum[idx][0])bbox = batch_boxout[idx][:num_det * 6].reshape(6, -1).transpose().astype(np.float32) # 6xN -> Nx6for idx, class_id in enumerate(bbox[:, 5]):obj_name = self.names[int(bbox[idx][5])]if not obj_name in self.object_list:continueconfidence = bbox[idx][4]if float(confidence) < self.conf_threshold:continuex1 = int(bbox[idx][0])y1 = int(bbox[idx][1])x2 = int(bbox[idx][2])y2 = int(bbox[idx][3])pred_boxes.append([x1, y1, x2, y2, obj_name, confidence]) #获取推理结果return pred_boxes
父主题: 代码实现
目标追踪逻辑代码
在实现目标检测的前提下,结合小车的基础控制部分,将小车的速度调整依赖到目标检测的推理结果上,就能实现目标追踪。
-
“python/src/scenes/tracking.py”为目标追踪的核心代码,示例代码定义追踪的运行逻辑。
class Tracking(BaseScene):def __init__(self, memory_name, camera_info, msg_queue):super().__init__(memory_name, camera_info, msg_queue)self.model = Nonedef init_state(self):log.info(f'start init {self.__class__.__name__}')model_path = os.path.join(os.getcwd(), 'weights', 'tracking.om')if not os.path.exists(model_path):log.error(f'Cannot find the offline inference model(.om) file needed for {self.__class__.__name__} scene.')return Trueself.model = YoloV5(model_path) #加载模型log.info(f'{self.__class__.__name__} model init succ.')self.ctrl.execute(SetServo(servo=[90, 65])) #设置舵机角度return Falsedef loop(self):ret = self.init_state() #执行初始化if ret:log.error(f'{self.__class__.__name__} init failed.')returnframe = np.ndarray((self.height, self.width, 3), dtype=np.uint8, buffer=self.broadcaster.buf) #获取共享内存中的图片log.info(f'{self.__class__.__name__} loop start')last_action = Nonelast_not_seen = Trueforward_speed_slow = 30forward_speed_fast = 40while True:action = Noneif self.stop_sign.value:breakif self.pause_sign.value:continueimg_bgr = frame.copy()bboxes = self.model.infer(img_bgr)log.info(f'{bboxes}')if not bboxes:if last_not_seen:action = Stop()else:last_not_seen = Truecontinueelse:if len(bboxes) > 1:ori_box = sorted(bboxes, key=lambda x: x[-1], reverse=True)[0][:4]else:ori_box = bboxes[0][:4]x1, y1, x2, y2 = ori_boxx, y = (x1 + x2) // 2, (y1 + y2) // 2 #计算目标中心点的x与y坐标h, w = y2 - y1, x2 - x1 #计算目标的宽高if h * w < 141 * 128 or y < 110: #进行距离判断,如果过远就加速,否则减速speed = forward_speed_fastelse:speed = forward_speed_slowif x < 400:action = TurnLeft(degree=1.1, speed=speed) #左转elif x > 1000:action = TurnRight(degree=1.1, speed=speed) #右转else:action = Advance(speed=speed) #直行if h * w > 800 * 500 or y > 390: #如果距离过近则停车action = Stop()if action is None or action == last_action:continueself.ctrl.execute(action)last_action = action -
初始化并导入Yolov5目标检测模型。
def __init__(self, memory_name, camera_info, msg_queue):super().__init__(memory_name, camera_info, msg_queue)self.model = Nonedef init_state(self):log.info(f'start init {self.__class__.__name__}')model_path = os.path.join(os.getcwd(), 'weights', 'tracking.om')if not os.path.exists(model_path):log.error(f'Cannot find the offline inference model(.om) file needed for {self.__class__.__name__} scene.')return Trueself.model = YoloV5(model_path)log.info(f'{self.__class__.__name__} model init succ.')self.ctrl.execute(SetServo(servo=[90, 65]))return False -
在得到正确导入结果后,开启循环,不断获取推理结果,并根据结果估算智能小车和追踪目标之间的距离,再根据计算出的结果下达不同的运动指令,设置慢速和快速跟进的两个速度。
def loop(self):ret = self.init_state()if ret:log.error(f'{self.__class__.__name__} init failed.')returnframe = np.ndarray((self.height, self.width, 3), dtype=np.uint8, buffer=self.broadcaster.buf)log.info(f'{self.__class__.__name__} loop start')last_action = Nonelast_not_seen = Trueforward_speed_slow = 30forward_speed_fast = 40 -
获取推理结果的外接框。
bboxes = self.model.infer(img_bgr) -
计算出目标框的中心点的位置和目标框的宽高大小。
log.info(f'{bboxes}')if not bboxes:if last_not_seen:action = Stop()else:last_not_seen = Truecontinueelse:if len(bboxes) > 1:ori_box = sorted(bboxes, key=lambda x: x[-1], reverse=True)[0][:4]else:ori_box = bboxes[0][:4]x1, y1, x2, y2 = ori_boxx, y = (x1 + x2) // 2, (y1 + y2) // 2h, w = y2 - y1, x2 - x1根据计算出的目标框的大小来判断小车和目标之间的距离,再调整小车的行进速度。根据目标近大远小的简单规则,存在两个判断条件,如果目标框的面积小于一定值,就说明小车与目标距离较远,需要快速接近目标,另外如果识别框的中心点的纵坐标大于0,也就是在摄像头视角里的上半部分,也说明小车距离目标较远,也需快速接近目标,反之亦然。
if h * w < 141 * 128 or y < 110:speed = forward_speed_fastelse:speed = forward_speed_slow -
另外如果前方目标在小车的偏左或偏右的位置,也可以采用同样的判断方法,即判断目标框的中心点的横坐标落在小车摄像头视角画面中的左侧还是右侧,进而下发对应的微调转向的命令,实现跟踪目标的方向调整。
if x < 400:action = TurnLeft(degree=1.1, speed=speed)elif x > 1000:action = TurnRight(degree=1.1, speed=speed)else:action = Advance(speed=speed)if h * w > 800 * 500 or y > 390:action = Stop()
父主题: 代码实现
训练目标追踪功能模型
当前目标追踪功能,需要单独使用模型适配工具训练(模型适配工具的安装与使用请参见《[使用模型适配工具生成推理应用](https://www.hiascend.com/document/detail/zh/Atlas200IDKA2DeveloperKit/23.0.RC2/Getting Started with Application Development/iaqd/iaqd_0001.html)》),用户可参见本节,进行模型的训练获得对应模型文件。
-
收集待标记的png、jpg、JPEG、bmp、webp格式图片数据,推荐使用jpg格式。图片分辨率不高于1080P,单张图片不小于1MB,推荐使用小车上的摄像头进行图片收集,数量为200张以上且各角度均包含,并放置在全英文路径下。
注:图片名称不要带字符"."。
-
为模型迁移准备数据集,进行图像标注,在模型适配工具界面选择“检测模型”。
-
单击“打开目录”选择1收集的数据集目录进行标注。
-
单击
按钮,使用矩形框包围目标后单击鼠标左键,弹出添加标签界面,如图1所示。填写对应目标分类标签与Group ID号,当一个图片中有多个目标时需填写不同的ID号,单击“确定”完成标注。
图1 添加标签

图2 标注结果

-
若标记错误可单击
按钮,按住左键可以移动标记框,移动鼠标至矩形框并单击“鼠标右键”,对矩形标签进行修改。
图3 修改标签

-
当前图片标注完成后,单击图片上方菜单栏中
图标或在左侧文件列表选择下一张图片进行标记,直到完成所有图片的标注任务。
:::note 说明
- 标注时输入标签仅支持数字、字母、下划线。
- 数据集图片要从实际模型部署使用的环境获得。
- 需将图片中的所有待检测目标都标注出来,漏标注将影响模型精度。
- 边框需要紧密框住每个目标,且类别正确,标注无误。 :::
-
模型迁移
-
在工具界面单击下方“一键迁移”按钮,进入配置界面,输入迁移信息,单击“一键迁移”开始迁移。
图4 模型一键迁移配置界面

- 数据集路径:2输出的自定义数据集输出路径。
- 数据集拆分:将图片划分成训练、验证以及测试集的比例,推荐值:0.3。默认拆分0.1的测试集用于边缘推理,训练集与验证集按输入拆分比例再次进行拆分。
- 迭代次数:训练轮次,推荐值:100。
- 每批图片数:参与每个批次训练的图片张数,推荐值:12。
- 预训练模型:可选yolov5s,yolov5n,yolov5l,yolov5x,默认yolov5s。
- 输出目录:模型输出路径。
- 使用早停策略:勾选后,可根据设置的mAP值(均值平均精度,一般指图片内所有类别的AP的平均值)和持续迭代不上升次数,提前停止训练。
- mAP达到(值):该训练模型精度已达标,可停止训练的阈值,默认值:0.99。
- mAP连续迭代不上升次数:mAP值达到某一水平,多次迭代后并无提升的次数,默认值:10。
-
迁移完成后会出现提示框,提示已生成打包好的文件,如图5所示。在训练输出目录会生成以下文件与目录,如图6所示。
- train_output:训练输出的权重文件、onnx文件以及训练数据信息json文件。
- trans_output:经过数据转换,根据数据集拆分设置生成的测试集、验证集、训练集。
- infer_project.tar.gz:打包好的推理相关模型文件与脚本。
图5 迁移完成
图6 输出文件
在线提单