def __init__(self,
              car_serial,
              base_speed=100,
              proportional=0.4,
              integral=0,
              diff=0):
     """
         初始化类
     :param car_serial: 串口通信类
     :param base_speed: 车子在直线行走时两个轮子的速度(-255,255)
     :param proportional: PID比例参数
     :param integral: PID的积分参数
     :param diff: PID的微分参数
     """
     self.__serial = car_serial
     self.base_speed = base_speed
     self.proportional = proportional
     self.__offset = 0
     self.task_list = []
     self.__function_timer = CarTimer()  # 提供一个全局的计时器供函数使用
     self.task_list.append(
         CarTask(name="follow_line",
                 activated=True,
                 priority=5,
                 work=self.__follow_line))
Esempio n. 2
0
def do(direction=True, delay=3):
    task_list.append(
        CarTask(name="hello",
                activated=True,
                priority=1,
                timer=CarTimer(interval=delay),
                work=_do_work,
                direction=direction))
 def go_straight(self, delay_time=8):
     """
     直接向前走,不要巡线
     :param delay_time: 延迟时间
     """
     self.task_list.append(
         CarTask(name="go_straight",
                 activated=True,
                 priority=3,
                 timer=CarTimer(interval=delay_time),
                 work=self.__go_straight))
Esempio n. 4
0
 def __init__(self, line_camera='/dev/video1', od_camera='/dev/video0', serial_port='/dev/ttyUSB0'):
     self._line_camera = line_camera
     self._od_camera = od_camera
     self._serial_port = serial_port
     self.line_camera_width = 320
     self.line_camera_height = 240
     self.od_camera_width = 320
     self.od_camera_height = 240
     self.recognition = Recognition(device=od_camera, width=self.od_camera_width, height=self.od_camera_height)
     self._serial = CarSerial(self._serial_port)
     self.car_controller = CarController(self._serial)
     self.line_camera_capture = cv2.VideoCapture(self._line_camera)
     self.video = VideoWriter("video/" + time.strftime("%Y%m%d%H%M%S"), 320, 240)
     # ret, self.original_frame = self.line_camera_capture.read()
     self.available_frame = None
     self.render_frame = None  # cv2.resize(self.original_frame, (320, 240))
     self.frame_rate_timer = CarTimer()
     self.display = ShowImage()
     self.is_open_window = True
     self.is_print_frame_rate = True
     self.is_save_video = False
 def turn(self, direction=True, delay_time=1):
     """
     转弯
     :param direction: 方向(True为左,False为右)
     :param delay_time: 转弯延迟时间
     """
     self.task_list.append(
         CarTask(name="turn",
                 activated=True,
                 priority=2,
                 timer=CarTimer(interval=delay_time),
                 work=self.__turn,
                 direction=direction))
