How to adjust for slow sampling when animating - python

I am getting input from a serial interface, and I am using it to move up to three digital needles. I have three gauges, and I can sample at a rate of around 10 Hz regularly, shared between all the gauges. When I'm only running one gauge, the needle moves very smoothly with a 10Hz input, but as soon as I add another one, the needle gets rather jumpy.
I need to figure out a way to smooth out the data so my needle doesn't jump so much when the sample rate slows down.
I am using the following code to change the angle of the needle in a more realistic manner:
def move(self):
self.real_angle = self.real_angle + ((360 - self.value * 270/120) - self.real_angle) * 0.2
needle_angle = self.real_angle
if needle_angle > 360:
needle_angle = 360
self.needle.angle = needle_angle
self.real_angle is the angle the needle is actually at (it may be above 360 degrees). self.value is the value returned from the serial port. self.needle_angle isn't really important here. I simply use that to set my angle, because the real angle may be higher than 360 degrees.
This code gives a more realistic analogue style motion, and looks great at high sample rates, but I'm trying to figure out how to make the needle move more smoothly.
Any advice would be appreciated.
Thanks.

Related

Is there a way to change my ODE inside ODEINT in Python?

I'm trying to simulate an oscillatory system with a little difference: I want it to use a specific equation of motion (an O.D.E.) when the body is moving up, and another equation of motion when the body is moving down. To solve these equations, I'm using the ODEINT from Scypi.
For example, let's consider the classic mass-spring system. I've tried to induce the system to work with the equation of motion for external excitations on the body when it's moving up, and with the simple equation when it's moving down.
def function (x,t):
F0 = 10.00
w = 1.00
m = 2.00
c = 1.00
k = 20.00
s = x[0]
dsdt = x[1]
if x[1] >= 0:
d2sdt2 = (F0*np.sin(w*t)-c*dsdt-k*s)/m
else:
d2sdt2 = (-c*dsdt-k*s)/m
result = [dsdt,d2sdt2]
return result
initial = [3.00,0.00]
t = np.linspace(0.00,10.00,101)
y = odeint(function, initial, t)
The results obtained show that only the second equation of motion is working on the body (Results Obtained). I was expecting a more chaotic pattern of movement when the body is moving up, due the external force.
Is there a better way to implement this?
Just adding some parameters to increase the density of internal steps and the density of the output
t = np.linspace(0.00,10.00,301)
y = odeint(function, initial, t, hmax=0.1, atol = 1e-8, rtol=1e-10)
and not using dashed lines gives the plot
where the kinks in the second half are clearly visible.
So your code is correct, but you need to take into account that the ODE solvers are constructed for smooth right sides, starting with the mathematical methods and up to and especially for the predictors for the optimal step size. As here there are locally unpredictable non-smooth changes in the right side, one has to tell the solver not to use very large step sizes as that may jump over a change of the model phase and back.
As an aside, for smooth plots an output density of 100 points on the whole horizontal axis is usually too small. Use 200 to 600 points depending on the final "in-print" size of the plot.

Implementing collision detection on a curve in pygame

I have wrote a class in python that will randomly generate a line with a curve at the end. I've added movement to the class using an two variable: xChange and yChange. I've tried to add collision detection to the curve by calculating a y value (testY) which I got by rearranging the equation of an ellipse ((x-h)^2/a^2 + (y-k)^2/b^2 = 1).
if playerC[0] >= self.x1 and playerC[0] <= self.x4:
#Tests if the player coords are the same as the curves Y
testY = self.k + (self.b*math.sqrt(self.a**2-self.h**2+2*self.h*playerC[0]-playerC[0]**2))/self.a
pygame.draw.line(gameDisplay, WHITE, [0, testY ], [1000, testY])
playerC[0] is the fixed x-coordinate I need collision detection on.
I've tried using the same code on another project and it worked fine, however using it in this format seems to break it.
self.h and self.k are the only variables which will change.
This answer is based on the idea that the curve is some randomly curving line-section.
I would first calculate a bounding box for the curve, using this to initially perform a fast and efficient does-it-collide-at-all test. If the player does not collide with a bounding box, no further tests are needed.
Now it's been determined that playerC is in the vicinity of the curve, the code can do further tests. Depending on the size / complexity / shape of your curve, it may be efficient to split the line into N sub-lines (say 8?), and then bounding-box test those sections against your player. If high accuracy is needed, then further test against the pixels (or further sub-sub-lines) of the curve.
This type of splitting and testing is often implemented with a quadtree data structure. Using a quadtree mimics the process above. It quickly finds the relevant part of the collision test, so that means the code is not spending a huge amount of time processing thousands of points.
Failing all this, generate the list of points for the line, and test these (plus th e movement offset) against the player's bounding box.

