我一直在研究涉及机械臂的遥控应用程序。我创建了一个 VB.net 程序,它获取从网页(通过 mySQL)接收到的坐标并将坐标转换为电机步数,然后使用串行命令将其输出到机器人。
机器人手册(MICROBOT TeachMover)指出,每当向机器人发送串行命令时,机器人都会返回一个字符(0、1 或 2),指示操作成功或失败。手册指出,这个“握手”字符必须在程序中接收。
每当我运行我的程序时,机械臂都不会按预期运行。有一个很长的初始滞后,其中机器人根本不移动,之后它最终会沿正 x 方向移动。之后,它再次完全停止移动。我不断收到 COM 端口超时的异常,这表明握手字符没有被正确读取。我确实知道串行命令正在正确发送(我在表单加载期间使用关闭夹具命令对此进行了测试)但是我的串行接收命令总是以超时异常结束,而不管我设置的超时秒数。我在下面包含了我当前的代码。我对串行通信了解不多,所以如果有人能指出我的任何误解,那就太好了。
VB.net 代码:
' Libraries
Imports MySql.Data.MySqlClient ' Enables connection with MySQL
Imports System.IO.Ports ' Enables communcation with ports
Imports System.Threading.Tasks ' Enables use of Timer class
Imports System.Timers ' Enables use of Timer class
Public Class Form1
' MySQL Variables
Private connectionString As String = "server=localhost;user id=root; password=;database=coordinates"
Private commandText As String = "SELECT * FROM coordinatevals"
Private con As MySqlDataAdapter
Private table As DataTable
' COM Port Variables
Private com As SerialPort ' Port Variable
Private COMPortNumber As Integer = 1 ' Variable to hold COM port number
' Joystick/Robot Coordinates
Private X As Double ' Holds x-axis value received from mySQL
Private Y As Double ' Holds y-axis value received from mySQL
Private Z As Double ' Holds z-axis value received from mySQL
Private P As Double ' Holds pitch angle received from mySQL
Private R As Double ' Holds roll angle received from mySQL
Private thumbPressed As Boolean ' Holds thumb state value received from mySQL
' Robot Arm Constants
Private Const H As Double = 7.625 ' Shoulder height above table (in.)
Private Const L As Double = 7.0 ' Shoulder-to-elbow & elbow-to-wrist length (in.)
Private Const LL As Double = 3.8 ' Wrist-to-fingertip length (Gripper closed) (in.)
Private Const C As Double = 180 / Math.PI ' Degrees to Radians conversion constant (degrees in 1.0 radian)
Private Const R1 As Integer = 0 ' Roll is WRT Arm frame (Change to 1 if WRT Cartesian Frame)
' Variables to hold joint angles
Private T1 As Double
Private T2 As Double
Private T3 As Double
Private T4 As Double
Private T5 As Double
' Variables to hold distances and angles for moving robot
Private RR As Double ' Variable that holds radius
Private R0 As Double ' Variable that holds the distance from the shoulder to the wrist
Private Z0 As Double ' Variable that holds the height of the wrist above the shoulder
Private B As Double ' Angle about which shoulder-elbow-wrist must be pivoted
Private A As Double ' Angle in shoulder-elbow-wrist triangle
' Variables to hold robot arm scale factors
Private Const S1 As Double = 1125
Private Const S2 As Double = -S1
Private Const S3 As Double = -661.2
Private Const S4 As Double = -244.4
Private Const S5 As Double = S4
' Joint Steps from zeroed joint angles to initialization position
Private Const P1 As Integer = 0
Private Const P2 As Integer = -508
Private Const P3 As Integer = 1162
Private Const P4 As Integer = 384
Private Const P5 As Integer = P4
' Variables to hold correct coordinates
Private W1 As Integer
Private W2 As Integer
Private W3 As Integer
Private W4 As Integer
Private W5 As Integer
' Variables to hold previous coordinates
Private oldW1 As Integer = 0
Private oldW2 As Integer = 0
Private oldW3 As Integer = 0
Private oldW4 As Integer = 0
Private oldW5 As Integer = 0
' Variable to hold speed of robot
Private robotSpeed As Integer = 50
' String for command
Private cmdString As String = Nothing ' String to hold command to be sent to robot
' Timer
Dim Timer1 As New Timer(5000)
' Form Load Event
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Set up the COM port.
com = My.Computer.Ports.OpenSerialPort("COM" & COMPortNumber.ToString)
com.BaudRate = 9600
com.DataBits = 8
com.Parity = IO.Ports.Parity.None
com.StopBits = IO.Ports.StopBits.One
' Reset the robot.
SendSerialData("@RESET")
' Start the timer.
Timer1.Start()
' Add handler for receiving handshake from the TeachMover.
AddHandler com.DataReceived, AddressOf DataReceivedHandler
' Add handler for ticks of clock.
AddHandler Timer1.Elapsed, AddressOf Timer1_Tick_1
End Sub
' Handler for Timer1 Object
Private Sub Timer1_Tick_1(sender As Object, e As EventArgs)
' Disable the timer.
Timer1.Enabled = False
' Fill DataTable object with MySQL data.
Try
con = New MySqlDataAdapter(commandText, connectionString)
table = New DataTable
con.Fill(table)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
' Update form with mySQL data
' Trigger
' x-axis
X = table.Rows(0).Item(0)
' y-axis
Y = table.Rows(0).Item(1)
' z-Axis
Z = table.Rows(0).Item(2)
' Roll angle
R = table.Rows(0).Item(3)
' Pitch angle
P = table.Rows(0).Item(4)
' Thumb button state
thumbPressed = table.Rows(0).Item(5)
' If the thumb button on the joystick is pressed, close the program.
If thumbPressed = True Then
Me.Close()
End If
moveTeachMover()
' Re-enable the timer.
Timer1.Enabled = True
End Sub
' convertToRadians takes an angle in degrees and returns the equivalent angle in radians
Function convertToRadians(ByVal angleInDeg As Double) As Double
Return (angleInDeg / C)
End Function
' sendSerialData takes a string and sends it to the COM port.
Sub SendSerialData(ByVal data As String)
com.WriteLine(data & vbCrLf)
End Sub
Private Shared Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs)
Dim sp As SerialPort = CType(sender, SerialPort)
Try
sp.ReadTimeout = 20000
Dim indata As String = sp.ReadLine()
Dim handshake As Integer = CType(indata, Integer) ' Variable that holds the data recieved from the robot
If handshake = 0 Or handshake = 2 Then
MsgBox("ERROR: The command was not executed correctly.")
End If
Catch ex As TimeoutException
MsgBox("ERROR: Serial Port read timed out.")
End Try
End Sub
'' ReceiveSerialData receives data from the TeachMover
'Sub ReceiveSerialData()
' ' Receive strings from a serial port.
' Dim returnStr As String = ""
' Try
' com.ReadTimeout = 10000
' handshake = CType(com.ReadLine(), Integer)
' If handshake = 0 Or handshake = 2 Then
' MsgBox("ERROR: The command was not executed correctly.")
' End If
' Catch ex As TimeoutException
' MsgBox("ERROR: Serial Port read timed out.")
' End Try
'End Sub
Private Sub moveTeachMover()
' Convert angles to radians.
P = convertToRadians(P)
R = convertToRadians(R)
' theta1
' Special case where x-coordinate is 0
If X = 0 Then
T1 = Math.Sign(Y) * Math.PI / 2
Else
T1 = Math.Atan(Y / X)
End If
' ERROR: theta1 is out of range.
If T1 < 0 Then
Exit Sub
End If
' radius
RR = Math.Sqrt((X * X) + (Y * Y))
' ERROR: Hand too close to body
If RR < 2.25 And Z < 15 Then
Exit Sub
End If
' ERROR: Reach out of range
If RR > 17.8 Then
Exit Sub
End If
' Distance from shoulder to wrist
R0 = RR - LL * Math.Cos(P)
If X < 2.25 And Z < 1.25 And R0 < 3.5 Then
' ERROR: Hand interference with base
If P < convertToRadians(-90) Then
Exit Sub
End If
End If
' Height of the wrist above the shoulder
Z0 = Z - LL * Math.Sin(P) - H
' Angles
If R0 = 0 Then
B = (Math.Sign(Z0)) * Math.PI / 2
Else
B = Math.Atan(Z0 / R0)
End If
A = R0 * R0 + Z0 * Z0
A = 4 * L * L / A - 1
' ERROR: Reach out of range for shoulder
If A < 0 Then
Exit Sub
End If
A = Math.Atan(Math.Sqrt(A))
' theta2 and theta3
T2 = A + B
T3 = B - A
' ERROR: Shoulder out of range
If T2 > convertToRadians(144) Or T2 < convertToRadians(-35) Then
Exit Sub
End If
' ERROR: Elbow out of range
If T2 - T3 < 0 Or T2 - T3 > convertToRadians(149) Then
Exit Sub
End If
' ERROR: Pitch out of range
If (R > convertToRadians(270) Or R < convertToRadians(-270)) Then
If (P > ((convertToRadians(90) + T3) - (R + convertToRadians(270))) Or
P < ((convertToRadians(-90) + T3) + (R - convertToRadians(270)))) Then
Exit Sub
End If
End If
' ERROR: Pitch out of range
If P > (convertToRadians(90) + T3) Or P < (convertToRadians(-90) + T3) Then
Exit Sub
End If
' ERROR: Roll out of range
If (R > (convertToRadians(360) - Math.Abs(P - T3)) Or R < (convertToRadians(-360) + Math.Abs(P - T3))) Then
Exit Sub
End If
' theta4
T4 = P - R - R1 * T1
' theta5
T5 = P + R + R1 * T1
' Get correct coordinates.
W1 = CType((S1 * T1 + 0.5), Integer) - P1
W2 = CType((S2 * T2 + 0.5), Integer) - P2
W3 = CType((S3 * T3 + 0.5), Integer) - P3
W4 = CType((S4 * T4 + 0.5), Integer) - P4
W5 = CType((S5 * T5 + 0.5), Integer) - P5
' Send command to robot via serial port.
cmdString = "@STEP " & robotSpeed.ToString & "," & (W1 - oldW1).ToString & "," & (W2 - oldW2).ToString & "," & (W3 - oldW3).ToString & "," & (W4 - oldW4).ToString & "," & (W5 - oldW5).ToString
SendSerialData(cmdString)
'ReceiveSerialData()
oldW1 = W1
oldW2 = W2
oldW3 = W3
oldW4 = W4
oldW5 = W5
End Sub
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
If com IsNot Nothing Then
com.Close()
End If
Timer1.Stop()
End Sub
End Class
(ReceiveSerialData sub 是我最初实现的;我把它留在了程序中,作为我尝试过的两种方法的示例而被注释掉了。)
我知道从 mySQL 获取坐标没有延迟,因为我已经自己测试过了。但是,我不知道这是否会导致串行通信的滞后。
如果有人对如何使我的串行通信代码更可靠和更快有任何建议或建议,我将不胜感激。我还想知道是否应该使用握手属性,但我还没有尝试过,因为手册指出 TeachMover 臂不使用标准接口信号(DTR、CTS、RTS 等)我有兴趣找出这是软件问题还是硬件问题。
谢谢你,戈皮卡
更新:我一直在进行一些更改并测试串行通信。我暂时将超时更改为无限。我还尝试设置握手属性和 RTSEnable/DTREnable,但程序仍然没有收到来自 TeachMover 的握手并且正在无限等待。