evildmp/BrachioGraph

`ValueError: math domain error` triggered in drive_xy

willprice opened this issue · 9 comments

Using the following testing script

from brachiograph import BrachioGraph
from time import sleep

# The servo_x_degree_ms signs need flipping as my motors
# go in the opposite direction to those in the article
plotter = BrachioGraph(
  inner_arm=8.2,
  outer_arm=8.85,
  servo_1_degree_ms=10,
  servo_2_degree_ms=-10,
)
print("Resetting arm position")
plotter.set_angles(angle_1=-90, angle_2=90)
print("Set up complete")

plotter.drive_xy()

and moving around with a,d,l,k I can consistently trigger a math domain error. I think this happens when the hypotenuse becomes very small or perhaps negative?

Here's the stack trace

Traceback (most recent call last):
File "test.py", line 23, in <module>
plotter.drive_xy()
File "/home/pi/branchiograph/brachiograph.py", line 698, in drive_xy
self.xy(self.current_x, self.current_y)
File "/home/pi/branchiograph/brachiograph.py", line 434, in xy
(angle_1, angle_2) = self.xy_to_angles(x, y)
File "/home/pi/branchiograph/brachiograph.py", line 590, in xy_to_angles
(hypotenuse**2+self.INNER_ARM**2-self.OUTER_ARM**2)/(2*hypotenuse*self.INNER_ARM)
ValueError: math domain error

and here are the values printed during driving the plotter

Resetting arm position
Set up complete
-9.2 8.85
-10.2 8.85
-11.2 8.85
-12.2 8.85
-13.2 8.85
-14.2 8.85
-13.2 8.85
-12.2 8.85
-11.2 8.85
-12.2 8.85
-13.2 8.85
-14.2 8.85
-13.2 8.85
-12.2 8.85
-11.2 8.85
-11.2 9.85
-11.2 10.85
-11.2 11.85
-11.2 12.85
-11.2 13.85
moe5k commented

I have the same problem. Did you solved it?

@moe5k, well I think the idea is that you use bg.drive_xy() to figure out the min/max x/y values before this is triggered, although I'm not certain about that. I've not had much success yet, as when I try the bg.box() test, the plotter draws nothing like a box! It's more like two squashed circles.

moe5k commented

@willprice i thought using bg.drive_xy() for define the physical limits of the hardware. At least bg.box() should drive along these points. I got also a similar behavior while starting bg.box().

@moe5k Yes, I think you're probably right. It doesn't seem particularly user friendly for the app to crash, instead, an error should be raised. I've not quite pinned down the cause of the issue. At first, I thought it was when the hypotenuse went to 0 causing a div by 0 error, but I tried to catch this issue but still observed the domain error. I think I need to log the values going to the trigonometric functions as this is where I suspect the exception is being raised.

OK, figured out why this happens. When (hypotenuse**2+self.INNER_ARM**2-self.OUTER_ARM**2)/(2*hypotenuse*self.INNER_ARM) (derived from the cosine rule) is less than -1 or greater than 1 then arccos will throw a math domain error since cos can only produce values in the range [-1, 1]. I need to think about this a bit more, but my intuition is that it's not possible to produce a triangle with its tip at (x, y) given the inner and outer arm lengths.

This looks reasonable to me.

>>> import math
>>> math.sqrt(11.2 **2  + 13.85 **2) < (8.2 + 8.85)
False

The distance to the point is further than arms can reach.

A better message would be useful though.

I've added a bit of code to catch this issue and inform the user.
willprice@c8d764d
In that branch I've turned all the return str messages into exceptions too.

In the better-turtle-graphics branch (in development) we can get useful output like:

Screenshot 2019-12-08 21 38 40

that helps visualise this better.

That looks super useful. Thanks @evildmp, I'll check it out.