13 在 Jupyter Lab 中显示实时画面

来自Waveshare Wiki
跳转至: 导航搜索

在 Jupyter Lab 中显示实时视频画面

在上一章中我们使用 Flask 来显示摄像头的实时画面,那个方法需要在浏览器中格外开启一个新标签页或者使用其它设备打开浏览器来访问,在本章教程中我们使用在 Jupyter Lab 中观看实时视频画面的方案。

准备工作

由于产品开机默认会自动运行主程序,主程序会占用摄像头资源,这种情况下是不能使用本教程的,需要结束主程序或禁止主程序自动运行后再重新启动机器人。

这里需要注意的是,由于机器人主程序中使用了多线程且由 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 释放摄像头资源后重新运行代码块
  • 重启设备
import matplotlib.pyplot as plt  # 导入 matplotlib 库用于绘图
import cv2  # 导入 OpenCV 库用于图像处理
from picamera2 import Picamera2  # 导入 Picamera2 库用于访问 Raspberry Pi Camera
import numpy as np  # 导入 NumPy 库用于数学计算
from IPython.display import display, Image  # 导入 IPython 显示功能
import ipywidgets as widgets  # 导入 ipywidgets 库用于创建交互式控件
import threading  # 导入 threading 库用于多线程编程

# 创建一个切换按钮作为停止按钮
stopButton = widgets.ToggleButton(
    value=False,  # 按钮的初始状态为未选中
    description='Stop',  # 按钮上显示的文本
    disabled=False,  # 按钮初始为可用状态
    button_style='danger',  # 按钮样式为红色
    tooltip='Description',  # 鼠标悬停在按钮上时的提示信息
    icon='square'  # 按钮上显示的图标
)

# 定义一个函数用于显示视频流
def view(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)  # 创建一个显示句柄,用于更新显示的内容
    while True:
        # frame = picam2.capture_array()  # 从摄像头捕获一帧图像
        _, frame = camera.read() # 从摄像头捕获一帧图像
        
        # 如果需要,可以在这里对帧进行处理(例如翻转、颜色转换等)

        _, 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)
# 创建并启动一个线程,目标函数是 view 函数,参数是停止按钮
thread = threading.Thread(target=view, args=(stopButton,))
thread.start()  # 启动线程