电赛学习笔记(3)——图像识别小车(jetson nano部分)

目录

零.前言

1.jetson
nano购买商家及技术支持

2.相关环境配置

3.做好系统备份

一.vscode远程ssh操作

<1>局域网连接

<2>电脑直连

<3>电脑直连+VNC远程桌面

二.板载摄像头教程:

三.运行例程

四.GPIO使用

GPIO库的API用法

1.导入库

2.引脚编号

3.警告

4.设置通道

5.输入

6.输出

set first channel to HIGH and rest to
LOW

7.清理

8.jetson模块信息和库版本

9.中断

wait_for_edge()函数

timeout is in milliseconds

event_detected()函数

set rising edge detection on the
channel

当检测到边缘时运行回调函数

define callback function

add rising edge detection

bouncetime set in
milliseconds

10.检查GPIO通道的功能

11. PWM

五.串口通信

<1>UART

1.python代码:JetsonHacksNano/UARTDemo: UART Demo Code
(github.com)

注意事项:如果使用USB转TTL模块电脑和nano进行通信注意一下几点

2.C++代码:Uart c++ class for Nvidia Jetson
Nano

六.在jetson
nano上使用OpenCV

七.jetson
nano开机自启动程序

八.实战程序

1.jetson
nano上进行霍夫圆检测并将圆心坐标通过uart传给stm32


零.前言

1.jetson nano购买商家及技术支持

***选购亚博,支持相关开源资料,资料网址:JETSON NANO B01
(yahboom.com)

***常见问题及解答:Jetson nano A01/B01
(yuque.com)

2.相关环境配置

<1>亚博已经在SD卡中将环境配好,插上即可直接使用

<2>***自行烧录镜像,配置环境

下载之前要格式化SD卡或者U盘,具体操作见“jetson—主板—2.Jetson Nano B01基础教程”,然后烧录镜像文件:

亚博资料里的 “jetson—主板—附录—镜像” 包含**已安装:**

CUDA10.2、CUDNNv8、tensorRT、opencv4.1.1、python2、python3、tensorflow2.3、jetpack4.6.1、yolov5、jetson- inference包(包括相关模型)、jetson-gpio库、安装pytorch1.8和torchvesion0.9、安装node v15.0.1、npm7.2.3,jupyter,已开启VNC服务,还有其他课程中所用到的模块。

以及Swap空间配置,Jtop安装,只需要TF/U盘扩容即可

减少自行配置的麻烦。

如果要从零开始自己配置,“jetson—主板—2.Jetson Nano B01基础教程” 和“4.系统基础设置教程”中包括了大部分操作,甚至包括空环境下载

3.做好系统备份

如果只是使用亚博官方提供的版本库,可以只用 “jetson—主板—附录—镜像”
中的镜像文件作为备份。如果自行配置了其他环境,建议备份系统,可在“jetson—主板—4.系统基础设置教程” 找到具体操作。

一.vscode远程ssh操作

<1>局域网连接

1.将jetson连接至路由器——电脑使用该路由wifi——打开VS Code——下载ssh-
remote插件——jetson上终端ifconfig查ip——vscode按格式输入:ssh
jetson@IP地址——输入密码登录(原始默认用户名为jetson,密码为yahboom)

2.crtl+o选择要打开的文件或侧栏资源管理器选择,文件可在主机写好直接拖入jetson;终端写入代码并操作

<2>电脑直连

参考:Jetson
Nano——通过网线实现笔记本ssh远程连接

<3>电脑直连+VNC远程桌面

jetson nano上设置:[Jetson nano 通过 vnc
实现远程桌面控制(已在nano实现)](https://blog.csdn.net/manbushuizhong/article/details/122121251?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171299147716800184169928%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171299147716800184169928&biz_id=0&utm_medium=distribute.pc_search_result.none-
task-blog-2alltop_positive~default-1-122121251-null-
null.142%5Ev100%5Epc_search_result_base9&utm_term=jetson%20nano%20vnc&spm=1018.2226.3001.4187
“Jetson nano 通过 vnc 实现远程桌面控制(已在nano实现)”)

下载VNC Viewer软件并连接(端口号参考<2>),效果如下:

如果无法连接检查接口是否插好,如果成功会有一个不同于端口地址的地址:

如果vnc server显示窗口过小,打开终端输入以下命令调整分辨率:


​ xrandr –fb 1920x1080

二.板载摄像头教程:

获取摄像头相关参数:[linux查看摄像头分辨率参数](https://blog.csdn.net/weixin_44942126/article/details/115110762#:~:text=sudo%20apt%20install%20v4l-utils%2F%2F%E5%AE%89%E8%A3%85v4l2%E5%B7%A5%E5%85%B7%E5%8C%85%20sudo%20v4l2-ctl%20
–list-
devices%2F%2F%E9%80%9A%E8%BF%87v4l2%E6%9F%A5%E7%9C%8B%E6%91%84%E5%83%8F%E5%A4%B4%E8%AE%BE%E5%A4%87%20%2F%2F%E5%BD%93%E5%90%8C%E6%97%B6%E8%BF%9E%E6%8E%A5%E5%A4%9A%E4%B8%AA%E6%91%84%E5%83%8F%E5%A4%B4%E6%97%B6%EF%BC%8C%E6%8C%87%E5%AE%9A%E6%91%84%E5%83%8F%E5%A4%B4%E7%9A%84%E4%B8%A4%E7%A7%8D%E6%96%B9%E6%B3%95,sudo%20rmmod%20uvcvideo%2F%2F%E7%A7%BB%E9%99%A4%E6%A8%A1%E5%9D%97%20sudo%20modprobe%20uvcvideo%20nodrop%3D1%20timeout%3D5000
“linux查看摄像头分辨率参数”)


