引子

非常偶然的机会收到了CSDN发送过来的手机短信,邀请参与香橙派开发板评测活动。一开始没注意到,以为是广告。后来直到香橙派的工作人员微信加群之后,才确认活动真实性。看到微信群里来自各地的开发者,大家的评测积极性也很高。

本人之前有过树莓派的开发经验,在收到香橙派开发板之前也对开发板进行了一些了解。首先是价格,这一块开发板1076元起!我的树莓派4B当年也就500元左右。

看下香橙派Kunpeng Pro(下面简称香橙派)的主要硬件参数:
项目 | 规格
-------- | -----
CPU | 4核64位处理器+ AI处理器
GPU | 集成图形处理器
AI算力 | 8TOPS算力
内存 | LPDDR4X:8GB/16GB(可选),速率:3200Mbps
存储 | TF,SATA/NVME SSD,eMMC
Wi-Fi+蓝牙 | Wi-Fi 5双频2.4G和5G,BT4.2/BLE
以太网收发器 | 10/100/1000Mbps以太网
显示 | 2xHDMI2.0 Type-A TX 4K@60FPS
1x2 lane MIPI DSI via FPC connector
摄像头 | 2x2-lane MIPI CSI camera interface,兼容树莓派摄像头
USB | USB 3.0 HOST x2
USB Type-C 3.0 HOST x1
Micro USB x1 串口打印功能
音频 | 3.5mm耳机孔音频输入/输出
按键 | 1x关机键、1xRESET键、2x启动方式拨动键、1x烧录按键
40PIN | GPIO、UART、I2C、SPI、 I2S、PWM
风扇 | 风扇接口x1
预留接口 | 2PIN电池接口
电源 | Type-C PD 20V IN ,标准65W
支持的操作系统| openEuler

初步总结一下香橙派与树莓派的主要差异:

  1. 除了支持TF卡烧录系统,还支持SATA/NVME SSD,eMMC等其它大容量存储介质烧录系统,为应用提供基础环境支持。
  2. 香橙派Kunpeng Pro具有8TOPS的AI算力。
  3. 操作系统,香橙派Kunpeng Pro官方推荐的是openEuler,该系统支持云计算、边缘计算等应用场景。

开箱测评

首先来个正面照~

香橙派Kunpeng Pro实物


找到香橙派的用户手册,可以看到香橙派的硬件接口还是很丰富的,各种硬件接口都具备。如下图:

在这里插入图片描述

友情提醒:使用开发板时,WIFI天线不能贴到开发板上,同时天线上的导电布也不能挨着班子,否则可能会烧坏开发板。

初步体验

拿到了开发板,首先要将系统启动起来。很贴心,寄送过来的开发板连SD卡系统都烧录好了,但我们还是希望从零开始一步一步的“玩起来”。

系统烧录

首先我们可以从官方网站下载香橙派对应的官方工具,里面有各种可以用得上的工具。

在这里插入图片描述


其中系统烧录的工具balenEther就在里面。然后在官方网站页面下载推荐的系统镜像文件,用于烧录。

在这里插入图片描述


然后用TF卡阅读器接入电脑,就可以对其烧录系统了。

在这里插入图片描述


烧录完成后,将SD卡取出,插入到香橙派SD卡槽。接上电源,启动香橙派。

开启SSH远程

启动香橙派后,我们就可以通过HDMI接口接在显示器或者电视机上,等待数秒,就显示了登录界面。香橙派默认提供了两个HDMI接口,根据官方文档说明,只能使用HDMI0接入。然后通过香橙派的USB接口接入鼠标键盘就可以玩耍起来了。

在这里插入图片描述


openEuler系统默认创建了openEuler账户,输入密码openEuler即可进入系统。

在这里插入图片描述


但为了方便开发,我们需要开启SSH和VNC远程。
我们给香橙派接上网线,然后可以在路由器的管理后台查看到分配给香橙派的IP地址

在这里插入图片描述

然后使用MobaXterm远程工具进行连接

在这里插入图片描述


SSH连接成功之后的界面:

在这里插入图片描述

开启VNC桌面远程

在如上的控制台输入如下命令即可开启VNC远程

sudo systemctl enable vncserver@:service.1
sudo systemctl start vncserver@:service.1
sudo system status vncserver@:service.1
vncserver