How to determine which direction a machine is moving given all its positional data using Python?

Question Explanation:
I have a fairly large file(up to 1 million lines) containing data from a mining machine. The data includes the X and Y coordinates of the machine. The machine should only be able to mine to the left (-X direction) or to the right (+X direction) and this occurs in a 'lane'. It should not be able to mine the same area again, but it can change Y-coordinates at any time(i.e, it can change lanes at any time) and skip certain areas. I want to determine in which direction the machine is moving at a given time. For example I want to see where the machine is moving to the left or to the right. Although this sounds simple enough, I am having a bit of trouble.
My attempt:
I am currently finding the sum of 100 values of the difference array of the X-coordinates (the difference array gives the distance the machine has moved). Ideally, if the X-coordinates are decreasing in a 100 point interval, then it should be moving to the left, and if the X-coordinates were increasing in that interval, it should be moving to the right. So if the sum was negative, it will append "0" to an array 100 times. I chose 100 because I thought it would be fairly accurate considering the high number of samples.
# miner_x = array of the x-coordinates of the machine
# miner_x_diff = numpy.diff(miner_x)
# 0 = Negative X direction
# 1 = Positive X direction
diffSample= [sum(miner_x_diff[i:i+100]) for i in range(0, len(miner_x_diff), 100)]
Direction = np.array([])
for value in diffSample:
if value < 0:
for _ in itertools.repeat(None, 100):
Direction = np.append(Direction,0)
else:
for _ in itertools.repeat(None, 100):
Direction = np.append(Direction,1)
# correct size
if Direction.size > miner_x.size:
Direction = Direction[0:Direction.size-(Direction.size-miner_x.size)]
The problem I am having is at the point it changes direction from left to right or vice versa, and this mostly occurs during a lane change. This if course expected, since the sum around that area could be close to 0. But I am not sure how to go about accounting for this change using my current method. Is there a more accurate way to determine the direction of the machine?
An example of a plot of the machines position using one particular data set is shown below (code omitted because it is unnecessary). The yellow points are positions where I had incorrect results occur .In this example there shouldn't be any yellow points. I have not shown the direction color in this example but you can of course see it if I say the start position is at the bottom right.
I will appreciate any help/advice/hints :)
Edit:
Here is a picture of what I want (mind the Paint skills). Note that I don't want to 'see' a straight line, just to know that it is moving in those directions at those points. Green is the start point.
Start by using a smaller sample window. How much time is represented by 100 sample points? The interval should be small enough that you can simply look at the displacement between the initial and final position and have a direction for that time segment. (You do realize that summing the difference of consecutive pairs of numbers is the same as subtracting the first from the last number, don't you?) If you catch the machine at a point where it's turning around, you may get indeterminate data but the samples before and after will show a straight vector.

How do I create a spiral off-shoot from my larger spiral?

I'm coding in python. I've created a spiral with my current for loop below. I want to create tiny spiral off-shoots around every 10 degrees. The problem is I don't know how to do that. I'm trying to do something like this:
My question is how would I create the off-shoots? Is this even possible or not?
Any suggestions are greatly appreciated.
import turtle
import math
me = turtle.Turtle()
def miniSpiral():
for i in range(0,360):
x,y = me.position()
me.left(1)
if x%10==0:
x2,y2 = me.forward(((5*i)+5)*math.pi/360)
else:
x2,y2= me.forward(5*math.pi/360)
me.goto((x+x2),(y+y2))
for x2 in range(0,720):
me.left(1)
if x2%10==0:
me.forward(((10*x2)+10)*math.pi/360)
#miniSpiral()
me.forward(10*math.pi/360)
In general, the easiest way to draw a fractal programmatically is to use recursion. Start with the code to draw one "segment" of your fractal. In the image you linked to, that would be one 90-degree piece of the spiral (since that's the distance between the branchings).
Once you have code that can draw one segment, you add a recursive call to it. Give it some parameter (e.g. the initial size), and have the recursive call reduce the value passed on to the next call. Add a base case where a call with that parameter set too small gets skipped (e.g. if size < 1: return) so that the recursion doesn't go on forever.
Now you can add branching. Instead of just one recursive call, make two. You'll need to add some extra logic to move the position of the turtle in between the calls (so the second one starts at roughly the same spot as the first), but that shouldn't be too hard. To make the two branches distinct, vary their initial position or angle, or give them different parameters. In your example image, the "extra" branches all start going the oposite direction from the "main" branch, and they start smaller.
Here's a pseudo-code implementation of the spiral you want (I'm not adding actual turtle code because you seem to be using a different turtle module than the one I have from the standard library):
def spiral(size):
if size < 1: return # base case
draw_segment(size) # this draws a 90 degree piece of the spiral
position = getposition() # save state so we can back up after the first recursive call
angle = getangle()
spiral(size - 1) # main branch of the recursion
penup() # restore state (mostly)
setposition(position)
pendown()
setangle(angle + 180) # we want to start facing the other direction for the second branch
spiral(size - 2) # extra branch of the recursion
You can play around with the details (like how you modify the size for the recursive calls) to suit your tastes or the fractal design you're looking for. For instance, you could multiply the size by some factor (e.g. size * 0.75) rather than subtracting a fixed amount.
Each mini spiral is just a smaller version of the original spiral, as such you can carefully call the original function to make each smaller one.
import turtle
import math
def spiral(steps, left, forward, level):
for step in range(50, steps):
me.left(left)
me.forward(step / forward)
if step % 200 == 0 and level > 0:
x, y = me.position()
heading = me.heading()
spiral((steps * 2) / 3, -left * 1.2, forward * 1.2, level - 1)
me.up()
me.setpos(x, y)
me.setheading(heading)
me.down()
#turtle.tracer(5, 200) # to speed things up
me = turtle.Turtle()
spiral(800, 0.6, 200.0, 4)
Each time spiral() is called, the arguments are modified slightly and the level is reduced by 1. Also the left argument is made negative which has the effect of changing the direction for each sub spiral. Each sub spiral is called after 200 steps. Each sub spiral has 2/3rds of the original steps and so on.... lots of numbers to play with to see how they effect the outcome. When the sub spiral finishes drawing, it jumps back to the point where it started drawing it an continues with the original spiral.
Calling it with a level of 0 would give you a single simple spiral for example.
This would give you the following type of output:

Zoom function in python? (2.7.9)

I'm currently working with the turtle library of python.
I'm working on my midterm project for my coding class and my project is to draw cos, sin, and tangent curves using turtle as well as their inverse functions.
My problem is that when I'm coding inverse sin, the graph shows up way too small and is impossible to be seen by the user. I was wondering if there was a zoom function or a way to stretch the graph to make it bigger?
Here is my code for arcsin:
def drawarcsincurve(amplitude, period, horShift, verShift):
turtle.speed(0)
startPoint = -1
turtle.goto(startPoint, math.asin(startPoint))
turtle.pendown()
for angles in range(-1,1):
y = math.asin(angles)
turtle.goto(angles,y)
Your main problem here, I think, is the range over which your are iterating your angles variable. The line
for angles in range(-1,1):
will execute the loop only twice, with angle == 1 and angle == 0 - i.e. it is equivalent to using
for angles in [-1,0]:
Type range(-1,1) in a Python interpreter window to see what I mean.
You might be getting confused over names as well. You call your loop variable angles, but it's actually representing a ratio (the sine value whose inverse you are calculating).
What you probably want really is something that iterates over the range -1 to 1 in fairly small steps. Lets choose 0.01 as our step (that's arbitrary)
I've altered your code directly rather than doing my own implementation.
I've put in a scale factor (plot_scale) which is equivalent to the zoom that I think you want in your original question.
I've left your original function arguments in, although I don't use them. I thought you might want to play with them later.
def drawarcsincurve(amplitude, period, horShift, verShift):
plot_scale = 100 # Arbitrary value - up to you - similar to "zoom"
turtle.speed(1)
turtle.penup()
startPoint = -1
turtle.goto(plot_scale*startPoint, plot_scale*math.asin(startPoint))
turtle.pendown()
for angles in range(-100,100):
sinval = 1.0 * angles / 100 # this will run -1 to 1 in 0.01 steps
y = math.asin(sinval)
turtle.goto(plot_scale*sinval,plot_scale*y)
This outputs:

Categories

Resources