旭日X3派 ROS2安装和视觉检测

1、前言

​ 非常感谢古月居平台推出的高校合作计划,我有幸能够使用旭日X3派进行学习和制作毕业设计。旭日X3派是一款面向生态开发者的嵌入式开发板,接口兼容树莓派、具有5Tops端侧推理与4核ARM A53处理能力。本人使用旭日X3派作为主控芯片,安装ROS2操作系统,并调试。

2、硬件准备

旭日X3板、USB-typeC数据线一根、usb摄像头一个

3、 旭日x3派安装ROS2 详细教程

1:ROS 目前主要支持Linux和MacOS系统,新发布的 ROS2 也支持 Windows和RTOS 系统。
对ROS兼容性最好的当属Ubuntu系统。从ROS发布以来,每版的Ubuntu系统版本都有与之对应ROS版本的,每一版ROS都有其对应版本的Ubuntu版本,切记不可随便装。

ROS 和Ubuntu之间的版本对应关系:

ROS2版本 发布时间 维护截止时间 Ubuntu版本
Ardent Apalone 2017.12 2018.12 Ubuntu 16.04(Xenial Xerus)
Bouncy Bolson 2018.7 2019.7 Ubuntu 16.04(Xenial Xerus)、Ubuntu 18.04 (Bionic Beaver)
Crystal Clemmys 2018.12 2019.12 Ubuntu 18.04 (Bionic Beaver)
Dashing Diademata 2019.5 2021.5 Ubuntu 18.04 (Bionic Beaver)
Eloquent Elusor 2019.11 2020.11 Ubuntu 18.04 (Bionic Beaver)
Foxy Fitzroy 2020.6 2023.5 Ubuntu 20.04 (Focal Fossa)
Galactic Geochelone 2021.5 2022.11 Ubuntu 20.04 (Focal Fossa)
Humble Hawksbill 2022.5 2027.5 Ubuntu 22.04 (Jammy Jellyfish)

从表中看出Ubuntu 20.04 支持的ROS2版本为Foxy,可以从这个网站查看ROS2的各个发行版本的介绍信息。

2:旭日X3派安装准备

  • 打开MobaXterm,

  • 点击Session,点击SSH,输入刚刚查看的IP地址,输入旭日X3派密码:sunrise.(注意电脑要和X3派在相同wifi上)

  • 点击ok,即可看到如下画面:

2:ROS2 系统安装 【ubuntu 20.04】

1):设置编码UTF-8

终端输入:

locale

图中看出确定支持UTF-8,无需设置。

2):添加源

## 通过检查此命令的输出,确保已启用Ubuntu Universe存储库。
$ apt-cache policy | grep universe

## 输出应如下:
 100 http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports focal-backports/universe arm64 Packages
     release v=20.04,o=Ubuntu,a=focal-backports,n=focal,l=Ubuntu,c=universe,b=arm64
 500 http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports focal-updates/universe arm64 Packages
     release v=20.04,o=Ubuntu,a=focal-updates,n=focal,l=Ubuntu,c=universe,b=arm64
 500 http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports focal-security/universe arm64 Packages
     release v=20.04,o=Ubuntu,a=focal-security,n=focal,l=Ubuntu,c=universe,b=arm64
 500 http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports focal/universe arm64 Packages
     release v=20.04,o=Ubuntu,a=focal,n=focal,l=Ubuntu,c=universe,b=arm64

若没有看到像上面这样的输出行,依次执行如下命令:

$ sudo apt install software-properties-common
$ sudo add-apt-repository universe

继续执行如下命令:

$ sudo apt update && sudo apt install curl gnupg2 lsb-release
$ sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key  -o /usr/share/keyrings/ros-archive-keyring.gpg
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

如果遇到报错“ailed to connect to raw.githubusercontent.com port 443 after 13 ms: 拒绝连接”

解决办法如下:

1.登录网站:https://www.ipaddress.com
2.在打开的网站中将“raw.githubusercontent.com”复制到查询栏中进行搜索,可以看到域名对应的IP地址信息

3.结果中展示的Ip地址和域名拷贝系统hosts文件中:

sudo vi /etc/hosts