然后使用vncviewer软件远程连接香橙派:

在这里插入图片描述


点击Connect,顺利连接。我们可以在里面将WIFI改成无线连接方式,这样后面就不需要连接网线了。即刻去体验吧~

在这里插入图片描述

注意:上面最后一步vncserver一定要执行,否则不能启动VNC远程桌面。

实例应用

GPIO的应用

拿到一个开发板,最先要测试的就是GPIO接口。而最基本的GPIO应用就是点亮LED或者蜂鸣器。这里以RGB三色彩灯为例。

GPIO测试

查看用户手册,查看40Pin接口引脚说明:

在这里插入图片描述


RGB三色灯有4根线,通过杜邦线,负极接14号引脚,R(红色)接12号引脚,G(绿色)接16号引脚,B(蓝色)接18号引脚。

在控制台先执行下面命令,将PIN口设置为输出

sudo gpio_operate set_direction 7 3 1
sudo gpio_operate set_direction 2 16 1
sudo gpio_operate set_direction 0 25 1

然后可以执行下面命令,将PIN口对应的值修改,实现高低电平的切换,从而实现点亮。

sudo gpio_operate set_value 7 3 1   #红色亮
sudo gpio_operate set_value 2 16 1  #绿色亮
sudo gpio_operate set_value 0 25 1  #蓝色亮

在这里插入图片描述


可以看到RGB三种颜色同时打开的时候,显示为白色光。

Python实现

进一步,我们可以利用openEuler里面安装好的VSCode进行Python代码的编写测试。

那么,怎么在Python里面进行GPIO操作呢?

先上一段封装好的操作GPIO的Python代码,GPIO的操作其实就是对export、unexport等文件的操作。

# -*- coding: utf-8 -*-
__version__ = '1.0.0'

from threading import Lock
try:
    from collections.abc import Iterable
except ImportError:
    from collections import Iterable
import os


_export_lock = Lock()
_open_pins = {}


GPIO_ROOT = '/sys/class/gpio'
GPIO_EXPORT = os.path.join(GPIO_ROOT, 'export')
GPIO_UNEXPORT = os.path.join(GPIO_ROOT, 'unexport')
FMODE = 'w+'  # w+ overwrites and truncates existing files
IN, OUT = 'in', 'out'
LOW, HIGH = 0, 1


