Compare commits

...

194 Commits

Author SHA1 Message Date
e910041c95 added possibility to load level file 2021-09-11 01:44:57 +02:00
65f195fecf implemented level generator 2021-09-11 01:44:03 +02:00
794c987899 implemented option to switch between randomly generated levels 2021-09-10 23:30:15 +02:00
e374198fef improved layout of website 2021-09-10 23:29:20 +02:00
0956f6d0fe show grayscale image by default 2021-09-10 22:04:06 +02:00
3d64e2e834 show current program in beamer mode + general improvements; stable version 2021-09-10 16:07:46 +02:00
e669b10ebc connected game to web server 2021-09-10 13:46:24 +02:00
ca8e5aac9b allow further distance from target position 2021-09-10 01:23:14 +02:00
6aef0660a3 empty response queue to avoid desync 2021-09-10 01:22:44 +02:00
885a516fe3 implemented won text and set consistent seed 2021-09-10 01:22:17 +02:00
ca1fe99ff2 stable version of the game 2021-09-10 01:04:17 +02:00
a69d45f97e make it possible to get positions outside of grid rect 2021-09-10 00:44:16 +02:00
eea1cee1ec use mpc controller with short horizon 2021-09-10 00:43:02 +02:00
1c8d4a1d57 send heartbeat while waiting for control to finish 2021-09-10 00:42:31 +02:00
029068b259 added some output 2021-09-09 21:36:38 +02:00
7962199c3a reset time when stopping controller 2021-09-09 21:36:13 +02:00
f3babdcf0a cleaner exit in case of errors 2021-09-09 21:34:54 +02:00
019c4590aa implemented communication interface with real robots 2021-09-07 22:35:02 +02:00
3082eebc8d implemented blocking events to make it possible to wait for the robot to reach the desired target position 2021-09-07 22:31:28 +02:00
2c54e56f95 also enable computation of position outside of marker rectangle and for arbitrary grid sizes 2021-09-07 22:29:27 +02:00
1db24bc573 changed some defaults 2021-09-07 00:24:05 +02:00
c65dcce176 added grid move to measurement server 2021-09-07 00:23:36 +02:00
2dc3eef2d6 adjusted key events to Qt and changed robot ips 2021-09-07 00:21:51 +02:00
bbb341ea56 improved stepping mode 2021-09-06 09:00:00 +02:00
4c3c3f973e implemented stepping mode 2021-09-06 00:34:26 +02:00
57cd60de8c use KEYUP instead of KEYDOWN because otherwise switching from beamer mode does not work 2021-09-06 00:22:58 +02:00
8c544f8fcc implemented quitting the program 2021-09-06 00:05:52 +02:00
ff6caa457f missing copy to restore initial game state 2021-09-05 23:42:08 +02:00
9eea9bd245 refactored game into Game class for better maintainability 2021-09-05 23:37:47 +02:00
945833c8ac implemented state machine and option to switch display mode for second screen 2021-09-05 20:43:22 +02:00
e931085ca8 implemented basic input 2021-09-03 22:41:35 +02:00
439cf44d70 implemented core game logic and display 2021-09-03 21:38:30 +02:00
c5f3f3babb implemented control of multiple robots through GUI using measurement server 2021-08-31 00:05:16 +02:00
d0c17c0b91 got mpc controller running again 2021-08-25 22:53:09 +02:00
b65b6568d0 run GUI in main thread and measurement server in secondary thread 2021-08-25 21:59:24 +02:00
2607e94958 enable auto-exposure by default, some cleanup 2021-08-25 20:39:39 +02:00
f9f4a2c1c6 added qt gui with changeable parameters 2020-11-28 19:57:22 +01:00
9c1115d505 use qt gui instead of opencv 2020-11-25 21:19:56 +01:00
f26f958dc7 fixed errors in pid and mpc controller 2020-11-22 15:57:53 +01:00
0fddd75393 make parameters of mpc controller changeable 2020-11-18 21:54:23 +01:00
c2307261c5 second robot controlled by mpc controller 2020-11-18 21:53:32 +01:00
2f081faee8 restructured controller to work with mpc 2020-11-18 21:38:41 +01:00
17637f427b initial commit for control commander 2020-11-17 21:24:55 +01:00
edc3e8d290 some cleanup 2020-11-17 21:23:22 +01:00
35b6c3bdfd rewrote pid controller to derive from ControllerBase. works with multiple robots now 2020-11-14 16:41:16 +01:00
2060d8eb15 fixed problem with slow event server 2020-11-14 16:40:33 +01:00
708284749d fps counter 2020-11-14 16:39:53 +01:00
a3af40b001 implemented control from multiple robots 2020-11-14 16:06:57 +01:00
47f652fd62 about to make changes s.t. PID controller derives from ControllerBase class 2020-11-14 15:22:41 +01:00
6b9373cce5 enabled better mouse interaction 2020-11-14 15:01:01 +01:00
eff5474ac2 switched to using TCP sockets for measurement and event communication 2020-11-11 21:33:48 +01:00
8fc5ad9868 measurment client and server example 2020-11-09 22:32:27 +01:00
a872232880 enabled backwards driving and adjusted parameters a bit 2020-11-09 22:07:09 +01:00
91cde26908 working PID controller for driving forwards 2020-11-09 21:34:43 +01:00
993d4c0141 added options to run detection on inverted markers and to disable auto-exposure 2020-11-08 16:29:48 +01:00
119d857531 general refactoring 2020-10-24 21:16:17 +02:00
8728db82e8 general refactoring 2020-10-24 21:12:03 +02:00
a0f701fe83 general refactoring 2020-10-24 21:11:22 +02:00
7ded3bee79 general refactoring 2020-10-24 21:08:20 +02:00
dd162018d8 improved grid drawing code to also work when markers are occluded and added documentation 2020-10-24 20:54:47 +02:00
a22a3fdea3 adjusted to rename of aruco estimator and changed order for server commands 2020-10-24 20:11:32 +02:00
ab6cc79eea added kick-start for small motor control signals 2020-10-24 20:05:39 +02:00
7f194f57f2 added python3 support 2020-10-24 20:05:00 +02:00
409a9980bd added exception for lost connection 2020-10-24 20:01:16 +02:00
9e1ce9b512 renamed aruco estimator file 2020-10-24 19:59:56 +02:00
3bd11a53ce client and server for robot position estimate 2020-10-24 19:56:18 +02:00
02a83f405f added option to draw marker coordinate system and modified dict for corner marker estimates 2020-10-24 19:55:04 +02:00
ec6b2bbf4a removed unnecessary inheritance 2020-10-23 20:56:41 +02:00
9bb4f2920f rename ArucoEstimator file 2020-10-23 20:55:08 +02:00
0acc59cc53 replaced cv2.aruco with python bindings for new aruco version 2020-10-23 20:44:20 +02:00
280fb10427 replaced cv2.aruco with python bindings for new aruco version 2020-10-23 20:43:45 +02:00
0bc6b68a20 playing around with different markers and sizes 2020-10-21 21:10:04 +02:00
e93ae65e0f added option to dynamically initialize grid and robots and added support for arbitrary grid orientation w.r.t. camera 2020-10-18 18:03:33 +02:00
056d91da52 fixed error with turn around command 2020-10-18 16:58:32 +02:00
3ab8fadc7b robot driving to position determined by mouse click 2020-10-18 15:16:28 +02:00
49645765d7 input handling using opencv window 2020-10-15 22:41:30 +02:00
e3753f7644 improved grid logic for robots + general cleanup 2020-10-15 20:35:32 +02:00
32892e5dcf added option to compute angle for marker based grid 2020-10-14 23:08:19 +02:00
149e9b3536 coupled robot control with new position estimator 2020-10-14 21:15:43 +02:00
36f3ccbd6c fixed a problem where robot could drive diagonally caused by int casting error and improved the socket buffer size which could lead to problems for longer messages 2020-09-15 15:23:01 +02:00
e7ab92dfe2 added more commands and improved error checking 2020-09-09 20:21:34 +02:00
c98760a2c7 working version with two robots 2020-09-09 18:12:12 +02:00
280aee75ee parameter tuning. works well with control scaling of 0.3 2020-09-09 17:33:13 +02:00
826fa4be0f fixed problem with target angle of -pi, pi which was caused by discontinuities in the angle measurements 2020-09-07 16:18:35 +02:00
6fb3cd5484 so-so working version for mpc control 2020-09-04 16:53:39 +02:00
d170524370 added 8th waypoint 2020-01-16 10:58:33 +01:00
0c59a77b28 changed marker ids 2020-01-16 07:51:54 +01:00
c1b7640d03 fixed bug with initialization of i2c option 2020-01-12 16:11:58 +01:00
aab1b78e8f added accessability features (choosing ip, robot id at startup, changing max speed), remove obstacle code 2020-01-12 16:11:26 +01:00
c2fad13698 added scripts 2019-09-12 13:06:22 +02:00
86b9ad659d removed unnecessary code and fixed formatting 2019-08-14 14:49:11 +02:00
cd2fc8cc3b cleaned up game code: read track from config file, added option to have a variable number of markers, removed hardcoded marker ids, added debug output for track setup 2019-08-14 13:46:01 +02:00
522bd87c9c removed comments 2019-08-13 14:11:03 +02:00
4cc44b3cd6 connect to Micropython network if no other network is found 2019-08-13 13:20:57 +02:00
4947bd4539 added WebREPL files 2019-08-13 13:19:11 +02:00
5d6a5ad7bf started documentation for race game 2019-08-12 17:36:50 +02:00
9d9d539962 fixed wrong import 2019-08-12 17:36:26 +02:00
cc98f96562 minor fix in docu 2019-08-12 16:50:19 +02:00
4830c2e252 removed fiducial transformation script duplicate 2019-08-12 16:48:59 +02:00
bt304019
2987da4373 improved keyboard controller script. added quit option and option to pass ip as command line argument 2019-08-12 16:42:57 +02:00
8484264a65 removed nodemcu firmware (no longer used) 2019-08-12 16:01:35 +02:00
b968dbe03c removed roboflut (moved to seperate repository) 2019-08-12 16:00:27 +02:00
55b468d314 added quit event 2019-08-12 15:52:38 +02:00
fa863d4e77 minor formatting fixes 2019-08-12 12:31:36 +02:00
e15ac512f2 added link to next instructions 2019-08-12 12:30:12 +02:00
31804b4f34 added general notes 2019-08-12 12:24:07 +02:00
5e5ee609a2 added paper for description of dynamical system 2019-08-12 12:23:48 +02:00
d57a125ec0 fixed description of expected data in comment 2019-08-12 12:22:51 +02:00
b7242359dc deleted old docu folder 2019-08-12 12:21:59 +02:00
5984a05c2a addef fritzing sketches 2019-08-12 12:17:49 +02:00
236e738ef6 added folder for other documents 2019-08-12 12:16:16 +02:00
8b4e80d15e deleted old robot setup instructions file 2019-08-12 12:13:59 +02:00
c31c674ab2 some minor fixes 2019-08-12 12:12:23 +02:00
db615af775 small fixes 2019-08-12 12:10:52 +02:00
609a890572 fixed wrong import 2019-08-12 12:10:30 +02:00
7efb0486b7 Finished setup instructions for ArUco based position detection 2019-08-12 12:04:19 +02:00
fc843c18a2 added illustrative images for image_view and fiducial detection 2019-08-12 11:54:53 +02:00
49561a656a added images of fiducial markers 2019-08-12 11:14:55 +02:00
761c6f8bf1 added checkerboard pdf 2019-08-12 10:18:22 +02:00
afdd3dbc15 added parameter options to transformation script 2019-08-08 19:13:08 +02:00
3b364fd9b3 added ROS package for fiducial position transformation 2019-08-08 18:55:19 +02:00
d9ffcc544b changed title 2019-07-31 16:24:59 +02:00
70afbf09ee added demo video 2019-07-31 16:15:28 +02:00
f134c302f0 updated readme 2019-07-31 16:13:48 +02:00
2a095959a9 fixed some formatting problems 2019-07-31 15:39:57 +02:00
1827b740e0 polishing for assembly instructions 2019-07-31 15:30:25 +02:00
b9ac7f4caf added image of finished robot 2019-07-31 15:10:17 +02:00
839f5c85c3 finished instructions for robot assembly and setup. now for some polishing 2019-07-31 15:04:46 +02:00
9bb8bb805d modified fritzing sketches 2019-07-31 15:02:39 +02:00
dbcc87c9bc resized charging image 2019-07-31 15:01:29 +02:00
b6c1139f07 added images for velcro tape 2019-07-31 14:58:27 +02:00
e05bcdccde added image of isolation tape 2019-07-31 14:50:22 +02:00
97cfd92850 added more pictures for assembly instructions 2019-07-31 14:01:07 +02:00
50b3779de3 fixed a few small errors 2019-07-30 22:32:36 +02:00
a6af1aa5a6 fixed formatting in assembly instructions 2019-07-30 22:11:06 +02:00
ec09447740 cropped images 2019-07-30 22:09:52 +02:00
b1d66c1e7c updated docu for motor shield fix 2019-07-30 21:53:52 +02:00
54e22a2886 edited image for motor shield jumper 2019-07-30 21:43:44 +02:00
d0b9ab4340 continued work on documentation 2019-07-30 17:00:26 +02:00
27088a3f77 added webrepl illustration images 2019-07-30 16:54:08 +02:00
db87c88b3d added motor connection sketch 2019-07-30 16:05:53 +02:00
3cfc256a0b added robot setup instructions 2019-07-30 15:08:52 +02:00
da2f2458f2 worked on documentation of assembly 2019-07-30 14:59:48 +02:00
2254326461 started working on assembly instructions 2019-07-29 17:42:17 +02:00
ec140b76f0 added image for d1 mini 2019-07-29 16:45:37 +02:00
10be4c707f added some images for assembly instructions 2019-07-29 16:42:40 +02:00
11148fe514 added notes on how to install realsense camera 2019-07-24 09:25:49 +02:00
aed4cfbd52 initial commit for roboflut with a single robot 2019-07-15 23:04:23 +02:00
034d88394c timer for game 2019-07-12 18:09:42 +02:00
a630150a24 added more waypoints. final version for TdM 2019 2019-07-12 17:45:31 +02:00
e219f7de38 updated race game program by Dmitriy 2019-07-09 19:48:39 +02:00
15da18f949 changed marker ids 2019-07-09 19:42:19 +02:00
d985b830b9 version of the problem that works quite nicely with L293D robot and multistep MPC2 2019-07-04 14:39:34 +02:00
c6582c9e6c added plots for x and y position 2019-07-04 13:48:32 +02:00
cfa669e498 fixed a problem with undefined variables 2019-07-04 10:55:40 +02:00
24cf87e9dd added plot for race track 2019-07-04 10:54:50 +02:00
9c222b1099 fixed problems with simultaneous inputs not being handled and gamepads not detected 2019-06-28 10:28:51 +02:00
67405fbd3f measure postprocessing time 2019-06-27 16:30:24 +02:00
9dfc06169f cleaned up code, removed old controllers 2019-06-27 14:43:42 +02:00
ac0ad6c45a added joystick controller written by Dmitriy 2019-06-27 14:11:52 +02:00
2a5e0e8ae7 added condition if marker cannot be found 2019-06-27 14:11:19 +02:00
b54c2d565c implemented obstacle avoidance 2019-06-27 12:12:42 +02:00
b755173c6b moved code for L293D motor driver to seperate file 2019-06-26 13:02:37 +02:00
7d69c91752 working mpc with L293D motor driver 2019-06-26 11:36:54 +02:00
2711373d44 testing not sending time in mpc 2019-06-26 10:49:52 +02:00
0835690659 changed ips and max speed for testing 2019-06-26 10:49:05 +02:00
85e2019370 simplified structure of the program and added motor class for L293D driver 2019-06-26 10:48:23 +02:00
843b30f5d3 added option to set target angle 2019-06-14 10:38:21 +02:00
465309ee45 added test for max speed and prediction step in MPC 2019-06-14 10:37:46 +02:00
fb12a3c94b implemented queue for controls 2019-06-14 10:03:02 +02:00
05d80fa6ed test with polling for new data (not working well) 2019-06-14 08:45:00 +02:00
c31bb9cb11 testing multistep mpc 2019-06-13 13:46:29 +02:00
8548348edd added warmstart 2019-06-13 13:45:45 +02:00
b8927cf1c5 working version of MPC controller 2019-06-13 13:18:46 +02:00
f100f21162 started working on optimization based controller 2019-06-11 17:40:39 +02:00
0d14738671 changed forward, backward and turning directions in keyboard control script such that it fits the model and the camera image 2019-06-11 16:28:01 +02:00
480e1f523c rewrote position controller script. now animated plot works for keyboard control and pid control 2019-06-11 16:26:53 +02:00
9595a68494 added custom messages for pos and angle 2019-06-11 09:56:25 +02:00
19ec0283bf added notes on add custom messages and some initial notes for the setup (required ros packages) 2019-06-11 09:55:05 +02:00
1bedc7f01d added note on how to resolve problem with buggy motor shield 2019-06-11 09:53:13 +02:00
5715fc6349 added webrepl for third robot 2019-06-11 09:51:36 +02:00
441e55000a added option to sest maximum speed 2019-06-10 23:16:56 +02:00
79f4fcc032 added error message if no i2c devices are found 2019-06-10 23:16:14 +02:00
2fb69de9c5 added ip of 3. robot 2019-06-10 23:15:36 +02:00
685dd6a4c9 Webrepl with preset ip address for the robot 2019-06-07 14:22:19 +02:00
1d8d2d8043 added ros node for transformation of translation and quaternion rotation to position and angle in 2d plane 2019-06-07 14:20:05 +02:00
5bc1c9ca9f fixed another reconnection issue that only seems to occur with windows 2019-06-06 15:19:14 +02:00
4bff8e1df5 fixed a bug in keyboard remote controller 2019-06-05 08:06:11 +02:00
6ebb9338c0 removed remote control script from micropython folder 2019-06-05 08:05:34 +02:00
69c40a3fe2 fixed issue with reconnection problem after socket is closed 2019-06-05 08:04:26 +02:00
4d7c0e7be7 robot now tries to automatically connect to an existing wifi network. if the network is not found then the robot will become an access point instead 2019-06-05 08:03:31 +02:00
530cef54b3 added simulation of robot 2019-05-24 09:21:54 -05:00
f3de1b173a example for optimal control of robot 2019-05-24 09:20:49 -05:00
06bcf565f3 added position detection with opencv 2019-05-06 22:23:31 +02:00
148 changed files with 7639 additions and 321 deletions

