175 lines
5.2 KiB
Python
175 lines
5.2 KiB
Python
# Arlo Robot Controller
|
|
|
|
from time import sleep
|
|
import serial
|
|
|
|
class Robot(object):
|
|
"""
|
|
Defines the Arlo robot API
|
|
|
|
DISCLAIMER: This code does not contain error checking - it is the
|
|
responsibility of the caller to ensure proper parameters and not to send
|
|
commands to the Arduino too frequently (give it time to process the command
|
|
by adding a short sleep wait statement). Failure to do some may lead to
|
|
strange robot behaviour.
|
|
|
|
In case you experience trouble - consider using only commands that do not
|
|
use the wheel encoders.
|
|
"""
|
|
def __init__(self, port = '/dev/ttyACM0'):
|
|
"""
|
|
The constructor port parameter can be changed from default value if you
|
|
want to control the robot directly from your labtop (instead of from
|
|
the on-board raspberry pi). The value of port should point to the USB
|
|
port on which the robot Arduino is connected.
|
|
"""
|
|
self.port = port
|
|
|
|
# 1 sec. timeout, wait until data is received or until timeout
|
|
# self.serialRead = serial.Serial(self.port,9600, timeout=1)
|
|
|
|
# No timeout, wait forever or until data is received
|
|
self.serial_read = serial.Serial(self.port,9600, timeout=None)
|
|
|
|
# Wait if serial port is not open yet
|
|
while not self.serial_read.isOpen():
|
|
sleep(1)
|
|
|
|
print("Waiting for serial port connection ...")
|
|
sleep(2)
|
|
|
|
print("Running ...")
|
|
|
|
def __del__(self):
|
|
print("Shutting down the robot ...")
|
|
|
|
sleep(0.05)
|
|
print(self.stop())
|
|
sleep(0.1)
|
|
|
|
cmd='k\n'
|
|
print((self.send_command(cmd)))
|
|
self.serial_read.close()
|
|
|
|
def send_command(self, cmd, sleep_ms=0.0):
|
|
"""Sends a command to the Arduino robot controller"""
|
|
self.serial_read.write(cmd.encode('ascii'))
|
|
sleep(sleep_ms)
|
|
str_val=self.serial_read.readline()
|
|
return str_val
|
|
|
|
|
|
def _power_checker(self, power):
|
|
"""
|
|
Checks if a power value is in the set {0, [40;127]}.
|
|
This is an internal utility function.
|
|
"""
|
|
return (power == 0) or (power >=40 and power <=127)
|
|
|
|
def go_diff(self, power_left, power_right, dir_left, dir_right):
|
|
"""
|
|
Start left motor with motor power powerLeft (in {0, [40;127]} and the
|
|
numbers must be integer) and direction dirLeft (0=reverse, 1=forward)
|
|
and right motor with motor power powerRight (in {0, [40;127]} and the
|
|
numbers must be integer) and direction dirRight (0=reverse, 1=forward).
|
|
|
|
The Arlo robot may blow a fuse if you run the motors at less than 40 in
|
|
motor power, therefore choose either power = 0 or 40 < power <= 127.
|
|
|
|
This does NOT use wheel encoders.
|
|
"""
|
|
|
|
if (
|
|
(not self._power_checker(power_left)) or
|
|
(not self._power_checker(power_right))
|
|
):
|
|
print("WARNING: Read the docstring of Robot.go_diff()!")
|
|
return ""
|
|
else:
|
|
pl_int = int(power_left)
|
|
pr_int = int(power_right)
|
|
dl_int = int(dir_left)
|
|
dr_int = int(dir_right)
|
|
cmd = f"d{pl_int},{pr_int},{dl_int},{dr_int}\n"
|
|
return self.send_command(cmd)
|
|
|
|
|
|
def stop(self):
|
|
"""
|
|
Send a stop command to stop motors. Sets the motor power on both wheels
|
|
to zero.
|
|
|
|
This does NOT use wheel encoders.
|
|
"""
|
|
cmd='s\n'
|
|
return self.send_command(cmd)
|
|
|
|
|
|
|
|
def read_sensor(self, sensorid):
|
|
"""
|
|
Send a read sensor command with sensorid and return sensor value.
|
|
|
|
Will return -1, if error occurs.
|
|
"""
|
|
cmd=str(sensorid) + '\n'
|
|
str_val=self.send_command(cmd)
|
|
if len(str_val) > 0:
|
|
return int(str_val)
|
|
else:
|
|
return -1
|
|
|
|
def read_front_ping_sensor(self):
|
|
"""
|
|
Read the front sonar ping sensor and return the measured range in
|
|
milimeters [mm].
|
|
"""
|
|
return self.read_sensor(0)
|
|
|
|
def read_back_ping_sensor(self):
|
|
"""
|
|
Read the back sonar ping sensor and return the measured range in
|
|
milimeters [mm].
|
|
"""
|
|
return self.read_sensor(1)
|
|
|
|
def read_left_ping_sensor(self):
|
|
"""
|
|
Read the left sonar ping sensor and return the measured range in
|
|
milimeters [mm].
|
|
"""
|
|
return self.read_sensor(2)
|
|
|
|
def read_right_ping_sensor(self):
|
|
"""
|
|
Read the right sonar ping sensor and return the measured range in
|
|
milimeters [mm].
|
|
"""
|
|
return self.read_sensor(3)
|
|
|
|
|
|
def read_left_wheel_encoder(self):
|
|
"""
|
|
Reads the left wheel encoder counts since last reset_encoder_counts
|
|
command.
|
|
|
|
The encoder has 144 counts for one complete wheel revolution.
|
|
"""
|
|
cmd='e0\n'
|
|
return self.send_command(cmd, 0.045)
|
|
|
|
def read_right_wheel_encoder(self):
|
|
"""
|
|
Reads the right wheel encoder counts since last clear
|
|
reset_encoder_counts command.
|
|
|
|
The encoder has 144 counts for one complete wheel revolution.
|
|
"""
|
|
cmd='e1\n'
|
|
return self.send_command(cmd, 0.045)
|
|
|
|
def reset_encoder_counts(self):
|
|
"""Reset the wheel encoder counts."""
|
|
cmd='c\n'
|
|
return self.send_command(cmd)
|