4.保存退出后,就可以正常使用了。

3)安装ROS2

sudo apt update
sudo apt upgrade
## 推荐桌面版,比较推荐。
sudo apt install ros-foxy-desktop
## 安装时间可能较长,安心等待。

4)设置环境变量

source /opt/ros/foxy/setup.bash
echo " source /opt/ros/foxy/setup.bash" >> ~/.bashrc

至此,ROS2就已经在系统中安装好了。

5)ROS2 示例测试

示例一:小海龟仿真示例

再来试一试ROS中的经典示例——小海龟仿真器。
使用MobaXterm打开两个终端,分别运行如下指令:

ros2 run turtlesim turtlesim_node

在另一个终端中输入:

示例二:

启动第一个终端,通过以下命令启动一个数据的发布者节点:

4、视觉检测

4.1 创建功能包

如何在ROS2中创建一个功能包呢?我们可以使用这个指令:

ros2 pkg create --build-type <build-type> <package_name>

ros2命令中:

  • pkg:表示功能包相关的功能;
  • create:表示创建功能包;
  • build-type:表示新创建的功能包是C++还是Python的,如果使用C++或者C,那这里就跟ament_cmake,如果使用Python,就跟ament_python;
  • package_name:新建功能包的名字。

在视觉检测过程中,需要调用CV库因此,采用python版本编写功能包

ros2 pkg create —build-type ament_python camera_pkg_python # Python

在命令包新建python文件

##camera_test.py
import rclpy                            # ROS2 Python接口库
from rclpy.node import Node             # ROS2 节点类

import cv2 as cv                            # OpenCV图像处理库
import numpy as np                      # Python数值计算库

lower_red = np.array([0, 90, 128])     # 红色的HSV阈值下限
upper_red = np.array([180, 255, 255])  # 红色的HSV阈值上限
 V3.2.1
class Transbot_Camera(object):

    def __init__(self, video_id=8, width=640, height=480, debug=False):
        self.__debug = debug
        self.__video_id = video_id
        self.__state = False
        self.__width = width
        self.__height = height

        self.__video = cv.VideoCapture(self.__video_id)
        # success, _ = self.__video.read()
        success = self.__video.isOpened()
        if not success:
            self.__video_id = (self.__video_id + 1) % 2
            self.__video = cv.VideoCapture(self.__video_id)
            # success, _ = self.__video.read()
            success = self.__video.isOpened()
            if not success:
                if self.__debug:
                    print("---------Camera Init Error!------------")
                return
        self.__state = True

        self.__config_camera()

        if self.__debug:
            print("---------Video%d Init OK!------------" % self.__video_id)

    def __del__(self):
        if self.__debug:
            print("---------Del Camera!------------")
        self.__video.release()
        self.__state = False

    def __config_camera(self):
        cv_edition = cv.__version__
        if cv_edition[0]=='3':
            self.__video.set(cv.CAP_PROP_FOURCC, cv.VideoWriter_fourcc(*'XVID'))
        else:
            self.__video.set(cv.CAP_PROP_FOURCC, cv.VideoWriter.fourcc('M', 'J', 'P', 'G'))

        # self.__video.set(cv.CAP_PROP_BRIGHTNESS, 30)  # 设置亮度 -64 - 64  0.0
        # self.__video.set(cv.CAP_PROP_CONTRAST, 50)  # 设置对比度 -64 - 64  2.0
        # self.__video.set(cv.CAP_PROP_EXPOSURE, 156)  # 设置曝光值 1.0 - 5000  156.0
        self.__video.set(cv.CAP_PROP_FRAME_WIDTH, self.__width)  # 640
        self.__video.set(cv.CAP_PROP_FRAME_HEIGHT, self.__height)  # 480

    # 摄像头是否打开成功
    # Check whether the camera is enabled successfully
    def isOpened(self):
        return self.__video.isOpened()

    # 释放摄像头 Release the camera
    def clear(self):
        self.__video.release()

    # 重新连接摄像头 
    # Reconnect the camera
    def reconnect(self):
        self.__video = cv.VideoCapture(self.__video_id)
        success, _ = self.__video.read()
        if not success:
            self.__video_id = (self.__video_id + 1) % 2
            self.__video = cv.VideoCapture(self.__video_id)
            success, _ = self.__video.read()
            if not success:
                if self.__debug:
                    self.__state = False
                    print("---------Camera Reconnect Error!------------")
                return False
        if not self.__state:
            if self.__debug:
                print("---------Video%d Reconnect OK!------------" % self.__video_id)
            self.__state = True
            self.__config_camera()
        return True

    # 获取摄像头的一帧图片 
    # Gets a frame of the camera
    def get_frame(self):
        success, image = self.__video.read()
        if not success:
            return success, bytes({1})
        return success, image

    # 获取摄像头的jpg图片 
    # Gets the JPG image of the camera
    def get_frame_jpg(self, text="", color=(0, 255, 0)):
        success, image = self.__video.read()
        if not success:
            return success, bytes({1})
        if text != "":
            # 各参数依次是:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细
            # The parameters are: image, added text, top left coordinate, font, font size, color, font size  
            cv.putText(image, str(text), (10, 20), cv.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)
        success, jpeg = cv.imencode('.jpg', image)
        return success, jpeg.tobytes()