class GPIOPin(object):
    """Handle pin state.

    Create a singleton instance of a GPIOPin(n) and track its state internally.

    Args:
        pin (int): the pin to configure
        mode (str): use either gpio.OUT or gpio.IN
        initial (bool, optional): Initial pin value. Default is LOW
        active_low (bool, optional): Set the pin to active low. Default
            is None which leaves things as configured in sysfs
    Raises:
        RuntimeError: if pin is already configured
    """
    def __init__(self, pin, direction=None, initial=LOW, active_low=None):
        #  .configured() will raise a TypeError if "pin" is not convertable to int
        if GPIOPin.configured(pin, False) is not None:
            raise RuntimeError("pin {} is already configured".format(pin))

        self.value = None
        self.pin = int(pin)
        self.root = os.path.join(GPIO_ROOT, 'gpio{0}'.format(self.pin))

        if not os.path.exists(self.root):
            with _export_lock:
                with open(GPIO_EXPORT, FMODE) as f:
                    f.write(str(self.pin))
                    f.flush()

        # Using unbuffered binary IO is ~ 3x faster than text
        self.value = open(os.path.join(self.root, 'value'), 'wb+', buffering=0)

        # I hate manually calling .setup()!
        self.setup(direction, initial, active_low)

        # Add class to open pins
        _open_pins[self.pin] = self

    def setup(self, direction=None, initial=LOW, active_low=None):
        if direction is not None:
            self.set_direction(direction)

        if active_low is not None:
            self.set_active_low(active_low)

        if direction == OUT:
            self.write(initial)

    @staticmethod
    def configured(pin, assert_configured=True):
        """Get a configured GPIOPin instance where available.

        Args:
            pin (int): the pin to check
            assert_configured (bool): True to raise exception if pin unconfigured

        Returns:
            object: GPIOPin if configured, otherwise None

        Raises:
            RuntimeError: if pin is not configured
        """
        try:
            # Implicitly convert str to int, ie: "1" -> 1
            pin = int(pin)
        except (TypeError, ValueError):
            raise ValueError("pin must be an int")

        if pin not in _open_pins and assert_configured:
            raise RuntimeError("pin {} is not configured".format(pin))

        return _open_pins.get(pin)

    def get_direction(self):
        '''Get the direction of pin

        Returns:
            str: "in" or "out"
        '''
        with open(os.path.join(self.root, 'direction'), FMODE) as f:
            return f.read().strip()

    def set_direction(self, mode):
        '''Set the direction of pin

        Args:
            mode (str): use either gpio.OUT or gpio.IN
        '''
        if mode not in (IN, OUT, LOW, HIGH):
            raise ValueError("Unsupported pin mode {}".format(mode))

        with open(os.path.join(self.root, 'direction'), FMODE) as f:
            f.write(str(mode))
            f.flush()

    def set_active_low(self, active_low):
        '''Set the polarity of pin

        Args:
            mode (bool): True = active low / False = active high
        '''
        if not isinstance(active_low, bool):
            raise ValueError("active_low must be True or False")

        with open(os.path.join(self.root, 'active_low'), FMODE) as f:
            f.write('1' if active_low else '0')
            f.flush()

    def read(self):
        '''Read pin value

        Returns:
            int: gpio.HIGH or gpio.LOW
        '''
        self.value.seek(0)
        value = self.value.read()
        try:
            # Python > 3 - bytes
            # Subtracting 48 converts an ASCII "0" or "1" to an int
            # ord("0") == 48
            return value[0] - 48
        except TypeError:
            # Python 2.x - str
            return int(value)

    def write(self, value):
        '''Write pin value

        Args:
            value (bool): use either gpio.HIGH or gpio.LOW
        '''
        # write as bytes, about 3x faster than string IO
        self.value.write(b'1' if value else b'0')

    def cleanup(self):
        '''Clean up pin

        Unexports the pin and deletes it from the open list.

        '''
        # Note: I have not put "cleanup" into the __del__ method since it's not
        # always desireable to unexport pins at program exit.
        # Additionally "open" can be deleted *before* the GPIOPin instance.
        self.value.close()

        if os.path.exists(self.root):
            with _export_lock:
                with open(GPIO_UNEXPORT, FMODE) as f:
                    f.write(str(self.pin))
                    f.flush()

        del _open_pins[self.pin]


def cleanup(pin=None, assert_exists=False):
    """Cleanup the pin by closing and unexporting it.

    Args:
        pin (int, optional): either the pin to clean up or None (default).
            If None, clean up all pins.
        assert_exists: if True, raise a ValueError if the pin was not
            setup. Otherwise, this function is a NOOP.
    """
    # Note: since "pin" is a kwarg in this function, it has not been renamed it to "pins" above
    pins = pin

    if pins is None:
        # Must be converted to a list since _open_pins is potentially modified below
        pins = list(_open_pins.keys())

    if not isinstance(pins, Iterable):
        pins = [pins]

    for pin in pins:
        state = GPIOPin.configured(pin, assert_exists)

        if state is not None:
            state.cleanup()  # GPIOPin will remove itself from _open_pins


# TODO RPi.GPIO uses "pull_up_down", does rpio differ?
def setup(pins, mode, pullup=None, initial=LOW, active_low=None):
    '''Setup pin with mode IN or OUT.

    Args:
        pin (int):
        mode (str): use either gpio.OUT or gpio.IN
        pullup (None): rpio compatibility. If anything but None, raises
            value Error
        initial (bool, optional): Initial pin value. Default is LOW
        active_low (bool, optional): Set the pin to active low. Default
            is None which leaves things as configured in sysfs
    '''
    if not isinstance(pins, Iterable):
        pins = [pins]

    if pullup is not None:
        raise ValueError("sysfs does not support pull up/down")

    for pin in pins:
        state = GPIOPin.configured(pin, False)

        # Attempt to create the pin if not configured
        if state is None:
            state = GPIOPin(pin)  # GPIOPin will add itself to _open_pins

        state.setup(mode, initial, active_low)


def mode(pin):
    '''get the pin mode

    Returns:
        str: "in" or "out"
    '''
    return GPIOPin.configured(pin).get_direction()