def main():
    detect = MaskDetect(json_path=j_path, weight_path=w_path)
    camera = cv2.VideoCapture(CAMERA)
    fr = FaceRecognition(known_folder="faces/", callback=callback)
    cv2.namedWindow("re_image")
    count = 0
    begin = 0

    # 每过5秒,尝试把识别数据写入数据库
    recond_timer = CarTimer(5)

    while True:
        begin = time.perf_counter()
        ret, frame = camera.read()  # 读取每一帧
        test_frame = cv2.resize(frame, (detect_width, detect_height))
        faces = detect.detect(test_frame)
        if faces:
            no_masks = [one for one in faces if one[0] == 1]
            if no_masks:
                if timer.timeout():
                    for one in no_masks:
                        re_image = get_face(frame, one)
                        fr.recognition(re_image)
                        cv2.imshow("re_image", re_image)
                    timer.restart()
        cv2.imshow("testWindow", frame)  # 把帧显示在名字为testWindow的窗口中

        frame_rate = 1 / (time.perf_counter() - begin)
        # print("frame_rate:{}".format(frame_rate))
        # 检测键盘,发现按下 q 键 退出循环
        if cv2.waitKey(1) == ord('q'):
            break
        if recond_timer.timeout():
            write_db()
            recond_timer.restart()
    fr.close()
    camera.release()  # 释放摄像头
    cv2.destroyAllWindows()  # 关闭所有窗口
 def servo_move(self, angle, delay_time=1):
     """
     旋转舵机到指定的角度,舵机的旋转需要一定的时间,在这段时间内Arduino将不会响应nano的传输的命令
     delay_time用于指定这一段时间,同时本函数应该避免在这段时间内反复调用,否则会出现Arduino因为无法响应指令而出错。
     :param angle: 转向角度
     :param delay_time: 延迟时间
     """
     self.__serial.drive_servo(
         angle)  # 向发一个指令给Arduino,然后用下面的任务占着时间,不给其他任务执行
     self.task_list.append(
         CarTask(name="servo_move",
                 activated=True,
                 priority=1,
                 timer=CarTimer(interval=delay_time),
                 work=self.__sevo_move))
 def bypass_obstacle(self, first_delay_time, second_delay_time):
     """
     避障函数,第一个时间段主要通过右轮的倒转,快速旋转,第二个时间段,通过缓慢的偏转回归到主线上
     :param first_delay_time:偏离主线的运行时间
     :param second_delay_time: 回归主线的运行时间
     """
     self.__function_timer.restart()
     ct = CarTimer(interval=first_delay_time + second_delay_time + 1.5)
     self.task_list.append(
         CarTask(name="bypass",
                 activated=True,
                 priority=2,
                 timer=ct,
                 work=self.__bypass_obstacle,
                 first_delay_time=first_delay_time,
                 second_delay_time=second_delay_time))
 def group(self, base_control_list):
     """
     连续执行BaseControl列表的函数
     :param base_control_list: 必须提供一个BaseControl对象的List
     """
     bc_list = base_control_list
     delay_time = 0.0
     for bc in bc_list:
         delay_time += bc.delay_time
     self.__function_timer.restart()
     self.task_list.append(
         CarTask(name="group_control",
                 activated=True,
                 priority=2,
                 timer=CarTimer(interval=delay_time + 0.2),
                 work=self.__group_control,
                 base_control_list=base_control_list))
Esempio n. 10
0
 def stop(self, delay_time=0):
     """
     如果delay_time = 0 将完全停止,如果delay_time> 0 将会暂停几秒
     :param delay_time: 暂停的时间,0将无限时暂停
     """
     if delay_time == 0:
         self.task_list.append(
             CarTask(name="stop",
                     activated=True,
                     priority=0,
                     work=self.__stop))
     else:
         self.task_list.append(
             CarTask(name="pause",
                     activated=True,
                     priority=1,
                     timer=CarTimer(interval=delay_time),
                     work=self.__stop))
# 从上一级目录导入模块,必须加入这两行
import sys
sys.path.append('..')
import time
from car.car_controller import CarController
from car.car_timer import CarTimer
from car.car_serial import CarSerial
from car.car_controller import BaseControl

# 新建串口通信对象,除非测试,不要直接调用此类,控制小车应该通过CarController类
serial = CarSerial("/dev/ttyACM0", receive=True)  # 参数为串口文件
# 新建一个CarController,传入串口通信对象,用于控制小车的各种动作
controller = CarController(serial, base_speed=100)

# 新建一个计时器对象,设定他的计时时间为30秒
timer = CarTimer(interval=20)

# 创建一个列表用于存储马达动作组合的列表
control_list = []

# 按需要控制的顺序,添加各种马达速度和执行时间
control_list.append(BaseControl(100, 100, 5))  # 直走5秒
control_list.append(BaseControl(0, 150, 2))  # 左转 2秒
control_list.append(BaseControl(0, 0, 2))  # 暂停2秒
control_list.append(BaseControl(150, 0, 2))  # 右转2秒
control_list.append(BaseControl(-100, -100, 5))  # 后退5秒
control_list.append(BaseControl(0, 0, 2))  # 停车

