16 通过按键控制拍照
通过按键控制拍照
本章教程通过在页面上增加按键来控制摄像头拍照和录像,和之前的教程类似,图片会被默认保存在 static 文件夹内,视频会被默认保存在 videos 文件夹内。
准备工作
由于产品开机默认会自动运行主程序,主程序会占用摄像头资源,这种情况下是不能使用本教程的,需要结束主程序或禁止主程序自动运行后再重新启动机器人。
这里需要注意的是,由于机器人主程序中使用了多线程且由 crontab 配置开机自动运行,所以常规的 sudo killall python 的方法通常是不起作用的,所以我们这里介绍禁用主程序自动运行的方法。
如果你已经禁用了机器人主程序的开机自动运行,则不需要执行下面的结束主程序章节。
结束主程序
1. 点击上方本页面选项卡旁边的 “+”号,会打开一个新的名为 Launcher 的选项卡。
2. 点击 Other 内的 Terminal,打开终端窗口。
3. 在终端窗口内输入 bash 后按回车。
4. 现在你可以使用 Bash Shell 来控制机器人了。
5. 输入命令: crontab -e
6. 如果询问希望使用什么编辑器,输入 1 后按回车,选择使用 nano。
7. 打开 crontab 的配置文件后,你可以看到以下两行内容
@reboot ~/ugv_pt_rpi/ugv-env/bin/python ~/ugv_pt_rpi/app.py >> ~/ugv.log 2>&1 @reboot /bin/bash ~/ugv_pt_rpi/start_jupyter.sh >> ~/jupyter_log.log 2>&1
8.在 ……app.py >> …… 这行的最前面添加一个 # 号来注释掉这行。
# @reboot ~/ugv_pt_rpi/ugv-env/bin/python ~/ugv_pt_rpi/app.py >> ~/ugv.log 2>&1 @reboot /bin/bash ~/ugv_pt_rpi/start_jupyter.sh >> ~/jupyter_log.log 2>&1
9. 在终端页面,按 Ctrl + X 退出,它会询问你 Save modified buffer? 输入 Y,按回车,保存变更。
10. 重启设备,注意该过程会暂时关闭当前的 jupyter Lab,如果你上一步没有注释掉 ……start_jupyter.sh >>…… 这一行,那么当机器人重新开机后,你仍然可以正常使用 jupyter Lab (JupyterLab 与 机器人主程序 app.py 是互相独立运行的),可能需要重新刷新页面。
11. 这里需要注意一点,由于下位机持续通过串口与上位机通信,上位机在重启过程中有可能会由于串口电平的连续变化不能正常开机,拿上位机为树莓派的情况举例,重启时树莓派关机后不会再开机,红灯常亮绿灯不亮,此时可以关闭机器人电源开关,再打开,机器人就能够正常重启了。
12. 输入重启命令: sudo reboot
13. 等待设备重启后(重启过程中树莓派的绿灯会闪烁,当绿灯闪烁频率降低或灭掉后即代表已经启动成功),刷新页面,继续该教程的剩余部分。
例程
以下代码块可以直接运行:
1. 选中下面的代码块
2. 按 Shift + Enter 运行代码块
3. 观看实时视频窗口
4. 按 STOP 关闭实时视频,释放摄像头资源
如果运行时不能看到摄像头实时画面
- 需要点击上方的 Kernel - Shut down all kernels
- 关闭本章节选项卡,再次打开
- 点击 STOP 释放摄像头资源后重新运行代码块
- 重启设备
注意事项
如果使用USB摄像头则需要取消注释 frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) 这一句。
运行
当代码块运行时,可以通过点击 PHOTO 拍照。
import cv2 # 导入 OpenCV 库,用于图像处理 from picamera2 import Picamera2 # 用于访问 Raspberry Pi Camera 的库 import numpy as np # 用于数学计算的库 from IPython.display import display, Image # 用于在 Jupyter Notebook 中显示图像 import ipywidgets as widgets # 用于创建交互界面的小部件,如按钮 import threading # 用于创建新线程,以便异步执行任务 import os, time # 用于文件和目录操作以及时间相关的函数 time_intervel = 3 # 设置定时拍照的时间间隔(秒) photo_path = '/home/ws/ugv_pt_rpi/static/' # 设置存储照片和视频的目录路径 # 创建“停止”按钮,用户可以通过点击它来停止视频捕获和拍照 # ================ stopButton = widgets.ToggleButton( value=False, description='Stop', disabled=False, button_style='danger', # 设置按钮样式 'success', 'info', 'warning', 'danger' or '' tooltip='Description', icon='square' # 设置按钮图标 (FontAwesome names without the `fa-` prefix) ) # 创建“拍照”按钮,用户可以通过点击它来即时拍摄一张照片 # ================ photoButton = widgets.ToggleButton( value=False, description='Photo', disabled=False, button_style='danger', # 'success', 'info', 'warning', 'danger' or '' tooltip='Description', icon='square' # (FontAwesome names without the `fa-` prefix) ) # 设置连续拍照的时间间隔(秒) time_interval = 3 photo_num_count = 0 #初始化拍摄照片的计数器 capture_lock = threading.Lock() last_photo_time = time.time() #记录上一次拍照的时间 def photo_button_clicked(change, frame): global photo_num_count if change['new']: # 当“拍照”按钮被点击时 photo_num_count = photo_num_count + 1 # 照片计数器加1 photo_filename = f'{photo_path}photo_{photo_num_count}.jpg' # 设置照片的保存路径和文件名 cv2.imwrite(photo_filename, frame) # 保存照片 print(f'{photo_num_count} photos saved. new photo: {photo_filename}') # 打印照片保存信息 photoButton.value = False # 重置“拍照”按钮的状态 # 定义显示函数,用于捕获和显示视频帧,并响应拍照请求 # ================ def view(stop_button, photo_button): # 如果你使用的是CSI摄像头 需要取消注释 picam2 这些代码,并注释掉 camera 这些代码 # 因为新版本的 OpenCV 不再支持 CSI 摄像头(4.9.0.80),你需要使用 picamera2 来获取摄像头画面 # picam2 = Picamera2() # 创建 Picamera2 的实例 # picam2.configure(picam2.create_video_configuration(main={"format": 'XRGB8888', "size": (640, 480)})) # 配置摄像头参数 # picam2.start() camera = cv2.VideoCapture(-1) # 创建摄像头实例 #设置分辨率 camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640) camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) display_handle=display(None, display_id=True) # 创建显示句柄用于更新显示的图像 i = 0 while True: # frame = picam2.capture_array() # 从摄像头捕获一帧图像 _, frame = camera.read() # 从摄像头捕获一帧图像 photoButton.observe(lambda change: photo_button_clicked(change, frame), names='value') # 监听“拍照”按钮的点击事件 _, frame = cv2.imencode('.jpeg', frame) # 将图像帧编码为 JPEG 格式 display_handle.update(Image(data=frame.tobytes())) if stopButton.value==True: # picam2.close() # 如果是,则关闭摄像头 cv2.release() # 如果是,则关闭摄像头 display_handle.update(None) # 显示“停止”和“拍照”按钮,并启动一个新线程来执行显示函数 # ================ display(stopButton) display(photoButton) thread = threading.Thread(target=view, args=(stopButton, photoButton,)) thread.start()
这里需要注意的是,由于该例程使用 JupyterLab 的组件来实现,由于存在一些稳定性方面的问题,所以当你按下 Photo 拍照后,可能会保存下多张照片,你可以 JupyterLab 左边的区域浏览到 ugv_pt_rpi/static/ 内来查看拍摄的照片。