Plotter modules#

class Plotter#

Plotter#

A base class for the BrachioGraph and PantoGraph subclasses.

This class provides all the interfaces you’ll need for the plotter in normal use.

All the classes (Plotter, BrachioGraph and PantoGraph) can be instantiated without any arguments and will work for testing.

For testing with turtle graphics, you will need to use one of the subclasses.

Methods in general#

Hierarchy#

This table shows how the methods in the class Plotter depend on each other. Each named method calls the method in the cell below it.

Order of arguments#

Many of these methods accept a similar set of arguments. For consistency, they always appear in the same order. Note that not all are available to all methods. They are:

  • what to draw: filename or lines

  • where to draw:bounds

  • precision and speed: angular_step, wait, resolution

  • whether to draw: draw

  • direction and repetition: repeat, reverse, both

  • image transformations: flip, rotate

Initialisation#

Plotter.__init__(virtual=False, turtle=False, turtle_coarseness=None, bounds=[-10, 5, 10, 15], servo_1_parked_pw=1500, servo_2_parked_pw=1500, servo_1_degree_ms=-10, servo_2_degree_ms=10, servo_1_parked_angle=0, servo_2_parked_angle=0, hysteresis_correction_1=0, hysteresis_correction_2=0, servo_1_angle_pws=(), servo_2_angle_pws=(), servo_1_angle_pws_bidi=(), servo_2_angle_pws_bidi=(), pw_up=None, pw_down=None, angular_step=None, wait=None, resolution=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 – 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 (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) – A time in seconds that the plotter will rest after making a movement. If not specified, will be initialised as 0.01, or 0 for a virtual-only plotter.

  • angular_step (float) – An angle in degrees that determines how big each discrete step in a series of movements of the arm servos will be. If not specified, will be initialised as 0.1.

  • resolution (float) – 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 than resolution cm 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, will be initialised as 0.1.

In all the methods below, arguments that are also attributes of the plotter class need only be used to override those values (which is generally not required).

Image-plotting methods#

These methods draw an image (as a series of lines, encoded in JSON).

Plotter.plot_file(filename='', bounds=None, angular_step=None, wait=None, resolution=None)#

Plots and image encoded as JSON lines in filename. Passes the lines in the supplied JSON file to plot_lines().

Plotter.plot_lines(lines=[], bounds=None, angular_step=None, wait=None, resolution=None, flip=False, rotate=False)#

Passes each segment of each line in lines to draw_line()

Pattern-drawing methods#

Plotter.box(bounds=None, angular_step=None, wait=None, resolution=None, repeat=1, reverse=False)#

Draw a box marked out by the bounds.

Plotter.test_pattern(lines=4, bounds=None, angular_step=None, wait=None, resolution=None, repeat=1, reverse=False, both=False)#
Plotter.vertical_lines(lines=4, bounds=None, angular_step=None, wait=None, resolution=None, repeat=1, reverse=False, both=False)#
Plotter.horizontal_lines(lines=4, bounds=None, angular_step=None, wait=None, resolution=None, repeat=1, reverse=False, both=False)#
Plotter.draw_line(start=(0, 0), end=(0, 0), angular_step=None, wait=None, resolution=None, both=False)#

Draws a line between two points

Drawing methods using x/y co-ordinates#

All of the methods above call xy:

Plotter.xy(x=None, y=None, angular_step=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 methods using servo angle values#

Plotter.move_angles(angle_1=None, angle_2=None, angular_step=None, wait=None, draw=False)#

Moves the servo motors to the specified angles step-by-step, calling set_angles() for each step. 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()#

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 better

  • x_mid_point, y_mid_point: mid-points of the image

  • box_x_mid_point, box_y_mid_point: mid-points of the bounds

  • 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).

Calibration and manual driving#

Plotter.capture_pws()#

Helps capture angle/pulse-width data for the servos, as a dictionary to be used in a Plotter definition.

The controls are:

-10 µs

-1 µs

+ 10 µs

+ 1 µs

Shoulder

a

A

s

S

Elbow

k

K

l

L

Pen

z

x

Capture pulse-width value

c

Show captured values

v

Exit

0

Reporting methods#

Plotter.status()#

Provides a report of the plotter status. Subclasses should override this to report on their own status.

Trigonometric methods#

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#

class BrachioGraph#
__init__(virtual=False, turtle=False, turtle_coarseness=None, bounds=[-8, 4, 6, 13], inner_arm=8, outer_arm=8, servo_1_parked_pw=1500, servo_2_parked_pw=1500, servo_1_degree_ms=-10, servo_2_degree_ms=10, servo_1_parked_angle=-90, servo_2_parked_angle=90, hysteresis_correction_1=0, hysteresis_correction_2=0, servo_1_angle_pws=[], servo_2_angle_pws=[], servo_1_angle_pws_bidi=[], servo_2_angle_pws_bidi=[], pw_up=1500, pw_down=1100, wait=None, angular_step=None, resolution=None)#

Parameters are as for the Plotter parent class, except for:

Parameters
  • inner_arm (float) – The length of the inner arm, in cm.

  • outer_arm (float) – The length of the outer arm, in cm.

  • virtual (bool) –

  • turtle (bool) –

  • bounds (tuple) –

  • servo_1_parked_pw (int) –

  • servo_2_parked_pw (int) –

  • servo_1_degree_ms (int) –

  • servo_2_degree_ms (int) –

  • servo_1_parked_angle (int) –

  • servo_2_parked_angle (int) –

  • hysteresis_correction_1 (int) –

  • hysteresis_correction_2 (int) –

  • 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) –

  • pw_down (int) –

  • wait (float) –

  • angular_step (float) –

  • resolution (float) –