controller.group(control_list)
# 当时间未到时循环
while not timer.timeout():
Esempio n. 12
0
class CarController:
    """
        控制车子动作的类,通过该类可以控制车子的前进,转弯,暂停,控制车子舵机角度等。
        PID控制功能暂时没有添加
        本类通过设置一个控制任务列表来收集循环(每一帧)中的各个任务,并选择优先级高的任务执行。数字越小优先级越高。
        各个函数的优先级如下:
        0:stop() 小车完全停止,不能继续行走。
        1:stop(停止时间)暂停一段时间。
        2:Turn  转弯
        2:group 组合。
        3:go_straight 直走
        4:保留
        5:following_line 巡线
    """
    def __init__(self,
                 car_serial,
                 base_speed=100,
                 proportional=0.4,
                 integral=0,
                 diff=0):
        """
            初始化类
        :param car_serial: 串口通信类
        :param base_speed: 车子在直线行走时两个轮子的速度(-255,255)
        :param proportional: PID比例参数
        :param integral: PID的积分参数
        :param diff: PID的微分参数
        """
        self.__serial = car_serial
        self.base_speed = base_speed
        self.proportional = proportional
        self.__offset = 0
        self.task_list = []
        self.__function_timer = CarTimer()  # 提供一个全局的计时器供函数使用
        self.task_list.append(
            CarTask(name="follow_line",
                    activated=True,
                    priority=5,
                    work=self.__follow_line))

    # region 实际操作函数
    def __follow_line(self):
        """
        巡线的实际执行函数
        """
        self.__serial.drive_motor(
            int(self.base_speed + self.__offset * self.proportional),
            int(self.base_speed - self.__offset * self.proportional))

    def __stop(self):
        self.__serial.drive_motor(0, 0)

    def __go_straight(self):
        """
        直走实际执行函数
        """
        self.__serial.drive_motor(self.base_speed, self.base_speed)

    def __bypass_obstacle(self, **kwargs):
        """
        避障实际控制函数
        :param kwargs:需要两个参数,第一阶段延时,第二阶段延时
        """
        first_delay_time = kwargs['first_delay_time']
        second_delay_time = kwargs['second_delay_time']

        if self.__function_timer.duration() < first_delay_time:
            self.__serial.drive_motor(50, -200)
        elif first_delay_time < self.__function_timer.duration(
        ) < first_delay_time + 1.5:
            self.__serial.drive_motor(self.base_speed, self.base_speed)
        else:
            self.__serial.drive_motor(50, 200)

    def __group_control(self, **kwargs):
        bc_list = kwargs['base_control_list']

        if bc_list:
            if self.__function_timer.duration() < bc_list[0].delay_time:
                self.__serial.drive_motor(bc_list[0].left_speed,
                                          bc_list[0].right_speed)
            else:
                self.__function_timer.restart()
                bc_list.pop(0)

    def __turn(self, **kwargs):
        """
        转弯实际控制函数
        :param kwargs: 方向
        """
        direction = kwargs['direction']
        if direction:
            self.__serial.drive_motor(0, 250)
        else:
            self.__serial.drive_motor(250, 0)

    def __servo_move(self):
        """
        纯属占位置,不需要动作
        """
        pass

    # endregion

    def update(self):
        """
        在每一个帧中执行这个函数来选择优先级最高的一项控制动作,并执行该动作。
        同时删除超时的动作。
        """
        a_list = [one for one in self.task_list if one.activated]  # 筛选出活的任务
        a_list.sort(key=lambda t: t.priority)  # 按任务的优先级排序
        if len(a_list) > 0:
            task = a_list[0]  # 选择优先级最高的任务
            # print(task.name)
            if task.args:  # 如果带参数,调用实际函数,传送参数
                task.work_function(**task.args)
            else:
                task.work_function()  # 没有参数的调用没有参数的函数

        for task in a_list:  # 检测是否已经超时,超时的activated设置为False
            # print(task.name)
            if not (task.timer is None):
                # print(task.timer.duration())
                if task.timer.timeout():
                    task.activated = False

        self.task_list = a_list  # 这里相当于删除队列中超时的任务

    # region 以下为接口函数

    def follow_line(self, offset):
        """
        巡线接口
        :param offset:
        """
        self.__offset = offset

    def bypass_obstacle(self, first_delay_time, second_delay_time):
        """
        避障函数,第一个时间段主要通过右轮的倒转,快速旋转,第二个时间段,通过缓慢的偏转回归到主线上
        :param first_delay_time:偏离主线的运行时间
        :param second_delay_time: 回归主线的运行时间
        """
        self.__function_timer.restart()
        ct = CarTimer(interval=first_delay_time + second_delay_time + 1.5)
        self.task_list.append(
            CarTask(name="bypass",
                    activated=True,
                    priority=2,
                    timer=ct,
                    work=self.__bypass_obstacle,
                    first_delay_time=first_delay_time,
                    second_delay_time=second_delay_time))

    def turn(self, direction=True, delay_time=1):
        """
        转弯
        :param direction: 方向(True为左,False为右)
        :param delay_time: 转弯延迟时间
        """
        self.task_list.append(
            CarTask(name="turn",
                    activated=True,
                    priority=2,
                    timer=CarTimer(interval=delay_time),
                    work=self.__turn,
                    direction=direction))

    def stop(self, delay_time=0):
        """
        如果delay_time = 0 将完全停止,如果delay_time> 0 将会暂停几秒
        :param delay_time: 暂停的时间,0将无限时暂停
        """
        if delay_time == 0:
            self.task_list.append(
                CarTask(name="stop",
                        activated=True,
                        priority=0,
                        work=self.__stop))
        else:
            self.task_list.append(
                CarTask(name="pause",
                        activated=True,
                        priority=1,
                        timer=CarTimer(interval=delay_time),
                        work=self.__stop))

    def go_straight(self, delay_time=8):
        """
        直接向前走,不要巡线
        :param delay_time: 延迟时间
        """
        self.task_list.append(
            CarTask(name="go_straight",
                    activated=True,
                    priority=3,
                    timer=CarTimer(interval=delay_time),
                    work=self.__go_straight))

    def group(self, base_control_list):
        """
        连续执行BaseControl列表的函数
        :param base_control_list: 必须提供一个BaseControl对象的List
        """
        bc_list = base_control_list
        delay_time = 0.0
        for bc in bc_list:
            delay_time += bc.delay_time
        self.__function_timer.restart()
        self.task_list.append(
            CarTask(name="group_control",
                    activated=True,
                    priority=2,
                    timer=CarTimer(interval=delay_time + 0.2),
                    work=self.__group_control,
                    base_control_list=base_control_list))

    def servo_move(self, angle, delay_time=1):
        """
        旋转舵机到指定的角度,舵机的旋转需要一定的时间,在这段时间内Arduino将不会响应nano的传输的命令
        delay_time用于指定这一段时间,同时本函数应该避免在这段时间内反复调用,否则会出现Arduino因为无法响应指令而出错。
        :param angle: 转向角度
        :param delay_time: 延迟时间
        """
        self.__serial.drive_servo(
            angle)  # 向发一个指令给Arduino,然后用下面的任务占着时间,不给其他任务执行
        self.task_list.append(
            CarTask(name="servo_move",
                    activated=True,
                    priority=1,
                    timer=CarTimer(interval=delay_time),
                    work=self.__sevo_move))

    # endregion

    def exit(self):
        """
            清空任务列表,停止马达转动。
            本方法可用于循环结束后的收尾工作。
        """
        self.task_list.clear()
        self.__serial.drive_motor(0, 0)
        self.__serial.drive_servo(90)
        self.__serial.close()
