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 子程序,程序现在可以正确接收握手。感谢大家的意见和建议。