​ Driver Info (not using libv4l2):
​ Driver name : tegra-video
​ Card type : vi-output, imx219 8-0010
​ Bus info : platform:54080000.vi:4
​ Driver version: 4.9.255
​ Capabilities : 0x84200001
​ Video Capture
​ Streaming
​ Extended Pix Format
​ Device Capabilities
​ Device Caps : 0x04200001
​ Video Capture
​ Streaming
​ Extended Pix Format
​ Priority: 2
​ Video input : 0 (Camera 4: ok)
​ Format Video Capture:
​ Width/Height : 3264/2464
​ Pixel Format : ‘RG10’
​ Field : None
​ Bytes per Line : 6528
​ Size Image : 16084992
​ Colorspace : sRGB
​ Transfer Function : Default (maps to sRGB)
​ YCbCr/HSV Encoding: Default (maps to ITU-R 601)
​ Quantization : Default (maps to Full Range)
​ Flags :

​ Camera Controls

group_hold 0x009a2003 (bool) : default=0 value=0 flags=execute-on-write
sensor_mode 0x009a2008 (int64) : min=0 max=0 step=0 default=0 value=0 flags=slider
gain 0x009a2009 (int64) : min=0 max=0 step=0 default=0 value=16 flags=slider
exposure 0x009a200a (int64) : min=0 max=0 step=0 default=0 value=13 flags=slider
frame_rate 0x009a200b (int64) : min=0 max=0 step=0 default=0 value=2000000 flags=slider
bypass_mode 0x009a2064 (intmenu): min=0 max=1 default=0 value=0
override_enable 0x009a2065 (intmenu): min=0 max=1 default=0 value=0
height_align 0x009a2066 (int) : min=1 max=16 step=1 default=1 value=1
size_align 0x009a2067 (intmenu): min=0 max=2 default=0 value=0
write_isp_format 0x009a2068 (bool) : default=0 value=0
sensor_signal_properties 0x009a2069 (u32) : min=0 max=0 step=0 default=0 flags=read-only, has-payload
sensor_image_properties 0x009a206a (u32) : min=0 max=0 step=0 default=0 flags=read-only, has-payload
sensor_control_properties 0x009a206b (u32) : min=0 max=0 step=0 default=0 flags=read-only, has-payload
sensor_dv_timings 0x009a206c (u32) : min=0 max=0 step=0 default=0 flags=read-only, has-payload
low_latency_mode 0x009a206d (bool) : default=0 value=0
preferred_stride 0x009a206e (int) : min=0 max=65535 step=1 default=0 value=0
sensor_modes 0x009a2082 (int) : min=0 max=30 step=1 default=30 value=6 flags=read-only

注意jetson nano要连接显示屏

jetson@yahboom:/home$ nvgstcapture-1.0 ** **
//打开摄像头命令

响应:
Encoder null, cannot set bitrate!
Encoder Profile = High
Supported resolutions in case of ARGUS Camera //设置分辨率
(2) : 640x480
(3) : 1280x720
(4) : 1920x1080
(5) : 2104x1560
(6) : 2592x1944
(7) : 2616x1472
(8) : 3840x2160
(9) : 3896x2192
(10): 4208x3120
(11): 5632x3168
(12): 5632x4224

Runtime ARGUS Camera Commands:

Help : ‘h’
Quit : ‘q’

Set Capture Mode:
mo:
(1): image
(2): video
Get Capture Mode:
gmo
Set sensor orientation:
so:
(0): none
(1): Rotate counter-clockwise 90 degrees
(2): Rotate 180 degrees
(3): Rotate clockwise 90 degrees
Get sensor orientation:
gso
Set sensor mode:
smo: e.g., smo:1
Get sensor mode:
gsmo
Set Whitebalance Mode:
//更改相机参数(曝光、饱和度等等)
wb:
(0): off
(1): auto
(2): incandescent
(3): fluorescent
(4): warm-fluorescent
(5): daylight
(6): cloudy-daylight
(7): twilight
(8): shade
(9): manual
Get Whitebalance Mode:
gwb
Set Saturation (0 to 2):
st: e.g., st:1.25
Get Saturation:
gst
Set Exposure Compensation (-2 to 2):
ec: e.g., ec:-2
Get Exposure Compensation:
gec
Set Auto Whitebalance Lock:
awbl: e.g., awbl:0
Get Auto Whitebalance Lock:
awbl
Set Auto Exposure Lock:
ael: e.g., ael:0
Get Auto Exposure Lock:
gael
Set TNR Mode:
tnrm: e.g., tnrm:1
(0): OFF
(1): FAST
(2): HIGH QUALITY
Get TNR Mode:
gtnrm
Set TNR Strength (-1 to 1):
tnrs: e.g., tnrs:0.5
Get TNR Strength:
gtnrs
Set EE Mode:
eem: e.g., eem:1
(0): OFF
(1): FAST
(2): HIGH QUALITY
Get EE Mode:
geem
Set EE Strength (-1 to 1):
ees: e.g., ees:0.5
Get EE Strength:
gees
Set Auto Exposure Anti-Banding (0 to 3):
aeab: e.g., aeab:2
(0): OFF
(1): MODE AUTO
(2): MODE 50HZ
(3): MODE 60HZ
Get Auto Exposure Anti-Banding:
gaeab
Set Gain Range:
gr: e.g., gr:1 16
Get Gain Range:
ggr
Set Exposure Time Range:
etr: e.g., etr:34000 35000
Get Exposure Time Range:
getr
Set ISP Digital Gain Range:
dgr: e.g., dgr:2 152
Get ISP Digital Gain Range:
gdgr
Capture: enter ‘j’ OR
followed by a timer (e.g., jx5000, capture after 5 seconds) OR
followed by multishot count (e.g., j:6, capture 6 images)
timer/multihot values are optional, capture defaults to single shot with timer=0s
Start Recording : enter ‘1’
Stop Recording : enter ‘0’

