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

目录

零.前言

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

2.相关环境配置

3.做好系统备份

一.vscode远程ssh操作(局域网连接)

二.板载摄像头教程:

三.运行例程

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

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

二.板载摄像头教程:

获取摄像头相关参数: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)

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.其它情况:串口收不到数据或者收到错误数据 串口乱码总结_串口没有数据

2.C++代码: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 [email protected]   */
/************************************/

#include "uart.h"
#include 
#include        // Used for UART
#include     // Used for UART
#include       // Used for UART
#include 

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=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()

测试结果:

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

<2>IIC

六.在jetson nano上使用OpenCV

<1>python打开摄像头:参考通过OpenCV调用CSI和USB摄像头

C++打开,示例如下:

//适用于jetson nano上的模版
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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 
#include 
#include        // Used for UART
#include     // Used for UART
#include       // Used for UART
#include 

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=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, ¶m1, p1_max, on_Trackbar_4);
  createTrackbar("p_2",windowname, ¶m2, 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 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出错,重启即可,也可以在启动时调节各个参数使稳定

你可能感兴趣的:(电赛,C/C++,学习笔记,学习,笔记)