【自动化毕业设计】基于机械视觉控制的板球控制装置
大四自动化专业毕业设计,断断续续制作了三个月,中途参加了校招和其他杂事,按照一般工作强度制作大概需要3个星期。
【实现功能】
自平衡控制
定位控制
运动轨迹规划控制
随动系统控制
【视频演示】
https://www.bilibili.com/video/av36573493
https://www.bilibili.com/video/av36688397
【制作过程】
板材:
所有黑色板材都是用哑光(磨砂)亚克力板。
400* 400* 3 一块(蓝色箭头)
500* 600* 3 两块(红色箭头)
40 * 500* 3 四块(绿色箭头)
40 * 580* 3 四块(绿色箭头)
舵机固定架:
丝杆与板材连接结构:
三个丝杆,M4 (2支) 、M8(1支)、M3螺丝+M3螺帽数个
25T金属舵机摆臂
2
连杆头
2:
10
10mm(1个)、5
5mm(2个)万向节:
M8丝杆底座(关键字:管道支架):
用ab胶粘牢后组装效果图:
如果觉得有松动的话可以适当加上杜邦线拧紧,会有减震效果
其余元件:
舵机
2(串行总线舵机、普通的模拟舵机都行,自行制作一个下位机即可)
usb摄像头1
氧化锆小球12mm * 1
之所以选用氧化锆小球,是我做了很多实验得出的结果
表面越光滑的小球,可控制程度越高。比如我设置底盘倾角为5°,乒乓球在上面可能会保持静止,而氧化硅小球可能在1°的时候就会发生运动。如果可控度低,在控制算法上就要用大量算法解决这个问题。摄像头我采用1920*1080分辨率采集图像数据,大概是30fps。
完成机械机构标准是,小球的底盘尽量不能出现在垂直Z轴放向的转动;调节舵机处于中值附近时底盘上可停留小球使其静止(我会放出串口控制舵机的源码供大家修改参考);底盘与相机画面尽量平衡(如图)
【程序框图】
【图像算法】
通过opencv获取摄像头画面,初始化时设置平板的左上与右下坐标,在之后的处理中即可排除无相关的环境。
/* 打开摄像头 */ capture = VideoCapture(CV_CAP_DSHOW); capture.open(cap); /* 设置摄像头 */ capture.set(CV_CAP_PROP_FOURCC, CV_FOURCC('M', 'J', 'P', 'G'));//设置为MJPG格式 capture.set(CV_CAP_PROP_FRAME_WIDTH, CAP_WIDTH);//宽度 capture.set(CV_CAP_PROP_FRAME_HEIGHT, CAP_HEIGHT);//高度 capture.set(CV_CAP_PROP_FPS, CAP_FPS);//帧率 帧/秒 capture.set(CV_CAP_PROP_BRIGHTNESS, 1);//亮度 capture.set(CV_CAP_PROP_CONTRAST, 40);//对比度 40 capture.set(CV_CAP_PROP_SATURATION, 50);//饱和度 50 capture.set(CV_CAP_PROP_HUE, 50);//色调 50 capture.set(CV_CAP_PROP_EXPOSURE, -6.5);//曝光 /* 获取可识别范围 */ Mat frame; capture >> frame; imshow("Vidio", frame); cvSetMouseCallback("Vidio", OnMouseCallBack, &frame); while (tmp2.y == -1) { waitKey(10); } destroyWindow("Vidio"); broad_position1.x = tmp1.x; broad_position1.y = tmp1.y; broad_position2.x = tmp2.x; broad_position2.y = tmp2.y; /* 点击鼠标执行 */ void OnMouseCallBack(int event, int x, int y, int flags, void *param) { static bool click_flag = false; if (event == CV_EVENT_LBUTTONDOWN) { if (!click_flag) { tmp1.x = x; tmp1.y = y; click_flag = true; } else { tmp2.x = x; tmp2.y = y; click_flag = false; } } }
这里为了提高算法的可移植性,应该加上一个转换系统,作为像素点与实际长度的转换中间量,比如我的底盘长度是400mm的,图像上占了600个像素点,那么之间的比例系数就是1.5,往后的小球运算中输入的都是mm为单位的数据。
/* 初始化平板 */ exchange_value_x = REAL_HEIGHT / (broad_position2.x - broad_position1.x); exchange_value_y = REAL_WIDTH / (broad_position2.y - broad_position1.y);
获取到一帧图像数据后,经过裁剪、灰度化、二值化,理想的图片应为:
再通过扫描像素点,判断每行像素点中白色所占长度是否为预设的小球半径,如果符合条件则得出小球x轴坐标与y轴坐标。小球的速度则是两次通过两次坐标差算出的。
capture >> frame; //将摄像头的视频流转换成Mat格式的图像 frame = frame(Range(broad_position1.y, broad_position2.y - 1), Range(broad_position1.x, broad_position2.x - 1)); //裁剪 cvtColor(frame2, frame, COLOR_RGB2GRAY); //彩色图像灰度化 threshold(frame, frame, THRESH_BINARY_C_VALUE, THRESH_BINARY_H_VALUE, CV_THRESH_BINARY); //二值化 Point position; for (int i = 0; i < frame.rows - 1; i++) { unsigned char *data1 = frame.ptr<uchar>(i); for (int j = 0; j < frame.cols - 1; j++) { if (data1[j] >= 250) { int begin = j; while (data1[j++] >= 250 && (j < frame.cols - 2)); if ((double)(j - begin)*exchange_value_x <= mindiameter || ((double)(j - begin)*exchange_value_x >= maxdiameter))//不是球,扫描下一行 { continue; } position.x = (j + begin)/2; position.y = i; double radiu = j - begin; radiu = exchange_value_x* radiu; ball_state = true; x = (double)position.x*exchange_value_x; y = (double)position.y *exchange_value_y; cout << "半径=" << radiu << " "; _x_speed = (x- position_x) ; _y_speed= (y- position_y) ; position_x = x; position_y = y; lasttime = newtime; return true; } } } ball_state = false; return false;
【控制算法】
采用的是“X/Y坐标位置外环——速度内环串级PID控制”与“X轴——Y轴并级PID控制”,系统结构框图如下。速度环主要起着限速的作用,所以无需积分,控制精度由位置环的积分环节发挥主要作用。调试过程是先把解决速度PD环,首先加入比例参数,使关闭微分作用,速度设定值为0,把小球推置底盘中,观察系统是否可把小球平衡至静止状态,调节幅度过大舵机反复震荡则把比例系数减小,调节幅度过小球直接按照原速度方向滚下底盘则把比例系数增大。当系统可把小球稳定在底盘上时则开始调节微分系数。微分的作用是加快系统稳定速度,减少超调量,这个大家调试时候自行酌情调试吧。
外环的调试顺序是P-D-I,比例系数和微分系数还是根据上述情况去调试,我在积分部分加上了积分分离算法和防止积分过饱和算法,积分是为了提高位置控制精度的,如果积分作用有过大的负面作用,可以适当降低精度要求。
最后的圆周运动,是在原有控制上加了一个半径外环PI算法
【后序扩展】
我自己加了红点识别做的随动系统和手柄的控制、模式切换,这个大家可以自由发挥啦~
资源:
链接: https://pan.baidu.com/s/1NgEjScvQoY_u7rp_vTdHPA 提取码: qumd
原文链接:https://blog.csdn.net/automan05/article/details/84501990