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))
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))
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))
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():
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()
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")
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")
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) # 计时时间到,控制小车停止
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())
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
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!')
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() # 关闭录像对象