Esempio n. 13
0
                     bitwise_not=False)
# 巡线对象
qf_line = FollowLine(LINE_CAMERA_WIDTH,
                     LINE_CAMERA_HEIGHT,
                     direction=False,
                     threshold=5)
# 寻找路口对象
fi = FindIntersection(radius=150, threshold=7, repeat_count=2, delay_time=3)
# 寻找路障对象
fr = FindRoadblock(0, 200, 134, 255, 202, 255, 0.05)
# 寻找斑马线对象
fzc = FindZebraCrossing(threshold=4, floor_line_count=4, delay_time=20)
# 保存视频对象
# vw = VideoWriter("video/" + time.strftime("%Y%m%d%H%M%S"), 320, 240)
# 一个计时器,用于计算帧速
timer = CarTimer()
# 显示图片的对象
si = ShowImage()
# endregion

while True:
    # # 帧计时开始
    # timer.restart()

    # 通过摄像头读入一帧
    ret, frame = camera.read()

    # 改变图像的大小
    frame = img_init.resize(frame)
    si.show(frame, "camera")
Esempio n. 14
0
                     bitwise_not=True)
# 巡线对象
qf_line = FollowLine(LINE_CAMERA_WIDTH,
                     LINE_CAMERA_HEIGHT,
                     direction=False,
                     threshold=5)
# 寻找路口对象
fi = FindIntersection(radius=150, threshold=4, repeat_count=2, delay_time=1.7)
# 寻找路障对象
fr = FindRoadblock(0, 200, 134, 255, 202, 255, 0.05)
# 寻找斑马线对象
fzc = FindZebraCrossing(threshold=4, floor_line_count=3)
# 保存视频对象
# vw = VideoWriter("video/" + time.strftime("%Y%m%d%H%M%S"), 320, 240)
# 一个计时器,用于计算帧速
timer = CarTimer()
# 显示图片的对象
si = ShowImage()
# endregion
serial.drive_servo(90)
while True:
    # 帧计时开始
    timer.restart()

    # 通过摄像头读入一帧
    ret, frame = camera.read()

    # 改变图像的大小
    frame = img_init.resize(frame)
    si.show(frame, "camera")
