基于Jetson Nano与STM32通信的颜色识别与伺服驱动器控制
基于Jetson Nano与STM32通信的颜色识别与伺服驱动器控制
- jetrson nano部分
- 颜色识别
- 串口通信
- 数据传输
- 完整代码
- stm32 部分
- 数据解读
- 电机控制
- 主函数
- 电机加减速
- 硬件图:
- 总结
本文主要是使用Jetson Nano通过颜色识别识别物体后,将目标中心点坐标与摄像头中心点坐标的误差传送到stm32开发板。由stm32判断数据进行一系列操作,并使用定时器产生PWM驱动伺服驱动器使摄像头中心正对目标中心。
jetrson nano部分
jetson nano主要负责图像的识别和误差坐标(x轴和y轴)的传输,我这里为了实验方便使用的是opencv的HSV色域颜色识别,也可以使用神经网络。
其中需要的库包括
import cv2
import numpy as np
import serial
import struct,time
import sys
有些可能没用到
颜色识别
cam= cv2.VideoCapture(0)
#因为选定的颜色是红色,正好处于0与180连接处,所以需要2个HSV色域范围融合
l_b=np.array([0,130,105])
u_b=np.array([4,255,217])l_b2=np.array([166,130,105])
u_b2=np.array([179,255,217])ret, frame = cam.read()frame = cv2.resize(frame, (width, height)) #resizeframe_=cv2.GaussianBlur(frame,(5,5),0) #高斯滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。 hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV) #转换色域FGmask=cv2.inRange(hsv,l_b,u_b)FGmask2=cv2.inRange(hsv,l_b2,u_b2)mask=cv2.add(FGmask,FGmask2)mask=cv2.erode(mask,None,iterations=2) #cv2.erode()腐蚀:将前景物体变小,理解成将图像断开裂缝变大(在图片上画上黑色印记,印记越来越大)扩大黑色mask=cv2.dilate(mask,None,iterations=2) #cv2.dilate()膨胀:将前景物体变大,理解成将图像断开裂缝变小(在图片上画上黑色印记,印记越来越小)缩小黑色mask=cv2.GaussianBlur(mask,(3,3),0) # contours=sorted(contours,key=lambda x:cv2.contourArea(x),reverse=True)cnts=cv2.findContours(mask.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2] #查找检测物体的轮廓,不能在源图像上直接修改#contours, hierarchy = cv2.findContours(image,mode,method)#image:输入图像#mode:轮廓的模式。cv2.RETR_EXTERNAL只检测外轮廓;cv2.RETR_LIST检测的轮廓不建立等级关系;cv2.RETR_CCOMP建立两个等级的轮廓,上一层为外边界,内层为内孔的边界。如果内孔内还有连通物体,则这个物体的边界也在顶层;cv2.RETR_TREE建立一个等级树结构的轮廓。#method:轮廓的近似方法。cv2.CHAIN_APPROX_NOME存储所有的轮廓点,相邻的两个点的像素位置差不超过1;cv2.CHAIN_APPROX_SIMPLE压缩水平方向、垂直方向、对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需要4个点来保存轮廓信息;cv2.CHAIN_APPROX_TC89_L1,cv2.CV_CHAIN_APPROX_TC89_KCOS#contours:返回的轮廓#hierarchy:每条轮廓对应的属性#[-2]的作用是只返回轮廓,不返回其他的if len(cnts)>0:cnt = max (cnts,key=cv2.contourArea) #按像素面积计算轮廓,进行排序,取最大的(color_x,color_y),color_radius=cv2.minEnclosingCircle(cnt) #寻找包裹轮廓的最小圆:1.轮廓上的点均在圆形空间内。2.没有面积更小的满足条件的圆。#返回值:圆心,圆半径 if color_radius > 10: #如果半径大于10个像素# 将检测到的颜色标记出来cv2.circle(frame,(int(color_x),int(color_y)),int(color_radius),(255,0,255),2) #在图像上画圆
串口通信
串口通信函数,主要参考:基于JETSON NANO的激光测距和色块识别综合代码(包括和STM32通信)连接的是stm32UART1
注意!!!使用前需要开启 ttyTHS1
打开串口权限(jetson 系列好像每次开机都需要这样做),在终端使用sudo chmod 777 ‘/dev/tthTHS1’
.如果想开机自启动打开权限请参考:linux systemctl命令添加开机启动脚本
class Comcontrol(serial.Serial):def __init__(self, port, baudrate, bytesize, stopbits, timeout, parity):super(Comcontrol, self).__init__()self.port = portself.baudrate = baudrateself.bytesize = bytesizeself.stopbits = stopbitsself.timeout = timeoutself.parity = parityself.com = serial.Serial(port = self.port,baudrate = self.baudrate,bytesize = self.bytesize,stopbits = self.stopbits,timeout = self.timeout,parity = self.parity)def mpu_com_connect():mpucom = Comcontrol(port = '/dev/ttyTHS1', baudrate = 115200,bytesize = 8,stopbits = 1,timeout = 0.8,parity = 'N')if(mpucom.com.is_open):print("mpu connection success\r\n")return mpucom
数据传输
x_bias = int(color_x - width/2)y_bias = int(color_y - height/2)# print(int(x_bias),int(y_bias))# print('Y')mpucom.com.write('#'.encode()+str(int(x_bias)).encode()+'e'.encode())mpucom.com.write('$'.encode()+str(int(y_bias)).encode()+'e'.encode())print('Y'.encode()+str(int(y_bias)).encode()+'e'.encode())print('X'.encode()+str(int(x_bias)).encode()+'e'.encode())# print(len('Y'))# print(len(str(int(x_bias))))# print(len(str(int(y_bias))))
添加报头报尾,方便判断x轴y轴,也方便判断数据位数。
uart通信每次发送到格式是 起始位 + 8位数据位 +1位停止位
这其中的8位数据就是我们要发送的,发送的规格是 # 123 e其实是发送了5次 。
串口只能发送为str()格式的数据,同时,如果有汉字可以使用‘汉字’.encode('utf-8')
完整代码
import cv2
import numpy as np
import serial
import struct,time
import sys
print(cv2.__version__)def nothing(x):passclass Comcontrol(serial.Serial):def __init__(self, port, baudrate, bytesize, stopbits, timeout, parity):super(Comcontrol, self).__init__()self.port = portself.baudrate = baudrateself.bytesize = bytesizeself.stopbits = stopbitsself.timeout = timeoutself.parity = parityself.com = serial.Serial(port = self.port,baudrate = self.baudrate,bytesize = self.bytesize,stopbits = self.stopbits,timeout = self.timeout,parity = self.parity)def mpu_com_connect():mpucom = Comcontrol(port = '/dev/ttyTHS1', # 串口baudrate = 115200, #波特率 bytesize = 8, #数据位stopbits = 1, #停止位timeout = 0.8, #间隔parity = 'N') #校验位if(mpucom.com.is_open):print("mpu connection success\r\n")return mpucomcam= cv2.VideoCapture(0) #使用的是USB摄像头,如果使用SCI摄像头,请使用以下接口
#camSet='nvarguscamerasrc ! video/x-raw(memory:NVMM), width=3264, height=2464, format=NV12, framerate=21/1 ! nvvidconv flip-method='+str(flip)+' ! video/x-raw, width='+str(dispW)+', height='+str(dispH)+', format=BGRx ! videoconvert ! video/x-raw, format=BGR ! appsink'
#cam= cv2.VideoCapture(camSet)width = 400
height = 400l_b=np.array([0,130,105])
u_b=np.array([4,255,217])l_b2=np.array([166,130,105])
u_b2=np.array([179,255,217])mpucom = mpu_com_connect()while 1:ret, frame = cam.read()frame = cv2.resize(frame, (width, height)) #resizeframe_=cv2.GaussianBlur(frame,(5,5),0) #高斯滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。 hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV) #转换色域FGmask=cv2.inRange(hsv,l_b,u_b)FGmask2=cv2.inRange(hsv,l_b2,u_b2)mask=cv2.add(FGmask,FGmask2)mask=cv2.erode(mask,None,iterations=2) #cv2.erode()腐蚀:将前景物体变小,理解成将图像断开裂缝变大(在图片上画上黑色印记,印记越来越大)扩大黑色mask=cv2.dilate(mask,None,iterations=2) #cv2.dilate()膨胀:将前景物体变大,理解成将图像断开裂缝变小(在图片上画上黑色印记,印记越来越小)缩小黑色mask=cv2.GaussianBlur(mask,(3,3),0) # contours=sorted(contours,key=lambda x:cv2.contourArea(x),reverse=True)cnts=cv2.findContours(mask.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2] #查找检测物体的轮廓,不能在源图像上直接修改#contours, hierarchy = cv2.findContours(image,mode,method)#image:输入图像#mode:轮廓的模式。cv2.RETR_EXTERNAL只检测外轮廓;cv2.RETR_LIST检测的轮廓不建立等级关系;cv2.RETR_CCOMP建立两个等级的轮廓,上一层为外边界,内层为内孔的边界。如果内孔内还有连通物体,则这个物体的边界也在顶层;cv2.RETR_TREE建立一个等级树结构的轮廓。#method:轮廓的近似方法。cv2.CHAIN_APPROX_NOME存储所有的轮廓点,相邻的两个点的像素位置差不超过1;cv2.CHAIN_APPROX_SIMPLE压缩水平方向、垂直方向、对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需要4个点来保存轮廓信息;cv2.CHAIN_APPROX_TC89_L1,cv2.CV_CHAIN_APPROX_TC89_KCOS#contours:返回的轮廓#hierarchy:每条轮廓对应的属性#[-2]的作用是只返回轮廓,不返回其他的if len(cnts)>0:cnt = max (cnts,key=cv2.contourArea) #按像素面积计算轮廓,进行排序,取最大的(color_x,color_y),color_radius=cv2.minEnclosingCircle(cnt) #寻找包裹轮廓的最小圆:1.轮廓上的点均在圆形空间内。2.没有面积更小的满足条件的圆。#返回值:圆心,圆半径 if color_radius > 10: #如果半径大于10个像素# 将检测到的颜色标记出来cv2.circle(frame,(int(color_x),int(color_y)),int(color_radius),(255,0,255),2) #在图像上画圆x_bias = int(color_x - width/2)y_bias = int(color_y - height/2)# print(int(x_bias),int(y_bias))# print('Y')mpucom.com.write('#'.encode()+str(int(x_bias)).encode()+'e'.encode())mpucom.com.write('$'.encode()+str(int(y_bias)).encode()+'e'.encode())print('Y'.encode()+str(int(y_bias)).encode()+'e'.encode())print('X'.encode()+str(int(x_bias)).encode()+'e'.encode())# print(len('Y'))# print(len(str(int(x_bias))))# print(len(str(int(y_bias))))else: print('N')mpucom.com.write('N'.encode())if cv2.waitKey(1) == ord('q'):break
cam.release()
cv2.destroyAllWindows()
stm32 部分
stm32部分主要分为数据解读和伺服驱动器控制部分,使用到的常规led beep代码就不放了,可以自行设计。
stm32不仅要产生PWM波控制伺服驱动器,更要考虑实际情况进行软件限位,即精确获得产出的PWM数。
这里使用的伺服驱动器是台达的A2,使用差分信号驱动模式,实测stm32的3.3V电压可以驱动。
数据解读
stm32f10x_it.c
//中断函数变量
static u8 i=0; //i为数组接收计数
static u8 j=0; //j为取数据计数
char uctemp[8] = {0}; //uctemp为接收数组,因为uart接收只能一个字节一个字节的接收
char x_temp[8] = {0}; //x_temp为x轴偏移量存储数组
char y_temp[8] = {0}; //y_temp为y轴偏移量存储数组
extern volatile int x_bais; //x为x轴偏移量
extern volatile int y_bais; //y为y轴偏移量
extern volatile int target; //y为y轴偏移量
extern volatile int receive; //数据接收flagextern volatile int Rotation_angle;
extern volatile int Limit_angle;
首先判断是否存在目标,如果不存在则不启动电机,同时亮红灯表示;
检测到目标则亮绿灯,同时可以启动电机,并判断出x轴y轴偏移误差,将char转化为int。
atoi(‘124e’)= 124;最后的‘e’会被忽略掉。
void DEBUG_USART_IRQHandler(void) //每次中断都会调用中断函数
{u8 k=0; //k为循环计数if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET) //USART_IT_RXNE为接收中断标志位{ receive = 3; //是否接收到传输的数据uctemp[i] = USART_ReceiveData(DEBUG_USARTx); //一位一位的接收j=i;if((uctemp[0] != '#'&&uctemp[0] != '$')||uctemp[i]=='e') i=0; //判断数据报头报尾,当数据接收完毕的时i归0if(uctemp[0] == 'N') //判断是否检测到目标,用target和LED作为检测结果展示 {target=0;LED2_OFF;LED1_ON;x_bais = 0;y_bais = 0;}else if((uctemp[0] == '#') &&(uctemp[j] == 'e')) //判断X偏移量{target=1;LED1_OFF;LED2_ON; for(k=0;k<j;k++){x_temp[k] = uctemp[k+1];}x_bais = atoi(x_temp); //char转int}else if((uctemp[0] == '$') &&(uctemp[j] == 'e')) //判断y偏移量{target=1;LED1_OFF;LED2_ON;for(k=0;k<j;k++){y_temp[k] = uctemp[k+1];}y_bais = atoi(y_temp);}else{i++;} }
}
电机控制
驱动伺服电机需要使用pwm波,我采用通用定时器产生PWM波,同时为了精确限位,使用另一个从定时器统计产生的脉冲数量。
因为伺服驱动器是通过脉冲数量来驱动电机运行的,本文设置的3600脉冲转一转,则转一度需要10脉冲
timer.c
#include "stm32f10x.h"
#include "timer.h"/***************
主定时器配置函数
period:PWM周期
prescaler:预分频系数
pulse:占空比控制变量 也就是PWM有效电平的宽度
PWM输出IO为GPIOC_7
完全重映射至 TIM3_CH2
***************/void Master_TIM(u16 period,u16 prescaler,u16 pulse)
{ GPIO_InitTypeDef GPIO_InitStructure;// 输出比较通道2 GPIO 初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);/*--------------------时基结构体初始化-------------------------*/// 配置周期,这里配置为100KTIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断TIM_TimeBaseStructure.TIM_Period=period; // 驱动CNT计数器的时钟 = Fck_int/(psc+1)TIM_TimeBaseStructure.TIM_Prescaler= prescaler; // 时钟分频因子 ,配置死区时间时需要用到TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 计数器计数模式,设置为向上计数TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 重复计数器的值,没用到不用管TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 初始化定时器TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);/*--------------------输出比较结构体初始化-------------------*/TIM_OCInitTypeDef TIM_OCInitStructure;// 配置为PWM模式1TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;// 输出使能TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// 输出通道电平极性配置 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;// 输出比较通道 2TIM_OCInitStructure.TIM_Pulse = pulse;TIM_OC2Init(TIM3, &TIM_OCInitStructure);TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);// 使能计数器TIM_Cmd(TIM3, DISABLE);TIM_SelectMasterSlaveMode(TIM3,TIM_MasterSlaveMode_Enable); //TIM3选择使能主从模式主定时器TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Update); //TIM3选择更新事件作为trgo触发源,触发其他定时器
}/************
从定时器配置函数
period:TIM4的溢出值 即设定的PWM脉冲数
***********/void Slave_TIM(u16 period)
{NVIC_InitTypeDef NVIC_InitStructure; //中断初始化结构体TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //TIM4时基初始化结构体RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //使能TIM4时钟/********初始化TIM4时基设定所需PWM脉冲数********/TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //时钟不分频即直接使用 //TIM3脉冲TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period=period;TIM_TimeBaseInitStructure.TIM_Prescaler=0; //PWM不分频TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);TIM_SelectSlaveMode(TIM4,TIM_SlaveMode_External1); //TIM4选择从定时器模式TIM_SelectInputTrigger(TIM4,TIM_TS_ITR2); //TIM4选择内部触发来源为 //TIM3TIM_Cmd(TIM4,DISABLE); //同样初始化中不使能TIM4/***************配置TIM4的中断利用TIM4计数溢出作为中断事件关闭TIM3可达到精确计数的目的****************/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置中断优先级NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;NVIC_Init(&NVIC_InitStructure);TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //TIM4中断触发事件为update}/*************
方向控制IO配置函数
GPIOE_5
高电平顺时针
低电平逆时针
*************/void DIR_Crl(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOE,&GPIO_InitStructure);
}
主函数
主函数主要是控制电机,因为x轴y轴的控制方式基本一样,我这里只写了一个电机的控制,需要两个的可以复制粘贴,稍微修改就可以了。
#include "stm32f10x.h"
#include "bsp_usart.h"
#include <stdlib.h>
#include <stdio.h>
#include "bsp_led.h"
#include "systick.h"
#include "bsp_beep.h"
#include "timer.h"volatile int x_bais;
volatile int y_bais;
volatile int target;
volatile int receive = 3;volatile int Rotation_angle = 100; //一秒多少角度
volatile int Limit_angle = 90; //当前驱动器的角度,用来限制旋转角度
/*** @brief 主函数* @param 无* @retval 无//36号线为方向控制线,链接PE9,43号线为脉冲线,链接PA7(tim3 2通道),41和37接地*/int main(void)
{ int Pulse_num = Rotation_angle*10; //每秒多少脉冲(伺服驱动器设置3600脉冲一周,所以是10脉冲一度)int value_num = 100000/Pulse_num - 1;int Duty_cycle = 50; //设置占空比,脉冲信号只要被检测到就可以了,不用太长的占空比(低电平占空比)int cycle_num = (Duty_cycle*value_num)/100;/*初始化USART 配置模式为 115200 8-N-1,中断接收*/USART_Config();initSysTick();LED_GPIO_Config();BEEP_GPIO_Config(); //BEEP作为通讯中断警报Usart_SendString( DEBUG_USARTx,"串口通讯调试实验\n");Master_TIM(value_num,719,cycle_num); //720分频,主定时器设置每秒产生脉冲数和占空比DIR_Crl(); //电机方向接口初始化Motor_CW; //控制电机顺时针旋转Slave_TIM(20); //从定时器设定脉冲数为20,小数量便于验证delay_ms(10);TIM_Cmd(TIM4,ENABLE);while(1){ if(receive == 0){TIM_Cmd(TIM3,DISABLE);BEEP(1);delay_ms(1000);printf("长时间未接收到数据,系统异常!!!\n");}else{BEEP(0);receive--;
// delay_ms(1000);
// printf("target:%s ,x=%d,y=%d\r",(target==1?"YES":"NO"),x_bais,y_bais);if(target == 1){if(x_bais > 5 && Limit_angle <= 150){Motor_CW; TIM_Cmd(TIM3,ENABLE);}else if(x_bais < -5 && Limit_angle >= 50){Motor_CCW; TIM_Cmd(TIM3,ENABLE);}else TIM_Cmd(TIM3,DISABLE);}else TIM_Cmd(TIM3,DISABLE);}}
}
电机加减速
我目前的实验没有用到加减速,但是可以作为全局变量放进去自动控制。
暂时使用的是外部中断
//外部中断#include "bsp_exti.h" //配置中断优先级 static 静态声明,只能被声明过的文件调用
static void EXTI_NVIC_Config(void)
{NVIC_InitTypeDef NVIC_InitStructure1;NVIC_InitTypeDef NVIC_InitStructure2;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);NVIC_InitStructure1.NVIC_IRQChannel = EXTI0_IRQn; //对应中断函数NVIC_InitStructure1.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure1.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure1.NVIC_IRQChannelCmd = ENABLE;NVIC_Init( &NVIC_InitStructure1);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //配置中断优先级组号NVIC_InitStructure2.NVIC_IRQChannel = EXTI15_10_IRQn; //对应中断函数NVIC_InitStructure2.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure2.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure2.NVIC_IRQChannelCmd = ENABLE;NVIC_Init( &NVIC_InitStructure2);
}void EXTI_KEY_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;EXTI_InitTypeDef EXTI_InitStructure1;EXTI_InitTypeDef EXTI_InitStructure2;EXTI_NVIC_Config(); //配置中断优先级/*开启按键端口的时钟*/RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK|RCC_APB2Periph_AFIO,ENABLE);//选择按键的引脚GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN; // 设置按键的引脚为浮空输入GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //使用结构体初始化按键GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure); //初始化EXTIGPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);EXTI_InitStructure1.EXTI_Line = EXTI_Line0;EXTI_InitStructure1.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure1.EXTI_Trigger = EXTI_Trigger_Rising;EXTI_InitStructure1.EXTI_LineCmd = ENABLE;EXTI_Init( &EXTI_InitStructure1);//初始化EXT15_10GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource13);EXTI_InitStructure2.EXTI_Line = EXTI_Line13;EXTI_InitStructure2.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure2.EXTI_Trigger = EXTI_Trigger_Rising;EXTI_InitStructure2.EXTI_LineCmd = ENABLE;EXTI_Init( &EXTI_InitStructure2);}
//中断函数
void EXTI0_IRQHandler(void)
{if(EXTI_GetITStatus( EXTI_Line0)!= RESET){Rotation_angle+=10;printf("Rotation_angle = %d\r\n",Rotation_angle);}EXTI_ClearITPendingBit(EXTI_Line0);
}void EXTI15_10_IRQHandler(void)
{if(EXTI_GetITStatus( EXTI_Line13)!= RESET){Rotation_angle-=10;printf("Rotation_angle = %d\r\n",Rotation_angle);}EXTI_ClearITPendingBit(EXTI_Line13);
}
硬件图:
STM32 :
伺服驱动器 41号,37号接stm32的GND
43号连接PA7(TIM3的channel2 PWM输出通道)
36号连接PE9(转动方向控制)
Stm32的PA9链接串口RX(用于发送到上位机观看数据)
切记,伺服驱动器的接地工作一定要做好,不然会出现即使不上电或者不给信号电机依旧会转的情况。
jetson nano:
硬件连线如图,8号(TX)接PA10(UART1_RX),10号(RX)可以用来接收stm32的信息,PA9(UART1_TX)
总结
以上就是整个程序了,除了一些小细节外其他的应该都比较清楚,欢迎有想法或者有疑问的同学提问,如果有错误也欢迎指正!
基于Jetson Nano与STM32通信的颜色识别与伺服驱动器控制相关推荐
- Jetson nano和STM32通信
Jetson nano系列之串口使用 Jetson nano与STM32通信 Jetson nano系列之串口使用 前言 一.Jetson nano串口配置 二.通信过程 1.使用数据包的形式发送数据 ...
- 【AI达人创造营第二期】基于Jetson nano的餐厅自助结账系统部署
基于Jetson nano的餐厅自助结账系统部署 一. 前言 二. 开发环境 2.1 硬件 2.2 软件 三. Jetson nano基础环境配置 3.1 镜像烧录 3.2 网络连接 3.3 更换镜像 ...
- 基于Jetson nano的人脸识别系统
基于Jetson nano的人脸识别系统 一.Jetson nano简述 二.人脸检测 A.人脸检测常用算法 B.人脸对齐 C.活体检测 三.人脸跟踪 A.常用跟踪算法 四.TCP/UDP通信协议 A ...
- 基于jetson nano和yolov5 的 车行人检测(一)
毕业设计ing,但中途要出去一波.做个记录,备忘. 基于jetson nano和yolov5 的 车行人检测. 目前已经做的工作: 1.数据集的制作,原本是用的老师给的自己拍的一些数据(含夜间),但效 ...
- Jetson Nano入门(图像分类+图像分割+人脸识别)
Jetson Nano入门(图像分类+图像分割+人脸识别) 一.认识Jetson Nano 二.Jetson Nano准备工作 1.配件 2.系统烧写 三.Jetson平台软件资源测试功能 1. je ...
- 自己制作智能语音机器人(基于jetson nano)
1 简介 如上图,主要采用jetson上编写python代码实现,支持离线语音唤醒.在线语音识别.大模型智能文档.在线语音合成. 所需硬件如下: jetson nano:linux 科大讯飞麦克风硬件 ...
- 基于Jetson Nano的羽毛球自拾取系统设计(本科毕业设计)
目录 前言 一.移动平台 1.硬件简介 2.软件配置Rt Thread Nano 二.视觉部分 1.YOLOV4_tiny+OPENCV4.4(C++) 2.跟踪的思路 三.拾取部分 四.其它 前言 ...
- 基于jetson nano和yolov5 的车行人检测(二)
接着上篇~~ 3:准备使用tensorrt加速模型,onnx-tensortrt 将yolov5生成 pt文件先转成onnx文件,这个直接使用 yolov5源码里的 model/export.py转就 ...
- 71、基于STM32单片机的颜色识别感应传感器检测系统设计
毕设帮助.开题指导.技术解答(有偿)见文末. 目录 摘要 一.硬件方案 二.设计功能 三.实物图 四.原理图 五.PCB图 六.硬件框图 七.程序源码 八.资料包括 摘要 随着现代工业生产向高速化.自 ...
最新文章
- 目标感太弱怎么办?如何做目标管理?
- 在Eclipse中集成Ant编程之配置篇
- vs linux 交叉编译,VS结合VisualGDB搭建OpenWrt交叉编译远程调试开发环境
- U深度利用iso文件制作U盘启动盘
- 基于AIO的CS聊天室
- redhat 添加ssh端口_RHEL 7修改ssh默认端口号
- 贪心——区间选点问题(用最少数量的箭引爆气球 Leetcode 452)
- 第八章 (二)贪心法
- java keytool 生成p12证书
- Stroke:利用人类遗传学理解缺血性卒中预后的机制
- 内核驱动 (二)Linux按键驱动分析
- 3dmax学习记录(二)
- 直播继续搅局双11?
- IE浏览器图标不见了
- 使用oc的block方法回调
- plc远程服务器,PLC远程控制
- 领英精灵和领英助理哪个好,为什么领英精灵会成为领英最好的辅助工具。置顶收藏
- ZCash的零知识证明
- JMeter中级篇-9-网站性能测试用例2设计
- 【转】美国公司管理层的洗脑技巧
热门文章
- 用Spark GraphX进行图计算(详细步骤)
- 分页计算总页数算法:总页数=(总数-1)/每页数量+1----总页数=(总数+每页数量-1)/每页数量
- 1-7更新:高级进程管理器Process Lasso pro的免费注册码
- 十分钟计算机说课稿,我的十分钟说课稿
- macOS制作系统启动盘教程
- C#之CAD二次开发 (11) 文本对象
- 核密度函数详解,核密度函数图如何看?
- Proteus中MSP430与SHT11的IIC通信问题
- 【产品】可视化大屏设计思路
- 大数据可视化物流平台后台HTML5模板 免费下载