Video snapshot : enter ‘2’ (While recording video)
Get Preview Resolution:
gpcr
Get Image Capture Resolution:
gicr
Get Video Capture Resolution:
gvcr

Runtime encoder configuration options:

Set Encoding Bit-rate(in bytes):
br: e.g., br:4000000
Get Encoding Bit-rate(in bytes):
gbr
Set Encoding Profile(only for H.264):
ep: e.g., ep:1
(0): Baseline
(1): Main
(2): High
Get Encoding Profile(only for H.264):
gep
Force IDR Frame on video Encoder(only for H.264):
Enter ‘f’

bitrate = 4000000
Encoder Profile = High
Encoder control-rate = 1
Encoder EnableTwopassCBR = 0
Opening in BLOCKING MODE

摘自亚博教程:命令

  1. --prev_res 预览视屏的分辨率,高度和宽度,用的是CSI摄像头的话范围是 2 to 12 (5632x4224)

e.g., nvgstcapture-1.0 –prev-res=3

  1. --cus-prev-res 自定义预览分辨率,宽度和高度,仅支持CSI摄像头

e.g., nvgstcapture-1.0 –cus-prev-res=1920x1080

多个命令同时使用的话用!隔开

想关掉摄像头的额话,直接在终端输入q再按回车

想捕获图片的话,在终端输入j再按回车,图片将保存当前目录下

关键参数的调整NVIDIA Jetson Nano 2GB 系列文章(9):调节 CSI
图像质量

