Update:
I am addressing one of @KeithW’s major comments with the code - everything was named the same and following the variables through the code was like listening to Abbot and Costello’s Who’s On First?
What I did:
- Created a global “dictionary” structure that has a “slot” for all the robot’s functional attributes, (variables), and puts them all in one place where they can be referenced. Note that this is not just the data returned from the browser, but also includes other essential data, (such as calculated speeds), that are used virtually everywhere.
Viz.:
robot = {
"controller_status": 'Disconnected',
"motion_state": 'Waiting for Joystick',
"direction": 'None',
"time_stamp": 0, # a large integer that, (sometimes), becomes a float (shrug shoulders)
"x_axis": 0.00, # x-axis < 0, joystick pushed left - x-axis > 0, joystick pushed right
"y_axis": 0.00, # y-axis < 0, joystick pushed forward - y-axis > 0 , joystick pullled back
"head_x_axis": 0.00, # head x and y axes mirror the joystick x and y axes
"head_y_axis": 0.00, # if the pinky-switch, (head motion enable), is pressed
"force": 0.00, # force is the absolute value of the y-axis deflection
"trigger_1": 0, # Partial primary trigger press (motion enabled)
"trigger_2": 0, # Full primary trigger press (faster speed - not yet implemented)
"head_enable": 0,
"normal_speed": 150, # Max speed if the trigger is pressed half-way
"turbo_speed": 300, # Max speed if the trigger is fully pressed
"speed": 0, # this represents the currently selected maximum, either normal or turbo speed
"desired_speed": 0, # This is the adjusted speed based on joystick force.
"vcenter": 92,
"hcenter": 97,
"vposition": 92, # The "calibrated" positions for Charlie's head
"hposition": 97 # to be centered in both axes.
}
. . . and so a robot variable used outside a method, or referencing a global attribute is now referenced as robot["variable"]
and any local reference, (i.e. inside a routine that calculates something), is given the variable’s “descriptive” name.
Viz.:
In the place where I capture the data sent back from the browser, (args), I capture that data into the “global” data structure.
robot["controller_status"] = str(args['controller_status'])
robot["motion_state"] = str(args['motion_state'])
robot["direction"] = str(args['angle_dir'])
robot["time_stamp"] = int(args['time_stamp'])
robot["x_axis"] = float(args['x_axis'])
robot["y_axis"] = float(args['y_axis'])
robot["head_x_axis"] = float(args['head_x_axis'])
robot["head_y_axis"] = float(args['head_y_axis'])
robot["force"] = float(args['force'])
robot["trigger_1"] = int(args['trigger_1'])
robot["trigger_2"] = int(args['trigger_2'])
robot["head_enable"] = int(args['head_enable'])
However, where I do a calculation, I use the “descriptive” variable name within the function, which should be local in scope.
Code in the “main” process commands function, (the top-level function that actually moves the robot around), uses the robot[“variable”] construction to use the “global” values returned from the browser or calculated in vivo.
if robot["x_axis"] < 0: # Moving backward to the left
# Moving to the left, the left wheel must be moving slower than
# the right wheel by some percentage.
robot["desired_speed"] = int(calc_desired_speed(robot["speed"], robot["force"]))
robot["differential_speed"] = int(calculate_differential_speed(robot["desired_speed"], robot["x_axis"]))
my_gopigo3.set_motor_dps(my_gopigo3.MOTOR_RIGHT, -robot["desired_speed"])
my_gopigo3.set_motor_dps(my_gopigo3.MOTOR_LEFT, -robot["differential_speed"])
print("moving backward to the left\n")
The calculations themselves are done in local scope
def calc_desired_speed(speed, force):
desired_speed = int(round_up(speed * force))
if desired_speed > speed:
desired_speed = speed
# print\("calc_desired_speed: speed =", speed, "force =", force, "desired_speed =", desired_speed)
return (desired_speed)
def calculate_differential_speed(desired_speed, x_axis):
differential_speed = int(round_up(desired_speed - abs(desired_speed * x_axis)))
if differential_speed > desired_speed:
differential_speed = desired_speed
# print\("calculate_differential_speed: desired_speed =", desired_speed, "robot["x_axis"] =", robot["x_axis"], "differential_speed =", differential_speed)
return (differential_speed)
def round_up(x):
x = 100 * x # This will allow two decimal digits of precision ranging from zero to one.
if x > 0:
return (int(x + 0.5) / 100)
elif x < 0:
return (int(x - 0.5) / 100)
else:
return(0)
This should help solve some of the scoping and variable confusion that Keith noticed.
IMHO, cleaning up variable naming and scope is the first step toward actually “chunking” the program and turning it into scalable modules.
Another interesting thing:
Along with learning how to use Python dictionaries, I discovered an interesting thing with Chrome.
- If you have the developer window open, (F12), and the focus is within the developer window, pressing the “up” arrow brings you a message asking you to “accept the EULA”. (Easter Egg? Maybe not.)