Added RT robot post
This commit is contained in:
parent
fae2cb7f9f
commit
a407af087d
185
_posts/2019-11-18-Realtime-robot-code.md
Normal file
185
_posts/2019-11-18-Realtime-robot-code.md
Normal file
@ -0,0 +1,185 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Programming a live robot"
|
||||
description: "Living on the edge is an understatement"
|
||||
date: 2019-11-20 10:04:00
|
||||
categories: random frc
|
||||
redirect_from:
|
||||
- /post/e9gdhj90/
|
||||
- /e9gdhj90/
|
||||
---
|
||||
|
||||
> *"So.. what if we could skip asking for driver inputs, and just have the robot operators control the bot through a commandline interface?"*
|
||||
|
||||
This is exactly the kind of question I randomly ask while sitting in the middle of class, staring at my laptop. So, here is a post about my real-time programming adventure!
|
||||
|
||||
## Geting started
|
||||
|
||||
To get started, I needed a few things. Firstly, I have a laptop running [a Linux distribution](/about/#my-gear). This allows me to use [SSH](https://en.wikipedia.org/wiki/Secure_Shell) and [SCP](https://en.wikipedia.org/wiki/Secure_copy). There are Windows versions of both of these programs, but I find the "linux experience" easier to use. Secondly, I have grabbed one of [5024](https://www.thebluealliance.com/team/5024)'s [robots](https://cs.5024.ca/webdocs/docs/robots) to be subjected to my experiment. The components I care about are:
|
||||
|
||||
- A RoboRIO running 2019v12 firmware
|
||||
- 2 [TalonSRX](https://www.ctr-electronics.com/talon-srx.html) motor controllers
|
||||
- An FRC router
|
||||
|
||||
Most importantly, the RoboRIO has [RobotPy](https://robotpy.readthedocs.io/en/stable/install/robot.html#install-robotpy) and the [CTRE Libraries](https://robotpy.readthedocs.io/en/stable/install/ctre.html) installed.
|
||||
|
||||
### SSH connection
|
||||
|
||||
To get some code running on the robot, we must first connect to it via SSH. Depending on your connection to the RoboRIO, this step may be different. Generally, the following command will work just fine to connect (assuming your computer has an [mDNS](https://en.wikipedia.org/wiki/Multicast_DNS) service):
|
||||
|
||||
```sh
|
||||
ssh admin@roborio-<team>-frc.local
|
||||
```
|
||||
|
||||
If you have issues, try one of the following addresses instead:
|
||||
|
||||
```
|
||||
roborio-<team>-FRC
|
||||
roborio-<team>-FRC.lan
|
||||
roborio-<team>-FRC.frc-field.local
|
||||
10.TE.AM.2
|
||||
172.22.11.2 # Only works on a USB connection
|
||||
```
|
||||
|
||||
If you are asked for a password, and have not set one, press <kbd>Enter</kbd> 3 times (Don't ask why.. this just works).
|
||||
|
||||
## REPL-based control
|
||||
|
||||
If you have seen my work before, you'll know that I use Python for basically everything. This project is no exception. Conveniently, the RoboRIO is a linux-based device, and can run a Python3 [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop). This allows real-time robot programming using a REPL via SSH.
|
||||
|
||||
WPILib requires a robot class to act as a "callback" for robot actions. My idea was to build a special robot class with static methods to allow me to start it, then use the REPL to interact with some control methods (like `setSpeed` and `stop`).
|
||||
|
||||
After connecting to the robot via SSH, a Python REPL can be started by running `python3`. If there is already robot code running, it will be automatically killed in the next step.
|
||||
|
||||
With Python running, we will need 2 libraries imported. `wpilib` and `ctre`. When importing `wpilib` a message may appear to notify you that the old robot code has been stopped.
|
||||
|
||||
```python
|
||||
>>> import wpilib
|
||||
Killing previously running FRC program...
|
||||
FRC pid 1445 did not die within 0ms. Force killing with kill -9
|
||||
>>> import ctre
|
||||
```
|
||||
Keep in mind, this is a REPL. Lines that start with `>>>` or `...` are *user input*. Everything else is produced by code.
|
||||
|
||||
Next, we need to write a little code to get the robot operational. To save time, I wrote this "library" to do most of the work for me. Just save this as `rtrbt.py` somewhere, then use SCP to copy it to `/home/lvuser/rtrbt.py`.
|
||||
|
||||
```python
|
||||
# RealTime FRC Robot control helper
|
||||
# By: Evan Pratten <ewpratten>
|
||||
|
||||
# Import normal robot stuff
|
||||
import wpilib
|
||||
import ctre
|
||||
|
||||
# Handle WPI trickery
|
||||
try:
|
||||
from unittest.mock import patch
|
||||
except ImportError:
|
||||
from mock import patch
|
||||
import sys
|
||||
from threading import Thread
|
||||
|
||||
|
||||
## Internal methods ##
|
||||
_controllers = []
|
||||
_thread: Thread
|
||||
|
||||
|
||||
class _RTRobot(wpilib.TimedRobot):
|
||||
|
||||
def robotInit(self):
|
||||
|
||||
# Create motors
|
||||
_controllers.append(ctre.WPI_TalonSRX(1))
|
||||
_controllers.append(ctre.WPI_TalonSRX(2))
|
||||
|
||||
# Set safe modes
|
||||
_controllers[0].setSafetyEnabled(False)
|
||||
_controllers[1].setSafetyEnabled(False)
|
||||
|
||||
|
||||
|
||||
def _start():
|
||||
# Handle fake args
|
||||
args = ["run", "run"]
|
||||
with patch.object(sys, "argv", args):
|
||||
print(sys.argv)
|
||||
wpilib.run(_RTRobot)
|
||||
|
||||
## Utils ##
|
||||
|
||||
|
||||
def startRobot():
|
||||
""" Start the robot code """
|
||||
global _thread
|
||||
_thread = Thread(target=_start)
|
||||
_thread.start()
|
||||
|
||||
|
||||
def setMotor(id, speed):
|
||||
""" Set a motor speed """
|
||||
_controllers[id].set(speed)
|
||||
|
||||
def arcadeDrive(speed, rotation):
|
||||
""" Control the robot with arcade inputs """
|
||||
|
||||
l = speed + rotation
|
||||
r = speed - rotation
|
||||
|
||||
setMotor(0, l)
|
||||
setMotor(1, r)
|
||||
```
|
||||
|
||||
The idea is to create a simple robot program with global hooks into the motor controllers. Python's mocking tools are used to fake commandline arguments to trick robotpy into thinking this script is being run via the RIO's robotCommand.
|
||||
|
||||
Once this script has been placed on the robot, SSH back in as `lvuser` (not `admin`), and run `python3`. If using `rtrbt.py`, the imports mentioned above are handled for you. To start the robot, just run the following:
|
||||
|
||||
```python
|
||||
>>> from rtrbt import *
|
||||
>>> startRobot()
|
||||
```
|
||||
|
||||
WPILib will dump some logs into the terminal (and probably some spam) from it's own thread. Don't worry if you can't see the REPL prompt. It's probably just hidden due to the use of multiple threads in the same shell. Pressing <kbd>Enter</kbd> should show it again.
|
||||
|
||||
I added 2 functions for controlling motors. The first, `setMotor`, will set either the left (0), or right (1) motor to the specified speed. `arcadeDrive` will allow you to specify a speed and rotational force for the robot's drivetrain.
|
||||
|
||||
To kill the code and exit, press <kbd>CTRL</kbd> + <kbd>D</kbd> then <kbd>CTRL</kbd> + <kbd>C</kbd>.
|
||||
|
||||
Here is an example where I start the bot, then tell it to drive forward, then kill the left motor:
|
||||
```python
|
||||
Python 3.6.8 (default, Oct 7 2019, 12:59:55)
|
||||
[GCC 8.3.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> from rtrbt import *
|
||||
>>> startRobot()
|
||||
['run', 'run']
|
||||
17:53:46:472 INFO : wpilib : WPILib version 2019.2.3
|
||||
17:53:46:473 INFO : wpilib : HAL base version 2019.2.3;
|
||||
17:53:46:473 INFO : wpilib : robotpy-ctre version 2019.3.2
|
||||
17:53:46:473 INFO : wpilib : robotpy-cscore version 2019.1.0
|
||||
17:53:46:473 INFO : faulthandler : registered SIGUSR2 for PID 5447
|
||||
17:53:46:474 INFO : nt : NetworkTables initialized in server mode
|
||||
17:53:46:497 INFO : robot : Default IterativeRobot.disabledInit() method... Override me!
|
||||
17:53:46:498 INFO : robot : Default IterativeRobot.disabledPeriodic() method... Override me!
|
||||
17:53:46:498 INFO : robot : Default IterativeRobot.robotPeriodic() method... Override me!
|
||||
>>>
|
||||
>>> arcadeDrive(1.0, 0.0)
|
||||
>>> setMotor(0, 0.0)
|
||||
>>>
|
||||
^C
|
||||
Exception ignored in: <module 'threading' from '/usr/lib/python3.6/threading.py'>
|
||||
Traceback (most recent call last):
|
||||
File "/usr/lib/python3.6/threading.py", line 1294, in _shutdown
|
||||
t.join()
|
||||
File "/usr/lib/python3.6/threading.py", line 1056, in join
|
||||
self._wait_for_tstate_lock()
|
||||
File "/usr/lib/python3.6/threading.py", line 1072, in _wait_for_tstate_lock
|
||||
elif lock.acquire(block, timeout):
|
||||
KeyboardInterrupt
|
||||
```
|
||||
|
||||
The message at the end occurs when killing the code.
|
||||
|
||||
## Conclusion
|
||||
|
||||
I have no idea why any of this would be useful, or if it is even field legal.. It's just a fun project for a monday morning.
|
@ -14,7 +14,7 @@
|
||||
<meta property="og:url" content="http://0.0.0.0:4000/about/" />
|
||||
<meta property="og:site_name" content="Evan Pratten" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Evan Pratten","@type":"WebSite","url":"http://0.0.0.0:4000/about/","name":"Evan Pratten","description":"Computer wizard, student, @frc5024 programming team lead, and radio enthusiast.","@context":"https://schema.org"}</script>
|
||||
{"@type":"WebSite","url":"http://0.0.0.0:4000/about/","name":"Evan Pratten","description":"Computer wizard, student, @frc5024 programming team lead, and radio enthusiast.","headline":"Evan Pratten","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -219,7 +219,7 @@ sub rsa4096/0xA61A2F1676E35144 2019-08-11 [] [expires: 2025-08-09]
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -1,25 +1,18 @@
|
||||
body{
|
||||
margin:0;
|
||||
padding:0;
|
||||
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
|
||||
font-family: 'IBM Plex Sans', sans-serif;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.foot{
|
||||
.foot {}
|
||||
|
||||
}
|
||||
|
||||
.site-info{
|
||||
color:rgb(199, 195, 195);
|
||||
.site-info {
|
||||
color: rgb(199, 195, 195);
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
.header-gap{
|
||||
.header-gap {
|
||||
/* height: 30px;
|
||||
background-color: #ebeef1; */
|
||||
}
|
||||
@ -31,8 +24,6 @@ body{
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.table-fix {
|
||||
@ -44,11 +35,9 @@ table {
|
||||
margin-bottom: 1rem;
|
||||
color: #212529;
|
||||
vertical-align: top;
|
||||
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
table th, table td {
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
@ -74,23 +63,23 @@ thead th {
|
||||
.header .container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center; /* align vertical */
|
||||
align-items: center;
|
||||
/* align vertical */
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
max-width:100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.centre{
|
||||
.centre {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6{
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
}
|
||||
|
||||
@ -105,7 +94,6 @@ a h5 {
|
||||
/* border-radius: 15px;
|
||||
transform: translateY(-30px); */
|
||||
background-color: #fff;
|
||||
|
||||
}
|
||||
|
||||
.home.container {
|
||||
@ -115,8 +103,8 @@ a h5 {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.container .profile{
|
||||
width:30vw;
|
||||
.container .profile {
|
||||
width: 30vw;
|
||||
max-width: 242px;
|
||||
transform: translateY(calc(10vh * -1));
|
||||
border-color: #fff;
|
||||
@ -130,25 +118,25 @@ a h5 {
|
||||
border-radius: 5%;
|
||||
}
|
||||
|
||||
.home-header{
|
||||
height:100%;
|
||||
.home-header {
|
||||
height: 100%;
|
||||
background-image: url('/assets/images/banner.jpg');
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.site-ctr{
|
||||
min-height:100vh;
|
||||
.site-ctr {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
margin:auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.container .info{
|
||||
.container .info {
|
||||
transform: translateY(calc(13vh * -1));
|
||||
padding: 20px;
|
||||
}
|
||||
@ -161,6 +149,7 @@ a h5 {
|
||||
/* #particles-js{
|
||||
position: absolute;
|
||||
} */
|
||||
|
||||
/*
|
||||
.reactive-bg{
|
||||
position:relative
|
||||
@ -192,6 +181,7 @@ blockquote {
|
||||
padding: 0.5em 10px;
|
||||
/* quotes: "\201C""\201D""\2018""\2019"; */
|
||||
}
|
||||
|
||||
blockquote:before {
|
||||
color: #ccc;
|
||||
/* content: open-quote; */
|
||||
@ -200,6 +190,35 @@ blockquote:before {
|
||||
margin-right: 0.25em;
|
||||
vertical-align: -0.4em;
|
||||
}
|
||||
|
||||
blockquote p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
kbd {
|
||||
margin: 0px 0.1em;
|
||||
padding: 0.1em 0.6em;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgb(204, 204, 204);
|
||||
color: rgb(51, 51, 51);
|
||||
line-height: 1.4;
|
||||
font-size: 10px;
|
||||
display: inline-block;
|
||||
box-shadow: 0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px #ffffff;
|
||||
background-color: rgb(247, 247, 247);
|
||||
-moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset;
|
||||
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: #faf9f9;
|
||||
border-radius:5px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding:3px;
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2018-06-27T14:32:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Using a python script to create devRant posts based on the style and content of another user","dateModified":"2018-06-27T14:32:00-04:00","datePublished":"2018-06-27T14:32:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2018/06/27/becomeranter"},"url":"http://0.0.0.0:4000/blog/2018/06/27/becomeranter","description":"if/else ++","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2018-06-27T14:32:00-04:00","dateModified":"2018-06-27T14:32:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2018/06/27/becomeranter"},"url":"http://0.0.0.0:4000/blog/2018/06/27/becomeranter","description":"if/else ++","headline":"Using a python script to create devRant posts based on the style and content of another user","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -140,7 +140,7 @@ pip3 install tensorflow-gpu #for gpu processing
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-04-30T14:32:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"The language hunt","dateModified":"2019-04-30T14:32:00-04:00","datePublished":"2019-04-30T14:32:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/04/30/frc-languages"},"url":"http://0.0.0.0:4000/blog/2019/04/30/frc-languages","description":"Our programming team is looking to switch languages in the 2020 season. Here is the what, why, and how.","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-04-30T14:32:00-04:00","dateModified":"2019-04-30T14:32:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/04/30/frc-languages"},"url":"http://0.0.0.0:4000/blog/2019/04/30/frc-languages","description":"Our programming team is looking to switch languages in the 2020 season. Here is the what, why, and how.","headline":"The language hunt","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -104,7 +104,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-05-27T05:22:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Building a safe and easy system for sending computer vision data from a raspberry pi to a roborio","dateModified":"2019-05-27T05:22:00-04:00","datePublished":"2019-05-27T05:22:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/05/27/building-safe-vision-comms"},"url":"http://0.0.0.0:4000/blog/2019/05/27/building-safe-vision-comms","description":"Computer vision on an FRC robot has some problems. RoboRIO is not powerfull enough NetworkTables is not fast enough A TCP connection is great until you lose connection mDNS discovery is not reliable on the field UDP can skip frames","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-05-27T05:22:00-04:00","dateModified":"2019-05-27T05:22:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/05/27/building-safe-vision-comms"},"url":"http://0.0.0.0:4000/blog/2019/05/27/building-safe-vision-comms","description":"Computer vision on an FRC robot has some problems. RoboRIO is not powerfull enough NetworkTables is not fast enough A TCP connection is great until you lose connection mDNS discovery is not reliable on the field UDP can skip frames","headline":"Building a safe and easy system for sending computer vision data from a raspberry pi to a roborio","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -117,7 +117,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-06-12T09:09:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"GitHub’s CSS is boring. So I refreshed the design","dateModified":"2019-06-12T09:09:00-04:00","datePublished":"2019-06-12T09:09:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/12/styiling-github"},"url":"http://0.0.0.0:4000/blog/2019/06/12/styiling-github","description":"I have been using GitHub since 2017, and have been getting tired of GitHub’s theme. I didn’t need a huge change, just a small refresh. So, to solve this, I whipped out Stylus and made a nice little CSS file for it.","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-06-12T09:09:00-04:00","dateModified":"2019-06-12T09:09:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/12/styiling-github"},"url":"http://0.0.0.0:4000/blog/2019/06/12/styiling-github","description":"I have been using GitHub since 2017, and have been getting tired of GitHub’s theme. I didn’t need a huge change, just a small refresh. So, to solve this, I whipped out Stylus and made a nice little CSS file for it.","headline":"GitHub’s CSS is boring. So I refreshed the design","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -128,7 +128,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-06-16T11:51:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Graphing the relation between wheels and awards for FRC","dateModified":"2019-06-16T11:51:00-04:00","datePublished":"2019-06-16T11:51:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/16/graphing-w2a"},"url":"http://0.0.0.0:4000/blog/2019/06/16/graphing-w2a","description":"AKA. Why programmer + reddit + matplotlib is a bad idea.","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-06-16T11:51:00-04:00","dateModified":"2019-06-16T11:51:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/16/graphing-w2a"},"url":"http://0.0.0.0:4000/blog/2019/06/16/graphing-w2a","description":"AKA. Why programmer + reddit + matplotlib is a bad idea.","headline":"Graphing the relation between wheels and awards for FRC","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -142,7 +142,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-06-17T06:20:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"I made a new song!","dateModified":"2019-06-17T06:20:00-04:00","datePublished":"2019-06-17T06:20:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/17/amm2m1-release"},"url":"http://0.0.0.0:4000/blog/2019/06/17/amm2m1-release","description":"Releasing a new song with friends at school","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-06-17T06:20:00-04:00","dateModified":"2019-06-17T06:20:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/17/amm2m1-release"},"url":"http://0.0.0.0:4000/blog/2019/06/17/amm2m1-release","description":"Releasing a new song with friends at school","headline":"I made a new song!","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -101,7 +101,7 @@ Your browser does not support audio players
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-06-21T11:14:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"What I have learned from 2 years of FRC programming","dateModified":"2019-06-21T11:14:00-04:00","datePublished":"2019-06-21T11:14:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/21/robot-experiences"},"url":"http://0.0.0.0:4000/blog/2019/06/21/robot-experiences","description":"Robots are pretty cool","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-06-21T11:14:00-04:00","dateModified":"2019-06-21T11:14:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/21/robot-experiences"},"url":"http://0.0.0.0:4000/blog/2019/06/21/robot-experiences","description":"Robots are pretty cool","headline":"What I have learned from 2 years of FRC programming","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -141,7 +141,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-06-23T18:04:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"I gave Google’s CTF a short try and learned a thing or two","dateModified":"2019-06-23T18:04:00-04:00","datePublished":"2019-06-23T18:04:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/23/googlectf"},"url":"http://0.0.0.0:4000/blog/2019/06/23/googlectf","description":"But exams got in the way and took all the fun","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-06-23T18:04:00-04:00","dateModified":"2019-06-23T18:04:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/23/googlectf"},"url":"http://0.0.0.0:4000/blog/2019/06/23/googlectf","description":"But exams got in the way and took all the fun","headline":"I gave Google’s CTF a short try and learned a thing or two","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -99,7 +99,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-06-24T17:36:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"The language hunt: Part 2","dateModified":"2019-06-24T17:36:00-04:00","datePublished":"2019-06-24T17:36:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/24/languagehunt2"},"url":"http://0.0.0.0:4000/blog/2019/06/24/languagehunt2","description":"A quick followup","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-06-24T17:36:00-04:00","dateModified":"2019-06-24T17:36:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/24/languagehunt2"},"url":"http://0.0.0.0:4000/blog/2019/06/24/languagehunt2","description":"A quick followup","headline":"The language hunt: Part 2","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -99,7 +99,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-06-26T11:48:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"BashSmash","dateModified":"2019-06-26T11:48:00-04:00","datePublished":"2019-06-26T11:48:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/26/bashsmash"},"url":"http://0.0.0.0:4000/blog/2019/06/26/bashsmash","description":"A tool for driving people crazy","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-06-26T11:48:00-04:00","dateModified":"2019-06-26T11:48:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/26/bashsmash"},"url":"http://0.0.0.0:4000/blog/2019/06/26/bashsmash","description":"A tool for driving people crazy","headline":"BashSmash","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -208,7 +208,7 @@ __<span class="o">()</span> <span class="o">{</span>/???/???/???n?f <span class=
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-06-27T13:16:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"I had some fun with a router","dateModified":"2019-06-27T13:16:00-04:00","datePublished":"2019-06-27T13:16:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/27/pwnlink"},"url":"http://0.0.0.0:4000/blog/2019/06/27/pwnlink","description":"cleartext passwords + external management = death wish","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-06-27T13:16:00-04:00","dateModified":"2019-06-27T13:16:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/27/pwnlink"},"url":"http://0.0.0.0:4000/blog/2019/06/27/pwnlink","description":"cleartext passwords + external management = death wish","headline":"I had some fun with a router","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -129,7 +129,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-06-27T03:00:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Hunting snakes with a shotgun","dateModified":"2019-06-27T03:00:00-04:00","datePublished":"2019-06-27T03:00:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/27/python"},"url":"http://0.0.0.0:4000/blog/2019/06/27/python","description":"Python is a little too forgiving","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-06-27T03:00:00-04:00","dateModified":"2019-06-27T03:00:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/06/27/python"},"url":"http://0.0.0.0:4000/blog/2019/06/27/python","description":"Python is a little too forgiving","headline":"Hunting snakes with a shotgun","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -194,7 +194,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-07-01T18:13:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"devDNS","dateModified":"2019-07-01T18:13:00-04:00","datePublished":"2019-07-01T18:13:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/07/01/devdns"},"url":"http://0.0.0.0:4000/blog/2019/07/01/devdns","description":"The DNS over devRant service","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-07-01T18:13:00-04:00","dateModified":"2019-07-01T18:13:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/07/01/devdns"},"url":"http://0.0.0.0:4000/blog/2019/07/01/devdns","description":"The DNS over devRant service","headline":"devDNS","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -118,7 +118,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-07-06T11:08:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Scraping FRC team’s GitHub accounts to gather large amounts of data","dateModified":"2019-07-06T11:08:00-04:00","datePublished":"2019-07-06T11:08:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/07/06/scrapingfrcgithub"},"url":"http://0.0.0.0:4000/blog/2019/07/06/scrapingfrcgithub","description":"There are a lot of teams…","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-07-06T11:08:00-04:00","dateModified":"2019-07-06T11:08:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/07/06/scrapingfrcgithub"},"url":"http://0.0.0.0:4000/blog/2019/07/06/scrapingfrcgithub","description":"There are a lot of teams…","headline":"Scraping FRC team’s GitHub accounts to gather large amounts of data","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -191,7 +191,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-07-13T10:43:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Taking a look back at GMAD","dateModified":"2019-07-13T10:43:00-04:00","datePublished":"2019-07-13T10:43:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/07/13/lookback-gmad"},"url":"http://0.0.0.0:4000/blog/2019/07/13/lookback-gmad","description":"Fun, Simple, and Quick","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-07-13T10:43:00-04:00","dateModified":"2019-07-13T10:43:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/07/13/lookback-gmad"},"url":"http://0.0.0.0:4000/blog/2019/07/13/lookback-gmad","description":"Fun, Simple, and Quick","headline":"Taking a look back at GMAD","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-07-15T14:38:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Mind map generation with Python","dateModified":"2019-07-15T14:38:00-04:00","datePublished":"2019-07-15T14:38:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/07/15/mindmap"},"url":"http://0.0.0.0:4000/blog/2019/07/15/mindmap","description":"Step 1","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-07-15T14:38:00-04:00","dateModified":"2019-07-15T14:38:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/07/15/mindmap"},"url":"http://0.0.0.0:4000/blog/2019/07/15/mindmap","description":"Step 1","headline":"Mind map generation with Python","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -206,7 +206,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-08-10T16:57:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"My weird piece of EDC","dateModified":"2019-08-10T16:57:00-04:00","datePublished":"2019-08-10T16:57:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/08/10/why-i-carry-nfc"},"url":"http://0.0.0.0:4000/blog/2019/08/10/why-i-carry-nfc","description":"Reasons why I always carry NFC cards with me","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-08-10T16:57:00-04:00","dateModified":"2019-08-10T16:57:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/08/10/why-i-carry-nfc"},"url":"http://0.0.0.0:4000/blog/2019/08/10/why-i-carry-nfc","description":"Reasons why I always carry NFC cards with me","headline":"My weird piece of EDC","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -124,7 +124,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-08-12T15:40:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"How I set up ひらがな input on my laptop","dateModified":"2019-08-12T15:40:00-04:00","datePublished":"2019-08-12T15:40:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/08/12/setting-up-ja"},"url":"http://0.0.0.0:4000/blog/2019/08/12/setting-up-ja","description":"I3wm makes everything 10x harder than it should be","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-08-12T15:40:00-04:00","dateModified":"2019-08-12T15:40:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/08/12/setting-up-ja"},"url":"http://0.0.0.0:4000/blog/2019/08/12/setting-up-ja","description":"I3wm makes everything 10x harder than it should be","headline":"How I set up ひらがな input on my laptop","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -169,7 +169,7 @@ ibus-daemon <span class="nt">-drx</span>
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-08-24T09:13:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Keyed data encoding with Python","dateModified":"2019-08-24T09:13:00-04:00","datePublished":"2019-08-24T09:13:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/08/24/shift2"},"url":"http://0.0.0.0:4000/blog/2019/08/24/shift2","description":"XOR is pretty cool","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-08-24T09:13:00-04:00","dateModified":"2019-08-24T09:13:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/08/24/shift2"},"url":"http://0.0.0.0:4000/blog/2019/08/24/shift2","description":"XOR is pretty cool","headline":"Keyed data encoding with Python","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -154,7 +154,7 @@ shift2 <span class="nt">-h</span>
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-08-27T08:37:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"I did some cleaning","dateModified":"2019-08-27T08:37:00-04:00","datePublished":"2019-08-27T08:37:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/08/27/github-cleanup"},"url":"http://0.0.0.0:4000/blog/2019/08/27/github-cleanup","description":"Spring cleaning is fun when it isn’t spring, and a computer does all the work","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-08-27T08:37:00-04:00","dateModified":"2019-08-27T08:37:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/08/27/github-cleanup"},"url":"http://0.0.0.0:4000/blog/2019/08/27/github-cleanup","description":"Spring cleaning is fun when it isn’t spring, and a computer does all the work","headline":"I did some cleaning","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@ Starting from the top, scroll through, and middle click on anything you want to
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2019-09-07T09:13:00-04:00" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Doing Python OOP the wrong way","dateModified":"2019-09-07T09:13:00-04:00","datePublished":"2019-09-07T09:13:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/09/07/wrong-python"},"url":"http://0.0.0.0:4000/blog/2019/09/07/wrong-python","description":"In the name of science!","@context":"https://schema.org"}</script>
|
||||
{"datePublished":"2019-09-07T09:13:00-04:00","dateModified":"2019-09-07T09:13:00-04:00","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4000/blog/2019/09/07/wrong-python"},"url":"http://0.0.0.0:4000/blog/2019/09/07/wrong-python","description":"In the name of science!","headline":"Doing Python OOP the wrong way","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -174,7 +174,7 @@ fn printMyNumber(MyClass* self){
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
<meta property="og:url" content="http://0.0.0.0:4000/blog/" />
|
||||
<meta property="og:site_name" content="Evan Pratten" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Evan Pratten","@type":"WebPage","url":"http://0.0.0.0:4000/blog/","description":"Computer wizard, student, @frc5024 programming team lead, and radio enthusiast.","@context":"https://schema.org"}</script>
|
||||
{"@type":"WebPage","url":"http://0.0.0.0:4000/blog/","description":"Computer wizard, student, @frc5024 programming team lead, and radio enthusiast.","headline":"Evan Pratten","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -79,22 +79,22 @@
|
||||
Featured Post
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Turning a web IDE into a disposable linux environment
|
||||
<h5 class="card-title">Programming a live robot
|
||||
|
||||
</h5>
|
||||
<p class="card-text">My Python code keeps popping shells</p>
|
||||
<a href="/blog/2019/11/09/easyshell" class="btn btn-primary">View</a>
|
||||
<p class="card-text">Living on the edge is an understatement</p>
|
||||
<a href="/blog/2019/11/20/realtime-robot-code" class="btn btn-primary">View</a>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<a href="/blog/2019/11/09/easyshell" class="list-group-item list-group-item-action">
|
||||
<a href="/blog/2019/11/20/realtime-robot-code" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<div class="card-body">
|
||||
<h5 class="mb-1">Turning a web IDE into a disposable linux environment
|
||||
<h5 class="mb-1">Programming a live robot
|
||||
|
||||
</h5>
|
||||
<p class="card-text">My Python code keeps popping shells</p>
|
||||
<p class="card-text">Living on the edge is an understatement</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@ -107,6 +107,21 @@
|
||||
|
||||
|
||||
|
||||
<a href="/blog/2019/11/09/easyshell" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Turning a web IDE into a disposable linux environment</h5>
|
||||
<!-- <small>2019-11-09 19:00:00 -0500</small> -->
|
||||
</div>
|
||||
<p class="card-text">My Python code keeps popping shells</p>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="/blog/2019/10/05/billwurtz" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Using an RNN to generate Bill Wurtz notes</h5>
|
||||
@ -550,7 +565,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
<meta property="og:url" content="http://0.0.0.0:4000/documentation" />
|
||||
<meta property="og:site_name" content="Evan Pratten" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Evan Pratten","@type":"WebPage","url":"http://0.0.0.0:4000/documentation","description":"Computer wizard, student, @frc5024 programming team lead, and radio enthusiast.","@context":"https://schema.org"}</script>
|
||||
{"@type":"WebPage","url":"http://0.0.0.0:4000/documentation","description":"Computer wizard, student, @frc5024 programming team lead, and radio enthusiast.","headline":"Evan Pratten","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
276
_site/feed.xml
276
_site/feed.xml
@ -1,4 +1,175 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.6">Jekyll</generator><link href="http://0.0.0.0:4000/feed.xml" rel="self" type="application/atom+xml" /><link href="http://0.0.0.0:4000/" rel="alternate" type="text/html" /><updated>2019-11-17T10:22:12-05:00</updated><id>http://0.0.0.0:4000/feed.xml</id><title type="html">Evan Pratten</title><subtitle>Computer wizard, student, <a href="https://frc5024.github.io">@frc5024</a> programming team lead, and radio enthusiast.</subtitle><entry><title type="html">Using an RNN to generate Bill Wurtz notes</title><link href="http://0.0.0.0:4000/blog/2019/10/05/billwurtz" rel="alternate" type="text/html" title="Using an RNN to generate Bill Wurtz notes" /><published>2019-10-05T14:54:00-04:00</published><updated>2019-10-05T14:54:00-04:00</updated><id>http://0.0.0.0:4000/blog/2019/10/05/BillWurtz</id><content type="html" xml:base="http://0.0.0.0:4000/blog/2019/10/05/billwurtz"><p><a href="https://billwurtz.com/">Bill Wurtz</a> is an American musician who became <a href="https://socialblade.com/youtube/user/billwurtz/realtime">reasonably famous</a> through short musical videos posted to Vine and YouTube. I was searching through his website the other day, and stumbled upon a page labeled <a href="https://billwurtz.com/notebook.html"><em>notebook</em></a>, and thought I should check it out.</p>
|
||||
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.6">Jekyll</generator><link href="http://0.0.0.0:4000/feed.xml" rel="self" type="application/atom+xml" /><link href="http://0.0.0.0:4000/" rel="alternate" type="text/html" /><updated>2019-11-20T10:04:40-05:00</updated><id>http://0.0.0.0:4000/feed.xml</id><title type="html">Evan Pratten</title><subtitle>Computer wizard, student, <a href="https://frc5024.github.io">@frc5024</a> programming team lead, and radio enthusiast.</subtitle><entry><title type="html">Programming a live robot</title><link href="http://0.0.0.0:4000/blog/2019/11/20/realtime-robot-code" rel="alternate" type="text/html" title="Programming a live robot" /><published>2019-11-20T05:04:00-05:00</published><updated>2019-11-20T05:04:00-05:00</updated><id>http://0.0.0.0:4000/blog/2019/11/20/Realtime-robot-code</id><content type="html" xml:base="http://0.0.0.0:4000/blog/2019/11/20/realtime-robot-code"><blockquote>
|
||||
<p><em>“So.. what if we could skip asking for driver inputs, and just have the robot operators control the bot through a commandline interface?”</em></p>
|
||||
</blockquote>
|
||||
|
||||
<p>This is exactly the kind of question I randomly ask while sitting in the middle of class, staring at my laptop. So, here is a post about my real-time programming adventure!</p>
|
||||
|
||||
<h2 id="geting-started">Geting started</h2>
|
||||
|
||||
<p>To get started, I needed a few things. Firstly, I have a laptop running <a href="/about/#my-gear">a Linux distribution</a>. This allows me to use <a href="https://en.wikipedia.org/wiki/Secure_Shell">SSH</a> and <a href="https://en.wikipedia.org/wiki/Secure_copy">SCP</a>. There are Windows versions of both of these programs, but I find the “linux experience” easier to use. Secondly, I have grabbed one of <a href="https://www.thebluealliance.com/team/5024">5024</a>’s <a href="https://cs.5024.ca/webdocs/docs/robots">robots</a> to be subjected to my experiment. The components I care about are:</p>
|
||||
|
||||
<ul>
|
||||
<li>A RoboRIO running 2019v12 firmware</li>
|
||||
<li>2 <a href="https://www.ctr-electronics.com/talon-srx.html">TalonSRX</a> motor controllers</li>
|
||||
<li>An FRC router</li>
|
||||
</ul>
|
||||
|
||||
<p>Most importantly, the RoboRIO has <a href="https://robotpy.readthedocs.io/en/stable/install/robot.html#install-robotpy">RobotPy</a> and the <a href="https://robotpy.readthedocs.io/en/stable/install/ctre.html">CTRE Libraries</a> installed.</p>
|
||||
|
||||
<h3 id="ssh-connection">SSH connection</h3>
|
||||
|
||||
<p>To get some code running on the robot, we must first connect to it via SSH. Depending on your connection to the RoboRIO, this step may be different. Generally, the following command will work just fine to connect (assuming your computer has an <a href="https://en.wikipedia.org/wiki/Multicast_DNS">mDNS</a> service):</p>
|
||||
|
||||
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh admin@roborio-&lt;team&gt;-frc.local
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>If you have issues, try one of the following addresses instead:</p>
|
||||
|
||||
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>roborio-&lt;team&gt;-FRC
|
||||
roborio-&lt;team&gt;-FRC.lan
|
||||
roborio-&lt;team&gt;-FRC.frc-field.local
|
||||
10.TE.AM.2
|
||||
172.22.11.2 # Only works on a USB connection
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>If you are asked for a password, and have not set one, press <kbd>Enter</kbd> 3 times (Don’t ask why.. this just works).</p>
|
||||
|
||||
<h2 id="repl-based-control">REPL-based control</h2>
|
||||
|
||||
<p>If you have seen my work before, you’ll know that I use Python for basically everything. This project is no exception. Conveniently, the RoboRIO is a linux-based device, and can run a Python3 <a href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop">REPL</a>. This allows real-time robot programming using a REPL via SSH.</p>
|
||||
|
||||
<p>WPILib requires a robot class to act as a “callback” for robot actions. My idea was to build a special robot class with static methods to allow me to start it, then use the REPL to interact with some control methods (like <code class="highlighter-rouge">setSpeed</code> and <code class="highlighter-rouge">stop</code>).</p>
|
||||
|
||||
<p>After connecting to the robot via SSH, a Python REPL can be started by running <code class="highlighter-rouge">python3</code>. If there is already robot code running, it will be automatically killed in the next step.</p>
|
||||
|
||||
<p>With Python running, we will need 2 libraries imported. <code class="highlighter-rouge">wpilib</code> and <code class="highlighter-rouge">ctre</code>. When importing <code class="highlighter-rouge">wpilib</code> a message may appear to notify you that the old robot code has been stopped.</p>
|
||||
|
||||
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">wpilib</span>
|
||||
<span class="n">Killing</span> <span class="n">previously</span> <span class="n">running</span> <span class="n">FRC</span> <span class="n">program</span><span class="o">...</span>
|
||||
<span class="n">FRC</span> <span class="n">pid</span> <span class="mi">1445</span> <span class="n">did</span> <span class="ow">not</span> <span class="n">die</span> <span class="n">within</span> <span class="mi">0</span><span class="n">ms</span><span class="o">.</span> <span class="n">Force</span> <span class="n">killing</span> <span class="k">with</span> <span class="n">kill</span> <span class="o">-</span><span class="mi">9</span>
|
||||
<span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">ctre</span>
|
||||
</code></pre></div></div>
|
||||
<p>Keep in mind, this is a REPL. Lines that start with <code class="highlighter-rouge">&gt;&gt;&gt;</code> or <code class="highlighter-rouge">...</code> are <em>user input</em>. Everything else is produced by code.</p>
|
||||
|
||||
<p>Next, we need to write a little code to get the robot operational. To save time, I wrote this “library” to do most of the work for me. Just save this as <code class="highlighter-rouge">rtrbt.py</code> somewhere, then use SCP to copy it to <code class="highlighter-rouge">/home/lvuser/rtrbt.py</code>.</p>
|
||||
|
||||
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># RealTime FRC Robot control helper
|
||||
# By: Evan Pratten &lt;ewpratten&gt;
|
||||
</span>
|
||||
<span class="c1"># Import normal robot stuff
|
||||
</span><span class="kn">import</span> <span class="nn">wpilib</span>
|
||||
<span class="kn">import</span> <span class="nn">ctre</span>
|
||||
|
||||
<span class="c1"># Handle WPI trickery
|
||||
</span><span class="k">try</span><span class="p">:</span>
|
||||
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">patch</span>
|
||||
<span class="k">except</span> <span class="nb">ImportError</span><span class="p">:</span>
|
||||
<span class="kn">from</span> <span class="nn">mock</span> <span class="kn">import</span> <span class="n">patch</span>
|
||||
<span class="kn">import</span> <span class="nn">sys</span>
|
||||
<span class="kn">from</span> <span class="nn">threading</span> <span class="kn">import</span> <span class="n">Thread</span>
|
||||
|
||||
|
||||
<span class="c1">## Internal methods ##
|
||||
</span><span class="n">_controllers</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="n">_thread</span><span class="p">:</span> <span class="n">Thread</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">_RTRobot</span><span class="p">(</span><span class="n">wpilib</span><span class="o">.</span><span class="n">TimedRobot</span><span class="p">):</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">robotInit</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># Create motors
|
||||
</span> <span class="n">_controllers</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ctre</span><span class="o">.</span><span class="n">WPI_TalonSRX</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
|
||||
<span class="n">_controllers</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ctre</span><span class="o">.</span><span class="n">WPI_TalonSRX</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
|
||||
|
||||
<span class="c1"># Set safe modes
|
||||
</span> <span class="n">_controllers</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">setSafetyEnabled</span><span class="p">(</span><span class="bp">False</span><span class="p">)</span>
|
||||
<span class="n">_controllers</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">setSafetyEnabled</span><span class="p">(</span><span class="bp">False</span><span class="p">)</span>
|
||||
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">_start</span><span class="p">():</span>
|
||||
<span class="c1"># Handle fake args
|
||||
</span> <span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="s">"run"</span><span class="p">,</span> <span class="s">"run"</span><span class="p">]</span>
|
||||
<span class="k">with</span> <span class="n">patch</span><span class="o">.</span><span class="nb">object</span><span class="p">(</span><span class="n">sys</span><span class="p">,</span> <span class="s">"argv"</span><span class="p">,</span> <span class="n">args</span><span class="p">):</span>
|
||||
<span class="k">print</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span>
|
||||
<span class="n">wpilib</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">_RTRobot</span><span class="p">)</span>
|
||||
|
||||
<span class="c1">## Utils ##
|
||||
</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">startRobot</span><span class="p">():</span>
|
||||
<span class="s">""" Start the robot code """</span>
|
||||
<span class="k">global</span> <span class="n">_thread</span>
|
||||
<span class="n">_thread</span> <span class="o">=</span> <span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">_start</span><span class="p">)</span>
|
||||
<span class="n">_thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">setMotor</span><span class="p">(</span><span class="nb">id</span><span class="p">,</span> <span class="n">speed</span><span class="p">):</span>
|
||||
<span class="s">""" Set a motor speed """</span>
|
||||
<span class="n">_controllers</span><span class="p">[</span><span class="nb">id</span><span class="p">]</span><span class="o">.</span><span class="nb">set</span><span class="p">(</span><span class="n">speed</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">arcadeDrive</span><span class="p">(</span><span class="n">speed</span><span class="p">,</span> <span class="n">rotation</span><span class="p">):</span>
|
||||
<span class="s">""" Control the robot with arcade inputs """</span>
|
||||
|
||||
<span class="n">l</span> <span class="o">=</span> <span class="n">speed</span> <span class="o">+</span> <span class="n">rotation</span>
|
||||
<span class="n">r</span> <span class="o">=</span> <span class="n">speed</span> <span class="o">-</span> <span class="n">rotation</span>
|
||||
|
||||
<span class="n">setMotor</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">l</span><span class="p">)</span>
|
||||
<span class="n">setMotor</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span>
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>The idea is to create a simple robot program with global hooks into the motor controllers. Python’s mocking tools are used to fake commandline arguments to trick robotpy into thinking this script is being run via the RIO’s robotCommand.</p>
|
||||
|
||||
<p>Once this script has been placed on the robot, SSH back in as <code class="highlighter-rouge">lvuser</code> (not <code class="highlighter-rouge">admin</code>), and run <code class="highlighter-rouge">python3</code>. If using <code class="highlighter-rouge">rtrbt.py</code>, the imports mentioned above are handled for you. To start the robot, just run the following:</p>
|
||||
|
||||
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">rtrbt</span> <span class="kn">import</span> <span class="o">*</span>
|
||||
<span class="o">&gt;&gt;&gt;</span> <span class="n">startRobot</span><span class="p">()</span>
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>WPILib will dump some logs into the terminal (and probably some spam) from it’s own thread. Don’t worry if you can’t see the REPL prompt. It’s probably just hidden due to the use of multiple threads in the same shell. Pressing <kbd>Enter</kbd> should show it again.</p>
|
||||
|
||||
<p>I added 2 functions for controlling motors. The first, <code class="highlighter-rouge">setMotor</code>, will set either the left (0), or right (1) motor to the specified speed. <code class="highlighter-rouge">arcadeDrive</code> will allow you to specify a speed and rotational force for the robot’s drivetrain.</p>
|
||||
|
||||
<p>To kill the code and exit, press <kbd>CTRL</kbd> + <kbd>D</kbd> then <kbd>CTRL</kbd> + <kbd>C</kbd>.</p>
|
||||
|
||||
<p>Here is an example where I start the bot, then tell it to drive forward, then kill the left motor:</p>
|
||||
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Python</span> <span class="mf">3.6.8</span> <span class="p">(</span><span class="n">default</span><span class="p">,</span> <span class="n">Oct</span> <span class="mi">7</span> <span class="mi">2019</span><span class="p">,</span> <span class="mi">12</span><span class="p">:</span><span class="mi">59</span><span class="p">:</span><span class="mi">55</span><span class="p">)</span>
|
||||
<span class="p">[</span><span class="n">GCC</span> <span class="mf">8.3.0</span><span class="p">]</span> <span class="n">on</span> <span class="n">linux</span>
|
||||
<span class="n">Type</span> <span class="s">"help"</span><span class="p">,</span> <span class="s">"copyright"</span><span class="p">,</span> <span class="s">"credits"</span> <span class="ow">or</span> <span class="s">"license"</span> <span class="k">for</span> <span class="n">more</span> <span class="n">information</span><span class="o">.</span>
|
||||
<span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">rtrbt</span> <span class="kn">import</span> <span class="o">*</span>
|
||||
<span class="o">&gt;&gt;&gt;</span> <span class="n">startRobot</span><span class="p">()</span>
|
||||
<span class="p">[</span><span class="s">'run'</span><span class="p">,</span> <span class="s">'run'</span><span class="p">]</span>
|
||||
<span class="mi">17</span><span class="p">:</span><span class="mi">53</span><span class="p">:</span><span class="mi">46</span><span class="p">:</span><span class="mi">472</span> <span class="n">INFO</span> <span class="p">:</span> <span class="n">wpilib</span> <span class="p">:</span> <span class="n">WPILib</span> <span class="n">version</span> <span class="mf">2019.2.3</span>
|
||||
<span class="mi">17</span><span class="p">:</span><span class="mi">53</span><span class="p">:</span><span class="mi">46</span><span class="p">:</span><span class="mi">473</span> <span class="n">INFO</span> <span class="p">:</span> <span class="n">wpilib</span> <span class="p">:</span> <span class="n">HAL</span> <span class="n">base</span> <span class="n">version</span> <span class="mf">2019.2.3</span><span class="p">;</span>
|
||||
<span class="mi">17</span><span class="p">:</span><span class="mi">53</span><span class="p">:</span><span class="mi">46</span><span class="p">:</span><span class="mi">473</span> <span class="n">INFO</span> <span class="p">:</span> <span class="n">wpilib</span> <span class="p">:</span> <span class="n">robotpy</span><span class="o">-</span><span class="n">ctre</span> <span class="n">version</span> <span class="mf">2019.3.2</span>
|
||||
<span class="mi">17</span><span class="p">:</span><span class="mi">53</span><span class="p">:</span><span class="mi">46</span><span class="p">:</span><span class="mi">473</span> <span class="n">INFO</span> <span class="p">:</span> <span class="n">wpilib</span> <span class="p">:</span> <span class="n">robotpy</span><span class="o">-</span><span class="n">cscore</span> <span class="n">version</span> <span class="mf">2019.1.0</span>
|
||||
<span class="mi">17</span><span class="p">:</span><span class="mi">53</span><span class="p">:</span><span class="mi">46</span><span class="p">:</span><span class="mi">473</span> <span class="n">INFO</span> <span class="p">:</span> <span class="n">faulthandler</span> <span class="p">:</span> <span class="n">registered</span> <span class="n">SIGUSR2</span> <span class="k">for</span> <span class="n">PID</span> <span class="mi">5447</span>
|
||||
<span class="mi">17</span><span class="p">:</span><span class="mi">53</span><span class="p">:</span><span class="mi">46</span><span class="p">:</span><span class="mi">474</span> <span class="n">INFO</span> <span class="p">:</span> <span class="n">nt</span> <span class="p">:</span> <span class="n">NetworkTables</span> <span class="n">initialized</span> <span class="ow">in</span> <span class="n">server</span> <span class="n">mode</span>
|
||||
<span class="mi">17</span><span class="p">:</span><span class="mi">53</span><span class="p">:</span><span class="mi">46</span><span class="p">:</span><span class="mi">497</span> <span class="n">INFO</span> <span class="p">:</span> <span class="n">robot</span> <span class="p">:</span> <span class="n">Default</span> <span class="n">IterativeRobot</span><span class="o">.</span><span class="n">disabledInit</span><span class="p">()</span> <span class="n">method</span><span class="o">...</span> <span class="n">Override</span> <span class="n">me</span><span class="err">!</span>
|
||||
<span class="mi">17</span><span class="p">:</span><span class="mi">53</span><span class="p">:</span><span class="mi">46</span><span class="p">:</span><span class="mi">498</span> <span class="n">INFO</span> <span class="p">:</span> <span class="n">robot</span> <span class="p">:</span> <span class="n">Default</span> <span class="n">IterativeRobot</span><span class="o">.</span><span class="n">disabledPeriodic</span><span class="p">()</span> <span class="n">method</span><span class="o">...</span> <span class="n">Override</span> <span class="n">me</span><span class="err">!</span>
|
||||
<span class="mi">17</span><span class="p">:</span><span class="mi">53</span><span class="p">:</span><span class="mi">46</span><span class="p">:</span><span class="mi">498</span> <span class="n">INFO</span> <span class="p">:</span> <span class="n">robot</span> <span class="p">:</span> <span class="n">Default</span> <span class="n">IterativeRobot</span><span class="o">.</span><span class="n">robotPeriodic</span><span class="p">()</span> <span class="n">method</span><span class="o">...</span> <span class="n">Override</span> <span class="n">me</span><span class="err">!</span>
|
||||
<span class="o">&gt;&gt;&gt;</span>
|
||||
<span class="o">&gt;&gt;&gt;</span> <span class="n">arcadeDrive</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">)</span>
|
||||
<span class="o">&gt;&gt;&gt;</span> <span class="n">setMotor</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">)</span>
|
||||
<span class="o">&gt;&gt;&gt;</span>
|
||||
<span class="o">^</span><span class="n">C</span>
|
||||
<span class="nb">Exception</span> <span class="n">ignored</span> <span class="ow">in</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">module</span> <span class="s">'threading'</span> <span class="k">from</span> <span class="s">'/usr/lib/python3.6/threading.py'</span><span class="o">&gt;</span>
|
||||
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
|
||||
<span class="n">File</span> <span class="s">"/usr/lib/python3.6/threading.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1294</span><span class="p">,</span> <span class="ow">in</span> <span class="n">_shutdown</span>
|
||||
<span class="n">t</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
|
||||
<span class="n">File</span> <span class="s">"/usr/lib/python3.6/threading.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1056</span><span class="p">,</span> <span class="ow">in</span> <span class="n">join</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_wait_for_tstate_lock</span><span class="p">()</span>
|
||||
<span class="n">File</span> <span class="s">"/usr/lib/python3.6/threading.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1072</span><span class="p">,</span> <span class="ow">in</span> <span class="n">_wait_for_tstate_lock</span>
|
||||
<span class="k">elif</span> <span class="n">lock</span><span class="o">.</span><span class="n">acquire</span><span class="p">(</span><span class="n">block</span><span class="p">,</span> <span class="n">timeout</span><span class="p">):</span>
|
||||
<span class="nb">KeyboardInterrupt</span>
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>The message at the end occurs when killing the code.</p>
|
||||
|
||||
<h2 id="conclusion">Conclusion</h2>
|
||||
|
||||
<p>I have no idea why any of this would be useful, or if it is even field legal.. It’s just a fun project for a monday morning.</p></content><author><name></name></author><summary type="html">“So.. what if we could skip asking for driver inputs, and just have the robot operators control the bot through a commandline interface?”</summary></entry><entry><title type="html">Using an RNN to generate Bill Wurtz notes</title><link href="http://0.0.0.0:4000/blog/2019/10/05/billwurtz" rel="alternate" type="text/html" title="Using an RNN to generate Bill Wurtz notes" /><published>2019-10-05T14:54:00-04:00</published><updated>2019-10-05T14:54:00-04:00</updated><id>http://0.0.0.0:4000/blog/2019/10/05/BillWurtz</id><content type="html" xml:base="http://0.0.0.0:4000/blog/2019/10/05/billwurtz"><p><a href="https://billwurtz.com/">Bill Wurtz</a> is an American musician who became <a href="https://socialblade.com/youtube/user/billwurtz/realtime">reasonably famous</a> through short musical videos posted to Vine and YouTube. I was searching through his website the other day, and stumbled upon a page labeled <a href="https://billwurtz.com/notebook.html"><em>notebook</em></a>, and thought I should check it out.</p>
|
||||
|
||||
<p>Bill’s notebook is a large (about 580 posts) collection of random thoughts, ideas, and sometimes just collections of words. A prime source of entertainment, and neural network inputs..</p>
|
||||
|
||||
@ -568,105 +739,4 @@ ibus-daemon <span class="nt">-drx</span>
|
||||
<p>If you would like to add a distro or three to the website, feel free to make a pull request over on <a href="https://github.com/Ewpratten/GiveMeADistro">GitHub</a>.</p>
|
||||
|
||||
<h2 id="why-make-a-post-about-it-a-year-later">Why make a post about it a year later?</h2>
|
||||
<p>I just really enjoyed working with the project and sharing it with friends, so I figured I should mention it here too. Maybe it will inspire someone to make something cool!</p></content><author><name></name></author><summary type="html">One day, back in June of 2018, I was both looking for a new project to work on, and trying to decide which Linux distro to install on one of my computers. From this, a little project was born. Give Me a Distro (or, GMAD, as I like to call it) is a little website that chooses a random distribution of Linux and shows a description of what you are about to get yourself into, and a download link for the latest ISO.</summary></entry><entry><title type="html">Scraping FRC team’s GitHub accounts to gather large amounts of data</title><link href="http://0.0.0.0:4000/blog/2019/07/06/scrapingfrcgithub" rel="alternate" type="text/html" title="Scraping FRC team's GitHub accounts to gather large amounts of data" /><published>2019-07-06T11:08:00-04:00</published><updated>2019-07-06T11:08:00-04:00</updated><id>http://0.0.0.0:4000/blog/2019/07/06/ScrapingFRCGithub</id><content type="html" xml:base="http://0.0.0.0:4000/blog/2019/07/06/scrapingfrcgithub"><p>I was curious about the most used languages for FRC, so I build a Python script to find out what they where.</p>
|
||||
|
||||
<h2 id="some-basic-data">Some basic data</h2>
|
||||
<p>Before we get to the heavy work done by my script, let’s start with some general data.</p>
|
||||
|
||||
<p>Thanks to the <a href="https://www.thebluealliance.com/apidocs/v3">TBA API</a>, I know that there are 6917 registered teams. 492 of them have registered at least one account on GitHub.</p>
|
||||
|
||||
<h2 id="how-the-script-works">How the script works</h2>
|
||||
<p>The script is split into steps:</p>
|
||||
<ul>
|
||||
<li>Get a list of every registered team</li>
|
||||
<li>Check for a github account attached to every registered team
|
||||
<ul>
|
||||
<li>If a team has an account, it is added to the dataset</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Load each github profile
|
||||
<ul>
|
||||
<li>If it is a private account, move on</li>
|
||||
<li>Use Regex to find all languages used</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Compile data and sort</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="getting-a-list-of-accounts">Getting a list of accounts</h3>
|
||||
<p>This is probably the simplest step in the whole process. I used the auto-generated <a href="https://github.com/TBA-API/tba-api-client-python">tbaapiv3client</a> python library’s <code class="highlighter-rouge">get_teams_keys(key)</code> function, and kept incrementing <code class="highlighter-rouge">key</code> until I got an empty array. All returned data was then added together into a big list of team keys.</p>
|
||||
|
||||
<h3 id="checking-for-a-teams-github-account">Checking for a team’s github account</h3>
|
||||
<p>The <a href="https://www.thebluealliance.com/apidocs/v3">TBA API</a> helpfully provides a <code class="highlighter-rouge">/api/v3/team/&lt;number&gt;/social_media</code> API endpoint that will give the GitHub username for any team you request. (or nothing if they don’t use github)</p>
|
||||
|
||||
<p>A <code class="highlighter-rouge">for</code> loop on this with a list of every team number did the trick for finding accounts.</p>
|
||||
|
||||
<h3 id="fetching-language-info">Fetching language info</h3>
|
||||
<p>To remove the need for an Oauth login to use the script, GitHub data is retrieved using standard HTTPS requests instead of AJAX requests to the API. This gets around the tiny rate limit, but takes a bit longer to complete.</p>
|
||||
|
||||
<p>To check for language usage, a simple Regex pattern can be used: <code class="highlighter-rouge">/programmingLanguage"\&gt;(.*)\&lt;/gm</code></p>
|
||||
|
||||
<p>When combined with an <code class="highlighter-rouge">re.findall()</code>, this pattern will return a list of all recent languages used by a team.</p>
|
||||
|
||||
<h3 id="data-saves--backup-solution">Data saves / backup solution</h3>
|
||||
<p>To deal with the fact that large amounts of data are being requested, and people might want to pause the script, I have created a system to allow for “savestates”.</p>
|
||||
|
||||
<p>On launch of the script, it will check for a <code class="highlighter-rouge">./data.json</code> file. If this does not exist, one will be created. Otherwise, the contents will be read. This file contains both all the saved data, and some counters.</p>
|
||||
|
||||
<p>Each stage of the script contains a counter, and will increment the counter every time a team has been processed. This way, if the script is stopped and restarted, the parsers will just keep working from where they left off. This was very helpful when writing the script as, I needed to stop and start it every time I needed to implement a new feature.</p>
|
||||
|
||||
<p>All parsing data is saved to the json file every time the script completes, or it detects a <code class="highlighter-rouge">SIGKILL</code>.</p>
|
||||
|
||||
<h2 id="what-i-learned">What I learned</h2>
|
||||
<p>After letting the script run for about an hour, I got a bunch of data from every registered team.</p>
|
||||
|
||||
<p>This data includes every project (both on and offseason) from each team, so teams that build t-shirt cannons using the CTRE HERO, would have C# in their list of languages. Things like that.</p>
|
||||
|
||||
<p>Unsurprisingly, by far the most popular programming language is Java, with 3232 projects. These projects where all mostly, or entirely written in Java. Next up, we have C++ with 725 projects, and Python with 468 projects.</p>
|
||||
|
||||
<p>After Java, C++, and Python, we start running in to languages used for dashboards, design, lessons, and offseason projects. Before I get to everything else, here is the usage of the rest of the valid languages for FRC robots:</p>
|
||||
<ul>
|
||||
<li>C (128)</li>
|
||||
<li>LabView (153)</li>
|
||||
<li>Kotlin (96)</li>
|
||||
<li>Rust (4)</li>
|
||||
</ul>
|
||||
|
||||
<p>Now, the rest of the languages below Python:</p>
|
||||
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>295 occurrences of JavaScript
|
||||
153 occurrences of LabVIEW
|
||||
128 occurrences of C
|
||||
96 occurrences of Kotlin
|
||||
72 occurrences of Arduino
|
||||
71 occurrences of C#
|
||||
69 occurrences of CSS
|
||||
54 occurrences of PHP
|
||||
40 occurrences of Shell
|
||||
34 occurrences of Ruby
|
||||
16 occurrences of Swift
|
||||
16 occurrences of Jupyter Notebook
|
||||
15 occurrences of Scala
|
||||
12 occurrences of D
|
||||
12 occurrences of TypeScript
|
||||
9 occurrences of Dart
|
||||
8 occurrences of Processing
|
||||
7 occurrences of CoffeeScript
|
||||
6 occurrences of Go
|
||||
6 occurrences of Groovy
|
||||
6 occurrences of Objective-C
|
||||
4 occurrences of Rust
|
||||
3 occurrences of MATLAB
|
||||
3 occurrences of R
|
||||
1 occurrences of Visual Basic
|
||||
1 occurrences of Clojure
|
||||
1 occurrences of Cuda
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>I have removed markup and shell languages from that list because most of them are probably auto-generated.</p>
|
||||
|
||||
<p>In terms of github account names, 133 teams follow FRC convention and use a username starting with <code class="highlighter-rouge">frc</code>, followed by their team number, 95 teams use <code class="highlighter-rouge">team</code> then their number, and 264 teams use something else.</p>
|
||||
|
||||
<h2 id="using-the-script">Using the script</h2>
|
||||
<p>This script is not on PYPI this time. You can obtain a copy from my GitHub repo: <a href="https://github.com/Ewpratten/frc-code-stats">https://github.com/Ewpratten/frc-code-stats</a></p>
|
||||
|
||||
<p>First, make sure both <code class="highlighter-rouge">python3.7</code> and <code class="highlighter-rouge">python3-pip</code> are installed on your computer. Next, delete the <code class="highlighter-rouge">data.json</code> file. Then, install the requirements with <code class="highlighter-rouge">pip3 install -r requirements.txt</code>. Finally, run with <code class="highlighter-rouge">python3 main.py</code> to start the script. Now, go outside and enjoy nature for about an hour, and your data should be loaded!.</p></content><author><name></name></author><summary type="html">I was curious about the most used languages for FRC, so I build a Python script to find out what they where.</summary></entry></feed>
|
||||
<p>I just really enjoyed working with the project and sharing it with friends, so I figured I should mention it here too. Maybe it will inspire someone to make something cool!</p></content><author><name></name></author><summary type="html">One day, back in June of 2018, I was both looking for a new project to work on, and trying to decide which Linux distro to install on one of my computers. From this, a little project was born. Give Me a Distro (or, GMAD, as I like to call it) is a little website that chooses a random distribution of Linux and shows a description of what you are about to get yourself into, and a download link for the latest ISO.</summary></entry></feed>
|
@ -14,7 +14,7 @@
|
||||
<meta property="og:url" content="http://0.0.0.0:4000/fossl-feeds" />
|
||||
<meta property="og:site_name" content="Evan Pratten" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"FOSS/L RSS Feeds","@type":"WebPage","url":"http://0.0.0.0:4000/fossl-feeds","description":"RSS feeds from our blogs","@context":"https://schema.org"}</script>
|
||||
{"@type":"WebPage","url":"http://0.0.0.0:4000/fossl-feeds","description":"RSS feeds from our blogs","headline":"FOSS/L RSS Feeds","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -103,7 +103,7 @@ https://blog.mrtnrdl.de/feed.xml
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
<meta property="og:url" content="http://0.0.0.0:4000/" />
|
||||
<meta property="og:site_name" content="Evan Pratten" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"Evan Pratten","@type":"WebSite","url":"http://0.0.0.0:4000/","name":"Evan Pratten","description":"Computer wizard, student, @frc5024 programming team lead, and radio enthusiast.","@context":"https://schema.org"}</script>
|
||||
{"@type":"WebSite","url":"http://0.0.0.0:4000/","name":"Evan Pratten","description":"Computer wizard, student, @frc5024 programming team lead, and radio enthusiast.","headline":"Evan Pratten","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -116,7 +116,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
<meta property="og:url" content="http://0.0.0.0:4000/projects" />
|
||||
<meta property="og:site_name" content="Evan Pratten" />
|
||||
<script type="application/ld+json">
|
||||
{"headline":"My Projects","@type":"WebPage","url":"http://0.0.0.0:4000/projects","description":"Computer wizard, student, @frc5024 programming team lead, and radio enthusiast.","@context":"https://schema.org"}</script>
|
||||
{"@type":"WebPage","url":"http://0.0.0.0:4000/projects","description":"Computer wizard, student, @frc5024 programming team lead, and radio enthusiast.","headline":"My Projects","@context":"https://schema.org"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
|
||||
|
||||
@ -273,7 +273,7 @@
|
||||
<span class="site-info">
|
||||
Site design by: <a href="https://retrylife.ca">Evan Pratten</a> |
|
||||
|
||||
This site was last updated at: 2019-11-17 10:22:12 -0500
|
||||
This site was last updated at: 2019-11-20 10:04:40 -0500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -1 +1 @@
|
||||
{"/post/ef7b3166/":"http://0.0.0.0:4000/blog/2019/09/11/buildingimgfrombin","/ef7b3166/":"http://0.0.0.0:4000/blog/2019/09/11/buildingimgfrombin","/post/e9gb3490/":"http://0.0.0.0:4000/blog/2019/09/19/i-want-to-build-a-sat","/e9gb3490/":"http://0.0.0.0:4000/blog/2019/09/19/i-want-to-build-a-sat","/post/e9g9d6s90/":"http://0.0.0.0:4000/blog/2019/09/28/offseason1","/e9g9d6s90/":"http://0.0.0.0:4000/blog/2019/09/28/offseason1","/post/99g9j2r90/":"http://0.0.0.0:4000/blog/2019/10/05/billwurtz","/99g9j2r90/":"http://0.0.0.0:4000/blog/2019/10/05/billwurtz","/post/e9g9dfs90/":"http://0.0.0.0:4000/blog/2019/11/09/easyshell","/e9g9dfs90/":"http://0.0.0.0:4000/blog/2019/11/09/easyshell","/r/5kcomm":"https://imgur.com/a/77bnlZN","/r/locngn":"https://github.com/frc5024/uBase/blob/lib-upgrade/src/main/java/frc/lib5k/spatial/LocalizationEngine.java","/music":"https://retrylife.bandcamp.com/"}
|
||||
{"/post/ef7b3166/":"http://0.0.0.0:4000/blog/2019/09/11/buildingimgfrombin","/ef7b3166/":"http://0.0.0.0:4000/blog/2019/09/11/buildingimgfrombin","/post/e9gb3490/":"http://0.0.0.0:4000/blog/2019/09/19/i-want-to-build-a-sat","/e9gb3490/":"http://0.0.0.0:4000/blog/2019/09/19/i-want-to-build-a-sat","/post/e9g9d6s90/":"http://0.0.0.0:4000/blog/2019/09/28/offseason1","/e9g9d6s90/":"http://0.0.0.0:4000/blog/2019/09/28/offseason1","/post/99g9j2r90/":"http://0.0.0.0:4000/blog/2019/10/05/billwurtz","/99g9j2r90/":"http://0.0.0.0:4000/blog/2019/10/05/billwurtz","/post/e9g9dfs90/":"http://0.0.0.0:4000/blog/2019/11/09/easyshell","/e9g9dfs90/":"http://0.0.0.0:4000/blog/2019/11/09/easyshell","/post/e9gdhj90/":"http://0.0.0.0:4000/blog/2019/11/20/realtime-robot-code","/e9gdhj90/":"http://0.0.0.0:4000/blog/2019/11/20/realtime-robot-code","/r/5kcomm":"https://imgur.com/a/77bnlZN","/r/locngn":"https://github.com/frc5024/uBase/blob/lib-upgrade/src/main/java/frc/lib5k/spatial/LocalizationEngine.java","/music":"https://retrylife.bandcamp.com/"}
|
@ -1,25 +1,18 @@
|
||||
body{
|
||||
margin:0;
|
||||
padding:0;
|
||||
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
|
||||
font-family: 'IBM Plex Sans', sans-serif;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.foot{
|
||||
.foot {}
|
||||
|
||||
}
|
||||
|
||||
.site-info{
|
||||
color:rgb(199, 195, 195);
|
||||
.site-info {
|
||||
color: rgb(199, 195, 195);
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
.header-gap{
|
||||
.header-gap {
|
||||
/* height: 30px;
|
||||
background-color: #ebeef1; */
|
||||
}
|
||||
@ -31,8 +24,6 @@ body{
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.table-fix {
|
||||
@ -44,11 +35,9 @@ table {
|
||||
margin-bottom: 1rem;
|
||||
color: #212529;
|
||||
vertical-align: top;
|
||||
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
table th, table td {
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
@ -74,23 +63,23 @@ thead th {
|
||||
.header .container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center; /* align vertical */
|
||||
align-items: center;
|
||||
/* align vertical */
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
max-width:100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.centre{
|
||||
.centre {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6{
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
}
|
||||
|
||||
@ -105,7 +94,6 @@ a h5 {
|
||||
/* border-radius: 15px;
|
||||
transform: translateY(-30px); */
|
||||
background-color: #fff;
|
||||
|
||||
}
|
||||
|
||||
.home.container {
|
||||
@ -115,8 +103,8 @@ a h5 {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.container .profile{
|
||||
width:30vw;
|
||||
.container .profile {
|
||||
width: 30vw;
|
||||
max-width: 242px;
|
||||
transform: translateY(calc(10vh * -1));
|
||||
border-color: #fff;
|
||||
@ -130,25 +118,25 @@ a h5 {
|
||||
border-radius: 5%;
|
||||
}
|
||||
|
||||
.home-header{
|
||||
height:100%;
|
||||
.home-header {
|
||||
height: 100%;
|
||||
background-image: url('/assets/images/banner.jpg');
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.site-ctr{
|
||||
min-height:100vh;
|
||||
.site-ctr {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
margin:auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.container .info{
|
||||
.container .info {
|
||||
transform: translateY(calc(13vh * -1));
|
||||
padding: 20px;
|
||||
}
|
||||
@ -161,6 +149,7 @@ a h5 {
|
||||
/* #particles-js{
|
||||
position: absolute;
|
||||
} */
|
||||
|
||||
/*
|
||||
.reactive-bg{
|
||||
position:relative
|
||||
@ -192,6 +181,7 @@ blockquote {
|
||||
padding: 0.5em 10px;
|
||||
/* quotes: "\201C""\201D""\2018""\2019"; */
|
||||
}
|
||||
|
||||
blockquote:before {
|
||||
color: #ccc;
|
||||
/* content: open-quote; */
|
||||
@ -200,6 +190,35 @@ blockquote:before {
|
||||
margin-right: 0.25em;
|
||||
vertical-align: -0.4em;
|
||||
}
|
||||
|
||||
blockquote p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
kbd {
|
||||
margin: 0px 0.1em;
|
||||
padding: 0.1em 0.6em;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgb(204, 204, 204);
|
||||
color: rgb(51, 51, 51);
|
||||
line-height: 1.4;
|
||||
font-size: 10px;
|
||||
display: inline-block;
|
||||
box-shadow: 0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px #ffffff;
|
||||
background-color: rgb(247, 247, 247);
|
||||
-moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset;
|
||||
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: #faf9f9;
|
||||
border-radius:5px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding:3px;
|
||||
}
|
||||
}
|
BIN
assets/python/__pycache__/rtrbt.cpython-36.pyc
Normal file
BIN
assets/python/__pycache__/rtrbt.cpython-36.pyc
Normal file
Binary file not shown.
64
assets/python/rtrbt.py
Normal file
64
assets/python/rtrbt.py
Normal file
@ -0,0 +1,64 @@
|
||||
# RealTime FRC Robot control helper
|
||||
# By: Evan Pratten <ewpratten>
|
||||
|
||||
# Import norma robot stuff
|
||||
import wpilib
|
||||
import ctre
|
||||
|
||||
# Handle WPI trickery
|
||||
try:
|
||||
from unittest.mock import patch
|
||||
except ImportError:
|
||||
from mock import patch
|
||||
import sys
|
||||
from threading import Thread
|
||||
|
||||
|
||||
## Internal mathods ##
|
||||
_controllers = []
|
||||
_thread: Thread
|
||||
|
||||
|
||||
class _RTRobot(wpilib.TimedRobot):
|
||||
|
||||
def robotInit(self):
|
||||
|
||||
# Create motors
|
||||
_controllers.append(ctre.WPI_TalonSRX(1))
|
||||
_controllers.append(ctre.WPI_TalonSRX(2))
|
||||
|
||||
# Set safe modes
|
||||
_controllers[0].setSafetyEnabled(False)
|
||||
_controllers[1].setSafetyEnabled(False)
|
||||
|
||||
|
||||
|
||||
def _start():
|
||||
# Handle fake args
|
||||
args = ["run", "run"]
|
||||
with patch.object(sys, "argv", args):
|
||||
print(sys.argv)
|
||||
wpilib.run(_RTRobot)
|
||||
|
||||
## Utils ##
|
||||
|
||||
|
||||
def startRobot():
|
||||
""" Start the robot code """
|
||||
global _thread
|
||||
_thread = Thread(target=_start)
|
||||
_thread.start()
|
||||
|
||||
|
||||
def setMotor(id, speed):
|
||||
""" Set a motor speed """
|
||||
_controllers[id].set(speed)
|
||||
|
||||
def arcadeDrive(speed, rotation):
|
||||
""" Control the robot with arcade inputs """
|
||||
|
||||
l = speed + rotation
|
||||
r = speed - rotation
|
||||
|
||||
setMotor(0, l)
|
||||
setMotor(1, r)
|
Loading…
x
Reference in New Issue
Block a user