树莓派上安装 kubeedge

安装 docker

参考资料:树莓派4B raspberrypi在线安装Docker

安装完成后,在 /etc/docker/daemon.json 中添加 : :"exec-opts": ["native.cgroupdriver=systemd"],使用 systemd 驱动

安装 kubeedge

参考 kubeedege 的离线安装文档,注意下载的资源包时注意要一定要下载 arm64 的

树莓派系列实验_温度传感器

边缘节点 join 时,指定 cgroupdriver 为 systemd

keadm join --cloudcore-ipport=xxxxx:10000 --cgroupdriver=systemd --edgenode-name=raspberrypi --kubeedge-versinotallow=1.10.2 --token=3cca11140cab1c229d……

Note:

在 join 后,如果出现报错:

Failed to start container manager, err: system validation failed - Following Cgroup subsystem not mounted: [memory] E0125 16:42:09.131414 1655 edged.go:291] initialize module error: system validation failed - Following Cgroup subsystem not mounted: [memory]

则修改树莓派的 /boot/cmdline.txt 文件,添加如下内容

cgroup_enable=memory cgroup_memory=1

在同一行的最后面,接着内容后空格后添加,注意:不要换行添加,重启机器配置生效

控制 USB 摄像头

实验介绍

本示例根据官方实验中的计数器实验代码修改而来,实验架构图如下(省略了一部分 kubeedge 组件)

树莓派系列实验_树莓派_02

页面 web app 以 Pod 的方式运行在云端的 K8S 集群节点,用户访问 web app 的页面执行操作,向 k8s 的 API server 发起修改 device instance 的动作,当 cloudcore 中的 deviceController 观察到设备发生变化,就通过 cloud hub 和 edge hub 通信,最终将该变化动作通过 EventBus 向 MQTT Broker 发布一条 topic,边端的 camera.py 进程中初始了一个 MQTT Client 订阅了该 topic,就能接收到云端对于摄像头的操作动作,根据接收到的消息内容,对摄像头发起相应的操作。

camera.py 的实现内容如下:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import cv2
import numpy as np
import paho.mqtt.client as mqtt
import json
import threading
import time

last_status = "Nothing"
picture_flag = False
lock = threading.Lock()


# 连接成功回调函数
def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))
    # 连接完成之后订阅主题
    client.subscribe('$hw/events/device/camera/twin/update/document')


# 消息处理回调函数
def on_message(client, userdata, msg):
    global last_status
    global picture_flag
    # 获得负载中的 和
    json_obj = json.loads(msg.payload.decode('utf-8').strip('\t\r\n'))
    action = json_obj['twin']['status']['current']['expected']['value']

    print("当前操作:" + action + ", 上次操作:" + last_status)
    if action == "ON" and last_status != action:
        last_status = action
        t = threading.Thread(target=open_camera)
        t.start()

    if action == "OFF" and last_status != action:
        last_status = action

    if action == "STATUS" and (last_status == "ON" or last_status == "STATUS"):
        picture_flag = True


def open_camera():
    global picture_flag
    cap = cv2.VideoCapture(0) # 0 代表 /dev/video0 这个设备,也可以使用 url
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
    cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))

    if cap.isOpened():
        while 1:
            # 图像处理
            ret, frame = cap.read()
            rows, cols, channels = frame.shape
            # 灰度化
            gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            # 平滑滤波
            blur = cv2.blur(gray_img, (3, 3))
            # 二值化
            ret1, th1 = cv2.threshold(blur, 190, 255, cv2.THRESH_BINARY)
            # 透视变换
            b = 50
            pts1 = np.float32([[b, 0], [cols - b, 0], [0, rows], [cols, rows]])
            pts2 = np.float32([[0, 0], [cols, 0], [0, rows], [cols, rows]])
            M = cv2.getPerspectiveTransform(pts1, pts2)
            dst = cv2.warpPerspective(blur, M, (cols, rows))
            # 展示窗口
            cv2.imshow('usb camera', dst) # 在操作系统界面展示图像

            if cv2.waitKey(1) and last_status == "OFF":
                break
            if cv2.waitKey(1) and picture_flag:
                lock.acquire()
                try:
                    # time.sleep(10)
                    date_str = time.strftime('%Y-%m-%d_%H:%M:%S', time.localtime())
                    filename = r'./camera/' + date_str + '.jpg'
                    cv2.imwrite(filename, frame)
                    print("截图保存为: " + filename)
                finally:
                    picture_flag = False
                    lock.release()
        cap.release()
        cv2.destroyAllWindows()


