本节教程将会教会你JetsonNX的基础环境搭建,保证各位在之后的学习道路上不再会为环境问题而苦恼
实验平台:FanciSwarm机载电脑无人机 ubuntu-20.04LTS
20.04-rs-yolo.zip 提取码: g3w6
20.04-rs-yolo
├─ 70-ttyusb.rules
├─ auto_ap.sh
├─ datalink_serial.py
├─ librealsense.zip
├─ mavcrc.py
├─ mavcrc.pyc
├─ mavlink.py
├─ mavlink.pyc
├─ opencv-4.6.0.zip
├─ opencv_contrib-4.6.0.zip
├─ tensorrtx.zip
├─ torch-2.1.0a0+41361538.nv23.06-cp38-cp38-linux_aarch64.whl
├─ vision-release-0.16.zip
├─ yolov5.zip
└─ yolov5_det_trt_rs.py
说明:① 系统已预装 ROS1,无需重复安装,可直接跳过 ROS1 安装步骤。
② 如何确认系统已安装ROS1:
第一步:打开终端,输入命令:roscore
第二步:查看运行结果,如下图:
我们使用fishros一件环境配置工具进行ROS1-noetic版本的安装
wget http://fishros.com/install -O fishros && . fishros
我们首先选择5,系统源的更新
这一步我们选择2
这一步选择1,添加ros源
wget http://fishros.com/install -O fishros && . fishros
然后我们输入 1 一键安装 –> 不更换源安装 –> 选择你ubuntu版本对应的ros版本(这里我们选择ROS1的noetic) –> 桌面版进行安装
source ~/.bashrc
sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential
sudo rosdep init
rosdep update
至此rosdep已经安装完毕
超级终端便于同时运行多个程序,可以进行横向纵向分割
sudo apt-get install terminator -y
sudo gedit /etc/apt/sources.list.d/nvidia-l4t-apt-source.list
加下面两行进去
deb http://repo.download.nvidia.com/jetson/common r35.4 main
deb http://repo.download.nvidia.com/jetson/t234 r35.4 main
sudo apt upgrade
sudo apt update
sudo apt install nvidia-jetpack
gedit ~/.bashrc
下面添加:
export LD_LIBRARY_PATH=/usr/local/cuda/lib64
export PATH=/usr/local/cuda/bin:$PATH
export CUDA_HOME=/usr/local/cuda
source ~/.bashrc
cd /usr/include && sudo cp cudnn* /usr/local/cuda/include
cd /usr/lib/aarch64-linux-gnu && sudo cp libcudnn* /usr/local/cuda/lib64
nvcc -V (出现下述信息代表安装cuda成功)
sudo apt install python3-pip
sudo -H pip3 install -U pip
sudo -H pip install jetson-stats
重启之后执行如下指令运行
jtop
下载地址:Release OpenCV 4.6.0 · opencv/opencv
扩展模块:Release 4.6.0 · opencv/opencv_contrib
(如果网不好,这两个依赖库也可以用幻思提供的)
解压放在home路径即可
确定 Jetson Orin NX 的算力为 8.7
cd opencv-4.6.0/
mkdir build && cd build (如果用幻思的库,已经存在build文件夹,只需要cd build)
预编译opencv 4.6.0及其扩展模块 opencv_contrib-4.6.0,生成 Makefiles 文件,如果使用幻思的库,要先将build文件夹下的缓存文件全部删除,并且确保opencv和opencv_contrib都解压到home文件夹后再去执行下面指令
cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local/ \
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.6.0/modules \
-D WITH_CUDA=ON \
-D CUDA_ARCH_BIN=8.7 \
-D CUDA_ARCH_PTX="" \
-D ENABLE_FAST_MATH=ON \
-D CUDA_FAST_MATH=ON \
-D WITH_CUBLAS=ON \
-D WITH_LIBV4L=ON \
-D WITH_GSTREAMER=ON \
-D WITH_GSTREAMER_0_10=OFF \
-D WITH_QT=ON \
-D WITH_OPENGL=ON \
-D CUDA_NVCC_FLAGS="--expt-relaxed-constexpr" \
-D WITH_TBB=ON \
..
其中CMAKE_INSTALL_PREFIX=/usr/local/ 为安装地址,OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.6.0/modules 为扩展模块所在路径,
CUDA_ARCH_BIN=8.7 为 GPU 算力,
编译完成后如下所示
然后make install 编译安装 opencv 4.6.0 及其扩展模块 opencv_contrib-4.6.0,此安装过程较为漫长,请耐心等待。
sudo make install -j8
安装完成后用
jtop
查看版本
如果已经在VINS教程中安装了realsense驱动则跳过此步骤!
git clone https://github.com/IntelRealSense/librealsense.git
如果下载失败可以用幻思库里的
cd librealsense
sudo apt-get update
sudo apt-get install git cmake libssl-dev libusb-1.0-0-dev pkg-config libgtk-3-dev libglfw3-dev libgl1-mesa-dev libglu1-mesa-dev
mkdir build(如果用幻思提供的软件包,这个build已经存在,跳过此步)
cd build
cmake ../ (这一步如果网不好多试几次)
make -j4
sudo make install
cd ..
sudo cp config/99-realsense-libusb.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
rs-enumerate-devices #成功的话会列出设备信息
realsense-viewer #查看视频流
pip install pyrealsense2
sudo pip3 install torch-2.1.0a0+41361538.nv23.06-cp38-cp38-linux_aarch64.whl
sudo apt-get install libopenblas-dev
cd vision-release-0.16
export BUILD_VERSION=0.16
python setup.py install --user
cd ..
$ python3
>>> import torch
>>> import torchvision
>>> print(torch.__version__)
>>> print(torchvision.__version__)
以上为检查torch版本信息,如果有证明安装成功。
pip install tqdm
pip install Ipython
pip install seaborn
git clone -b v7.0 https://github.com/ultralytics/yolov5.git
git clone -b yolov5-v7.0 https://github.com/wang-xinyu/tensorrtx.git
如果以上两个工程用git下载,应把幻思提供的压缩包中的.py和.pyc文件复制到/tensorrtx/yolov5/文件夹中,如果直接用幻思压缩包中的工程则无需复制。
cd yolov5/
wget https://github.com/ultralytics/yolov5/releases/download/v7.0/yolov5s.pt
cp ~/tensorrtx/yolov5/gen_wts.py .
pip install numpy==1.20.3
python gen_wts.py -w yolov5s.pt -o yolov5s.wts
pip install numpy==1.23.0
此时会生成 'yolov5s.wts' 文件.
cd ~/tensorrtx/yolov5/
mkdir build
cd build
cp ~/yolov5/yolov5s.wts .
cmake .. # 如果使用幻思提供的tensorrtx包,最好先清空build文件夹再进行cmake ..
make
./yolov5_det -s yolov5s.wts yolov5s.engine s
pip install pycuda
pip install pyserial
cd ~/tensorrtx/yolov5
sudo chmod 777 /dev/ttyTHS0
将Jetson电源调到MAXN,运行下面
python yolov5_det_trt_rs.py
配置串口可执行权限:
用如下指令把权限配置文件复制到目录/etc/udev/rules.d/中
sudo cp 70-ttyusb.rules /etc/udev/rules.d/
重启电脑后生效
sh auto_ap.sh
(2)配置自启动文件 auto_ap.sh
注意文件中的工程路径是否与自己的一致,不一致的自己修改一下。
(3)终端输入sh auto_ap.sh
检测slam工程是否在一分钟内正常启动
(4)配置auto_ap.sh开机自动运行
打开Startup Application
点击add
先填个Name,再在Command处添加sh /home/nv/auto_ap.sh。
此时已完成开机自启动配置
(5)推荐把Jetson NX的功率设置为MAXN
重启系统验证一下吧!看看yolo界面有没有在开机1分钟内正常运行。
本节教程你将学会如何将JetsonNX上的CSI摄像头数据以TCP协议发送到PC端。
TcpNoDelay_transtream_NX.py
TcpNoDelay_recvstream_PC.py
确保JetsonNX环境已经按照“JetsonNX基础环境搭建”配置完毕
PC端环境配置:
python环境:3.10
安装opencv以及对应版本的numpy
pip install opencv-contrib-python==4.6.0.66
pip install numpy==1.24.0
JetsonNX端
python3 TcpNoDelay_transtream_NX.py
PC端
python TcpNoDelay_recvstream_PC.py
import socket
import cv2
import numpy as np
# 配置 {#配置 }
TCP_IP = '0.0.0.0'
TCP_PORT = 5005
# 实例化套接字通信对象 {#实例化套接字通信对象 }
def gstreamer_pipeline(
sensor_id=0,
capture_width=1920,
capture_height=1080,
display_width=960,
display_height=540,
framerate=30,
flip_method=0,
):
return (
"nvarguscamerasrc sensor-id=%d ! "
"video/x-raw(memory:NVMM), width=(int)%d, height=(int)%d, framerate=(fraction)%d/1 ! "
"nvvidconv flip-method=%d ! "
"video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! "
"videoconvert ! "
"video/x-raw, format=(string)BGR ! appsink"
% (
sensor_id,
capture_width,
capture_height,
framerate,
flip_method,
display_width,
display_height,
)
)
# 初始化摄像头 {#初始化摄像头 }
cap = cv2.VideoCapture(gstreamer_pipeline(flip_method=2), cv2.CAP_GSTREAMER)
if not cap.isOpened():
print("无法打开摄像头,尝试使用默认摄像头设备")
cap = cv2.VideoCapture(0)
# 创建服务器套接字 {#创建服务器套接字 }
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((TCP_IP, TCP_PORT))
sock.listen(1)
print(f"服务器已启动,监听 {TCP_IP}:{TCP_PORT}")
try:
while True:
print("等待客户端连接...")
conn, addr = sock.accept()
print(f"客户端已连接:{addr}")
conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
try:
while True:
ret, frame = cap.read()
if not ret:
print("无法读取摄像头画面")
break
# 编码为JPEG
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 70]
result, img_encoded = cv2.imencode('.jpg', frame, encode_param)
if not result:
continue
data = img_encoded.tobytes()
# 发送图像大小(4字节)
conn.sendall(len(data).to_bytes(4, 'big'))
# 发送图像数据
conn.sendall(data)
# 显示本地画面(可选)
cv2.imshow('Sending Image', frame)
if cv2.waitKey(1) == 27: # 按 ESC 退出
raise KeyboardInterrupt
except (BrokenPipeError, ConnectionResetError) as e:
print(f"[错误] 客户端断开连接: {e}")
finally:
conn.close()
print("客户端连接已关闭,等待新连接...")
except KeyboardInterrupt:
print("服务器正在关闭...")
finally:
cap.release()
cv2.destroyAllWindows()
sock.close()
print("所有资源已释放")
import cv2
import numpy as np
import subprocess
from multiprocessing import Array
def video_display(ffmpeg_command, frame_array, W_img, H_img):
process = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE)
while True:
raw_frame = process.stdout.read(W_img * H_img * 3) # RGB 3 channels
if not raw_frame:
break
# 将帧复制到共享内存中
frame = np.frombuffer(raw_frame, dtype=np.uint8).reshape(H_img, W_img, 3)
np.copyto(np.frombuffer(frame_array.get_obj(), dtype=np.uint8).reshape(H_img, W_img, 3), frame)
# 显示帧
cv2.imshow('Real-Time Video', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
process.terminate()
if __name__ == '__main__':
W_img = 640
H_img = 480
shared_frame = Array('B', W_img * H_img * 3) # RGB 3通道,大小为像素数 × 3
ffmpeg_command = [
'ffplay',
'-i', 'tcp://192.168.0.112:5005', # 这里的地址要使用JetsonNX的地址,需要自行替换。
'-vf', 'setpts=N/30',
'-fflags', 'nobuffer',
'-flags', 'low_delay',
'-framedrop'
]
video_display(ffmpeg_command, shared_frame, W_img, H_img)
把下面文件复制到tensorrtx/yolov5/文件夹中替换yolov5_det_trt.py文件
替换之后可以实现我们在运行第一章yolo识别程序的同时,将视频流传输到PC端
下面说明一下我们更改/增添的python代码
我们需要重新定义一个线程去监听端口等待客户端连接,因此需要引入多线程的库,将下面库放在yolov5_det_trt.py的导入库文件部分
import queue
import socket
import multiprocessing as mp
TCP_IP = '0.0.0.0'
TCP_PORT = 5005
然后将我们的线程类插入到yolov5_det_trt.py文件当中,可以加在程序主入口的上面部分
class TranstreamThread(threading.Thread):
def __init__(self, TCP_IP, TCP_PORT):
super().__init__()
self.TCP_IP = TCP_IP
self.TCP_PORT = TCP_PORT
self.sock = None
self.conn = None
self.addr = None
self.running = True
self.connected = False
self.queue = queue.Queue()
def run(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 允许端口重用
self.sock.bind((self.TCP_IP, self.TCP_PORT))
self.sock.listen(1)
print(f"服务器已启动,监听 {self.TCP_IP}:{self.TCP_PORT}")
while self.running:
print("等待客户端连接...")
try:
self.conn, self.addr = self.sock.accept() # 每次 accept 一个新的客户端
print(f"客户端已连接:{self.addr}")
self.connected = True
# 设置 TCP_NODELAY
self.conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
# 处理当前客户端通信
while self.running and self.connected:
try:
frame = self.queue.get(timeout=1) # 等待图像帧
self._send_frame(frame)
except queue.Empty:
continue
except socket.error as e:
print(f"accept() 出错:{e}")
self.connected = False
continue
def _send_frame(self, frame):
try:
size_data = len(frame).to_bytes(4, 'big')
self.conn.sendall(size_data)
self.conn.sendall(frame)
except (socket.error, BrokenPipeError) as e:
print(f"客户端断开连接:{e}")
self.connected = False # 客户端断开后标记为未连接
self._close_connection()
def send_frame(self, frame):
if self.connected:
self.queue.put(frame)
return True
return False
def _close_connection(self):
if self.conn:
try:
self.conn.close()
except:
pass
self.conn = None
def stop(self):
self.running = False
self.connected = False
self._close_connection()
if self.sock:
self.sock.close()
if __name__ == "__main__":
###
接着我们在程序入口创建线程类的实例并且运行
# create a new thread to do warm_up {#create-a-new-thread-to-do-warm_up }
thread1 = inferThread(yolov5_wrapper)
thread1.start()
thread1.join()
thread2 = TranstreamThread(TCP_IP, TCP_PORT) # 这句是我们要加入的
thread2.start() # 这句是我们要加入的
print('data link is starting...\n')
drone_thread=threading.Thread(target=dl.drone)
最后,由于多了一个线程计算,主线程中我们需要更快的处理速度,将cv2.imshow函数后面的waitkey(10)改成waitkey(1),再中间加入我们的发送图像帧的代码。
if cv2.getWindowProperty(window_title, cv2.WND_PROP_AUTOSIZE) >= 0:
cv2.imshow(window_title, img)
# 推理完图像后,编码为 JPEG 并发送
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 70]
result, img_encoded = cv2.imencode('.jpg', frame, encode_param)
data = img_encoded.tobytes()
thread2.send_frame(data) # 通过队列异步发送
else:
break
keyCode = cv2.waitKey(1) & 0xFF
# Stop the program on the ESC key or 'q' {#stop-the-program-on-the-esc-key-or-q }
if keyCode == 27 or keyCode == ord('q'):
break