104 Commits

Author SHA1 Message Date
Packt-ITService 34528fbd5d add free ebook notification 2023-03-19 04:30:39 +05:30
Packt-ITService 49d0fb35b3 add free ebook notification 2023-03-18 04:30:51 +05:30
rajat-packt 366b0feb40 Create README.md 2023-02-20 02:31:55 +05:30
Danny Staple 766ef5261e Tidy up - leave only the working one here. 2023-02-06 22:42:39 +00:00
Danny Staple 0bd04f3fec Tidy up these figure generators 2023-02-06 22:40:30 +00:00
Danny Staple ae79a2f8c1 Review comments
- Indentation issues
- Inconsistencies
2023-02-06 20:26:04 +00:00
Danny Staple 542e92a3ee Push back the low_probability concept and fixes to finding weights. 2023-01-30 23:18:35 +00:00
Danny Staple d507ebec6a Further bugfix on the arena contains function 2023-01-30 22:59:49 +00:00
Danny Staple 985b8d844a More observations - more fixes. 2023-01-30 22:34:00 +00:00
Danny Staple aa2bc483f0 Extra debug revealed a problem with the sensor calculations. 2023-01-30 14:02:34 +00:00
Danny Staple e557eb74a7 Resampling index error appears to be gone. Still need more dialing in. 2023-01-29 22:16:12 +00:00
Danny Staple d1d13fc618 Undo concatenate experiment 2023-01-29 13:43:02 +00:00
Danny Staple 416bb52ebf Latest changes for error and perf checking 2023-01-28 21:32:02 +00:00
Danny Staple a4f71ef889 Be explicit about the value and tb parameters.
See https://github.com/adafruit/circuitpython/issues/7482
2023-01-24 22:38:38 +00:00
Danny Staple dd3fd206dc Add amended version of 4.3 with performance reporting and some fixes. 2023-01-22 22:56:12 +00:00
Danny Staple 172daae295 Base the boundary on tweakable values 2023-01-21 23:20:32 +00:00
Danny Staple 7208a9472f Another rewording 2023-01-21 20:04:14 +00:00
Danny Staple 7b894dd5a8 Simplify code wording 2023-01-21 20:00:43 +00:00
Danny Staple 2b720cedca This rename makes the code easier to type and read. 2023-01-21 19:54:31 +00:00
Danny Staple 2d785f90ec Leave utf-8 to be implicit. 2023-01-21 09:43:17 +00:00
Danny Staple 368b9ed361 Reviewer comment - Extend ax into axes. 2023-01-19 23:19:43 +00:00
Danny Staple fa67d9e21a Make a nicer error when the robot isn't found. 2023-01-19 23:12:21 +00:00
Danny Staple a509fa540c where did that come from 2023-01-19 23:07:32 +00:00
Danny Staple ccb94f4091 Add the device match check to find devices.
Leave the example only in the section it is used.
2023-01-19 23:04:02 +00:00
Danny Staple 7907a3fecf Rename folders as per suggestion from Rajat 2023-01-18 13:57:13 +00:00
Danny Staple a7e7d55d62 Time budget override not needed - 50 is default. 2023-01-18 13:41:59 +00:00
Danny Staple 2db7a0fe3c Chapter 13 reduction 2023-01-08 23:11:35 +00:00
Danny Staple 68cfd39a19 Organising the chapter 2023-01-08 22:54:13 +00:00
Danny Staple f7695b4290 Ch-11 make teh robot stop. 2023-01-05 21:49:49 +00:00
Danny Staple 9fa461fb2a Chapter 12 TR 2023-01-04 23:11:28 +00:00
Danny Staple fe92edb605 Propagate the send_line change down 2023-01-04 22:50:25 +00:00
Danny Staple 9e39796cf2 Chapter 11 TR 2023-01-03 22:05:12 +00:00
Danny Staple dd22da9d3d Error handling. Simpler prints. 2023-01-03 21:40:11 +00:00
Danny Staple a9072b523e PID controller for TR11 matches text. 2023-01-02 13:18:25 +00:00
Danny Staple d56b89787e Simplify 2023-01-02 12:26:53 +00:00
Danny Staple 92e3f85094 TR-11 Swapping out G command. Making line sending more compact 2023-01-02 12:04:49 +00:00
Danny Staple a137464ced Clear up old versions 2022-12-28 20:50:51 +00:00
Danny Staple d168b2664d We now have the distance sensor full code working 2022-12-28 20:49:55 +00:00
Danny Staple 6ebb615044 Making the numbers match topic numbers 2022-12-28 18:44:53 +00:00
Danny Staple 6e8606fbeb Make the example numbering consistent 2022-12-28 18:40:46 +00:00
Danny Staple 180bc33561 Add section 3.1 - generating weights and resampling 2022-12-28 18:38:06 +00:00
Danny Staple b47fa45a20 Another removed example 2022-12-28 16:52:40 +00:00
Danny Staple 2923c6e125 Remove examples we have replaced. 2022-12-28 16:52:27 +00:00
Danny Staple 44509f515b Stop teh collision avoider task - so the robot stops moving on error. 2022-12-28 16:52:08 +00:00
Danny Staple 6535128a06 Movement with uncertainty 2022-12-28 13:26:38 +00:00
Danny Staple c87ef96477 Move the computer folder up - a bit easier to manage.
Integrate fixes found in earlier examples.
2022-12-28 11:27:16 +00:00
Danny Staple 9cff9fe1f1 Rename this. Also break down the apply method. 2022-12-28 11:26:40 +00:00
Danny Staple 584e189cd1 Encoder based pose movement. 2022-12-23 21:28:58 +00:00
Danny Staple 411baece11 More unwatned repeat busio imports 2022-12-23 10:52:38 +00:00
Danny Staple 661edf9133 Remove the extra busio import 2022-12-23 10:51:20 +00:00
Danny Staple 1ed91022ae Collision avoidance section ready 2022-12-22 10:44:25 +00:00
Danny Staple c14c83e286 Displaying a pose written up. 2022-12-21 14:16:37 +00:00
Danny Staple 752c298f8d Examples firmed up for first section. 2022-12-21 13:23:44 +00:00
Danny Staple 66738da6d9 Resampling and angle calculation fixes. 2022-12-18 15:06:45 +00:00
Danny Staple d896e2a80a Ch13 current state 2022-12-17 20:27:39 +00:00
Danny Staple 45ba981386 Ch-10 TR feedback 2022-12-17 16:05:25 +00:00
Danny Staple b2228889e8 Beam endpoint works. 2022-12-14 23:54:31 +00:00
Danny Staple f57add5106 Instrumented version - to look at timings
Including:
- replacing guassian with simpler triangle distribution
- Timing calculations -resulting in using only 50 points.
- Changes (which may need to be reconsidered) for optimization. These are likely to be simplified back.
2022-12-11 20:44:50 +00:00
Danny Staple 46daf66024 Fixes - found the heading was incorrect.
Make the loops tighter.
Remove some debug output.
Simplify the collision avoidance.
2022-12-06 21:58:22 +00:00
Danny Staple 389f2f768a Display working. Sensors starting to work.
Clusters are forming.
2022-12-06 21:32:47 +00:00
Danny Staple d3e128e010 Bugfixing monte carlo 2022-12-06 13:53:57 +00:00
Danny Staple 9b98bd4db7 Remember to eliminate points outside the arena 2022-12-06 08:48:07 +00:00
Danny Staple 543f4f3f0b Try again - using resampling and weighted observation model 2022-12-05 22:27:51 +00:00
Danny Staple bac9e4b69c Current state - before further changes 2022-12-04 19:03:49 +00:00
Danny Staple 38e26f9bb9 Move this to 7 - it was closer to that example.
Some further fixes - and an experiment.
2022-11-22 22:19:25 +00:00
Danny Staple 579ed7632b Generate the image shown in this section 2022-11-22 13:15:32 +00:00
Danny Staple bcf9586c5c These sections need testing 2022-11-22 13:15:10 +00:00
Danny Staple fc7ceec136 Test code to reproduce issue seen with newlines 2022-11-22 13:14:49 +00:00
Danny Staple 5a8ea3cd87 try an "all in on numpy" version 2022-11-22 13:14:35 +00:00
Danny Staple 97018fc119 Refinements - ensure gca/gcf are gone. 2022-11-16 22:44:25 +00:00
Danny Staple 66a49b4ec3 Refining the distance sensor part. 2022-11-15 22:48:59 +00:00
Danny Staple 2c91a42160 Using GCA and GCF causes weird stuff to happen when buttons are added. 2022-11-15 22:34:17 +00:00
Danny Staple 1cfd93887b Start on eliminating poses 2022-11-15 21:25:07 +00:00
Danny Staple 605b0afa9b Put into a function.
break down the
large function
2022-11-15 21:24:59 +00:00
Danny Staple 02c1f81756 More removing embelishments 2022-11-13 20:44:15 +00:00
Danny Staple d2264f4421 Simplify the ble connection a little 2022-11-13 19:21:55 +00:00
Danny Staple d1aa653e1b Make robust 2022-11-13 19:11:50 +00:00
Danny Staple 41ef282cd7 Remove embelishments 2022-11-13 19:11:40 +00:00
Danny Staple f6f7e0f2eb Sensor movement of poses 2022-11-13 19:11:20 +00:00
Danny Staple be73bc3dc3 Improve serial handling - make similar to other examples. 2022-11-13 19:11:04 +00:00
Danny Staple d3e380ba5f Reduce embellishments 2022-11-13 19:10:03 +00:00
Danny Staple 1d31bee775 Make a little robust with lessons learned later 2022-11-13 18:46:59 +00:00
Danny Staple e444040b86 The robot moves - not sure the arrows face the right way. 2022-11-10 22:25:09 +00:00
Danny Staple 80076b24bf No longer using this example 2022-11-10 21:50:14 +00:00
Danny Staple 5d25b86732 This can display poses from the robot 2022-11-10 21:50:00 +00:00
Danny Staple 2f3e4193b9 Update for modelling - remove what we don't yet need. 2022-11-10 21:49:43 +00:00
Danny Staple 5a951d3b64 Further simplify for this example 2022-11-03 22:55:02 +00:00
Danny Staple 8e8783d9b2 Further ways to simplify code 2022-11-03 22:13:48 +00:00
Danny Staple 0a8a86960d Do this without the asyncio with. 2022-11-03 16:34:28 +00:00
Danny Staple d03c48bf8d Simplify a little 2022-11-03 16:24:51 +00:00
Danny Staple 601d0de7dd Fix up the poetry dependancies 2022-11-03 14:28:32 +00:00
Danny Staple be368a6644 Chapter 13 modelling space initial. 2022-11-03 14:16:08 +00:00
Danny Staple 1b8533b459 Know turn behaviour for chapter 12. 2022-10-09 22:33:18 +01:00
Danny Staple 07f3f3c96e Add the updated robot CAD files.
Add the always face north behaviour code.
2022-10-09 16:57:21 +01:00
Danny Staple 88924b38db Ch-12 code to calibrate the sensor 2022-10-09 16:41:00 +01:00
Danny Staple f6517eda44 Ch-8 connect to sensor code 2022-10-09 16:39:50 +01:00
Danny Staple 3be25e15b0 Tidy up. Remove that handler - circuitpy will stop motors anyway. 2022-09-11 21:30:37 +01:00
Danny Staple 3c47ff9874 Current chapter 11 2022-09-11 20:26:45 +01:00
Danny Staple df1d67e42d Current state of experiments 2022-09-05 11:08:03 +01:00
Carol Staple d7ba881234 Current ch-11 state 2022-09-02 18:48:10 +01:00
Danny Staple b0db885480 Working speed control 2022-08-28 20:39:32 +01:00
Danny Staple 1b71aecddc Updated speed control 2022-08-22 23:39:25 +01:00
Danny Staple c986d25bf5 Fixed this -we now do not have the glitches 2022-08-13 08:50:09 +01:00
Danny Staple ad3f30b559 Chapter 11 code changes - async and bluetooth 2022-08-09 21:49:35 +01:00
234 changed files with 25101 additions and 834 deletions
+1
View File
@@ -5,3 +5,4 @@
*.FCStd1
secrets.py
libs
.idea/
+93
View File
@@ -0,0 +1,93 @@
# Robotics at Home with Raspberry Pi Pico
<a href="https://www.packtpub.com/product/robotics-at-home-with-raspberry-pi-pico/9781803246079?utm_source=github&utm_medium=repository&utm_campaign=9781803246079"><img src="https://static.packt-cdn.com/products/9781803246079/cover/smaller" alt="" height="256px" align="right"></a>
This is the code repository for [Robotics at Home with Raspberry Pi Pico](https://www.packtpub.com/product/robotics-at-home-with-raspberry-pi-pico/9781803246079?utm_source=github&utm_medium=repository&utm_campaign=9781803246079), published by Packt.
**Build autonomous robots with the versatile low-cost Raspberry Pi Pico controller and Python**
## What is this book about?
The field of robotics is expanding, and this is the perfect time to learn how to create robots at home for different purposes. This book will help you take your first steps in planning, building, and programming a robot with Raspberry Pi Pico, an impressive controller bursting with IO capabilities. After a quick tour of the Pico, you'll begin designing a robot chassis in 3D CAD. With easy-to-follow instructions, shopping lists, and plans, you'll start building the robot. Further, you'll add simple sensors and outputs to extend the robot, reinforce design skills and build knowledge in programming with circuit Python, understand interactions with electronics, standard robotics algorithms, and the discipline and process for building robots. Moving forward, you'll learn to add more complicated sensors and robotic behaviors, with increasing complexity levels, giving you hands-on experience. You'll learn Raspberry Pi Pico's excellent features like PIO, adding capabilities like avoiding walls, detecting movement and compass heading. You'll combine these with Bluetooth BLE for seeing sensor data and remotely control with a smartphone. Finally, you'll program the robot to find its location in an arena.
This book covers the following exciting features:
* Interface Raspberry Pi Pico with motors to move parts
* Design in 3D CAD with Free CAD
* Build a simple robot and extend it for more complex projects
* Interface Raspberry Pi Pico with sensors and Bluetooth BLE
* Visualize robot data with Matplotlib
* Gain an understanding of robotics algorithms on Pico for smart behavior
If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1803246073) today!
<a href="https://www.packtpub.com/?utm_source=github&utm_medium=banner&utm_campaign=GitHubBanner"><img src="https://raw.githubusercontent.com/PacktPublishing/GitHub/master/GitHub.png"
alt="https://www.packtpub.com/" border="5" /></a>
## Instructions and Navigations
All of the code is organized into folders. For example, ch-01.
A block of code is set as follows:
```
import time
import board
import digitalio
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
while True:
led.value = True
time.sleep(0.5)
led.value = False
time.sleep(0.5)
```
When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:
```
>>> 𝗽𝗿𝗶𝗻𝘁("𝗛𝗲𝗹𝗹𝗼, 𝘄𝗼𝗿𝗹𝗱!")
Hello, World!
>>>
```
Any command-line input or output is written as follows:
```
𝗰𝗼𝗱𝗲.𝗽𝘆 𝗼𝘂𝘁𝗽𝘂𝘁:
𝟰𝟰𝟰𝟯 𝟰𝟱𝟮𝟮
```
**Following is what you need for this book:**
This book is for beginner robot makers, keen hobbyists, technical enthusiasts, developers and STEM teachers who want to build robots at home. Prior knowledge of coding - beginner to intermediate programming, will be helpful.
With the following software and hardware list you can run all code files present in the book (Chapter 1-14).
### Software and Hardware List
| Software required | OS required |
| ------------------------------------ | ----------------------------------- |
| Thonny > 3.3 or Mu Editor > 1.1 | macOS, Linux, or Windows |
| Python 3.7 or later | macOS, Linux, or Windows |
| Matplotlib 3.6.1 or later | macOS, Linux, or Windows |
| NumPy 1.23.4 or later | macOS, Linux, or Windows |
| Bleak (Python BLE library) 0.19.0 or above | macOS, Linux, or Windows |
| Free USB port | macOS, Linux, or Windows |
| Smartphone/tablet with Bluetooth LE (Bluetooth > 4.0) | iOS or Android |
| Adafruit Bluefruit LE Connect > 3.3.2 | iOS or Android |
| Bluetooth LE-enabled laptop (or BLE dongle) | macOS, Linux, or Windows |
| FreeCAD | macOS, Linux, or Windows |
| Raspberry Pi Pico | |
| CircuitPython > 7.2.0 | Raspberry Pi Pico |
We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://packt.link/7x3ku).
### Related products
* Raspberry Pi Pico DIY Workshop [[Packt]](https://www.packtpub.com/product/raspberry-pi-pico-diy-workshop/9781801814812?utm_source=github&utm_medium=repository&utm_campaign=9781801814812) [[Amazon]](https://www.amazon.com/dp/1801814813)
* Learn Robotics Programming - Second Edition [[Packt]](https://www.packtpub.com/product/learn-robotics-programming-second-edition/9781839218804?utm_source=github&utm_medium=repository&utm_campaign=9781839218804) [[Amazon]](https://www.amazon.com/dp/1839218800)
## Get to Know the Author
**Danny Staple**
Danny Staple is an author who builds robots and gadgets at home, and makes videos about his work on the YouTube channel OrionRobots. He attends robotics and maker events such as PiWars and Arduino Day. He's been building robots for 20 years, having run Lego Robotics clubs with Mindstorms and is a mentor at a local CoderDojo, where he teaches kids how to code with Python.
He has been a professional Python programmer since 2009, and a software engineer since 2000. He has worked with embedded systems, throughout the majority of his career. The robots he has built at home with his children include TankBot, SkittleBot (a PiWars 2018 robot), Bangers n Bash (LunchBot), ArmBot, and SpiderBot. He is better at building robots than naming them.
## Other books by the authors
* [Learn Robotics Programming - Second Edition](https://www.packtpub.com/product/learn-robotics-programming-second-edition/9781839218804?utm_source=github&utm_medium=repository&utm_campaign=9781839218804)
### Download a free PDF
<i>If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.<br>Simply click on the link to claim your free PDF.</i>
<p align="center"> <a href="https://packt.link/free-ebook/9781803246079">https://packt.link/free-ebook/9781803246079 </a> </p>
View File
@@ -14,22 +14,22 @@ class PController:
## We'll set up a single distance sensor, and keep a set distance from an object
robot.left_distance.distance_mode = 1
robot.left_distance.start_ranging()
robot.right_distance.distance_mode = 1
robot.right_distance.start_ranging()
distance_set_point = 10
distance_controller = PController(-0.1)
while True:
if robot.left_distance.data_ready:
distance = robot.left_distance.distance
if robot.right_distance.data_ready:
distance = robot.right_distance.distance
error = distance_set_point - distance
speed = distance_controller.calculate(error)
if abs(speed) < 0.2:
if abs(speed) < 0.3:
speed = 0
uart.write(f"{error},{speed}\n".encode())
print(f"{error},{speed}")
robot.set_left(speed)
robot.set_right(speed)
robot.left_distance.clear_interrupt()
robot.right_distance.clear_interrupt()
time.sleep(0.05)
+6 -6
View File
@@ -17,28 +17,28 @@ class PIController:
## We'll set up a single distance sensor, and keep a set distance from an object
robot.left_distance.distance_mode = 1
robot.left_distance.start_ranging()
robot.right_distance.distance_mode = 1
robot.right_distance.start_ranging()
distance_set_point = 10
distance_controller = PIController(-0.19, -0.005)
prev_time = time.monotonic()
while True:
if robot.left_distance.data_ready:
distance = robot.left_distance.distance
if robot.right_distance.data_ready:
distance = robot.right_distance.distance
error = distance_set_point - distance
current_time = time.monotonic()
speed = distance_controller.calculate(error, current_time - prev_time)
prev_time = current_time
# Control the motors with the speed
if abs(speed) < 0.35:
if abs(speed) < 0.3:
speed = 0
uart.write(f"{error},{speed},"
f"{distance_controller.integral}\n".encode())
print(f"{error},{speed},{distance_controller.integral}")
robot.set_left(speed)
robot.set_right(speed)
robot.left_distance.clear_interrupt()
robot.right_distance.clear_interrupt()
time.sleep(0.05)
+6 -6
View File
@@ -8,28 +8,28 @@ uart = busio.UART(board.GP12, board.GP13, baudrate=9600)
## We'll set up a single distance sensor, and keep a set distance from an object
robot.left_distance.distance_mode = 1
robot.left_distance.start_ranging()
robot.right_distance.distance_mode = 1
robot.right_distance.start_ranging()
distance_set_point = 10
distance_controller = PIDController(-0.09, -0.02, -0.07)
prev_time = time.monotonic()
while True:
if robot.left_distance.data_ready:
distance = robot.left_distance.distance
if robot.right_distance.data_ready:
distance = robot.right_distance.distance
error = distance_set_point - distance
current_time = time.monotonic()
speed = distance_controller.calculate(error, current_time - prev_time)
prev_time = current_time
# Control the motors with the speed
if abs(speed) < 0.35:
if abs(speed) < 0.3:
speed = 0
uart.write(f"{error},{speed},{distance_controller.integral},{distance_controller.derivative}\n".encode())
print(f"{error},{speed},{distance_controller.integral},{distance_controller.derivative}")
robot.set_left(speed)
robot.set_right(speed)
# reset the distance sensor
robot.left_distance.clear_interrupt()
robot.right_distance.clear_interrupt()
time.sleep(0.05)
+2 -1
View File
@@ -25,7 +25,8 @@ while True:
current_time = time.monotonic()
deflection = distance_controller.calculate(error, current_time - prev_time)
prev_time = current_time
uart.write(f"{error},{deflection}\n".encode()) # ,{distance_controller.derivative}
uart.write(f"{error},{deflection},"
f"{distance_controller.derivative}\n".encode())
if motors_active:
robot.set_left(speed - deflection)
robot.set_right(speed + deflection)
Binary file not shown.
-73
View File
@@ -1,73 +0,0 @@
import time
import json
from adafruit_esp32spi import adafruit_esp32spi_wsgiserver
from adafruit_wsgi.wsgi_app import WSGIApp
import robot
import robot_wifi
class SpeedCountApp:
def __init__(self):
self.intended_speed = 0.9
self.last_time = time.monotonic()
self.wifi = None
self.server = None
def setup_wifi(self, app):
print("Setting up wifi.")
self.wifi, esp = robot_wifi.connect_to_wifi()
self.server = adafruit_esp32spi_wsgiserver.WSGIServer(80, application=app)
adafruit_esp32spi_wsgiserver.set_interface(esp)
print("Starting server")
self.server.start()
ip_int = ".".join(str(int(n)) for n in esp.ip_address)
print(f"IP Address is {ip_int}")
def index(self, request):
new_time = time.monotonic()
time_delta = new_time - self.last_time
self.last_time = new_time
left_speed = robot.left_encoder.get_speed(time_delta)
right_speed = robot.right_encoder.get_speed(time_delta)
return (
200,
[("Content-Type", "application/json")],
[
json.dumps(
{
"left_speed": left_speed,
"right_speed": right_speed,
"time": self.last_time,
}
)
],
)
def main_loop(self):
robot.set_left(self.intended_speed)
robot.set_right(self.intended_speed)
while True:
try:
self.server.update_poll()
except RuntimeError as e:
print(f"Server poll error: {type(e)}, {e}")
print(f"Resetting ESP...")
self.wifi.reset()
print("Reset complete.")
def start(self):
app = WSGIApp()
app.route("/")(self.index)
print("Starting")
try:
self.setup_wifi(app)
self.main_loop()
finally:
robot.stop()
SpeedCountApp().start()
-25
View File
@@ -1,25 +0,0 @@
import board
import busio
from digitalio import DigitalInOut
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
def connect_to_wifi():
esp32_cs = DigitalInOut(board.GP10)
esp32_ready = DigitalInOut(board.GP9)
esp32_reset = DigitalInOut(board.GP8)
spi = busio.SPI(board.GP14, MOSI=board.GP11, MISO=board.GP12)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
esp.reset()
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets)
wifi.connect()
return wifi, esp
+47
View File
@@ -0,0 +1,47 @@
import asyncio
import robot
class Settings:
speed = 0.7
time_interval = 0.2
async def motor_speed_loop():
left_last, right_last = robot.left_encoder.read(), robot.right_encoder.read()
while True:
await asyncio.sleep(Settings.time_interval)
left_new, right_new = robot.left_encoder.read(), robot.right_encoder.read()
left_speed = robot.ticks_to_m * (left_new - left_last) / Settings.time_interval
left_last = left_new
right_speed = robot.ticks_to_m * (right_new - right_last) / Settings.time_interval
right_last = right_new
robot.send_line(f"{left_speed:.2f},{right_speed:.2f},0")
async def stop_motors_after(seconds):
await asyncio.sleep(seconds)
robot.stop()
async def command_handler():
while True:
if robot.uart.in_waiting:
command = robot.uart.readline().decode().strip()
if command.startswith("M"):
Settings.speed = float(command[1:])
elif command.startswith("T"):
Settings.time_interval = float(command[1:])
elif command == "G":
robot.stop()
elif command.startswith("G"):
await asyncio.sleep(5)
asyncio.create_task(
stop_motors_after(float(command[1:]))
)
robot.set_left(Settings.speed)
robot.set_right(Settings.speed)
elif command.startswith("?"):
robot.send_line(f"M{Settings.speed:.1f}")
robot.send_line(f"T{Settings.time_interval:.1f}")
await asyncio.sleep(3)
await asyncio.sleep(0)
asyncio.create_task(motor_speed_loop())
asyncio.run(command_handler())
@@ -0,0 +1,20 @@
class PIDController:
def __init__(self, kp, ki, kd, d_filter_gain=0.1):
self.kp = kp
self.ki = ki
self.kd = kd
self.d_filter_gain = d_filter_gain
self.integral = 0
self.error_prev = 0
self.derivative = 0
def calculate(self, error, dt):
self.integral += error * dt
# Add a low pass filter to the difference
difference = (error - self.error_prev) * self.d_filter_gain
self.error_prev += difference
self.derivative = difference / dt
return self.kp * error + self.ki * self.integral + self.kd * self.derivative
@@ -1,6 +1,8 @@
import rp2pio
import adafruit_pioasm
import array
import asyncio
program = """
; use the osr for count
@@ -67,18 +69,16 @@ class QuadratureEncoder:
)
self.reversed = reversed
self._buffer = array.array("i", [0])
self.previous_reading = 0
asyncio.create_task(self.poll_loop())
async def poll_loop(self):
while True:
await asyncio.sleep(0)
while self.sm.in_waiting:
self.sm.readinto(self._buffer)
def read(self):
while self.sm.in_waiting:
self.sm.readinto(self._buffer)
if self.reversed:
return -self._buffer[0]
else:
return self._buffer[0]
def get_speed(self, delta_time):
new_read = self.read()
distance = new_read - self.previous_reading
self.previous_reading = new_read
return distance / delta_time
@@ -3,18 +3,27 @@ import pwmio
import pio_encoder
import busio
import adafruit_vl53l1x
import math
uart = busio.UART(board.GP12, board.GP13, baudrate=9600)
motor_A1 = pwmio.PWMOut(board.GP17)
motor_A2 = pwmio.PWMOut(board.GP16)
motor_B1 = pwmio.PWMOut(board.GP18)
motor_B2 = pwmio.PWMOut(board.GP19)
wheel_diameter_mm = 70
wheel_circumference_mm = math.pi * wheel_diameter_mm
gear_ratio = 298
encoder_poles = 28
ticks_per_revolution = encoder_poles * gear_ratio
ticks_to_m = (wheel_circumference_mm / ticks_per_revolution) / 1000
motor_A2 = pwmio.PWMOut(board.GP17, frequency=100)
motor_A1 = pwmio.PWMOut(board.GP16, frequency=100)
motor_B2 = pwmio.PWMOut(board.GP18, frequency=100)
motor_B1 = pwmio.PWMOut(board.GP19, frequency=100)
right_motor = motor_A1, motor_A2
left_motor = motor_B1, motor_B2
right_encoder = pio_encoder.QuadratureEncoder(board.GP20, board.GP21, reversed=True)
left_encoder = pio_encoder.QuadratureEncoder(board.GP26, board.GP27)
right_encoder = pio_encoder.QuadratureEncoder(board.GP20, board.GP21)
left_encoder = pio_encoder.QuadratureEncoder(board.GP26, board.GP27, reversed=True)
i2c0 = busio.I2C(sda=board.GP0, scl=board.GP1)
i2c1 = busio.I2C(sda=board.GP2, scl=board.GP3)
@@ -50,3 +59,6 @@ def set_left(speed):
def set_right(speed):
set_speed(right_motor, speed)
def send_line(message):
uart.write(f"{message}\n".encode())
+88
View File
@@ -0,0 +1,88 @@
import asyncio
import time
import robot
import pid_controller
class Settings:
speed = 0.17
time_interval = 0.2
motors_enabled = False
class SpeedController:
def __init__(self, encoder, motor_fn):
self.encoder = encoder
self.motor_fn = motor_fn
self.pid = pid_controller.PIDController(3, 0, 1)
self.reset()
def reset(self):
self.last_ticks = self.encoder.read()
self.pwm = 0
self.actual_speed = 0
self.pid.reset()
def update(self, dt):
current_ticks = self.encoder.read()
speed_in_ticks = (current_ticks - self.last_ticks) / dt
self.last_ticks = current_ticks
self.actual_speed = robot.ticks_to_m * speed_in_ticks
# calculate the error
error = Settings.speed - self.actual_speed
# calculate the control signal
control_signal = self.pid.calculate(error, dt)
self.pwm += control_signal
self.motor_fn(self.pwm * Settings.motors_enabled)
left = SpeedController(robot.left_encoder, robot.set_left)
right = SpeedController(robot.right_encoder, robot.set_right)
async def motor_speed_loop():
last_time = time.monotonic()
while True:
await asyncio.sleep(Settings.time_interval)
current_time = time.monotonic()
dt = current_time - last_time
last_time = current_time
left.update(dt)
right.update(dt)
robot.send_line(f"{left.actual_speed:.2f},{Settings.speed * Settings.motors_enabled:.2f},0")
async def stop_motors_after(seconds):
await asyncio.sleep(seconds)
Settings.motors_enabled = False
async def command_handler():
while True:
if robot.uart.in_waiting:
command = robot.uart.readline().decode().strip()
if command.startswith("M"):
Settings.speed = float(command[1:])
elif command.startswith("T"):
Settings.time_interval = float(command[1:])
elif command == "G":
Settings.motors_enabled = False
elif command.startswith("G"):
await asyncio.sleep(5)
asyncio.create_task(
stop_motors_after(float(command[1:]))
)
Settings.motors_enabled = True
left.reset()
right.reset()
elif command.startswith("?"):
robot.send_line(f"M{Settings.speed:.1f}")
robot.send_line(f"T{Settings.time_interval:.1f}")
await asyncio.sleep(3)
await asyncio.sleep(0)
try:
motors_task = asyncio.create_task(motor_speed_loop())
asyncio.run(command_handler())
finally:
motors_task.cancel()
robot.stop()
+21
View File
@@ -0,0 +1,21 @@
class PIDController:
def __init__(self, kp, ki, kd, d_filter_gain=0.1):
self.kp = kp
self.ki = ki
self.kd = kd
self.d_filter_gain = d_filter_gain
self.reset()
def reset(self):
self.integral = 0
self.error_prev = 0
self.derivative = 0
def calculate(self, error, dt):
self.integral += error * dt
# Add a low pass filter to the difference
difference = (error - self.error_prev) * self.d_filter_gain
self.error_prev += difference
self.derivative = difference / dt
return self.kp * error + self.ki * self.integral + self.kd * self.derivative
@@ -1,6 +1,8 @@
import rp2pio
import adafruit_pioasm
import array
import asyncio
program = """
; use the osr for count
@@ -67,18 +69,16 @@ class QuadratureEncoder:
)
self.reversed = reversed
self._buffer = array.array("i", [0])
self.previous_reading = 0
asyncio.create_task(self.poll_loop())
async def poll_loop(self):
while True:
await asyncio.sleep(0)
while self.sm.in_waiting:
self.sm.readinto(self._buffer)
def read(self):
while self.sm.in_waiting:
self.sm.readinto(self._buffer)
if self.reversed:
return -self._buffer[0]
else:
return self._buffer[0]
def get_speed(self, delta_time):
new_read = self.read()
distance = new_read - self.previous_reading
self.previous_reading = new_read
return distance / delta_time
@@ -3,27 +3,34 @@ import pwmio
import pio_encoder
import busio
import adafruit_vl53l1x
import adafruit_bno055
import math
uart = busio.UART(board.GP12, board.GP13, baudrate=9600)
motor_A1 = pwmio.PWMOut(board.GP17)
motor_A2 = pwmio.PWMOut(board.GP16)
motor_B1 = pwmio.PWMOut(board.GP18)
motor_B2 = pwmio.PWMOut(board.GP19)
wheel_diameter_mm = 70
wheel_circumference_mm = math.pi * wheel_diameter_mm
gear_ratio = 298
encoder_poles = 28
ticks_per_revolution = encoder_poles * gear_ratio
ticks_to_m = (wheel_circumference_mm / ticks_per_revolution) / 1000
motor_A2 = pwmio.PWMOut(board.GP17, frequency=100)
motor_A1 = pwmio.PWMOut(board.GP16, frequency=100)
motor_B2 = pwmio.PWMOut(board.GP18, frequency=100)
motor_B1 = pwmio.PWMOut(board.GP19, frequency=100)
right_motor = motor_A1, motor_A2
left_motor = motor_B1, motor_B2
right_encoder = pio_encoder.QuadratureEncoder(board.GP20, board.GP21, reversed=True)
left_encoder = pio_encoder.QuadratureEncoder(board.GP26, board.GP27)
right_encoder = pio_encoder.QuadratureEncoder(board.GP20, board.GP21)
left_encoder = pio_encoder.QuadratureEncoder(board.GP26, board.GP27, reversed=True)
i2c0 = busio.I2C(sda=board.GP0, scl=board.GP1)
# i2c1 = busio.I2C(sda=board.GP2, scl=board.GP3)
i2c1 = busio.I2C(sda=board.GP2, scl=board.GP3)
# right_distance = adafruit_vl53l1x.VL53L1X(i2c0)
# left_distance = adafruit_vl53l1x.VL53L1X(i2c1)
left_distance = adafruit_vl53l1x.VL53L1X(i2c0)
right_distance = adafruit_vl53l1x.VL53L1X(i2c1)
imu = adafruit_bno055.BNO055_I2C(i2c0)
def stop():
motor_A1.duty_cycle = 0
@@ -34,6 +41,10 @@ def stop():
def set_speed(motor, speed):
# Swap motor pins if we reverse the speed
if abs(speed) < 0.1:
motor[0].duty_cycle = 0
motor[1].duty_cycle = 1
return
if speed < 0:
direction = motor[1], motor[0]
speed = -speed
@@ -52,3 +63,6 @@ def set_left(speed):
def set_right(speed):
set_speed(right_motor, speed)
def send_line(message):
uart.write(f"{message}\n".encode())
-39
View File
@@ -1,39 +0,0 @@
class PID:
def __init__(self, proportional_k, integral_k, differential_k, set_point):
self.proportional_k = proportional_k
self.integral_k = integral_k
self.differential_k = differential_k
self.set_point = set_point
self.error_sum = 0
self.last_value = 0
self.min_output = -1
self.max_output = 1
self.dead_zone = 0.3
def update(self, measurement, time_delta):
error_value = measurement - self.set_point
proportional = error_value * self.proportional_k
# calculate integral
self.error_sum += error_value * time_delta
# clamp it
self.error_sum = min(self.max_output, self.error_sum)
self.error_sum = max(self.min_output, self.error_sum)
integral = self.error_sum * self.integral_k
differentiated_error = (error_value - self.last_value) / time_delta
differential = differentiated_error * self.differential_k
self.last_value = error_value
output = proportional + integral + differential
# clamp output
if abs(output) < self.dead_zone:
output = 0
else:
output = min(self.max_output, output)
output = max(self.min_output, output)
return output
-25
View File
@@ -1,25 +0,0 @@
import board
import busio
from digitalio import DigitalInOut
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise
def connect_to_wifi():
esp32_cs = DigitalInOut(board.GP10)
esp32_ready = DigitalInOut(board.GP9)
esp32_reset = DigitalInOut(board.GP8)
spi = busio.SPI(board.GP14, MOSI=board.GP11, MISO=board.GP12)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
esp.reset()
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets)
wifi.connect()
return wifi, esp
@@ -1,109 +0,0 @@
import time
import json
from adafruit_esp32spi import adafruit_esp32spi_wsgiserver
from adafruit_wsgi.wsgi_app import WSGIApp
import robot
import robot_wifi
import pid
class SpeedControlApp:
def __init__(self):
self.wifi = None
self.server = None
self.intended_speed = 0.9
self.last_time = time.monotonic()
self.speed_to_encoder_factor = 1/5100
self.left_speed_pid = pid.PID(0, -0.7, 0, self.intended_speed)
self.right_speed_pid = pid.PID(0, -0.7, 0, self.intended_speed)
self.left_pid_output = 0
self.right_pid_output = 0
self.left_speed = 0
self.right_speed = 0
def setup_wifi(self, app):
print("Setting up wifi.")
self.wifi, esp = robot_wifi.connect_to_wifi()
self.server = adafruit_esp32spi_wsgiserver.WSGIServer(80, application=app)
adafruit_esp32spi_wsgiserver.set_interface(esp)
print("Starting server")
self.server.start()
ip_int = ".".join(str(int(n)) for n in esp.ip_address)
print(f"IP Address is {ip_int}")
def update(self):
new_time = time.monotonic()
time_delta = new_time - self.last_time
self.last_time = new_time
self.left_speed = robot.left_encoder.get_speed(time_delta) * self.speed_to_encoder_factor
self.right_speed = robot.right_encoder.get_speed(time_delta) * self.speed_to_encoder_factor
self.left_pid_output = self.left_speed_pid.update(self.left_speed, time_delta)
self.right_pid_output = self.right_speed_pid.update(self.right_speed, time_delta)
# print({
# "left_speed": self.left_speed,
# "left_pid": self.left_pid_output,
# "right_speed": self.right_speed,
# "right_pid": self.right_pid_output,
# "time": self.last_time,
# "error_sum": self.left_speed_pid.error_sum
# })
# robot.set_left(self.left_pid_output)
# robot.set_right(self.right_pid_output)
def movement_generator(self):
while True:
self.update()
data = json.dumps(
{
"left_speed": self.left_speed,
"left_pid": self.left_pid_output,
"right_speed": self.right_speed,
"right_pid": self.right_pid_output,
"time": self.last_time,
}
) + "/n"
print(data)
yield data
def index(self, request):
return (
200,
[("Content-Type", "application/json")],
self.movement_generator(),
)
def main_loop(self):
while True:
try:
self.update()
# time.sleep(0.1)
self.server.update_poll()
except RuntimeError as e:
print(f"Server poll error: {type(e)}, {e}")
print(f"Resetting ESP...")
self.wifi.reset()
print("Reset complete.")
def start(self):
app = WSGIApp()
app.route("/")(self.index)
print("Starting")
try:
self.setup_wifi(app)
self.main_loop()
finally:
robot.stop()
SpeedControlApp().start()
+84
View File
@@ -0,0 +1,84 @@
import asyncio
import time
import robot
import pid_controller
class DistanceController:
def __init__(self, encoder, motor_fn):
self.encoder = encoder
self.motor_fn = motor_fn
self.pid = pid_controller.PIDController(3.25, 0.5, 0.5, d_filter_gain=1)
self.start_ticks = self.encoder.read()
self.error = 0
def update(self, dt, expected):
self.actual = self.encoder.read() - self.start_ticks
# calculate the error
self.error = (expected - self.actual) / robot.ticks_per_revolution
# calculate the control signal
control_signal = self.pid.calculate(self.error, dt)
self.motor_fn(control_signal)
class DistanceTracker:
def __init__(self):
self.speed = 0.17
self.time_interval = 0.2
self.start_time = time.monotonic()
self.current_position = 0
self.total_distance_in_ticks = 0
self.total_time = 0.1
def set_distance(self, new_distance):
# add the last travelled distance to the current position
self.current_position += self.total_distance_in_ticks
# calculate the new additional distance
self.total_distance_in_ticks = robot.m_to_ticks * new_distance
self.total_time = max(0.1, abs(new_distance / self.speed))
self.start_time = time.monotonic()
async def loop(self):
left = DistanceController(robot.left_encoder, robot.set_left)
right = DistanceController(robot.right_encoder, robot.set_right)
last_time = time.monotonic()
while True:
await asyncio.sleep(self.time_interval)
current_time = time.monotonic()
dt = current_time - last_time
last_time = current_time
elapsed_time = current_time - self.start_time
time_proportion = min(1, elapsed_time / self.total_time)
expected = time_proportion * self.total_distance_in_ticks + self.current_position
left.update(dt, expected)
right.update(dt, expected)
robot.send_line(f"{expected:.2f},{left.actual:.2f},0")
distance_tracker = DistanceTracker()
async def command_handler():
while True:
if robot.uart.in_waiting:
command = robot.uart.readline().decode().strip()
if command.startswith("M"):
distance_tracker.speed = float(command[1:])
elif command.startswith("T"):
distance_tracker.time_interval = float(command[1:])
elif command == "G":
distance_tracker.set_distance(0)
elif command.startswith("G"):
await asyncio.sleep(5)
distance_tracker.set_distance(float(command[1:]))
elif command.startswith("?"):
robot.send_line(f"M{distance_tracker.speed:.1f}")
robot.send_line(f"T{distance_tracker.time_interval:.1f}")
await asyncio.sleep(3)
await asyncio.sleep(0)
try:
motors_task = asyncio.create_task(distance_tracker.loop())
asyncio.run(command_handler())
finally:
motors_task.cancel()
robot.stop()
+21
View File
@@ -0,0 +1,21 @@
class PIDController:
def __init__(self, kp, ki, kd, d_filter_gain=0.1):
self.kp = kp
self.ki = ki
self.kd = kd
self.d_filter_gain = d_filter_gain
self.reset()
def reset(self):
self.integral = 0
self.error_prev = 0
self.derivative = 0
def calculate(self, error, dt):
self.integral += error * dt
# Add a low pass filter to the difference
difference = (error - self.error_prev) * self.d_filter_gain
self.error_prev += difference
self.derivative = difference / dt
return self.kp * error + self.ki * self.integral + self.kd * self.derivative
+84
View File
@@ -0,0 +1,84 @@
import rp2pio
import adafruit_pioasm
import array
import asyncio
program = """
; use the osr for count
; input pins c1 c2
set y, 0 ; clear y
mov osr, y ; and clear osr
read:
; x will be the old value
; y the new values
mov x, y ; store old Y in x
in null, 32 ; Clear ISR - using y
in pins, 2 ; read two pins into y
mov y, isr
jmp x!=y, different ; Jump if its different
jmp read ; otherwise loop back to read
different:
; x has old value, y has new.
; extract the upper bit of X.
in x, 31 ; get bit 31 - old p1 (remember which direction it came in)
in null, 31 ; keep only 1 bit
mov x, isr ; put this back in x
jmp !x, c1_old_zero
c1_old_not_zero:
jmp pin, count_up
jmp count_down
c1_old_zero:
jmp pin, count_down
; fall through
count_up:
; for a clockwise move - we'll add 1 by inverting
mov x, ~ osr ; store inverted OSR on x
jmp x--, fake ; use jump to take off 1
fake:
mov x, ~ x ; invert back
jmp send
count_down:
; for a clockwise move, just take one off
mov x, osr ; store osr in x
jmp x--, send ; dec and send
send:
; send x.
mov isr, x ; send it
push noblock ; put ISR into input FIFO
mov osr, x ; put X back in OSR
jmp read ; loop back
"""
assembled = adafruit_pioasm.assemble(program)
class QuadratureEncoder:
def __init__(self, first_pin, second_pin, reversed=False):
"""Encoder with 2 pins. Must use sequential pins on the board"""
self.sm = rp2pio.StateMachine(
assembled,
frequency=0,
first_in_pin=first_pin,
jmp_pin=second_pin,
in_pin_count=2,
)
self.reversed = reversed
self._buffer = array.array("i", [0])
asyncio.create_task(self.poll_loop())
async def poll_loop(self):
while True:
await asyncio.sleep(0)
while self.sm.in_waiting:
self.sm.readinto(self._buffer)
def read(self):
if self.reversed:
return -self._buffer[0]
else:
return self._buffer[0]
+70
View File
@@ -0,0 +1,70 @@
import board
import pwmio
import pio_encoder
import busio
import adafruit_vl53l1x
import math
uart = busio.UART(board.GP12, board.GP13, baudrate=9600)
wheel_diameter_mm = 70
wheel_circumference_mm = math.pi * wheel_diameter_mm
gear_ratio = 298
encoder_poles = 28
ticks_per_revolution = encoder_poles * gear_ratio
ticks_to_m = (wheel_circumference_mm / ticks_per_revolution) / 1000
m_to_ticks = 1 / ticks_to_m
motor_A2 = pwmio.PWMOut(board.GP17, frequency=100)
motor_A1 = pwmio.PWMOut(board.GP16, frequency=100)
motor_B2 = pwmio.PWMOut(board.GP18, frequency=100)
motor_B1 = pwmio.PWMOut(board.GP19, frequency=100)
right_motor = motor_A1, motor_A2
left_motor = motor_B1, motor_B2
right_encoder = pio_encoder.QuadratureEncoder(board.GP20, board.GP21)
left_encoder = pio_encoder.QuadratureEncoder(board.GP26, board.GP27, reversed=True)
i2c0 = busio.I2C(sda=board.GP0, scl=board.GP1)
i2c1 = busio.I2C(sda=board.GP2, scl=board.GP3)
left_distance = adafruit_vl53l1x.VL53L1X(i2c0)
right_distance = adafruit_vl53l1x.VL53L1X(i2c1)
def stop():
motor_A1.duty_cycle = 0
motor_A2.duty_cycle = 0
motor_B1.duty_cycle = 0
motor_B2.duty_cycle = 0
def set_speed(motor, speed):
# Swap motor pins if we reverse the speed
if abs(speed) < 0.1:
motor[0].duty_cycle = 0
motor[1].duty_cycle = 1
return
if speed < 0:
direction = motor[1], motor[0]
speed = -speed
else:
direction = motor
speed = min(speed, 1) # limit to 1.0
max_speed = 2 ** 16 - 1
direction[0].duty_cycle = int(max_speed * speed)
direction[1].duty_cycle = 0
def set_left(speed):
set_speed(left_motor, speed)
def set_right(speed):
set_speed(right_motor, speed)
def send_line(message):
uart.write(f"{message}\n".encode())
+34
View File
@@ -0,0 +1,34 @@
import time
import board
import busio
import robot
uart = busio.UART(board.GP12, board.GP13, baudrate=9600)
speed = 0.7
stop_time = 0
while True:
current_time = time.monotonic()
if current_time > stop_time:
robot.set_left(0)
robot.set_right(0)
left_value = robot.left_encoder.read()
right_value = robot.right_encoder.read()
uart.write(f"{left_value:.3f},{right_value:.3f}\n".encode() )
time.sleep(0.02)
if uart.in_waiting:
command = uart.readline().decode().strip()
if command.startswith("M"):
speed = float(command[1:])
elif command == "O":
stop_time = 0
robot.set_left(0)
robot.set_right(0)
elif command.startswith("O"):
stop_time = float(command[1:]) + current_time
robot.set_left(speed)
robot.set_right(speed)
elif command.startswith("?"):
uart.write(f"M{speed:.1f}\n".encode())
time.sleep(3)
+8
View File
@@ -0,0 +1,8 @@
class LPF:
"""Low pass filter."""
def __init__(self, alpha):
self.alpha = alpha
self.last = 0
def update(self, value):
self.last = self.alpha * value + (1 - self.alpha) * self.last
return self.last
@@ -0,0 +1,20 @@
class PIDController:
def __init__(self, kp, ki, kd, d_filter_gain=0.1):
self.kp = kp
self.ki = ki
self.kd = kd
self.d_filter_gain = d_filter_gain
self.integral = 0
self.error_prev = 0
self.derivative = 0
def calculate(self, error, dt):
self.integral += error * dt
# Add a low pass filter to the difference
difference = (error - self.error_prev) * self.d_filter_gain
self.error_prev += difference
self.derivative = difference / dt
return self.kp * error + self.ki * self.integral + self.kd * self.derivative

Some files were not shown because too many files have changed in this diff Show More