if __name__ == '__main__':
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message

    try:
        # 请根据实际情况改变MQTT代理服务器的IP地址
        client.connect("127.0.0.1", 1883, 60)
        client.loop_forever()
    except KeyboardInterrupt:
        client.disconnect()

先决条件

硬件需求

  1. 树莓派(树莓派3/4可用于此演示)。
  2. USB 摄像头

软件需求

  1. kubernetes 集群
  2. KubeEdge 1.5+

实验现象

web app 运行后可以通过 IP:port 访问

树莓派系列实验_温度传感器_03

该页面通过 kubeedge-counter-demo 的例子修改而来,主要修改的内容如下:

前端页面修改:

树莓派系列实验_json_04

前端控制器 controller

树莓派系列实验_json_05

在边端运行 test.py 程序

树莓派系列实验_json_06

在云端点击执行开启摄像头,收到了来自云端的操作,并打开了摄像头

树莓派系列实验_温度传感器_07

点击拍照后,显示图片已经保存

树莓派系列实验_树莓派_08

树莓派系列实验_温度传感器_09

边缘节点交通信号灯

实验描述

LED 灯与树莓派的连接方式

下图左侧4根线是LED的线。(最左侧的黑色线是地线。其余3根线对应红黄绿三个灯。)

树莓派系列实验_json_10

GPIO 的编号如下所示,注意 BCM 的编号和 wiringPi 的编号不太一样。

示例代码中使用的是 23,24,25 编号,采用的是 wiringPi 的编号方式,对应到物理口为 33 35 37 39 接口。

树莓派系列实验_温度传感器_11

树莓派系列实验_json_12

先决条件

硬件需求

  1. 树莓派(树莓派3/4可用于此演示)。
  2. LED 三色信号灯

软件需求

  1. kubernetes 集群
  2. KubeEdge 1.5+

实验步骤

  • 在边缘节点通过 make 命令构建:
$ make

make 的内容

树莓派系列实验_温度传感器_13

  • 在云端创建 crd 只有
$ cd crd
$ kubectl apply -f model.yaml
# 替换 "<your edge node name>" 为边缘节点名称
$ sed -i 's#raspberrypi#<your edge node name>#' instance.yaml
$ kubectl apply -f instance.yaml

Note:实例必须在模型之后创建,在模型之前删除.

  • 在云端部署创建示例程序:
# 启动一个 pod 作为 mapper 的作用运行在边缘
$ kubectl apply -f deploy.yaml
  • 修改 instance.yaml 对应灯的期望值为ON,
  • kubectl apply -f crd/instance.yaml 更新配置后对应的灯点亮。如将灯的状态由绿灯变为红灯:

边缘节点温度传感器实验

实验描述

温度映射器包含从温度传感器采集温度的代码,温度传感器通过 gpio 连接到树莓派。

树莓派系列实验_json_14

树莓派系列实验_温度传感器_15

根据温度传感器的预期状态,程序收集温度。

先决条件

硬件需求

  1. 树莓派(树莓派3/4可用于此演示)。
  2. GPIO(树莓派自带的 gpio 即可)
  3. 温度传感器 (DHT11)

软件需求

  1. Golang 1.14+
  2. KubeEdge 1.5+

实验步骤

  1. 根据上面的电路图将温度传感器和树莓派通过 GPIO 连接
  2. 克隆 KubeEdge 代码并运行
  3. 克隆 kubeedge/example 代码
$ git clone https://github.com/kubeedge/examples.git
  1. 创建温度传感器的 device model 和 device instance.
$ cd $GOPATH/src/github.com/kubeedge/examples/temperature-demo/crds
$ kubectl apply -f devicemodel.yaml
$ sed -i "s#edge-node#<your-edge-node-name>#g" instance.yaml
$ kubectl apply -f device.yaml
  1. 构建运行在树莓派上的温度传感器 mapper
$ cd $GOPATH/src/github.com/kubeedge/examples/temperature-demo
$ docker build -t <your_registry>/kubeedge-temperature-mapper:<your_tag> .
  1. 部署 mapper
$ cd $GOPATH/src/github.com/kubeedge/examples/temperature-demo/
# 修改 deployment.yaml 文件中的下述内容:
#    1. 替换 "edge-node" 为边缘节点名称
#    2. 替换 "kubeedge/temperature-mapper-demo:arm32" 为 mapper 镜像的名字
$ kubectl create -f deployment.yaml
  1. 映射器将在更新后向云报告温度,通过在云端查看设备的属性来查看温度
$ kubectl get device temperature -o yaml -w