Move root car to pycar, put other pycar back to car.
This commit is contained in:
33
pycar/src/car/control/PythonRemoteController.py
Normal file
33
pycar/src/car/control/PythonRemoteController.py
Normal file
@@ -0,0 +1,33 @@
|
||||
print("Connecting to pi")
|
||||
|
||||
import grpc
|
||||
from concurrent import futures
|
||||
import motorService_pb2_grpc
|
||||
from motorService_pb2 import SteeringRequest, ThrottleRequest
|
||||
import time
|
||||
|
||||
throttle = 0.1
|
||||
timer = None
|
||||
|
||||
class ThrottleIterator:
|
||||
'''
|
||||
Class to get the current throttle for the car.
|
||||
Will return a random throttle between
|
||||
'''
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if throttle > 1 or throttle < -1:
|
||||
raise StopIteration
|
||||
time.sleep(1)
|
||||
return ThrottleRequest(throttle=throttle)
|
||||
|
||||
|
||||
channel = grpc.insecure_channel('10.0.0.53:50051')
|
||||
stub = motorService_pb2_grpc.CarControlStub(channel)
|
||||
|
||||
response = stub.SetThrottle(ThrottleIterator())
|
||||
|
||||
while True:
|
||||
throttle = int(input('Please enter a value for the throttle between -100 and 100'))
|
||||
0
pycar/src/car/control/__init__.py
Normal file
0
pycar/src/car/control/__init__.py
Normal file
0
pycar/src/car/control/gpio/__init__.py
Normal file
0
pycar/src/car/control/gpio/__init__.py
Normal file
18
pycar/src/car/control/gpio/factory.py
Normal file
18
pycar/src/car/control/gpio/factory.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from .mockvehicle import MockVehicle
|
||||
import os
|
||||
|
||||
|
||||
def get_vehicle(motor_pin=19, steering_pin=18):
|
||||
ENV_CAR = None if 'CAR_VEHICLE' not in os.environ else os.environ['CAR_VEHICLE']
|
||||
if ENV_CAR == "CAR_2D":
|
||||
try:
|
||||
from .vehicle import Vehicle
|
||||
return Vehicle(motor_pin, steering_pin)
|
||||
except ImportError:
|
||||
print(
|
||||
'Could not import CAR_2D vehicle. Have you installed the GPIOZERO package?')
|
||||
elif ENV_CAR == "CAR_MOCK":
|
||||
return MockVehicle(motor_pin, steering_pin)
|
||||
else:
|
||||
print('No valid vehicle found. Have you set the CAR_VEHICLE environment variable?')
|
||||
return None
|
||||
43
pycar/src/car/control/gpio/mockvehicle.py
Normal file
43
pycar/src/car/control/gpio/mockvehicle.py
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
|
||||
# A dummy vehicle class to use when
|
||||
class MockVehicle:
|
||||
def __init__(self, motor_pin=19, servo_pin=18):
|
||||
self.motor_pin = motor_pin
|
||||
self.steering_pin = servo_pin
|
||||
print('Using Mock Vehicle')
|
||||
|
||||
@property
|
||||
def throttle(self):
|
||||
return self._throttle
|
||||
|
||||
@throttle.setter
|
||||
def throttle(self, value):
|
||||
self._throttle = value
|
||||
|
||||
@property
|
||||
def steering(self):
|
||||
return self._steering
|
||||
|
||||
@steering.setter
|
||||
def steering(self, value):
|
||||
self._steering = value
|
||||
|
||||
@property
|
||||
def motor_pin(self):
|
||||
return self._motor_pin
|
||||
|
||||
@motor_pin.setter
|
||||
def motor_pin(self, value):
|
||||
self._motor_pin = value
|
||||
|
||||
@property
|
||||
def steering_pin(self):
|
||||
return self._steering_pin
|
||||
|
||||
@steering_pin.setter
|
||||
def steering_pin(self, value):
|
||||
self._steering_pin = value
|
||||
|
||||
def stop(self):
|
||||
self.throttle = 0
|
||||
86
pycar/src/car/control/gpio/recording_vehicle_decorator.py
Normal file
86
pycar/src/car/control/gpio/recording_vehicle_decorator.py
Normal file
@@ -0,0 +1,86 @@
|
||||
import datetime
|
||||
|
||||
|
||||
class VehicleRecordingDecorator:
|
||||
def __init__(self, vehicle):
|
||||
"""
|
||||
A decorator for a vehicle object to record the changes in steering/throttle.
|
||||
This will be recorded to memory, and will save to the given file when save is called.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
vehicle
|
||||
Base vehicle to decorate.
|
||||
|
||||
outfile: str
|
||||
Filename to write to. Will create/overwrite existing file. You must call save to save the
|
||||
data to the file.
|
||||
"""
|
||||
self._vehicle = vehicle
|
||||
self._recording = False
|
||||
self._records = []
|
||||
|
||||
@property
|
||||
def vehicle(self):
|
||||
return self._vehicle
|
||||
|
||||
def save_data(self, outfile):
|
||||
"""
|
||||
Flushes all current records to disk.
|
||||
"""
|
||||
with open(outfile, 'w') as f:
|
||||
for line in self._records:
|
||||
f.write('%s\n' % line)
|
||||
self._records = []
|
||||
|
||||
@property
|
||||
def record(self):
|
||||
return self._recording
|
||||
|
||||
@record.setter
|
||||
def record(self, value: bool):
|
||||
if not value:
|
||||
self._records = []
|
||||
self._recording = value
|
||||
|
||||
@property
|
||||
def throttle(self):
|
||||
return self._vehicle.throttle
|
||||
|
||||
@throttle.setter
|
||||
def throttle(self, value):
|
||||
if self._recording:
|
||||
self._records.append(
|
||||
't,' + str(value) + ',' + datetime.datetime.now().isoformat(sep=' ', timespec='seconds'))
|
||||
self._vehicle.throttle = value
|
||||
|
||||
@property
|
||||
def steering(self):
|
||||
return self._vehicle.steering
|
||||
|
||||
@steering.setter
|
||||
def steering(self, value):
|
||||
if self._recording:
|
||||
self._records.append(
|
||||
's,' + str(value) + ',' + datetime.datetime.now().isoformat(sep=' ', timespec='seconds'))
|
||||
self._vehicle.steering = value
|
||||
|
||||
@property
|
||||
def motor_pin(self):
|
||||
return self._vehicle.motor_pin
|
||||
|
||||
@motor_pin.setter
|
||||
def motor_pin(self, value):
|
||||
self._vehicle.motor_pin = value
|
||||
|
||||
@property
|
||||
def steering_pin(self):
|
||||
return self._vehicle.steering_pin
|
||||
|
||||
@steering_pin.setter
|
||||
def steering_pin(self, value):
|
||||
self._vehicle.steering_pin = value
|
||||
|
||||
def stop(self):
|
||||
self.throttle = 0
|
||||
89
pycar/src/car/control/gpio/vehicle.py
Normal file
89
pycar/src/car/control/gpio/vehicle.py
Normal file
@@ -0,0 +1,89 @@
|
||||
from gpiozero import Servo, Device
|
||||
from gpiozero.pins.pigpio import PiGPIOFactory
|
||||
import subprocess
|
||||
|
||||
|
||||
def _safely_set_servo_value(servo, value):
|
||||
try:
|
||||
if value < -1 or value > 1:
|
||||
print("Not setting throttle, invalid value set.")
|
||||
return False
|
||||
servo.value = value
|
||||
except TypeError:
|
||||
print("throttle should be a number, preferably a float.")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _is_pin_valid(pin):
|
||||
if isinstance(pin, int):
|
||||
if pin < 2 or pin > 21:
|
||||
print("Invalid GPIO pin")
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
print("Value must be an int.")
|
||||
return False
|
||||
|
||||
# TODO: Allow a vector to be set to change the throttle/steering, for vehicles that don't use
|
||||
# two servos for controls (e.g. drone, dog)
|
||||
|
||||
|
||||
class Vehicle:
|
||||
def __init__(self, motor_pin=19, servo_pin=18):
|
||||
subprocess.call(['sudo', 'pigpiod'])
|
||||
Device.pin_factory = PiGPIOFactory()
|
||||
print('Using pin factory:')
|
||||
print(Device.pin_factory)
|
||||
self.motor_pin = motor_pin
|
||||
self.steering_pin = servo_pin
|
||||
self.initialise_motor()
|
||||
|
||||
def initialise_motor(self):
|
||||
self._motor_servo = Servo(
|
||||
self._motor_pin, pin_factory=Device.pin_factory)
|
||||
self._steering_servo = Servo(
|
||||
self._steering_pin, pin_factory=Device.pin_factory)
|
||||
|
||||
@property
|
||||
def throttle(self):
|
||||
return self._motor_servo.value
|
||||
|
||||
@throttle.setter
|
||||
def throttle(self, value):
|
||||
_safely_set_servo_value(self._motor_servo, value)
|
||||
|
||||
@property
|
||||
def steering(self):
|
||||
return self._motor_servo.value
|
||||
|
||||
@steering.setter
|
||||
def steering(self, value):
|
||||
_safely_set_servo_value(self._motor_servo, value)
|
||||
|
||||
@property
|
||||
def motor_pin(self):
|
||||
return self._motor_pin
|
||||
|
||||
@motor_pin.setter
|
||||
def motor_pin(self, value):
|
||||
# TODO: Reinitialise the servo when the pin changes, or discard this method
|
||||
# (probably don't want to allow pin changes whilst the device is in use anyway)
|
||||
self._motor_pin = value if _is_pin_valid(value) else self._motor_pin
|
||||
|
||||
@property
|
||||
def steering_pin(self):
|
||||
return self._steering_pin
|
||||
|
||||
@steering_pin.setter
|
||||
def steering_pin(self, value):
|
||||
self._steering_pin = value if _is_pin_valid(
|
||||
value) else self._steering_pin
|
||||
|
||||
def stop(self):
|
||||
self.throttle = 0
|
||||
self.steering = 0
|
||||
|
||||
def change_with_vector(self, vector):
|
||||
# TBD how this will work. Could just be (magnitude,angle) change
|
||||
pass
|
||||
68
pycar/src/car/control/motor_servicer.py
Normal file
68
pycar/src/car/control/motor_servicer.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from threading import Timer, Thread
|
||||
from concurrent import futures
|
||||
import time
|
||||
|
||||
import car.control.motorService_pb2 as motorService_pb2
|
||||
import car.control.motorService_pb2_grpc as motorService_pb2_grpc
|
||||
from car.control.gpio.recording_vehicle_decorator import VehicleRecordingDecorator
|
||||
import google.protobuf.empty_pb2 as empty
|
||||
|
||||
|
||||
class MotorServicer(motorService_pb2_grpc.CarControlServicer):
|
||||
def __init__(self, vehicle):
|
||||
self.vehicle = VehicleRecordingDecorator(vehicle)
|
||||
self._timer = None
|
||||
self._timeout_elapsed = False
|
||||
|
||||
def set_throttle(self, request, context):
|
||||
# gRPC streams currently don't work between python and android.
|
||||
# If we don't get a response every 3 seconds, stop the car.
|
||||
print('Setting throttle to: ' + str(request.throttle))
|
||||
self.set_timeout(3)
|
||||
self.vehicle.throttle = request.throttle
|
||||
return motorService_pb2.ThrottleResponse(throttleSet=True)
|
||||
|
||||
def set_steering(self, request, context):
|
||||
print('Setting steering to: ' + str(request.steering))
|
||||
self.vehicle.steering = request.steering
|
||||
return motorService_pb2.SteeringResponse(steeringSet=True)
|
||||
|
||||
def stream_vehicle_2d(self, request_iterator, context):
|
||||
print('Starting Vehicle Control Stream')
|
||||
self._timeout_elapsed = False
|
||||
for request_2d in request_iterator:
|
||||
# End the stream if the user fails to respond in time.
|
||||
if self._timeout_elapsed:
|
||||
break
|
||||
print('Setting 2d values to: ' + str((request_2d.throttle.throttle, request_2d.steering.steering)))
|
||||
self.set_timeout(3)
|
||||
# TODO: Make a vehicle method set 2d throttle/steering.
|
||||
self.vehicle.throttle = request_2d.throttle.throttle
|
||||
self.vehicle.steering = request_2d.steering.steering
|
||||
|
||||
return empty.Empty()
|
||||
|
||||
def set_timeout(self, min_timeout):
|
||||
"""Stops the old timer and restarts it to the specified time.
|
||||
|
||||
min_timeout -- The minimum time that can be used for the timer.
|
||||
"""
|
||||
if self._timer is not None:
|
||||
self._timer.cancel()
|
||||
self._timer = Timer(min_timeout, self.timeout_elapsed)
|
||||
self._timer.start()
|
||||
|
||||
def timeout_elapsed(self):
|
||||
"""Election or heartbeat timeout has elapsed."""
|
||||
print("Node timeout elapsed")
|
||||
self._timeout_elapsed = True
|
||||
self.vehicle.stop()
|
||||
|
||||
def record(self, request, context):
|
||||
"""Indicate whether the vehicle data should be recorded."""
|
||||
self.vehicle.record = request.record
|
||||
return empty.Empty()
|
||||
|
||||
def save_recorded_data(self, request, context):
|
||||
self.vehicle.save_data(request.file)
|
||||
return empty.Empty()
|
||||
Reference in New Issue
Block a user