Add Esp32 (serial) vehicle to pycar
This commit is contained in:
26
pycar/src/car/control/gpio/abstract_vehicle.py
Normal file
26
pycar/src/car/control/gpio/abstract_vehicle.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from abc import ABC, abstractmethod, abstractproperty
|
||||
|
||||
class AbstractVehicle(ABC):
|
||||
|
||||
@abstractmethod
|
||||
@property
|
||||
def throttle(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@throttle.setter
|
||||
def throttle(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@property
|
||||
def steering(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@steering.setter
|
||||
def throttle(self):
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
@@ -1,18 +1,25 @@
|
||||
from car.control.gpio.abstract_vehicle import AbstractVehicle
|
||||
from car.control.gpio.serial_vehicle import SerialVehicle
|
||||
from .mockvehicle import MockVehicle
|
||||
import os
|
||||
|
||||
|
||||
def get_vehicle(motor_pin=19, steering_pin=18):
|
||||
# TODO: Remove need for motor/steering pin, instead retrieve from env variable.
|
||||
# TODO: Dependency injectino in python?
|
||||
def get_vehicle(motor_pin=19, steering_pin=18) -> AbstractVehicle:
|
||||
ENV_CAR = None if 'CAR_VEHICLE' not in os.environ else os.environ['CAR_VEHICLE']
|
||||
if ENV_CAR == "CAR_2D":
|
||||
if ENV_CAR == "VEHICLE_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)
|
||||
elif ENV_CAR == "VEHICLE_MOCK":
|
||||
return MockVehicle()
|
||||
elif ENV_CAR == "VEHICLE_SERIAL":
|
||||
# TODO: Pins in environment variables.
|
||||
return SerialVehicle()
|
||||
else:
|
||||
print('No valid vehicle found. Have you set the CAR_VEHICLE environment variable?')
|
||||
return None
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
|
||||
|
||||
# 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
|
||||
# A dummy vehicle class to use when testing/not connected to a real device.
|
||||
from car.control.gpio.abstract_vehicle import AbstractVehicle
|
||||
|
||||
|
||||
class MockVehicle(AbstractVehicle):
|
||||
def __init__(self):
|
||||
print('Using Mock Vehicle')
|
||||
|
||||
@property
|
||||
@@ -23,21 +24,5 @@ class MockVehicle:
|
||||
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
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import datetime
|
||||
from .abstract_vehicle import AbstractVehicle
|
||||
|
||||
|
||||
class VehicleRecordingDecorator:
|
||||
class VehicleRecordingDecorator(AbstractVehicle):
|
||||
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.
|
||||
This will be recorded to memory, and will save to the given file when save_data is called.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -66,21 +67,5 @@ class VehicleRecordingDecorator:
|
||||
'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
|
||||
|
||||
42
pycar/src/car/control/gpio/serial_vehicle.py
Normal file
42
pycar/src/car/control/gpio/serial_vehicle.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from .abstract_vehicle import AbstractVehicle
|
||||
from serial import Serial
|
||||
|
||||
STEERING_CHANNEL = 1
|
||||
THROTTLE_CHANNEL = 2
|
||||
|
||||
|
||||
class SerialVehicle(AbstractVehicle):
|
||||
|
||||
def __init__(self, serial_port='/dev/ttyUSB0', steering_pin=12, throttle_pin=14):
|
||||
self.serial_port = Serial(port=serial_port, baudrate=115200)
|
||||
|
||||
# Initialise the channels and pins on esp32.
|
||||
self._init_esp32_pwm(steering_pin, throttle_pin)
|
||||
self.throttle = 0
|
||||
self.steering = 0
|
||||
|
||||
@property
|
||||
def throttle(self) -> float:
|
||||
return self.throttle
|
||||
|
||||
@throttle.setter
|
||||
def throttle(self, new_throttle: float):
|
||||
self.throttle = new_throttle
|
||||
self._set_servo_value(THROTTLE_CHANNEL, new_throttle)
|
||||
|
||||
@property
|
||||
def steering(self) -> float:
|
||||
return self.steering
|
||||
|
||||
@steering.setter
|
||||
def steering(self, new_steering: float):
|
||||
self.steering = new_steering
|
||||
self._set_servo_value(STEERING_CHANNEL, new_steering)
|
||||
|
||||
def _set_servo_value(self, channel, value):
|
||||
# Scale the value to a byte, as 0-255 is the angle range for the esp32 servo.
|
||||
self.serial_port.write(bytes[channel, (value + 1) / 2 * 255])
|
||||
|
||||
def _init_esp32_pwm(self, steering_pin, throttle_pin):
|
||||
self.serial_port.write(bytes([0, 2, STEERING_CHANNEL,
|
||||
steering_pin, THROTTLE_CHANNEL, throttle_pin]))
|
||||
@@ -1,3 +1,4 @@
|
||||
from .abstract_vehicle import AbstractVehicle
|
||||
from gpiozero import Servo, Device
|
||||
from gpiozero.pins.pigpio import PiGPIOFactory
|
||||
import subprocess
|
||||
@@ -29,7 +30,7 @@ def _is_pin_valid(pin):
|
||||
# two servos for controls (e.g. drone, dog)
|
||||
|
||||
|
||||
class Vehicle:
|
||||
class Vehicle(AbstractVehicle):
|
||||
def __init__(self, motor_pin=19, servo_pin=18):
|
||||
subprocess.call(['pigpiod'])
|
||||
Device.pin_factory = PiGPIOFactory()
|
||||
|
||||
Reference in New Issue
Block a user