Visual Basic 程序中的 COM 端口超时和滞后
COM Port Timeout and Lag in Visual Basic Program
我一直在研究涉及机械臂的遥控应用程序。我创建了一个 VB.net 程序,它获取从网页(通过 mySQL)接收到的坐标并将坐标转换为电机步进,然后使用串行命令将其输出到机器人。
机器人(MICROBOT TeachMover)的手册指出,无论何时向机器人发送串行命令,机器人都会发回一个字符(0、1 或 2),指示操作成功或失败.手册上说这个"handshake"字符必须在程序中接收。
每当我 运行 我的程序时,机械臂不会按预期运行。有一个很长的初始滞后,在此期间机器人根本不移动,之后它最终在正 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 arm 不使用标准接口信号(DTR、CTS、RTS 等)我会有兴趣了解这是软件问题还是硬件问题。
谢谢,
戈皮卡
更新:我一直在进行一些更改并测试串行通信。我暂时把timeout改成了infinite。我也试过设置握手 属性 和 RTSEnable/DTREnable,但是程序仍然没有收到来自 TeachMover 的握手并且在无限等待。
我对我的项目做了进一步的测试,我发现程序现在实际上正在接收握手。我将超时更改为无限,然后恢复到 ReceiveSerialData 子程序,程序现在可以正确接收握手。感谢大家的意见和建议。
我一直在研究涉及机械臂的遥控应用程序。我创建了一个 VB.net 程序,它获取从网页(通过 mySQL)接收到的坐标并将坐标转换为电机步进,然后使用串行命令将其输出到机器人。
机器人(MICROBOT TeachMover)的手册指出,无论何时向机器人发送串行命令,机器人都会发回一个字符(0、1 或 2),指示操作成功或失败.手册上说这个"handshake"字符必须在程序中接收。
每当我 运行 我的程序时,机械臂不会按预期运行。有一个很长的初始滞后,在此期间机器人根本不移动,之后它最终在正 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 arm 不使用标准接口信号(DTR、CTS、RTS 等)我会有兴趣了解这是软件问题还是硬件问题。
谢谢, 戈皮卡
更新:我一直在进行一些更改并测试串行通信。我暂时把timeout改成了infinite。我也试过设置握手 属性 和 RTSEnable/DTREnable,但是程序仍然没有收到来自 TeachMover 的握手并且在无限等待。
我对我的项目做了进一步的测试,我发现程序现在实际上正在接收握手。我将超时更改为无限,然后恢复到 ReceiveSerialData 子程序,程序现在可以正确接收握手。感谢大家的意见和建议。