前言

本文介绍一种Qt下进行ROS开发的完美方案,同时给出一个使用TCPROS进行图像传输的一个例子,使用的是ros-industrial的Levi-Armstrong在2015年12月开发的一个Qt插件ros_qtc_plugin,这个插件使得Qt“新建项目”和“新建文件”选项中出现ROS的相关选项,让我们可以直接在Qt下创建、编译、调试ROS项目,也可以直接在Qt项目中添加ROS的package、urdf、launch,感谢Levi-Armstrong。目前这个插件还在不断完善,有问题或者其他功能建议可以在ros_qtc_plugin的项目主页的讨论区提出。

本文是用的操作系统是ubuntu kylin 14.04中文版,ROS版本是indigo,Qt版本是Qt5.5.1(Qt Creator 4.0.3)

本文地址:http://blog.csdn.net/u013453604/article/details/52186375
视频教程:ros_qtc_plugin插件作者Levi-Armstrong录制的插件使用教程
参考:
ROS wiki IDEs
1. Setup ROS Qt Creator Plug in
2. Setup Qt Creator for ROS
3. Debugging Catkin Workspace
4. Where to find Qt Creator Plug in Support
github ros-industrial/ros_qtc_plugin项目主页
插件使用问题

第一部分、入门

一、开发环境准备

//安装开发插件ros_qtc_plugin

//安装方法见历史博客【传送门】

二、新建ROS功能包与节点及编译测试

//查看我的历史博客【 传送门】

三、demo练手

3.1图像发布节点

3.1.1具体代码及解析

#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>int main(int argc, char **argv)
{ros::init(argc, argv, "img_pub");ros::NodeHandle nh;image_transport::ImageTransport it(nh);image_transport::Publisher pub = it.advertise("camera/image",1);cv::Mat image = cv::imread(argv[1],CV_LOAD_IMAGE_COLOR);sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(),"bgr8",image).toImageMsg();ros::Rate loop_rate(5);while(nh.ok()){pub.publish(msg);ros::spinOnce();loop_rate.sleep();}ROS_INFO("Hello world!");
}

3.1.2修改cmakeList.txt文件

添加以下代码,然后回到工作空间,执行catkin_make

  • find_package
find_package(catkin REQUIRED COMPONENTScv_bridgeroscpprospystd_msgsimage_transport
)
#find_package(OpenCV)   #这里我注销了好像也没有报错
  • 添加头文件包含
include_directories(
# include${OpenCV_INCLUDE_DIRS}${catkin_INCLUDE_DIRS}
)
  • 生成可执行文件
add_executable(img_pub src/img_pub.cpp)
  • 连接到库文件
target_link_libraries(img_pub${catkin_LIBRARIES}#  ${OpenCV_LIBRARIES}  #这里我注销了好像也没有报错
)

3.1.3修改package.xml文件

package.xml文件中存放的是创建功能包时候节点所依赖的其他功能包,如果创建时候没有添加,则必须在这里手动添加。同时,在cmakeList.txt文件中添加以下内容。关于package.xml文件的具体讲解戳这里:https://www.cnblogs.com/qixianyu/p/6669687.html。

find_package(catkin REQUIRED COMPONENTScv_bridgeroscpprospystd_msgsimage_transport
)

//博主把这两句删了catkin_make也还是能够通过,先不加,如果报错再改

  <build_depend>opencv2</build_depend><build_depend>image_transport</build_depend><exec_depend>image_transport</exec_depend><exec_depend>opencv2</exec_depend>

3.1.4编译

点击qtcreator左下角的小锤子,开始编译,如果有错误就会在下方显示,双击跳转到出错位置

发现是需要在cmakeList.txt文件中添加

find_package(catkin REQUIRED COMPONENTSimage_transport
)

注意,在构建自己的功能包的时候如果依赖了其他功能包,必须在find_package中添加依赖的包名。添加这句话后重新编译。

3.2图像订阅节点