def read(pin):
    '''read the pin value

    Returns:
        bool: either gpio.LOW or gpio.HIGH
    '''
    # These function calls lose us a little speed
    # but we're already > 2x faster so...
    # If you want things to be faster use a GPIOPin instance directly.
    return GPIOPin.configured(pin).read()


def write(pin, value):
    '''set the pin value to LOW or HIGH

    Args:
        pin (int): any configured pin
        value (bool): use gpio.LOW or gpio.HIGH
    '''
    # These function calls lose us a little speed
    # but we're already > 2x faster so...
    # If you want things to be faster use a GPIOPin instance directly.
    GPIOPin.configured(pin).write(value)
    

input = read
output = write
set = write  # TODO Set should be dropped, since it's a Python reserved word

然后在VSCode里面进行读写文件的操作就好了。在VSCode中写上如下代码:

from random import Random
from gpio import HIGH, LOW, OUT, GPIOPin
import time

pin1 = GPIOPin(227,OUT,0,False)
pin1.setup(OUT,None)
pin1.set_direction(OUT)

pin2 = GPIOPin(80,OUT,0,False)
pin2.setup(OUT,None)
pin2.set_direction(OUT)

pin3 = GPIOPin(25,OUT,0,False)
pin3.setup(OUT,None)
pin3.set_direction(OUT)

try:
    while True:
        pin1.write(HIGH)
        time.sleep(1)
        pin1.write(LOW)
        time.sleep(1)
        pin2.write(HIGH)
        time.sleep(1)
        pin2.write(LOW)
        time.sleep(1)
        pin3.write(HIGH)
        time.sleep(1)
        pin3.write(LOW)
        time.sleep(1)
except:
    pass

pin1.cleanup()
pin2.cleanup()
pin3.cleanup()

实现了RGB三种颜色的切换。然后直接VSCode中运行代码,产生如下错误:

在这里插入图片描述


根据错误提示,设置文件的读写权限就好了,如下图:

在这里插入图片描述


然后我们再次在VSCode里面运行python脚本即可。

YOLO的应用

要在香橙派的openEuler系统里面使用YOLO,需要先安装一些软件包。
系统默认的源更新较慢,首先需要更新pip软件安装源。

更改pip软件源

由于openEuler是CentOS系的系统,因此可以采用CentOS的方式修改。

  1. 创建或编辑~/.pip/pip.conf文件。如果文件不存在,可以使用以下命令创建一个:
    mkdir -p ~/.pip
    touch ~/.pip/pip.conf
    
  2. 使用文本编辑器打开~/.pip/pip.conf文件,例如使用vi编辑器:
vi ~/.pip/pip.conf
  1. 在文件中添加以下内容,将替换为你想要使用的镜像源地址: 输入i,进入vi文本编辑模式。

    [global]
    index-url = <your-mirror-url>
    

    其中 your-mirror-url 可以更改为更新的软件源,比如清华的源:https://pypi.tuna.tsinghua.edu.cn/simple/%E3%80%82

  2. 保存并关闭文件
    按esc键,输入:wq!,保存后退出文本编辑模式。

这时我们输入pip install 命令安装软件包时,速度快到飞起~

在这里插入图片描述

部署YOLO环境

  1. 下载YOLOv9
    git clone https://github.com/WongKinYiu/yolov9.git
    cd yolov9
    
  2. 安装依赖
    pip install torch torchvision
    pip install -r requirements.txt
    
  3. 下载yolov9权重文件

在这里插入图片描述


这里我们选择E级的权重模型来测试香橙派。将YOLOv9-E的权重文件下载下来放到yolov9根目录下即可。
地址:https://github.com/WongKinYiu/yolov9

然后就可以执行命令来测试识别效果了。

目标检测

输入图片中的命令即可进行图片中目标的检测与识别。

在这里插入图片描述


yolov9里面默认有一张图供测试,我们等待数秒,识别结果就默认保存好了。我们直接来查看一下检测的效果:

在这里插入图片描述


识别数量和识别精度都OK。

总结

通过初步的使用,明显感觉到香橙派Kunpeng Pro无论是硬件的接口类型还是计算能力,都明显要优先于树莓派4B。应该比树莓派能有更多的应用场景。

参考资料

  1. http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-kunpeng.html

  2. https://www.openeuler.org/zh/

Logo

鲲鹏展翅 立根铸魂 深耕行业数字化

更多推荐