I am using pitch and yaw angle from an IMU to move a mouse pointer for PC in vb.net.
Unfortunately it doesn't work steady.
First I set the max angles which should be performed
LOAD-Event
'set max angles in degree
maxAngle_YawRight = 30
maxAngle_YawLeft = -30
maxAngle_PitchUp = 20
maxAngle_PitchDown = -20
calculateCurParameter(maxAngle_YawRight, Panel_Right.Location.X, maxAngle_YawLeft, Panel_Left.Location.X + Panel_Left.Width, maxAngle_PitchUp, Panel_Top.Location.Y+Panel_Top.Height, maxAngle_NickDown, Panel_Bottom.Location.Y)
I use four these max angles (pitch_up, pitch_down, yaw_right, yaw_left) to assign them to a part of my form, to get 4 points, e.g. (pitch_up,panel_2.location.Y)
pitch_up and pitch_down are used for the y coordinate of the mouse pointer and yaw_right and yaw_left are used for the x coordinate. This is done by function calculateCurParameter.
Then I use this four points to calculate parameters for an linear equation:
CursorPos=m*angle+b
So you get for every new angle a new Cursor Position.
Private mX As Double
Private bX As Double
Private mY As Double
Private bY As Double
...
Private Sub calculateCurParameter(ByVal maxYawRight As Double, ByVal rightBorder As Double, ByVal maxYawLeft As Double, ByVal leftBorder As Double, ByVal maxPitchUp As Double, ByVal topBorder As Double, ByVal maxPitchDown As Double, ByVal bottomBorder As Double)
'this function calculates the parameter for Ycur=m*Xangle+b for each Pitch and Yaw
'calculate the parameter for the CursorPosition X
dim deltaX= maxYawLeft-maxYawRight
dim deltaY=leftBorder-rightBorder
mX=deltaY/deltaX
bX=rightBorder-mX*maxYawRight
'calculate the parameter for the CursorPosition Y
deltaX=bottomBorder-topBorder
deltaY=maxPitchDown-maxPitchUp
mY=deltaY/deltaX
bY=topBorder-mY*maxPitchUp
end Sub
After that calculation, I use a quadratic weighted moving average (QWMA) as a low pass filter cursor outputs calculated by the function y=m*x+b.
For that moving average I use the last 50 samples of each angle.
After that I put the two angles in each linear equation(cursorPos=m*angle+b) as described.
'Timer-Event which is triggered each 8ms
' yaw and pitch are in degrees and are updated in this timer event before
Dim xCoordCur = Math.Round(mX * yaw + bX)
Dim yCoordCur = Math.Round(mY * pitch + bY)
arrayCurY(cursorCounter) = yCoordCur
arrayCurX(cursorCounter) = xCoordCur
If cursorCounter =50 Then 'need 50 samples to do the QWMA
Dim aqwmY = qwma_calculating(arrayCurY)'function to calculate the QWMA, seems to be working
Dim aqwmX = qwma_calculating(arrayCurX)
'the mouse pointer should not leave the form
If aqwmX > Panel_Right.Location.X Then
aqwmX = Panel_Right.Location.X
ElseIf aqwmX < (Panel_Left.Location.X + Panel_Left.Width) Then
aqwmX = Panel_Left.Location.X + Panel_Left.Width
End If
If aqwmY > Panel_Bottom.Location.Y Then
aqwmY = Panel_Bottom.Location.Y
ElseIf aqwmY < (Panel_Top.Location.Y + Panel_Top.Height) Then
aqwmY = Panel_Top.Location.Y + Panel_Top.Height
End If
'Set the new Cursor Position
Cursor.Position = New Point(aqwmX, aqwmY)
arrayValuesMoving(arrayCurX)'function to move the values one index forward
arrayValuesMoving(arrayCurY)
Else
cursorCounter += 1
End If
In the my last step I set the new Cursor-Position
Cursor.Position=New Point(xCoor,yCoor)
Now I can control the mouse pointer by moving the IMU, but it is very unstable.
For example the mouse pointer still moves although the IMU doesn't move.
It's even impossible to hover over some form elements.
What did I do wrong?
Thanks in advance!
I added the follwoing code:
If xCoor > Cursor.Position.X + 3 Or xCoor < Cursor.Position.X - 3 Then
istxCoor = True
Else
istxCoor = False
End If
If yCoor > Cursor.Position.Y + 3 Or yCoor < Cursor.Position.Y - 3 Then
istyCoor = True
Else
istyCoor = False
End If
If istxCoor And istyCoor Then
SetCursorPos(xCoor, yCoor)
ElseIf istxCoor Then
SetCursorPos(xCoor, Cursor.Position.Y)
ElseIf istyCoor Then
SetCursorPos(Cursor.Position.X, yCoor)
End If
it is now more steady but it has now a time lag.
Related
I am having problems with a project which should show the acceleration of a simple pendulum swinging over time. It should model a negative cosine curve, and I have the X and Y axis set so that the cosine curve will peak at the top of the chart area. However, it does not do this. For example, the maximum acceleration is calculated to be 17.746 m/s^2, however on the chart it says the maximum is around 1.71 m/s^2. Screenshot attached below.
https://drive.google.com/file/d/1AcmIDXsJIbzihH8Nq4mgSn1CofJCkQ6T/view?usp=sharing
I have wondered whether my maths are wrong or whether the problem lies somewhere in the chart. I have two other charts working, displacement and velocity of the pendulum, so I am confused as to why this will not work, especially as the coding behind each different chart is almost identical. I will attach all necessary code beneath.
Private Function CalculateAcceleration(ByVal amplitude As Double, ByVal frequency As Double, ByVal time As Double) As Double
Dim acceleration, maxAcc As Double
maxAcc = sq((2 * PI * frequency)) * amplitude
acceleration = -(maxAcc * Cos(2 * PI * frequency * time))
Return acceleration
End Function
Private Sub CreateAccelerationGraph(ByVal angularSpeed As Double, ByVal amplitude As Double, ByVal timePeriod As Double, ByVal frequency As Double)
If AccelerationChart.Series("Acceleration").Points.Count <> 0 Then
AccelerationChart.Series("Acceleration").Points.Clear()
End If
Dim maxAcceleration As Double = sq(angularSpeed) * amplitude
With AccelerationChart.ChartAreas("Default")
.AxisX.Minimum = 0
.AxisX.Maximum = (1 * timePeriod)
.AxisY.Minimum = -(maxAcceleration)
.AxisY.Maximum = maxAcceleration
End With
Dim increment As Double = (1 * timePeriod) / 100
For time As Double = 0 To (1 * timePeriod) Step increment
Dim xPos, yPos As Double
yPos = CalculateAcceleration(amplitude, frequency, time)
xPos = time
AccelerationChart.Series("Acceleration").Points.AddXY(xPos, yPos)
Dim label As New CalloutAnnotation
With label
AccelerationChart.Annotations.Add(label)
End With
Next
End Sub
Private Sub SetUpAccelerationGraph()
Dim innerPlotPosition As ElementPosition = New ElementPosition(10, 10, 80, 80)
AccelerationChart.Series.Add("Acceleration")
AccelerationChart.ChartAreas.Add("Default")
With AccelerationChart.Series("Acceleration")
.Color = Color.BlueViolet
.BorderWidth() = 5
.ChartType = DataVisualization.Charting.SeriesChartType.Spline
End With
With AccelerationChart.ChartAreas("Default")
.IsSameFontSizeForAllAxes = True
.InnerPlotPosition = innerPlotPosition ' sets the inner plot position
End With
AccelerationChart.ChartAreas.FirstOrDefault.AxisX.LabelStyle.Format = "{0:0.0}" 'sets the format of labels to 1dp
AccelerationChart.ChartAreas.FirstOrDefault.AxisY.LabelStyle.Format = "{0:0.000}"
End Sub
I have a loop that generates rectangles automatically on a visio drawing but I need to have the script to arrange them automatically right know I recorded a macro while I rearranged the rectangles manually under the title box. But my rectangle count change constantly because the results from my if statement change constantly because my data continuously changes. I need for my loop to start drawing them under the title box in columns of six or seven rectangles.
For I = 1 To WS_Count
Set vsoShape =
Application.ActiveWindow.Page.Drop(Application.DefaultRectangleDataObject,
aoffset, boffset)
vsoShape.Text = ActiveWorkbook.Worksheets(I).Name
aoffset = aoffset
boffset = boffset + 0.75
Dev_Count = Dev_Count + 1
ActiveDocument.DiagramServicesEnabled = DiagramServices
Next I
I need to be able to set a starting position to begin dropping the rectangles below the title rectangle creating a new column every six to seven rectangles. Thanks
Increment aOffset every time I is divisible by the number of shapes you want horizontally...
You can do this with the Mod Operator If (iterator Mod runEveryXIterations = 0) Then ...
The example below should clarify the idea, the code is not exactly what you need but you should be able to grasp the idea:
Option Explicit
Public Sub printXY()
xyDistribute 10, 3, 0, 0, 0.75, 1.5
End Sub
Private Function xyDistribute(ByRef iterations As Long, _
ByRef newColAfter As Long, _
ByRef xPosInitial As Double, _
ByRef yPosInitial As Double, _
ByRef xStep As Double, _
ByRef yStep As Double)
Dim iter As Long
Dim xPos As Double
Dim yPos As Double
yPos = yPosInitial
xPos = xPosInitial
Debug.Print "xPos", "yPos"
For iter = 1 To iterations
Debug.Print xPos, yPos
' your code goes here
If (iter Mod newColAfter = 0) Then
yPos = yPos + yStep
xPos = xPosInitial
Else
xPos = xPos + xStep
End If
Next iter
End Function
I'm trying to implement a small tool that draws sin and cos functions. The program is supposed to draw from the center of the form, so that the history will extend to the right. Imagine the following gif but with the right end of the line moving up and down, and the path to the left "showing the trace"
What I would like to do is, every time a timer elapses, draw a point (via Graphics.FillRectangle) in the center of a PictureBox. In the next timer fire move the graphics one pixel to the left, and draw the next pixel. This is what I have so far:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
bmp = New Drawing.Bitmap(PictureBox1.Size.Width, PictureBox1.Size.Height)
g1 = Graphics.FromImage(bmp)
MathTimer = New Timers.Timer(30)
AddHandler MathTimer.Elapsed, AddressOf OnTimedEvent
MathTimer.Enabled = True
MathTimer.Start()
End Sub
Private Sub OnTimedEvent(source As Object, e As System.Timers.ElapsedEventArgs)
g1.FillRectangle(Brushes.Red, PictureBox1.Size.Width \ 2, PictureBox1.Size.Height \ 2, 1, 1)
g1.TranslateTransform(-1, 0)
PictureBox1.Image = bmp
End Sub
However, this doesn't achieve the desired effect, since the canvas of the graphics object g1 is moved to the left with this. Eventually it's not drawing anymore. (No wonder, since with this I'm drawing "with the left end of the line")
Anybody have a better idea that achieves the desired effect?
For i As Integer = 0 To pointsToDraw.Count - 2
Dim p As Point = pointsToDraw(i)
Dim xPos As Integer = (pctrBxSinCosDraw.Width / 2) + p.X - currentTick
e.Graphics.FillRectangle(Brushes.Black, xPos, CInt(p.Y + pctrBxSinCosDraw.Height / 2), 1, 1)
If xPos <= 0 Then
pointsToDraw.RemoveAt(i)
End If
Next
Where currentTick is set by a Timer, which on Tick, calculates the x/y values:
Dim yVal As Double
If useSinCalc Then
yVal = Math.Sin(DegreeToRadian(currentTick)) * (180 / Math.PI)
Else
yVal = Math.Cos(DegreeToRadian(currentTick)) * (180 / Math.PI)
End If
pointsToDraw.Add(New Point(currentTick, yVal))
currentTick += 1
pctrBxSinCosDraw.Invalidate()
And DegreeToRadian simply does (as it states):
Private Function DegreeToRadian(ByVal angle As Double)
DegreeToRadian = Math.PI * angle / 180.0
End Function
And pointsToDraw is List(Of Point)
A sample project can be found on my download page.
I am experimenting on the paint event of VB.Net, and for this experiment I would like to create a repeating horizontal or vertical (depending on the parameter that I inputted) line and loop through until it meets the corresponding end point x and y.
Something like this:
What I'm trying to achieve is given the x and y start point and x and y end point the function should create either vertical or horizontal line that starts with the given start point until it reaches the given end point.
I can create curveline and straightline using the paintevent, but right now I don't have any idea on how to perform looping in the given x and y start point and end point.
You just need to use a For loop to iterate the x/y coordinates. Here's an example:
Public Class Form1
Private Enum Orientation
Vertical
Horizontal
End Enum
Protected Overrides Sub OnPaint(e As PaintEventArgs)
Dim orient As Orientation = Orientation.Vertical
Dim x As Integer = 100 'X Coord
Dim y As Integer = 100 'Y Coord
Dim count As Integer = 10 'Number of Lines to draw
Dim spacing As Integer = 5 'Spacing between lines in pixels
Dim length As Integer = 20 'Length of each line in pixels
Dim thickness As Integer = 3 'Thickness of each line in pixels
drawLines(x, y, orient, count, spacing, length, thickness, e.Graphics)
End Sub
Private Sub drawLines(x As Integer, y As Integer, orient As Orientation, count As Integer, spacing As Integer, length As Integer, thickness As Integer, g As Graphics)
'Create the Pen in a using block so it will be disposed.
'The code uses a red pen, you can use whatever color you want
Using p As New Pen(Brushes.Red, CSng(thickness))
'Here we iterate either the x or y coordinate to draw each
'small segment.
For i As Integer = 0 To count - 1
If orient = Orientation.Horizontal Then
g.DrawLine(p, x + ((thickness + spacing) * i), y, x + ((thickness + spacing) * i), y + length)
Else
g.DrawLine(p, x, y + ((thickness + spacing) * i), x + length, y + ((thickness + spacing) * i))
End If
Next
End Using
End Sub
End Class
Have you tried something like:
For x = xstart to xend Step Spacing
Next
Where:
xstart = your start point
xend = your end point
Spacing = distance between lines
My image is inside the panel, I set up a if-statement for the boundary which it can only be moved. When I tried to run it, it looks crappy when the mouse has panned it outside the boundary. Here is my code for panning:
If (mouse.Button = Windows.Forms.MouseButtons.Left) Then
Dim mousePosNow As Point = mouse.Location
Dim deltaX As Integer = mousePosNow.X - mouseDowns.X
Dim deltaY As Integer = mousePosNow.Y - mouseDowns.Y
Dim newX As Integer
Dim newY As Integer
If PictureBox1.Location.X <= Panel1.Location.X And PictureBox1.Location.Y <= Panel1.Location.Y And _
(PictureBox1.Location.X + PictureBox1.Width) >= (Panel1.Location.X + Panel1.Width) And _
(PictureBox1.Location.Y + PictureBox1.Height) >= (Panel1.Location.Y + Panel1.Height) Then
newX = PictureBox1.Location.X + deltaX
newY = PictureBox1.Location.Y + deltaY
End If
PictureBox1.Location = New Point(newX, newY)
End If
First of all, if you've got your PictureBox inside your Panel, then you don't need to account for the Panel's location, since the PictureBox's location will be zeroed at the top-left of the Panel.
This condition:
If PictureBox.Location.X <= Panel1.Location.X ...
should be changed to this condition:
If PictureBox.Location.X <= 0
Also, the problem you're running into is due to that fact that your event-handler is flipping between moving the PictureBox from 0,0 to moving the PictureBox to the delta location.
E.g:
When you drag the PictureBox towards the right such that it's left boundary goes past the Panel's left boundary (i.e. PictureBox.Location.X > 0) then the condition of your if-statement evaluates to False and the PictureBox's location is set to 0. However, since you've now changed its location, the MouseMove event is triggered again and this time the condition of your if-statement evaluates to True and the PictureBox's location is set to the delta location.
Once again the MouseMove event is triggered and the scenario repeats, flipping the PictureBox's location back and forth, causing a jittering effect.
You can fix this by changing your condition to rely on the new location of the PictureBox, instead of the current location:
This condition:
If PictureBox.Location.X <= 0 ...
should be changed to this condition:
If (PictureBox.Location.X + deltaX) <= 0 ...
This fixes the jittering problem but your code only takes care of the case where the PictureBox is dragged towards the right and bottom.
Instead of writing more conditions, you could simplify your code by moving the calculations into a separate function that handles each axis separately:
If (mouse.Button = Windows.Forms.MouseButtons.Left) Then
Dim mousePosNow As Point = mouse.Location
Dim deltaX As Integer = mousePosNow.X - mouseDowns.X
Dim deltaY As Integer = mousePosNow.Y - mouseDowns.Y
Dim newX As Integer = Clamp(PictureBox1.Location.X + deltaX, PictureBox1.Width, Panel1.Width)
Dim newY As Integer = Clamp(PictureBox1.Location.Y + deltaY, PictureBox1.Height, Panel1.Height)
PictureBox1.Location = New Point(newX, newY)
End If
...
Private Function Clamp(val As Integer, outerBound As Integer, innerBound As Integer) As Integer
Dim newVal As Integer = val
If newVal > 0 Then
newVal = 0
End If
If newVal + outerBound < innerBound Then
newVal = innerBound - outerBound
End If
Return newVal
End Function