3.2.1具体代码及分析

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>void imageCallback(const sensor_msgs::ImageConstPtr& msg)
{try{cv::imshow("view", cv_bridge::toCvShare(msg, "bgr8")->image);}catch (cv_bridge::Exception& e){ROS_ERROR("Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str());}
}int main(int argc, char **argv)
{ros::init(argc, argv, "img_listener");ros::NodeHandle nh;cv::namedWindow("view");cv::startWindowThread();image_transport::ImageTransport it(nh);image_transport::Subscriber sub = it.subscribe("/test/image",1,imageCallback);ros::spin();cv::destroyWindow("view");return 0;
}

3.2.2修改cmakeList.txt和package.xml文件

这个文件中没有使用其他依赖功能包,所以package.xml文件暂时不需要做其他修改

cmakeList.txt文件中需要做一些修改:添加

  • 链接库文件
target_link_libraries(img_listener${catkin_LIBRARIES}# ${OpenCV_LIBRARIES}
)
  • 生成可执行文件
add_executable(img_listener src/img_listener.cpp

3.2.3代码编译

查阅资料发现,可以在~/.bashrc中添加如下代码,创建快捷方式cw,cs,cm。分别执行' command'中的命令

#ROS alais command
alias cw='cd ~/Travel_Assistance_Robot'
alias cs='cd ~/Travel_Assistance_Robot/src'
alias cm='cd ~/Travel_Assistance_Robot && catkin_make'

3.3执行

rosrun image_trans img_pub cal.png

然后可以查看话题,发现有我们自己发布的/test/image的话题

rosrun image_trans img_listener

显示图像

3.4 话题关系查看与节点关闭

3.4.1节点交互

为了查看话题之间的关系,我们可以使用rgt_graph指令查看

rosrun rqt_graph rqt_graph

3.4.2节点关闭

想要优雅的关掉节点,请使用rosnode kill命令。当然你使用ctrl+c或者直接关闭终端也可以关闭节点,但这种方法不免过于简单粗暴,而且并未完全的关闭节点,因为节点管理器那里仍然有记录

参考资料

[1]http://wiki.ros.org/image_transport/Tutorials

[2]http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29

[3]https://blog.csdn.net/github_30605157/article/details/50990493

[4]https://blog.csdn.net/xiaocainiaoshangxiao/article/details/13295625

*****************************************我是萌萌哒的分割线***************************************************

第二部分、实战

一、图像采集节点

//2.1、启动相机launch文件

//2.2、相机启动launch文件阅读,代码阅读,调试

//节点间功能的切换

roslaunch ueye_cam rgb8.launch

打开相机后,显示如下

二、图像显示节点

这个就是上面的话题监听节点,定时从指定话题获取图像

三、图像传输节点

//节点上的图像传输到服务器上

我们需要基于校园网来传输图像,两个不同的设备连接在校园网上。问题是校园网上的节点有很多,当机器人移动到不同的位置的时候,所处的节点变化,IP地址也发生变化,此时如何和服务器进行通信?校园网采用了动态IP地址分配方式DHCP。一旦连接上校园网,只要不断开IP地址不应该不变吗?

TCP/IP的话,客户端需要指定服务器的IP地址,服务器端需要知道客户端的IP地址。然后建立三次握手连接和关闭连接(四次握手)。

校园网的的IP地址一般都是和MAC地址绑定的,就好比路由器中的静态地址保留功能,但是也有例外,有的是动态的分配,每次登陆,都会随机的分配。这个要看学校的实际情况,如果学校是动态分配的,那改成静态的IP地址是没法上网的,也有可能造成IP地址冲突。

需要解决的问题:如何获取动态分配DHCP的地址?目前发现有这种方式:

  • 安装arp-scan工具[在github上下载最新版本,否则有些获取不到主机名],使用rp-scan工具扫出局域网所有的IP地址

arp协议是一个数据链路层协议,负责IP地址和Mac地址的转换。

sudo apt-get arp-scan

通过硬件地址筛选

 sudo arp-scan --interface=wlan0 --localnet | grep 54:35:30:19:68:8f

en0是网卡的设备名称,可以通过ifconfig命令获得,有多种网卡时注意不要写错

当我的IP为 inet addr:10.88.60.14时候,通过该方式只能够查到10.88网络上的主机列表,而看不到其他网络上的主机列表。比如看不到10.95网络上的主机列表。10.95.6.210。

  • 在学校申请固定IP地址【赵海武】,那电脑每次开机IP地址会发生改变吗?

3.1、客户端图像发送程序

3.1.1、TX2上图像传输节点,从指定话题上获取消息,传输图像

https://blog.csdn.net/hanshuning/article/details/50581725

使用装有ROS插件的qtcrreator来调试。这里需要先设置一下:

点击左边的带三角的run,在右边add attach to node,选择需要调试的节点;同时,add run step,运行需要运行的节点

然后就可以happy的调试了。

F5执行

F9设置断点

F10跳过,执行下一步指令

F11进入,具体实现调试

注意:这里有一个小坑,就是工程代码里面切记不要有中文,否则调试时候会进入汇编代码中,如下图所示:

设置断点后调试,直接进入了汇编代码界面

因为我的代码有中文路径:

改成全英文路径,重新调试即可:

3.1.2、读取本地图片,并发送

(client)

/*socket image transfer
* 2018.5.10
* wzw
* qt head**************************/
#include <QCoreApplication>
#include <QDebug>//socket headfile
#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <stdio.h>        // for qDebug
#include <stdlib.h>        // for exit
#include <string.h>        // for bzero
#include <time.h>                //for time_t and time
#include <arpa/inet.h>
#include <unistd.h>    //close(client_socket);//opencv headfile
#include <vector>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using  namespace cv;
using  namespace std;#define HELLO_WORLD_SERVER_PORT   7754
#define BUFFER_SIZE 1024int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);qDebug("test ok");//get server ip addrif (argc != 2){qDebug("Usage: %s ServerIPAddress\n",argv[0]);exit(1);}qDebug("ServerIPAddress %s\n",argv[1]);/*************** socket struct* ***********///设置一个socket地址结构client_addr,代表客户机internet地址, 端口struct sockaddr_in client_addr;bzero(&client_addr,sizeof(client_addr)); //把一段内存区的内容全部设置为0// memset(&client_addr,sizeof(client_addr));client_addr.sin_family = AF_INET;    //internet协议族client_addr.sin_addr.s_addr = htons(INADDR_ANY);//INADDR_ANY表示自动获取本机地址client_addr.sin_port = htons(0);    //0表示让系统自动分配一个空闲端口/****** socket descriptor* 创建用于internet的流协议(TCP)socket,用client_socket代表客户机socket* ****/int client_socket = socket(AF_INET,SOCK_STREAM,0);if( client_socket < 0){qDebug("Create Socket Failed!\n");exit(1);}/******* bind port* 把客户机的socket和客户机的socket地址结构联系起来,zhiding client_socket ip,point to addr* *******/if( bind(client_socket,(struct sockaddr*)&client_addr,sizeof(client_addr))){qDebug("Client Bind Port Failed!\n");exit(1);}/****** get server addr from argv[1],* set server params* 设置一个socket地址结构server_addr,代表服务器的internet地址, 端口* server_addr.sin_addr.s_addr=inet_addr(argv[1]);//same as up inet_aton* ****///struct sockaddr_in server_addr;bzero(&server_addr,sizeof(server_addr));server_addr.sin_family = AF_INET;if(inet_aton(argv[1],&server_addr.sin_addr) == 0){qDebug("Server IP Address Error!\n");exit(1);}server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);socklen_t server_addr_length = sizeof(server_addr);/**** connect request,to server ip.success return 0;fail is 1* 向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接* **/if(connect(client_socket,(struct sockaddr*)&server_addr, server_addr_length) < 0){qDebug("Can Not Connect To %s!\n",argv[1]);exit(1);}qDebug("success connect To %s!\n",argv[1]);/*********************  data transfer test  ****************************//****** data prepare,set buffer* bzero == memset* ****/char buffer[BUFFER_SIZE];bzero(buffer,BUFFER_SIZE);//从服务器接收数据到buffer中int length = recv(client_socket,buffer,BUFFER_SIZE,0);if(length < 0){qDebug("Recieve Data From Server %s Failed!\n", argv[1]);exit(1);}qDebug("\n%s\n",buffer);bzero(buffer,BUFFER_SIZE);// 向服务器发buffer中的数据bzero(buffer,BUFFER_SIZE);strcpy(buffer,"Hello, World! From Client\n");int send_flag=send(client_socket,buffer,BUFFER_SIZE,0);if(!send_flag)qDebug("send error\n");qDebug("send success\n");/********************* 向服务器发送image  ****************************///1.load image,get imagesizeMat s_img=imread("1.jpg");imshow("s_img",s_img);vector<uchar> encode_img;imencode(".jpg", s_img, encode_img);///***** test* ************/// align value/*Mat test_img(650,552,CV_8UC3);uchar* pxvec=test_img.ptr<uchar>(0);int i,j;for(i=0;i<s_img.rows;i++){pxvec = test_img.ptr<uchar>(i);// 3 channels range,BGRfor(j=0;j<s_img.cols*s_img.channels();j++){pxvec[j]=244;}}imshow("test_img",test_img);imshow("s_img_encode",s_img);
*///s_img-->vector/*int i,j;uchar* pxvec = s_img.ptr<uchar>(0);for(i=0;i<s_img.rows;i++){pxvec = s_img.ptr<uchar>(i);// 3 channels range,BGRfor(j=0;j<s_img.cols*s_img.channels();j++){//qDebug("px value is %d",pxvec[j]) ;encode_img.push_back(pxvec[j]);}}*///get send_bufferint encode_img_size=encode_img.size();int s_img_size=s_img.rows*s_img.cols*3;qDebug("filesize is %d,width*hight*3 is %d\n",encode_img_size,s_img_size);uchar* send_buffer=new uchar[encode_img.size()];copy(encode_img.begin(),encode_img.end(),send_buffer);//2.send file_nameint toSend=encode_img_size, receive  = 0, finished = 0;QString photoName;char* file_name;char char_len[10];photoName=QString("1.jpg");file_name=photoName.toLatin1().data();// file_name,qDebug file_name be empty//qDebug("file name is %s\n",file_name);bzero(buffer,BUFFER_SIZE);send_flag=send(client_socket, file_name, 10, 0);if(!send_flag){qDebug(" send file_name failed\n ");exit(1);}qDebug("success send file_name \n");//3.send image lengthsprintf(char_len, "%d", toSend);send(client_socket, char_len, 10, 0);//hello world!!hei hei(xiao)!!  strlen(char_len)这里要写一个固定长度,然后让服务器端读出一个固定长度,否则会出错qDebug("char_len is %s\n",char_len);// send test//4.send image datawhile(toSend  >  0){int size = qMin(toSend, 1000);//以前是1000if((receive = send(client_socket, send_buffer + finished, size, 0)))  //send wenzi{if(receive==-1){printf ("receive error");break;}else{toSend -= receive;// shengxia de unsendfinished += receive; //sended}}}//5.close socketclose(client_socket);qDebug("close socket\n");return a.exec();
}

(server)

#include <QCoreApplication>
//本文件是服务器的代码
#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <stdio.h>        // for QDebug
#include <stdlib.h>        // for exit
#include <string.h>        // for bzero
#include <time.h>                //for time_t and time
#include <unistd.h>
//#include <printf>#define HELLO_WORLD_SERVER_PORT 7754
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 1024int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);//服务器代码/////设置一个socket地址结构server_addr,代表服务器internet地址, 端口struct sockaddr_in server_addr;bzero(&server_addr,sizeof(server_addr)); //把一段内存区的内容全部设置为0server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htons(INADDR_ANY);server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);char file_name[10] = "25.jpg";char* file_buffer = new char[552 * 650 * 3];//创建用于internet的流协议(TCP)socket,用server_socket代表服务器socketint server_socket = socket(AF_INET,SOCK_STREAM,0);if( server_socket < 0){printf("Create Socket Failed!");exit(1);}//把socket和socket地址结构联系起来if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))){printf("Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT);exit(1);}listen(server_socket, LENGTH_OF_LISTEN_QUEUE);while (1) //服务器端要一直运行{struct sockaddr_in client_addr;socklen_t length = sizeof(client_addr);//accept() kai shi jie shou shu juint new_server_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length);if ( new_server_socket < 0){printf("Server Accept Failed!\n");break;}char buffer[BUFFER_SIZE];bzero(buffer, BUFFER_SIZE);//send hello worldstrcpy(buffer,"Hello,World! 从服务器来!");strcat(buffer,"\n"); //C语言字符串连接send(new_server_socket,buffer,BUFFER_SIZE,0);bzero(buffer,BUFFER_SIZE);//接收客户端发送来的信息到buffer中length = recv(new_server_socket,buffer,BUFFER_SIZE,0);printf("%s\n",buffer);printf("regional file_name is %s\n",file_name);recv(new_server_socket, file_name, 10, 0);printf("received file_name is %s\n", file_name);char char_len[10];long file_length,read_length;char directory[100] = "/home/sa/abc/";int finished = 0;recv(new_server_socket, char_len, 10, 0);read_length = atoi(char_len);file_length = read_length;printf("received file_length is %d\n", read_length);while (read_length > 1000){int receive = recv(new_server_socket, file_buffer + finished, 1000, 0);//strcpy(file_buff, buff);read_length -= receive;finished += receive;}read_length = recv(new_server_socket, file_buffer + finished, 1000, 0);FILE* fp = fopen(strcat(directory, file_name), "wb");if (fp == NULL)printf("create file failed!\n");else{fwrite(file_buffer, 1, file_length, fp);fclose(fp);printf("create file succed!\n");}//关闭与客户端的连接close(new_server_socket);}//关闭监听用的socketclose(server_socket);return a.exec();
}