def object_detect(image):
    hsv_img = cv.cvtColor(image, cv.COLOR_BGR2HSV)       # 图像从BGR颜色模型转换为HSV模型
    mask_red = cv.inRange(hsv_img, lower_red, upper_red)  # 图像二值化

    contours, hierarchy = cv.findContours(mask_red, cv.RETR_LIST, cv.CHAIN_APPROX_NONE) # 图像中轮廓检测

    for cnt in contours:                                   # 去除一些轮廓面积太小的噪声
        if cnt.shape[0] < 150:
            continue

        (x, y, w, h) = cv.boundingRect(cnt)               # 得到红色所在轮廓的左上角xy像素坐标及轮廓范围的宽和高
        cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2) # 将红色的轮廓勾勒出来
        cv2.circle(image, (int(x+w/2), int(y+h/2)), 5, (0, 255, 0), -1)    # 将苹果的图像中心点画出来

    cv.imshow("object", image)                            # 使用OpenCV显示处理后的图像效果
    cv.waitKey(50)

def main(args=None):                                       # ROS2节点主入口main函数
    rclpy.init(args=args)                                  # ROS2 Python接口初始化
    node = Node("camera_test")                      # 创建ROS2节点对象并进行初始化
    camera = Transbot_Camera(debug=True)
    node.get_logger().info("ROS2节点示例:检测图片中的红色")
    while rclpy.ok():
        ret, image = camera.get_frame()          # 读取一帧图像

        if ret == True:
            object_detect(image)         # 红色检测

    node.destroy_node()                  # 销毁节点对象
    rclpy.shutdown()                     # 关闭ROS2 Python接口

打开功能包的setup.py文件,加入如下入口点的配置:

  entry_points={
        'console_scripts': [
        'camera_pkg_python    = camera_pkg_python.camera_test:main',
        ],

4.2 也可以使用rosdep工具自动安装:

$ sudo apt install -y python3-pip
$ sudo pip3 install rosdepc
$ sudo rosdepc init
$ rosdepc update$ cd ..
$ rosdepc install -i --from-path src --rosdistro humble -y

4.3 编译工作空间

依赖安装完成后,可以使用如下命令编译工作空间:

$ sudo apt install python3-colcon-ros
$ colcon build

其中各文件夹表示:

  • src,代码空间,未来编写的代码、脚本,都需要人为的放置到这里;
  • build,编译空间,保存编译过程中产生的中间文件;
  • install,安装空间,放置编译得到的可执行文件和脚本;
  • log,日志空间,编译和运行过程中,保存各种警告、错误、信息等日志。

这四个空间的文件夹,我们绝大部分操作都是在src中进行的,编译成功后,就会执行install里边的结果。

4.4 设置环境变量

编译成功后,为了让系统能够找到我们的功能包和可执行文件,还需要设置环境变量:

$ source install/local_setup.sh # 仅在当前终端生效
$ echo " source ~/dev_ws/install/local_setup.sh" >> ~/.bashrc # 所有终端均生效

4.5 结果