Esempio n. 15
0
import time
import sys
sys.path.append('..')

from car.car_controller import CarController
from car.car_timer import CarTimer
from car.car_serial import CarSerial

# 新建串口通信对象,除非测试,不要直接调用此类,控制小车应该通过CarController类
# 查看串口实际的串口文件可以使用 ls /dev/tty* 命令。通常 Arduino的串口文件都是 "/dev/ttyACM0" 或者"/dev/ttyUSB0"
serial = CarSerial("/dev/ttyUSB0", receive=True)  # 参数为串口文件 receive为True,可以接收到Arduino的串口反馈信息
# 新建一个CarController,传入串口通信对象,用于控制小车的各种动作
controller = CarController(serial, base_speed=100)

# 新建一个计时器对象,设定他的计时时间为30秒
timer = CarTimer(interval=30)
timer2 = CarTimer(interval=5)
controller.go_straight(delay_time=5)   # 直走5秒
# 当时间未到时循环
while not timer.timeout():
    print("time2.duration:{}".format(timer2.duration()))
    if timer2.timeout():
        # CarController 根据动作的优先级来选择需要执行的动作,我们同时输入5秒的直行和1秒转弯,它将优先执行转弯1秒
        # 当转弯一秒时间到后,转弯任务结束,这时直走还剩下4秒,所以第二秒开始就执行直走任务。
        controller.turn(direction=True, delay_time=1)  # 左转1秒
        controller.go_straight(delay_time=5)           # 直走5秒
        timer2.restart()

    controller.update()   # CarController的update方法必须在每次循环中调用,才能更新任务列表
    time.sleep(0.05)
# 计时时间到,控制小车停止
Esempio n. 16
0
import sys
import time
sys.path.append("..")
from car.car_serial import CarSerial
from car.car_timer import CarTimer
serial = CarSerial("/dev/ttyACM0", receive=True)
timer = CarTimer(interval=30)
while not timer.timeout():
    serial.drive_motor(100, -100)
    time.sleep(0.05)
    print(timer.duration())
Esempio n. 17
0
import time
import sys
sys.path.append("..")
from car.car_timer import CarTimer
from car.car_controller import CarController
from v_serial import VSerial

timer1 = CarTimer(30)
timer2 = CarTimer(5)

serial = VSerial()
control = CarController(car_serial=serial)
control.go_straight(delay_time=5)  # 直走5秒

while not timer1.timeout():
    print("timer2.dur:{}".format(timer2.duration()))
    if timer2.timeout():
        control.turn(direction=True, delay_time=1.5)  # 左转1秒
        control.go_straight(delay_time=5)
        timer2.restart()
        print("timer2.timeout")
        timer2.restart()
    control.update()
    time.sleep(0.05)

    os.getcwd())) + '/FaceMaskDetection/models/face_mask_detection.hdf5'
detect_width = 260
detect_height = 260
camera_width = 640
camera_height = 480
CAMERA = '/dev/video0'  # USB摄像头,如果有多个摄像头,各个摄像头设备文件就是video0,video1,video2等等
webPath = "../web/webDB.db"

cnn = get_conn(webPath)
id_base = fetchall(cnn,
                   "select id from student order by id desc limit 1")[0][0]
print("id_base:{}".format(id_base))
find_face = False
find_name = ""
# 间隔5秒做一次人脸识别
timer = CarTimer(5)


def write_db():
    global find_face
    global id_base
    global find_name

    print("find_face:{}".format(find_face))
    print("find_name:{}".format(find_name))
    if find_face:
        if find_name != "":
            id_base += 1
            save(cnn, "insert into student values(?,?,?)",
                 [(id_base, find_name, datetime.datetime.now())])
            find_face = False
Esempio n. 19
0
import signal

OD_CAMERA = '/dev/video0'  # 物体检测摄像头
OD_CAMERA_WIDTH = 640  # 识别视频高度
OD_CAMERA_HEIGHT = 480  # 识别视频高度
#serial = GenericSerial("/dev/ttyACM0")
serial = CarSerial("/dev/ttyACM0", receive=True)
# 新建一个识别对象,用于识别操作,程序中的识别对象只能有一个
# 指定设备,指定窗口的宽度和高度,是否打开识别显示窗口(默认是打开)
recognition = Recognition(device=OD_CAMERA,
                          width=OD_CAMERA_WIDTH,
                          height=OD_CAMERA_HEIGHT,
                          display_window=True)

