深度相机+人体跟随(直线)

人体跟随

  • 上位机使用的是工控机,下位机主控为stm32

  • 上位机使用通过Intel的Realsense D435i来读取相关相关信息,使用串口和下位机进行通信

  • 通过深度相机来对人体进行判别,根据相关的距离向下位机发出不同的信息,来控制小车是否跟进

  • 上位机主要分为两个部分,一个部分是用来检测,另外一个部分是用来和下位机进行通信

  • 上层的检测是利用OpenCV来调用D435i的图像信息并利用Intel官方提供的Caffe模型进行检测(百度网盘链接放在了文末)

  • 根据检测到的不同信息,来向下位机发送相关信息,控制小车的运行

  • 主要检测的源码如下:

    #include <opencv2/dnn.hpp>
    #include <librealsense2/rs.hpp>
    #include "../cv-helpers.hpp"
    #include "send.h"
    
    const size_t inWidth = 300;
    const size_t inHeight = 300;
    const float WHRatio = inWidth / (float)inHeight;
    const float inScaleFactor = 0.007843f;
    const float meanVal = 127.5;
    const char* classNames[] = { "background",
    							 "aeroplane", "bicycle", "bird", "boat",
    							 "bottle", "bus", "car", "cat", "chair",
    							 "cow", "diningtable", "dog", "horse",
    							 "motorbike", "person", "pottedplant",
    							 "sheep", "sofa", "train", "tvmonitor" };
    
    int main(int argc, char** argv) try
    {
    	using namespace cv;
    	using namespace cv::dnn;
    	using namespace rs2;
    	/**********/
    
    	WZSerialPort w;
    	/**********/
    	Net net = readNetFromCaffe("MobileNetSSD_deploy.prototxt",
    		"MobileNetSSD_deploy.caffemodel");
    
    	// Start streaming from Intel RealSense Camera
    	pipeline pipe;
    	auto config = pipe.start();
    	auto profile = config.get_stream(RS2_STREAM_COLOR)
    		.as<video_stream_profile>();
    	rs2::align align_to(RS2_STREAM_COLOR);
    
    	Size cropSize;
    	if (profile.width() / (float)profile.height() > WHRatio)
    	{
    		cropSize = Size(static_cast<int>(profile.height() * WHRatio),
    			profile.height());
    	}
    	else
    	{
    		cropSize = Size(profile.width(),
    			static_cast<int>(profile.width() / WHRatio));
    	}
    
    	Rect crop(Point((profile.width() - cropSize.width) / 2,
    		(profile.height() - cropSize.height) / 2),
    		cropSize);
    
    	const auto window_name = "Display Image";
    	namedWindow(window_name, WINDOW_AUTOSIZE);
    
    	while (getWindowProperty(window_name, WND_PROP_AUTOSIZE) >= 0)
    	{
    		// Wait for the next set of frames
    		auto data = pipe.wait_for_frames();
    		// Make sure the frames are spatially aligned
    		data = align_to.process(data);
    
    		auto color_frame = data.get_color_frame();
    		auto depth_frame = data.get_depth_frame();
    
    		// If we only received new depth frame, 
    		// but the color did not update, continue
    		static int last_frame_number = 0;
    		if (color_frame.get_frame_number() == last_frame_number) continue;
    		last_frame_number = static_cast<int>(color_frame.get_frame_number());
    
    		// Convert RealSense frame to OpenCV matrix:
    		auto color_mat = frame_to_mat(color_frame);
    		auto depth_mat = depth_frame_to_meters(depth_frame);
    
    		Mat inputBlob = blobFromImage(color_mat, inScaleFactor,
    			Size(inWidth, inHeight), meanVal, false); //Convert Mat to batch of images
    		net.setInput(inputBlob, "data"); //set the network input
    		Mat detection = net.forward("detection_out"); //compute output
    
    		Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());
    
    		// Crop both color and depth frames
    		color_mat = color_mat(crop);
    		depth_mat = depth_mat(crop);
    
    		float confidenceThreshold = 0.8f;
    		for (int i = 0; i < detectionMat.rows; i++)
    		{
    			float confidence = detectionMat.at<float>(i, 2);
    
    			if (confidence > confidenceThreshold)
    			{
    				size_t objectClass = (size_t)(detectionMat.at<float>(i, 1));
    
    				int xLeftBottom = static_cast<int>(detectionMat.at<float>(i, 3) * color_mat.cols);
    				int yLeftBottom = static_cast<int>(detectionMat.at<float>(i, 4) * color_mat.rows);
    				int xRightTop = static_cast<int>(detectionMat.at<float>(i, 5) * color_mat.cols);
    				int yRightTop = static_cast<int>(detectionMat.at<float>(i, 6) * color_mat.rows);
    
    				Rect object((int)xLeftBottom, (int)yLeftBottom,
    					(int)(xRightTop - xLeftBottom),
    					(int)(yRightTop - yLeftBottom));
    
    				object = object & Rect(0, 0, depth_mat.cols, depth_mat.rows);
    
    				// Calculate mean depth inside the detection region
    				// This is a very naive way to estimate objects depth
    				// but it is intended to demonstrate how one might 
    				// use depth data in general
    				Scalar m = mean(depth_mat(object));
    
    				std::ostringstream ss;
    				ss << classNames[objectClass] << " ";
    				ss << std::setprecision(2) << m[0] << " meters away";
    				String conf(ss.str());
    				/******************************/
    				if (m[0] <= 1.0 && objectClass == 15)
    				{
    					string i = "1";
    					if (w.open("COM3", 19200, 0, 8, 1))
    					{
    						cout << "人体距离摄像机小于1m,前进" << endl;
    						w.send(i);
    
    					}
    					else
    						cout << "打开失败" << endl;
    					w.close();
    				}
    				else
    				{
    					string i = "0";
    					if (w.open("COM3", 19200, 0, 8, 1))
    					{
    						w.send(i);
    					}
    					else
    						cout << "打开失败" << endl;
    					w.close();
    				}
    				/*******************************/
    				rectangle(color_mat, object, Scalar(0, 255, 0));
    				int baseLine = 0;
    				Size labelSize = getTextSize(ss.str(), FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
    
    				auto center = (object.br() + object.tl())*0.5;
    				center.x = center.x - labelSize.width / 2;
    
    				rectangle(color_mat, Rect(Point(center.x, center.y - labelSize.height),
    					Size(labelSize.width, labelSize.height + baseLine)),
    					Scalar(255, 255, 255), FILLED);
    				putText(color_mat, ss.str(), center,
    					FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0));
    			}
    		}
    
    		imshow(window_name, color_mat);
    		if (waitKey(1) >= 0) break;
    	}
    
    	return EXIT_SUCCESS;
    }
    catch (const rs2::error & e)
    {
    	std::cerr << "RealSense error calling " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n    " << e.what() << std::endl;
    	return EXIT_FAILURE;
    }
    catch (const std::exception& e)
    {
    	std::cerr << e.what() << std::endl;
    	return EXIT_FAILURE;
    }
    
  • 串口发送程序如下:

    #include "send.h"
    
    #include <stdio.h>
    #include <string.h>
    
    #include <WinSock2.h>
    #include <windows.h>
    #include<iostream>
    using namespace std;
    
    WZSerialPort::WZSerialPort()
    {
    
    }
    
    WZSerialPort::~WZSerialPort()
    {
    
    }
    
    bool WZSerialPort::open(const char* portname,
    	int baudrate,
    	char parity,
    	char databit,
    	char stopbit,
    	char synchronizeflag)
    {
    	this->synchronizeflag = synchronizeflag;
    	HANDLE hCom = NULL;
    	if (this->synchronizeflag)
    	{
    		//同步方式
    		hCom = CreateFileA(portname, //串口名
    			GENERIC_READ | GENERIC_WRITE, //支持读写
    			0, //独占方式,串口不支持共享
    			NULL,//安全属性指针,默认值为NULL
    			OPEN_EXISTING, //打开现有的串口文件
    			0, //0:同步方式,FILE_FLAG_OVERLAPPED:异步方式
    			NULL);//用于复制文件句柄,默认值为NULL,对串口而言该参数必须置为NULL
    	}
    	else
    	{
    		//异步方式
    		hCom = CreateFileA(portname, //串口名
    			GENERIC_READ | GENERIC_WRITE, //支持读写
    			0, //独占方式,串口不支持共享
    			NULL,//安全属性指针,默认值为NULL
    			OPEN_EXISTING, //打开现有的串口文件
    			FILE_FLAG_OVERLAPPED, //0:同步方式,FILE_FLAG_OVERLAPPED:异步方式
    			NULL);//用于复制文件句柄,默认值为NULL,对串口而言该参数必须置为NULL
    	}
    
    	if (hCom == (HANDLE)-1)
    	{
    		return false;
    	}
    
    	//配置缓冲区大小 
    	if (!SetupComm(hCom, 1024, 1024))
    	{
    		return false;
    	}
    
    	// 配置参数 
    	DCB p;
    	memset(&p, 0, sizeof(p));
    	p.DCBlength = sizeof(p);
    	p.BaudRate = baudrate; // 波特率
    	p.ByteSize = databit; // 数据位
    
    	switch (parity) //校验位
    	{
    	case 0:
    		p.Parity = NOPARITY; //无校验
    		break;
    	case 1:
    		p.Parity = ODDPARITY; //奇校验
    		break;
    	case 2:
    		p.Parity = EVENPARITY; //偶校验
    		break;
    	case 3:
    		p.Parity = MARKPARITY; //标记校验
    		break;
    	}
    
    	switch (stopbit) //停止位
    	{
    	case 1:
    		p.StopBits = ONESTOPBIT; //1位停止位
    		break;
    	case 2:
    		p.StopBits = TWOSTOPBITS; //2位停止位
    		break;
    	case 3:
    		p.StopBits = ONE5STOPBITS; //1.5位停止位
    		break;
    	}
    
    	if (!SetCommState(hCom, &p))
    	{
    		// 设置参数失败
    		return false;
    	}
    
    	//超时处理,单位:毫秒
    	//总超时=时间系数×读或写的字符数+时间常量
    	COMMTIMEOUTS TimeOuts;
    	TimeOuts.ReadIntervalTimeout = 1000; //读间隔超时
    	TimeOuts.ReadTotalTimeoutMultiplier = 500; //读时间系数
    	TimeOuts.ReadTotalTimeoutConstant = 5000; //读时间常量
    	TimeOuts.WriteTotalTimeoutMultiplier = 500; // 写时间系数
    	TimeOuts.WriteTotalTimeoutConstant = 2000; //写时间常量
    	SetCommTimeouts(hCom, &TimeOuts);
    
    	PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);//清空串口缓冲区
    
    	memcpy(pHandle, &hCom, sizeof(hCom));// 保存句柄
    
    	return true;
    }
    
    void WZSerialPort::close()
    {
    	HANDLE hCom = *(HANDLE*)pHandle;
    	CloseHandle(hCom);
    }
    
    int WZSerialPort::send(string dat)
    {
    	HANDLE hCom = *(HANDLE*)pHandle;
    
    	if (this->synchronizeflag)
    	{
    		// 同步方式
    		DWORD dwBytesWrite = dat.length(); //成功写入的数据字节数
    		BOOL bWriteStat = WriteFile(hCom, //串口句柄
    			(char*)dat.c_str(), //数据首地址
    			dwBytesWrite, //要发送的数据字节数
    			&dwBytesWrite, //DWORD*,用来接收返回成功发送的数据字节数
    			NULL); //NULL为同步发送,OVERLAPPED*为异步发送
    		if (!bWriteStat)
    		{
    			return 0;
    		}
    		return dwBytesWrite;
    	}
    	else
    	{
    		//异步方式
    		DWORD dwBytesWrite = dat.length(); //成功写入的数据字节数
    		DWORD dwErrorFlags; //错误标志
    		COMSTAT comStat; //通讯状态
    		OVERLAPPED m_osWrite; //异步输入输出结构体
    
    		//创建一个用于OVERLAPPED的事件处理,不会真正用到,但系统要求这么做
    		memset(&m_osWrite, 0, sizeof(m_osWrite));
    		m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, L"WriteEvent");
    
    		ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误,获得设备当前状态
    		BOOL bWriteStat = WriteFile(hCom, //串口句柄
    			(char*)dat.c_str(), //数据首地址
    			dwBytesWrite, //要发送的数据字节数
    			&dwBytesWrite, //DWORD*,用来接收返回成功发送的数据字节数
    			&m_osWrite); //NULL为同步发送,OVERLAPPED*为异步发送
    		if (!bWriteStat)
    		{
    			if (GetLastError() == ERROR_IO_PENDING) //如果串口正在写入
    			{
    				WaitForSingleObject(m_osWrite.hEvent, 1000); //等待写入事件1秒钟
    			}
    			else
    			{
    				ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误
    				CloseHandle(m_osWrite.hEvent); //关闭并释放hEvent内存
    				return 0;
    			}
    		}
    		return dwBytesWrite;
    	}
    }
    
    string WZSerialPort::receive()
    {
    	HANDLE hCom = *(HANDLE*)pHandle;
    	string rec_str = "";
    	char buf[1024];
    	if (this->synchronizeflag)
    	{
    		//同步方式
    		DWORD wCount = 1024; //成功读取的数据字节数
    		BOOL bReadStat = ReadFile(hCom, //串口句柄
    			buf, //数据首地址
    			wCount, //要读取的数据最大字节数
    			&wCount, //DWORD*,用来接收返回成功读取的数据字节数
    			NULL); //NULL为同步发送,OVERLAPPED*为异步发送
    		for (int i = 0; i < 1024; i++)
    		{
    			if (buf[i] != -52)
    				rec_str += buf[i];
    			else
    				break;
    		}
    		return rec_str;
    	}
    	else
    	{
    		//异步方式
    		DWORD wCount = 1024; //成功读取的数据字节数
    		DWORD dwErrorFlags; //错误标志
    		COMSTAT comStat; //通讯状态
    		OVERLAPPED m_osRead; //异步输入输出结构体
    
    		//创建一个用于OVERLAPPED的事件处理,不会真正用到,但系统要求这么做
    		memset(&m_osRead, 0, sizeof(m_osRead));
    		m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, L"ReadEvent");
    
    		ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误,获得设备当前状态
    		if (!comStat.cbInQue)return 0; //如果输入缓冲区字节数为0,则返回false
    		//std::cout << comStat.cbInQue << std::endl;
    		BOOL bReadStat = ReadFile(hCom, //串口句柄
    			buf, //数据首地址
    			wCount, //要读取的数据最大字节数
    			&wCount, //DWORD*,用来接收返回成功读取的数据字节数
    			&m_osRead); //NULL为同步发送,OVERLAPPED*为异步发送
    		if (!bReadStat)
    		{
    			if (GetLastError() == ERROR_IO_PENDING) //如果串口正在读取中
    			{
    				//GetOverlappedResult函数的最后一个参数设为TRUE
    				//函数会一直等待,直到读操作完成或由于错误而返回
    				GetOverlappedResult(hCom, &m_osRead, &wCount, TRUE);
    			}
    			else
    			{
    				ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误
    				CloseHandle(m_osRead.hEvent); //关闭并释放hEvent的内存
    				return 0;
    			}
    		}
    		for (int i = 0;i < 1024; i++)
    		{
    			if (buf[i] != -52)
    				rec_str += buf[i];
    			else
    				break;
    		}
    		return rec_str;
    	}
    }
    
  • 底层主要是由stm32来进行小车的控制,使用stm32的定时计数器1的的PWM进行互补的输出,控制电机的运转

  • 底层的相关程序如下:

    #include "Motor.h"
    
    /***********************************/
    //  TIM1->CCRx等于99,电机全速反转
    //  TIM1->CCRx等于0,电机全速正转
    //  TIM1->CCRx等于49,电机停止转动
    /***********************************/
    #define kp1 0.82
    #define ki1 0.45
    #define kd1 0.00
    #define kp2 0.80
    #define ki2 0.45
    #define kd2 0.00
    #define kp3 0.75
    #define ki3 0.55
    #define kd3 0.0005
    #define kp4 0.75
    #define ki4 0.45
    #define kd4 0.00
    float TargetSpeed1=0.1;
    float TargetSpeed2=0.1;
    float TargetSpeed3=0.1;
    float TargetSpeed4=0.1;
    float PID_CallBack1(float Speed,float *error)
    {
    	float Error = TargetSpeed1 - Speed;
    	static float Error_last1 = 0,Error_prev1 = 0;
    	float Speed_add=0;
    	*error = Error;
    	Speed_add = (float)((float)kp1*(Error - Error_last1) + (float)ki1*Error + (float)kd1*(Error-2.0f*Error_last1+Error_prev1));
    	Error_prev1 = Error_last1;// 保存上上次误差
      Error_last1 = Error;	    // 保存上次偏差
    	return Speed_add;
    }
    float PID_CallBack2(float Speed,float *error)
    {
    	float Error = TargetSpeed2 - Speed;
    	static float Error_last2 = 0,Error_prev2 = 0;
    	float Speed_add=0;
    	*error = Error;
    	Speed_add = (float)((float)kp2*(Error - Error_last2) + (float)ki2*Error + (float)kd2*(Error-2.0f*Error_last2+Error_prev2));
    	Error_prev2 = Error_last2;// 保存上上次误差
      Error_last2 = Error;	    // 保存上次偏差
    	return Speed_add;
    }
    float PID_CallBack3(float Speed,float *error)
    {
    	float Error = TargetSpeed3 - Speed;
    	static float Error_last3 = 0,Error_prev3 = 0;
    	float Speed_add=0;
    	*error = Error;
    	Speed_add = (float)((float)kp3*(Error - Error_last3) + (float)ki3*Error + (float)kd3*(Error-2.0f*Error_last3+Error_prev3));
    	Error_prev3 = Error_last3;// 保存上上次误差
      Error_last3 = Error;	    // 保存上次偏差
    	return Speed_add;
    }
    float PID_CallBack4(float Speed,float *error)
    {
    	float Error = TargetSpeed4 - Speed;
    	static float Error_last4 = 0,Error_prev4 = 0;
    	float Speed_add=0;
    	*error = Error;
    	Speed_add = (float)((float)kp4*(Error - Error_last4) + (float)ki4*Error + (float)kd4*(Error-2.0f*Error_last4+Error_prev4));
    	Error_prev4 = Error_last4;// 保存上上次误差
      Error_last4 = Error;	    // 保存上次偏差
    	return Speed_add;
    }
    void Set_Speed(float speed,uint8_t i)
    {
    	float PWM=0;
    	if(i==1)
    	{
    		if(speed>0)
    		{
    			PWM=48.0f-((float)(speed/0.2)*48.0f);
    				TIM1->CCR1=(uint16_t)PWM;
    		}
    		else
    		{
    			if(speed<0)
    			{
    				PWM=51.0f+((float)(speed/0.2)*48.0f);
    				TIM1->CCR1=(uint16_t)PWM;
    			}
    			else
    			TIM1->CCR1=49;
    		}
    	}
    	if(i==2)
    	{
    		if(speed>0)
    		{
    			PWM=48.0f-((float)(speed/0.2)*48.0f);
    				TIM1->CCR2=(uint16_t)PWM;
    		}
    		else
    		{
    			if(speed<0)
    			{
    				PWM=51.0f+((float)(speed/0.2)*48.0f);
    				TIM1->CCR2=(uint16_t)PWM;
    			}
    			else
    			TIM1->CCR2=49;
    		}
    	}
    	if(i==3)
    	{
    		if(speed>0)
    		{
    			PWM=48.0f-((float)(speed/0.2)*48.0f);
    				TIM1->CCR3=(uint16_t)PWM;
    		}
    		else
    		{
    			if(speed<0)
    			{
    				PWM=51.0f+((float)(speed/0.2)*48.0f);
    				TIM1->CCR3=(uint16_t)PWM;
    			}
    			else
    			TIM1->CCR3=49;
    		}
    	}
    	if(i==4)
    	{
    		if(speed>0)
    		{
    			PWM=48.0f-((float)(speed/0.2)*48.0f);
    				TIM8->CCR4=TIM1->CCR4=(uint16_t)PWM;
    			  
    		}
    		else
    		{
    			if(speed<0)
    			{
    				PWM=51.0f+((float)(speed/0.2)*48.0f);
    				TIM8->CCR4=TIM1->CCR4=(uint16_t)PWM;
    			}
    			else
    			TIM8->CCR4=TIM1->CCR4=49;
    		}
    	}
    }
    
  • 底层使用中断来接收上位机的信息

    void USART1_IRQHandler(void)
    {
      /* USER CODE BEGIN USART1_IRQn 0 */
    
      /* USER CODE END USART1_IRQn 0 */
      //HAL_UART_IRQHandler(&huart1);
      /* USER CODE BEGIN USART1_IRQn 1 */
      if(__HAL_UART_GET_FLAG( &huart1, UART_FLAG_RXNE ) != RESET)
    	{
    		Receive_data=( uint16_t)READ_REG(huart1.Instance->DR);
    	}
      /* USER CODE END USART1_IRQn 1 */
    }
    
  • 主函数根据接收到的相关信息,进行控制

    while(1)	
     {
       		
     	 if(Receive_data=='0')
     	 {
     			Set_Speed(0,1);
     			Set_Speed(0,2);
     			Set_Speed(0,3);
     	    Set_Speed(0,4);
     	 }
     	 else
     	 {
     		 
     		 Set_Speed(0.15,1);
     		 Set_Speed(0.15,2);
     		 Set_Speed(0.15,3);
     	   Set_Speed(0.15,4);
     	 }
       
     }
    

Caffe模型链接提取码:8888

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值