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
import sys sys.path.append("..") from od.recognition import Recognition from car.car_timer import CarTimer # from car.generic_serial import GenericSerial from car.car_serial import CarSerial import re, sys import string 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
""" 本例演示了CarController中group方法的使用。CarcCntroller已经提供了基本的马达控制函数,在某些特定的情况下, Carcontroller本身提供的基本控制方法无法满足用户的控制需求,group方法向用户提供了一个超级接口,用户可以创建 一个马达动作组合列表,向列表中添加一系列BaseControl对象,用于实现马达的动作组合。 """ # 从上一级目录导入模块,必须加入这两行 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秒
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=60) angle = 90 direct = True while not timer.timeout(): if angle < 45: direct = True elif angle > 135: direct = False if direct: angle += 4 else: angle -= 4 serial.drive_servo(angle) time.sleep(0.5)
# 对象用于对输入的图形进行二值化(或者灰度),同时对图形进行腐蚀,以去除部分图像噪声。 # 具体的参数的意义请参考类说明 # 这里要特别注意,bitwise_not为True时图像颜色进行了反转,对于灰度图,也就是黑变白,白变黑,适合于引导线是黑色的地图。 init = ImageInit(width=320, height=240, convert_type="BINARY", threshold=120, bitwise_not=True) # fl对象用于寻找引导线偏离图像中心的位置,threshold是控制连续白色的的阈值,也就是只有连续多少个白色像素点才认为已经找到引导线 # direction是开始寻找的方向,True是从左边开始寻找,False是右边。当顺时针绕圈时,引导线大概率出现在右边,所以可以选择False。 fl = FollowLine(width=320, height=240, threshold=15, direction=False) # 串口类,此类最好不要直接使用,而是通过CarController来对车子进行控制 serial = CarSerial(SERIAL, receive=True) # 此类并没有实现PID控制,而是简单的使用了比例这个参数。(现在这么简单的地图还无需用到PID) # 如果需要使用PID可以直接调用car目录下的pid类,同时把此类的比例参数设置为1 ctrl = CarController(serial, proportional=0.4) p_offset = 0 while True: ret, frame = camera.read() # 读取每一帧 frame = init.resize(frame) # 把图像缩小,尺寸有ImageInit在初始化时指定 display.show(frame, "original") image = init.processing(frame) # 对帧进行处理 # 偏置就是白色线的中心点距离图片中心点的距离,比如320*240的图像,中心点在160 offset, render_image = fl.get_offset(image, frame) # 第一个参数是需要处理的图像,第二个参数是需要渲染的图像 # 直接把Offset赋值给CarController,对于一般的线没有问题。
# 对象用于对输入的图形进行二值化(或者灰度),同时对图形进行腐蚀,以去除部分图像噪声。 # 具体的参数的意义请参考类说明 # 这里要特别注意,bitwise_not为True时图像颜色进行了反转,对于灰度图,也就是黑变白,白变黑,适合于引导线是黑色的地图。 init = ImageInit(width=320, height=240, convert_type="BINARY", threshold=120, bitwise_not=True) # fl对象用于寻找引导线偏离图像中心的位置,threshold是控制连续白色的的阈值,也就是只有连续多少个白色像素点才认为已经找到引导线 # direction是开始寻找的方向,True是从左边开始寻找,False是右边。当顺时针绕圈时,引导线大概率出现在右边,所以可以选择False。 fl = FollowLine(width=320, height=240, threshold=15, direction=False) # 串口类,此类最好不要直接使用,而是通过CarController来对车子进行控制 serial = CarSerial(SERIAL, receive=False) # 此类并没有实现PID控制,而是简单的使用了比例这个参数。(现在这么简单的地图还无需用到PID) # 如果需要使用PID可以直接调用car目录下的pid类,同时把此类的比例参数设置为1 ctrl = CarController(serial, base_speed=150, proportional=0.4) p_offset = 0 findRoadblock = FindRoadblock(h_low=0, h_high=100, s_low=0, s_high=159, v_low=80, v_high=255, threshold=0.2) while True: ret, frame = camera.read() # 读取每一帧
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())
""" 本例演示了定时器的使用,定时器简单的封装了计时,设定时长,并判断是否超时两个功能。 下面例子演示了怎样在30秒内,循环控制车子直线行走5 秒,然后左转1 秒,最后停车。 """ # 从上一级目录导入模块,必须加入这两行 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秒
from discards.line_base import * import io from car.car_serial import CarSerial from cv.video_writer import videoWriter #from objcet_detection import object_detection ser = CarSerial("/dev/ttyACM0", 115200) #obd = object_detection("tmp.jpg") IM_WIDTH = 240 IM_HEIGHT = 180 count = 5 frequency = 5 #test camera = cv2.VideoCapture(1) ret = camera.set(3, IM_WIDTH) ret = camera.set(4, IM_HEIGHT) ret, frame = camera.read() frame_rate_calc = 1 freq = cv2.getTickFrequency() exit_flag = False #fps = 15 #fourcc = cv2.VideoWriter_fourcc('h', '2', '6', '4') #sz = (int(camera.get(cv2.CAP_PROP_FRAME_WIDTH)), int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))) #vout = cv2.VideoWriter() #vout.open('sample.avi', fourcc, fps, sz) vw = videoWriter('test_one', 240, 180) # Create the in-memory stream stream = io.BytesIO()
LINE_CAMERA = '/dev/video0' # 巡线摄像头 OD_CAMERA = '/dev/video1' # 物体检测摄像头 SERIAL = "/dev/ttyACM0" # 串口 LINE_CAMERA_WIDTH = 320 # 巡线视频高度 LINE_CAMERA_HEIGHT = 240 # 巡线视频宽度 OD_CAMERA_WIDTH = 320 # 识别视频高度 OD_CAMERA_HEIGHT = 240 # 识别视频高度 section = 0 # 分段标识 p_offset = 0 # endregion # region 新立需要的各种对象 # 串口通信对象 serial = CarSerial(port=SERIAL, receive=True) # 小车控制器 ctrl = CarController(car_serial=serial, base_speed=80) # 识别对象 rc = Recognition(device=OD_CAMERA, width=OD_CAMERA_WIDTH, height=OD_CAMERA_HEIGHT, frequency=20) # cv巡线对象 camera = cv2.VideoCapture(LINE_CAMERA) ret, frame = camera.read() # 基本图像处理对象 img_init = ImageInit(LINE_CAMERA_WIDTH, LINE_CAMERA_HEIGHT,
OD_CAMERA_HEIGHT = 240 # 识别视频高度 section = 0 # 分段标识 p_offset = 0 # endregion # region 新立需要的各种对象 # 识别对象 rc = Recognition(device=OD_CAMERA, width=OD_CAMERA_WIDTH, height=OD_CAMERA_HEIGHT, frequency=20) # 串口通信对象 serial = CarSerial(port=SERIAL, receive=False) # 小车控制器 ctrl = CarController(car_serial=serial, base_speed=80) # cv巡线对象 camera = cv2.VideoCapture(LINE_CAMERA) ret, frame = camera.read() # 基本图像处理对象 img_init = ImageInit(LINE_CAMERA_WIDTH, LINE_CAMERA_HEIGHT, threshold=250, kernel_type=(3, 3), iterations=4, bitwise_not=False) # 巡线对象
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() # 关闭录像对象
本例演示了jetson nano 通过 Arduino 驱动小车的马达和舵机。 注意:在测试马达和舵机时,可以调用car_serial。在小车运行过程中,不要直接调用CarSerial类, 应该通过Car_controller类的函数来对小车进行控制。如果直接使用CarSerial类,可能会出现不可 预知的效果。 本例为了简单演示马达的控制,使用了sleep函数,在实际使用中,尽量不要使用sleep函数, 它会暂停程序的执行,这在一个多进程的程序中容易出现不可预知的错误。 建议使用car模块中的car_timer来控制时间的延迟。car_timer的使用可以参考use_timer.py. """ import time import sys sys.path.append("..") # 添加模块路径 from car.car_serial import CarSerial SERIAL = "/dev/ttyACM0" # USB 串口 # 新建一个串口类,此类最好不要直接使用,而是通过CarController来对车子进行控制 serial = CarSerial(SERIAL, receive=True) print("start") time.sleep(1) # 等待一秒 print("马达开始转动:") time.sleep(1) print("两个马达正向转动:") for i in range(20, 255, 5): serial.drive_motor(i, i) time.sleep(0.3) print("右马达正向转动,左马达停止:") for i in range(20, 255, 5):