# 新建一个计时器对象,用于程序结束的计时,设置时间为60秒
timer = CarTimer(interval=2)
timer2 = CarTimer(interval=1)
timer3 = CarTimer(interval=1)

angle = 90
pre_angle = angle
direct = True
is_sigint_up = False


def sigint_handler(signum, frame):
    global is_sigint_up
    is_sigint_up = True
    print('catched interrupt signal!')

Esempio n. 20
0
class CarBase:
    """
        为车子的控制提供一个基类,把一些重复的变量和函数写在基类
        继承本类的子类只需要关注于具体的操作
        类变量task_list用于存储子类的操作任务,并由基类的mail_loop负责执行
    """
    task_list = []

    def __init__(self, line_camera='/dev/video1', od_camera='/dev/video0', serial_port='/dev/ttyUSB0'):
        self._line_camera = line_camera
        self._od_camera = od_camera
        self._serial_port = serial_port
        self.line_camera_width = 320
        self.line_camera_height = 240
        self.od_camera_width = 320
        self.od_camera_height = 240
        self.recognition = Recognition(device=od_camera, width=self.od_camera_width, height=self.od_camera_height)
        self._serial = CarSerial(self._serial_port)
        self.car_controller = CarController(self._serial)
        self.line_camera_capture = cv2.VideoCapture(self._line_camera)
        self.video = VideoWriter("video/" + time.strftime("%Y%m%d%H%M%S"), 320, 240)
        # ret, self.original_frame = self.line_camera_capture.read()
        self.available_frame = None
        self.render_frame = None  # cv2.resize(self.original_frame, (320, 240))
        self.frame_rate_timer = CarTimer()
        self.display = ShowImage()
        self.is_open_window = True
        self.is_print_frame_rate = True
        self.is_save_video = False

    def main_loop(self):
        """
            整个程序的主循环,子类的各个操作模块,建立后,把它加入task_lisk列表,由本函数负责循环执行
            子类不用再写循环。
        """
        # 通过摄像头读入一帧
        while True:
            ret, self.original_frame = self.line_camera_capture.read()  # 读取一帧
            size = (self.line_camera_width, self.line_camera_height)    # 改变大小
            self.render_frame = cv2.resize(self.original_frame, size)
            self.original_frame = self.render_frame

            # 循环任务列表,按顺序执行,ImageInit需要先于其他cv下面的对象执行
            for task in CarBase.task_list:
                tmp = []
                if isinstance(task, ImageInit):     # 没办法弄成一样,所以写了两个if
                    self.available_frame = task.execute(self.original_frame)
                elif isinstance(task, FindRoadblock):
                    task.execute(self.original_frame, None)
                else:
                    tmp.append(self.render_frame)
                    task.execute(self.available_frame, tmp)
            # 实际的小车控制操作由update控制
            self.car_controller.update()

            if self.is_open_window:     # 其实如果不开窗口,必定无法退出
                self.display_window()
            if self.is_print_frame_rate:    # 这个可以取消
                self.display_frame_rate()
            if self.is_save_video:          # 保存视频
                self.video.write(self.render_frame)

            # 检测键盘,发现按下 q 键 退出循环
            if cv2.waitKey(1) == ord('q'):
                break

    def display_window(self):
        """
            显示三个窗口,大多数情况下都是需要三个窗口,所以干脆用一个函数建立把它显示出来。
        :return:
        """
        self.display.show(self.original_frame, '原始')
        self.display.show(self.available_frame, '实际')
        self.display.show(self.render_frame, '渲染')

    def display_frame_rate(self):
        """
            打印帧速率
        """
        print("帧速度:{} 帧/秒".format(1.0/self.frame_rate_timer.duration()))
        self.frame_rate_timer.restart()

    def close(self):
        """
            一些需要手动释放的对象
        """
        self._serial.drive_motor(0, 0)  # 停车
        self._serial.drive_servo(90)    # 把舵机调到90度
        self.line_camera_capture.release()  # 释放巡线摄像头
        cv2.destroyAllWindows()     # 关闭窗口
        self.recognition.close()    # 关闭对象检测
        self._serial.close()        # 关闭窗口
        self.video.release()        # 关闭录像对象