模块化机械臂上位机设计及下位机通信
作者:互联网
文章目录
前言
本周完成机械臂上位机界面各模块程序编写,并借用师兄图像识别代码完成机械臂位置控制,此外本周还进行了srtp结题材料准备,撰写研究报告等
一、上位机功能介绍
(由于博客不能放视频,用图片替代)
1、上、下位机通讯,选择端口号和波特率
2、机械臂位姿初始化及机械爪的状态控制(由于机械爪在实验过程中不小心损坏,这里不贴图了)
3、三维空间目标点定位
4、机械臂点动控制
二、上位机程序设计及所解决问题
1.串口通信
代码如下:
private void SearchAndAddSerialToComboBox(SerialPort MyPort, ComboBox MyBox)
{
string[] MyString = new string[20];
string Buffer;
MyBox.Items.Clear();
int count = 0;
for (int i = 1; i < 20; i++)
{
try//扫描
{
Buffer = "COM" + i.ToString();
MyPort.PortName = Buffer;
MyPort.Open();
MyString[count] = Buffer;
MyBox.Items.Add(Buffer);
MyPort.Close();
count++;
}
catch{}
MyBox.Text = MyString[0];
}
}
思路为轮流打开前20个端口号,如果端口号可以打开,记录该端口号于ComboBox中以供选择,关闭端口号。
需要注意的是创建端口最好使用以下代码,其可以在多窗口使用,而工具箱控件创建的端口不能被其他窗口调用
public static SerialPort serialPort1 = new SerialPort();
2.C#调用matlab函数
参考此篇博客
1.首先配置环境。
Matlab Runtime (MCR)一定要和你的C#项目平台一致。
c#项目平台如下图,可以点击箭头→配置管理器→活动解决方案平台→新建→选择需要的项目平台。
而Matlab Runtime的配置可以在系统的环境变量中查找,具体步骤:我的电脑右键属性→高级系统设置→环境变量→Path
2.配置完成之后可以生成dll文件了,首先新建函数,然后在matlab命令行中输入deploytool,跳出如下页面
选择Library Compiler 进入如下页面。
第一个箭头选择.net 环境,第二个箭头添加你刚才保存的函数。后面其他的配置不需要更改,如果想要对生成的dll文件里面的类名以及命名空间进行修改可以在此图片最后一部分进行修改,此处就不修改。点击右上角Package,保存并等待。此处如果生成失败,说明matlab安装存在问题。
3.C#调用matlab函数
首先创建项目,并导入两个依赖项,(1)刚才Package生成的dll文件。(2)MWArray.dll文件,在Matlab安装目录下%matlabroot%\toolbox\dotnetbuilder\bin\win64\v4.0\MWArray.dll。导入依赖项成功之后编写函数。
private void Inv_Kinematics(double x, double y, double z)//调用matlab逆解函数
{
//当前位置
CurrPosX = x;
CurrPosY = y;
CurrPosZ = z;
//初始化
double Q1 = 0, Q2 = -67.92, Q3 = 28.64, Q4 = 20;
//输入参数
MWArray X_coor = (MWNumericArray)new double[1] { x };
MWArray Y_coor = (MWNumericArray)new double[1] { y };
MWArray Z_coor = (MWNumericArray)new double[1] { z };
MWArray[] agrsIn = new MWArray[] { (MWNumericArray)X_coor, (MWNumericArray)Y_coor, (MWNumericArray)Z_coor };//输入参数
MWArray[] agrsOut = new MWArray[4];//输出存放的数组,4个输出参数,一定要写数量
InvKinematics.Class1 RobArm = new InvKinematics.Class1();//实例化对象
RobArm.InvKinematics(4, ref agrsOut, agrsIn);//计算,调用函数,输出参数需要加ref关键字;
//4表示输入参数个数,输出结构都在argsOut中,类似于C的指针参数输入
MWNumericArray MTheta1 = agrsOut[0] as MWNumericArray;//转换得到实际的输出参数,从下标0开始
MWNumericArray MTheta2 = agrsOut[1] as MWNumericArray;
MWNumericArray MTheta3 = agrsOut[2] as MWNumericArray;
MWNumericArray MTheta4 = agrsOut[3] as MWNumericArray;
double[,] CTheta1 = (double[,])MTheta1.ToArray();//转换到C#中可用的实际输出参数
double[,] CTheta2 = (double[,])MTheta2.ToArray();
double[,] CTheta3 = (double[,])MTheta3.ToArray();
double[,] CTheta4 = (double[,])MTheta4.ToArray();
double ATheta1 = Math.Round(CTheta1[0, 0] - Q1, 2);
double ATheta2 = Math.Round(CTheta2[0, 0] - Q2, 2);
double ATheta3 = Math.Round(CTheta3[0, 0] - Q3, 2);
double ATheta4 = Math.Round(CTheta4[0, 0] - Q4, 2);
textBox1.Text = ATheta1.ToString();//各角度输入文本框中
textBox2.Text = ATheta2.ToString();
textBox3.Text = ATheta3.ToString();
textBox4.Text = ATheta4.ToString();
if (GrabState == 0)
{
textBox8.Text = "Release";
string Send = ATheta1.ToString() + ";" + ATheta2.ToString() + ";" + ATheta3.ToString() + ";" + ATheta4.ToString() + ";" + "-150";//整成一个字符串
ToSend = Send;//使用全局变量ToSend
}
else
{
textBox8.Text = "Hold";
string Send = ATheta1.ToString() + ";" + ATheta2.ToString() + ";" + ATheta3.ToString() + ";" + ATheta4.ToString() + ";" + "150";//整成一个字符串
ToSend = Send;//使用全局变量ToSend
}
}
在调用MWArray类时出现问题,此时应注意所用的MWArray的环境需要和你的项目平台保持一致。即,引用的win64下的MWArray时,c#的项目平台应该是x64。
3.OpencvSharp矩阵运算
总体来说opencv有的功能opencvsharp都有,本周主要学习了opencv的一些基本操作,关于矩阵运算以及opencvsharp实现
OpenCV Mat 类型定义、赋值及矩阵运算
1.一般的Mat定义方法:cv::Mat M(height,width,),例:cv::Mat M(480,640,CV_8UC3); 表示定义了一个480行640列的矩阵,矩阵的每个单元的由三个(C3:3 Channel)8位无符号整形(U Unsigned U8 8位)构成。
2.将已有数组赋给Mat矩阵的方法:
cv::Mat M = cv::Mat(height,width,,data),例:
float K[3][3] = {fc[0], 0, cc[0], 0, fc[1], cc[1], 0, 0, 1}; //摄像机内参数矩阵K
cv::Mat mK = cv::Mat(3,3,CV_32FC1,K); //内参数K Mat类型变量
3.类似matlab:zeros(),ones(),eyes()的初始化方法:
cv::Mat M = cv::Mat::eye(height,width,<Type>)
cv::Mat M = cv::Mat::ones(height,width,<Type>)
cv::Mat M = cv::Mat::zeros(height,width,<Type>)
对于opencv矩阵运算详细可参考次篇博客https://blog.csdn.net/hookie1990/article/details/79213722
OpenCVSharp矩阵定义和赋值
float[,] Rot = { { 0, -1, 0, 0 }, { 1, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };设置4*4的数组
float[,] Trans = { { 1, 0, 0, 4 }, { 0, 1, 0, -3 }, { 0, 0, 1, 7 }, { 0, 0, 0, 1 } };
float[,] Rot0 = { { 0, 0, 1, 0 }, { 0, 1, 0, 0 }, { -1, 0, 0, 0 }, { 0, 0, 0, 1 } };
float[,] pxyz = new float[4, 1];//初始化一个类型为float4*4的数组
Mat MRot = new Mat(4, 4, MatType.CV_32FC1, Rot);//将数组放入mat类型中
Mat MTrans = new Mat(4, 4, MatType.CV_32FC1, Trans);
Mat MRot0 = new Mat(4, 4, MatType.CV_32FC1, Rot0);
Mat MPnoa = new Mat(4, 1, MatType.CV_32FC1, pnoa);
Mat MPxyz;
MPxyz = MRot * MTrans * MRot0 * MPnoa;
MPxyz.GetArray(0, 0, pxyz);
Mat AB;
AB = qq * pp;
AB.GetArray(0, 0, bb);//mat类型转数组
Mat pinv2 = new Mat();
Cv2.Invert(mt, pinv2, DecompTypes.SVD);//矩阵的逆
textBox9.Text = bb.ToString();
其中建立矩阵必须要指定矩阵存储的数据类型,图像处理中常用的几种数据类型可参考此篇博客
CV_8UC1// 8位无符号单通道
CV_8UC3// 8位无符号3通道
CV_8UC4
CV_32FC1// 32位浮点型单通道
CV_32FC3// 32位浮点型3通道
CV_32FC4
包括数据位深度8位、32位,数据类型U:uchar、F:float型以及通道数C1:单通道、C3:三通道、C4:四通道。
4.C#读取txt文件数据到数组
public double[,] ReadTxttest(string Path)
{
//初始化二维数组
double[,] array = new double[300, 3];
int i = 0;
// 新建一个DataTable
DataTable tb = new DataTable();
// 添加一列用于存放读入的浮点数
DataColumn c = tb.Columns.Add("Value", typeof(double));
// 打开文件准备读取数据
StreamReader rd = File.OpenText(@Path);
string line;
while ((line = rd.ReadLine()) != null)
{
// 拆分出一行的所有用逗号分割的数据项
string[] values = line.Split(',');
//values为每行数据切割后的数组
// 将每个数据项转换成浮点数,并存入DataTable
foreach (string s in values) //s为行内元素
{
if (!string.IsNullOrEmpty(s))
{
// 转换成浮点数
double v = double.Parse(s);
// 存入DataTable
DataRow r = tb.NewRow();
r["Value"] = v;
tb.Rows.Add(v);
}
}
//计算每行的数据量
Console.WriteLine(tb.Rows.Count);
int j = 0; // 列数
//输出DataTable中保存的数组
foreach (DataRow r in tb.Rows)
{
var k = (double)r["Value"];//获取行内元素
if (!string.IsNullOrEmpty(k.ToString()))
{
array[i, j] = k;
Console.WriteLine(i.ToString() + "--" + j.ToString() + "--" + k.ToString());
}
else
{
array[i, j] = 1.23456;
}
j = j + 1;
}
// Console.WriteLine("行数是:" + tb.Rows.Count);
//清除每行数据
tb.Rows.Clear();
i = i + 1;
}
在读取数据的过程中调用了C#的DataTable,并通过Console.WriteLine()函数对结果进行验证。
三、arduino下位机程序设计及所解决问题
1.数据通信
上位机向下位机发送字符串,各数据通过";"分隔符区分开来,arduino的处理函数是有数据就进入处理,因此做了个循环接受完整数据
while(Serial.available() > 0)
{
Instructions += char(Serial.read());
delay(2);
mark = 1;
}
注意最好使用帧头帧尾标识符,以及校验码来判断通讯数据是非有效,会非常准确的接收而不出错。
数据处理代码如下:
do
{
StrPos = Instructions.indexOf(";");//找到位置
if(StrPos != -1)//如果位置不为空
{
TempStr = Instructions.substring(0, StrPos);//打印取出第一个字符
Instructions = Instructions.substring(StrPos + 1, Instructions.length()); //分隔后只取后面一段内容 以方便后面找查
}
else
{
//上面实在找不到了就把最后的 一个分割值赋值出来以免遗漏
if(Instructions.length() > 0)
TempStr=Instructions;
}
delay(10);
Angle[count] = atof(TempStr.c_str()) + 150;
count ++;
TempStr = "";
}
while(StrPos >= 0);
总结
以上是本周的上、下位机的工作情况,此外srtp的结题材料同时在撰写中,下周开始学院开展为期4周的课设,任务比较重,因此接下来主要是学习冈萨雷斯的《数字图像处理》及matlab实现。
标签:Mat,double,下位,上位,ToString,模块化,new,cv,MWNumericArray 来源: https://blog.csdn.net/qq_45637099/article/details/111771100