三.运行例程

  • MIPI CSI cameras (csi://0)

  • V4L2 cameras (/dev/video0)

  • RTP/RTSP streams (rtsp://username:password@ip:port)

    在/home/jetson/jetson-inference/build/aarch64/bin 中打开终端

    输入示例(c++)(python 加.py即可)
    ./imagenet images/orange_0.jpg images/test/output_0.jpg
    ./imagenet csi://0 //图像分类推理
    ./detectnet csi://0 //目标检测推理
    ./segnet –network= csi://0 //语义分割
    ./posenet /dev/video0 //姿态估计
    ./posenet –network=resnet18-hand csi://0 //手部姿态
    ./actionnet csi://0 //动作识别
    ./backgroundnet csi://0 //背景去除
    ./depthnet csi://0 //单眼深度

四.GPIO使用

Python版使用说明NVIDIA/jetson-gpio: A Python library that enables the use of
Jetson’s GPIOs

C++版使用说明pjueon/JetsonGPIO: A C++ library that enables the use of
Jetson’s GPIOs

GPIO库的API用法

Jetson GPIO库提供了RPi.GPIO库提供的所有公共API。下面讨论每种API的用法:

1.导入库

要导入Jetson.GPIO模块,请使用:

import Jetson.GPIO as GPIO

通过这种方式,您可以在应用程序的其余部分中将该模块称为GPIO。模块也可以使用RPi的名称导入。GPIO代替了Jetson。GPIO用于使用RPi库的现有代码。

2.引脚编号

Jetson
GPIO库提供了四种给IO引脚编号的方法。前两个对应于RPi.GPIO库提供的模式,即BOARD和BCM,分别引用40引脚GPIO接头连接器的引脚号和Broadcom
SoC GPIO编号。其余两种模式CVM和TEGRA_SOC使用字符串代替数字,而数字分别对应于CVM CVB连接器和Tegra SoC上的信号名称。

要指定您正在使用哪种模式(强制性),请使用以下函数调用:

GPIO.setmode(GPIO.BOARD)# or

GPIO.setmode(GPIO.BCM)# or

GPIO.setmode(GPIO.CVM)# or

GPIO.setmode(GPIO.TEGRA_SOC)

要检查已设置的模式,可以调用:

mode = GPIO.getmode()

该模式必须为GPIO.BOARD,GPIO.BCM,GPIO.CVM,GPIO.TEGRA_SOC或无。

可参考:Jetson Nano
GPIO使用、四种模式以及串口解释

3.警告

您尝试使用的GPIO可能已在当前应用程序外部使用。在这种情况下,如果使用的GPIO配置为除默认方向(输入)以外的任何值,Jetson
GPIO库将向您发出警告。如果在设置模式和通道之前尝试清理,它也会警告您。要禁用警告,请使用:

GPIO.setwarnings(False)

4.设置通道

在用作输入或输出之前,必须先设置GPIO通道。要将通道配置为输入,请调用:

GPIO.setup(channel, GPIO.IN)

要将通道设置为输出,请调用:

GPIO.setup(channel, GPIO.OUT)

也可以为输出通道指定一个初始值:

GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)

将一个通道设置为输出时,也可以一次设置多个通道:

channels = [18, 12, 13]

GPIO.setup(channels, GPIO.OUT)

5.输入

要读取通道的值,请使用:

GPIO.input(channel)

这将返回GPIO.LOW或GPIO.HIGH。

6.输出

要设置配置为输出的引脚的值,请使用:

GPIO.output(channel, state)

状态可以是GPIO.LOW或GPIO.HIGH。

您还可以输出到频道列表或元组:

channels = [18, 12, 13] # or use tuples

GPIO.output(channels, GPIO.HIGH) # or GPIO.LOW

set first channel to HIGH and rest to LOW

GPIO.output(channel, (GPIO.LOW, GPIO.HIGH, GPIO.HIGH))

7.清理

在程序结束时,最好清理通道,以便将所有引脚设置为默认状态。要清理所有使用的通道,请使用:

GPIO.cleanup()

如果您不想清除所有通道,也可以清除单个通道或通道列表或元组:

GPIO.cleanup(chan1) # cleanup only chan1

GPIO.cleanup([chan1, chan2]) # cleanup only chan1 and chan2

GPIO.cleanup((chan1, chan2)) # does the same operation as previous statement

8.jetson模块信息和库版本

要获取有关Jetson模块的信息,请使用/阅读:

GPIO.JETSON_INFO

这为Python字典提供了以下键:P1_REVISION,RAM,REVISION,TYPE,MANUFACTURER和PROCESSOR。字典中的所有值都是字符串,但P1_REVISION是整数。

要获取有关库版本的信息,请使用/阅读:

GPIO.VERSION

这提供了XYZ版本格式的字符串。

9.中断

除了繁忙的轮询外,该库还提供了三种监视输入事件的方法:

wait_for_edge()函数

该函数阻塞调用线程,直到检测到提供的边缘为止。该函数可以如下调用:

GPIO.wait_for_edge(channel, GPIO.RISING)

第二个参数指定要检测的边缘,可以是GPIO.RISING,GPIO.FALLING或GPIO.BOTH。如果只想将等待时间限制为指定的时间,则可以选择设置超时:

timeout is in milliseconds

GPIO.wait_for_edge(channel, GPIO.RISING, timeout=500)

该函数返回检测到边缘的通道;如果发生超时,则返回无。

event_detected()函数

此功能可用于定期检查自上次通话以来是否发生了事件。该函数可以如下设置和调用:

set rising edge detection on the channel

GPIO.add_event_detect(channel, GPIO.RISING)

run_other_code()

if GPIO.event_detected(channel):

do_something()

和以前一样,您可以检测GPIO.RISING,GPIO.FALLING或GPIO.BOTH的事件。

当检测到边缘时运行回调函数

此功能可用于为回调函数运行第二个线程。因此,响应边缘,回调函数可以与主程序并发运行。可以按以下方式使用此功能:

define callback function

def callback_fn(channel):

print(“Callback called from channel %s” % channel)

add rising edge detection

GPIO.add_event_detect(channel, GPIO.RISING, callback=callback_fn)

如果需要,还可以添加多个回调:

def callback_one(channel):

print(“First Callback”)

def callback_two(channel):

print(“Second Callback”)

GPIO.add_event_detect(channel, GPIO.RISING)

GPIO.add_event_callback(channel, callback_one)

GPIO.add_event_callback(channel, callback_two)

在这种情况下,这两个回调是顺序运行的,而不是同时运行,因为只有线程运行所有回调函数。

为了通过将多个事件折叠为一个事件来防止多次调用回调函数,可以选择设置反跳时间:

bouncetime set in milliseconds

GPIO.add_event_detect(channel, GPIO.RISING,
callback=callback_fn,bouncetime=200)

如果不再需要边缘检测,可以按以下步骤将其删除:

GPIO.remove_event_detect(channel)

10.检查GPIO通道的功能

此功能使您可以检查提供的GPIO通道的功能:

GPIO.gpio_function(channel)

该函数返回GPIO.IN或GPIO.OUT。

11. PWM

请参阅samples/simple_pwm.py有关如何使用PWM通道的详细信息。

Jetson.GPIO库仅在附带硬件PWM控制器的引脚上支持PWM。与RPi.GPIO库不同,Jetson.GPIO库不实现软件仿真的PWM。Jetson
Nano支持2个PWM通道,而Jetson AGX Xavier支持3个PWM通道。Jetson TX1和TX2不支持任何PWM通道。

系统引脚复用器必须配置为将硬件PWM控制器连接到相关引脚。如果未配置pinmux,则PWM信号将不会到达引脚!Jetson.GPIO库不会动态修改pinmux配置来实现此目的。阅读L4T文档以获取有关如何配置pinmux的详细信息

完整英文版说明见: https://github.com/NVIDIA/jetson-gpio

五.串口通信

<1>UART
1.python代码 :[JetsonHacksNano/UARTDemo: UART Demo Code

(github.com)](https://github.com/JetsonHacksNano/UARTDemo
“JetsonHacksNano/UARTDemo: UART Demo Code (github.com)“)

jetson nano的串口的TXD和RXD引脚分别对应物理引脚8,10

开启串口权限,注意这个权限关机后就也被关闭,下次需要重新开启


​ sudo chmod 777 /dev/ttyTHS1

如果要永久打开,可参考:永久修改jetson
nano上电启动串口权限

注意事项:如果使用USB转TTL模块电脑和nano进行通信注意一下几点
    1.杜邦线不可太长,太长会乱码

    2.出现只能收不能发的情况是电压不足导致的,把usb转ttl模块的5V口和nano的5V进行连接

    3.如果线合理,但出现乱码,波特率、奇偶校验、停止位检查是否一致

    4.其它情况:[串口收不到数据或者收到错误数据 串口乱码总结_串口没有数据](https://blog.csdn.net/lxj362343/article/details/89646731 "串口收不到数据或者收到错误数据 串口乱码总结_串口没有数据")
2.C++代码:[Uart c++ class for Nvidia Jetson

Nano](https://github.com/MathieuBahin/JetsonNanoUart/tree/master “Uart c++
class for Nvidia Jetson Nano”)

在四中的C++版GPIO库中并未包括uart相关内容,但是利用jetson nano设备中的


​ /dev/ttyTHS1

进行系统调用,可实现串口通信。

但github上的C++ uart代码比较复杂,且只适用于特点场景,笔者修改了部分内容如下:


​ //uart.h
​ #ifndef _UART_H
​ #define _UART_H


​ // Define Constants
​ const char *uart_target = “/dev/ttyTHS1”;
​ #define NSERIAL_CHAR 256
​ #define VMINX 1
​ #define BAUDRATE B115200


​ class Uart {
​ private:
​ /* data */
​ int fid;
​ public:
​ char serial_message[NSERIAL_CHAR];


​ Uart ();
​ void sendUart(char *msg);
​ void readUart();
​ void closeUart();

​ };
​ #endif



​ //
​ /* @auteur Mathieu Bahin /
​ /
@date_création mars 2020 /
​ /
@version 1.0 /
​ /
@email bahin.mathieu@gmail.com */
​ /
/

​ #include “uart.h”
​ #include <stdio.h>
​ #include <unistd.h> // Used for UART
​ #include <sys/fcntl.h> // Used for UART
​ #include <termios.h> // Used for UART
​ #include <string.h>

using namespace std;

Uart :: Uart (){
  int ii, jj, kk;

  // SETUP SERIAL WORLD

  struct termios  port_options;   // Create the structure

  tcgetattr(fid, &port_options);	// Get the current attributes of the Serial port


​ //————————————————
​ // OPEN THE UART
​ //————————————————
​ // The flags (defined in fcntl.h):
​ // Access modes (use 1 of these):
​ // O_RDONLY - Open for reading only.
​ // O_RDWR - Open for reading and writing.
​ // O_WRONLY - Open for writing only.
​ // O_NDELAY / O_NONBLOCK (same function)
​ // - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
​ // if there is no input immediately available (instead of blocking). Likewise, write requests can also return
​ // immediately with a failure status if the output can’t be written immediately.
​ // Caution: VMIN and VTIME flags are ignored if O_NONBLOCK flag is set.
​ // O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.fid = open(“/dev/ttyTHS1”, O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode

​ fid = open(uart_target, O_RDWR | O_NOCTTY );

tcflush(fid, TCIFLUSH);
tcflush(fid, TCIOFLUSH);

  if (fid == -1)
  {
    printf("**Error - Unable to open UART**.  \n=>Ensure it is not in use by another application\n=>Ensure proper privilages are granted to accsess /dev/.. by run as a sudo\n");
  }

  //------------------------------------------------
  // CONFIGURE THE UART
  //------------------------------------------------
  // flags defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html
  //	Baud rate:
  //         - B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200,
  //           B230400, B460800, B500000, B576000, B921600, B1000000, B1152000,
  //           B1500000, B2000000, B2500000, B3000000, B3500000, B4000000
  //	CSIZE: - CS5, CS6, CS7, CS8
  //	CLOCAL - Ignore modem status lines
  //	CREAD  - Enable receiver
  //	IGNPAR = Ignore characters with parity errors
  //	ICRNL  - Map CR to NL on input (Use for ASCII comms where you want to auto correct end of line characters - don't use for bianry comms!)
  //	PARENB - Parity enable
  //	PARODD - Odd parity (else even)

//*******************************begin::uart参数设置************************************
  port_options.c_cflag &= ~PARENB;            // Disables the Parity Enable bit(PARENB),So No Parity
  port_options.c_cflag &= ~CSTOPB;            // CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit
  port_options.c_cflag &= ~CSIZE;	            // Clears the mask for setting the data size
  port_options.c_cflag |=  CS8;               // Set the data bits = 8
  port_options.c_cflag &= ~CRTSCTS;           // No Hardware flow Control
  port_options.c_cflag |=  CREAD | CLOCAL;                  // Enable receiver,Ignore Modem Control lines
  port_options.c_iflag &= ~(IXON | IXOFF | IXANY);          // Disable XON/XOFF flow control both input & output
  port_options.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // Non Cannonical mode
  port_options.c_oflag &= ~OPOST;                           // No Output Processing

  port_options.c_lflag = 0;               //  enable raw input instead of canonical,

  port_options.c_cc[VMIN]  = VMINX;       // Read at least 1 character
  port_options.c_cc[VTIME] = 0;           // Wait indefinetly

  cfsetispeed(&port_options,BAUDRATE);    // Set Read  Speed
  cfsetospeed(&port_options,BAUDRATE);    // Set Write Speed

//********************************end::uart参数设置*************************************

  // Set the attributes to the termios structure
  int att = tcsetattr(fid, TCSANOW, &port_options);

  if (att != 0 )
  {
    printf("\nERROR in Setting port attributes");
  }
  else
  {
    printf("\nSERIAL Port Good to Go.\n");
  }

  // Flush Buffers
  tcflush(fid, TCIFLUSH);
  tcflush(fid, TCIOFLUSH);
}

void Uart :: sendUart(char *msg){
  //--------------------------------------------------------------
  // TRANSMITTING BYTES
  //--------------------------------------------------------------
  char tx_buffer[256]={0};

  for (int i = 0; msg[i]!='\0'; i++) {
    tx_buffer[i] = msg[i];
  }
  printf("%s\n",tx_buffer);

  if (fid != -1)
  {
    int count = write(fid, &tx_buffer[0], strlen((const char*)tx_buffer));		//Filestream, bytes to write, number of bytes to write

    printf("Count = %d\n", count);

    if (count < 0)  printf("UART TX error\n");
  }

}

void Uart :: readUart(){

  //--------------------------------------------------------------
  // RECEIVING BYTES - AND BUILD MESSAGE RECEIVED
  //--------------------------------------------------------------
  unsigned char rx_buffer[VMINX];
  bool          pickup = true;
  int ii;
  int           rx_length;
  int           nread = 0;

  //更新缓存
  tcflush(fid, TCIOFLUSH);
  //清空接收区
  for (ii=0; ii<NSERIAL_CHAR; ii++)  serial_message[ii]='\0';

  while (pickup && fid != -1)
  {
    rx_length = read(fid, (void*)rx_buffer, VMINX);   // Filestream, buffer to store in, number of bytes to read (max)

    if (rx_length>=0)
    {
      if (nread<=NSERIAL_CHAR-1){
        serial_message[nread] = rx_buffer[0];   // Build message 1 character at a time
      }

      if (rx_buffer[0]=='$')   pickup=false;    // # symbol is terminator 自定协议规定以$结尾
    }

    nread++;
  }

}

void Uart :: closeUart(){
  //-------------------------------------------
  //  CLOSE THE SERIAL PORT
  //-------------------------------------------
  close(fid);
}

//*******************************begin::测试程序************************************
int main(int argc, char *argv[]) {
  Uart u;
  int i;
  char m[256]="hello world!";
  u.sendUart("%s","hello world!\r\n");
  u.sendUart(m);
   
  while (1)
  {
    u.readUart();
    if(strlen(u.serial_message)!=0)
    {
      for(i=0;u.serial_message[i]!='$';i++)    //自定协议规定以$结尾
      {
        printf("%c ",u.serial_message[i]);
      }
    }
    printf("\n");    
  }
  u.closeUart();

  return 0;
}
//********************************end::测试程序*************************************

其中删去了大部分不必要片段,并加入了Uart_Printf()函数,用法同printf()

测试结果:

<2>IIC

六.在jetson nano上使用OpenCV

<1>python打开摄像头:参考[通过OpenCV调用CSI和USB摄像头](https://blog.csdn.net/qq_56548850/article/details/123967404?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169070226116800188569163%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169070226116800188569163&biz_id=0&utm_medium=distribute.pc_search_result.none-
task-blog-2allsobaiduend~default-2-123967404-null-
null.142%5Ev91%5Einsert_down28v1,239%5Ev12%5Econtrol2&utm_term=jetsonnano%E8%B0%83%E7%94%A8usb%E6%91%84%E5%83%8F%E5%A4%B4&spm=1018.2226.3001.4187
“通过OpenCV调用CSI和USB摄像头”)

C++打开,示例如下:


​ //适用于jetson nano上的模版
​ #include
​ #include
​ #include <opencv2/opencv.hpp>
​ #include <opencv2/core.hpp>
​ #include <opencv2/highgui.hpp>
​ #include <opencv2/imgproc.hpp>
​ #include <opencv2/objdetect.hpp>
​ #include <opencv2/imgproc/types_c.h>
​ #include <opencv2/videoio.hpp>

​ using namespace std;
​ using namespace cv;

string gstreamer_pipeline(int capture_width, int capture_height, int display_width, int display_height, int framerate, int flip_method)
{
return “nvarguscamerasrc ! video/x-raw(memory:NVMM), width=(int)” + to_string(capture_width) + “, height=(int)” +
to_string(capture_height) + “, format=(string)NV12, framerate=(fraction)” + to_string(framerate) +
“/1 ! nvvidconv flip-method=” + to_string(flip_method) + “ ! video/x-raw, width=(int)” + to_string(display_width) + “, height=(int)” +
to_string(display_height) + “, format=(string)BGRx ! videoconvert ! video/x-raw, format=(string)BGR ! appsink”;
}

int main()
{
    int capture_width = 1280;
    int capture_height = 720;
    int display_width = 1280;
    int display_height = 720;
    int framerate = 60;
    int flip_method = 0;

    //创建管道
    string pipeline = gstreamer_pipeline(capture_width,
        capture_height,
        display_width,
        display_height,
        framerate,
        flip_method);
    std::cout << "使用gstreamer管道: \n\t" << pipeline << "\n";

    //管道与视频流绑定
    VideoCapture cap(pipeline, CAP_GSTREAMER);

    //创建显示窗口
    namedWindow("CSI Camera", WINDOW_AUTOSIZE);
    Mat img;

    //逐帧显示
    while (true)
    {
        cap.read(img);
        imshow("CSI Camera", img);

        if (waitKey(10) == 27)break;
    }

    cap.release();
    destroyAllWindows();
}

<2>使用官方自带的4.1.1版本OpenCV

自带的OpenCV文件夹在**/usr/share/opencv4** 中,**~/sample/cpp **内含众多示例代码

运行示例代码时使用g++编译,命令为:


​ g++ name.cpp -o name pkg-config --cflags --libs opencv4

命令行输入如下命令即可运行:


​ ./name parameters

其中name为编写的程序名,parameters程序具体需要的参数

因为官方镜像所有的环境都是配好的,g++编译时加入pkg-config --cflags --libs opencv4就可运行。想要下载全新版本自己配置并了解其中的原理可以参考下面的资料

<3>自己配置其他版本OpenCV参考资料

1.jetson nano上开发环境配置及使用QT(C++)或VsCode编写运行(Python)Jetson Nano
从入门到实战(案例:Opencv配置、人脸检测、二维码检测)

2.使用CMake编译链接(C++)

【C++】Cmake使用教程(看这一篇就够了)_c++ cmake_隐居的遮天恶鬼的博客-
CSDN博客

3.使用g++命令(C++)

4.配置最新版或其他版本OpenCV:linux下编译安装opencv生成opencv.pc_浓茶淡酒的博客-
CSDN博客

如何在Linux上安装OpenCV_linux 安装opencv_lvzt的博客-
CSDN博客

七.jetson nano开机自启动程序

步骤:

1.打开文件:


​ /usr/bin/yoyo.sh

2.写入命令(开机自动执行的命令),例如:


​ cd /home/jetson/test/circle_control
​ ./circle

注:终端路径转至/home/jetson/test/circle_control,执行./circle文件

3.终端执行:


​ sudo systemctl daemon-reload
​ sudo systemctl enable start.service
​ sudo reboot

八.实战程序

1.jetson nano上进行霍夫圆检测并将圆心坐标通过uart传给stm32


​ //circle.cpp*
​ #include “opencv2/opencv.hpp”
​ #include “opencv2/highgui.hpp”
​ #include
​ using namespace cv;

​ //begin::uart部分************************************************************************
​ #include “uart.h”
​ #include <stdio.h>
​ #include <stdarg.h>
​ #include <unistd.h> // Used for UART
​ #include <sys/fcntl.h> // Used for UART
​ #include <termios.h> // Used for UART
​ #include <string.h>

using namespace std;

Uart :: Uart (){
  int ii, jj, kk;
  struct termios  port_options;   // Create the structure

  tcgetattr(fid, &port_options);	// Get the current attributes of the Serial port
  fid = open(uart_target, O_RDWR | O_NOCTTY );
  tcflush(fid, TCIFLUSH);
  tcflush(fid, TCIOFLUSH);
  if (fid == -1)
  {
    printf("**Error - Unable to open UART**.  \n=>Ensure it is not in use by another application\n=>Ensure proper privilages are granted to accsess /dev/.. by run as a sudo\n");
  }
  port_options.c_cflag &= ~PARENB;            // Disables the Parity Enable bit(PARENB),So No Parity
  port_options.c_cflag &= ~CSTOPB;            // CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit
  port_options.c_cflag &= ~CSIZE;	            // Clears the mask for setting the data size
  port_options.c_cflag |=  CS8;               // Set the data bits = 8
  port_options.c_cflag &= ~CRTSCTS;           // No Hardware flow Control
  port_options.c_cflag |=  CREAD | CLOCAL;                  // Enable receiver,Ignore Modem Control lines
  port_options.c_iflag &= ~(IXON | IXOFF | IXANY);          // Disable XON/XOFF flow control both input & output
  port_options.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // Non Cannonical mode
  port_options.c_oflag &= ~OPOST;                           // No Output Processing

  port_options.c_lflag = 0;               //  enable raw input instead of canonical,

  port_options.c_cc[VMIN]  = VMINX;       // Read at least 1 character
  port_options.c_cc[VTIME] = 0;           // Wait indefinetly

  cfsetispeed(&port_options,BAUDRATE);    // Set Read  Speed
  cfsetospeed(&port_options,BAUDRATE);    // Set Write Speed

  // Set the attributes to the termios structure
  int att = tcsetattr(fid, TCSANOW, &port_options);

  if (att != 0 )
  {
    printf("\nERROR in Setting port attributes");
  }
  else
  {
    printf("\nSERIAL Port Good to Go.\n");
  }

  // Flush Buffers
  tcflush(fid, TCIFLUSH);
  tcflush(fid, TCIOFLUSH);
}

void Uart :: sendUart(char *msg){
  //--------------------------------------------------------------
  // TRANSMITTING BYTES
  //--------------------------------------------------------------
  char tx_buffer[256]={0};

  for (int i = 0; msg[i]!='\0'; i++) {
    tx_buffer[i] = msg[i];
  }
  printf("%s\n",tx_buffer);

  if (fid != -1)
  {
    int count = write(fid, &tx_buffer[0], strlen((const char*)tx_buffer));		//Filestream, bytes to write, number of bytes to write

    printf("Count = %d\n", count);

    if (count < 0)  printf("UART TX error\n");
  }

}

void Uart :: readUart(){

  //--------------------------------------------------------------
  // RECEIVING BYTES - AND BUILD MESSAGE RECEIVED
  //--------------------------------------------------------------
  unsigned char rx_buffer[VMINX];
  bool          pickup = true;
  int ii;
  int           rx_length;
  int           nread = 0;

  //更新缓存
  tcflush(fid, TCIOFLUSH);
  //清空接收区
  for (ii=0; ii<NSERIAL_CHAR; ii++)  serial_message[ii]='\0';

  while (pickup && fid != -1)
  {
    rx_length = read(fid, (void*)rx_buffer, VMINX);   // Filestream, buffer to store in, number of bytes to read (max)

    if (rx_length>=0)
    {
      if (nread<=NSERIAL_CHAR-1){
        serial_message[nread] = rx_buffer[0];   // Build message 1 character at a time
      }

      if (rx_buffer[0]=='$')   pickup=false;    // # symbol is terminator 自定协议规定以$结尾
    }

    nread++;
  }

}

int Uart :: fputc(int ch, FILE *f)
{
	write(fid, &ch, 1);
	return ch;
}

void Uart :: Uart_Printf(char *format, ...)
{
	char String[256];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	sendUart(String);
}

void Uart :: closeUart(){
  //-------------------------------------------
  //  CLOSE THE SERIAL PORT
  //-------------------------------------------
  close(fid);
}
//end::uart部分**************************************************************************

const char* windowname="win";
int max_r=100;
int min_r=60;
int min_d=80;
int t_hold=25;
int param1=100;
int param2=20;
const int t_max=255;
const int r_max=1000;
const int d_max=100;
const int p1_max=200;
const int p2_max=200;


​ int main()
​ {
​ VideoCapture capture(0);
​ Mat image;
​ Mat matCanny;
​ Mat BinImg;
​ Mat matDst;
​ Uart u;

​ void on_Trackbar_1(int, void*);
​ void on_Trackbar_2(int, void*);
​ void on_Trackbar_3(int, void*);
​ void on_Trackbar_4(int, void*);
​ void on_Trackbar_5(int, void*);
​ void on_Trackbar_6(int, void*);
​ namedWindow(windowname,0);
​ setWindowProperty(windowname, WND_PROP_ASPECT_RATIO , WINDOW_FREERATIO);
​ resizeWindow(windowname, 400, 300);
​ moveWindow(windowname, 0, 0);
​ createTrackbar(“t_hold”,windowname, &t_hold, t_max, on_Trackbar_3);
​ createTrackbar(“max_r”,windowname, &max_r, r_max, on_Trackbar_1);
​ createTrackbar(“min_r”,windowname, &min_r, r_max, on_Trackbar_2);
​ createTrackbar(“min_d”,windowname, &min_d, d_max, on_Trackbar_6);
​ createTrackbar(“p_1”,windowname, &param1, p1_max, on_Trackbar_4);
​ createTrackbar(“p_2”,windowname, &param2, p2_max, on_Trackbar_5);

while (capture.isOpened())
{
capture >> image;
if (image.empty())break;
cvtColor(image, matDst, COLOR_BGR2GRAY);
threshold(matDst, BinImg, t_hold, 255, THRESH_BINARY_INV|THRESH_OTSU);
Canny(BinImg, matCanny, 100, 300, 3, false); //canny算子

		std::vector<Vec3f> circles;
		HoughCircles(matCanny, circles, HOUGH_GRADIENT, 1, min_d, param1, param2, min_r, max_r);
		//在原图中画出圆心和圆  
		for (size_t i = 0; i < circles.size(); i++) {
			//提取出圆心坐标  
			Point center(round(circles[i][0]), round(circles[i][1]));
			//提取出圆半径  
			int radius = round(circles[i][2]);
			//圆心  
			circle(image, center, 3, Scalar(255,0,0), -1, 4, 0);
			//圆  
			circle(image, center, radius, Scalar(255,0,0), 3, 4, 0);
		}

    printf("x:%d\ny:%d\n",(int)round(circles[0][0]),(int)round(circles[0][1]));
    u.Uart_Printf("#x%dy%d$",(int)round(circles[0][0]),(int)round(circles[0][1]));

    imshow("matCanny", matCanny);
    imshow("BinImg", BinImg);
		imshow(windowname, image);
		if (waitKey(1) == 27)break;
	}

  u.closeUart();
  return 0;

}

void on_Trackbar_1(int, void*)
{
    ;
}

void on_Trackbar_2(int, void*)
{
    ;
}
void on_Trackbar_3(int, void*)
{
    ;
}
void on_Trackbar_4(int, void*)
{
    ;
}
void on_Trackbar_5(int, void*)
{
    ;
}
void on_Trackbar_6(int, void*)
{
    ;
}

使用说明:

使用前打开串口权限;程序使用usb摄像头;要将uart.h包含到该文件(circle.cpp)同目录下;

该目录下打开终端输入:
进行编译产生可执行文件


​ g++ circle.cpp -o circle pkg-config --cflags --libs opencv4

该目录下打开终端输入:
即可运行


​ ./circle

注:霍夫圆检测不太稳定,会显示core出错,重启即可,也可以在启动时调节各个参数使稳定

本文转自 https://blog.csdn.net/qq_32971095/article/details/131511658,如有侵权,请联系删除。

> --------------- THE END -------------- <