View File

@ -1,9 +1,19 @@
# README # # RoboRally
![Finished robot](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/finished_robot_2.jpeg)
This project aims to provide a low-cost framework for automatic control experiments with small two-wheeled robots.
It consists of a list of components, step-by-step assembly instructions and the necessary software for getting started with robot control.
Here's a demonstration video of what is possible with these robots:
[![Robot MPC demo](http://img.youtube.com/vi/u44QiJX27_s/0.jpg)](http://www.youtube.com/watch?v=u44QiJX27_s)
The project aims to be as affordable as possible. The total cost for a single robot is about 30 € or 35 $. This means you can easily build several of them e.g. for doing swarm robotics or cooperative robot experiments.
### What is this repository for? ### ### Getting started ###
For assembly instructions for the robots see the files int the docs/ subfolder of the repository.
The first one is [1_ASSEMBLY.md](https://imaginaerraum.de/git/Telos4/RoboRally/src/branch/master/docs/1_ASSEMBLY.md) which explains what components to get and how to assemble them into a robot.
This is a project thats aims to create a Robo Rally clone with real robots.
Nothing here yet..

90
docs/1_ASSEMBLY.md Normal file
View File

@ -0,0 +1,90 @@
# Robot assembly
This file contains assembly instructions for building the robots and soldering the electronics.
Here is how the finished robot will look like:
![finished robot](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/finished_robot_2.jpeg)
## Components
The robot consists of the following components:
* [Adafruit Mini 3-Layer Round Robot Chassis Kit](https://www.adafruit.com/product/3244)
* [Wemos D1 mini](https://aliexpi.com/41SY)
* [Wemos D1 mini battery shield](https://aliexpi.com/sNwB)
* [Wemos D1 mini motor shield (v1.0.0)](https://aliexpi.com/wngF)
* LiPo battery
In addition you will need the following tools:
* A soldering iron
* Solder wire
* Heat shrink tube
* Some wires
* Battery connectors (fitting your battery)
* Velcro tape
* Self adhesive tape
* Isolation tape
* Pin headers
For the camera based position detection of the robots you will need:
* A camera
* A printer
* Paper
* Cardboard
## Assembly instructions
### Robot chassis
Assemble the chassis according to the instructions provided by adafruit ([see here](https://learn.adafruit.com/tri-layer-mini-round-robot-chassis-kit)).
### Soldering the electronics
####Preparing Wemos D1 mini and shields
| Description | Image|
|--------------|---------------|
| Solder on two rows of 8 male pin headers to the D1 mini | ![D1 mini](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/d1_mini_pins.jpg) |
| Solder on two rows of 8 female pin headers to the motor shield . | ![Motor shield](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_shield_1.JPG) ![Motor shield](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_shield_2.JPG)|
| Also solder a single row of bent pins to the motor shield. Make sure they are soldered on the right way around.| ![Motor shield](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_shield_3.JPG)|
| Solder a jumper to the motor shield to connect the RTS and 3V pin. This will become important later on. Also solder the I2C bridge. | ![Motor shield](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_shield_jumper.jpeg)|
| Solder on two rows of 8 female pin headers with long pins to the battery shield | ![Battery shield](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/battery_shield_1.JPG) ![Battery shield](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/battery_shield_2.JPG) |
| Cut off the tips of the long pins such that the battery shield nicely fits into the motor shield | ![Mind the gap](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/battery_shield_3.JPG) ![Cut off tips](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/battery_shield_4.JPG) ![Nice fit](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/battery_shield_5.JPG) |
#### Connecting battery shield and motor shield for motor power supply
In the next step we connect the battery connector pins of the battery shield to the + and - pins of the motor shield. In addition we will add a connector to the motor shield where we can plug in the battery. Here is a schematic:
![Motor shield and battery shield sketch](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_bb.jpg)
*Note: I added a new connector instead of using the one already on the battery shield because the plug of my battery wouldn't fit in the on on the shield*
- First, solder two wires to the + and - pins of the battery shield and isolate them with heat shrink:
| Description | Image|
|--------------|---------------|
|Cut two small pieces of wire (about 4 cm), remove the isolation on both ends and cover them with a bit of solder | ![Battery Shield Wires](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_batter_stack_2.jpeg)|
|Solder the first wire to the + pin of the battery shield | ![First wire soldered](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_3.jpeg) |
| Solder the second wire to the - pin | ![Second wire soldered](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_4.jpeg) |
| Cover both wires with small pieces of heat shrink tube | ![Heat shrink](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_5.jpeg) |
| Heat the shrink tube with a lighter | ![I see fire](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_6.jpeg) |
| This is how the battery shield should look like with the two wires | ![Two wires with heat shrink](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_6.5.jpeg) |
- Next, we create a connector for the battery. In my case the battery had a male JST 2-Pin connector, so I soldered a female JST 2-Pin connector for plugin it in:
| Description | Image|
|--------------|---------------|
| Cut two small pieces of wire (about 3 cm), remove the isolation on both ends and cover them with a bit of solder | ![DIY JST connector](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_7.jpeg) |
| Solder the first wire to the JST connector |![First wire soldered](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_8.jpeg) |
| Solder the second wire to the JST connector | ![Second wire soldered](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_9.jpeg) |
| Cover both wires with heat shrink | ![Heat shrink cover](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_10.jpeg) |
| Solder the wires to the motor shield, paying attention to the polarity *(make sure it matches the polarity of your batteries!)* | ![Battery connector to motor shield](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_11.jpeg) |
- Now, stack the battery shield on top of the motor shield and solder the battery connector:
| Description | Image|
|---|---|
| Connect the + pin of the battery shield to the + pin of the motor shield. Be sure to isolate the connection with heat shrink. Then repeat the same for the - pins. | ![Motor shield and battery shield connection](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_15.JPG)|
| This is how it should look like in the end. | ![Side view](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_12.jpeg) ![Top view](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_14.jpeg) |
### Next steps
This concludes the main steps for the robot assembly. The next step is to fix a problem with the firmware on the motor shield in order to use it via I2C. Head over to [2_MOTOR_SHIELD_FIX.md](https://imaginaerraum.de/git/Telos4/RoboRally/src/branch/master/docs/2_MOTOR_SHIELD_FIX.md) for the instructions.

161
docs/2_MOTOR_SHIELD_FIX.md Normal file
View File

@ -0,0 +1,161 @@
# Motor shield fix
The Wemos motor shield comes with a buggy firmware which has problems with the I2C communication ([see here]([buggy firmware](https://hackaday.io/project/18439-motor-shield-reprogramming))).
Luckily, some guys figured out how to update the firmware of the STM32 on the motor shield with a custom one that fixes these issues.
These are the necessary steps:
- Install arm compiler:
```
$ sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa
$ sudo apt-get update
$ sudo apt-get install gcc-arm-embedded
```
- Build the firmware:
Get the firmware from: [https://github.com/pbugalski/wemos_motor_shield](https://github.com/pbugalski/wemos_motor_shield)
```
$ make
```
-> This creates a file called motor_shield.bin
### Upload empty firmware to Wemos D1 mini
We will program the STM32 on the motor shield using the serial USB connection from the Wemos D1 mini. The D1 mini should
just pass through the serial communication to the STM32. For this we need to upload a dummy firmware to the D1 mini.
- Install esptool
The chip on the Wemos D1 mini is a Esp8266. In order to flash programs on this chip we will use esptool by Espressif.
You can install it using pip:
```
$ pip install esptool
```
- Make sure the RTS and 3V port of the Wemos motor shield are connected by a wire. Also make sure that the I2C bridge connection on the motor shield is set.
![Motor shield jumper](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_shield_jumper.jpeg)
- Plug the Wemos D1 mini on top of the motor shield and then connect it to your PC using a micro USB cable
- Compile the sketch below into a .bin file using the Arduino IDE -> `dummy_sketch.ino.d1_mini.bin` or use the one from `bin/dummy_sketch.ino.d1_min.bin`
Note: If you compile it yourself make sure you choose the Wemos D1 mini board as target for the build.
```
void setup() {
// just some dummy code
}
void loop() {
// just some dummy code
}
```
- Erase flash on the chip:
```
$ esptool.py --port /dev/ttyUSB0 erase_flash
```
- Flash the dummy sketch:
```
$ esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect 0 dummy_sketch.ino.d1_mini.bin/dummy_sketch.ino.d1_mini.bin.ino.d1_mini.bin
```
### Install stm32flash utility:
To upload a new firmware to the STM32 that is running on the motor shield we need the STM32 flash utility.
You can download from: [https://sourceforge.net/projects/stm32flash/files/](https://sourceforge.net/projects/stm32flash/files/)
You should then build and install it using the usual:
```
$ make
$ make install
```
### Connect the motor shield to D1 mini:
Use some jumper cables to connect the motor shield and the D1 mini in the following way.
|Wemos D1 mini | Motor shield|
|--------------|---------------|
|TX | D1|
|RX | D2|
|3V | 3V|
|GND | GND |
![Firmware fix connections sketch](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_firmware_update_bb.jpg)
![Firmware fix connections image](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_shield_fix_4.JPG)
### Flash the new firmware on the STM32:
- Connect the D1 mini to your PC using a micro USB cable.
![Micro USB connection](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_shield_fix_1.JPG)
- Check if you can communicate with the STM32:
```
$ stm32flash /dev/ttyUSB0
```
Output should be:
```
Interface serial_posix: 57600 8E1
Version : 0x31
Option 1 : 0x00
Option 2 : 0x00
Device ID : 0x0444 (STM32F03xx4/6)
- RAM : 4KiB (2048b reserved by bootloader)
- Flash : 32KiB (size first sector: 4x1024)
- Option RAM : 16b
- System RAM : 3KiB
```
- Unlock the shield for flashing:
```
$ stm32flash -k /dev/ttyUSB0
```
Output should be:
```
stm32flash 0.5
http://stm32flash.sourceforge.net/
Interface serial_posix: 57600 8E1
Version : 0x31
Option 1 : 0x00
Option 2 : 0x00
Device ID : 0x0444 (STM32F03xx4/6)
- RAM : 4KiB (2048b reserved by bootloader)
- Flash : 32KiB (size first sector: 4x1024)
- Option RAM : 16b
- System RAM : 3KiB
Read-UnProtecting flash
Done.
```
- Flash new motor driver:
```
$ stm32flash -f -v -w motor_shield.bin /dev/ttyUSB0
```
Output should be:
```
stm32flash 0.5
http://stm32flash.sourceforge.net/
Using Parser : Raw BINARY
Interface serial_posix: 57600 8E1
Version : 0x31
Option 1 : 0x00
Option 2 : 0x00
Device ID : 0x0444 (STM32F03xx4/6)
- RAM : 4KiB (2048b reserved by bootloader)
- Flash : 32KiB (size first sector: 4x1024)
- Option RAM : 16b
- System RAM : 3KiB
Write to memory
Erasing memory
Wrote and verified address 0x08000be8 (100.00%) Done.
```
#### Cleaning up
Finally, you can remove the wire between RTS and 3V.
At this point, please make sure that the I2C bridge on the Wemos Motor shield is connected. This is necessary for I2C to work correctly.
### Next steps
Continue with the instructions in the file [3_ROBOT_SETUP.md](https://imaginaerraum.de/git/Telos4/RoboRally/src/branch/master/docs/3_ROBOT_SETUP.md) to complete the setup of the robot.

140
docs/3_ROBOT_SETUP.md Normal file
View File

@ -0,0 +1,140 @@
# Robot setup
This file explains how to setup the software for the robots. It assumes that you already assembled your robot according to the instructions in [1_ASSEMBLY.md](https://imaginaerraum.de/git/Telos4/RoboRally/src/branch/master/docs/1_ASSEMBLY.md) and that you updated the firmware of the motor shield as described in [2_MOTOR_SHIELD_FIX.md](https://imaginaerraum.de/git/Telos4/RoboRally/src/branch/master/docs/2_MOTOR_SHIELD_FIX.md).
Before we start, stack the D1 mini ontop of the battery shield and connect it to your computer using a micro USB cable:
![Electronics assembly done](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/motor_battery_stack_13.jpeg)
![Micro USB connection](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/usb_connection.jpeg)
### Flash Micropython to the Wemos D1 mini
The first step is to flash the micropython firmware to the D1 mini.
You can download the latest micropython firmware for the ESP8266 from [here](http://micropython.org/download#esp8266). I used version _esp8266-20190125-v1.10.bin_ but you can also use a more recent version.
The firmware can be flashed on the D1 mini using the following commands:
```
$ esptool.py --port /dev/ttyUSB0 erase_flash
$ esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect 0 esp8266-20190125-v1.10.bin
```
### Communicating with the D1 mini
After flashing the firmware we can access the python prompt on the microcontroller using a serial communication program such as picocom.
On Ubuntu you can install this with
```
$ sudo apt-get install picocom
```
Connect using picocom:
```
$ picocom /dev/ttyUSB0 -b115200
```
First we enable WebREPL on the microcontroller which allows us to easily upload files and debug the robots via WiFi.
*(I will use `>>>` in order to indicate commands which should be issued in the Micropython prompt)*
```
>>> import webrepl_setup
```
-> choose enable, set a password and choose reboot
Now we set up an access point on the D1 mini:
```
>>> import network
>>> ap_if = network.WLAN(network.AP_IF)
>>> ap_if.active(True)
>>> ap_if.ifconfig() # this prints the IP
```
The default password for this access point is: `micropythoN`
Alternatively, you can also connect to an existing wifi network using:
```
>>> import network
>>> sta_if = network.WLAN(network.STA_IF)
>>> sta_if.active(True)
>>> sta_if.connect("<SSID>", "<PW>")
>>> sta_if.ifconfig() # this prints the IP
```
Join the same WiFi network with your PC (either the access point or the local network) and connect to the D1 mini via the [online WebREPL](http://micropython.org/webrepl):
Enter IP from above and click connect. Login with the password you set for the WebREPL.
![WebREPL connection](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/webrepl0.jpg)
![WebREPL login](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/webrepl1.jpg)
![WebREPL prompt](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/webrepl2.jpg)
### Making settings permanent
In order to avoid having to set up the WebREPL and the WiFi everytime we reboot the robot we can upload a file called boot.py which runs each time the robot restarts:
- Find the file `boot.py` in the `micropython_firmware/` subfolder and edit the lines indicated with `# TODO`. In particular, set the wifi network, password and IP addresses. You should choose a unique IP address for each robot
```
# TODO: edit these lines
network_name = 'Your WiFi network' # existing wifi network to connect to (leave empty if unused)
password = 'Your password' # password for the network
desired_ip = '192.168.1.101' # the robot will be reachable by this IP in the network
subnet = '255.255.255.0'
gateway = '192.168.1.1'
dns = '192.168.1.1'
# TODO end edit
```
- Upload boot.py via WebREPL
![WebREPL file upload](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/webrepl3.jpg)
- Now the robot will automatically connect to WiFi network you set and start a WebREPL server after reboot.
*Note: If the WiFi network cannot be found the microcontroller will instead open an access point.*
### Upload robot firmware
Next, upload the motor controller script to the microcontroller. At the moment the firmware consists of the following three files located in `micropython_firmware/`:
- `main.py` Main script handling network communication and control
- `d1motor.py` Interface for the Wemos D1 mini motor shield
- `l293dmotor.py`Interface for alternative motor driver using L293D (no longer used)
You can upload these files the same way you did before with the WebREPL. After uploading all the files reboot the microcontroller by pressing the reset button or by pressing `Ctrl+D` in the WebREPL prompt.
### Test the robot
Now it's time to test if everything is working fine.
- Disconnect the USB cable from the microcontroller and connect the battery to the battery connector:
![Battery connection](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/battery_connection.jpeg)
- Next, connect the two motors of the robot to the motor shield according to the following sketch:
![Connect motors to motor shield](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/robot_bb.jpg)
![Motor and battery connection overview](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/battery_connection_overview.jpeg)
- Test if you can reach the robot using
```
$ ping 192.168.1.101
```
*Note: Make sure you use the correct IP address for your robot.*
- If everything worked, connect to the robot via WebREPL as before. Don't worry if the prompt does not appear to be working. The robot is running a program waiting for incoming connections for remote control.
We can connect to the robot via a socket on port `1234`. There is a simple demo program which illustrates how to control the robot remotely via keyboard. The program uses `pygame` for input handling so make sure to install it using
```
$ sudo pip install pygame
```
The program is located in the `remote_control/`. Run it using
```
$ python keyboard_controller.py 192.168.1.101
```
When running the program you should see an output in the WebREPL that a connection was established.
You can use the arrow keys for sending commands to the microcontroller which it then passes on to the motors.
Now you're all set up for controlling your robot remotely via wifi.
You can control the robot by sending commands to the robot as a string containing `'(u1, u2)\n'`, where `u1` and `u2` are floats in the range `[-1.0, 1.0]`, e.g. `'(0.3, -0.6)\n'`.
Have a look at the `keyboard_controller.py` script to see how this works in python.
### Isolating the electronics
Since the mounting plates of the robot are made of metal it is a good idea to put some isolation around the electronics. You can use some isolation tape to cover the exposed pins:
![Isolation tape](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/isolation_tape_2.JPG)
You can also add some velcro tape to the electronics, the battery and the robot to easily attach and remove the microcontroller from the robot.
![Velcro tape electronics and battery](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/velcro_tape_2.jpeg)
![Velcro tape robot](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/velcro_tape_3.jpeg)
### Charging the battery
In order to charge the battery of the robot just plug a micro USB cable into the USB port of the battery shield.
![Battery charging](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/charging.jpeg)
### Next steps
The next step is to setup the position detection using ROS. For this see the [4_ROS_SETUP.md](https://imaginaerraum.de/git/Telos4/RoboRally/src/branch/master/docs/4_ROS_SETUP.md)

172
docs/4_ROS_SETUP.md Normal file
View File

@ -0,0 +1,172 @@
# ROS setup
This file explains how to install and set up ROS (Robot Operating System) such that it can be used to detect the position of the robot via camera.
## Installing Software
This installation process has been tested with Ubuntu 16.04 LTS.
### ROS installation
The first step is to install ROS on your computer. I used the version ROS Kinetic, but other versions of ROS should also work, as long as the packages we use are available or you compile them yourself.
Follow the instructions [here](http://wiki.ros.org/kinetic/Installation) and choose the version `ros-kinetic-desktop-full`.
### Installing required ROS packages
We use the following packages for image detection:
- ros-kinetic-aruco-detect
- ros-kinetic-fiducial-slam
- ros-kinetic-cv-camera
Install each of them using
```
$ sudo apt-get install <package-name>
```
## Setup
For control experiments we need to know the position of the robots on the ground and its orientation. To achieve this we place a camera on the ceiling and use image detection relying on ArUco markers.
### Camera setup
For the image detection to work correctly we need to calibrate the camera first.
#### Testing the camera
First, plug in your camera and test if it works with the `cv_camera` package:
- Start ROS:
```
$ roscore
```
- Set the device id for your camera. Typically this will be `0` or `1`
```
$ rosparam set cv_camera/device_id 0
```
- Run the camera node
```
$ rosrun cv_camera cv_camera_node
```
- Test if you can see the live image from the camera using the command
```
$ rosrun image_view image_view image:=/cv_camera/image_raw
```
#### Calibrating the camera
Next, in order to calibrate the camera follow the instructions at [http://wiki.ros.org/camera_calibration/Tutorials/MonocularCalibration](http://wiki.ros.org/camera_calibration/Tutorials/MonocularCalibration).
These instructions mainly consist of the following steps:
- Print a [checkerboard image](https://imaginaerraum.de/git/Telos4/RoboRally/src/branch/master/docs/images/check-108.pdf) on a sheet of paper and measure the size of the squares with a ruler.
- Run the command
```
$ rosrun camera_calibration cameracalibrator.py --size 8x6 --square 0.108 image:=/cv_camera/image_raw camera:=/cv_camera
```
*Note: Make sure to replace the dimensions of the checkerboard squares with the value you measured in the previous step (in meters)!*
- Move the checkerboard in front of the camera until you have gathered sufficient data and the click on calibrate
- After a successful calibration click *'SAVE'* to store the camera calibration file which ends in `.yaml`
- The calibration data gets written to `/tmp/calibrationdata.tar.gz`. Copy it from there to `~/.ros/camera_info` (create the folder if it does not exists). Then extract it and move and rename the file `ost.yaml` located in `calibrationdata/` to `~/.ros/camera_info/camera.yaml`. Afterwards, you can delete the `calibrationdata` folder and the file `calibrationdata.tar.gz`.
Now, when you restart the `cv_camera` node it also publishes the intrinsic camera parameters as the topic `/cv_camera/camera_info` which can be used to rectify the image.
*Remark: When you restart the camera node you may receive a warning that the camera name does not match the name in the calibration file. This can be safely ignored, but if you want to get rid of the warning just edit the file `camera.yaml` and replace `narrow_stereo` with `camera`.*
### Preparing robots for image detection
In the next step, we place markers on the robots such that the can be detected by the camera. For this we use the [fiducials](http://wiki.ros.org/fiducials) package:
- Generate some ArUco markers for your robots using this [ArUco marker generator](http://chev.me/arucogen/). Choose Original ArUco as the dictionary. You may have to experiment with the marker size a bit depending on the resolution of your camera and the distance to the robots. I used markers of size `100 mm`.
- Print ArUco markers on paper. Measure the marker size and make sure that it fits the size you specified.
![Printed marker](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/markers_1.JPG)
- Cut the markers and glue them on cardboard. Make sure to leave a white border around the marker. It's also a good idea to label the marker with the id.
![Marker on cardboard](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/markers_2.JPG)
- Fix the markers on the robot. First, add a new layer to the robot using screws and then fix the marker to the layer using e.g. double sided tape or velcro tape.
![Marker on robot bottom view](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/markers_3.JPG)
![Marker on robot top view](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/markers_4.JPG)
### Install position detection node
The image detection computes the position and orientation of the marker that was placed on the robot in relation to the camera position.
In order to get from this the position of the robot in the 2D plane (the floor) we need to transform it first.
In this step we will build and install a ROS node that will do exactly this for us.
The node projects the position of each detected marker on the 2D plane and publishes its ID, position and angle of rotation in the plane as a ROS messages.
To install the node do the following:
- Change in the directory `fiducial_transform/`in the root folder of the repository:
```
$ cd fiducial_transform
```
- Build the package:
```
$ catkin_make
```
- Source that package so you can use it:
```
$ source devel/setup.bash
```
*Note: In order to avoid doing this every time you want to start the marker detection you can add the following line to your `~/.bashrc` file:*
```
$ source $ROBO_RALLY_GIT/fiducial_transform/devel/setup.bash
```
*where `$ROBO_RALLY_GIT` is the location of this repository.*
## Testing
Now it's time to test if the position detection is working correctly.
### Running the position detection
Generally, the following steps are necessary to run the position detection for the robots:
- Start roscore:
```
$ roscore
```
- Plug in the camera and start the camera node:
```
$ rosparam set cv_camera/device_id 0 # make sure to set the correct device id for your camera!
$ rosrun cv_camera cv_camera_node
```
- Optional: Check if the camera works correctly;
```
$ rosrun image_view image_view image:=/cv_camera/image_raw
```
You should see a window displaying a live view of the camera image.
![image_view output](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/image_view_2.jpg)
- Start ArUco marker detection:
```
$ roslaunch aruco_detect aruco_detect.launch camera:=cv_camera image:=image_raw dictionary:=16 transport:= fiducial_len:=0.1
```
*Note: Make sure you use the correct camera, image, dictionary and marker length in this step!*
- Optional: Check if marker detection works correctly:
```
$ rosrun image_view image_view image:=/fiducial_images
```
Place a marker in the field of view of the camera and check if it gets correctly detected by the image detection, i.e. there is a border around the square of the marker and arrows indicating its orientation.
![fiducial_images output](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/image_view_3.jpg)
![fiducial_images output](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/image_view_4.jpg)
If the marker is not detected correctly, please check that you are using the correct dictionary, that you camera was calibrated and that you are launching the `aruco_detect` node with the correct parameters
- Run marker transformation node
```
$ rosrun fiducial_transform fiducial_transform_script
```
Now when you run
```
$ rostopic echo /marker_id_pos_angle
```
and you place a marker in the field of view of the camera you should see the markers id, position and orientation as output on the console.
![fiducial_transform node output](https://imaginaerraum.de/git/Telos4/RoboRally/raw/branch/master/docs/images/image_view_5.jpg)
You can also access this data using a ROS listener.
## Wrapping up
Now you're all set up and ready to start your own control experiments with the robots.
You could for example implement a simple PID controller to let the robot perform turns or drive from one position to another.
For some inspiration take a look at the demo programs `MPC_position_controller.py` or `racing_game.py`.

5
docs/5_RACE_GAME.md Normal file
View File

@ -0,0 +1,5 @@
This documents explains how to set up and run the race game demo.
Requirements:
- pygame
- shapely package

Binary file not shown.

Binary file not shown.

BIN
docs/fritzing/robot.fzz Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/images/charging.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
docs/images/check-108.pdf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
docs/images/markers_1.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
docs/images/markers_2.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
docs/images/markers_3.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
docs/images/markers_4.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
docs/images/robot_bb.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
docs/images/webrepl0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
docs/images/webrepl1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
docs/images/webrepl2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
docs/images/webrepl3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,44 @@
Installing Intel RealSense drivers:
###################################
echo 'deb http://realsense-hw-public.s3.amazonaws.com/Debian/apt-repo xenial main' || sudo tee /etc/apt/sources.list.d/realsense-public.list
sudo add-apt-repository "deb http://realsense-hw-public.s3.amazonaws.com/Debian/apt-repo xenial main"
sudo apt-get update
sudo apt-get install librealsense2-dev
sudo apt-get install librealsense2-dkms # enter a key for installing driver when asked (in my case the pw was realsense2019bt)
sudo apt-get install librealsense2-utils
Check if drivers were installed sucessfully:
modinfo uvcvideo | grep "version:" # -> this should report the realsense camera driver
# Installing the ROS package (assuming ros base is installed):
Install dependencies:
sudo apt-get install ros-kinetic-cv-bridge -y
sudo apt-get install ros-kinetic-image-transport
sudo apt-get install ros-kinetic-tf -y
sudo apt-get install ros-kinetic-diagnostic-updater -y
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
sudo apt-get update
sudo apt-get install ros-kinetic-ddynamic-reconfigure -y
# clone github repo:
git clone https://github.com/IntelRealSense/realsense-ros.git
cd realsense-ros/
git checkout `git tag | sort -V | grep -P "^\d+\.\d+\.\d+" | tail -1`
mkdir -p ~/realsense_catkin_ws/src/realsense
mv * ~/realsense_catkin_ws/src/realsense/
cd ~/realsense_catkin_ws/src/
catkin_init_workspace
cd ..
catkin_make clean
catkin_make -DCATKIN_ENABLE_TESTING=False -DCMAKE_BUILD_TYPE=Release
catkin_make install
echo "source ~/realsense_catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc
Test with:
roslaunch realsense2_camera rs_camera.launch
Possible issue:
The camera requires a USB 3 port to provide full resolution streams

View File

@ -0,0 +1,56 @@
Creating ros package with catkin and adding custom messages:
############################################################
1. Create a workspace:
----------------------
$ mkdir catkin_ws
$ cd catkin_ws
$ mkdir src
$ catkin_make
$ source devel/setup.bash
2. Create the package:
----------------------
$ cd catkin_ws/src
$ catkin_create_pkg <package_name> std_msgs rospy roscpp
$ cd ..
$ catkin_make # this build the package
3. Create the custom messages:
------------------------------
$ cd catkin_ws/src/<package_name>
$ mkdir msg
$ cd msg
$ echo "int64 num" > Num.msg
Then edit the package.xml file and add the following:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
Also edit CMakeLists.txt and to the find_package() function add:
message_generation
and to the catkin_package() add:
CATKIN_DEPENDS message_runtime
Then, find add_message_files() (uncomment if necessary) and change it to the following:
add_message_files(
FILES
Num.msg
)
Finally, uncomment the generate_messages() function such that it reads:
generate_messages(
DEPENDENCIES
std_msgs
)
To build the new messages do the following:
$ cd catkin_ws
$ catkin_make # builds the new messages
$ catkin_make install
As the last step, source the setup.bash:
$ . install/setup.bash
Now the new messages should be available and you can find them with
$ rosmsg list
Don't forget to source the setup.bash everytime you want to use the messages!

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

View File

@ -1,9 +0,0 @@
https://hackaday.com/2016/06/14/hackaday-prize-entry-micro-robots-for-education/
https://www.adafruit.com/product/3216
https://www.exp-tech.de/plattformen/robotik/roboterfahrzeuge/7673/adafruit-mini-round-robot-chassis-kit-2wd-with-dc-motors?gclid=EAIaIQobChMIrpHIq4Ke4QIVSeAYCh0LTgdWEAQYCCABEgIQcfD_BwE
https://www.exp-tech.de/plattformen/robotik/roboterfahrzeuge/7898/adafruit-mini-3-layer-round-robot-chassis-kit-2wd-with-dc-motors?gclid=EAIaIQobChMIrpHIq4Ke4QIVSeAYCh0LTgdWEAQYCyABEgI76vD_BwE

View File

@ -1,21 +0,0 @@
Breadboard | NodeMCU, GPIO, index | H-bridge
-------------------------------------
13 | D0, 16, 0 | 15 # driver channel 4 input
14 | D1, 5, 1 | 10 # driver channel 3 input
15 | D2, 4, 2 | 9 # driver 3/4 enable (pwm)
16 | D3, 0, 3 | 2 # driver channel 2 input
17 | D4, 2, 4 | 7 # driver channel 1 input
20 | D5, 14, 5 | 1 # driver 1/2 enable (pwm)
26 | GND
Motor example:
First motor:
GPIO16 -> HIGH (index 0)
GPIO05 -> LOW (index 1)
GPIO04 -> HIGH (index 2)
Second motor:
GPIO00 -> HIGH (index 3)
GPIO02 -> LOW (index 4)
GPIO14 -> HIGH (index 5)

View File

@ -1,5 +0,0 @@
TODO: Use the following shields
Battery shield: https://wiki.wemos.cc/products:retired:battery_shield_v1.1.0
Motor shield: https://wiki.wemos.cc/products:retired:motor_shield_v1.0.0

View File

@ -0,0 +1,206 @@
cmake_minimum_required(VERSION 2.8.3)
project(fiducial_transform)
## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)
## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
catkin_python_setup()
################################################
## Declare ROS messages, services and actions ##
################################################
## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
## * add a build_depend tag for "message_generation"
## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
## but can be declared for certainty nonetheless:
## * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
## * add "message_generation" and every package in MSG_DEP_SET to
## find_package(catkin REQUIRED COMPONENTS ...)
## * add "message_runtime" and every package in MSG_DEP_SET to
## catkin_package(CATKIN_DEPENDS ...)
## * uncomment the add_*_files sections below as needed
## and list every .msg/.srv/.action file to be processed
## * uncomment the generate_messages entry below
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
## Generate messages in the 'msg' folder
add_message_files(
FILES
id_pos_angle.msg
# Message1.msg
# Message2.msg
)
## Generate services in the 'srv' folder
# add_service_files(
# FILES
# Service1.srv
# Service2.srv
# )
## Generate actions in the 'action' folder
# add_action_files(
# FILES
# Action1.action
# Action2.action
# )
## Generate added messages and services with any dependencies listed here
generate_messages(
DEPENDENCIES
std_msgs
)
################################################
## Declare ROS dynamic reconfigure parameters ##
################################################
## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
## * add "dynamic_reconfigure" to
## find_package(catkin REQUIRED COMPONENTS ...)
## * uncomment the "generate_dynamic_reconfigure_options" section below
## and list every .cfg file to be processed
## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
# cfg/DynReconf1.cfg
# cfg/DynReconf2.cfg
# )
###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES fiducial_transform
# CATKIN_DEPENDS roscpp rospy std_msgs
# DEPENDS system_lib
CATKIN_DEPENDS message_runtime
)
###########
## Build ##
###########
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
${catkin_INCLUDE_DIRS}
)
## Declare a C++ library
# add_library(${PROJECT_NAME}
# src/${PROJECT_NAME}/fiducial_transform.cpp
# )
## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/fiducial_transform_node.cpp)
## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")
## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
# ${catkin_LIBRARIES}
# )
#############
## Install ##
#############
# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# install(PROGRAMS
# scripts/my_python_script
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
## Mark executables and/or libraries for installation
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_node
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
# FILES_MATCHING PATTERN "*.h"
# PATTERN ".svn" EXCLUDE
# )
## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
# # myfile1
# # myfile2
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )
catkin_install_python(PROGRAMS bin/fiducial_transform_script
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
#############
## Testing ##
#############
## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_fiducial_transform.cpp)
# if(TARGET ${PROJECT_NAME}-test)
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()
## Add folders to be run by python nosetests
# catkin_add_nosetests(test)

View File

@ -0,0 +1,7 @@
#!/usr/bin/env python
from fiducial_transform.fiducial_to_2d_pos_angle import main
import sys
if __name__ == '__main__':
main(sys.argv)

View File

@ -0,0 +1,4 @@
int32 id
float32 x
float32 y
float32 angle

View File

@ -0,0 +1,70 @@
<?xml version="1.0"?>
<package format="2">
<name>fiducial_transform</name>
<version>0.0.0</version>
<description>This package provides a script that transforms 3D position and orientation information from detected fiducial markers to 2D positions in the x-y-plane and orientation expressed as yaw angle (rotation around z axis). In addition, the id of the detected marker is published.</description>
<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<maintainer email="simon.pirkelmann@uni-bayreuth.de">spirkelmann</maintainer>
<!-- One license tag required, multiple allowed, one license per tag -->
<!-- Commonly used license strings: -->
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
<license>MIT</license>
<!-- Url tags are optional, but multiple are allowed, one per tag -->
<!-- Optional attribute type can be: website, bugtracker, or repository -->
<!-- Example: -->
<!-- <url type="website">http://wiki.ros.org/marker_pos_angle</url> -->
<!-- Author tags are optional, multiple are allowed, one per tag -->
<!-- Authors do not have to be maintainers, but could be -->
<!-- Example: -->
<!-- <author email="jane.doe@example.com">Jane Doe</author> -->
<!-- The *depend tags are used to specify dependencies -->
<!-- Dependencies can be catkin packages or system dependencies -->
<!-- Examples: -->
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
<!-- <depend>roscpp</depend> -->
<!-- Note that this is equivalent to the following: -->
<!-- <build_depend>roscpp</build_depend> -->
<!-- <exec_depend>roscpp</exec_depend> -->
<!-- Use build_depend for packages you need at compile time: -->
<!-- <build_depend>message_generation</build_depend> -->
<!-- Use build_export_depend for packages you need in order to build against this package: -->
<!-- <build_export_depend>message_generation</build_export_depend> -->
<!-- Use buildtool_depend for build tool packages: -->
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<!-- Use exec_depend for packages you need at runtime: -->
<!-- <exec_depend>message_runtime</exec_depend> -->
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<!-- Use doc_depend for packages you need only for building documentation: -->
<!-- <doc_depend>doxygen</doc_depend> -->
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<build_depend>message_generation</build_depend>
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>rospy</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<exec_depend>message_runtime</exec_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->
</export>
</package>

View File

@ -0,0 +1,12 @@
## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD
from distutils.core import setup
from catkin_pkg.python_setup import generate_distutils_setup
# fetch values from package.xml
setup_args = generate_distutils_setup(
packages=['fiducial_transform'],
package_dir={'': 'src'},
)
setup(**setup_args)

View File

@ -0,0 +1,44 @@
import sys
import rospy
from fiducial_transform.msg import id_pos_angle
from fiducial_msgs.msg import FiducialTransformArray
from tf.transformations import euler_from_quaternion
transform_topic = rospy.get_param("transform_topic", '/fiducial_transforms')
output = rospy.get_param("output", 'True')
pub = rospy.Publisher('marker_id_pos_angle', id_pos_angle, queue_size=10)
'''
This callback function listens to fiducial messages that describe the translation and rotation of markers w.r.t. to the camera and transforms them to a position in the 2d plane and the angle of the robot in the plane
'''
def callback(data):
for d in data.transforms:
id = d.fiducial_id
q = d.transform.rotation
t = d.transform.translation
euler = euler_from_quaternion([q.x, q.y, q.z, q.w])
m = id_pos_angle()
m.id = id
m.x = t.x
m.y = -t.y
m.angle = -euler[2]
if output == 'True':
print("(marker_id, pos_x, pos_y, angle) = ({}, {}, {}, {})".format(m.id, m.x, m.y, m.angle))
pub.publish(m)
def main(args):
rospy.init_node('fiducial_transform_node', anonymous=True)
print("Starting subscriber listening for {} topic".format(transform_topic))
quaternion_sub = rospy.Subscriber(transform_topic, FiducialTransformArray, callback)
rospy.spin()
if __name__ == '__main__':
main(sys.argv)

View File

@ -0,0 +1,27 @@
import socket
import json
HOST, PORT = "localhost", 42424
def move_grid(x, y, orientation, dimx, dimy):
print("move grid")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
payload = json.dumps({"x": x, "y": y, "dimx": dimx, "dimy": dimy, "orientation": orientation})
sock.sendall(f"move_grid_blocking;{payload}\n".encode())
target_reached = False
try:
while not target_reached:
reply = sock.recv(1024)
if reply == b'':
raise RuntimeError
#print("got reply: ", reply)
target_reached = reply.decode() == 'ack\n'
print("target_reached = ", target_reached)
except RuntimeError:
# try again
print("controlling failed -> trying again")
move_grid(x, y, orientation, dimx, dimy)
if __name__ == "__main__":
move_grid(3,2,'v', 7, 4)

View File

@ -0,0 +1,542 @@
import numpy as np
import pygame
import os
import threading
from webapp import app
from event_server_comm import move_grid
BLACK = np.array([0, 0, 0], dtype=np.uint8)
WHITE = np.array([255, 255, 255], dtype=np.uint8)
GRAY = np.array([200, 200, 200], dtype=np.uint8)
RED = np.array([255, 0, 0], dtype=np.uint8)
BLUE = np.array([0, 0, 255], dtype=np.uint8)
YELLOW = np.array([255, 255, 0], dtype=np.uint8)
GREEN = np.array([0, 255, 0], dtype=np.uint8)
pygame.init()
pygame.font.init() # you have to call this at the start,
# if you want to use this module.
myfont = pygame.font.SysFont('Comic Sans MS', 55)
myfont_small = pygame.font.SysFont('Comic Sans MS', 45)
P0_text = myfont.render('P0', False, tuple(BLACK))
tiledt = np.dtype([('color', np.uint8, 3), ('star', np.bool)])
class Board:
valid_colors = [GRAY, RED, BLUE]
def __init__(self, dim_x, dim_y, n_coins=2, file=None):
if file is None:
self.tiles = np.zeros((dim_y, dim_x), dtype=tiledt)
for x in range(dim_x):
for y in range(dim_y):
self.tiles[y, x]['color'] = Board.valid_colors[np.random.randint(len(Board.valid_colors))]
coins_distributed = n_coins == 0
while not coins_distributed:
coinx = np.random.randint(0, dim_x)
coiny = np.random.randint(0, dim_y)
self.tiles[coiny,coinx]['star'] = True
coins_distributed = sum([t['star'] for t in self.tiles.flatten()]) == n_coins
else:
self.tiles = np.load(file)
def render(self, scale_fac):
dimy, dimx = self.tiles.shape
board_surf = pygame.Surface((dimx * scale_fac, dimy * scale_fac))
star_surf = pygame.Surface((scale_fac, scale_fac), pygame.SRCALPHA)
pygame.draw.circle(star_surf, YELLOW, (int(0.5 * scale_fac), int(0.5 * scale_fac)), int(0.25 * scale_fac))
for y in range(self.tiles.shape[0]):
for x in range(self.tiles.shape[1]):
pygame.draw.rect(board_surf, tuple(self.tiles[y, x]['color']),
(x * scale_fac, y * scale_fac, scale_fac, scale_fac), 0)
pygame.draw.rect(board_surf, (0, 0, 0),
(x * scale_fac, y * scale_fac, scale_fac, scale_fac), 1)
if self.tiles[y, x]['star']:
board_surf.blit(star_surf, (x * scale_fac, y * scale_fac, scale_fac, scale_fac))
return board_surf
def get_xdims(self):
return self.tiles.shape[1]
def get_ydims(self):
return self.tiles.shape[0]
# def __repr__(self):
# s = ''
# for y in range(self.tiles.shape[0]):
# for x in range(self.tiles.shape[1]):
# if (x,y) == (self.robot.x, self.robot.y):
# s += self.robot.orientation
# else:
# s += '.'
# s += '\n'
# return s
class Robot:
orientations = ['^', 'left', 'down', 'right']
resulting_orientation = {
'^': {'left': '<', 'right': '>'},
'>': {'left': '^', 'right': 'v'},
'v': {'left': '>', 'right': '<'},
'<': {'left': 'v', 'right': '^'},
}
def __init__(self, x, y, orientation, use_real_robot=False):
self.x = x
self.y = y
self.orientation = orientation
self.position_changed = False
self.use_real_robot = use_real_robot
def get_forward_coordinates(self):
# get the coordinates of the neighboring tile in the given direction
if self.orientation == '^':
return self.y - 1, self.x
elif self.orientation == '>':
return self.y, self.x + 1
elif self.orientation == 'v':
return self.y + 1, self.x
elif self.orientation == '<':
return self.y, self.x - 1
else:
raise Exception("error: undefined direction")
def get_angle(self):
angle = {'>': 0, '^': np.pi/2, '<': np.pi, 'v': 3*np.pi/2}[self.orientation]
return np.rad2deg(angle)
def render(self, scale_fac):
robot_surf = pygame.Surface((scale_fac, scale_fac), pygame.SRCALPHA)
pygame.draw.lines(robot_surf, (0, 0, 0), True, [(0.75 * scale_fac, 0.5 * scale_fac),
(0.25 * scale_fac, 0.25 * scale_fac),
(0.25 * scale_fac, 0.75 * scale_fac)], 3)
robot_surf = pygame.transform.rotate(robot_surf, self.get_angle())
return robot_surf
def update_pos(self, dimx, dimy):
if self.use_real_robot:
move_grid(self.x, self.y, self.orientation, dimx, dimy)
self.position_changed = False
def __repr__(self):
return f"({self.y}, {self.x}) - {self.orientation}"
class Command:
valid_actions = {'forward', 'left', 'right', 'P0', '-'}
def __init__(self, action=None, color=GRAY):
if not (action in Command.valid_actions and any([np.all(color == c) for c in Board.valid_colors])):
raise ValueError("invalid values for command")
self.action = action
self.color = color
def __repr__(self):
return f"{self.action}: {self.color}"
def render(self, scale_fac):
cmd_surf = pygame.Surface((scale_fac, scale_fac))
cmd_surf.fill(tuple(self.color))
arrow_surf = pygame.Surface((300, 300), pygame.SRCALPHA)
pygame.draw.polygon(arrow_surf, (0, 0, 0),
((0, 100), (0, 200), (200, 200), (200, 300), (300, 150), (200, 0), (200, 100)))
arrow_surf = pygame.transform.scale(arrow_surf, (int(0.9*scale_fac), int(0.9*scale_fac)))
if self.action == 'forward':
arrow_surf = pygame.transform.rotate(arrow_surf, 90)
elif self.action == 'left':
arrow_surf = pygame.transform.rotate(arrow_surf, 180)
if self.action in {'left', 'forward', 'right'}:
cmd_surf.blit(arrow_surf, (0.05*scale_fac,0.05*scale_fac,0.95*scale_fac,0.95*scale_fac))
elif self.action == 'P0':
cmd_surf.blit(P0_text, (0.05*scale_fac,0.05*scale_fac,0.95*scale_fac,0.95*scale_fac))
return cmd_surf
class Programmer:
def __init__(self, prg):
self.prg = prg
self.available_inputs = [Command('forward'), Command('left'), Command('right'), Command('P0'),
Command('-', color=RED), Command('-', color=BLUE), Command('-', color=GRAY)]
self.command_to_edit = 0
self.screen_rect = None
def render(self, scale_fac):
"""Render surface with possible inputs for the robot.
:return: surface of the input commands
"""
inp_surf = pygame.Surface((len(self.available_inputs) * scale_fac, 1 * scale_fac))
for i, inp in enumerate(self.available_inputs):
cmd_surf = inp.render(scale_fac)
inp_surf.blit(cmd_surf, (i * scale_fac, 0, scale_fac, scale_fac))
return inp_surf
def update_selected_command(self, pos):
print(f"clicked at pos = {pos}")
xoffset = pos[0] - self.screen_rect.x
selected_input_index = xoffset * len(self.available_inputs) // self.screen_rect.width
selected_input = self.available_inputs[selected_input_index]
edited_command = self.prg.cmds[self.command_to_edit]
print("command before edit = ", edited_command)
if selected_input.action != '-':
edited_command.action = selected_input.action
else:
edited_command.color = selected_input.color
print("command after edit = ", edited_command)
class Program:
def __init__(self, robot, board, cmds):
self.cmds = cmds
self.robot = robot
self.board = board
self.prg_counter = 0
self.screen_rect = None
def step(self, state='running', check_victory=True):
if self.prg_counter >= len(self.cmds):
return 'game_over'
cmd = self.cmds[self.prg_counter]
self.prg_counter += 1
# current position
x = self.robot.x
y = self.robot.y
# current tile the robot is on
tile = self.board.tiles[y, x]
# apply next instruction of the program
if np.all(cmd.color == GRAY) or np.all(cmd.color == tile['color']):
# matching color -> execute command
if cmd.action == 'forward':
ynew, xnew = self.robot.get_forward_coordinates()
self.robot.x = xnew
self.robot.y = ynew
self.robot.position_changed = True
#self.robot.update_pos(self.board.get_xdims(), self.board.get_ydims())
elif cmd.action in {'left', 'right'}:
self.robot.orientation = Robot.resulting_orientation[self.robot.orientation][cmd.action]
#self.robot.update_pos(self.board.get_xdims(), self.board.get_ydims())
self.robot.position_changed = True
elif cmd.action == 'P0':
self.prg_counter = 0
else:
#print("color not matching -> skipping command")
pass
# update state for new robot position
if (not (0 <= self.robot.x < self.board.tiles.shape[1])) or not (0 <= self.robot.y < self.board.tiles.shape[0]):
# robot leaves the board -> GAME OVER
print("GAME OVER")
self.robot.update_pos(self.board.get_xdims(), self.board.get_ydims())
return 'game_over'
# robot collects star on new tile
tile = self.board.tiles[self.robot.y, self.robot.x]
if tile['star']:
tile['star'] = False
if check_victory and all([not t['star'] for t in self.board.tiles.flatten()]):
self.robot.update_pos(self.board.get_xdims(), self.board.get_ydims())
print("YOU WON")
return 'won'
# by default we continue in the running state
return state
def render(self, scale_fac, prg_counter_override=None):
"""Render the current program. This will render all commands and highlight the next command to execute
(determined by self.prg_counter).
The prg_counter_override can be used to highlight a different command instead. This is used during input mode
to highlight the command the user will edit.
:param scale_fac:
:param prg_counter_override:
:return:
"""
prg_counter = self.prg_counter
if prg_counter_override is not None:
prg_counter = prg_counter_override
prg_surf = pygame.Surface((5 * scale_fac, 1 * scale_fac))
for i in range(5):
if i < len(self.cmds):
cmd = self.cmds[i]
cmd_surf = cmd.render(scale_fac)
else:
cmd_surf = pygame.Surface((scale_fac,scale_fac))
cmd_surf.fill(GRAY)
if prg_counter is not None and i == prg_counter:
pygame.draw.rect(cmd_surf, tuple(GREEN), (0, 0, scale_fac, scale_fac), 5)
prg_surf.blit(cmd_surf, (i * scale_fac, 0, scale_fac, scale_fac))
return prg_surf
def get_clicked_command(self, pos):
print(f"clicked at pos = {pos}")
xoffset = pos[0] - self.screen_rect.x
clicked_cmd = xoffset * len(self.cmds) // self.screen_rect.width
print("clicked command = ", clicked_cmd)
return clicked_cmd
class Game:
def __init__(self, dimx, dimy, robotx, roboty, orientation, use_real_robot=False):
self.robot = Robot(x=robotx, y=roboty, orientation=orientation, use_real_robot=use_real_robot)
#self.board = Board(dimx, dimy)
self.board = Board(dimx, dimy, file='levels/56.npy')
# TODO fix number of commands at 5
self.cmds = [Command('forward'), Command('left', color=RED), Command('left', color=BLUE), Command('P0'), Command('-')]
self.state = 'reset'
self.prg = Program(self.robot, self.board, self.cmds)
self.programmer = Programmer(self.prg)
#self.scale_fac = 180
self.scale_fac = 125
self.beamer_mode = False
self.screen = pygame.display.set_mode((int(self.board.tiles.shape[1] * self.scale_fac * 1.1),
int((self.board.tiles.shape[0] + 2) * self.scale_fac * 1.2)))
self.game_over_text = myfont.render('GAME OVER', False, WHITE)
self.won_text = myfont.render('YOU WON', False, BLACK)
self.run_text = myfont.render('RUN', False, tuple(BLACK))
self.stop_text = myfont_small.render('STOP', False, tuple(BLACK))
self.step_text = myfont_small.render('STEP', False, tuple(BLACK))
self.prg_text = myfont_small.render('CURRENT PROGRAM', False, tuple(GREEN))
# save initial state
self.initial_pos = (self.robot.x, self.robot.y, self.robot.orientation)
self.initial_board_tiles = self.board.tiles.copy()
def render(self):
"""Render the game screen.
This will render the board and the robot.
Depending on the display mode it will also render the current program and the input commands for the robot.
:param prg_counter:
:return:
"""
if self.beamer_mode:
dx = self.xoffset
dy = 0
else:
dx = int(0.05 * self.screen.get_width())
dy = int(0.05 * self.screen.get_height())
self.screen.fill(tuple(BLACK))
# render the board
board_surf = self.board.render(self.scale_fac)
# render robot onto the board surface
robot_surf = self.robot.render(self.scale_fac)
board_surf.blit(robot_surf, (self.robot.x * self.scale_fac, self.robot.y * self.scale_fac, self.scale_fac, self.scale_fac))
self.screen.blit(board_surf, (dx, dy, dx + self.board.tiles.shape[1] * self.scale_fac, dy + self.board.tiles.shape[0] * self.scale_fac))
# render program
if self.state == 'input':
# in input mode we highlight the command which is selected for edit
prg_surf = self.prg.render(self.scale_fac, prg_counter_override=self.programmer.command_to_edit)
else:
# in other modes we render the current program counter
prg_surf = self.prg.render(self.scale_fac)
prg_surf = pygame.transform.scale(prg_surf, (self.screen.get_width() * 2 // 3, self.scale_fac * 2 // 3))
self.prg.screen_rect = pygame.Rect(
(dx, board_surf.get_height() + 2 * dy, prg_surf.get_width(), prg_surf.get_height()))
# render input fields and buttons
inp_surf = self.programmer.render(self.scale_fac)
inp_surf = pygame.transform.scale(inp_surf, (self.screen.get_width() * 2 // 3, self.scale_fac * 2 // 3))
self.programmer.screen_rect = pygame.Rect(
(dx, board_surf.get_height() + prg_surf.get_height() + 3 * dy, inp_surf.get_width(), inp_surf.get_height()))
btn_surf = pygame.Surface((3 * self.scale_fac // 2, self.scale_fac))
self.btn_rect = pygame.Rect((2 * dx + prg_surf.get_width(), board_surf.get_height() + 2 * dy,
btn_surf.get_height(), btn_surf.get_width()))
if self.state == 'input':
btn_surf.fill(tuple(GREEN))
btn_surf.blit(self.run_text, (0, 10))
elif self.state == 'running':
btn_surf.fill(tuple(RED))
btn_surf.blit(self.stop_text, (0, 10))
elif self.state == 'stepping':
btn_surf.fill(tuple(YELLOW))
btn_surf.blit(self.step_text, (0, 10))
if not self.beamer_mode:
# if we are not in beamer mode we render program and inputs below the board
self.screen.blit(prg_surf, self.prg.screen_rect)
self.screen.blit(inp_surf, self.programmer.screen_rect)
self.screen.blit(btn_surf, self.btn_rect)
else:
prg_surf = pygame.transform.scale(prg_surf, (dx, dx//5))
# in beamer mode we render the program to the left of the board to appear on the laptop
self.screen.blit(prg_surf, (0,100))
prg_descr_surb = pygame.Surface((500, 100))
#prg_descr_surb.blit(self.prg_text)
self.screen.blit(self.prg_text, (50, 50))
mode_text = myfont_small.render(f'STATE: {self.state}', False, tuple(GREEN))
self.screen.blit(mode_text, (50, dx//5 + 350))
# render messages
if self.state == 'game_over':
game_over_surf = pygame.Surface(((self.screen.get_width() - dx) // 2, self.screen.get_height() // 2))
game_over_surf.fill(tuple(BLACK))
game_over_surf.blit(self.game_over_text, ((game_over_surf.get_width() - self.game_over_text.get_width()) // 2,
(game_over_surf.get_height() - self.game_over_text.get_height()) // 2))
self.screen.blit(game_over_surf, (dx + (self.screen.get_width() - dx) // 4, self.screen.get_height() // 4))
pygame.display.update()
pygame.time.wait(1500)
self.state = 'reset'
elif self.state == 'won':
won_surf = pygame.Surface(((self.screen.get_width() - dx) // 2, self.screen.get_height() // 2))
won_surf.fill(tuple(WHITE))
won_surf.blit(self.won_text,
((won_surf.get_width() - self.won_text.get_width()) // 2,
(won_surf.get_height() - self.won_text.get_height()) // 2))
self.screen.blit(won_surf, (dx + (self.screen.get_width() - dx) // 4, self.screen.get_height() // 4))
pygame.display.update()
pygame.time.wait(1500)
self.state = 'reset'
pygame.display.flip()
def process_inputs(self):
# proceed events
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.state = 'quit'
# handle MOUSEBUTTONUP
elif event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
# select command to edit by the programmer
if self.prg.screen_rect is not None:
if self.prg.screen_rect.collidepoint(pos):
print(f"clicked at pos = {pos}")
self.programmer.command_to_edit = self.prg.get_clicked_command(pos)
# set the selected command to a new value
if self.programmer.screen_rect is not None:
if self.programmer.screen_rect.collidepoint(pos):
self.programmer.update_selected_command(pos)
# clicked RUN/STOP button
if self.btn_rect is not None and self.btn_rect.collidepoint(*pos):
print(f"clicked at pos = {pos}")
if self.state == 'running':
self.state = 'input'
elif self.state == 'input':
self.state = 'running'
elif self.state == 'stepping':
self.state = self.prg.step(self.state)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_x:
if not self.beamer_mode:
# switch to beamer mode
self.xoffset = 1000
os.environ['SDL_VIDEO_WINDOW_POS'] = f'{1920-self.xoffset}, 280'
self.scale_fac = 180
self.screen = pygame.display.set_mode((self.xoffset + self.board.tiles.shape[1] * self.scale_fac,
self.board.tiles.shape[0] * self.scale_fac),
pygame.NOFRAME)
self.beamer_mode = True
else:
# switch to normal mode
os.environ['SDL_VIDEO_WINDOW_POS'] = '0, 0'
self.scale_fac = 125
self.screen = pygame.display.set_mode((int(self.board.tiles.shape[1] * self.scale_fac * 1.1),
int((self.board.tiles.shape[0] + 2) * self.scale_fac * 1.2)))
self.beamer_mode = False
elif event.key == pygame.K_s:
# run program
self.state = 'running'
elif event.key == pygame.K_SPACE:
if self.state != 'stepping':
self.state = 'stepping'
else:
self.state = self.prg.step(self.state)
elif event.key == pygame.K_r:
self.state = 'reset'
elif event.key == pygame.K_n:
self.initial_board_tiles = Board(self.board.get_xdims(), self.board.get_ydims()).tiles.copy()
self.state = 'reset'
elif event.type == pygame.USEREVENT:
for i, cmd in enumerate(event.cmds):
self.cmds[i].action = cmd.action
self.cmds[i].color = np.array(cmd.color, dtype=np.uint8)
self.reset()
self.state = 'running'
return self.state
def reset(self):
self.prg.prg_counter = 0
self.robot.x = self.initial_pos[0]
self.robot.y = self.initial_pos[1]
self.robot.orientation = self.initial_pos[2]
self.robot.update_pos(self.board.get_xdims(), self.board.get_ydims())
self.board.tiles = self.initial_board_tiles.copy()
return 'input'
def run(self):
running = True
while running:
self.state = self.process_inputs()
if self.state == 'input':
pass
elif self.state == 'running':
self.state = self.prg.step()
elif self.state == 'reset':
self.state = self.reset()
elif self.state == 'quit':
running = False
elif self.state == 'stepping':
pass
elif self.state == 'game_over' or self.state == 'won':
pass
else:
print("unknown state")
return
self.render()
if self.robot.position_changed:
self.robot.update_pos(self.board.get_xdims(), self.board.get_ydims())
pygame.time.wait(100)
if __name__ == "__main__":
# launch webapp in thread
webserver_thread = threading.Thread(target=app.run, kwargs={'host': '0.0.0.0', 'port': 5000})
webserver_thread.start()
seed = 4
np.random.seed(seed)
game = Game(dimx=7, dimy=4, robotx=5, roboty=1, orientation='>', use_real_robot=False)
game.run()
# TODOs
# - in stepping mode (s) it is possible to drive outside of the map
# - when no P0 command is present the program counter will overflow

View File

@ -0,0 +1,124 @@
from gauss_turing import Program, Board, Command, Robot
import numpy as np
import json
levels = {
0: lambda cmds: len(cmds) <= 3,
1: lambda cmds: len(cmds) == 3
}
def rate_level(robot_path, cmds, board):
path_length = len(set(robot_path))
n_cmds = len(cmds)
n_cmd_colors = len(set([tuple(c.color) for c in cmds]))
difficulty = (path_length - 1) * (n_cmds + n_cmd_colors)
# place coins on the robot path to create a solution
if difficulty > 0:
n_coins = np.random.randint(1, min(path_length, 5)) - 1
# put one coin on last tile visited
coins = [robot_path[-1]]
# distribute other coins randomly on the path
# path without first and list tile
unique_tiles = list(set(robot_path) - {robot_path[-1]} - {robot_path[0]})
for _ in range(n_coins):
c = np.random.randint(0, len(unique_tiles))
new_coin = unique_tiles.pop(c)
coins.append(new_coin)
pass
else:
coins = []
return difficulty, coins
def generate_level(dimx, dimy, max_steps=100):
n_cmds = np.random.randint(2, 6)
assert n_cmds <= 5
# generate random board without any coins
board = Board(dimx, dimy, n_coins=0)
cmds = []
actions = list(sorted(Command.valid_actions - {'-'}))
# generate random commands
for i in range(n_cmds):
action = np.random.choice(actions)
color = Board.valid_colors[np.random.randint(len(Board.valid_colors))]
cmds.append(Command(action, color))
# generate robot at random position
rx = np.random.randint(0, dimx-1)
ry = np.random.randint(0, dimy-1)
orientation = np.random.choice(['>','v','<','^'])
r = Robot(rx, ry, orientation)
prg = Program(r, board, cmds)
continue_running = True
state = 'running'
prg_counter_old = prg.prg_counter
robot_path = [(r.x, r.y)]
step = 0
while continue_running and step < max_steps:
#print(f"prg_counter = {prg.prg_counter} - robot: {r} - state: {state}")
state = prg.step(state, check_victory=False)
robot_path.append((r.x, r.y))
stuck = prg.prg_counter == prg_counter_old
prg_counter_old = prg.prg_counter
if state == 'game_over' or stuck:
continue_running = False
step += 1
last_pos = robot_path[-1]
if not ((0 <= last_pos[0] < dimx) and (0 <= last_pos[1] < dimy)):
# remove last entry of path if robot leaves the board
robot_path.pop(-1)
difficulty, coins = rate_level(robot_path, cmds, board)
# put coins on the board
for coin in coins:
board.tiles[coin[1], coin[0]]['star'] = True
n_coins = len(coins)
return difficulty, board, n_coins, set(robot_path), (rx, ry, orientation), cmds
if __name__ == "__main__":
np.random.seed(2)
levels = {}
for i in range(100):
diff, board, n_coins, robot_path, init_robot_pos, solution = generate_level(7, 4)
if diff > 0:
print("difficulty: ", diff, "n_coins: ", n_coins, "path length: ", len(robot_path))
if diff in levels:
if n_coins > levels[diff]['n_coins'] and len(robot_path) > levels[diff]['path_length']:
levels[diff] = {'board': board, 'init_robot_pos': init_robot_pos, 'solution': solution,
'n_coins': n_coins, 'path_length': len(robot_path)}
else:
levels[diff] = {'board': board, 'init_robot_pos': init_robot_pos, 'solution': solution,
'n_coins': n_coins, 'path_length': len(robot_path)}
level_info = {}
for l, data in levels.items():
np.save(f'levels/{l}.npy', data['board'].tiles)
sol = [(cmd.action, tuple(map(int, cmd.color))) for cmd in data['solution']]
level_info[l] = {'init_robot_pos': data['init_robot_pos'], 'solution': sol,
'n_coins': int(data['n_coins']), 'path_length': int(data['path_length']),
'file': f'levels/{l}.npy'}
with open('levels/level_info.json', 'w') as f:
json.dump(level_info, f, indent=4)
pass

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,61 @@
.cmd-canvas {
outline: 3px solid #dddddd;;
width: 430px;
height: 120px;
margin: 50px auto 0;
position: relative;
}
.programmer-canvas {
outline: 3px solid #dddddd;;
width: 338px;
height: 220px;
margin: 50px auto 0;
position: relative;
}
.register {
outline: 3px dashed #dddddd;;
width: 120px;
height: 240px;
margin: 0px auto 0;
position: relative;
}
.box{
width: 62px;
height: 100px;
position: absolute !important;
top: 100px;
font-size: 15px;
color: #000000;
line-height: 25px;
text-align: center;
cursor: move;
}
.top-buffer { margin-top:20px; }
.center {
margin: 20px;
position: absolute;
left: 50%;
-ms-transform: translateX(-50%, -50%);
transform: translateX(-50%, -50%);
}
.cmd0 { left: 0; top: 0; background-color: #E74C3C; position: absolute !important;}
.cmd1 { left: 92px; top: 0; background-color: #8E44AD; position: absolute !important;}
.cmd2 { left: 184px; top: 0; background-color: #5DADE2; }
.cmd3 { left: 276px; top: 0; background-color: #1ABC9C; }
.cmd4 { left: 368px; top: 0; background-color: #F1C40F; }
.programmer_cmd0 { left: 0; top: 0; background-color: #E74C3C; position: absolute !important;}
.programmer_cmd1 { left: 92px; top: 0; background-color: #8E44AD; position: absolute !important;}
.programmer_cmd2 { left: 184px; top: 0; background-color: #5DADE2; }
.programmer_cmd3 { left: 276px; top: 0; background-color: #1ABC9C; }
.programmer_cmd4 { left: 38px; top: 120px; background-color: #F1C40F; }
.programmer_cmd5 { left: 138px; top: 120px; background-color: #F39C12; }
.programmer_cmd6 { right: 38px; top: 120px; background-color: #34495E; }
.card_hidden { background-color: #dddddd; }

View File

@ -0,0 +1,136 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>imaginaerraum.de</title>
<script src="static/jquery-3.5.1.js"></script>
<script src="static/jquery-ui.min.js"></script>
<script src="static/bootstrap.min.js"></script>
<link rel="stylesheet" href="static/bootstrap.css">
<link rel="stylesheet" href="static/style.css">
</head>
<body>
<!-- container -->
<div class="container">
<!-- current program -->
<div class="row top-buffer">
Aktuelles Programm:
<div class="cmd-canvas">
{% for cmd in current_program %}
<div class="box cmd{{ loop.index0 }}" id="prg_cmd{{ loop.index0 }}" data-action="{{ cmd.action }}" data-color="{{ cmd.color }}" data-img="prg_cmd_img{{ loop.index0 }}"
style="background-color: rgba({{ cmd.color[0] }},{{ cmd.color[1] }},{{ cmd.color[2] }},0.85)">
<img id="prg_cmd_img{{ loop.index0 }}" src="static/{{ cmd.action }}.png">
{{ loop.index0 }}
</div>
{% endfor %}
</div>
</div>
<!-- possible commands -->
<div class="row top-buffer">
Mögliche Befehle:
<div class="programmer-canvas">
{% for cmd in valid_commands %}
<div class="box programmer_cmd{{ loop.index0 }}" id="valid_cmd{{ loop.index0 }}" data-action="{{ cmd.action }}"
data-color="{{ cmd.color }}" data-img="valid_cmd_img{{ loop.index0 }}"
style="background-color: rgba({{ cmd.color[0] }},{{ cmd.color[1] }},{{ cmd.color[2] }},0.85)">
<img id="valid_cmd_img{{ loop.index0 }}" src="static/{{ cmd.action }}.png">
</div>
{% endfor %}
</div>
</div>
<div class="row top-buffer center">
<div class="col-sm">
<input class="btn btn-primary" id="btnSubmit" type="submit" value="Befehle abschicken" />
</div>
<div class="col-sm">
<div id = "alert_placeholder"></div>
</div>
</div>
</div><!-- container -->
<script>
$(document).ready(function () {
var please_wait = $(".notification").hide();
var box = $(".box");
bootstrap_alert = function() {};
bootstrap_alert.warning = function(message) {
$('#alert_placeholder').html('<div class="alert alert-primary"><a class="close" data-dismiss="alert">×</a><span>'+message+'</span></div>')
};
$("#btnSubmit").click(function () {
//alert("button");
var cmds = {
{% for i in range(5) %}
"cmd{{ i }}": {"color": document.getElementById("prg_cmd{{ i }}").getAttribute("data-color"),
"action": document.getElementById("prg_cmd{{ i }}").getAttribute("data-action")},
{% endfor %}
}
//bootstrap_alert.warning('Bitte warten bis alle Spieler ihre Aktion gewählt haben!'); //$(please_wait).show();
var cmds_json = JSON.stringify(cmds);
$.post("/send_cmds", {"cmds_json": cmds_json}, function (data) {
console.log(data);
/*
if (data === 'OK') {
location.reload(); // reload page to get new cards for next round
}
else {
console.log('waiting...')
$.get("/send_cmds", "", function (data) {
if (data === 'OK') {
location.reload(); // reload page to get new cards for next round
}
})
}
*/
});
});
var edited_command = null;
box.click(function () {
//debugger
if ( this.id.includes('prg_cmd') ) {
// program card clicked -> select clicked card for editing
edited_command = this;
}
else if (this.id.includes('valid_cmd')) {
// progamming card clicked -> edit currently selected card
var this_card_action = this.getAttribute('data-action');
var this_card_color = this.getAttribute('data-color');
if (!!edited_command) { // only if there is a card selected
console.log("editing command " + edited_command);
if (this_card_action === "-") {
// set color
edited_command.setAttribute("data-color", this_card_color);
edited_command.style["backgroundColor"] = this.style["backgroundColor"];
}
else {
// set action
edited_command.setAttribute("data-action", this_card_action)
var edited_cmd_img = document.getElementById(edited_command.getAttribute("data-img"));
var prg_img = document.getElementById(this.getAttribute("data-img"));
edited_cmd_img.src = prg_img.src;
}
}
}
});
});
</script>
</body>
</html>

163
gauss-turing/game/webapp.py Normal file
View File

@ -0,0 +1,163 @@
from flask import Flask, render_template, request, session, make_response
import socket
import time
import numpy as np
from playsound import playsound
from itertools import zip_longest
import random
import json
import pygame
app = Flask(__name__)
app.secret_key = b'RoboRallyRolling'
probabilities = [0.21428571428571427, 0.14285714285714285, 0.07142857142857142, 0.07142857142857142,
0.21428571428571427, 0.21428571428571427, 0.07142857142857142]
#deck = roborally.CardDeck()
class Cmd:
possible_moves = ['forward', 'left', 'right', 'P0']
possible_colors = [(200,200,200), (255, 0, 0), (0,0,255)]
def __init__(self, action, color):
self.action = action
self.color = color
def __str__(self):
return "Cmd No. " + self.action
def __repr__(self):
return self.action + " " + str(self.color)
available_robots = {
0: {'id': 11, 'ip': '192.168.1.11', 'x': 1, 'y': 2, 'orientation': '>'},
1: {'id': 12, 'ip': '192.168.1.12', 'x': 3, 'y': 3, 'orientation': '>'}
}
players = {}
class Player:
MAX_PLAYERS = 4
player_counter = 0
def __init__(self):
if Player.player_counter < Player.MAX_PLAYERS:
self.id = Player.player_counter
Player.player_counter += 1
self.max_cards = 9
self.player_hand = deck.draw_cards(self.max_cards)
print("current hand: ", [str(c) for c in self.player_hand])
self.action_count = 5
self.action_chosen = False
self.initialize_robot()
else:
print("max players reached!")
def initialize_robot(self):
x = available_robots[self.id]['x']
y = available_robots[self.id]['y']
marker_id = available_robots[self.id]['id']
ip = available_robots[self.id]['ip']
orientation = available_robots[self.id]['orientation']
self.robot = game.board.create_robot(x, y, orientation, self.id, marker_id)
if game.comm_socket is not None:
cmd = f"initialize_robot, {marker_id}, {ip}, {x-1}, {y-1}, {orientation}\n"
game.comm_socket.sendall(cmd.encode())
data = game.comm_socket.recv(32)
if data.decode() == 'OK\n':
print("robot sucessfully initialized!")
else:
print("error: could not initialize robot!")
self.robot = None
def draw_new_cards(self):
self.player_hand += deck.draw_cards(self.max_cards - len(self.player_hand))
@app.route('/send_cmds', methods=['POST', 'GET'])
def send_cmds():
if request.method == 'POST':
cmds = json.loads(request.form['cmds_json'])
cmd_list = []
for cmd_nr in [f"cmd{i}" for i in range(5)]:
cmd = cmds[cmd_nr]
action = cmd['action']
color_str = cmd['color']
color_str = color_str.strip("()").split(",")
color = tuple(map(int, color_str))
cmd_list.append(Cmd(action, color))
print("got commands: ", cmd_list)
ev = pygame.event.Event(pygame.USEREVENT, {'cmds': cmd_list})
pygame.event.post(ev)
# send commands to the game
# if game.ready():
# game.process_actions()
#
# return 'OK'
# else:
# return 'please wait'
return "OK"
elif request.method == 'GET':
# GET is used when we have to wait for other players to finish
while not game.processing_done: # wait for other players to choose commands and processing to finish
pass
return 'OK'
WHITE = (255, 255, 255)
GRAY = (200, 200, 200)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
@app.route('/', methods=['GET', 'POST'])
def hello_world():
prg = [Cmd('forward', GRAY), Cmd('left', GRAY), Cmd('right', BLUE),
Cmd('P0', GRAY), Cmd('right', RED)]
valid_cmds = [Cmd('forward', WHITE), Cmd('right', WHITE), Cmd('left', WHITE),
Cmd('P0', WHITE), Cmd('-', RED), Cmd('-', BLUE), Cmd('-', GRAY)]
if request.method == 'GET':
robot_info = None
return render_template('drag_example.html', current_program=prg, valid_commands=valid_cmds, robot_info=robot_info)
elif request.method == 'POST':
# print(request.form)
if request.form.get('drag') and request.form.get('drop'):
# swap cards in the current hand
i1 = int(request.form.get('drag')) # number of first card
i2 = int(request.form.get('drop')) # number of second card
card1 = deck.deck[i1] # get card by number
card2 = deck.deck[i2]
print("swapping {} and {}".format(card1, card2))
j1 = player_hand.index(card1) # get index of card in the hand
j2 = player_hand.index(card2)
player_hand[j1], player_hand[j2] = player_hand[j2], player_hand[j1] # swap the cards in the list
# print("current hand: ", [str(c) for c in player_hand[player_id]])
return 'OK'
else:
return render_template('drag_example.html', cmds=player_hand, player_id=player_id)
if __name__ == '__main__':
#app.run(host='192.168.1.222', port=5000)
app.run(host='0.0.0.0', port=5000)

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