├── donkeycar
├── gym
│ ├── __init__.py
│ ├── remote_controller.py
│ └── gym_real.py
├── parts
│ ├── __init__.py
│ ├── web_controller
│ │ └── templates
│ │ │ ├── static
│ │ │ ├── img_placeholder.jpg
│ │ │ ├── donkeycar-logo-sideways.png
│ │ │ ├── bootstrap
│ │ │ │ └── 3.3.7
│ │ │ │ │ └── fonts
│ │ │ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ │ │ ├── glyphicons-halflings-regular.woff
│ │ │ │ │ └── glyphicons-halflings-regular.woff2
│ │ │ └── style.css
│ │ │ ├── pilots_list.html
│ │ │ ├── session_list.html
│ │ │ ├── vehicle_list.html
│ │ │ ├── home.html
│ │ │ ├── base.html
│ │ │ ├── wsTest.html
│ │ │ └── base_fpv.html
│ ├── pipe.py
│ ├── throttle_filter.py
│ ├── sombrero.py
│ ├── file_watcher.py
│ ├── explode.py
│ ├── pytorch
│ │ ├── torch_utils.py
│ │ ├── torch_train.py
│ │ └── ResNet18.py
│ ├── graph.py
│ ├── logger.py
│ ├── ros.py
│ ├── fps.py
│ ├── launch.py
│ ├── perfmon.py
│ ├── serial_controller.py
│ ├── behavior.py
│ ├── simulation.py
│ ├── leopard_imaging.py
│ ├── tfmini.py
│ ├── odometer.py
│ ├── voice_control
│ │ └── alexa.py
│ ├── fast_stretch.py
│ ├── pigpio_enc.py
│ ├── teensy.py
│ ├── coral.py
│ ├── image.py
│ ├── dgym.py
│ ├── imu.py
│ ├── led_status.py
│ ├── text_writer.py
│ └── realsense2.py
├── contrib
│ ├── __init__.py
│ └── robohat
│ │ ├── lib
│ │ └── adafruit_logging.mpy
│ │ └── rear_light.py
├── utilities
│ ├── __init__.py
│ ├── dk_platform.py
│ └── deprecated.py
├── management
│ ├── ui
│ │ ├── __init__.py
│ │ ├── ui.kv
│ │ ├── ui.py
│ │ └── rc_file_handler.py
│ ├── __init__.py
│ └── tub_web
│ │ ├── static
│ │ ├── bootstrap
│ │ │ └── 3.3.7
│ │ │ │ └── fonts
│ │ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ │ ├── glyphicons-halflings-regular.woff
│ │ │ │ └── glyphicons-halflings-regular.woff2
│ │ └── style.css
│ │ ├── tubs.html
│ │ ├── base.html
│ │ └── tub.html
├── tests
│ ├── test_parts.py
│ ├── __init__.py
│ ├── tub
│ │ └── tub.tar.gz
│ ├── pytest.ini
│ ├── test_actuator.py
│ ├── test_web_controller.py
│ ├── test_controller.py
│ ├── test_template.py
│ ├── test_catalog_v2.py
│ ├── test_tubwriter.py
│ ├── test_odometer.py
│ ├── test_vehicle.py
│ ├── test_memory.py
│ ├── test_launch.py
│ ├── setup.py
│ ├── test_tub_v2.py
│ ├── test_datastore_v2.py
│ ├── test_scripts.py
│ ├── test_lidar.py
│ ├── test_keras.py
│ ├── test_seekable_v2.py
│ ├── test_telemetry.py
│ └── test_circular_buffer.py
├── pipeline
│ ├── __init__.py
│ └── augmentations.py
├── templates
│ ├── myconfig.py
│ ├── train.py
│ ├── calibration_odometry.json
│ ├── cfg_arduino_drive.py
│ ├── arduino_drive.py
│ ├── cfg_square.py
│ └── just_drive.py
├── __init__.py
├── geom.py
├── benchmarks
│ ├── tub_v2.py
│ └── tub.py
├── memory.py
└── config.py
├── .coveragerc
├── Makefile
├── scripts
├── disable_js_mouse.sh
├── tflite_convert.py
├── profile_coral.py
├── profile.py
├── tflite_profile.py
├── salient_vis_listener.py
├── graph_listener.py
├── remote_cam_view_tcp.py
├── convert_to_tflite.py
├── preview_augumentations.py
├── remote_cam_view.py
├── freeze_model.py
├── multi_train.py
├── pigpio_donkey.py
└── convert_to_tub_v2.py
├── MANIFEST.in
├── pyproject.toml
├── .github
├── linters
│ └── .python-black
└── workflows
│ ├── superlinter.yml
│ └── python-package-conda.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── setup.cfg
└── CONTRIBUTING.md
/donkeycar/gym/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/donkeycar/parts/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/donkeycar/contrib/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/donkeycar/utilities/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/donkeycar/management/ui/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/donkeycar/tests/test_parts.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/donkeycar/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | branch = True
3 | omit =
4 | donkeycar/tests/*
--------------------------------------------------------------------------------
/donkeycar/pipeline/__init__.py:
--------------------------------------------------------------------------------
1 | # The new donkey car training pipeline.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 |
3 | tests:
4 | pytest
5 |
6 | package:
7 | python setup.py sdist
8 |
9 |
--------------------------------------------------------------------------------
/donkeycar/management/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logger = logging.getLogger(__name__)
4 |
--------------------------------------------------------------------------------
/scripts/disable_js_mouse.sh:
--------------------------------------------------------------------------------
1 | xinput set-prop "Sony PLAYSTATION(R)3 Controller" "Device Enabled" 0
2 |
3 |
--------------------------------------------------------------------------------
/donkeycar/tests/tub/tub.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autorope/donkeycar/HEAD/donkeycar/tests/tub/tub.tar.gz
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include donkeycar/templates/*
2 | include scripts
3 | recursive-include donkeycar/parts/web_controller/templates/ *
4 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools", "wheel"]
3 | build-backend = "setuptools.build_meta"
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/donkeycar/contrib/robohat/lib/adafruit_logging.mpy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autorope/donkeycar/HEAD/donkeycar/contrib/robohat/lib/adafruit_logging.mpy
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/static/img_placeholder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autorope/donkeycar/HEAD/donkeycar/parts/web_controller/templates/static/img_placeholder.jpg
--------------------------------------------------------------------------------
/donkeycar/tests/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | filterwarnings =
3 | ignore::DeprecationWarning
4 | ignore::FutureWarning
5 |
6 | log_cli = True
7 | log_cli_level = INFO
8 | reruns = 3
9 |
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/static/donkeycar-logo-sideways.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autorope/donkeycar/HEAD/donkeycar/parts/web_controller/templates/static/donkeycar-logo-sideways.png
--------------------------------------------------------------------------------
/donkeycar/management/tub_web/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autorope/donkeycar/HEAD/donkeycar/management/tub_web/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/donkeycar/management/tub_web/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autorope/donkeycar/HEAD/donkeycar/management/tub_web/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/donkeycar/management/tub_web/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autorope/donkeycar/HEAD/donkeycar/management/tub_web/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autorope/donkeycar/HEAD/donkeycar/parts/web_controller/templates/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autorope/donkeycar/HEAD/donkeycar/parts/web_controller/templates/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/autorope/donkeycar/HEAD/donkeycar/parts/web_controller/templates/static/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/.github/linters/.python-black:
--------------------------------------------------------------------------------
1 | [tool.black]
2 | line-length = 80
3 | target-version = ['py37']
4 | multi_line_output = 3
5 | include_trailing_comma = true
6 | force_grid_wrap = 0
7 | use_parentheses = true
8 | ensure_newline_before_comments = true
9 | skip-string-normalization = true
10 |
11 |
--------------------------------------------------------------------------------
/donkeycar/templates/myconfig.py:
--------------------------------------------------------------------------------
1 | # """
2 | # My CAR CONFIG
3 |
4 | # This file is read by your car application's manage.py script to change the car
5 | # performance
6 |
7 | # If desired, all config overrides can be specified here.
8 | # The update operation will not touch this file.
9 | # """
10 |
11 |
--------------------------------------------------------------------------------
/donkeycar/parts/pipe.py:
--------------------------------------------------------------------------------
1 | class Pipe:
2 | """
3 | Just pipe all inputs to the output, so they can be renamed.
4 | """
5 | def run(self, *args):
6 | # seems to be a python bug that takes a single argument
7 | # return makes it into two element tuple with empty last element.
8 | return args if len(args) > 1 else args[0]
9 |
--------------------------------------------------------------------------------
/donkeycar/management/tub_web/tubs.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends "base.html" %}
3 | {% block content %}
4 |
5 |
6 |
14 |
15 |
16 |
17 | {% end %}
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | env/*
3 | data/*
4 | dist/*
5 |
6 | *.pyc
7 | .ipynb_checkpoints
8 | *.egg-info/
9 | .git.bfg-report
10 | .DS_Store
11 | lag_log.csv
12 |
13 | .cache
14 | site/*
15 | build
16 |
17 | #IDE
18 | .idea
19 | .spyproject
20 | .pytest_cache
21 | .coverage
22 | .vscode
23 |
24 | # codecov
25 | htmlcov/
26 |
27 | # PyTorch
28 | lightning_logs
29 | tb_logs
30 |
--------------------------------------------------------------------------------
/donkeycar/tests/test_actuator.py:
--------------------------------------------------------------------------------
1 | from .setup import on_pi
2 |
3 | from donkeycar.parts.actuator import PCA9685, PWMSteering, PWMThrottle
4 | import pytest
5 |
6 |
7 | @pytest.mark.skipif(on_pi() == False, reason='Not on RPi')
8 | def test_PCA9685():
9 | c = PCA9685(0)
10 |
11 | @pytest.mark.skipif(on_pi() == False, reason='Not on RPi')
12 | def test_PWMSteering():
13 | c = PCA9685(0)
14 | s = PWMSteering(c, 300, 440)
15 |
--------------------------------------------------------------------------------
/scripts/tflite_convert.py:
--------------------------------------------------------------------------------
1 | '''
2 | Usage:
3 | tflite_convert.py --model="mymodel.h5" --out="mymodel.tflite"
4 |
5 | Note:
6 | may require tensorflow > 1.11 or
7 | pip install tf-nightly
8 | '''
9 | import os
10 |
11 | from docopt import docopt
12 | from donkeycar.parts.interpreter import keras_model_to_tflite
13 |
14 | args = docopt(__doc__)
15 |
16 | in_model = os.path.expanduser(args['--model'])
17 | out_model = os.path.expanduser(args['--out'])
18 | keras_model_to_tflite(in_model, out_model)
19 |
20 |
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/pilots_list.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends "base.html" %}
3 | {% block content %}
4 |
5 |
Pilots
6 |
7 |
21 |
22 |
23 | {% end %}
24 |
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/session_list.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends "base.html" %}
3 | {% block content %}
4 |
5 |
Sessions
6 |
7 |
21 |
22 |
23 | {% end %}
24 |
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/vehicle_list.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends "base.html" %}
3 | {% block content %}
4 |
5 |
Vehicles
6 |
7 |
21 |
22 |
23 | {% end %}
24 |
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/home.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends "base.html" %}
3 | {% block content %}
4 |
5 |
6 |
7 |
8 |
Your donkey is ready to ride
9 |
10 |
Using this web interface you can do the following
11 |
12 |
13 | - Drive your car.
14 | - View data collected from your car.
15 |
16 |
17 |
18 |
Features we are working on include:
19 |
20 | - Train & debug models from web.
21 | - Browse library of driving models and download them.
22 | - Combine models
23 |
24 |
25 |
26 | {% end %}
27 |
--------------------------------------------------------------------------------
/donkeycar/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from pyfiglet import Figlet
4 | import logging
5 |
6 | __version__ = '5.2.dev5'
7 |
8 | logging.basicConfig(level=os.environ.get('LOGLEVEL', 'INFO').upper())
9 |
10 | f = Figlet(font='speed')
11 |
12 |
13 | print(f.renderText('Donkey Car'))
14 | print(f'using donkey v{__version__} ...')
15 |
16 | if sys.version_info.major < 3 or sys.version_info.minor < 11:
17 | msg = f'Donkey Requires Python 3.11 or greater. You are using {sys.version}'
18 | raise ValueError(msg)
19 |
20 | # The default recursion limits in CPython are too small.
21 | sys.setrecursionlimit(10**5)
22 |
23 | from .vehicle import Vehicle
24 | from .memory import Memory
25 | from . import utils
26 | from . import config
27 | from . import contrib
28 | from .config import load_config
29 |
--------------------------------------------------------------------------------
/donkeycar/parts/throttle_filter.py:
--------------------------------------------------------------------------------
1 |
2 | class ThrottleFilter(object):
3 | '''
4 | allow reverse to trigger automatic reverse throttle
5 | '''
6 |
7 | def __init__(self):
8 | self.reverse_triggered = False
9 | self.last_throttle = 0.0
10 |
11 | def run(self, throttle_in):
12 | if throttle_in is None:
13 | return throttle_in
14 |
15 | throttle_out = throttle_in
16 |
17 | if throttle_out < 0.0:
18 | if not self.reverse_triggered and self.last_throttle < 0.0:
19 | throttle_out = 0.0
20 | self.reverse_triggered = True
21 | else:
22 | self.reverse_triggered = False
23 |
24 | self.last_throttle = throttle_out
25 | return throttle_out
26 |
27 | def shutdown(self):
28 | pass
29 |
--------------------------------------------------------------------------------
/donkeycar/utilities/dk_platform.py:
--------------------------------------------------------------------------------
1 | import os
2 | import platform
3 |
4 | #
5 | # functions to query hardware and os
6 | #
7 | def is_mac():
8 | return "Darwin" == platform.system()
9 |
10 | #
11 | # read tegra chip id if it exists.
12 | #
13 | def read_chip_id() -> str:
14 | """
15 | Read the tegra chip id.
16 | On non-tegra platforms this will be blank.
17 | """
18 | try:
19 | with open("/sys/module/tegra_fuse/parameters/tegra_chip_id", "r") as f:
20 | return next(f)
21 | except FileNotFoundError:
22 | pass
23 | return ""
24 |
25 | _chip_id = None
26 |
27 | def is_jetson() -> bool:
28 | """
29 | Determine if platform is a jetson
30 | """
31 | global _chip_id
32 | if _chip_id is None:
33 | _chip_id = read_chip_id()
34 | return _chip_id != ""
35 |
--------------------------------------------------------------------------------
/donkeycar/parts/sombrero.py:
--------------------------------------------------------------------------------
1 | class Sombrero:
2 | '''
3 | A pi hat developed by Adam Conway to manage power, pwm for a Donkeycar
4 | This requires that GPIO 26 is brought low to enable the pwm out.
5 | Because all GPIO modes have to be the same accross code, we use BOARD
6 | mode, which is physical pin 37.
7 | '''
8 |
9 | def __init__(self):
10 | try:
11 | import RPi.GPIO as GPIO
12 |
13 | GPIO.setmode(GPIO.BOARD)
14 | GPIO.setup(37, GPIO.OUT)
15 | GPIO.output(37, GPIO.LOW)
16 | print("sombrero enabled")
17 | except:
18 | pass
19 |
20 | def __del__(self):
21 | try:
22 | import RPi.GPIO as GPIO
23 |
24 | GPIO.cleanup()
25 | print("sombrero disabled")
26 | except:
27 | pass
28 |
--------------------------------------------------------------------------------
/scripts/profile_coral.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | from donkeycar.parts.coral import InferenceEngine
3 | from PIL import Image
4 | from donkeycar.utils import FPSTimer
5 | import numpy as np
6 |
7 | def main():
8 | parser = argparse.ArgumentParser()
9 | parser.add_argument(
10 | '--model', help='File path of Tflite model.', required=True)
11 | parser.add_argument(
12 | '--image', help='File path of the image to be recognized.', required=True)
13 | args = parser.parse_args()
14 | # Initialize engine.
15 | engine = InferenceEngine(args.model)
16 | # Run inference.
17 | img = Image.open(args.image)
18 | result = engine.Inference(np.array(img))
19 | print("inference result", result)
20 |
21 | timer = FPSTimer()
22 | while True:
23 | engine.Inference(np.array(img))
24 | timer.on_frame()
25 |
26 | if __name__ == '__main__':
27 | main()
--------------------------------------------------------------------------------
/donkeycar/parts/file_watcher.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | class FileWatcher(object):
4 | '''
5 | Watch a specific file and give a signal when it's modified
6 | '''
7 |
8 | def __init__(self, filename, verbose=False):
9 | self.modified_time = os.path.getmtime(filename)
10 | self.filename = filename
11 | self.verbose = verbose
12 |
13 | def run(self):
14 | '''
15 | return True when file changed. Keep in mind that this does not mean that the
16 | file is finished with modification.
17 | '''
18 | m_time = os.path.getmtime(self.filename)
19 |
20 | if m_time != self.modified_time:
21 | self.modified_time = m_time
22 | if self.verbose:
23 | print(self.filename, "changed.")
24 | return True
25 |
26 | return False
27 |
28 |
29 |
--------------------------------------------------------------------------------
/donkeycar/parts/explode.py:
--------------------------------------------------------------------------------
1 | #
2 | # part that explodes a dictionary argument into individually named arguments
3 | #
4 |
5 |
6 | class ExplodeDict:
7 | """
8 | part that expands a dictionary input argument
9 | into individually named output arguments
10 | """
11 | def __init__(self, memory, output_prefix = ""):
12 | """
13 | Break a map into key/value pairs and write
14 | them to the output memory, optionally
15 | prefixing the key on output.
16 | Basically, take a dictionary and write
17 | it to the output.
18 | """
19 | self.memory = memory
20 | self.prefix = output_prefix
21 |
22 | def run(self, key_values):
23 | if type(key_values) is dict:
24 | for key, value in key_values.items():
25 | self.memory[self.prefix + key] = value
26 | return None
27 |
--------------------------------------------------------------------------------
/donkeycar/templates/train.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | Scripts to train a keras model using tensorflow.
4 | Basic usage should feel familiar: train.py --tubs data/ --model models/mypilot.h5
5 |
6 | Usage:
7 | train.py [--tubs=tubs] (--model=)
8 | [--type=(linear|inferred|tensorrt_linear|tflite_linear)]
9 | [--comment=]
10 |
11 | Options:
12 | -h --help Show this screen.
13 | """
14 |
15 | from docopt import docopt
16 | import donkeycar as dk
17 | from donkeycar.pipeline.training import train
18 |
19 |
20 | def main():
21 | args = docopt(__doc__)
22 | cfg = dk.load_config()
23 | tubs = args['--tubs']
24 | model = args['--model']
25 | model_type = args['--type']
26 | comment = args['--comment']
27 | train(cfg, tubs, model, model_type, comment)
28 |
29 |
30 | if __name__ == "__main__":
31 | main()
32 |
--------------------------------------------------------------------------------
/donkeycar/tests/test_web_controller.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import pytest
3 | import json
4 | import os
5 | from donkeycar.parts.web_controller.web import LocalWebController
6 | import donkeycar.templates.cfg_complete as cfg
7 | from importlib import reload
8 |
9 | @pytest.fixture
10 | def server():
11 | server = LocalWebController(cfg.WEB_CONTROL_PORT)
12 | return server
13 |
14 |
15 | def test_json_output(server):
16 | result = server.run()
17 | json_result = json.dumps(result)
18 | d = json.loads(json_result)
19 |
20 | assert server.port == 8887
21 |
22 | assert d is not None
23 | assert int(d[0]) == 0
24 |
25 |
26 | def test_web_control_user_defined_port():
27 | os.environ['WEB_CONTROL_PORT'] = "12345"
28 | reload(cfg)
29 | server = LocalWebController(port=cfg.WEB_CONTROL_PORT)
30 |
31 | assert server.port == 12345
32 |
33 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.6
2 |
3 | WORKDIR /app
4 |
5 | # install donkey with tensorflow (cpu only version)
6 | ADD ./setup.py /app/setup.py
7 | ADD ./README.md /app/README.md
8 | RUN pip install -e .[tf]
9 |
10 | # get testing requirements
11 | RUN pip install -e .[dev]
12 |
13 | # setup jupyter notebook to run without password
14 | RUN pip install jupyter notebook
15 | RUN jupyter notebook --generate-config
16 | RUN echo "c.NotebookApp.password = ''">>/root/.jupyter/jupyter_notebook_config.py
17 | RUN echo "c.NotebookApp.token = ''">>/root/.jupyter/jupyter_notebook_config.py
18 |
19 | # add the whole app dir after install so the pip install isn't updated when code changes.
20 | ADD . /app
21 |
22 | #start the jupyter notebook
23 | CMD jupyter notebook --no-browser --ip 0.0.0.0 --port 8888 --allow-root --notebook-dir=/app/notebooks
24 |
25 | #port for donkeycar
26 | EXPOSE 8887
27 |
28 | #port for jupyter notebook
29 | EXPOSE 8888
--------------------------------------------------------------------------------
/.github/workflows/superlinter.yml:
--------------------------------------------------------------------------------
1 | name: Super-Linter
2 |
3 | # Run this workflow every time a new commit pushed to your repository
4 | on: [push, pull_request]
5 |
6 | jobs:
7 | # Set the job key. The key is displayed as the job name
8 | # when a job name is not provided
9 | super-lint:
10 | # Name the Job
11 | name: Lint code base
12 | # Set the type of machine to run on
13 | runs-on: ubuntu-latest
14 | # Don't flag failure
15 | continue-on-error: true
16 | steps:
17 | # Checks out a copy of your repository on the ubuntu-latest machine
18 | - name: Checkout code
19 | uses: actions/checkout@v2
20 |
21 | # Runs the Super-Linter action
22 | - name: Run Super-Linter
23 | uses: github/super-linter@v4
24 | env:
25 | DEFAULT_BRANCH: master
26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27 | FILTER_REGEX_EXCLUDE: .*.css|.*.js
28 | DISABLE_ERRORS: true
29 |
30 |
--------------------------------------------------------------------------------
/donkeycar/tests/test_controller.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from .setup import on_pi
3 | from donkeycar.parts.controller import PS3Joystick, PS3JoystickController
4 |
5 |
6 | def test_ps3_joystick():
7 | js = PS3Joystick()
8 | assert js is not None
9 | js.init()
10 |
11 | def test_ps3_joystick_controller():
12 | js = PS3JoystickController()
13 | assert js is not None
14 | js.init_js()
15 | js.run_threaded(None)
16 | js.print_controls()
17 | def test_fn():
18 | pass
19 | js.set_button_down_trigger("x", test_fn)
20 | js.erase_last_N_records()
21 | js.on_throttle_changes()
22 | js.emergency_stop()
23 | #js.update()
24 | js.set_steering(0.0)
25 | js.set_throttle(0.0)
26 | js.toggle_manual_recording()
27 | js.increase_max_throttle()
28 | js.decrease_max_throttle()
29 | js.toggle_constant_throttle()
30 | js.toggle_mode()
31 | js.chaos_monkey_on_left()
32 | js.chaos_monkey_on_right()
33 | js.chaos_monkey_off()
--------------------------------------------------------------------------------
/donkeycar/geom.py:
--------------------------------------------------------------------------------
1 | '''
2 | Geometry
3 | Author: Tawn Kramer
4 | Date: Nov 11, 2014
5 | '''
6 | from .la import Vec2
7 |
8 | class LineSeg2d(object):
9 |
10 | def __init__(self, x1, y1, x2, y2):
11 | a = Vec2(x1, y1)
12 | b = Vec2(x2, y2)
13 | self.point = a
14 | self.end = b
15 | self.ray = a - b
16 | self.ray.normalize()
17 |
18 | def closest_vec_to(self, vec2_pt):
19 | '''
20 | produces a vector normal to this line passing through the given point vec2_pt
21 | '''
22 | delta_pt = self.point - vec2_pt
23 | dp = delta_pt.dot(self.ray)
24 | return self.ray * dp - delta_pt
25 |
26 | def cross_track_error(self, vec2_pt):
27 | '''
28 | a signed magnitude of distance from line segment
29 | '''
30 | err_vec = self.closest_vec_to(vec2_pt)
31 | mag = err_vec.mag()
32 | err_vec.scale(1.0 / mag)
33 | sign = 1.
34 | if err_vec.cross(self.ray) < 0.0:
35 | sign = -1.
36 | return mag * sign
37 |
38 |
--------------------------------------------------------------------------------
/donkeycar/benchmarks/tub_v2.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import timeit
4 | from pathlib import Path
5 |
6 | import numpy as np
7 |
8 | from donkeycar.parts.tub_v2 import Tub
9 |
10 |
11 | def benchmark():
12 | # Change to a non SSD storage path
13 | path = Path('/media/rahulrav/Cruzer/benchmark')
14 |
15 | # Recreate paths
16 | if os.path.exists(path.absolute().as_posix()):
17 | shutil.rmtree(path)
18 |
19 | inputs = ['input']
20 | types = ['int']
21 | tub = Tub(path.as_posix(), inputs, types, max_catalog_len=1000)
22 | write_count = 1000
23 | for i in range(write_count):
24 | record = {'input': i}
25 | tub.write_record(record)
26 |
27 | deletions = np.random.randint(0, write_count, 100)
28 | tub.delete_records(deletions)
29 |
30 | for record in tub:
31 | print('Record %s' % record)
32 |
33 | tub.close()
34 |
35 |
36 | if __name__ == "__main__":
37 | timer = timeit.Timer(benchmark)
38 | time_taken = timer.timeit(number=1)
39 | print('Time taken %s seconds' % time_taken)
40 | print('\nDone.')
41 |
--------------------------------------------------------------------------------
/.github/workflows/python-package-conda.yml:
--------------------------------------------------------------------------------
1 | name: Python package and test
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | jobs:
8 | checkout-test:
9 | name: Checkout and test
10 | runs-on: ${{ matrix.os }}
11 | strategy:
12 | matrix:
13 | os: ["macos-latest", "ubuntu-latest"]
14 | fail-fast: false
15 | defaults:
16 | run:
17 | shell: bash -l {0}
18 | steps:
19 | - name: Checkout code
20 | uses: actions/checkout@v4
21 | - name: Create python 3.11 conda env
22 | uses: conda-incubator/setup-miniconda@v3
23 | with:
24 | python-version: 3.11
25 | mamba-version: "*"
26 | activate-environment: donkey
27 | auto-activate-base: false
28 | channels: default, conda-forge, pytorch
29 | channel-priority: true
30 | - name: Conda info and list
31 | run: |
32 | conda info
33 | conda list
34 | - name: Install donkey
35 | run: |
36 | pip install -e .[pc,dev]
37 | pip list
38 | - name: Run tests
39 | run: pytest
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Will Roscoe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/donkeycar/templates/calibration_odometry.json:
--------------------------------------------------------------------------------
1 | {
2 | "velocimeters": [
3 | {
4 | "scale_and_alignment": [
5 | 1.0,
6 | 0.0,
7 | 0.0,
8 | 0.0,
9 | 1.0,
10 | 0.0,
11 | 0.0,
12 | 0.0,
13 | 1.0
14 | ],
15 | "noise_variance": 0.001,
16 | "extrinsics": {
17 | "T": [
18 | 0.0,
19 | -0.1,
20 | 0.1
21 | ],
22 | "T_variance": [
23 | 9.999999974752427e-7,
24 | 9.999999974752427e-7,
25 | 9.999999974752427e-7
26 | ],
27 | "W": [
28 | -1.1155,
29 | -1.1690,
30 | -1.2115
31 | ],
32 | "W_variance": [
33 | 9.999999974752427e-5,
34 | 9.999999974752427e-5,
35 | 9.999999974752427e-5
36 | ]
37 | }
38 | }
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/scripts/profile.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | Script to drive a TF model as fast as possible
4 |
5 | Usage:
6 | profile.py (--model=) (--type=)
7 |
8 | Options:
9 | -h --help Show this screen.
10 | """
11 | import os
12 | from docopt import docopt
13 | import donkeycar as dk
14 | import numpy as np
15 | from donkeycar.utils import FPSTimer
16 |
17 |
18 | def profile(model_path, model_type):
19 | cfg = dk.load_config('config.py')
20 | model_path = os.path.expanduser(model_path)
21 | model = dk.utils.get_model_by_type(model_type, cfg)
22 | model.load(model_path)
23 |
24 | h, w, ch = cfg.IMAGE_H, cfg.IMAGE_W, cfg.IMAGE_DEPTH
25 |
26 | # generate random array in the right shape in [0,1)
27 | img = np.random.randint(0, 255, size=(h, w, ch))
28 |
29 | # make a timer obj
30 | timer = FPSTimer()
31 |
32 | try:
33 | while True:
34 | # run inferencing
35 | model.run(img)
36 | # time
37 | timer.on_frame()
38 |
39 | except KeyboardInterrupt:
40 | pass
41 |
42 |
43 | if __name__ == '__main__':
44 | args = docopt(__doc__)
45 | profile(model_path=args['--model'], model_type=args['--type'])
46 |
--------------------------------------------------------------------------------
/donkeycar/parts/pytorch/torch_utils.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 |
4 | def get_model_by_type(model_type, cfg, checkpoint_path=None):
5 | '''
6 | given the string model_type and the configuration settings in cfg
7 | create a Torch model and return it.
8 | '''
9 | if model_type is None:
10 | model_type = cfg.DEFAULT_MODEL_TYPE
11 | print("\"get_model_by_type\" model Type is: {}".format(model_type))
12 |
13 | input_shape = (cfg.BATCH_SIZE, cfg.IMAGE_DEPTH, cfg.IMAGE_H, cfg.IMAGE_W)
14 |
15 | if model_type == "resnet18":
16 | from donkeycar.parts.pytorch.ResNet18 import ResNet18
17 | # ResNet18 will always use the following input size
18 | # regardless of what the user specifies. This is necessary since
19 | # the model is pre-trained on ImageNet
20 | input_shape = (cfg.BATCH_SIZE, 3, 224, 224)
21 | model = ResNet18(input_shape=input_shape)
22 | else:
23 | raise Exception("Unknown model type {:}, supported types are "
24 | "resnet18"
25 | .format(model_type))
26 |
27 | if checkpoint_path:
28 | print("Loading model from checkpoint {}".format(checkpoint_path))
29 | model.load_from_checkpoint(checkpoint_path)
30 |
31 | return model
32 |
--------------------------------------------------------------------------------
/donkeycar/gym/remote_controller.py:
--------------------------------------------------------------------------------
1 | '''
2 | file: remote_controller.py
3 | author: Tawn Kramer
4 | date: 2019-01-24
5 | desc: Control a remote donkey robot over network
6 | '''
7 |
8 | import time
9 |
10 | from donkeycar.parts.network import MQTTValueSub, MQTTValuePub
11 | from donkeycar.parts.image import JpgToImgArr
12 |
13 | class DonkeyRemoteContoller:
14 | def __init__(self, donkey_name, mqtt_broker, sensor_size=(120, 160, 3)):
15 | self.camera_sub = MQTTValueSub("donkey/%s/camera" % donkey_name, broker=mqtt_broker)
16 | self.controller_pub = MQTTValuePub("donkey/%s/controls" % donkey_name, broker=mqtt_broker)
17 | self.jpgToImg = JpgToImgArr()
18 | self.sensor_size = sensor_size
19 |
20 | def get_sensor_size(self):
21 | return self.sensor_size
22 |
23 | def wait_until_connected(self):
24 | pass
25 |
26 | def take_action(self, action):
27 | self.controller_pub.run(action)
28 |
29 | def quit(self):
30 | self.camera_sub.shutdown()
31 | self.controller_pub.shutdown()
32 |
33 | def get_original_image(self):
34 | return self.img
35 |
36 | def observe(self):
37 | jpg = self.camera_sub.run()
38 | self.img = self.jpgToImg.run(jpg)
39 | return self.img
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/donkeycar/tests/test_template.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from tempfile import gettempdir
4 | from donkeycar.templates import complete
5 | import donkeycar as dk
6 | import os
7 |
8 | from .setup import default_template, d2_path, custom_template
9 |
10 |
11 | def test_config():
12 | path = default_template(d2_path(gettempdir()))
13 | cfg = dk.load_config(os.path.join(path, 'config.py'))
14 | assert (cfg is not None)
15 |
16 |
17 | def test_drive():
18 | path = default_template(d2_path(gettempdir()))
19 | myconfig = open(os.path.join(path, 'myconfig.py'), "wt")
20 | myconfig.write("CAMERA_TYPE = 'MOCK'\n")
21 | myconfig.write("USE_SSD1306_128_32 = False \n")
22 | myconfig.write("DRIVE_TRAIN_TYPE = 'None'")
23 | myconfig.close()
24 | cfg = dk.load_config(os.path.join(path, 'config.py'))
25 | cfg.MAX_LOOPS = 10
26 | complete.drive(cfg=cfg)
27 |
28 |
29 | def test_custom_templates():
30 | template_names = ["complete", "basic", "square"]
31 | for template in template_names:
32 | path = custom_template(d2_path(gettempdir()), template=template)
33 | cfg = dk.load_config(os.path.join(path, 'config.py'))
34 | assert (cfg is not None)
35 | mcfg = dk.load_config(os.path.join(path, 'myconfig.py'))
36 | assert (mcfg is not None)
37 |
--------------------------------------------------------------------------------
/donkeycar/benchmarks/tub.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 | import shutil
4 | import timeit
5 | from pathlib import Path
6 |
7 | import numpy as np
8 |
9 | from donkeycar.parts.datastore import Tub
10 |
11 |
12 | def benchmark():
13 | # Change with a non SSD storage path
14 | path = Path('/media/rahulrav/Cruzer/tub')
15 | if os.path.exists(path.absolute().as_posix()):
16 | shutil.rmtree(path)
17 |
18 | inputs = ['input']
19 | types = ['int']
20 | tub = Tub(path.absolute().as_posix(), inputs, types)
21 | write_count = 1000
22 | for i in range(write_count):
23 | record = {'input': i}
24 | tub.put_record(record)
25 |
26 | # old tub starts counting at 1
27 | deletions = set(np.random.randint(1, write_count + 1, 100))
28 | for index in deletions:
29 | index = int(index)
30 | tub.remove_record(index)
31 |
32 | files = path.glob('*.json')
33 | for record_file in files:
34 | contents = record_file.read_text()
35 | if contents:
36 | contents = json.loads(contents)
37 | print('Record %s' % contents)
38 |
39 |
40 | if __name__ == "__main__":
41 | timer = timeit.Timer(benchmark)
42 | time_taken = timer.timeit(number=1)
43 | print('Time taken %s seconds' % time_taken)
44 | print('\nDone.')
45 |
--------------------------------------------------------------------------------
/donkeycar/parts/graph.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import cv2
3 |
4 |
5 | class Graph(object):
6 | '''
7 | Take input values and plot them on an image.
8 | Takes a list of (x, y) (b, g, r) pairs and
9 | plots the color at the given coordinate.
10 | When the x value exceeds the width, the graph is erased
11 | and begins with an offset to x values such that drawing
12 | begins again at the left edge.
13 | This assumes x is monotonically increasing, like a time value.
14 | '''
15 | def __init__(self, res=(200, 200, 3)):
16 | self.img = np.zeros(res)
17 | self.prev = 0
18 |
19 | def clamp(self, val, lo, hi):
20 | if val < lo:
21 | val = lo
22 | elif val > hi:
23 | val = hi
24 | return int(val)
25 |
26 | def run(self, values):
27 | if values is None:
28 | return self.img
29 |
30 | for coord, col in values:
31 | x = coord[0] % self.img.shape[1]
32 | y = self.clamp(coord[1], 0, self.img.shape[0] - 1)
33 | self.img[y, x] = col
34 |
35 | if abs(self.prev - x) > self.img.shape[1] / 2:
36 | self.img = np.zeros_like(self.img)
37 |
38 | self.prev = x
39 |
40 | return self.img
41 |
42 | def shutdown(self):
43 | pass
44 |
--------------------------------------------------------------------------------
/donkeycar/management/tub_web/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tub Manager
6 |
7 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
41 |
42 | {% block content %}{% end %}
43 |
44 |
45 |
--------------------------------------------------------------------------------
/donkeycar/parts/logger.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import List, Set, Dict, Tuple, Optional
3 |
4 |
5 | class LoggerPart:
6 | """
7 | Log the given values in vehicle memory.
8 | """
9 | def __init__(self, inputs: List[str], level: str="INFO", rate: int=1, logger=None):
10 | self.inputs = inputs
11 | self.rate = rate
12 | self.level = logging._nameToLevel.get(level, logging.INFO)
13 | self.logger = logging.getLogger(logger if logger is not None else "LoggerPart")
14 |
15 | self.values = {}
16 | self.count = 0
17 | self.running = True
18 |
19 | def run(self, *args):
20 | if self.running and args is not None and len(args) == len(self.inputs):
21 | self.count = (self.count + 1) % (self.rate + 1)
22 | for i in range(len(self.inputs)):
23 | field = self.inputs[i]
24 | value = args[i]
25 | old_value = self.values.get(field)
26 | if old_value != value:
27 | # always log changes
28 | self.logger.log(self.level, f"{field} = {old_value} -> {value}")
29 | self.values[field] = value
30 | elif self.count >= self.rate:
31 | self.logger.log(self.level, f"{field} = {value}")
32 |
33 | def shutdown(self):
34 | self.running = False
35 |
--------------------------------------------------------------------------------
/scripts/tflite_profile.py:
--------------------------------------------------------------------------------
1 | '''
2 | Usage:
3 | tflite_test.py --model="mymodel.tflite"
4 |
5 | Note:
6 | may require tensorflow > 1.11 or
7 | pip install tf-nightly
8 | '''
9 | import os
10 |
11 | from docopt import docopt
12 | import tensorflow as tf
13 | import numpy as np
14 |
15 | from donkeycar.utils import FPSTimer
16 |
17 | args = docopt(__doc__)
18 |
19 | in_model = os.path.expanduser(args['--model'])
20 |
21 | # Load TFLite model and allocate tensors.
22 | interpreter = tf.lite.Interpreter(model_path=in_model)
23 | interpreter.allocate_tensors()
24 |
25 | # Get input and output tensors.
26 | input_details = interpreter.get_input_details()
27 | output_details = interpreter.get_output_details()
28 |
29 | # Test model on random input data.
30 | input_shape = input_details[0]['shape']
31 | input_data = np.array(np.random.random_sample(input_shape), dtype=np.float32)
32 |
33 | interpreter.set_tensor(input_details[0]['index'], input_data)
34 | interpreter.invoke()
35 |
36 | #sample output
37 | for tensor in output_details:
38 | output_data = interpreter.get_tensor(tensor['index'])
39 | print(output_data)
40 |
41 | #run in a loop to test performance.
42 | print("test performance: hit CTRL+C to break")
43 | timer = FPSTimer()
44 | while True:
45 | interpreter.set_tensor(input_details[0]['index'], input_data)
46 | interpreter.invoke()
47 | timer.on_frame()
48 |
49 |
--------------------------------------------------------------------------------
/donkeycar/contrib/robohat/rear_light.py:
--------------------------------------------------------------------------------
1 | import neopixel
2 | import board
3 | import adafruit_logging as logging
4 | logger = logging.getLogger('rear_light')
5 | logger.setLevel(logging.INFO)
6 |
7 |
8 | class RearLight:
9 | """
10 | Class for controlling rear light
11 | """
12 | RED = (255, 0, 0)
13 | DARK = (0, 0, 0)
14 | YELLOW = (255, 150, 0)
15 |
16 | def __init__(self):
17 | pixel_pin = board.NEOPIXEL
18 | num_pixels = 16
19 | pixel_brightness = 0.4
20 | self.pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=pixel_brightness)
21 |
22 | self.is_brake_light_on = False
23 |
24 | def fill_pixels(self, color):
25 | self.pixels.fill(color)
26 | self.pixels.show()
27 |
28 | def turn_on_brake_light(self):
29 | if not self.is_brake_light_on:
30 | logger.info("turning on brake light")
31 | self.fill_pixels(RearLight.RED)
32 | self.is_brake_light_on = True
33 |
34 | def turn_off_brake_light(self):
35 | if self.is_brake_light_on:
36 | logger.info("turning off brake light")
37 | self.fill_pixels(RearLight.DARK)
38 | self.is_brake_light_on = False
39 |
40 | def run(self, angle, throttle):
41 | if throttle > 1600:
42 | self.turn_off_brake_light()
43 | else:
44 | self.turn_on_brake_light()
45 |
--------------------------------------------------------------------------------
/donkeycar/tests/test_catalog_v2.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import tempfile
4 | import time
5 | import unittest
6 | from pathlib import Path
7 |
8 | from donkeycar.parts.datastore_v2 import Catalog, CatalogMetadata, Seekable
9 |
10 |
11 | class TestCatalog(unittest.TestCase):
12 |
13 | def setUp(self):
14 | self._path = tempfile.mkdtemp()
15 | self._catalog_path = os.path.join(self._path, 'test.catalog')
16 |
17 | def test_basic_catalog_operations(self):
18 | catalog = Catalog(self._catalog_path)
19 | for i in range(0, 10):
20 | catalog.write_record(self._newRecord())
21 |
22 | self.assertEqual(os.path.exists(catalog.path.as_posix()), True)
23 | self.assertEqual(os.path.exists(catalog.manifest.manifest_path.as_posix()), True)
24 |
25 | catalog_2 = Catalog(self._catalog_path)
26 | catalog_2.seekable.seek_line_start(1)
27 | line = catalog_2.seekable.readline()
28 | count = 0
29 | while line is not None and len(line) > 0:
30 | print('Contents %s' % (line))
31 | count += 1
32 | line = catalog_2.seekable.readline()
33 |
34 | self.assertEqual(count, 10)
35 |
36 | def tearDown(self):
37 | shutil.rmtree(self._path)
38 |
39 | def _newRecord(self):
40 | record = {'at' : time.time()}
41 | return record
42 |
43 | if __name__ == '__main__':
44 | unittest.main()
45 |
--------------------------------------------------------------------------------
/donkeycar/parts/ros.py:
--------------------------------------------------------------------------------
1 | import rospy
2 | from std_msgs.msg import String, Int32, Float32
3 |
4 | '''
5 | sudo apt-get install python3-catkin-pkg
6 |
7 | ROS issues w python3:
8 | https://discourse.ros.org/t/should-we-warn-new-users-about-difficulties-with-python-3-and-alternative-python-interpreters/3874/3
9 | '''
10 |
11 | class RosPubisher(object):
12 | '''
13 | A ROS node to pubish to a data stream
14 | '''
15 | def __init__(self, node_name, channel_name, stream_type=String, anonymous=True):
16 | self.data = ""
17 | self.pub = rospy.Publisher(channel_name, stream_type)
18 | rospy.init_node(node_name, anonymous=anonymous)
19 |
20 | def run(self, data):
21 | '''
22 | only publish when data stream changes.
23 | '''
24 | if data != self.data and not rospy.is_shutdown():
25 | self.data = data
26 | self.pub.publish(data)
27 |
28 |
29 | class RosSubscriber(object):
30 | '''
31 | A ROS node to subscribe to a data stream
32 | '''
33 |
34 | def __init__(self, node_name, channel_name, stream_type=String, anonymous=True):
35 | self.data = ""
36 | rospy.init_node(node_name, anonymous=anonymous)
37 | self.pub = rospy.Subscriber(channel_name, stream_type, self.on_data_recv)
38 |
39 | def on_data_recv(self, data):
40 | self.data = data.data
41 |
42 | def run(self):
43 | return self.data
44 |
45 |
--------------------------------------------------------------------------------
/donkeycar/tests/test_tubwriter.py:
--------------------------------------------------------------------------------
1 | import shutil
2 | import tempfile
3 | import unittest
4 | from random import randint
5 |
6 | from donkeycar.parts.tub_v2 import Tub, TubWriter
7 |
8 |
9 | class TestTub(unittest.TestCase):
10 | def setUp(self):
11 | self._path = tempfile.mkdtemp()
12 |
13 | def test_tubwriter_sessions(self):
14 | # run tubwriter multiple times on the same tub directory
15 | write_counts = []
16 | for _ in range(5):
17 | tub_writer = TubWriter(self._path, inputs=['input'], types=['int'])
18 | write_count = randint(1, 10)
19 | for i in range(write_count):
20 | tub_writer.run(i)
21 | tub_writer.close()
22 | write_counts.append(write_count)
23 |
24 | # Check we have good session id for all new records:
25 | id = 0
26 | total = 0
27 | for record in tub_writer.tub:
28 | print(f'Record: {record}')
29 | session_number = int(record['_session_id'].split('_')[1])
30 | self.assertEqual(session_number, id,
31 | 'Session id not correctly generated')
32 | total += 1
33 | if total == write_counts[0]:
34 | total = 0
35 | id += 1
36 | write_counts.pop(0)
37 |
38 | def tearDown(self):
39 | shutil.rmtree(self._path)
40 |
41 |
42 | if __name__ == '__main__':
43 | unittest.main()
44 |
--------------------------------------------------------------------------------
/scripts/salient_vis_listener.py:
--------------------------------------------------------------------------------
1 | """
2 | Scripts to drive a donkey 2 car
3 |
4 | Usage:
5 | salient_vis_listener.py [--ip="localhost"] [--model=] [--type=(linear|categorical|rnn|imu|behavior|3d|localizer)] [--config="config.py"]
6 |
7 |
8 | Options:
9 | -h --help Show this screen.
10 | """
11 | import os
12 | import time
13 | import math
14 | from docopt import docopt
15 | import donkeycar as dk
16 |
17 | from donkeycar.parts.cv import CvImageView, ImgBGR2RGB, ImgRGB2BGR, ImageScale, ImgWriter
18 | from donkeycar.parts.salient import SalientVis
19 | from donkeycar.parts.network import ZMQValueSub, UDPValueSub, TCPClientValue
20 | from donkeycar.parts.transform import Lambda
21 | from donkeycar.parts.image import JpgToImgArr
22 |
23 | V = dk.vehicle.Vehicle()
24 | args = docopt(__doc__)
25 | cfg = dk.load_config(args['--config'])
26 |
27 | model_path = args['--model']
28 | model_type = args['--type']
29 | ip = args['--ip']
30 |
31 | if model_type is None:
32 | model_type = "categorical"
33 |
34 | model = dk.utils.get_model_by_type(model_type, cfg)
35 | model.load(model_path)
36 |
37 | V.add(TCPClientValue(name="camera", host=ip), outputs=["packet"])
38 | V.add(JpgToImgArr(), inputs=["packet"], outputs=["img"])
39 | V.add(ImgBGR2RGB(), inputs=["img"], outputs=["img"])
40 | V.add(SalientVis(model), inputs=["img"], outputs=["img"])
41 | V.add(ImageScale(4.0), inputs=["img"], outputs=["lg_img"])
42 | V.add(CvImageView(), inputs=["lg_img"])
43 |
44 | V.start(rate_hz=1)
45 |
46 |
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Donkey Monitor
6 |
7 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
44 |
45 | {% block content %}{% end %}
46 |
47 |
48 |
--------------------------------------------------------------------------------
/donkeycar/parts/fps.py:
--------------------------------------------------------------------------------
1 | import time
2 | import logging
3 |
4 | logger = logging.getLogger(__name__)
5 |
6 |
7 | class FrequencyLogger(object):
8 | """
9 | Log current, min and max of frequency value
10 | """
11 |
12 | def __init__(self, debug_interval=10):
13 | self.last_timestamp = None
14 | self.counter = 0
15 | self.fps = 0
16 | self.fps_list = []
17 |
18 | self.last_debug_timestamp = None
19 | self.debug_interval = debug_interval
20 |
21 | def run(self):
22 | if self.last_timestamp is None:
23 | self.last_timestamp = time.time()
24 |
25 | if time.time() - self.last_timestamp > 1:
26 | self.fps = self.counter
27 | self.fps_list.append(self.counter)
28 | self.counter = 0
29 | self.last_timestamp = time.time()
30 | else:
31 | self.counter += 1
32 |
33 | # Printing frequency info into shell
34 | if self.last_debug_timestamp is None:
35 | self.last_debug_timestamp = time.time()
36 |
37 | if time.time() - self.last_debug_timestamp > self.debug_interval:
38 | logger.info(f"current fps = {self.fps}")
39 | self.last_debug_timestamp = time.time()
40 |
41 | return self.fps, self.fps_list
42 |
43 | def shutdown(self):
44 | if self.fps_list:
45 | logger.info(f"fps (min/max) = {min(self.fps_list):2d} / {max(self.fps_list):2d}")
46 | logger.info(f"fps list = {self.fps_list}".format())
47 |
--------------------------------------------------------------------------------
/donkeycar/tests/test_odometer.py:
--------------------------------------------------------------------------------
1 |
2 | import unittest
3 | import time
4 |
5 | from donkeycar.parts.odometer import Odometer
6 |
7 | class TestOdometer(unittest.TestCase):
8 |
9 | def test_odometer(self):
10 | odometer = Odometer(0.2) # 0.2 meters per revolution
11 |
12 | ts = time.time() # initial time
13 | distance, velocity, timestamp = odometer.run(1, ts) # first reading is one revolution
14 | self.assertEqual(ts, timestamp)
15 | self.assertEqual(0.2, distance) # distance travelled is 0.2 meters
16 | self.assertEqual(0, velocity) # zero velocity until we get two distances
17 |
18 | ts += 1 # add one second
19 | distance, velocity, timestamp = odometer.run(2, ts) # total of 2 revolutions
20 | self.assertEqual(ts, timestamp)
21 | self.assertEqual(0.4, distance) # 0.4 meters travelled
22 | self.assertEqual(0.2, velocity) # 0.2 meters per second since last update
23 |
24 | ts += 1 # add one second
25 | distance, velocity, timestamp = odometer.run(2, ts) # don't move
26 | self.assertEqual(ts, timestamp)
27 | self.assertEqual(0.4, distance) # still at 0.4 meters travelled
28 | self.assertEqual(0, velocity) # 0 meter per second in last interval
29 |
30 |
--------------------------------------------------------------------------------
/scripts/graph_listener.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import time
4 | import math
5 | from docopt import docopt
6 | import donkeycar as dk
7 |
8 | from donkeycar.parts.cv import CvImageView
9 | from donkeycar.parts.graph import Graph
10 | from donkeycar.parts.network import ZMQValueSub
11 | from donkeycar.parts.transform import Lambda
12 |
13 | V = dk.vehicle.Vehicle()
14 | ip = "localhost"
15 | w = 640
16 | h = 480
17 | d = 3
18 |
19 | def condition_values(obj):
20 | if obj is None:
21 | return None
22 | '''
23 | This expects a tuple of 4 values.
24 | The first value is time (x), and the rest are y values
25 | from -2, +2
26 | This will work with the network publisher test:
27 | python ~/projects/donkey_tkramer/donkeycar/parts/network.py
28 | '''
29 |
30 | vals = obj[1:]
31 | x = round(obj[0] * 30.0)
32 | ret = []
33 |
34 | i = 0
35 | for val in vals:
36 | coord = (x, val * (h / 4.) + (h / 2.))
37 | color = [0, 0, 0]
38 | color[i] = 1
39 | i += 1
40 | ret.append( (coord, color) )
41 |
42 | #a solid white center line.
43 | coord = (x, h / 2.0)
44 | color = (1.0, 1.0, 1.0)
45 | ret.append( (coord, color) )
46 |
47 | return ret
48 |
49 | l = Lambda(condition_values)
50 |
51 | V.add(ZMQValueSub(name="test", ip=ip), outputs=["obj"])
52 | V.add(l, inputs=["obj"], outputs=["values"])
53 | V.add(Graph(res=(h, w, d)), inputs=["values"], outputs=["graph/img"])
54 | V.add(CvImageView(), inputs=["graph/img"])
55 |
56 | V.start(rate_hz=10)
57 |
--------------------------------------------------------------------------------
/scripts/remote_cam_view_tcp.py:
--------------------------------------------------------------------------------
1 | """
2 | Script to view a donkeycar camera remotely (when published using TcpServer)
3 |
4 | Usage:
5 | remote_cam_view_tcp.py (--ip=) [--record=]
6 |
7 | Options:
8 | -h --help Show this screen.
9 | --record= If data should be recorded (locally) specify the path
10 |
11 | """
12 | from docopt import docopt
13 | import donkeycar as dk
14 | from donkeycar.parts.cv import CvImageView, ImgBGR2RGB, ImageScale
15 | from donkeycar.parts.network import TCPClientValue
16 | from donkeycar.parts.image import JpgToImgArr
17 |
18 | args = docopt(__doc__)
19 | print(args)
20 |
21 | V = dk.vehicle.Vehicle()
22 | V.add(TCPClientValue("camera", args["--ip"]), outputs=["jpg"])
23 | V.add(JpgToImgArr(), inputs=["jpg"], outputs=["img_arr"])
24 | V.add(ImgBGR2RGB(), inputs=["img_arr"], outputs=["rgb"])
25 | V.add(ImageScale(4.0), inputs=["rgb"], outputs=["lg_img"])
26 | V.add(CvImageView(), inputs=["lg_img"])
27 |
28 | # Local saving of images?
29 | record_path = args["--record"]
30 | if record_path is not None:
31 | class ImageSaver:
32 | def __init__(self, path):
33 | self.index = 0
34 | self.path = path
35 |
36 | def run(self, img_arr):
37 | if img_arr is None:
38 | return
39 | dest_path = os.path.join(self.path, "img_%d.jpg" % self.index)
40 | self.index += 1
41 | cv2.imwrite(dest_path, img_arr)
42 |
43 | V.add(ImageSaver(record_path), inputs=["rgb"])
44 |
45 | V.start(rate_hz=20)
46 |
--------------------------------------------------------------------------------
/scripts/convert_to_tflite.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | Usage:
4 | convert_to_tflite.py [-o | --overwrite] ...
5 |
6 | Options:
7 | -h --help Show this screen.
8 | -o --overwrite Force overwriting existing TFLite files
9 |
10 | Note:
11 | This script converts a keras (.h5) or tensorflow (.savedmodel) into
12 | TFlite. Supports multiple input files.
13 |
14 | """
15 |
16 | from docopt import docopt
17 | from donkeycar.parts.interpreter import keras_model_to_tflite
18 | from os.path import splitext, exists
19 |
20 | if __name__ == '__main__':
21 | args = docopt(__doc__)
22 | model_path_list = args['']
23 | overwrite = args['-o'] or args['--overwrite']
24 | print(f"Found {len(model_path_list)} models to process.")
25 | print(f"Overwrite set to: {overwrite}.")
26 | count = 0
27 | for model_path in model_path_list:
28 | base_path, ext = splitext(model_path)
29 | if ext not in ['.h5', '.savedmodel']:
30 | print(f"Can only convert '.h5' or '.savedmodel' but not {ext}")
31 | continue
32 | tflite_filename = base_path + '.tflite'
33 | if exists(tflite_filename) and not overwrite:
34 | print(f"Found existing tflite mode {tflite_filename}, will skip. "
35 | f"If you want to overwrite existing files, please specify "
36 | f"the option --overwrite or -o ")
37 | continue
38 | keras_model_to_tflite(model_path, tflite_filename)
39 | count += 1
40 | print(f"Finished converting {count} models")
41 |
--------------------------------------------------------------------------------
/scripts/preview_augumentations.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | Usage:
4 | preview_augmentations.py
5 |
6 | Note:
7 | This script helps preview augmentations used when the model is being trained.
8 | """
9 |
10 | import time
11 | import cv2
12 |
13 | from donkeycar.pipeline.augmentations import Augmentations
14 |
15 | # Camera Parameters
16 | WIDTH = 640
17 | HEIGHT = 480
18 |
19 | # Example augumentations
20 | cropping = Augmentations.crop(0, 0, 100, 0, keep_size=True)
21 | mask = Augmentations.trapezoidal_mask(10, 630, 100, 300, 50, 480)
22 |
23 |
24 | def preview_augmentations():
25 | print('Connecting to Camera')
26 | capture = cv2.VideoCapture(0)
27 | time.sleep(2)
28 | if capture.isOpened():
29 | print('Camera Connected.')
30 | else:
31 | print('Unable to connect. Are you sure you are using the right camera parameters ?')
32 | return
33 |
34 | while True:
35 | success, frame = capture.read()
36 | if success:
37 | cropped = cropping.augment_image(frame)
38 | masked = mask.augment_image(frame)
39 | # Convert to RGB
40 | cv2.imshow('Preview', frame)
41 | cv2.imshow('Cropped', cropped)
42 | cv2.imshow('Trapezoidal Mask', masked)
43 | prompt = cv2.waitKey(1) & 0xFF
44 | if prompt == ord(' '):
45 | # Store output
46 | pass
47 | elif prompt == ord('q'):
48 | break
49 |
50 | capture.release()
51 | cv2.destroyAllWindows()
52 |
53 |
54 | if __name__ == "__main__":
55 | preview_augmentations()
56 |
--------------------------------------------------------------------------------
/donkeycar/parts/launch.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | class AiLaunch():
4 | '''
5 | This part will apply a large thrust on initial activation. This is to help
6 | in racing to start fast and then the ai will take over quickly when it's
7 | up to speed.
8 | '''
9 |
10 | def __init__(self, launch_duration=1.0, launch_throttle=1.0, keep_enabled=False):
11 | self.active = False
12 | self.enabled = False
13 | self.timer_start = None
14 | self.timer_duration = launch_duration
15 | self.launch_throttle = launch_throttle
16 | self.prev_mode = None
17 | self.trigger_on_switch = keep_enabled
18 |
19 | def enable_ai_launch(self):
20 | self.enabled = True
21 | print('AiLauncher is enabled.')
22 |
23 | def run(self, mode, ai_throttle):
24 | new_throttle = ai_throttle
25 |
26 | if mode != self.prev_mode:
27 | self.prev_mode = mode
28 | if mode == "local" and self.trigger_on_switch:
29 | self.enabled = True
30 |
31 | if mode == "local" and self.enabled:
32 | if not self.active:
33 | self.active = True
34 | self.timer_start = time.time()
35 | else:
36 | duration = time.time() - self.timer_start
37 | if duration > self.timer_duration:
38 | self.active = False
39 | self.enabled = False
40 | else:
41 | self.active = False
42 |
43 | if self.active:
44 | print('AiLauncher is active!!!')
45 | new_throttle = self.launch_throttle
46 |
47 | return new_throttle
48 |
49 |
--------------------------------------------------------------------------------
/donkeycar/parts/perfmon.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Performance monitor for analyzing real-time CPU/mem/execution frequency
5 |
6 | author: @miro (Meir Tseitlin) 2020
7 |
8 | Note:
9 | """
10 | import time
11 | import psutil
12 |
13 |
14 | class PerfMonitor:
15 |
16 | def __init__(self, cfg):
17 |
18 | self.STATS_BUFFER_SIZE = 10
19 | self._calc_buffer = [cfg.DRIVE_LOOP_HZ for i in range(self.STATS_BUFFER_SIZE)]
20 | self._runs_counter = 0
21 | self._last_calc_time = time.time()
22 | self._on = True
23 | self._update_metrics()
24 | print("Performance monitor activated.")
25 |
26 | def _update_metrics(self):
27 | self._mem_percent = psutil.virtual_memory().percent
28 | self._cpu_percent = psutil.cpu_percent()
29 |
30 | def update(self):
31 | while self._on:
32 | self._update_metrics()
33 | time.sleep(2)
34 |
35 | def shutdown(self):
36 | # indicate that the thread should be stopped
37 | self._on = False
38 | print('Stopping Perf Monitor')
39 | time.sleep(.2)
40 |
41 | def run_threaded(self):
42 |
43 | # Calc real frequency
44 | curr_time = time.time()
45 | if curr_time - self._last_calc_time > 1:
46 | self._calc_buffer[int(curr_time) % self.STATS_BUFFER_SIZE] = self._runs_counter
47 | self._runs_counter = 0
48 | self._last_calc_time = curr_time
49 |
50 | self._runs_counter += 1
51 |
52 | vehicle_frequency = float(sum(self._calc_buffer)) / self.STATS_BUFFER_SIZE
53 |
54 | return self._cpu_percent, self._mem_percent, vehicle_frequency
55 |
--------------------------------------------------------------------------------
/donkeycar/tests/test_vehicle.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import donkeycar as dk
3 | from donkeycar.parts.transform import Lambda
4 |
5 |
6 | def _get_sample_lambda():
7 | def f():
8 | return 1
9 | f.update = f
10 | return Lambda(f)
11 |
12 |
13 | @pytest.fixture()
14 | def vehicle():
15 | v = dk.Vehicle()
16 | v.add(_get_sample_lambda(), outputs=['test_out'])
17 | return v
18 |
19 |
20 | def test_create_vehicle():
21 | v = dk.Vehicle()
22 | assert v.parts == []
23 |
24 |
25 | def test_add_part():
26 | v = dk.Vehicle()
27 | v.add(_get_sample_lambda(), outputs=['test_out'])
28 | assert len(v.parts) == 1
29 |
30 |
31 | def test_vehicle_run(vehicle):
32 | vehicle.start(rate_hz=20, max_loop_count=2)
33 | assert vehicle is not None
34 |
35 |
36 | def test_should_raise_assertion_on_non_list_inputs_for_add_part():
37 | vehicle = dk.Vehicle()
38 | inputs = 'any'
39 | with pytest.raises(AssertionError):
40 | vehicle.add(_get_sample_lambda(), inputs=inputs)
41 | pytest.fail("inputs is not a list: %r" % inputs)
42 |
43 |
44 | def test_should_raise_assertion_on_non_list_outputs_for_add_part():
45 | vehicle = dk.Vehicle()
46 | outputs = 'any'
47 | with pytest.raises(AssertionError):
48 | vehicle.add(_get_sample_lambda(), outputs=outputs)
49 | pytest.fail("outputs is not a list: %r" % outputs)
50 |
51 |
52 | def test_should_raise_assertion_on_non_boolean_threaded_for_add_part():
53 | vehicle = dk.Vehicle()
54 | threaded = 'non_boolean'
55 | with pytest.raises(AssertionError):
56 | vehicle.add(_get_sample_lambda(), threaded=threaded)
57 | pytest.fail("threaded is not a boolean: %r" % threaded)
--------------------------------------------------------------------------------
/donkeycar/parts/serial_controller.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | Scrits to read signals from Arduino and convert into steering and throttle outputs
4 | Arduino input signal range: 0 to 200
5 | Output range: -1.00 to 1.00
6 | """
7 |
8 | import serial
9 | import time
10 |
11 | class SerialController:
12 | def __init__(self):
13 | print("Starting Serial Controller")
14 |
15 | self.angle = 0.0
16 | self.throttle = 0.0
17 | self.mode = 'user'
18 | self.recording = False
19 | self.serial = serial.Serial('/dev/ttyS0', 115200, timeout=1) #Serial port - laptop: 'COM3', Arduino: '/dev/ttyACM0'
20 |
21 |
22 | def update(self):
23 | # delay on startup to avoid crashing
24 | print("Warming Serial Controller")
25 | time.sleep(3)
26 |
27 | while True:
28 | line = str(self.serial.readline().decode()).strip('\n').strip('\r')
29 | output = line.split(", ")
30 | if len(output) == 2:
31 | if output[0].isnumeric() and output[1].isnumeric():
32 | self.angle = (float(output[0])-1500)/500
33 | self.throttle = (float(output[1])-1500)/500
34 | if self.throttle > 0.01:
35 | self.recording = True
36 | print("Recording")
37 | else:
38 | self.recording = False
39 | time.sleep(0.01)
40 |
41 | def run(self, img_arr=None):
42 | return self.run_threaded()
43 |
44 | def run_threaded(self, img_arr=None):
45 | #print("Signal:", self.angle, self.throttle)
46 | return self.angle, self.throttle, self.mode, self.recording
47 |
--------------------------------------------------------------------------------
/donkeycar/memory.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Created on Sun Jun 25 11:07:48 2017
5 |
6 | @author: wroscoe
7 | """
8 |
9 | class Memory:
10 | """
11 | A convenience class to save key/value pairs.
12 | """
13 | def __init__(self, *args, **kw):
14 | self.d = {}
15 |
16 | def __setitem__(self, key, value):
17 | if type(key) is str:
18 | self.d[key] = value
19 | else:
20 | if type(key) is not tuple:
21 | key = tuple(key)
22 | value = tuple(key)
23 | for i, k in enumerate(key):
24 | self.d[k] = value[i]
25 |
26 | def __getitem__(self, key):
27 | if type(key) is tuple:
28 | return [self.d[k] for k in key]
29 | else:
30 | return self.d[key]
31 |
32 | def update(self, new_d):
33 | self.d.update(new_d)
34 |
35 | def put(self, keys, inputs):
36 | if len(keys) > 1:
37 | for i, key in enumerate(keys):
38 | try:
39 | self.d[key] = inputs[i]
40 | except IndexError as e:
41 | error = str(e) + ' issue with keys: ' + str(key)
42 | raise IndexError(error)
43 |
44 | else:
45 | self.d[keys[0]] = inputs
46 |
47 |
48 |
49 | def get(self, keys):
50 | result = [self.d.get(k) for k in keys]
51 | return result
52 |
53 | def keys(self):
54 | return self.d.keys()
55 |
56 | def values(self):
57 | return self.d.values()
58 |
59 | def items(self):
60 | return self.d.items()
61 |
--------------------------------------------------------------------------------
/donkeycar/parts/behavior.py:
--------------------------------------------------------------------------------
1 | class BehaviorPart(object):
2 | '''
3 | Keep a list of states, and an active state. Keep track of switching.
4 | And return active state information.
5 | '''
6 | def __init__(self, states):
7 | '''
8 | expects a list of strings to enumerate state
9 | '''
10 | print("bvh states:", states)
11 | self.states = states
12 | self.active_state = 0
13 | self.one_hot_state_array = []
14 | for i in range(len(states)):
15 | self.one_hot_state_array.append(0.0)
16 | self.one_hot_state_array[0] = 1.0
17 |
18 | def increment_state(self):
19 | self.one_hot_state_array[self.active_state] = 0.0
20 | self.active_state += 1
21 | if self.active_state >= len(self.states):
22 | self.active_state = 0
23 | self.one_hot_state_array[self.active_state] = 1.0
24 | print("In State:", self.states[self.active_state])
25 |
26 | def decrement_state(self):
27 | self.one_hot_state_array[self.active_state] = 0.0
28 | self.active_state -= 1
29 | if self.active_state < 0:
30 | self.active_state = len(self.states) - 1
31 | self.one_hot_state_array[self.active_state] = 1.0
32 | print("In State:", self.states[self.active_state])
33 |
34 | def set_state(self, iState):
35 | self.one_hot_state_array[self.active_state] = 0.0
36 | self.active_state = iState
37 | self.one_hot_state_array[self.active_state] = 1.0
38 | print("In State:", self.states[self.active_state])
39 |
40 | def run(self):
41 | return self.active_state, self.states[self.active_state], self.one_hot_state_array
42 |
43 | def shutdown(self):
44 | pass
45 |
--------------------------------------------------------------------------------
/scripts/remote_cam_view.py:
--------------------------------------------------------------------------------
1 | """
2 | Scripts to drive a donkey car remotely
3 |
4 | Usage:
5 | remote_cam_view.py --name= --broker="localhost" [--record=]
6 |
7 |
8 | Options:
9 | -h --help Show this screen.
10 | """
11 | import os
12 | import time
13 | import math
14 | from docopt import docopt
15 | import donkeycar as dk
16 | import cv2
17 |
18 | from donkeycar.parts.cv import CvImageView, ImgBGR2RGB, ImgRGB2BGR, ImageScale, ImgWriter, ArrowKeyboardControls
19 | from donkeycar.parts.salient import SalientVis
20 | from donkeycar.parts.network import MQTTValuePub, MQTTValueSub
21 | from donkeycar.parts.transform import Lambda
22 | from donkeycar.parts.image import JpgToImgArr
23 |
24 | V = dk.vehicle.Vehicle()
25 | args = docopt(__doc__)
26 | print(args)
27 |
28 | V.add(MQTTValueSub(name="donkey/%s/camera" % args["--name"], broker=args["--broker"]), outputs=["jpg"])
29 | V.add(JpgToImgArr(), inputs=["jpg"], outputs=["img_arr"])
30 | V.add(ImgBGR2RGB(), inputs=["img_arr"], outputs=["rgb"])
31 | V.add(ImageScale(4.0), inputs=["rgb"], outputs=["lg_img"])
32 | V.add(CvImageView(), inputs=["lg_img"])
33 |
34 | V.add(ArrowKeyboardControls(), outputs=["control"])
35 | V.add(MQTTValuePub(name="donkey/%s/controls" % args["--name"]), inputs=["control"])
36 |
37 | record_path = args["--record"]
38 | if record_path is not None:
39 | class ImageSaver:
40 | def __init__(self, path):
41 | self.index = 0
42 | self.path = path
43 |
44 | def run(self, img_arr):
45 | if img_arr is None:
46 | return
47 | dest_path = os.path.join(self.path, "img_%d.jpg" % self.index)
48 | self.index += 1
49 | cv2.imwrite(dest_path, img_arr)
50 |
51 | V.add(ImageSaver(record_path), inputs=["rgb"])
52 |
53 |
54 | V.start(rate_hz=20)
55 |
56 |
--------------------------------------------------------------------------------
/donkeycar/parts/web_controller/templates/wsTest.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Websocket Test
7 |
8 |
9 |
10 |
63 |
64 |
65 |