3.1.3从指定话题读取摄像头头像,并按序号保存

/****** sub img from img topic:/camera/image_raw* *****/
//qt head/**** ros img headfile* ****/
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>
#include <iostream>using namespace std;
/***** socket headfile* ******/unsigned int fileNum = 1;
bool imageSaveFlag;
void imageTransCallback(const sensor_msgs::ImageConstPtr& msg)
{try{//imgshowcv::imshow("show image", cv_bridge::toCvShare(msg, "bgr8")->image);char key;key=cv::waitKey(33);if(key==32)imageSaveFlag=true;if(imageSaveFlag){stringstream fileName;stringstream filePath;fileName<<"goal rgbImage"<<fileNum<<".jpg";filePath<<"/home/nvidia/Travel_Assistance_Robot/image/"<<fileNum<<".jpg";string fn=fileName.str();string fp=filePath.str();cv::imwrite(fp,cv_bridge::toCvShare(msg, "bgr8")->image);imageSaveFlag =false;fileNum ++;cout<<fileName<<"had saved."<<endl;}//imgwrite//socket image trans}catch (cv_bridge::Exception& e){ROS_ERROR("Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str());}
}int main(int argc, char **argv)
{ros::init(argc, argv, "img_listener");ros::NodeHandle nh;cv::namedWindow("show image");cv::startWindowThread();image_transport::ImageTransport it(nh);image_transport::Subscriber sub = it.subscribe("/camera/image_raw",1,imageTransCallback);ros::spin();cv::destroyWindow("view");return 0;
}

3.1.4、结合3.1.1和3.1.2,从指定话题获取图像,并使用socket将图像传输至服务器

//github

https://github.com/zuwuwang/qt_ros_ws/blob/master/src/guitest/src/socketsend_node.cpp
https://github.com/zuwuwang/qt_ros_ws/blob/master/src/guitest/include/guitest/socketsend_node.hpp

//将获话题消息放到回调函数中

//ROS下定时从节点获取图像

//将socket节点放到回调函数中

vim 查看图像的二进制数据

Vim 可以用来查看和编辑二进制文件
vim -b fileName    加上-b参数,以二进制打开
然后输入命令  :%!xxd -g 1  切换到十六进制模式显示,:wq退出

三、服务器异常检测结果回传

客户端C++代码发送图像,服务器端用python实现了接收图像,连续接收。然后接收到图像之后,输入到MCNN进行检测,检测结果回传。

// 桑永龙,发送检测结果

参考资料

[1]https://blog.csdn.net/zhuoyueljl/article/details/53557822

[2]

ROS 下实现相机图像采集与图像传输到服务器,socket图传相关推荐

  1. ZED2相机SDK安装使用及ROS下使用

    等了快半个月的ZED2相机今天拿到手啦,开始ZED2和VINS之旅吧. 本篇博客主要记录ZED2相机SDK 安装过程以及在ROS下的环境搭建.编译使用等,搭建后期开发环境. ZED2相机实图 SDK安 ...

  2. 使用大恒水星相机利用OpenCV+ Zbar 解QR码在ROS下定位的实现

    使用大恒水星相机利用OpenCV+ Zbar 解QR码在ROS下定位的实现 本次的程序功能实现是在师兄原有程序的基础上,经我继续开发的.主要完成的功能是使用QR码定位,将位姿信息通过ROS中的tf变换 ...

  3. ZED 相机 ORB-SLAM2安装环境配置与ROS下的调试

    注:1. 对某些地方进行了更新(红色标注),以方便进行配置. 2. ZED ROS Wrapper官方github已经更新,根据描述新的Wrapper可能已经不适用与Ros Indigo了,如果大家想 ...

  4. ROS下获取kinectv2相机的仿照TUM数据集格式的彩色图和深度图

    准备工作: 1. ubuntu16.04上安装iai-kinect2, 2. 运行roslaunch kinect2_bridge kinect2_bridge.launch, 3. 运行 rosru ...

  5. basler相机外部硬触发,转换图像格式并发送到ROS下的topic

    运行环境:Ubuntu20.04 (64-Bit) 运行软件:pylon Viewer 64-Bit,pylon Release 6.3.0 相机型号:acA 1920-25gc (GigE接口) 触 ...

  6. ROS下使用乐视RGB-D深度相机/Orbbec Astra Pro显示图像和点云

    ROS下使用乐视RGB-D深度相机显示图像和点云 1. 正常安装 1.1 安装依赖 1.2 建立工作空间 1.3 克隆代码 1.4 Create astra udev rule 1.5 编译源码包 1 ...

  7. ROS入门:ROS下使用电脑相机运行ORB_Slam2

    介绍: 最近在学习slam,想将其应用在ros平台上,故跑了orb-slam2的代码.这里粗略总结一下"ROS下使用电脑相机运行ORB_Slam2"的过程.本人菜鸟一枚,如有问题欢 ...

  8. Ubuntu下ROS运行Pointgrey相机

    为了让Pointgrey相机能够在我的系统上run起来,真的是遇到很多坑,查了很多资料都没有特别全的,为了让大家能够少走一些弯路,写出来和大家分享. 目录 1. 环境介绍及步骤 2. 安装相机驱动 3 ...

  9. 深入理解ROS技术 【1】ROS下的模块详解(1-65)

    ROS初探--意义.基本模块_Peace-CSDN博客 概述: 初学者要想快速入门,必须对于众多的ROS模块初步有个认识,需要主动地.有计划地查阅.本篇以字典方式,列出所有的Ros下模块,给出初步解释 ...

最新文章

  1. [MVC 4] ActionResult 使用示例
  2. 一篇文章教你学会使用SpringBoot实现文件上传和下载
  3. 人工神经网络_用人工神经网络控制猴子大脑,MIT科学家做到了
  4. android 获取app自启动权限状态_央视批手机App权限问题:频繁自启动 搜集个人隐私触目惊心...
  5. 搏天短网址生成网站源码v3.1
  6. 紫米创始人张峰兼任小米笔记本总经理
  7. 《软件测试与质量保证》期末复习重点
  8. Codeforces Round #321 (Div. 2)
  9. 使用VC6.0缺少Dll或头文件解决方法
  10. 学习PLC不可错过的15个基础!
  11. 定位首款弹幕K歌软件 阿里鲸鸣未来究竟能够走多远?
  12. 微信小程序时间选择器
  13. 毕业设计之 - 基于深度学的图像修复 图像补全
  14. Linux ubuntu下载deb包的推荐网站
  15. 网易云课堂web安全第一天
  16. 连续投影算法-python版
  17. 红米note5软件打开速度测试,差一点才完美!红米Note5深度评测(骁龙636性能测试)...
  18. 无线渗透之KALI基础
  19. “线上围观”创先河 百合佳缘集团移动端上线多屏直播
  20. 导出来的手机数据无法使用快速恢复

热门文章

  1. 聊一聊中小型指挥中心的分布式坐席协作管理KVM系统
  2. AutoCAD2018_界面及优化
  3. 驱动轮是什么意思_汽车的驱动轮和被动轮分别是什么意思?
  4. Android横竖屏布局总结
  5. ViewPager2水滴与吸附式切换效果
  6. jQuery Sizzle选择器
  7. 由快速排序引申而来--如何学习算法
  8. HTML URL 编码大全(十六进制格式)
  9. 医学图像多分类的评价指标(包括混淆矩阵,metrics.classification_report等)
  10. UML 交互图(序列图和协作图)