Plotter modules¶
Plotter
¶
A base class for the BrachioGraph and PantoGraph subclasses.
This class provides all the interfaces you’ll need to use with the plotter in normal use, with the
exception of __init__()
.
All the classes, including this base class, can be instantiated without any arguments and will work for testing.
For testing with turtle graphics, you will need to use one one of the sublasses.
- Plotter.__init__(virtual: bool = False, turtle: bool = False, turtle_coarseness=None, bounds: tuple = [- 10, 5, 10, 15], servo_1_parked_pw: int = 1500, servo_2_parked_pw: int = 1500, servo_1_degree_ms: float = - 10, servo_2_degree_ms: float = 10, servo_1_parked_angle: float = 0, servo_2_parked_angle: float = 0, hysteresis_correction_1: float = 0, hysteresis_correction_2: float = 0, servo_1_angle_pws: tuple = (), servo_2_angle_pws: tuple = (), servo_1_angle_pws_bidi: tuple = (), servo_2_angle_pws_bidi: tuple = (), pw_up: int = 1500, pw_down: int = 1100, wait: Optional[float] = None, resolution: Optional[float] = None)¶
- Parameters
virtual (bool) – A virtual plotter will run in software only, and doesn’t expect any attached hardware. This allows work and development on a machine other than a Raspberry Pi, and to run automated tests.
turtle (bool) – Produces a graphical representation of the plotter and its behaviour using Python turtle graphics, as well as or instead of a physical plotter.
turtle_coarseness (float or None) – For use with
turtle
; a factor, in degrees, to represent the resolution of the servos by rounding values. Defaults to 1˚ if not specified.bounds (list or tuple) – Four numbers, indicating the area that the plotter should treat as its available area for drawing in. The numbers represent, in order the left, top, right and bottom boundaries. Defaults to usable values in the default subclass definitions.
servo_1_parked_pw (int) – The pulse-width of servo 1 when parked.
servo_2_parked_pw (int) – The pulse-width of servo 2 when parked.
servo_1_degree_ms (float) – Milliseconds pulse-width difference per degree of movement.
servo_2_degree_ms (float) – Milliseconds pulse-width difference per degree of movement.
servo_1_parked_angle (float) – The arm angle in the parked position.
servo_2_parked_angle (float) – The arm angle in the parked position.
hysteresis_correction_1 (float) – Servo 1 hysteresis error compensation.
hysteresis_correction_2 (float) – Servo 2 hysteresis error compensation.
servo_1_angle_pws (tuple) – Pulse-widths for various angles of servo 1.
servo_2_angle_pws (tuple) – Pulse-widths for various angles of servo 2.
servo_1_angle_pws_bidi (tuple) – Pulse-widths for various angles of servo 1, collected in both clockwise and anti-clockwise directions. This is introduced in the tutorial.
servo_2_angle_pws_bidi (tuple) – Pulse-widths for various angles of servo 2, collected in both clockwise and anti-clockwise directions.
pw_up (int) – The pulse-width for the pen’s up position.
pw_down (int) – The pulse-width for the pen’s down position.
wait (float or None) – A time in seconds that the plotter will rest after making a movement. If not specified, defaults to 0.1, or 0 for a virtual-only plotter.
resolution (float or None) – A distance in centimetres. When drawing between two points, any line longer than
resolution
will be broken down into a series of points no more thanresolution
apart. This allows the plotter to approximate straight lines by drawing a series of shorter curved lines (all the lines the plotter naturally draws are curved). If not specified, defaults to 1.
In all the methods below, arguments that are also atrributes of the plotter class need only be used to override those values (which is generally not required).
Plotting¶
- Plotter.plot_file(filename='', wait=None, resolution=None, bounds=None)¶
Plots and image encoded as JSON lines in
filename
. Passes the lines in the supplied JSON file toplot_lines()
.
- Plotter.plot_lines(lines=[], wait=None, resolution=None, rotate=False, flip=False, bounds=None)¶
Passes each segment of each line in lines to
draw_line()
Drawing according to x/y values¶
- Plotter.box(bounds=None, wait=None, resolution=None, repeat=1, reverse=False)¶
Draw a box marked out by the
bounds
.
- Plotter.test_pattern(bounds=None, lines=4, wait=None, resolution=None, repeat=1, reverse=False, both=False)¶
- Plotter.vertical_lines(bounds=None, lines=4, wait=None, resolution=None, repeat=1, reverse=False, both=False)¶
- Plotter.horizontal_lines(bounds=None, lines=4, wait=None, resolution=None, repeat=1, reverse=False, both=False)¶
- Plotter.draw_line(start=(0, 0), end=(0, 0), wait=None, resolution=None, both=False)¶
Draws a line between two points
- Plotter.xy(x=None, y=None, wait=None, resolution=None, draw=False)¶
Moves the pen to the xy position; optionally draws while doing it.
None
for x or y means that the pen will not be moved in that dimension.
Drawing according to servo angle values¶
- Plotter.move_angles(angle_1=None, angle_2=None, wait=None, resolution=None, draw=False)¶
Moves the servo motors to the specified angles step-by-step, calling
set_angles()
for each step.resolution
refers to degrees of movement.None
for one of the angles means that that servo will not move.
Pen-moving methods¶
- Plotter.set_angles(angle_1=None, angle_2=None)¶
Moves the servo motors to the specified angles immediately. Relies upon getting accurate pulse-width values.
None
for one of the angles means that that servo will not move.Calls
set_pulse_widths()
.Sets
current_x
,current_y
.
- Plotter.park()¶
Park the plotter.
Angles to pulse widths¶
A plotter needs to move its arms to the correct angles, by providing the appropriate pulse-width to each servo.
- Plotter.angles_to_pw_1()¶
- Plotter.angles_to_pw_2()¶
These methods - one for each servo - take the angle as an argument and return a pulse-width.
The methods themselves stand in for functions that do the actual calculation; which function is
assigned to the angles_to_pw_1
/angles_to_pw_2
attributes depends upon how much
information is provided about the servos when the plotter is initialised.
Naive calculation¶
The default is to use “naive” functions (naive_angles_to_pulse_widths_1
and
naive_angles_to_pulse_widths_2
), that assume linearity (1˚ of movement corresponds to a 10µs
change in pulse-width), will be used.
- Plotter.naive_angles_to_pulse_widths_1(angle)¶
A rule-of-thumb calculation of pulse-width for the desired servo angle
- Plotter.naive_angles_to_pulse_widths_2(angle)¶
A rule-of-thumb calculation of pulse-width for the desired servo angle
Sophisticated calculation¶
In practice the response of servos is not linear. If a series of
pulse-width/angle values are supplied, then numpy (numpy.poly1d(numpy.polyfit))
will provide a
polynomial funtion that matches the curve corresponding to those values.
Line processing¶
- Plotter.analyse_lines(lines=[], rotate=False, bounds=None)¶
Analyses the co-ordinates in
lines
, and returns:rotate
:True
if the image needs to be rotated by 90˚ in order to fit betterx_mid_point
,y_mid_point
: mid-points of the imagebox_x_mid_point
,box_y_mid_point
: mid-points of thebounds
divider
: the value by which we must divide all x and y so that they will fit safely inside the bounds.
lines
is a tuple itself containing a number of tuples, each of which contains a number of 2-tuples:[ [ [3, 4], # | [2, 4], # | [1, 5], # a single point in a line # | a list of points defining a line [3, 5], # | [3, 7], # | ], [ # all the lines [...], [...], ], [ [...], [...], ], ]
- Plotter.rotate_and_scale_lines(lines=[], rotate=False, flip=False, bounds=None)¶
Rotates and scales the lines so that they best fit the available drawing
bounds
.
Physical control¶
- Plotter.set_pulse_widths(pw_1=None, pw_2=None)¶
Applies the supplied pulse-width values to the servos, or pretends to, if we’re in virtual mode.
- Plotter.get_pulse_widths()¶
Returns the actual pulse-widths values; if in virtual mode, returns the nominal values - i.e. the values that they might be.
- Plotter.quiet(servos=[14, 15, 18])¶
Stop sending pulses to the servos, so that they are no longer energised (and so that they stop buzzing).
Manual driving¶
- Plotter.drive()¶
Control the pulse-widths using the keyboard.
The controls are:
Exit
-10 µs
-1 µs
+ 10 µs
+ 1 µs
0
Servo 1
a
A
s
S
Servo 2
k
K
l
L
- Plotter.drive_xy()¶
Control the x/y position using the keyboard.
The controls are:
Exit
-1 cm
-1 mm
+ 1 cm
+ 1 mm
0
Servo 1
a
A
s
S
Servo 2
k
K
l
L
Reporting¶
- Plotter.status()¶
Provides a report of the plotter status. Subclasses should override this to report on their own status.
Trigonometry¶
- Plotter.xy_to_angles(x=0, y=0)¶
Return the servo angles required to reach any x/y position. This is a dummy method in the base class; it needs to be overridden in a sub-class implementation.
- Plotter.angles_to_xy(angle_1, angle_2)¶
Return the servo angles required to reach any x/y position. This is a dummy method in the base class; it needs to be overridden in a sub-class implementation.
BrachioGraph
¶
- BrachioGraph.__init__(virtual: bool = False, turtle: bool = False, turtle_coarseness=None, bounds: tuple = [- 8, 4, 6, 13], inner_arm: float = 8, outer_arm: float = 8, servo_1_parked_pw: int = 1500, servo_2_parked_pw: int = 1500, servo_1_degree_ms: int = - 10, servo_2_degree_ms: int = 10, servo_1_parked_angle: int = - 90, servo_2_parked_angle: int = 90, hysteresis_correction_1: int = 0, hysteresis_correction_2: int = 0, servo_1_angle_pws: tuple = [], servo_2_angle_pws: tuple = [], servo_1_angle_pws_bidi: tuple = [], servo_2_angle_pws_bidi: tuple = [], pw_up: int = 1500, pw_down: int = 1100, wait: Optional[float] = None, resolution: Optional[float] = None)¶
Parameters are as for the
Plotter
parent class, except for: