diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75ec3f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/* \ No newline at end of file diff --git a/DESCRIPTION.rst b/DESCRIPTION.rst new file mode 100644 index 0000000..f51db93 --- /dev/null +++ b/DESCRIPTION.rst @@ -0,0 +1,3 @@ +PiSloth +======================= +Library for PiSloth diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..4217ee9 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,7 @@ +include DESCRIPTION.rst + +# Include the test suite (FIXME: does not work yet) +# recursive-include tests * + +# If using Python 3.5 or less, then have to include package data, even though +# it's already declared in setup.py diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..9534b01 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..bcb5688 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,89 @@ +alabaster==0.7.12 +altgraph==0.17 +argh==0.26.2 +awscli==1.18.124 +Babel==2.7.0 +beautifulsoup4==4.9.3 +bitstring==3.1.7 +boto3==1.14.34 +botocore==1.17.47 +certifi==2019.3.9 +cffi==1.14.5 +chardet==3.0.4 +click==7.1.2 +colorama==0.4.1 +commonmark==0.9.1 +cryptography==3.4.7 +Django==2.2.7 +docutils==0.15.2 +ecdsa==0.16.1 +esptool==3.0 +furo==2021.4.11b34 +future==0.18.2 +idna==2.8 +imagesize==1.1.0 +importlib-metadata==2.0.0 +Jinja2==2.10.3 +jmespath==0.10.0 +joblib==0.17.0 +karma-sphinx-theme==0.0.8 +livereload==2.6.1 +lunr==0.5.8 +Markdown==3.3.3 +MarkupSafe==1.1.1 +mkdocs==1.1.2 +mysql-connector==2.2.9 +nltk==3.5 +numpy==1.19.1 +opencv-python==4.4.0.40 +packaging==19.2 +pandas==1.1.1 +pathtools==0.1.2 +pefile==2019.4.18 +Pillow==7.2.0 +port-for==0.3.1 +pyasn1==0.4.8 +pycparser==2.20 +pygame==1.9.6 +Pygments==2.4.2 +PyInstaller==3.6 +PyMySQL==0.9.3 +pyparsing==2.4.2 +PyQt5==5.10.1 +PyQt5-sip==12.8.1 +pyserial==3.5 +python-dateutil==2.8.0 +python-magic==0.4.15 +pytz==2019.3 +pywin32-ctypes==0.2.0 +PyYAML==5.1.2 +recommonmark==0.6.0 +reedsolo==1.5.4 +regex==2020.10.23 +requests==2.21.0 +rsa==4.7 +s3cmd==2.0.2 +s3transfer==0.3.3 +sip==4.19.8 +six==1.12.0 +snowballstemmer==2.0.0 +soupsieve==2.2.1 +Sphinx==3.5.4 +sphinx-autobuild==0.7.1 +sphinx-intl==2.0.1 +sphinx-notfound-page==0.6 +sphinx-rtd-theme==0.4.3 +sphinxcontrib-applehelp==1.0.1 +sphinxcontrib-devhelp==1.0.1 +sphinxcontrib-htmlhelp==1.0.2 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.2 +sphinxcontrib-serializinghtml==1.1.3 +spi==0.2.0 +sqlparse==0.3.0 +tornado==6.0.3 +tqdm==4.51.0 +urllib3==1.24.2 +watchdog==0.9.0 +you-get==0.4.1475 +zipp==3.4.0 diff --git a/docs/source/about_robot_hat.rst b/docs/source/about_robot_hat.rst new file mode 100644 index 0000000..9f56f38 --- /dev/null +++ b/docs/source/about_robot_hat.rst @@ -0,0 +1,20 @@ +About Robot HAT +----------------------------- + +.. image:: img/picar_x_pic7.png + +**RST Button** + * Short pressing RST Button causes program resetting. + * Long press RST Button till the LED lights up then release, and you will disconnect the Bluetooth. + +**USR Button** + * The functions of USR Button can be set by your programming. (Pressing down leads to a input “0”; releasing produces a input “1”. ) + +**LED** + * Set by your program. (Outputting 1 turns the LED on; Outputting 0 turns it off.) + +**Battery Indicator** + * The voltage ranging above 7.8V, two LEDs light up; ranging 6.7V~7.8V, one LED turns on; ranging below 6.7V, all LEDs turn off. + +**Bluetooth Indicator** + * The Bluetooth indicator keeps turning on at a well Bluetooth connection, blink at a Bluetooth disconnection, blink fast at a signal transmission. \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..cc4f56d --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,62 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) +import sphinx_rtd_theme + +html_theme = 'sphinx_rtd_theme' +# -- Project information ----------------------------------------------------- + +project = 'pisloth' +copyright = '2021, sunfounder' +author = 'sunfounder' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +html_js_files = [ + 'https://ezblock.cc/readDocFile/topHead.js', +] +html_css_files = [ + 'https://ezblock.cc/readDocFile/topHead.css', +] diff --git a/docs/source/dance.rst b/docs/source/dance.rst new file mode 100644 index 0000000..0f26708 --- /dev/null +++ b/docs/source/dance.rst @@ -0,0 +1,90 @@ +Dance +========= + +In this project, we will play a piece of music and let Pisloth dance to the rhythm of the music. + +You can open ``dancing.py`` in the folder of the ``example`` with command ``sudo pyrhon3 dancing.py`` or directly copy the following code to the Python IDE to run. + +**Code** + +.. code:: python + + from pisloth import Sloth + from robot_hat import Music + from robot_hat import Ultrasonic + from robot_hat import Pin + import time + import os + + music = Music() + + sloth = Sloth([1,2,3,4]) + sloth.set_offset([0,0,0,0]) + + def main(): + + music.background_music('./musics/india-Arulo.mp3') + music.music_set_volume(20) + sloth.do_action('stomp left',3,bpm=129) + sloth.do_action('stomp rihgt',3,bpm=129) + sloth.do_action('moon walk left',3,bpm=129) + sloth.do_action('moon walk right',3,bpm=129) + for i in range(3): + sloth.do_action('swing',1,bpm=129) + sloth.do_action('stand',1,bpm=129) + for i in range(3): + sloth.do_action('close',1,bpm=129) + sloth.do_action('stand',1,bpm=129) + sloth.do_action('open',1,bpm=129) + sloth.do_action('stand',1,bpm=129) + sloth.do_action('tiptoe left',2,bpm=129) + sloth.do_action('tiptoe right',2,bpm=129) + + sloth.do_action('stomp left',3,bpm=129) + sloth.do_action('stomp rihgt',3,bpm=129) + sloth.do_action('moon walk left',3,bpm=129) + sloth.do_action('moon walk right',3,bpm=129) + for i in range(3): + sloth.do_action('hook',1,bpm=129) + sloth.do_action('stand',1,bpm=129) + for i in range(4): + sloth.do_action('swing',1,bpm=129) + sloth.do_action('big swing',1,bpm=129) + sloth.do_action('swing',1,bpm=129) + sloth.do_action('stand',1,bpm=129) + + sloth.do_action('tiptoe right',2,bpm=129) + sloth.do_action('stand',2,bpm=129) + + + + music.music_stop() + time.sleep(10) + + + + if __name__ == "__main__": + while True: + main() + +**How it works?** + +You can make Pisloth play music by importing the following library. + +.. code:: python + + from robot_hat import TTS, Music + +Play the background music in the music directory and set the volume to 20. + +.. code:: python + + music.background_music('./musics/india-Arulo.mp3') + music.music_set_volume(20) + +Execute the function ``sloth.do_action(motion_name,step=1,speed=None,bpm=None)`` to let Pisloth perform the actions we set. +The bpm parameter here will affect the interval time between Pisloth's actions, the larger the value, the shorter the interval time. +When we learn about the bpm of a piece of music through some methods, you can freely match the movement of the Pisloth according to the bpm of this music to make it dance with different styles of music. + +For music bmp, if you want to know more, you can refer to: +https://en.wikipedia.org/wiki/Tempo \ No newline at end of file diff --git a/docs/source/diy_action.rst b/docs/source/diy_action.rst new file mode 100644 index 0000000..f1d5f25 --- /dev/null +++ b/docs/source/diy_action.rst @@ -0,0 +1,41 @@ +DIY Action +=============== + +Through the previous courses, do you have a deeper understanding of Pisloth? And next let’s try to design the movement of Pisloth by ourselves. + +**Code** + +.. code:: python + + from pisloth import Sloth + import time + + sloth = Sloth([1,2,3,4]) + sloth.add_action("my_action", [ + [ 45,0 ,0, 0], + [0, 0, 0, 0] + ]) + + def main(): + sloth.do_action("my_action", 1, 80) + time.sleep(1) + + if __name__ == "__main__": + while True: + main() + +**How it works?** + +You can customize the action of Pisloth through the ``add_action`` function, the first parameter is the name of the action, +The second parameter is a two-dimensional array. The four parameters in the array are used to control the four servos of the Pisloth leg. +For how to choose the value we will introduce it in detail in the next lesson. + +.. code:: python + + sloth = Sloth([1,2,3,4]) + sloth.add_action("my_action", [ + [ 45,0 ,0, 0], + [0, 0, 0, 0] + ]) + +Then use the ``do_action'' function to make pisloth do the action to see the actual effect. \ No newline at end of file diff --git a/docs/source/diy_action2.rst b/docs/source/diy_action2.rst new file mode 100644 index 0000000..304b9dd --- /dev/null +++ b/docs/source/diy_action2.rst @@ -0,0 +1,140 @@ +DIY Action2 +=============== + +Through the last lesson, we learned how to customize the action of Pisloth with the ``add_action`` function. In this lesson, we will directly control the four servos of the Pisloth leg through the keyboard, which allows you to +have a more intuitive understanding of Pisloth's leg servos to help you customize some more interesting actions. + +**Code** + +.. code:: python + + from robot_hat import PWM + from robot_hat import Servo + + import sys + import termios + import time + + + right_up_servo = Servo(PWM('P0')) + right_down_servo = Servo(PWM('P1')) + left_up_servo = Servo(PWM('P2')) + left_down_servo = Servo(PWM('P3')) + + + def readchar(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + def readkey(getchar_fn=None): + getchar = getchar_fn or readchar + c1 = getchar() + if ord(c1) != 0x1b: + return c1 + c2 = getchar() + if ord(c2) != 0x5b: + return c1 + c3 = getchar() + return chr(0x10 + ord(c3) - 65) + + manual = ''' + Press keys on keyboard to control PiSloth! + Q: Increase left-up servo angle + W: Decrease left-up servo angle + Z: Increase left-down servo angle + X: Decrease left-down servo angle + I: Increase right-up servo angle + O: Decrease right-up servo angle + N: Increase right-down servo angle + M: Decrease right-down servo angle + SPACE: Print all angle + ESC: Quit + ''' + + def main(): + print(manual) + + left_up_angle=0 + left_down_angle=0 + right_up_angle=0 + right_down_angle=0 + while True: + key = readkey() + # print(key) + if key == "q": + left_up_angle = left_up_angle+5 + elif key == "w": + left_up_angle = left_up_angle-5 + elif key == "z": + left_down_angle = left_down_angle+5 + elif key == "x": + left_down_angle = left_down_angle-5 + elif key == "i": + right_up_angle = right_up_angle+5 + elif key == "o": + right_up_angle = right_up_angle-5 + elif key == "n": + right_down_angle = right_down_angle+5 + elif key == "m": + right_down_angle = right_down_angle-5 + elif key == chr(32): # 32 for space + print(right_up_angle,right_down_angle,left_up_angle,left_down_angle) + elif key == chr(27): # 27 for ESC + break + + right_up_servo.angle(right_up_angle) + right_down_servo.angle(right_down_angle) + left_up_servo.angle(left_up_angle) + left_down_servo.angle(left_down_angle) + + print("\nQuit") + + if __name__ == "__main__": + main() + +**How it works?** + +This program is divided into two parts. The first part reads the characters of the pressed keyboard, and the second part controls the four servos by pressing the characters of the keyboard. + +``left_up_angle``, ``left_down_angle``, ``right_up_angle``, ``right_down_angle`` corresponds to the four values ​​in the parameters of the function ``add_action``. We increase or decrease these four values ​​by pressing the preset keyboard characters to control the rotation direction and amplitude of the four servos on the pisloth leg. + +.. code:: python + + left_up_angle=0 + left_down_angle=0 + right_up_angle=0 + right_down_angle=0 + while True: + key = readkey() + # print(key) + if key == "q": + left_up_angle = left_up_angle+5 + elif key == "w": + left_up_angle = left_up_angle-5 + elif key == "z": + left_down_angle = left_down_angle+5 + elif key == "x": + left_down_angle = left_down_angle-5 + elif key == "i": + right_up_angle = right_up_angle+5 + elif key == "o": + right_up_angle = right_up_angle-5 + elif key == "n": + right_down_angle = right_down_angle+5 + elif key == "m": + right_down_angle = right_down_angle-5 + elif key == chr(32): # 32 for space + print(right_up_angle,right_down_angle,left_up_angle,left_down_angle) + elif key == chr(27): # 27 for ESC + break + + right_up_servo.angle(right_up_angle) + right_down_servo.angle(right_down_angle) + left_up_servo.angle(left_up_angle) + left_down_servo.angle(left_down_angle) \ No newline at end of file diff --git a/docs/source/dont_touch_me.rst b/docs/source/dont_touch_me.rst new file mode 100644 index 0000000..6175b13 --- /dev/null +++ b/docs/source/dont_touch_me.rst @@ -0,0 +1,70 @@ +Don\'t Touch Me +================== + +In this project, we will let Pisloth express his little emotions. When you try to touch Pisloth, He will be on his guard and get back from you. + +**Code** + +.. code:: python + + from pisloth import Sloth + from robot_hat import Music + from robot_hat import Ultrasonic + from robot_hat import Pin + import time + import os + + + music = Music() + + sloth = Sloth([1,2,3,4]) + sloth.set_offset([0,0,0,0]) + sonar = Ultrasonic(Pin("D0") ,Pin("D1")) + + alert_distance = 20 + + def main(): + distance = sonar.read() + print(distance) + if distance <= alert_distance : + try: + music.sound_effect_threading('./sounds/talk3.wav') + except Exception as e: + print(e) + sloth.do_action('backward', 2, 90) + else: + sloth.do_action('stand', 1, 90) + time.sleep(1) + + + if __name__ == "__main__": + while True: + main() + +**How it works?** + +Instantiate various classes of music, pislot and infrared sensor modules to be used. + +.. code:: python + + music = Music() + + sloth = Sloth([1,2,3,4]) + sloth.set_offset([0,0,0,0]) + sonar = Ultrasonic(Pin("D0") ,Pin("D1")) + +The ultrasonic module reads the distance between your hand and the Pisloth and sets the judgment condition. When the distance is less than or equal to alert_distance, Pisloth will play the audio file and do ``backward`` and ``stand`` actions. + +.. code:: python + + distance = sonar.read() + print(distance) + if distance <= alert_distance : + try: + music.sound_effect_threading('./sounds/talk3.wav') + except Exception as e: + print(e) + sloth.do_action('backward', 2, 90) + else: + sloth.do_action('stand', 1, 90) + time.sleep(1) \ No newline at end of file diff --git a/docs/source/emotional_pisloth.rst b/docs/source/emotional_pisloth.rst new file mode 100644 index 0000000..8df1627 --- /dev/null +++ b/docs/source/emotional_pisloth.rst @@ -0,0 +1,231 @@ +Emotional PiSloth +======================= + +In this project, we will make the Pisloth more vivid, and we will make it show more human emotions, +of course, after learning this lesson, you can also design more emotional actions yourself. + +**Code** + +.. code:: python + + from pisloth import Sloth + from robot_hat import Music + import time + + music = Music() + + sloth = Sloth([1,2,3,4]) + sloth.set_offset([0,0,0,0]) + + def confuse(): + try: + music.sound_effect_threading('./sounds/sign.wav') + except Exception as e: + print(e) + sloth.do_action('hook', 1, 100) + + def happy(): + try: + music.sound_effect_threading('./sounds/happy2.wav') + except Exception as e: + print(e) + for i in range(3): + sloth.do_action('hook', 1, 100) + sloth.do_action('stand', 1, 100) + + def fear(): + try: + music.sound_effect_threading('./sounds/warning.wav') + except Exception as e: + print(e) + sloth.do_action('hook', 1, 100) + sloth.do_action('stand', 1, 100) + try: + music.sound_effect_threading('./sounds/warning.wav') + except Exception as e: + print(e) + sloth.do_action('walk backward boldly', 1, 100) + sloth.do_action('stand', 1, 100) + + def sad(): + try: + music.sound_effect_threading('./sounds/depress.wav') + except Exception as e: + print(e) + sloth.do_action('big swing', 1, 100) + + def angry(): + try: + music.sound_effect_threading('./sounds/error.wav') + except Exception as e: + print(e) + sloth.do_action('walk backward boldly', 1, 100) + sloth.do_action('stand', 1, 100) + + def fail(): + try: + music.sound_effect_threading('./sounds/depress2.wav') + except Exception as e: + print(e) + sloth.do_action('fall left', 1, 100) + + def shy(): + try: + music.sound_effect_threading('./sounds/talk3.wav') + except Exception as e: + print(e) + sloth.do_action('close', 1, 100) + time.sleep(1) + try: + music.sound_effect_threading('./sounds/talk2.wav') + except Exception as e: + print(e) + sloth.do_action('stand', 1, 100) + + def main(): + + print("shy") + shy() + time.sleep(1) + sloth.do_action('stand', 1, 100) + time.sleep(2) + + print("confuse") + confuse() + time.sleep(1) + sloth.do_action('stand', 1, 100) + time.sleep(2) + + print("happy") + happy() + time.sleep(1) + sloth.do_action('stand', 1, 100) + time.sleep(2) + + print("fear") + fear() + time.sleep(1) + sloth.do_action('stand', 1, 100) + time.sleep(2) + + print("sad") + sad() + time.sleep(1) + sloth.do_action('stand', 1, 100) + time.sleep(2) + + print("angry") + angry() + time.sleep(1) + sloth.do_action('stand', 1, 100) + time.sleep(2) + + print("fail") + fail() + time.sleep(1) + sloth.do_action('stand', 1, 100) + time.sleep(2) + + + if __name__ == "__main__": + while True: + main() + +**How it works?** + +Pisloth plays ``sign.wav`` and executes the ``hook'' action to indicate confuse. + +.. code:: python + + def confuse(): + try: + music.sound_effect_threading('./sounds/sign.wav') + except Exception as e: + print(e) + sloth.do_action('hook', 1, 100) + +Pisloth plays ``happy2.wav`` and executes the ``hook`` and ``stand'' actions 3 times to indicate happy. + +.. code:: python + + def happy(): + try: + music.sound_effect_threading('./sounds/happy2.wav') + except Exception as e: + print(e) + for i in range(3): + sloth.do_action('hook', 1, 100) + sloth.do_action('stand', 1, 100) + +Pisloth plays warning.wav and executes ``hook``, ``stand``, ``walk backward boldly`` actions to indicate fear. + +.. code:: python + + def fear(): + try: + music.sound_effect_threading('./sounds/warning.wav') + except Exception as e: + print(e) + sloth.do_action('hook', 1, 100) + sloth.do_action('stand', 1, 100) + try: + music.sound_effect_threading('./sounds/warning.wav') + except Exception as e: + print(e) + sloth.do_action('walk backward boldly', 1, 100) + sloth.do_action('stand', 1, 100) + +Pisloth plays ``depress.wav`` and performs the ``big swing'' action to indicate sad. + +.. code:: python + + def sad(): + try: + music.sound_effect_threading('./sounds/depress.wav') + except Exception as e: + print(e) + sloth.do_action('big swing', 1, 100) + +Pisloth plays ``error.wav`` and performs the ``walk backward boldly`` and ``stand`` actions to indicate angry. + +.. code:: python + + def angry(): + try: + music.sound_effect_threading('./sounds/error.wav') + except Exception as e: + print(e) + sloth.do_action('walk backward boldly', 1, 100) + sloth.do_action('stand', 1, 100) + +Pisloth plays ``depress2.wav`` and executes the ``fall left`` action to indicate a fall. + +.. code:: python + + def fail(): + try: + music.sound_effect_threading('./sounds/depress2.wav') + except Exception as e: + print(e) + sloth.do_action('fall left', 1, 100) + +.. note:: + This action will make the Pisloth fall, be careful not to let it fall off the table and break it. + + +Pisloth plays ``talk3.wav``, ``talk2.wav`` and executes ``close``, ``stand'' action means shy. + +.. code:: python + + def shy(): + try: + music.sound_effect_threading('./sounds/talk3.wav') + except Exception as e: + print(e) + sloth.do_action('close', 1, 100) + time.sleep(1) + try: + music.sound_effect_threading('./sounds/talk2.wav') + except Exception as e: + print(e) + sloth.do_action('stand', 1, 100) \ No newline at end of file diff --git a/docs/source/ezblock/conf.py b/docs/source/ezblock/conf.py new file mode 100644 index 0000000..cc4f56d --- /dev/null +++ b/docs/source/ezblock/conf.py @@ -0,0 +1,62 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) +import sphinx_rtd_theme + +html_theme = 'sphinx_rtd_theme' +# -- Project information ----------------------------------------------------- + +project = 'pisloth' +copyright = '2021, sunfounder' +author = 'sunfounder' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +html_js_files = [ + 'https://ezblock.cc/readDocFile/topHead.js', +] +html_css_files = [ + 'https://ezblock.cc/readDocFile/topHead.css', +] diff --git a/docs/source/ezblock/dance_ez.rst b/docs/source/ezblock/dance_ez.rst new file mode 100644 index 0000000..7a1cd7a --- /dev/null +++ b/docs/source/ezblock/dance_ez.rst @@ -0,0 +1,32 @@ +Dance +========= + +In this project, we would play a piece of music and make Pisloth dance to the rhythm of the music. + +Cute Pisloth and good music can put you in a good mood. + +**TIPS** + +.. image:: img/dance.png + :width: 400 + +This block can play some music that we have preset. + +.. image:: img/dance2.png + :width: 400 + +And then you can adjust the volume with this block. + +.. image:: img/dance4.png + :width: 250 + +You may want to use repeat which can help you repeatedly execute the same statement and reduce code size. + + +**EXAMPLE** + +.. image:: img/dance6.png + +.. image:: img/dance7.png + +.. image:: img/dance8.png diff --git a/docs/source/ezblock/diy_action2_ez.rst b/docs/source/ezblock/diy_action2_ez.rst new file mode 100644 index 0000000..1419fd2 --- /dev/null +++ b/docs/source/ezblock/diy_action2_ez.rst @@ -0,0 +1,39 @@ +DIY Action2 +=============== + +Through the last lesson, I learned how to customize the movement of Pisloth. In this lesson, we will directly control the Pisloth's four-servo through the control-widget, so that you can have a more intuitive understanding of the Pisloth's operating machine. Help you customize some more interesting actions. + +**TIPS** + +.. image:: img/DIYII1.png + +First,we can drag 9 buttons to control the rotation angles of the four servos on the pisloth leg. + +.. image:: img/DIYII2.png + :width: 500 + +Variables can be created on this page. We create four variables to represent the rotation angles of the four servos. + +.. image:: img/DIYII3.png + :width: 300 + +Then initialize the rotation angle to zero. + +.. image:: img/DIYII4.png + :width: 400 + +Set the value of the servo rotation plus 1 or minus 1 each time you press the key. + +.. image:: img/DIYII5.png + +Press Button I to make the screen print out the rotation values ​​of the four servos, which can be seen in the control window in the lower left corner. + +.. image:: img/DIYII6.png + +.. image:: img/DIYII7.png + +Finally, on the Pisloth page, you can use these two blocks to view the actual action effect. + +**EXAMPLE** + +.. image:: img/DIYII.png \ No newline at end of file diff --git a/docs/source/ezblock/diy_action_ez.rst b/docs/source/ezblock/diy_action_ez.rst new file mode 100644 index 0000000..898d887 --- /dev/null +++ b/docs/source/ezblock/diy_action_ez.rst @@ -0,0 +1,32 @@ +DIY Action +=============== + +Through the previous courses, do you have a deeper understanding of Pisloth? And next let's try to design the movement of Pisloth by ourselves. + +**TIPS** + +You can use this block on the PiSloth page to design the Pisloth action yourself. + +.. image:: img/DIY2.png + +These values ​​represent the rotation angles of the four servos on the pisloth leg. The range is (-90~90). We will introduce this in detail in the next lesson.。 + +.. image:: img/DIY3.png + +Here you can increase or decrease the number of items by dragging it, so as to make more action of the pisloth. + +.. image:: img/DIY4.png + +This combination can make Pisloth perform familiar movements, let’s have a try. + +.. image:: img/DIY5.png + +Then you can find our customized action here. + +.. image:: img/DIY6.png + +Drag it and replace. + +**EXAMPLE** + +.. image:: img/DIY7.png \ No newline at end of file diff --git a/docs/source/ezblock/dont_touch_me_ez.rst b/docs/source/ezblock/dont_touch_me_ez.rst new file mode 100644 index 0000000..deefcb6 --- /dev/null +++ b/docs/source/ezblock/dont_touch_me_ez.rst @@ -0,0 +1,12 @@ +Don\'t Touch Me +================== + +In this project, we will let Pisloth express his little emotions. When you try to touch Pisloth, He will be on his guard and get back from you. + +**TIPS** + +This lesson has many similarities with Obstacle Avoidance, which can strengthen your understanding of the ultrasound module. + +**EXAMPLE** + +.. image:: img/no_touch.png \ No newline at end of file diff --git a/docs/source/ezblock/emotional_pisloth_ez.rst b/docs/source/ezblock/emotional_pisloth_ez.rst new file mode 100644 index 0000000..c345a6a --- /dev/null +++ b/docs/source/ezblock/emotional_pisloth_ez.rst @@ -0,0 +1,42 @@ +Emotional PiSloth +======================= + +In this project, we will make the Pisloth more vivid, and we will make it show more human emotions. Of course, after learning this lesson, you can also design more emotions yourself. + +**TIPS** + +.. image:: img/emotional1.png + :width: 400 + +You may want to simplify the program with Functions, especially when you perform the same operation multiple times. Putting these operations into a newly declared function can greatly facilitate your use. + +.. image:: img/emotional2.png + +You can find and call the function block you wrote here. + +**EXAMPLE** + +**shy:** + +.. image:: img/shy.png + +**confuse:** + +.. image:: img/confuse.png + +**happy:** + +.. image:: img/happy.png + +**fear:** + +.. image:: img/fear.png + +**sad:** + +.. image:: img/sad.png + +**call function** + +.. image:: img/emotional.png + :width: 350 \ No newline at end of file diff --git a/docs/source/ezblock/img/DIY.png b/docs/source/ezblock/img/DIY.png new file mode 100644 index 0000000..7c6c944 Binary files /dev/null and b/docs/source/ezblock/img/DIY.png differ diff --git a/docs/source/ezblock/img/DIY2.png b/docs/source/ezblock/img/DIY2.png new file mode 100644 index 0000000..59c2695 Binary files /dev/null and b/docs/source/ezblock/img/DIY2.png differ diff --git a/docs/source/ezblock/img/DIY3.png b/docs/source/ezblock/img/DIY3.png new file mode 100644 index 0000000..3fc784e Binary files /dev/null and b/docs/source/ezblock/img/DIY3.png differ diff --git a/docs/source/ezblock/img/DIY4.png b/docs/source/ezblock/img/DIY4.png new file mode 100644 index 0000000..7cb4eed Binary files /dev/null and b/docs/source/ezblock/img/DIY4.png differ diff --git a/docs/source/ezblock/img/DIY5.png b/docs/source/ezblock/img/DIY5.png new file mode 100644 index 0000000..5f71517 Binary files /dev/null and b/docs/source/ezblock/img/DIY5.png differ diff --git a/docs/source/ezblock/img/DIY6.png b/docs/source/ezblock/img/DIY6.png new file mode 100644 index 0000000..4df44fa Binary files /dev/null and b/docs/source/ezblock/img/DIY6.png differ diff --git a/docs/source/ezblock/img/DIY7.png b/docs/source/ezblock/img/DIY7.png new file mode 100644 index 0000000..6b02eec Binary files /dev/null and b/docs/source/ezblock/img/DIY7.png differ diff --git a/docs/source/ezblock/img/DIYII.png b/docs/source/ezblock/img/DIYII.png new file mode 100644 index 0000000..f1370b4 Binary files /dev/null and b/docs/source/ezblock/img/DIYII.png differ diff --git a/docs/source/ezblock/img/DIYII1.png b/docs/source/ezblock/img/DIYII1.png new file mode 100644 index 0000000..16d079d Binary files /dev/null and b/docs/source/ezblock/img/DIYII1.png differ diff --git a/docs/source/ezblock/img/DIYII2.png b/docs/source/ezblock/img/DIYII2.png new file mode 100644 index 0000000..c382f8d Binary files /dev/null and b/docs/source/ezblock/img/DIYII2.png differ diff --git a/docs/source/ezblock/img/DIYII3.png b/docs/source/ezblock/img/DIYII3.png new file mode 100644 index 0000000..a8052d5 Binary files /dev/null and b/docs/source/ezblock/img/DIYII3.png differ diff --git a/docs/source/ezblock/img/DIYII4.png b/docs/source/ezblock/img/DIYII4.png new file mode 100644 index 0000000..ea53929 Binary files /dev/null and b/docs/source/ezblock/img/DIYII4.png differ diff --git a/docs/source/ezblock/img/DIYII5.png b/docs/source/ezblock/img/DIYII5.png new file mode 100644 index 0000000..fec4fd0 Binary files /dev/null and b/docs/source/ezblock/img/DIYII5.png differ diff --git a/docs/source/ezblock/img/DIYII6.png b/docs/source/ezblock/img/DIYII6.png new file mode 100644 index 0000000..0715e6f Binary files /dev/null and b/docs/source/ezblock/img/DIYII6.png differ diff --git a/docs/source/ezblock/img/DIYII7.png b/docs/source/ezblock/img/DIYII7.png new file mode 100644 index 0000000..8c8f33a Binary files /dev/null and b/docs/source/ezblock/img/DIYII7.png differ diff --git a/docs/source/ezblock/img/Obstacle.png b/docs/source/ezblock/img/Obstacle.png new file mode 100644 index 0000000..6548e3c Binary files /dev/null and b/docs/source/ezblock/img/Obstacle.png differ diff --git a/docs/source/ezblock/img/Obstacle1.png b/docs/source/ezblock/img/Obstacle1.png new file mode 100644 index 0000000..7361028 Binary files /dev/null and b/docs/source/ezblock/img/Obstacle1.png differ diff --git a/docs/source/ezblock/img/Obstacle2.png b/docs/source/ezblock/img/Obstacle2.png new file mode 100644 index 0000000..dec84b3 Binary files /dev/null and b/docs/source/ezblock/img/Obstacle2.png differ diff --git a/docs/source/ezblock/img/Obstacle3.png b/docs/source/ezblock/img/Obstacle3.png new file mode 100644 index 0000000..258f718 Binary files /dev/null and b/docs/source/ezblock/img/Obstacle3.png differ diff --git a/docs/source/ezblock/img/Obstacle4.png b/docs/source/ezblock/img/Obstacle4.png new file mode 100644 index 0000000..56537aa Binary files /dev/null and b/docs/source/ezblock/img/Obstacle4.png differ diff --git a/docs/source/ezblock/img/Obstacle5.png b/docs/source/ezblock/img/Obstacle5.png new file mode 100644 index 0000000..c8dbddd Binary files /dev/null and b/docs/source/ezblock/img/Obstacle5.png differ diff --git a/docs/source/ezblock/img/Obstacle6.png b/docs/source/ezblock/img/Obstacle6.png new file mode 100644 index 0000000..a3593ee Binary files /dev/null and b/docs/source/ezblock/img/Obstacle6.png differ diff --git a/docs/source/ezblock/img/Obstacle7.png b/docs/source/ezblock/img/Obstacle7.png new file mode 100644 index 0000000..46316d5 Binary files /dev/null and b/docs/source/ezblock/img/Obstacle7.png differ diff --git a/docs/source/ezblock/img/confuse.png b/docs/source/ezblock/img/confuse.png new file mode 100644 index 0000000..9975f2e Binary files /dev/null and b/docs/source/ezblock/img/confuse.png differ diff --git a/docs/source/ezblock/img/control.png b/docs/source/ezblock/img/control.png new file mode 100644 index 0000000..961d2b6 Binary files /dev/null and b/docs/source/ezblock/img/control.png differ diff --git a/docs/source/ezblock/img/control1.png b/docs/source/ezblock/img/control1.png new file mode 100644 index 0000000..b845582 Binary files /dev/null and b/docs/source/ezblock/img/control1.png differ diff --git a/docs/source/ezblock/img/control2.png b/docs/source/ezblock/img/control2.png new file mode 100644 index 0000000..efc6a2c Binary files /dev/null and b/docs/source/ezblock/img/control2.png differ diff --git a/docs/source/ezblock/img/control3.png b/docs/source/ezblock/img/control3.png new file mode 100644 index 0000000..8f6b168 Binary files /dev/null and b/docs/source/ezblock/img/control3.png differ diff --git a/docs/source/ezblock/img/control4.png b/docs/source/ezblock/img/control4.png new file mode 100644 index 0000000..e152743 Binary files /dev/null and b/docs/source/ezblock/img/control4.png differ diff --git a/docs/source/ezblock/img/control5.png b/docs/source/ezblock/img/control5.png new file mode 100644 index 0000000..a717089 Binary files /dev/null and b/docs/source/ezblock/img/control5.png differ diff --git a/docs/source/ezblock/img/dance.png b/docs/source/ezblock/img/dance.png new file mode 100644 index 0000000..b2361dd Binary files /dev/null and b/docs/source/ezblock/img/dance.png differ diff --git a/docs/source/ezblock/img/dance2.png b/docs/source/ezblock/img/dance2.png new file mode 100644 index 0000000..dcccb6e Binary files /dev/null and b/docs/source/ezblock/img/dance2.png differ diff --git a/docs/source/ezblock/img/dance3.png b/docs/source/ezblock/img/dance3.png new file mode 100644 index 0000000..8d67324 Binary files /dev/null and b/docs/source/ezblock/img/dance3.png differ diff --git a/docs/source/ezblock/img/dance4.png b/docs/source/ezblock/img/dance4.png new file mode 100644 index 0000000..9929e9e Binary files /dev/null and b/docs/source/ezblock/img/dance4.png differ diff --git a/docs/source/ezblock/img/dance5.png b/docs/source/ezblock/img/dance5.png new file mode 100644 index 0000000..e2fbfb8 Binary files /dev/null and b/docs/source/ezblock/img/dance5.png differ diff --git a/docs/source/ezblock/img/dance6.png b/docs/source/ezblock/img/dance6.png new file mode 100644 index 0000000..545cdb0 Binary files /dev/null and b/docs/source/ezblock/img/dance6.png differ diff --git a/docs/source/ezblock/img/dance7.png b/docs/source/ezblock/img/dance7.png new file mode 100644 index 0000000..d68700b Binary files /dev/null and b/docs/source/ezblock/img/dance7.png differ diff --git a/docs/source/ezblock/img/dance8.png b/docs/source/ezblock/img/dance8.png new file mode 100644 index 0000000..b34d335 Binary files /dev/null and b/docs/source/ezblock/img/dance8.png differ diff --git a/docs/source/ezblock/img/emotional.png b/docs/source/ezblock/img/emotional.png new file mode 100644 index 0000000..b4edc3b Binary files /dev/null and b/docs/source/ezblock/img/emotional.png differ diff --git a/docs/source/ezblock/img/emotional1.png b/docs/source/ezblock/img/emotional1.png new file mode 100644 index 0000000..294fc83 Binary files /dev/null and b/docs/source/ezblock/img/emotional1.png differ diff --git a/docs/source/ezblock/img/emotional2.png b/docs/source/ezblock/img/emotional2.png new file mode 100644 index 0000000..7edd771 Binary files /dev/null and b/docs/source/ezblock/img/emotional2.png differ diff --git a/docs/source/ezblock/img/fear.png b/docs/source/ezblock/img/fear.png new file mode 100644 index 0000000..93da9a6 Binary files /dev/null and b/docs/source/ezblock/img/fear.png differ diff --git a/docs/source/ezblock/img/fight.png b/docs/source/ezblock/img/fight.png new file mode 100644 index 0000000..d7a11b1 Binary files /dev/null and b/docs/source/ezblock/img/fight.png differ diff --git a/docs/source/ezblock/img/fight0.png b/docs/source/ezblock/img/fight0.png new file mode 100644 index 0000000..26e43dc Binary files /dev/null and b/docs/source/ezblock/img/fight0.png differ diff --git a/docs/source/ezblock/img/fight1.png b/docs/source/ezblock/img/fight1.png new file mode 100644 index 0000000..8390e1b Binary files /dev/null and b/docs/source/ezblock/img/fight1.png differ diff --git a/docs/source/ezblock/img/fight2.png b/docs/source/ezblock/img/fight2.png new file mode 100644 index 0000000..a44d21e Binary files /dev/null and b/docs/source/ezblock/img/fight2.png differ diff --git a/docs/source/ezblock/img/fight3.png b/docs/source/ezblock/img/fight3.png new file mode 100644 index 0000000..903beef Binary files /dev/null and b/docs/source/ezblock/img/fight3.png differ diff --git a/docs/source/ezblock/img/fight4.png b/docs/source/ezblock/img/fight4.png new file mode 100644 index 0000000..473010a Binary files /dev/null and b/docs/source/ezblock/img/fight4.png differ diff --git a/docs/source/ezblock/img/fightx.png b/docs/source/ezblock/img/fightx.png new file mode 100644 index 0000000..62bffe2 Binary files /dev/null and b/docs/source/ezblock/img/fightx.png differ diff --git a/docs/source/ezblock/img/happy.png b/docs/source/ezblock/img/happy.png new file mode 100644 index 0000000..8cdbcb6 Binary files /dev/null and b/docs/source/ezblock/img/happy.png differ diff --git a/docs/source/ezblock/img/move1.png b/docs/source/ezblock/img/move1.png new file mode 100644 index 0000000..ed87fe7 Binary files /dev/null and b/docs/source/ezblock/img/move1.png differ diff --git a/docs/source/ezblock/img/move2.png b/docs/source/ezblock/img/move2.png new file mode 100644 index 0000000..b9be0bf Binary files /dev/null and b/docs/source/ezblock/img/move2.png differ diff --git a/docs/source/ezblock/img/move3.png b/docs/source/ezblock/img/move3.png new file mode 100644 index 0000000..6b7644a Binary files /dev/null and b/docs/source/ezblock/img/move3.png differ diff --git a/docs/source/ezblock/img/move4.png b/docs/source/ezblock/img/move4.png new file mode 100644 index 0000000..517f076 Binary files /dev/null and b/docs/source/ezblock/img/move4.png differ diff --git a/docs/source/ezblock/img/move5.png b/docs/source/ezblock/img/move5.png new file mode 100644 index 0000000..de4673c Binary files /dev/null and b/docs/source/ezblock/img/move5.png differ diff --git a/docs/source/ezblock/img/move6.png b/docs/source/ezblock/img/move6.png new file mode 100644 index 0000000..f4a11a9 Binary files /dev/null and b/docs/source/ezblock/img/move6.png differ diff --git a/docs/source/ezblock/img/move7.png b/docs/source/ezblock/img/move7.png new file mode 100644 index 0000000..df2f984 Binary files /dev/null and b/docs/source/ezblock/img/move7.png differ diff --git a/docs/source/ezblock/img/move8.png b/docs/source/ezblock/img/move8.png new file mode 100644 index 0000000..b1d481d Binary files /dev/null and b/docs/source/ezblock/img/move8.png differ diff --git a/docs/source/ezblock/img/move9.png b/docs/source/ezblock/img/move9.png new file mode 100644 index 0000000..7ee5135 Binary files /dev/null and b/docs/source/ezblock/img/move9.png differ diff --git a/docs/source/ezblock/img/no_touch.png b/docs/source/ezblock/img/no_touch.png new file mode 100644 index 0000000..b95f3f8 Binary files /dev/null and b/docs/source/ezblock/img/no_touch.png differ diff --git a/docs/source/ezblock/img/pin11_connect.png b/docs/source/ezblock/img/pin11_connect.png new file mode 100644 index 0000000..6baabbb Binary files /dev/null and b/docs/source/ezblock/img/pin11_connect.png differ diff --git a/docs/source/ezblock/img/sad.png b/docs/source/ezblock/img/sad.png new file mode 100644 index 0000000..3c9dbc6 Binary files /dev/null and b/docs/source/ezblock/img/sad.png differ diff --git a/docs/source/ezblock/img/servo_arm.png b/docs/source/ezblock/img/servo_arm.png new file mode 100644 index 0000000..c59a460 Binary files /dev/null and b/docs/source/ezblock/img/servo_arm.png differ diff --git a/docs/source/ezblock/img/shy.png b/docs/source/ezblock/img/shy.png new file mode 100644 index 0000000..56b64b3 Binary files /dev/null and b/docs/source/ezblock/img/shy.png differ diff --git a/docs/source/ezblock/lets_fight_warrior_ez.rst b/docs/source/ezblock/lets_fight_warrior_ez.rst new file mode 100644 index 0000000..948714a --- /dev/null +++ b/docs/source/ezblock/lets_fight_warrior_ez.rst @@ -0,0 +1,33 @@ +Let\'s fight! Warrior! +======================= + +In this project, we will make Pisloth a warrior who will provoke you, and it will rush towards you full of fighting spirit. + +**TIPS** + +For the distance between Pisloth and the obstacle, we can set a range of 5 to 40 meters. In this range Pisloth will execute the forward action in a loop + +.. image:: img/fight1.png + :width: 350 + +You can use this block to set up an endless loop. + +.. image:: img/fight2.png + :width: 400 + +This block can jump out of the current loop and enter the next loop. + +.. image:: img/fight3.png + :width: 350 + +This block can jump out of the entire loop. + +.. image:: img/fight4.png + :width: 500 + +When the pisloth is too far away from the obstacle or the data is not read due to wire problems, the detected distance will be less than 0. Such a combination of blocks can help us ignore these interferences. + + +**EXAMPLE** + +.. image:: img/fight.png \ No newline at end of file diff --git a/docs/source/ezblock/move_ez.rst b/docs/source/ezblock/move_ez.rst new file mode 100644 index 0000000..9307691 --- /dev/null +++ b/docs/source/ezblock/move_ez.rst @@ -0,0 +1,59 @@ +Move +======== + +First we need to know how to make PiSloth move. Here, we let it perform the five actions of “forward”, “backward”, “turn left”, “turn right”, and “stop” in order. + +Before that, you can learn the basic usage of Ezblock Studio from here. + +* `Quick User Guide for Ezblock 3 `_ + +* `How to Create a New Project? `_ + +**TIPS** + +.. image:: img/move8.png + :width: 150 + +In ezBlock, the program first executes the code in the block, mainly for some initialization. + +.. image:: img/move9.png + :width: 150 + +In ezBlock, the program is mainly placed in this block, indicating that the program executes in a loop + +.. image:: img/move1.png + :width: 500 + +This block causes pisloth to move forward three times at 90% speed. + +.. image:: img/move2.png + :width: 500 + +This block causes pisloth to move backwards four times at 95% speed. + +.. image:: img/move3.png + :width: 500 + +This block causes pisloth to move to the left three times at 95% speed. + +.. image:: img/move4.png + :width: 500 + +This block causes pisloth to move to the right five times at 95% speed. + +You can click the drop-down arrow to switch the direction Pisloth moves. + +.. image:: img/move5.png + :width: 500 + +Let PiSloth stop. + +.. image:: img/move7.png + :width: 250 + +make the program wait one second. + +**EXAMPLE** + +.. image:: img/move6.png + :width: 500 \ No newline at end of file diff --git a/docs/source/ezblock/obstacle_avoidance_ez.rst b/docs/source/ezblock/obstacle_avoidance_ez.rst new file mode 100644 index 0000000..ce63573 --- /dev/null +++ b/docs/source/ezblock/obstacle_avoidance_ez.rst @@ -0,0 +1,44 @@ +Obstacle Avoidance +===================== + +In this project, Pisloth will use ultrasonic modules to detect obstacles ahead. When Pisloth detects an obstacle, he gives a signal, then backs away and looks for another way to move forward. + +**TIPS** + +.. image:: img/Obstacle1.png + :width: 400 + +You can directly use this block to read the distance to the obstacle right ahead. + +To achieve conditional judgment of “if” type, you need to use an if do block. + +.. image:: img/Obstacle2.png + :width: 400 + +When you need to implement multiple conditional judgments, you will have to change if do into if else do or else if do. This can be achieved by clicking on the setting icon. + +.. image:: img/Obstacle3.png + +You need to use a conditional statements block in conjunction with if do. Judging conditions can be “=”, “>”, “<”, ” ≥ “, ” ≤ “, ” ≠ “. + +.. image:: img/Obstacle6.png + +Do nothing here means pass. + +.. image:: img/Obstacle4.png + :width: 120 + +A number block. + +.. image:: img/Obstacle5.png + +This block can emit some preset sound effects. The range of volume is 1~100. + +.. image:: img/Obstacle7.png + +Pause the program for 0.2 seconds. + +**EXAMPLE** + +.. image:: img/Obstacle.png + diff --git a/docs/source/ezblock/play_with_ezblock.rst b/docs/source/ezblock/play_with_ezblock.rst new file mode 100644 index 0000000..7684f7b --- /dev/null +++ b/docs/source/ezblock/play_with_ezblock.rst @@ -0,0 +1,21 @@ +Play with Ezblock +=========================== + +**Ezblock** is a development platform developed by SunFounder designed for beginners to lower the barriers to getting started with Raspberry Pi. +It has two programming languages: Graphical and Python, and available on almost all different types of devices. +With Bluetooth and Wi-Fi support, you can download code, remote control a Raspberry Pi, on Ezblock Studio. + +Ezblock has been upgraded to the third version. + +Compared with the Bluetooth communication of Ezblock 2, Ezblock 3 uses Websocket to communicate, which is much faster. + +To make it faster and easier to use, we have optimized the connection and usage process so that you can learn programming quickly with Ezblock Studio. + +So the rest of the content is done with Ezblock 3. + +.. toctree:: + :maxdepth: 1 + + quick_guide_on_ezblock + projects_ezblock + diff --git a/docs/source/ezblock/projects_ezblock.rst b/docs/source/ezblock/projects_ezblock.rst new file mode 100644 index 0000000..7851fff --- /dev/null +++ b/docs/source/ezblock/projects_ezblock.rst @@ -0,0 +1,24 @@ +Projects +========================= + +Here, we show you the projects of playing PiSloth on Ezblock Studio. If you are new to these, +you can try to write the corresponding function according to TIPS, or directly +use the reference code in Example. We suggest you do it yourself and experience the +fun of challenges. + +If you don't want to write these projects one by one, we have uploaded them to Ezblock Studio's Examples page and you can run them directly or edit them and run them later. + +Also on the Examples page you can choose Block or Python language, but in the projects below, only the Block language is explained. + + +.. toctree:: + + move_ez + obstacle_avoidance_ez + dont_touch_me_ez + lets_fight_warrior_ez + emotional_pisloth_ez + dance_ez + remote_control_ez + diy_action_ez + diy_action2_ez diff --git a/docs/source/ezblock/quick_guide_on_ezblock.rst b/docs/source/ezblock/quick_guide_on_ezblock.rst new file mode 100644 index 0000000..66145a3 --- /dev/null +++ b/docs/source/ezblock/quick_guide_on_ezblock.rst @@ -0,0 +1,36 @@ +Quick Guide on Ezblock +=========================== + +We've put all the detailed tutorials for Ezblock 3 in `Ezblock Studio 3 `_. + +Before Assembling +-------------------- + +But before assembling, you only need to complete `Download and Install Ezblock OS `_. + +After burning the Ezblock system, P11 was set to calibrate the servo angle to 0°. To make sure you can see that the servo has been set to 0°, you can insert a rocker arm in the servo shaft first and then turn the servo to another angle. + +.. image:: img/servo_arm.png + +Now follow the diagram below and insert the servo to the P11 position. + +.. image:: img/pin11_connect.png + :width: 600 + +So now if the servo arm returns after the servo arm returns, this function will take effect. If not, press the **RESET button** to restart. + +.. note:: + Before assembling each servo, you need to plug the servo pin into P11 and keep the power on. + + This function will be invalid after writing any programs. + + + +Before Programming +------------------------ + + +Before you program, you need to download Ezblock Studio. The current download is Ezblock 2, and you will need to manually upgrade to Ezblock 3. + +For detailed tutorials, please refer to: `Install Ezblock Studio `_. + diff --git a/docs/source/ezblock/remote_control_ez.rst b/docs/source/ezblock/remote_control_ez.rst new file mode 100644 index 0000000..8f676ab --- /dev/null +++ b/docs/source/ezblock/remote_control_ez.rst @@ -0,0 +1,27 @@ +Remote Control +================== + +Next we control the PiSloth with your device. You will need to use the D-pad and button at the Remote Control page. + +* `How to Use the Remote Control Function? `_ + +**TIPS** + +.. image:: img/control3.png + +To use the remote control function, you need to enter the Remote Control page from the left side of main page. +Then drag one D-pad and four buttons to the central area,you can change its ID, name or delete it. + +.. image:: img/control4.png + +After dragging and dropping a widget on the remote control page, the code block of the relevant widget will appear on this page. + +.. image:: img/control5.png + :width: 500 + +You can use this block to indicate that the UP button of the arrow keys is pressed +, and click the drop-down menu to switch to the down, left or right. + +**EXAMPLE** + +.. image:: img/control.png \ No newline at end of file diff --git a/docs/source/img/RPi_imager.png b/docs/source/img/RPi_imager.png new file mode 100644 index 0000000..6988ff3 Binary files /dev/null and b/docs/source/img/RPi_imager.png differ diff --git a/docs/source/img/RPi_imager2.png b/docs/source/img/RPi_imager2.png new file mode 100644 index 0000000..64d6339 Binary files /dev/null and b/docs/source/img/RPi_imager2.png differ diff --git a/docs/source/img/RPi_imager3.png b/docs/source/img/RPi_imager3.png new file mode 100644 index 0000000..d5377f4 Binary files /dev/null and b/docs/source/img/RPi_imager3.png differ diff --git a/docs/source/img/RPi_imager4.png b/docs/source/img/RPi_imager4.png new file mode 100644 index 0000000..3252723 Binary files /dev/null and b/docs/source/img/RPi_imager4.png differ diff --git a/docs/source/img/boot_disk.png b/docs/source/img/boot_disk.png new file mode 100644 index 0000000..4635240 Binary files /dev/null and b/docs/source/img/boot_disk.png differ diff --git a/docs/source/img/examples23.png b/docs/source/img/examples23.png new file mode 100644 index 0000000..4e58abf Binary files /dev/null and b/docs/source/img/examples23.png differ diff --git a/docs/source/img/install_py.png b/docs/source/img/install_py.png new file mode 100644 index 0000000..bace564 Binary files /dev/null and b/docs/source/img/install_py.png differ diff --git a/docs/source/img/picar_x_pic7.png b/docs/source/img/picar_x_pic7.png new file mode 100644 index 0000000..2677a8c Binary files /dev/null and b/docs/source/img/picar_x_pic7.png differ diff --git a/docs/source/img/pin11_connect.png b/docs/source/img/pin11_connect.png new file mode 100644 index 0000000..40c482e Binary files /dev/null and b/docs/source/img/pin11_connect.png differ diff --git a/docs/source/img/ping_rpi.png b/docs/source/img/ping_rpi.png new file mode 100644 index 0000000..722b5de Binary files /dev/null and b/docs/source/img/ping_rpi.png differ diff --git a/docs/source/img/secure_warning.png b/docs/source/img/secure_warning.png new file mode 100644 index 0000000..d75f64e Binary files /dev/null and b/docs/source/img/secure_warning.png differ diff --git a/docs/source/img/servo_arm.png b/docs/source/img/servo_arm.png new file mode 100644 index 0000000..fd00051 Binary files /dev/null and b/docs/source/img/servo_arm.png differ diff --git a/docs/source/img/setup_python1.png b/docs/source/img/setup_python1.png new file mode 100644 index 0000000..60a69f1 Binary files /dev/null and b/docs/source/img/setup_python1.png differ diff --git a/docs/source/img/slide_to_power.png b/docs/source/img/slide_to_power.png new file mode 100644 index 0000000..2f8c8fa Binary files /dev/null and b/docs/source/img/slide_to_power.png differ diff --git a/docs/source/img/ssh.png b/docs/source/img/ssh.png new file mode 100644 index 0000000..f918656 Binary files /dev/null and b/docs/source/img/ssh.png differ diff --git a/docs/source/img/ssh_pi_ip.png b/docs/source/img/ssh_pi_ip.png new file mode 100644 index 0000000..8d86666 Binary files /dev/null and b/docs/source/img/ssh_pi_ip.png differ diff --git a/docs/source/img/ssh_pi_terminal.png b/docs/source/img/ssh_pi_terminal.png new file mode 100644 index 0000000..0b43e48 Binary files /dev/null and b/docs/source/img/ssh_pi_terminal.png differ diff --git "a/docs/source/img/\345\276\256\344\277\241\346\210\252\345\233\276_20210713140636.png" "b/docs/source/img/\345\276\256\344\277\241\346\210\252\345\233\276_20210713140636.png" new file mode 100644 index 0000000..ba57c15 Binary files /dev/null and "b/docs/source/img/\345\276\256\344\277\241\346\210\252\345\233\276_20210713140636.png" differ diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..8e8a470 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,40 @@ +.. pisloth documentation master file, created by + sphinx-quickstart on Fri May 28 18:11:49 2021. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to PiSloth's documentation! +=================================== + + +Thanks for choosing our PiSloth. + +PiSloth is an AI . . . . . . + +In this tutorial, a list, assembly diagram, introduction to Robot HAT, and programming of PiSloth are included. + +The programming part is divided into two chapters: :ref:`Play with Ezblock` & :ref:`Play with Python`, and each of them can get you stated on making PiSloth work in way you want. + +Ezblock Studio is a development platform developed by SunFounder designed for beginners to lower the barriers to getting started with Raspberry Pi. It has two programming languages: Graphical and Python, and available on almost all different types of devices. With Bluetooth and Wi-Fi support, you can download code, remote control a Raspberry Pi, on Ezblock Studio. + +If you want to program in Python, then you will need to learn some basic Python programming skills and basic knowledge of Raspbeer Pi OS. + + + +**Content** + +.. toctree:: + :maxdepth: 2 + + introduction + list_and_assembly + about_robot_hat + ezblock/play_with_ezblock + play_with_python + faq + +Copyright Notice +-------------------------- + +All contents including but not limited to texts, images, and code in this manual are owned by the SunFounder Company. You should only use it for personal study,investigation, enjoyment, or other non-commercial or nonprofit purposes, under therelated regulations and copyrights laws, without infringing the legal rights of the author and relevant right holders. For any individual or organization that uses these for commercial profit without permission, the Company reserves the right to take legal action. + diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/source/lets_fight_warrior.rst b/docs/source/lets_fight_warrior.rst new file mode 100644 index 0000000..703d154 --- /dev/null +++ b/docs/source/lets_fight_warrior.rst @@ -0,0 +1,79 @@ +Let\'s fight! Warrior! +======================= + +In this project, we will make Pisloth a warrior who will provoke you, and it will rush towards you full of fighting spirit. + +**Code** + +.. code:: python + + from pisloth import Sloth + from robot_hat import Music + from robot_hat import Ultrasonic + from robot_hat import Pin + import time + import os + + + music = Music() + + sloth = Sloth([1,2,3,4]) + sloth.set_offset([0,0,0,0]) + sonar = Ultrasonic(Pin("D0") ,Pin("D1")) + + alert_distance = 40 + contact_distance = 5 + + def main(): + distance = sonar.read() + if distance <= alert_distance and distance >= contact_distance : + try: + music.sound_effect_play('./sounds/battle.wav') + music.background_music('./musics/attack.mp3') + music.music_set_volume(20) + except Exception as e: + print(e) + while True: + distance = sonar.read() + print(distance) + if distance< 0: + continue + if distance<=contact_distance: + break + sloth.do_action('forward', 1,95) + sloth.do_action('stand', 1, 90) + time.sleep(1) + + + if __name__ == "__main__": + while True: + main() + +**How it works?** + +The ultrasonic module reads the distance between the obstacle and the Pisloth, when the distance is less than or equal to alert_distance and +When greater than or equal to ``contact_distance``, Pisloth will play ``warning.wav`` and ``attack.mp3``, and let him cycle forward to charge, when the distance is less than or equal to ``contact_distance``, it will jump out of the loop and stop charging. + +.. code:: python + + distance = sonar.read() + if distance <= alert_distance and distance >= contact_distance : + try: + music.sound_effect_play('./sounds/battle.wav') + music.background_music('./musics/attack.mp3') + music.music_set_volume(20) + except Exception as e: + print(e) + while True: + distance = sonar.read() + print(distance) + if distance< 0: + continue + if distance<=contact_distance: + break + sloth.do_action('forward', 1,95) + sloth.do_action('stand', 1, 90) + time.sleep(1) + +.. note:: + When the ultrasonic module is too far away from the obstacle or the data is not read due to the wire problem, the situation of ``distance<0`` will appear, you can use it ``continue`` ignore these disturbances and continue the cycle. \ No newline at end of file diff --git a/docs/source/move.rst b/docs/source/move.rst new file mode 100644 index 0000000..778be35 --- /dev/null +++ b/docs/source/move.rst @@ -0,0 +1,63 @@ +Move +======== + +The basic functions of Pisloth have been encapsulated in ``sloth.py`` under the path ``/home/pi/pisloth``, chiefly controlling the servo. + +Let the Pisloth move forward, left and right, which requires us to use commands to drive the four servos on the Pisloth leg. + +You can open ``move.py`` in the folder of the ``example`` with command ``sudo pyrhon3 move.py`` or directly copy the following code to the Python IDE to run. + +**Code** + +.. code:: Python + + from pisloth import Sloth + + sloth = Sloth([1,2,3,4]) + sloth.set_offset([0,0,0,0]) + + def main(): + sloth.do_action('turn left', 7, 90) + sloth.do_action('forward', 5, 90) + sloth.do_action('turn right', 7, 90) + sloth.do_action('forward', 5, 90) + + + if __name__ == ``__main__``: + while True: + main() + +**How it works?** + +First, import the libraries to support the basic functionality of pisloth. + +.. code:: python + + from pisloth import Sloth + + +Then instantiate the Pisloth class. + +.. code:: python + + sloth = Sloth([1,2,3,4]) + sloth.set_offset([0,0,0,0]) + +Finally use the following function to make Pisloth move. + +.. code:: python + + sloth.do_action('turn left', 7, 90) + sloth.do_action('forward', 5, 90) + sloth.do_action('turn right', 7, 90) + sloth.do_action('forward', 5, 90) + +In general, all actions of PiSloth can be implemented with the function ``sloth.do_action(motion_name,step=1,speed=None,bpm=None)''. It has four parameters: + +* ``motion_name`` is the preset specific actions, including: ``forward`` , ``turn right`` , ``turn left`` , ``backward`` , ``stand`` , ``moon walk left`` , ``moon walk right`` , ``hook`` , ``big swing`` , ``swing`` , ``walk boldly`` , ``walk backward boldly`` , ``walk shyly`` , ``walk backward shyly`` , ``stomp rihgt`` , ``stomp left`` , ``close`` , ``open`` , ``tiptoe left`` , ``tiptoe right`` , ``fall left`` , ``fall right`` + +* ``step`` represents the number of actions, the default number is 1. + +* ``speed`` means the speed of the action, the default is 50, the range is 0~100. + +* ``bpm`` means rhythm, we will use it later in the dance course. diff --git a/docs/source/obstacle_avoidance.rst b/docs/source/obstacle_avoidance.rst new file mode 100644 index 0000000..f567906 --- /dev/null +++ b/docs/source/obstacle_avoidance.rst @@ -0,0 +1,86 @@ +Obstacle Avoidance +===================== + +In this project, Pisloth will use an ultrasonic module to detect obstacles in front. When an obstacle is detected, Pisloth will be taken aback, and then find another way to move on. + +**Code** + +.. code:: python + + from pisloth import Sloth + from robot_hat import TTS, Music + from robot_hat import Ultrasonic + from robot_hat import Pin + import time + import os + + tts = TTS() + music = Music() + + sloth = Sloth([1,2,3,4]) + sloth.set_offset([0,0,0,0]) + sonar = Ultrasonic(Pin("D0") ,Pin("D1")) + + alert_distance = 10 + + def main(): + distance = sonar.read() + if distance < 0: + pass + elif distance <= alert_distance: + try: + music.sound_effect_threading('./sounds/sign.wav') + except Exception as e: + print(e) + sloth.do_action('hook', 1,95) + sloth.do_action('stand', 1,95) + time.sleep(0.2) + sloth.do_action('turn left',7,95) + sloth.do_action('stand', 1,95) + time.sleep(0.2) + else : + sloth.do_action('forward', 1,95) + + + if __name__ == "__main__": + while True: + main() + +**How it works?** + +You can get the distance only by calling the Ultrasonic library. + +.. code:: python + + from robot_hat import Ultrasonic + +Then initialize the ultrasonic pins. + +.. code:: python + + sonar = Ultrasonic(Pin("D0") ,Pin("D1")) + +Read the distance detected by the ultrasonic, set the condition to judge, when the distance is less than ``alert_distance``, play the audio file ``sign.wav`` +and let Pisloth do the ``hook``, ``stand``, ``turn left``, ``stand`` actions in sequence. When the distance is greater than ``alert_distance``, +Pisloth does the ``forward`` action. + +.. code:: python + + distance = sonar.read() + if distance < 0: + pass + elif distance <= alert_distance: + try: + music.sound_effect_threading('./sounds/sign.wav') + except Exception as e: + print(e) + sloth.do_action('hook', 1,95) + sloth.do_action('stand', 1,95) + time.sleep(0.2) + sloth.do_action('turn left',7,95) + sloth.do_action('stand', 1,95) + time.sleep(0.2) + else : + sloth.do_action('forward', 1,95) + + diff --git a/docs/source/play_with_python.rst b/docs/source/play_with_python.rst new file mode 100644 index 0000000..5ed7e29 --- /dev/null +++ b/docs/source/play_with_python.rst @@ -0,0 +1,16 @@ +Play with Python +==================== + +If you want to program in python, then you will need to learn some basic Python programming skills and basic knowledge of Raspbeer Pi OS, please configure the Raspberry Pi first according to :ref:`Quick Guide on Python`. + + +.. toctree:: + + quick_guide_on_python + + +After the assembly is complete, you can try to run the projects below. + +.. toctree:: + + projects_python \ No newline at end of file diff --git a/docs/source/projects_python.rst b/docs/source/projects_python.rst new file mode 100644 index 0000000..5b3fec4 --- /dev/null +++ b/docs/source/projects_python.rst @@ -0,0 +1,34 @@ +Projects +============ + +.. toctree:: + + move + dance + obstacle_avoidance + dont_touch_me + lets_fight_warrior + emotional_pisloth + remote_control + diy_action + diy_action2 + + +**How to Run the Code File** + + +Open the **example** folder you downloaded earlier, and you will see some python code in it. + +.. code-block:: + + cd /home/pi/pisloth/examples + +You can run the python codes by the following command: + +.. code-block:: + + sudo python3 FILENAME.py + +You can start the corresponding code by changing ``FILENAME.py`` to a specific example. + + diff --git a/docs/source/quick_guide_on_python.rst b/docs/source/quick_guide_on_python.rst new file mode 100644 index 0000000..4d7849a --- /dev/null +++ b/docs/source/quick_guide_on_python.rst @@ -0,0 +1,229 @@ +Quick Guide on Python +========================== + +This section is to teach you how to install Raspberry Pi OS, configure wifi to Raspberry Pi, remote access to Raspberry Pi to run the corresponding code. + +If you are familiar with Raspberry Pi and can open the command line successfully, then you can skip the first 3 parts and start directly from :ref:`download_code` all the way to :ref:`run_servo_zeroing.py`. + +.. note:: + + servo_zeroing.py is what makes the P11 output a stable square wave that sets the servo angle to 0. You need to run this code before assembling each servo. + + +* :ref:`install_raspberry_pi_os` +* :ref:`configure_wifi_file` +* :ref:`remote_access_by_ssh` +* :ref:`download_code` +* :ref:`run_install.py` +* :ref:`run_servo_zeroing.py` + + +.. _install_raspberry_pi_os: + +Install Raspberry Pi OS +------------------------------- +You are required to prepare the following items: + +* Personal computer +* Raspberry Pi +* Card reader +* SD card +* Power supply + +1. Insert the SD card (with card reader) into the PC and open `Raspberry Pi Imager `_ . + + .. image:: img/RPi_imager.png + +#. Click CHOOSE OS and Select the first one (Recommended) to download and write. + + .. image:: img/RPi_imager2.png + +#. Click CHOOSE SD CARD. Select the one you just insert into PC. + + .. image:: img/RPi_imager3.png + +#. Write! + + .. image:: img/RPi_imager4.png + +.. note:: + + For more ways to install Raspberry Pi OS, please click https://www.raspberrypi.org/documentation/installation/. + +.. note:: + If you have a display, getting into the Raspberry Pi and opening Terminal to run the code is a simple task for you, for a detailed tutorial please refer to: `Setting up your Raspberry Pi `_ . + + If you do not have a display, you will need to follow the tutorial below to access the Raspberry Pi remotely via your own PC. + +.. _configure_wifi_file: + +Configure Wi-Fi File +----------------------------------------------- + +You will need to define a ``wpa_supplicant.conf`` file for your particular wireless network. Put this file in the ``boot`` folder, and when the Pi first boots, it will copy that file into the correct location and use those settings to start up wireless networking. + +Depending on the operating system and editor you are using, the file may have incorrect line breaks or the wrong file extension, so make sure the editor you are using can fix this. + +Linux expects the line feed (LF) newline character. For more information, see this `Wikipedia article `_ . + +.. image:: img/setup_python1.png + +.. code-block:: + + ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev + update_config=1 + country= + + network={ + ssid="" + psk="< Password for your wireless LAN>" + } + +.. warning:: + + * More information on the ``wpa_supplicant.conf`` file can be found in https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md. + * See `ISO_3166 - Wikipedia `_ for a list of 2 letter ISO 3166-1 country codes. + * Note that some older wireless dongles don't support 5GHz networks. + * For more ways to set up a wireless network, please click https://www.raspberrypi.org/documentation/configuration/wireless/README.md. + + +.. _remote_access_by_ssh: + +Remote Access by SSH +------------------------------------ + +**Enable SSH** + +You can access the command line of a Raspberry Pi remotely from another computer or device on the same network using SSH. + +The Raspberry Pi will act as a remote device: you can connect to it using a client on another machine. In this way, you only have access to the command line, not the full desktop environment. + +SSH can be enabled by placing a file named ``ssh``, without any extension, onto the ``boot`` partition of the SD card from another computer. When the Pi boots, it looks for the ssh file. If it is found, SSH is enabled and the file is deleted. The content of the file does not matter; it could contain text, or nothing at all. + +.. image:: img/ssh.png + +If you have loaded Raspberry Pi OS onto a blank SD card, you will have two partitions. The first one, which is the smaller one, is the boot partition. Place the file into this one. + +.. image:: img/boot_disk.png + +Now you can unplug the Micro SD, then plug it into the Raspberry Pi and power on the Raspberry Pi. + + +**Find the IP address** + +Any device connected to a Local Area Network is assigned an IP address. +In order to connect to your Raspberry Pi from another machine using SSH, you need to know the Pi's IP address. + +On Raspberry Pi OS, **multicast DNS** is supported out-of-the-box by the Avahi service. + +If your device supports mDNS, you can reach your Raspberry Pi by using its ``hostname`` and the ``.local`` suffix. The default hostname on a fresh Raspberry Pi OS install is ``raspberrypi``, so by default any Raspberry Pi running Raspberry Pi OS responds to: + + +.. code-block:: shell + + ping raspberrypi.local + + +.. image:: img/ping_rpi.png + +If the Raspberry Pi is reachable, ping will show its IP address: + +.. code-block:: shell + + Pinging raspberrypi.local [192.168.18.168] with 32 bytes of data: + Reply from 192.168.18.168: bytes=32 time=54ms TTL=64 + Reply from 192.168.18.168: bytes=32 time=1ms TTL=64 + Reply from 192.168.18.168: bytes=32 time=1ms TTL=64 + Reply from 192.168.18.168: bytes=32 time=2ms TTL=64 + + Ping statistics for 192.168.18.168: + Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), + Approximate round trip times in milli-seconds: + Minimum = 1ms, Maximum = 54ms, Average = 14ms + + +For more ways to find the IP address, please click https://www.raspberrypi.org/documentation/remote-access/ip-address.md . + + +**Remote Access** + +You can use SSH to connect to your Raspberry Pi from a **Windows 10** computer that is using **October 2018 Update or later** without having to use third-party clients. + +* For use SSH from a Linux computer, or a Mac, please click https://www.raspberrypi.org/documentation/remote-access/ssh/unix.md . +* For older version of Windows, please click https://www.raspberrypi.org/documentation/remote-access/ssh/windows.md . + +To connect to your Pi from a different computer, copy and paste the following command into the terminal window, but replace ```` with the IP address of the Raspberry Pi. + +.. code-block:: shell + + ssh pi@ + +.. image:: img/ssh_pi_ip.png + +When the connection works, you will see a security/authenticity warning, type ``yes`` to continue. You will only see this warning the first time you connect. + +.. image:: img/secure_warning.png + +.. warning:: + + In the event that your Pi has taken the IP address of a device to which your computer has connected before (even if this was on another network), you may be given a **warning** and asked to clear the record from your list of known devices. Following it and try to ssh again please. + +Next you will be prompted for the password for the user as which you are trying to connect: the default password for the pi user on Raspberry Pi OS is ``raspberry``. + +* When you input the password, the characters do not display on window accordingly, which is normal. What you need is to input the correct password. + +* For security reasons it is highly recommended to change the default password on the Raspberry Pi. You should now be able to see the Raspberry Pi prompt, which will be identical to the one found on the Raspberry Pi itself. + +.. image:: img/ssh_pi_terminal.png + +You are now connected to the Raspberry Pi remotely, and can execute commands in this terminal. + + +.. _download_code: + +Download Code +----------------- + +We can download the files by using ``git clone`` in the command line. + +Change directory to **/home/pi/** via `cd command `_ . + +.. code-block:: shell + + cd /home/pi/ + +Clone the repository from github via `git clone command `_ . + +.. code-block:: shell + + git clone -b v2.0 https://github.com/sunfounder/pisloth.git + +.. _run_install.py: + +Run install.py +----------------------------------- + +Enter the following two commands to run the ``install.py`` file in the ``ezb-pi`` folder. + +.. code-block:: shell + + cd ezb-pi + +.. code-block:: shell + + sudo python3 install.py + +This file will help you finish the installation of the required library and configuration of Raspberry Pi. + +.. image:: img/install_py.png + +.. warning:: + + For the reason of the network, ``install.py`` may encounter some **Errors** in some processes. If there is an error prompt, please check the network and re-run ``install.py`` until all processes show **Done** and prompt **Finished** at the end. + +This step will take a little time, so please be patient. After the file is fully executed and the prompt ``Finished`` is issued, please restart the Raspberry Pi. + +.. code-block:: shell + + sudo reboot + diff --git a/docs/source/remote_control.rst b/docs/source/remote_control.rst new file mode 100644 index 0000000..05649bf --- /dev/null +++ b/docs/source/remote_control.rst @@ -0,0 +1,127 @@ +Remote Control +================== + +In this project, we will learn how to use the keyboard to remotely control the Pisloth. You can control the Pisloth to move up, down, left, and right and speak through specific keys. +**Code** + +.. code:: python + + from pisloth import Sloth + from robot_hat import Music + from robot_hat import TTS + import sys + import tty + import termios + import time + + sloth = Sloth([1,2,3,4]) + tts = TTS() + music = Music() + sloth.set_offset([0,0,0,0]) + + def readchar(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + + manual = ''' + Press keys on keyboard to control PiSloth! + + W: Forward + A: Turn left + S: Backward + D: Turn right + 1: Sound effect: talk1 + 2: Sound effect: talk2 + 3: Sound effect: talk3 + 4: Sound effect: depress2 + Q: Say: "Oh hello there" + E: Say: "bye" + ESC: Quit + ''' + + def main(): + print(manual) + while True: + key = readkey() + # print(key) + if key == "w": + sloth.do_action('forward', 1, 90) + elif key == "a": + sloth.do_action('turn left', 1, 90) + elif key == "s": + sloth.do_action('backward', 1, 90) + elif key == "d": + sloth.do_action('turn right', 1, 90) + elif key == "1": + music.sound_effect_play('./sounds/talk1.wav') + elif key == "2": + music.sound_effect_play('./sounds/talk2.wav') + elif key == "3": + music.sound_effect_play('./sounds/talk3.wav') + elif key == "4": + music.sound_effect_play('./sounds/depress.wav') + elif key == "q": + tts.say("Oh hello there") + elif key == "e": + tts.say("bye") + elif key == chr(27): # 27 for ESC + break + time.sleep(0.05) + print("\nQuit") + + if __name__ == "__main__": + main() + +**How it works?** + +This function refers to the standard input stream and returns the first character of the data stream read. ``tty.setraw(sys.stdin.fileno)`` is to change the standard input stream to raw mode, that is, all characters +will not be escaped during transmission, including special characters. Before changing the mode, back up the original mode, and restore it after the change. ``old_settings = termios.tcgetattr(fd)'' and +``termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)'' plays the role of backup and restore. +.. code:: python + + def readchar(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + +Finally, according to reading the pressed keyboard character, let Pisloth do the actions we set, call the function of tts to speak or play the audio file prepared in advance + +.. code:: python + + key = readkey() + # print(key) + if key == "w": + sloth.do_action('forward', 1, 90) + elif key == "a": + sloth.do_action('turn left', 1, 90) + elif key == "s": + sloth.do_action('backward', 1, 90) + elif key == "d": + sloth.do_action('turn right', 1, 90) + elif key == "1": + music.sound_effect_play('./sounds/talk1.wav') + elif key == "2": + music.sound_effect_play('./sounds/talk2.wav') + elif key == "3": + music.sound_effect_play('./sounds/talk3.wav') + elif key == "4": + music.sound_effect_play('./sounds/depress.wav') + elif key == "q": + tts.say("Oh hello there") + elif key == "e": + tts.say("bye") + elif key == chr(27): # 27 for ESC + break \ No newline at end of file diff --git a/examples/1.move.py b/examples/1.move.py deleted file mode 100644 index cd76a79..0000000 --- a/examples/1.move.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/python3 -import sys -sys.path.append(r'/opt/ezblock') -from sloth import Sloth -__SLOTH__ = Sloth([1,2,3,4]) - -__SLOTH__.set_offset([0,0,0,0]) - - -def forever(): - __SLOTH__.do_action('turn left', 7, 100) - __SLOTH__.do_action('forward', 5, 100) - __SLOTH__.do_action('turn right', 7, 100) - __SLOTH__.do_action('forward', 5, 100) - -if __name__ == "__main__": - while True: - forever() \ No newline at end of file diff --git a/examples/2.dancing.py b/examples/2.dancing.py deleted file mode 100644 index 9bab9bb..0000000 --- a/examples/2.dancing.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python3 -import sys -sys.path.append(r'/opt/ezblock') -from sloth import Sloth -__SLOTH__ = Sloth([1,2,3,4]) - - -def forever(): - __SLOTH__.do_action('moon walk left', 2, 100) - __SLOTH__.do_action('moon walk right', 2, 100) - __SLOTH__.do_action('turn right', 1, 100) - __SLOTH__.do_action('shake left', 1, 100) - __SLOTH__.do_action('turn left', 1, 100) - __SLOTH__.do_action('shake right', 1, 100) - __SLOTH__.do_action('go up and down', 1, 100) - __SLOTH__.do_action('swing', 1, 100) - __SLOTH__.do_action('big swing', 1, 100) - __SLOTH__.do_action('stop', 1, 50) - -if __name__ == "__main__": - while True: - forever() \ No newline at end of file diff --git a/examples/3.obstacle_avoidance.py b/examples/3.obstacle_avoidance.py deleted file mode 100644 index 39da70f..0000000 --- a/examples/3.obstacle_avoidance.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/python3 -import sys -sys.path.append(r'/opt/ezblock') -from ezblock import Pin -from ezblock import Ultrasonic -from sloth import Sloth -__SLOTH__ = Sloth([1,2,3,4]) - -reference = None - -distance = None - -reference = 10 - -pin_D0 = Pin("D0") - -pin_D1 = Pin("D1") - - -def forever(): - global reference, distance - distance = Ultrasonic(pin_D0, pin_D1).read() - if distance >= reference: - __SLOTH__.do_action('forward', 1, 100) - else: - __SLOTH__.do_action('backward', 1, 100) - __SLOTH__.do_action('stop', 1, 100) - __SLOTH__.do_action('turn right', 1, 100) - __SLOTH__.do_action('stop', 1, 100) - -if __name__ == "__main__": - while True: - forever() \ No newline at end of file diff --git a/examples/4.wander.py b/examples/4.wander.py deleted file mode 100644 index a63d339..0000000 --- a/examples/4.wander.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/python3 -import sys -sys.path.append(r'/opt/ezblock') -import random -from sloth import Sloth -__SLOTH__ = Sloth([1,2,3,4]) -from ezblock import delay - -turn = None - -turn = ['turn left', 'turn right', 'stop'] - - -def forever(): - global turn - __SLOTH__.do_action(random.choice(turn), (random.randint(2, 7)), 100) - __SLOTH__.do_action('forward', (random.randint(4, 7)), 100) - __SLOTH__.do_action('stop', 1, 100) - delay((random.randint(4, 15) * 100)) - -if __name__ == "__main__": - while True: - forever() \ No newline at end of file diff --git a/examples/5.sound_effect.py b/examples/5.sound_effect.py deleted file mode 100644 index ab1ab5e..0000000 --- a/examples/5.sound_effect.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/python3 -import sys -sys.path.append(r'/opt/ezblock') -from ezblock import TTS -from Music import * - -count = None - -__tts__ = TTS() - - -def forever(): - global count - __tts__.say('Ready') - count = 3 - for count2 in range(3): - __tts__.say(count) - count = count - 1 - sound_effect_play('Weapon_Continue_Shooting.wav',50) - -if __name__ == "__main__": - while True: - forever() \ No newline at end of file diff --git a/examples/6.background_music.py b/examples/6.background_music.py deleted file mode 100644 index 57dcff4..0000000 --- a/examples/6.background_music.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/python3 -import sys -sys.path.append(r'/opt/ezblock') -from Music import * - -background_music('spry.mp3') - -def forever(): - music_set_volume(50) - -if __name__ == "__main__": - while True: - forever() \ No newline at end of file diff --git a/examples/7.diy_action.py b/examples/7.diy_action.py deleted file mode 100644 index c532dac..0000000 --- a/examples/7.diy_action.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/python3 -import sys -sys.path.append(r'/opt/ezblock') -from sloth import Sloth -__SLOTH__ = Sloth([1,2,3,4]) - -servoAngle = None - -"""Describe this function... - -""" - -def forever(): - global servoAngle - servoAngle = [0,0,0,0] #You can change the STEP of PiSloth by modifying the value of this list. - __SLOTH__.servo_write_all([(servoAngle[0]),(servoAngle[1]),(servoAngle[2]),(servoAngle[3])]) - -if __name__ == "__main__": - while True: - forever() \ No newline at end of file diff --git a/examples/8.diy_action_2.py b/examples/8.diy_action_2.py deleted file mode 100644 index a3314b0..0000000 --- a/examples/8.diy_action_2.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/python3 -import sys -sys.path.append(r'/opt/ezblock') -from sloth import Sloth -__SLOTH__ = Sloth([1,2,3,4]) -from ezblock import delay - -diyAction = None - -__SLOTH__.add_action("diyAction", [[0,(-50),0,50], [0,0,0,0], [0,50,0,(-50)], [0,0,0,0]]) -diyAction = "diyAction" - - -def forever(): - global diyAction - __SLOTH__.do_action(diyAction, 1, 80) - delay(1000) - -if __name__ == "__main__": - while True: - forever() \ No newline at end of file diff --git a/examples/avoid.py b/examples/avoid.py new file mode 100644 index 0000000..b422e45 --- /dev/null +++ b/examples/avoid.py @@ -0,0 +1,39 @@ + +from pisloth import Sloth +from robot_hat import TTS, Music +from robot_hat import Ultrasonic +from robot_hat import Pin +import time +import os + +tts = TTS() +music = Music() + +sloth = Sloth([1,2,3,4]) +sloth.set_offset([0,0,0,0]) +sonar = Ultrasonic(Pin("D0") ,Pin("D1")) + +alert_distance = 10 + +def main(): + distance = sonar.read() + if distance < 0: + pass + elif distance <= alert_distance: + try: + music.sound_effect_threading('./sounds/sign.wav') + except Exception as e: + print(e) + sloth.do_action('hook', 1,95) + sloth.do_action('stand', 1,95) + time.sleep(0.2) + sloth.do_action('turn left',7,95) + sloth.do_action('stand', 1,95) + time.sleep(0.2) + else : + sloth.do_action('forward', 1,95) + + +if __name__ == "__main__": + while True: + main() \ No newline at end of file diff --git a/examples/custom_action.py b/examples/custom_action.py new file mode 100644 index 0000000..eb2e8e3 --- /dev/null +++ b/examples/custom_action.py @@ -0,0 +1,17 @@ + +from pisloth import Sloth +import time + +sloth = Sloth([1,2,3,4]) +sloth.add_action("my_action", [ + [ 45,0 ,0, 0], + [0, 0, 0, 0] + ]) + +def main(): + sloth.do_action("my_action", 1, 80) + time.sleep(1) + +if __name__ == "__main__": + while True: + main() \ No newline at end of file diff --git a/examples/custom_step.py b/examples/custom_step.py new file mode 100644 index 0000000..bfd2984 --- /dev/null +++ b/examples/custom_step.py @@ -0,0 +1,98 @@ +from pisloth import Sloth +# from robot_hat import Music +# from robot_hat import TTS +from robot_hat import PWM +from robot_hat import Servo + +import sys +import tty +import termios +import time + +sloth = Sloth([1,2,3,4]) +# tts = TTS() +# music = Music() +sloth.set_offset([0,0,0,0]) + +right_up_servo = Servo(PWM('P0')) +right_down_servo = Servo(PWM('P1')) +left_up_servo = Servo(PWM('P2')) +left_down_servo = Servo(PWM('P3')) + + +def readchar(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + +def readkey(getchar_fn=None): + getchar = getchar_fn or readchar + c1 = getchar() + if ord(c1) != 0x1b: + return c1 + c2 = getchar() + if ord(c2) != 0x5b: + return c1 + c3 = getchar() + return chr(0x10 + ord(c3) - 65) + +manual = ''' +Press keys on keyboard to control PiSloth! + Q: Increase left-up servo angle + W: Decrease left-up servo angle + Z: Increase left-down servo angle + X: Decrease left-down servo angle + I: Increase right-up servo angle + O: Decrease right-up servo angle + N: Increase right-down servo angle + M: Decrease right-down servo angle + SPACE: Print all angle + ESC: Quit +''' + +def main(): + print(manual) + + left_up_angle=0 + left_down_angle=0 + right_up_angle=0 + right_down_angle=0 + while True: + key = readkey() + # print(key) + if key == "q": + left_up_angle = left_up_angle+5 + elif key == "w": + left_up_angle = left_up_angle-5 + elif key == "z": + left_down_angle = left_down_angle+5 + elif key == "x": + left_down_angle = left_down_angle-5 + elif key == "i": + right_up_angle = right_up_angle+5 + elif key == "o": + right_up_angle = right_up_angle-5 + elif key == "n": + right_down_angle = right_down_angle+5 + elif key == "m": + right_down_angle = right_down_angle-5 + elif key == chr(32): # 32 for space + print(right_up_angle,right_down_angle,left_up_angle,left_down_angle) + elif key == chr(27): # 27 for ESC + break + + right_up_servo.angle(right_up_angle) + right_down_servo.angle(right_down_angle) + left_up_servo.angle(left_up_angle) + left_down_servo.angle(left_down_angle) + # time.sleep(0.05) + + print("\nQuit") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/dancing.py b/examples/dancing.py new file mode 100644 index 0000000..a4c211a --- /dev/null +++ b/examples/dancing.py @@ -0,0 +1,59 @@ + +from pisloth import Sloth +from robot_hat import Music +from robot_hat import Ultrasonic +from robot_hat import Pin +import time +import os + +music = Music() + +sloth = Sloth([1,2,3,4]) +sloth.set_offset([0,0,0,0]) + + +def main(): + + music.background_music('./musics/india-Arulo.mp3') + music.music_set_volume(20) + sloth.do_action('stomp left',3,bpm=129) + sloth.do_action('stomp rihgt',3,bpm=129) + sloth.do_action('moon walk left',3,bpm=129) + sloth.do_action('moon walk right',3,bpm=129) + for i in range(3): + sloth.do_action('swing',1,bpm=129) + sloth.do_action('stand',1,bpm=129) + for i in range(3): + sloth.do_action('close',1,bpm=129) + sloth.do_action('stand',1,bpm=129) + sloth.do_action('open',1,bpm=129) + sloth.do_action('stand',1,bpm=129) + sloth.do_action('tiptoe left',2,bpm=129) + sloth.do_action('tiptoe right',2,bpm=129) + + sloth.do_action('stomp left',3,bpm=129) + sloth.do_action('stomp rihgt',3,bpm=129) + sloth.do_action('moon walk left',3,bpm=129) + sloth.do_action('moon walk right',3,bpm=129) + for i in range(3): + sloth.do_action('hook',1,bpm=129) + sloth.do_action('stand',1,bpm=129) + for i in range(4): + sloth.do_action('swing',1,bpm=129) + sloth.do_action('big swing',1,bpm=129) + sloth.do_action('swing',1,bpm=129) + sloth.do_action('stand',1,bpm=129) + + sloth.do_action('tiptoe right',2,bpm=129) + sloth.do_action('stand',2,bpm=129) + + + + music.music_stop() + time.sleep(10) + + + +if __name__ == "__main__": + while True: + main() \ No newline at end of file diff --git a/examples/dont_touch_me.py b/examples/dont_touch_me.py new file mode 100644 index 0000000..cb260d2 --- /dev/null +++ b/examples/dont_touch_me.py @@ -0,0 +1,34 @@ + +from pisloth import Sloth +from robot_hat import Music +from robot_hat import Ultrasonic +from robot_hat import Pin +import time +import os + + +music = Music() + +sloth = Sloth([1,2,3,4]) +sloth.set_offset([0,0,0,0]) +sonar = Ultrasonic(Pin("D0") ,Pin("D1")) + +alert_distance = 20 + +def main(): + distance = sonar.read() + print(distance) + if distance <= alert_distance : + try: + music.sound_effect_threading('./sounds/talk3.wav') + except Exception as e: + print(e) + sloth.do_action('backward', 2, 90) + else: + sloth.do_action('stand', 1, 90) + time.sleep(1) + + +if __name__ == "__main__": + while True: + main() \ No newline at end of file diff --git a/examples/emotional_bot.py b/examples/emotional_bot.py new file mode 100644 index 0000000..9a81fc4 --- /dev/null +++ b/examples/emotional_bot.py @@ -0,0 +1,124 @@ + +from pisloth import Sloth +from robot_hat import TTS, Music +import time + +tts = TTS() +music = Music() + +sloth = Sloth([1,2,3,4]) +sloth.set_offset([0,0,0,0]) + +def confuse(): + try: + music.sound_effect_threading('./sounds/sign.wav') + except Exception as e: + print(e) + sloth.do_action('hook', 1, 90) + +def happy(): + try: + music.sound_effect_threading('./sounds/happy2.wav') + except Exception as e: + print(e) + for i in range(3): + sloth.do_action('hook', 1, 90) + sloth.do_action('stand', 1, 90) + +def fear(): + try: + music.sound_effect_threading('./sounds/warning.wav') + except Exception as e: + print(e) + sloth.do_action('hook', 1, 90) + sloth.do_action('stand', 1, 90) + try: + music.sound_effect_threading('./sounds/warning.wav') + except Exception as e: + print(e) + sloth.do_action('walk backward boldly', 1, 90) + sloth.do_action('stand', 1, 90) + +def sad(): + try: + music.sound_effect_threading('./sounds/depress.wav') + except Exception as e: + print(e) + sloth.do_action('big swing', 1, 90) + +def angry(): + try: + music.sound_effect_threading('./sounds/error.wav') + except Exception as e: + print(e) + sloth.do_action('walk backward boldly', 1, 90) + sloth.do_action('stand', 1, 90) + +def fail(): + try: + music.sound_effect_threading('./sounds/depress2.wav') + except Exception as e: + print(e) + sloth.do_action('fall left', 1, 90) + +def shy(): + try: + music.sound_effect_threading('./sounds/talk3.wav') + except Exception as e: + print(e) + sloth.do_action('close', 1, 90) + time.sleep(1) + try: + music.sound_effect_threading('./sounds/talk2.wav') + except Exception as e: + print(e) + sloth.do_action('stand', 1, 90) + +def main(): + + print("shy") + shy() + time.sleep(1) + sloth.do_action('stand', 1, 90) + time.sleep(2) + + print("confuse") + confuse() + time.sleep(1) + sloth.do_action('stand', 1, 90) + time.sleep(2) + + print("happy") + happy() + time.sleep(1) + sloth.do_action('stand', 1, 90) + time.sleep(2) + + print("fear") + fear() + time.sleep(1) + sloth.do_action('stand', 1, 90) + time.sleep(2) + + print("sad") + sad() + time.sleep(1) + sloth.do_action('stand', 1, 90) + time.sleep(2) + + print("angry") + angry() + time.sleep(1) + sloth.do_action('stand', 1, 90) + time.sleep(2) + + print("fail") + fail() + time.sleep(1) + sloth.do_action('stand', 1, 90) + time.sleep(2) + + +if __name__ == "__main__": + while True: + main() \ No newline at end of file diff --git a/examples/keyboard_control.py b/examples/keyboard_control.py new file mode 100644 index 0000000..15e2a3b --- /dev/null +++ b/examples/keyboard_control.py @@ -0,0 +1,71 @@ +from pisloth import Sloth +from robot_hat import Music +from robot_hat import TTS +import sys +import tty +import termios +import time + +sloth = Sloth([1,2,3,4]) +tts = TTS() +music = Music() +sloth.set_offset([0,0,0,0]) + +def readchar(): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + +manual = ''' +Press keys on keyboard to control PiSloth! + + W: Forward + A: Turn left + S: Backward + D: Turn right + 1: Sound effect: talk1 + 2: Sound effect: talk2 + 3: Sound effect: talk3 + 4: Sound effect: depress2 + Q: Say: "Oh hello there" + E: Say: "bye" + ESC: Quit +''' + +def main(): + print(manual) + while True: + key = readchar() + # print(key) + if key == "w": + sloth.do_action('forward', 1, 90) + elif key == "a": + sloth.do_action('turn left', 1, 90) + elif key == "s": + sloth.do_action('backward', 1, 90) + elif key == "d": + sloth.do_action('turn right', 1, 90) + elif key == "1": + music.sound_effect_play('./sounds/talk1.wav') + elif key == "2": + music.sound_effect_play('./sounds/talk2.wav') + elif key == "3": + music.sound_effect_play('./sounds/talk3.wav') + elif key == "4": + music.sound_effect_play('./sounds/depress.wav') + elif key == "q": + tts.say("Oh hello there") + elif key == "e": + tts.say("bye") + elif key == chr(27): # 27 for ESC + break + time.sleep(0.05) + print("\nQuit") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/lets_fight.py b/examples/lets_fight.py new file mode 100644 index 0000000..00cafbf --- /dev/null +++ b/examples/lets_fight.py @@ -0,0 +1,42 @@ + +from pisloth import Sloth +from robot_hat import Music +from robot_hat import Ultrasonic +from robot_hat import Pin +import time +import os + + +music = Music() + +sloth = Sloth([1,2,3,4]) +sloth.set_offset([0,0,0,0]) +sonar = Ultrasonic(Pin("D0") ,Pin("D1")) + +alert_distance = 40 +contact_distance = 5 + +def main(): + distance = sonar.read() + if distance <= alert_distance and distance >= contact_distance : + try: + music.sound_effect_play('./sounds/battle.wav') + music.background_music('./musics/attack.mp3') + music.music_set_volume(20) + except Exception as e: + print(e) + while True: + distance = sonar.read() + print(distance) + if distance< 0: + continue + if distance<=contact_distance: + break + sloth.do_action('forward', 1,95) + sloth.do_action('stand', 1, 90) + time.sleep(1) + + +if __name__ == "__main__": + while True: + main() \ No newline at end of file diff --git a/examples/move.py b/examples/move.py new file mode 100644 index 0000000..f7d2294 --- /dev/null +++ b/examples/move.py @@ -0,0 +1,16 @@ + +from pisloth import Sloth + +sloth = Sloth([1,2,3,4]) +sloth.set_offset([0,0,0,0]) + +def main(): + sloth.do_action('turn left', 7, 90) + sloth.do_action('forward', 5, 90) + sloth.do_action('turn right', 7, 90) + sloth.do_action('forward', 5, 90) + + +if __name__ == "__main__": + while True: + main() \ No newline at end of file diff --git a/examples/musics/Christmas.mp3 b/examples/musics/Christmas.mp3 new file mode 100644 index 0000000..f876834 Binary files /dev/null and b/examples/musics/Christmas.mp3 differ diff --git a/examples/musics/attack.mp3 b/examples/musics/attack.mp3 new file mode 100644 index 0000000..45097d7 Binary files /dev/null and b/examples/musics/attack.mp3 differ diff --git a/examples/musics/feeling_happy-Ahjay_Stelino.mp3 b/examples/musics/feeling_happy-Ahjay_Stelino.mp3 new file mode 100644 index 0000000..e69de29 diff --git a/examples/musics/fun_times-Ahjay_Stelino.mp3 b/examples/musics/fun_times-Ahjay_Stelino.mp3 new file mode 100644 index 0000000..fc063bc Binary files /dev/null and b/examples/musics/fun_times-Ahjay_Stelino.mp3 differ diff --git a/examples/musics/hallowoon.mp3 b/examples/musics/hallowoon.mp3 new file mode 100644 index 0000000..4902c2d Binary files /dev/null and b/examples/musics/hallowoon.mp3 differ diff --git a/examples/musics/india-Arulo.mp3 b/examples/musics/india-Arulo.mp3 new file mode 100644 index 0000000..04a85bb Binary files /dev/null and b/examples/musics/india-Arulo.mp3 differ diff --git a/examples/musics/slow-trail-Ahjay_Stelino.mp3 b/examples/musics/slow-trail-Ahjay_Stelino.mp3 new file mode 100644 index 0000000..640fb8a Binary files /dev/null and b/examples/musics/slow-trail-Ahjay_Stelino.mp3 differ diff --git a/examples/musics/sports-Ahjay_Stelino.mp3 b/examples/musics/sports-Ahjay_Stelino.mp3 new file mode 100644 index 0000000..9bb5139 Binary files /dev/null and b/examples/musics/sports-Ahjay_Stelino.mp3 differ diff --git a/examples/pisloth/__init__.py b/examples/pisloth/__init__.py new file mode 100644 index 0000000..e09372a --- /dev/null +++ b/examples/pisloth/__init__.py @@ -0,0 +1 @@ +from .sloth import Sloth \ No newline at end of file diff --git a/examples/pisloth/__pycache__/__init__.cpython-37.pyc b/examples/pisloth/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..b0c3d49 Binary files /dev/null and b/examples/pisloth/__pycache__/__init__.cpython-37.pyc differ diff --git a/examples/pisloth/__pycache__/sloth.cpython-37.pyc b/examples/pisloth/__pycache__/sloth.cpython-37.pyc new file mode 100644 index 0000000..92ef53d Binary files /dev/null and b/examples/pisloth/__pycache__/sloth.cpython-37.pyc differ diff --git a/examples/pisloth/sloth.py b/examples/pisloth/sloth.py new file mode 100644 index 0000000..a4cf9c0 --- /dev/null +++ b/examples/pisloth/sloth.py @@ -0,0 +1,182 @@ +from robot_hat import Robot +from robot_hat import mapping + +class Sloth(Robot): + move_list = { + "forward":[ + [0, 40, 0, 15], + [-30, 40, -30, 15], + [-30, 0, -30, 0], + + [0, -15, 0, -40], + [30, -15, 30, -40], + [30, 0, 30, 0], + ], + + "turn right":[ + [0, -20, 0, -40], + [-20, -20, 0, -40], + [-20, 0, 0, 0], + + [0, 40, 0, 20], + [20, 40, 0, 20], + [20, 0, 0, 0], + ], + + "turn left":[ + [0, 40, 0, 20], + [0, 40, 20, 20], + [0, 0, 20, 0], + + [0, -20, 0, -40], + [0, -20, -20, -40], + [0, 0, -20, 0], + ], + "backward":[ + [0, 40, 0, 15], + [30, 40, 30, 15], + [30, 0, 30, 0], + + [0, -15, 0, -40], + [-30, -15, -30, -40], + [-30, 0, -30, 0], + ], + + "stand":[ + [0,0,0,0], + ], + + "moon walk left": [ + [0, 0, 0, -30], + [0, 30, 0, -60], + [0, 60, 0, -30], + [0, 30, 0, 0], + [0, 0, 0, 0] + ], + "moon walk right": [ + [0, 30, 0, 0], + [0, 60, 0, -30], + [0, 30, 0, -60], + [0, 0, 0, -30], + [0, 0, 0, 0] + ], + + + "hook": [ + [0, 50, 0, -50], + ], + + "big swing": [ + [0, -90, 0, 90], + ], + + "swing": [ + [0, -40, 0, 40], + ], + + "walk boldly": [ + [-15, -15, 15, -40], + [10, -30, 40, -40], + [10, 0, 40, 0], + + [-15, 40, 15, 15], + [-40, 40, -10, 30], + [-40, 0, -10, 0], + ], + + "walk backward boldly": [ + [-15, -15, 15, -40], + [-40, -30, -10, -40], + [-40, 0, -10, 0], + + [-15, 40, 15, 15], + [10, 40, 40, 30], + [10, 0, 40, 0], + ], + + "walk shyly": [ + [10, -15, -10, -40], + [25, -30, -5, -40], + [25, 0, -5, 0], + + [10, 40, -10, 15], + [5, 40, -25, 30], + [5, 0, -25, 0], + ], + + "walk backward shyly": [ + [10, -15, -10, -40], + [5, -30, -25, -40], + [5, 0, -25, 0], + + [10, 40, -10, 15], + [25, 40, -5, 30], + [25, 0, -5, 0], + ], + + + "stomp rihgt": [ + [0, 15, 0, 0], + [0, 30, 0, -15], + [0, 15, 0, -30], + [0, 0, 0, -15], + [0, 0, 0, 0], + ], + "stomp left": [ + [0, 0, 0, -15], + [0, 15, 0, -30], + [0, 30, 0, -15], + [0, 15, 0, 0], + [0, 0, 0, 0] + ], + + "close": [ + [30, 0, -30, 0], + # [0, 0, 0, 0] + ], + "open": [ + [-30, 0, 30, 0], + # [0, 0, 0, 0] + ], + + "tiptoe left":[ + [-20, 35, -20, 15], + [-20, 15, -20, 15], + ], + + "tiptoe right":[ + [20, -15, 20, -35], + [20, -15, 20, -15], + ], + + "fall left": [ + [-40, 70, -40, 30], + [-40, 30, -40, 30], + ], + "fall right": [ + [40, -30, 40, -70], + [40, -30, 40, -30], + ], + + } + + def do_action(self,motion_name, step=1, speed=None, bpm=None): + if bpm == None: + speed = 50 if speed == None else speed + # speed = mapping(speed, 0, 100, 0, 80) + for _ in range(step): + for motion in self.move_list[motion_name]: + if bpm != None: + self.servo_move(motion, bpm=bpm) + else: + self.servo_move(motion, speed=speed) + + def add_action(self,action_name,action_list): + if action_name not in self.move_list.keys(): + self.move_list[action_name] = action_list + +if __name__=="__main__": + a = Sloth([1,2,3,4]) + while 1: + for i in a.move_list: + a.do_action(i,step=2,speed=100) \ No newline at end of file diff --git a/examples/robot_hat/__init__.py b/examples/robot_hat/__init__.py new file mode 100644 index 0000000..8ee87ac --- /dev/null +++ b/examples/robot_hat/__init__.py @@ -0,0 +1,17 @@ +from .adc import ADC +from .filedb import fileDB +from .i2c import I2C +from .modules import * +from .music import Music +from .pin import Pin +from .pwm import PWM +from .servo import Servo +from .spi import SPI +from .switch import Switch +from .tts import TTS +from .utils import * +# from .vilib import Vilib +from .robot import Robot + +def __main__(): + print("Thanks for using Robot Hat") \ No newline at end of file diff --git a/examples/robot_hat/__pycache__/__init__.cpython-37.pyc b/examples/robot_hat/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..7eff9e9 Binary files /dev/null and b/examples/robot_hat/__pycache__/__init__.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/adc.cpython-37.pyc b/examples/robot_hat/__pycache__/adc.cpython-37.pyc new file mode 100644 index 0000000..f43b41d Binary files /dev/null and b/examples/robot_hat/__pycache__/adc.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/basic.cpython-37.pyc b/examples/robot_hat/__pycache__/basic.cpython-37.pyc new file mode 100644 index 0000000..656c6fd Binary files /dev/null and b/examples/robot_hat/__pycache__/basic.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/filedb.cpython-37.pyc b/examples/robot_hat/__pycache__/filedb.cpython-37.pyc new file mode 100644 index 0000000..3bffe85 Binary files /dev/null and b/examples/robot_hat/__pycache__/filedb.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/i2c.cpython-37.pyc b/examples/robot_hat/__pycache__/i2c.cpython-37.pyc new file mode 100644 index 0000000..ef1d876 Binary files /dev/null and b/examples/robot_hat/__pycache__/i2c.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/modules.cpython-37.pyc b/examples/robot_hat/__pycache__/modules.cpython-37.pyc new file mode 100644 index 0000000..6a8f786 Binary files /dev/null and b/examples/robot_hat/__pycache__/modules.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/music.cpython-37.pyc b/examples/robot_hat/__pycache__/music.cpython-37.pyc new file mode 100644 index 0000000..81495bd Binary files /dev/null and b/examples/robot_hat/__pycache__/music.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/pin.cpython-37.pyc b/examples/robot_hat/__pycache__/pin.cpython-37.pyc new file mode 100644 index 0000000..f1abf94 Binary files /dev/null and b/examples/robot_hat/__pycache__/pin.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/pwm.cpython-37.pyc b/examples/robot_hat/__pycache__/pwm.cpython-37.pyc new file mode 100644 index 0000000..9f7c303 Binary files /dev/null and b/examples/robot_hat/__pycache__/pwm.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/robot.cpython-37.pyc b/examples/robot_hat/__pycache__/robot.cpython-37.pyc new file mode 100644 index 0000000..98eb8dc Binary files /dev/null and b/examples/robot_hat/__pycache__/robot.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/servo.cpython-37.pyc b/examples/robot_hat/__pycache__/servo.cpython-37.pyc new file mode 100644 index 0000000..d8db2ea Binary files /dev/null and b/examples/robot_hat/__pycache__/servo.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/spi.cpython-37.pyc b/examples/robot_hat/__pycache__/spi.cpython-37.pyc new file mode 100644 index 0000000..d08eb8a Binary files /dev/null and b/examples/robot_hat/__pycache__/spi.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/switch.cpython-37.pyc b/examples/robot_hat/__pycache__/switch.cpython-37.pyc new file mode 100644 index 0000000..92bdd08 Binary files /dev/null and b/examples/robot_hat/__pycache__/switch.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/tts.cpython-37.pyc b/examples/robot_hat/__pycache__/tts.cpython-37.pyc new file mode 100644 index 0000000..6f54560 Binary files /dev/null and b/examples/robot_hat/__pycache__/tts.cpython-37.pyc differ diff --git a/examples/robot_hat/__pycache__/utils.cpython-37.pyc b/examples/robot_hat/__pycache__/utils.cpython-37.pyc new file mode 100644 index 0000000..df9f866 Binary files /dev/null and b/examples/robot_hat/__pycache__/utils.cpython-37.pyc differ diff --git a/examples/robot_hat/adc.py b/examples/robot_hat/adc.py new file mode 100644 index 0000000..aeff555 --- /dev/null +++ b/examples/robot_hat/adc.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +from .i2c import I2C + +class ADC(I2C): + ADDR=0x14 # 扩展板的地址为0x14 + + def __init__(self, chn): # 参数,通道数,树莓派扩展板上有8个adc通道分别为"A0, A1, A2, A3, A4, A5, A6, A7" + super().__init__() + if isinstance(chn, str): + if chn.startswith("A"): # 判断穿境来的参数是否为A开头,如果是,取A后面的数字出来 + chn = int(chn[1:]) + else: + raise ValueError("ADC channel should be between [A0, A7], not {0}".format(chn)) + if chn < 0 or chn > 7: # 判断取出来的数字是否在0~7的范围内 + self._error('Incorrect channel range') + chn = 7 - chn + self.chn = chn | 0x10 # 给从机地址 + self.reg = 0x40 + self.chn + # self.bus = smbus.SMBus(1) + + def read(self): # adc通道读取数---写一次数据,读取两次数据 (读取的数据范围是0~4095) + self._debug("Write 0x%02X to 0x%02X"%(self.chn, self.ADDR)) + # self.bus.write_byte(self.ADDR, self.chn) # 写入数据 + self.send([self.chn, 0, 0], self.ADDR) + + self._debug("Read from 0x%02X"%(self.ADDR)) + # value_h = self.bus.read_byte(self.ADDR) + value_h = self.recv(1, self.ADDR)[0] # 读取数据 + + self._debug("Read from 0x%02X"%(self.ADDR)) + # value_l = self.bus.read_byte(self.ADDR) + value_l = self.recv(1, self.ADDR)[0] # 读取数据(读两次) + + value = (value_h << 8) + value_l + self._debug("Read value: %s"%value) + return value + + def read_voltage(self): # 将读取的数据转化为电压值(0~3.3V) + return self.read*3.3/4095 + + +def test(): + import time + adc = ADC(0) + while True: + print(adc.read()) + time.sleep(1) + +if __name__ == '__main__': + test() \ No newline at end of file diff --git a/examples/robot_hat/basic.py b/examples/robot_hat/basic.py new file mode 100644 index 0000000..ff48aa3 --- /dev/null +++ b/examples/robot_hat/basic.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +import logging +import time + +class _Basic_class(object): + _class_name = '_Basic_class' + DEBUG_LEVELS = {'debug': logging.DEBUG, + 'info': logging.INFO, + 'warning': logging.WARNING, + 'error': logging.ERROR, + 'critical': logging.CRITICAL, + } + DEBUG_NAMES = ['critical', 'error', 'warning', 'info', 'debug'] + + def __init__(self): + self._debug_level = 0 + self.logger = logging.getLogger(self._class_name) + self.ch = logging.StreamHandler() + form = "%(asctime)s [%(levelname)s] %(message)s" + self.formatter = logging.Formatter(form) + self.ch.setFormatter(self.formatter) + self.logger.addHandler(self.ch) + self._debug = self.logger.debug + self._info = self.logger.info + self._warning = self.logger.warning + self._error = self.logger.error + self._critical = self.logger.critical + + @property + def debug(self): + return self._debug_level + + @debug.setter + def debug(self, debug): + if debug in range(5): + self._debug_level = self.DEBUG_NAMES[debug] + elif debug in self.DEBUG_NAMES: + self._debug_level = debug + else: + raise ValueError('Debug value must be 0(critical), 1(error), 2(warning), 3(info) or 4(debug), not \"{0}\".'.format(debug)) + self.logger.setLevel(self.DEBUG_LEVELS[self._debug_level]) + self.ch.setLevel(self.DEBUG_LEVELS[self._debug_level]) + self._debug('Set logging level to [%s]' % self._debug_level) + + def run_command(self, cmd): + import subprocess + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + result = p.stdout.read().decode('utf-8') + status = p.poll() + # print(result) + # print(status) + return status, result + + def map(self, x, in_min, in_max, out_min, out_max): + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min diff --git a/examples/robot_hat/filedb.py b/examples/robot_hat/filedb.py new file mode 100644 index 0000000..a5d390c --- /dev/null +++ b/examples/robot_hat/filedb.py @@ -0,0 +1,75 @@ +''' +********************************************************************** +* Filename : filedb.py +* Description : A simple file based database. +* Author : Cavon +* Brand : SunFounder +* E-mail : service@sunfounder.com +* Website : www.sunfounder.com +* Update : Cavon 2016-09-13 New release +********************************************************************** +''' + +class fileDB(object): + """A file based database. + + A file based database, read and write arguements in the specific file. + """ + def __init__(self, db=None): + '''Init the db_file is a file to save the datas.''' + + # Check if db_file is defined + if db != None: + self.db = db + else: + self.db = "config" + + def get(self, name, default_value=None): + """Get value by data's name. Default value is for the arguemants do not exist""" + try: + conf = open(self.db,'r') + lines=conf.readlines() + conf.close() + file_len=len(lines)-1 + flag = False + # Find the arguement and set the value + for i in range(file_len): + if lines[i][0] != '#': + if lines[i].split('=')[0].strip() == name: + value = lines[i].split('=')[1].replace(' ', '').strip() + flag = True + if flag: + return value + else: + return default_value + except FileNotFoundError: + conf = open(self.db,'w') + conf.write("") + conf.close() + return default_value + except : + return default_value + + def set(self, name, value): + """Set value by data's name. Or create one if the arguement does not exist""" + + # Read the file + conf = open(self.db,'r') + lines=conf.readlines() + conf.close() + file_len=len(lines)-1 + flag = False + # Find the arguement and set the value + for i in range(file_len): + if lines[i][0] != '#': + if lines[i].split('=')[0].strip() == name: + lines[i] = '%s = %s\n' % (name, value) + flag = True + # If arguement does not exist, create one + if not flag: + lines.append('%s = %s\n\n' % (name, value)) + + # Save the file + conf = open(self.db,'w') + conf.writelines(lines) + conf.close() diff --git a/examples/robot_hat/i2c.py b/examples/robot_hat/i2c.py new file mode 100644 index 0000000..6e8e08c --- /dev/null +++ b/examples/robot_hat/i2c.py @@ -0,0 +1,146 @@ +from .basic import _Basic_class +from smbus import SMBus + +class I2C(_Basic_class): + MASTER = 0 + SLAVE = 1 + RETRY = 5 + + def __init__(self, *args, **kargs): # *args表示位置参数(形式参数),可无,; **kargs表示默认值参数,可无。 + super().__init__() + self._bus = 1 + self._smbus = SMBus(self._bus) + + def _i2c_write_byte(self, addr, data): # i2C 写系列函数 + self._debug("_i2c_write_byte: [0x{:02X}] [0x{:02X}]".format(addr, data)) + return self._smbus.write_byte(addr, data) + + def _i2c_write_byte_data(self, addr, reg, data): + self._debug("_i2c_write_byte_data: [0x{:02X}] [0x{:02X}] [0x{:02X}]".format(addr, reg, data)) + return self._smbus.write_byte_data(addr, reg, data) + + def _i2c_write_word_data(self, addr, reg, data): + self._debug("_i2c_write_word_data: [0x{:02X}] [0x{:02X}] [0x{:04X}]".format(addr, reg, data)) + return self._smbus.write_word_data(addr, reg, data) + + def _i2c_write_i2c_block_data(self, addr, reg, data): + self._debug("_i2c_write_i2c_block_data: [0x{:02X}] [0x{:02X}] {}".format(addr, reg, data)) + return self._smbus.write_i2c_block_data(addr, reg, data) + + def _i2c_read_byte(self, addr): # i2C 读系列函数 + self._debug("_i2c_read_byte: [0x{:02X}]".format(addr)) + return self._smbus.read_byte(addr) + + def _i2c_read_i2c_block_data(self, addr, reg, num): + self._debug("_i2c_read_i2c_block_data: [0x{:02X}] [0x{:02X}] [{}]".format(addr, reg, num)) + return self._smbus.read_i2c_block_data(addr, reg, num) + + def is_ready(self, addr): + addresses = self.scan() + if addr in addresses: + return True + else: + return False + + def scan(self): # 查看有哪些i2c设备 + cmd = "i2cdetect -y %s" % self._bus + _, output = self.run_command(cmd) # 调用basic中的方法,在linux中运行cmd指令,并返回运行后的内容 + + outputs = output.split('\n')[1:] # 以回车符为分隔符,分割第二行之后的所有行 + self._debug("outputs") + addresses = [] + for tmp_addresses in outputs: + if tmp_addresses == "": + continue + tmp_addresses = tmp_addresses.split(':')[1] + tmp_addresses = tmp_addresses.strip().split(' ') # strip函数是删除字符串两端的字符,split函数是分隔符 + for address in tmp_addresses: + if address != '--': + addresses.append(int(address, 16)) + self._debug("Conneceted i2c device: %s"%addresses) # append以列表的方式添加address到addresses中 + return addresses + + def send(self, send, addr, timeout=0): # 发送数据,addr为从机地址,send为数据 + if isinstance(send, bytearray): + data_all = list(send) + elif isinstance(send, int): + data_all = [] + d = "{:X}".format(send) + d = "{}{}".format("0" if len(d)%2 == 1 else "", d) # format是将()中的内容对应填入{}中,()中的第一个参数是一个三目运算符,if条件成立则为“0”,不成立则为“”(空的意思),第二个参数是d,此行代码意思为,当字符串为奇数位时,在字符串最强面添加‘0’,否则,不添加, 方便以下函数的应用 + # print(d) + for i in range(len(d)-2, -1, -2): # 从字符串最后开始取,每次取2位 + tmp = int(d[i:i+2], 16) # 将两位字符转化为16进制 + # print(tmp) + data_all.append(tmp) # 添加到data_all数组中 + data_all.reverse() + elif isinstance(send, list): + data_all = send + else: + raise ValueError("send data must be int, list, or bytearray, not {}".format(type(send))) + + if len(data_all) == 1: # 如果data_all只有一组数 + data = data_all[0] + self._i2c_write_byte(addr, data) + elif len(data_all) == 2: # 如果data_all只有两组数 + reg = data_all[0] + data = data_all[1] + self._i2c_write_byte_data(addr, reg, data) + elif len(data_all) == 3: # 如果data_all只有三组数 + reg = data_all[0] + data = (data_all[2] << 8) + data_all[1] + self._i2c_write_word_data(addr, reg, data) + else: + reg = data_all[0] + data = list(data_all[1:]) + self._i2c_write_i2c_block_data(addr, reg, data) + + def recv(self, recv, addr=0x00, timeout=0): # 接收数据 + if isinstance(recv, int): # 将recv转化为二进制数 + result = bytearray(recv) + elif isinstance(recv, bytearray): + result = recv + else: + return False + for i in range(len(result)): + result[i] = self._i2c_read_byte(addr) + return result + + def mem_write(self, data, addr, memaddr, timeout=5000, addr_size=8): #memaddr match to chn + if isinstance(data, bytearray): + data_all = list(data) + elif isinstance(data, list): + data_all = data + elif isinstance(data, int): + data_all = [] + data = "%x"%data + if len(data) % 2 == 1: + data = "0" + data + # print(data) + for i in range(0, len(data), 2): + # print(data[i:i+2]) + data_all.append(int(data[i:i+2], 16)) + else: + raise ValueError("memery write require arguement of bytearray, list, int less than 0xFF") + # print(data_all) + self._i2c_write_i2c_block_data(addr, memaddr, data_all) + + def mem_read(self, data, addr, memaddr, timeout=5000, addr_size=8): # 读取数据 + if isinstance(data, int): + num = data + elif isinstance(data, bytearray): + num = len(data) + else: + return False + result = bytearray(self._i2c_read_i2c_block_data(addr, memaddr, num)) + return result + + def readfrom_mem_into(self, addr, memaddr, buf): + buf = self.mem_read(len(buf), addr, memaddr) + return buf + + def writeto_mem(self, addr, memaddr, data): + self.mem_write(data, addr, memaddr) + +# i2c = I2C() +# i2c.scan() +# i2c.mem_write(0xff53773, 20, 20) \ No newline at end of file diff --git a/examples/robot_hat/modules.py b/examples/robot_hat/modules.py new file mode 100644 index 0000000..31b41c5 --- /dev/null +++ b/examples/robot_hat/modules.py @@ -0,0 +1,282 @@ +from .i2c import I2C +from .adc import ADC +import time + +class Ultrasonic(): + def __init__(self, trig, echo, timeout=0.02): + self.trig = trig + self.echo = echo + self.timeout = timeout + + def _read(self): + self.trig.low() + time.sleep(0.01) + self.trig.high() + time.sleep(0.00001) + self.trig.low() + pulse_end = 0 + pulse_start = 0 + timeout_start = time.time() + while self.echo.value()==0: + pulse_start = time.time() + if pulse_start - timeout_start > self.timeout: + return -1 + while self.echo.value()==1: + pulse_end = time.time() + if pulse_end - timeout_start > self.timeout: + return -1 + during = pulse_end - pulse_start + cm = round(during * 340 / 2 * 100, 2) + return cm + + def read(self, times=10): + for i in range(times): + a = self._read() + if a != -1: + return a + return -1 + +class DS18X20(): + def __init__(self, *args, **kargs): + # self.pin = pin + pass + + def scan(self): + import os + roms = [] + for rom in os.listdir('/sys/bus/w1/devices'): + if rom.startswith('28-'): + roms.append(rom) + return roms + + def convert_temp(self): + pass + + def read_temp(self, rom): + location = '/sys/bus/w1/devices/' + rom + '/w1_slave' + with open(location) as f: + text = f.read() + secondline = text.split("\n")[1] + temperaturedata = secondline.split(" ")[9] + temperature = float(temperaturedata[2:]) + temperature = temperature / 1000 + return temperature + + def read(self, unit=1): + # unit=0: Fahrenheit + # unit=1: degree Celsius + self.roms = self.scan() + self.convert_temp() + temps = [] + for rom in self.roms: + temp = self.read_temp(rom) + if unit == 0: + temp = 32 + temp * 1.8 + temps.append(temp) + if len(temps) == 0: + raise IOError("Cannot detect any DS18X20, please check the connection") + elif len(temps) == 1: + temps = temps[0] + return temps + +class ADXL345(): + X = 0 + Y = 1 + Z = 2 + _REG_DATA_X = 0x32 # X-axis data 0 (6 bytes for X/Y/Z) + _REG_DATA_Y = 0x34 # Y-axis data 0 (6 bytes for X/Y/Z) + _REG_DATA_Z = 0x36 # Z-axis data 0 (6 bytes for X/Y/Z) + _REG_POWER_CTL = 0x2D # Power-saving features control + _AXISES = [_REG_DATA_X, _REG_DATA_Y, _REG_DATA_Z] + + def __init__(self, address=0x53): + self.i2c = I2C() + self.address = address + + def read(self, axis): + raw_2 = 0 + result = self.i2c._i2c_read_byte(self.address) + send = (0x08<< 8) + self._REG_POWER_CTL + if result: + self.i2c.send(send, self.address) + self.i2c.mem_write(0, 0x53, 0x31, timeout=1000) + self.i2c.mem_write(8, 0x53, 0x2D, timeout=1000) + raw = self.i2c.mem_read(2, self.address, self._AXISES[axis]) + # 第一次读的值总是为0,所以多读取一次 + self.i2c.mem_write(0, 0x53, 0x31, timeout=1000) + self.i2c.mem_write(8, 0x53, 0x2D, timeout=1000) + raw = self.i2c.mem_read(2, self.address, self._AXISES[axis]) + if raw[1]>>7 == 1: + + raw_1 = raw[1]^128 ^ 127 + raw_2 = (raw_1 + 1) * -1 + else: + raw_2 = raw[1] + g = raw_2 << 8 | raw[0] + value = g / 256.0 + return value + +class RGB_LED(): + def __init__(self, Rpin, Gpin, Bpin, common=1): + self.Rpin = Rpin + self.Gpin = Gpin + self.Bpin = Bpin + self.common = common + + def write(self, color): + if isinstance(color, str): + color = color.strip("#") + color = int(color, 16) + R_val = (color & 0xff0000) >> 16 + G_val = (color & 0x00ff00) >> 8 + B_val = (color & 0x0000ff) >> 0 + + if self.common == 1: # common anode + R_val = 255-R_val + G_val = 255-G_val + B_val = 255-B_val + + R_val = R_val / 255.0 * 100.0 + G_val = G_val / 255.0 * 100.0 + B_val = B_val / 255.0 * 100.0 + + self.Rpin.pulse_width_percent(R_val) + self.Gpin.pulse_width_percent(G_val) + self.Bpin.pulse_width_percent(B_val) + +class Buzzer(): + def __init__(self, pwm): + self.pwm = pwm + + def on(self): + self.pwm.pulse_width_percent(50) + + def off(self): + self.pwm.pulse_width_percent(0) + + def freq(self, freq): + self.pwm.freq(freq) + + def play(self, *args): + try: + freq = args[0] + except: + raise ValueError("Buzzer must have freq argument") + self.freq(freq) + self.on() + try: + ms = args[1] + except: + return freq + ms = int(ms) + from ezblock import delay + delay(ms) + self.off() + delay(ms) + return freq + +class Sound(): + def __init__(self, pin): + self.pin = pin + + def read_raw(self): + return self.pin.read() + + def read(self, times=50): + value_list = [] + for _ in range(times): + value = self.read_raw() + value_list.append(value) + value = sum(value_list)/times + return value + +class Joystick(): + import math + THRESHOLD = 2047 / math.sqrt(2) + def __init__(self, Xpin, Ypin, Btpin): + self.pins = [Xpin, Ypin, Btpin] + self.pins[2].init(self.pins[2].IN, pull=self.pins[2].PULL_UP,) + self.is_reversed = [False, False, False] + + @property + def is_x_reversed(self): + return self.is_reversed[0] + @property + def is_y_reversed(self): + return self.is_reversed[1] + @property + def is_z_reversed(self): + return self.is_reversed[2] + + @is_x_reversed.setter + def is_x_reversed(self, value): + if not isinstance(value, bool): + raise ValueError("reversed value must be bool, not %s(%s)"%(value, type(value))) + self.is_reversed[0] = value + @is_y_reversed.setter + def is_y_reversed(self, value): + if not isinstance(value, bool): + raise ValueError("reversed value must be bool, not %s(%s)"%(value, type(value))) + self.is_reversed[1] = value + @is_z_reversed.setter + def is_z_reversed(self, value): + if not isinstance(value, bool): + raise ValueError("reversed value must be bool, not %s(%s)"%(value, type(value))) + self.is_reversed[2] = value + + def read(self, axis): + pin = self.pins[axis] + if axis == 2: + value = pin.value() + if self.is_reversed[2]: + value = value + 1 & 1 + else: + value = pin.read() - 2047 + if self.is_reversed[axis]: + value = - value + return value + + def read_status(self): + state = ['home', 'up', 'down', 'left', 'right', 'pressed'] + i = 0 + if self.read(1) < -self.THRESHOLD: # Y + i = 2 #down + elif self.read(1) > self.THRESHOLD: # Y + i = 1 #up + elif self.read(0) < -self.THRESHOLD: # X + i = 3 #left + elif self.read(0) > self.THRESHOLD: # X + i = 4 #right + elif self.read(2) == 0: # Bt + i = 5 # Button pressed + else: + i = 0 + return state[i] + +class Grayscale_Module(object): + def __init__(self, pin0, pin1, pin2, reference=1000): + self.chn_0 = ADC(pin0) + self.chn_1 = ADC(pin1) + self.chn_2 = ADC(pin2) + self.reference = ref + + def get_line_status(self,fl_list): + + if fl_list[0] > self.reference and fl_list[1] > self.reference and fl_list[2] > self.reference: + return 'stop' + + elif fl_list[1] <= self.reference: + return 'forward' + + elif fl_list[0] <= self.reference: + return 'right' + + elif fl_list[2] <= self.reference: + return 'left' + + def get_grayscale_data(self): + adc_value_list = [] + adc_value_list.append(self.chn_0.read()) + adc_value_list.append(self.chn_1.read()) + adc_value_list.append(self.chn_2.read()) + return adc_value_list diff --git a/examples/robot_hat/music.py b/examples/robot_hat/music.py new file mode 100644 index 0000000..1eb5e18 --- /dev/null +++ b/examples/robot_hat/music.py @@ -0,0 +1,163 @@ +from .basic import _Basic_class +import time +import threading +import pyaudio +import numpy as np + +class Music(_Basic_class): + MUSIC_BEAT = 500 + MUSIC_DIR = '/home/pi/Music/' + SOUND_DIR = '/home/pi/Sound/' + + NOTES = { + "Low C": 261.63, + "Low C#": 277.18, + "Low D": 293.66, + "Low D#": 311.13, + "Low E": 329.63, + "Low F": 349.23, + "Low F#": 369.99, + "Low G": 392.00, + "Low G#": 415.30, + "Low A": 440.00, + "Low A#": 466.16, + "Low B": 493.88, + "Middle C": 523.25, + "Middle C#": 554.37, + "Middle D": 587.33, + "Middle D#": 622.25, + "Middle E": 659.25, + "Middle F": 698.46, + "Middle F#": 739.99, + "Middle G": 783.99, + "Middle G#": 830.61, + "Middle A": 880.00, + "Middle A#": 932.33, + "Middle B": 987.77, + "High C": 1046.50, + "High C#": 1108.73, + "High D": 1174.66, + "High D#": 1244.51, + "High E": 1318.51, + "High F": 1396.91, + "High F#": 1479.98, + "High G": 1567.98, + "High G#": 1661.22, + "High A": 1760.00, + "High A#": 1864.66, + "High B": 1975.53, + } + + def __init__(self): + import pygame + self.pygame = pygame + self.pygame.mixer.init() + + @property + def MUSIC_LIST(self): + from os import listdir + return listdir(self.MUSIC_DIR) + + @property + def SOUND_LIST(self): + from os import listdir + return listdir(self.SOUND_DIR) + + + def note(self, n): + try: + n = self.NOTES[n] + return n + except: + raise ValueError("{} is not a note".format(n)) + + def beat(self, b): + b = float(b) + b = b * self.MUSIC_BEAT + return b + + def tempo(self, *args): + if len(args) == 0: + return int(60.0 / (self.MUSIC_BEAT / 1000.0)) + else: + try: + tempo = int(args[0]) + self.MUSIC_BEAT = int((60.0 / tempo) * 1000.0) + return tempo + except: + raise ValueError("tempo must be int not {}".format(args[0])) + + def sound_play(self, file_name): + self.music_set_volume(80) + self.pygame.mixer.music.load(file_name) + self.pygame.mixer.music.play() + + def sound_effect_play(self, file_name): + # file_name = self.SOUND_DIR + file_name + music = self.pygame.mixer.Sound(str(file_name)) + time_delay = round(music.get_length(), 2) + music.play() + time.sleep(time_delay) + + def sound_effect_threading(self, file_name): + # file_name = './sound/' + file_name + obj = MyThreading(self.sound_effect_play, file_name=file_name) + obj.start() + + def background_music(self, file_name, loops=-1, start=0.0, volume=50):#-1:continue + if loops <= 0: + loops = 0 + volume = round(volume/100.0, 2) + # file_name = self.MUSIC_DIR + str(file_name) + self.pygame.mixer.music.load(str(file_name)) + self.pygame.mixer.music.play(loops-1, start) + + def music_set_volume(self, value=50): + value = round(value/100.0, 2) + self.pygame.mixer.music.set_volume(value) + + def music_stop(self): + self.pygame.mixer.music.stop() + + def music_pause(self): + self.pygame.mixer.music.pause() + + def music_unpause(self): + self.pygame.mixer.music.unpause() + + def sound_length(self, file_name): + music = self.pygame.mixer.Sound(str(file_name)) + return round(music.get_length(),2) + + def play_tone_for(self, freq, duration): + p = pyaudio.PyAudio() + volume = 1 # range [0.0, 1.0] + fs = 44100 # sampling rate, Hz, must be integer + duration /= 2000 # devide 2 for half tone up half rest, divide 1000 for ms to s + _duration = duration * 4 + # generate samples, note conversion to float32 array + samples = (np.sin(2*np.pi*np.arange(fs*_duration)*freq/fs)).astype(np.float32) + + # for paFloat32 sample values must be in range [-1.0, 1.0] + stream = p.open(format=pyaudio.paFloat32, + channels=1, + rate=fs, + output=True) + + # play. May repeat with different volume values (if done interactively) + stream.write(volume*samples) + + # stream.stop_stream() + # stream.close() + time.sleep(duration) + + +class MyThreading(threading.Thread): + + def __init__(self, func, **arg): + super(MyThreading,self).__init__() + self.func = func + self.arg = arg + + def run(self): + self.func(**self.arg) diff --git a/examples/robot_hat/pin.py b/examples/robot_hat/pin.py new file mode 100644 index 0000000..fce9661 --- /dev/null +++ b/examples/robot_hat/pin.py @@ -0,0 +1,205 @@ +from .basic import _Basic_class +import RPi.GPIO as GPIO + +class Pin(_Basic_class): + OUT = GPIO.OUT + IN = GPIO.IN + IRQ_FALLING = GPIO.FALLING + IRQ_RISING = GPIO.RISING + IRQ_RISING_FALLING = GPIO.BOTH + PULL_UP = GPIO.PUD_UP + PULL_DOWN = GPIO.PUD_DOWN + PULL_NONE = None + + _dict = { + "BOARD_TYPE": 12, + } + + _dict_1 = { + "D0": 17, + "D1": 18, + "D2": 27, + "D3": 22, + "D4": 23, + "D5": 24, + "D6": 25, + "D7": 4, + "D8": 5, + "D9": 6, + "D10": 12, + "D11": 13, + "D12": 19, + "D13": 16, + "D14": 26, + "D15": 20, + "D16": 21, + "SW": 19, + "LED": 26, + "BOARD_TYPE": 12, + "RST": 16, + "BLEINT": 13, + "BLERST": 20, + "MCURST": 21, + } + + _dict_2 = { + "D0": 17, + "D1": 4, # Changed + "D2": 27, + "D3": 22, + "D4": 23, + "D5": 24, + "D6": 25, # Removed + "D7": 4, # Removed + "D8": 5, # Removed + "D9": 6, + "D10": 12, + "D11": 13, + "D12": 19, + "D13": 16, + "D14": 26, + "D15": 20, + "D16": 21, + "SW": 25, # Changed + "LED": 26, + "BOARD_TYPE": 12, + "RST": 16, + "BLEINT": 13, + "BLERST": 20, + "MCURST": 5, # Changed + } + + def __init__(self, *value): + super().__init__() + GPIO.setmode(GPIO.BCM) + GPIO.setwarnings(False) + + self.check_board_type() + + if len(value) > 0: + pin = value[0] + if len(value) > 1: + mode = value[1] + else: + mode = None + if len(value) > 2: + setup = value[2] + else: + setup = None + if isinstance(pin, str): + try: + self._board_name = pin + self._pin = self.dict()[pin] + except Exception as e: + print(e) + self._error('Pin should be in %s, not %s' % (self._dict.keys(), pin)) + elif isinstance(pin, int): + self._pin = pin + else: + self._error('Pin should be in %s, not %s' % (self._dict.keys(), pin)) + self._value = 0 + self.init(mode, pull=setup) + self._info("Pin init finished.") + + def check_board_type(self): + type_pin = self.dict()["BOARD_TYPE"] + GPIO.setup(type_pin, GPIO.IN) + if GPIO.input(type_pin) == 0: + self._dict = self._dict_1 + else: + self._dict = self._dict_2 + + def init(self, mode, pull=PULL_NONE): + self._pull = pull + self._mode = mode + if mode != None: + if pull != None: + GPIO.setup(self._pin, mode, pull_up_down=pull) + else: + GPIO.setup(self._pin, mode) + + def dict(self, *_dict): + if len(_dict) == 0: + return self._dict + else: + if isinstance(_dict, dict): + self._dict = _dict + else: + self._error( + 'argument should be a pin dictionary like {"my pin": ezblock.Pin.cpu.GPIO17}, not %s' % _dict) + + def __call__(self, value): + return self.value(value) + + def value(self, *value): + if len(value) == 0: + if self._mode in [None, self.OUT]: + self.mode(self.IN) + result = GPIO.input(self._pin) + self._debug("read pin %s: %s" % (self._pin, result)) + return result + else: + value = value[0] + if self._mode in [None, self.IN]: + self.mode(self.OUT) + GPIO.output(self._pin, value) + return value + + def on(self): + return self.value(1) + + def off(self): + return self.value(0) + + def high(self): + return self.on() + + def low(self): + return self.off() + + def mode(self, *value): + if len(value) == 0: + return (self._mode, self._pull) + else: + self._mode = value[0] + if len(value) == 1: + GPIO.setup(self._pin, self._mode) + elif len(value) == 2: + self._pull = value[1] + GPIO.setup(self._pin, self._mode, self._pull) + + def pull(self, *value): + return self._pull + + def irq(self, handler=None, trigger=None, bouncetime=200): + self.mode(self.IN) + GPIO.add_event_detect(self._pin, trigger, callback=handler, bouncetime=bouncetime) + + def name(self): + return "GPIO%s"%self._pin + + def names(self): + return [self.name, self._board_name] + + class cpu(object): + GPIO17 = 17 + GPIO18 = 18 + GPIO27 = 27 + GPIO22 = 22 + GPIO23 = 23 + GPIO24 = 24 + GPIO25 = 25 + GPIO26 = 26 + GPIO4 = 4 + GPIO5 = 5 + GPIO6 = 6 + GPIO12 = 12 + GPIO13 = 13 + GPIO19 = 19 + GPIO16 = 16 + GPIO26 = 26 + GPIO20 = 20 + GPIO21 = 21 + + def __init__(self): + pass diff --git a/examples/robot_hat/pwm.py b/examples/robot_hat/pwm.py new file mode 100644 index 0000000..676294f --- /dev/null +++ b/examples/robot_hat/pwm.py @@ -0,0 +1,141 @@ +import smbus, math +from .i2c import I2C + +timer = [ + { + "arr": 0 + } +] * 4 + +class PWM(I2C): + REG_CHN = 0x20 + REG_FRE = 0x30 + REG_PSC = 0x40 + REG_ARR = 0x44 + + ADDR = 0x14 + + CLOCK = 72000000 + + def __init__(self, channel, debug="critical"): + super().__init__() + if isinstance(channel, str): + if channel.startswith("P"): + channel = int(channel[1:]) + else: + raise ValueError("PWM channel should be between [P1, P14], not {0}".format(channel)) + try: + self.send(0x2C, self.ADDR) + self.send(0, self.ADDR) + self.send(0, self.ADDR) + except IOError: + self.ADDR = 0x15 + + self.debug = debug + self._debug("PWM address: {:02X}".format(self.ADDR)) + self.channel = channel + self.timer = int(channel/4) + self.bus = smbus.SMBus(1) + self._pulse_width = 0 + self._freq = 50 + self.freq(50) + + def i2c_write(self, reg, value): + value_h = value >> 8 + value_l = value & 0xff + self._debug("i2c write: [0x%02X, 0x%02X, 0x%02X, 0x%02X]"%(self.ADDR, reg, value_h, value_l)) + self.send([reg, value_h, value_l], self.ADDR) + + def freq(self, *freq): + if len(freq) == 0: + return self._freq + else: + self._freq = int(freq[0]) + # [prescaler,arr] list + result_ap = [] + # accuracy list + result_acy = [] + # middle value for equal arr prescaler + st = int(math.sqrt(self.CLOCK/self._freq)) + # get -5 value as start + st -= 5 + # prevent negetive value + if st <= 0: + st = 1 + for psc in range(st,st+10): + arr = int(self.CLOCK/self._freq/psc) + result_ap.append([psc, arr]) + result_acy.append(abs(self._freq-self.CLOCK/psc/arr)) + i = result_acy.index(min(result_acy)) + psc = result_ap[i][0] + arr = result_ap[i][1] + self._debug("prescaler: %s, period: %s"%(psc, arr)) + self.prescaler(psc) + self.period(arr) + + def prescaler(self, *prescaler): + if len(prescaler) == 0: + return self._prescaler + else: + self._prescaler = int(prescaler[0]) - 1 + reg = self.REG_PSC + self.timer + self._debug("Set prescaler to: %s"%self._prescaler) + self.i2c_write(reg, self._prescaler) + + def period(self, *arr): + global timer + if len(arr) == 0: + return timer[self.timer]["arr"] + else: + timer[self.timer]["arr"] = int(arr[0]) - 1 + reg = self.REG_ARR + self.timer + self._debug("Set arr to: %s"%timer[self.timer]["arr"]) + self.i2c_write(reg, timer[self.timer]["arr"]) + + def pulse_width(self, *pulse_width): + if len(pulse_width) == 0: + return self._pulse_width + else: + self._pulse_width = int(pulse_width[0]) + reg = self.REG_CHN + self.channel + self.i2c_write(reg, self._pulse_width) + + def pulse_width_percent(self, *pulse_width_percent): + global timer + if len(pulse_width_percent) == 0: + return self._pulse_width_percent + else: + self._pulse_width_percent = pulse_width_percent[0] + temp = self._pulse_width_percent / 100.0 + print(temp) + pulse_width = temp * timer[self.timer]["arr"] + self.pulse_width(pulse_width) + + +def test(): + import time + p = PWM(0) + # p.debug = 'debug' + p.period(1000) + p.prescaler(10) + # p.pulse_width(2048) + while True: + for i in range(0, 4095, 10): + p.pulse_width(i) + print(i) + time.sleep(1/4095) + time.sleep(1) + for i in range(4095, 0, -10): + p.pulse_width(i) + print(i) + time.sleep(1/4095) + time.sleep(1) + +def test2(): + p = PWM("P0") + while True: + p.pulse_width_percent(50) + + +if __name__ == '__main__': + test2() \ No newline at end of file diff --git a/examples/robot_hat/robot.py b/examples/robot_hat/robot.py new file mode 100644 index 0000000..97fbd1d --- /dev/null +++ b/examples/robot_hat/robot.py @@ -0,0 +1,107 @@ +from .pwm import PWM +from .servo import Servo +import time +import math +from .filedb import fileDB + +class Robot(): + move_list = {} + PINS = [None, "P0","P1","P2","P3","P4","P5","P6","P7","P8","P9","P10","P11"] + def __init__(self, pin_list, group=4, db='/opt/ezblock/config'): + self.pin_list = [] + for i in range(0, len(pin_list), group): + _pin_list = pin_list[i:i+group] + for pin in _pin_list: + pwm = PWM(self.PINS[pin]) + servo = Servo(pwm) + servo.angle(0) + self.pin_list.append(servo) + time.sleep(0.2) + self.pin_num = len(pin_list) + self.origin_positions = self.new_list(0) + self.db = fileDB(db=db) + temp = self.db.get('servo_offset_list', default_value=str(self.new_list(0))) + temp = [float(i.strip()) for i in temp.strip("[]").split(",")] + self.offset = temp + self.servo_positions = self.new_list(0) + self.calibrate_position = self.new_list(0) + self.direction = self.new_list(1) + + def new_list(self, default_value): + _ = [default_value] * self.pin_num + return _ + + def angle_list(self, angle_list): + for i in range(self.pin_num): + self.pin_list[i].angle(angle_list[i]) + + def servo_write_all(self, angles): + rel_angles = [] # ralative angle to home + for i in range(self.pin_num): + rel_angles.append(self.direction[i] * (self.origin_positions[i] + angles[i] + self.offset[i])) + # rel_angles.append(angles[i]) + # print(rel_angles) + self.angle_list(rel_angles) + + def servo_move(self, targets, speed=50, bpm=None): + ''' + calculate the max delta angle, multiply by 2 to define a max_step + loop max_step times, every servo add/minus 1 when step reaches its adder_flag + ''' + # sprint("Servo_move") + speed = max(0, speed) + speed = min(100, speed) + delta = [] + absdelta = [] + max_step = 0 + steps = [] + + for i in range(self.pin_num): + value = targets[i] - self.servo_positions[i] + delta.append(value) + absdelta.append(abs(value)) + + max_step = int(1*max(absdelta)) + if max_step != 0: + for i in range(self.pin_num): + step = float(delta[i])/max_step + steps.append(step) + + if bpm != None: + step_time = 1 / bpm * 60 + step_delay = step_time / max_step + for _ in range(max_step): + for j in range(self.pin_num): + self.servo_positions[j] += steps[j] + self.servo_write_all(self.servo_positions) + #5~5005us + if bpm != None: + time.sleep(step_delay) + else: + t = (100-speed)*50+5 + time.sleep(t/100000) + + def do_action(self,motion_name, step=1, speed=50): + for _ in range(step): + for motion in self.move_list[motion_name]: + self.servo_move(motion, speed) + + def set_offset(self,offset_list): + offset_list = [ min(max(offset, -20), 20) for offset in offset_list] + temp = str(offset_list) + self.db.set('servo_offset_list',temp) + self.offset = offset_list + # self.calibration() + # self.reset() + + def calibration(self): + self.servo_positions = self.calibrate_position + self.servo_write_all(self.servo_positions) + + def reset(self,): + self.servo_positions = self.new_list(0) + self.servo_write_all(self.servo_positions) + + def soft_reset(self,): + temp_list = self.new_list(0) + self.servo_write_all(temp_list) diff --git a/examples/robot_hat/servo.py b/examples/robot_hat/servo.py new file mode 100644 index 0000000..ced1654 --- /dev/null +++ b/examples/robot_hat/servo.py @@ -0,0 +1,41 @@ +from .basic import _Basic_class +import time + +class Servo(_Basic_class): + MAX_PW = 2500 + MIN_PW = 500 + _freq = 50 + def __init__(self, pwm): + super().__init__() + self.pwm = pwm + self.pwm.period(4095) + prescaler = int(float(self.pwm.CLOCK) /self.pwm._freq/self.pwm.period()) + self.pwm.prescaler(prescaler) + # self.angle(90) + + # angle ranges -90 to 90 degrees + def angle(self, angle): + if not (isinstance(angle, int) or isinstance(angle, float)): + raise ValueError("Angle value should be int or float value, not %s"%type(angle)) + if angle < -90: + angle = -90 + if angle > 90: + angle = 90 + High_level_time = self.map(angle, -90, 90, self.MIN_PW, self.MAX_PW) + self._debug("High_level_time: %f" % High_level_time) + pwr = High_level_time / 20000 + self._debug("pulse width rate: %f" % pwr) + value = int(pwr*self.pwm.period()) + self._debug("pulse width value: %d" % value) + self.pwm.pulse_width(value) + +def test(): + from ezblock import PWM + print("Test") + p = PWM("P0") + s0 = Servo(p) + s0.debug = "debug" + s0.angle(90) + +if __name__ == "__main__": + test() \ No newline at end of file diff --git a/examples/robot_hat/spi.py b/examples/robot_hat/spi.py new file mode 100644 index 0000000..55adaf9 --- /dev/null +++ b/examples/robot_hat/spi.py @@ -0,0 +1,5 @@ +import spidev +class SPI(object): + def __init__(self, bus, device): + spi = spidev.SPiDev() + spi.open(bus, device) diff --git a/examples/robot_hat/switch.py b/examples/robot_hat/switch.py new file mode 100644 index 0000000..aad3477 --- /dev/null +++ b/examples/robot_hat/switch.py @@ -0,0 +1,14 @@ +from .pin import Pin + +class Switch(Pin): + def __init__(self): + super().__init__() + + def __call__(self): + return self.value() + + def value(self): + return not super().value() + + def callback(self, func=None): + self.irq(handler=func, trigger=self.FALLING) diff --git a/examples/robot_hat/tts.py b/examples/robot_hat/tts.py new file mode 100644 index 0000000..c92782c --- /dev/null +++ b/examples/robot_hat/tts.py @@ -0,0 +1,79 @@ +from .basic import _Basic_class +from .utils import mapping, is_installed +from .music import Music +from distutils.spawn import find_executable + +class TTS(_Basic_class): + _class_name = 'TTS' + SUPPORTED_LANGUAUE = [ + 'zh-CN', # 普通话(中国) + 'en-US', # 英语(美国)English-United States + 'en-GB', # 英语(英国)English-United Kingdom + 'de-DE', # 德语(德国)Germany-Deutsch + 'es-ES', # 西班牙语(西班牙)España-Español + 'fr-FR', # 法语(法国)France-Le français + 'it-IT', # 意大利语(意大利)Italia-lingua italiana + ] + + def __init__(self, engine='espeak'): + super().__init__() + self._lang = "en-US" # 默认输入的语言为英语 + self.engine = engine + if (engine == "espeak"): + if not is_installed("espeak"): + raise Exception("TTS engine: espeak is not installed.") + self._amp = 100 + self._speed = 175 + self._gap = 5 + self._pitch = 50 + + def _check_executable(self, executable): + executable_path = find_executable(executable) + found = executable_path is not None + return found + + def say(self, words): # 输入的文字 + eval(f"self.{self.engine}(words)") + + def espeak(self, words): + self._debug('espeak:\n [%s]' % (words)) + if not self._check_executable('espeak'): + self._debug('espeak is busy. Pass') + + cmd = 'espeak -a%d -s%d -g%d -p%d \"%s\" --stdout | aplay 2>/dev/null & ' % (self._amp, self._speed, self._gap, self._pitch, words) + self.run_command(cmd) + self._debug('command: %s' %cmd) + + def lang(self, *value): # 切换语言,可识别5种语言 + if len(value) == 0: + return self._lang + elif len(value) == 1: + v = value[0] + if v in self.SUPPORTED_LANGUAUE: + self._lang = v + return self._lang + raise ValueError("Arguement \"%s\" is not supported. run tts.supported_lang to get supported language type."%value) + + def supported_lang(self): # 返回支持的语言类型 + return self.SUPPORTED_LANGUAUE + + def espeak_params(self, amp=None, speed=None, gap=None, pitch=None): + if amp == None: + amp=self._amp + if speed == None: + speed=self._speed + if gap == None: + gap=self._gap + if pitch == None: + pitch=self._pitch + + if amp not in range(0, 200): + raise ValueError('Amp should be in 0 to 200, not "{0}"'.format(amp)) + if speed not in range(80, 260): + raise ValueError('speed should be in 80 to 260, not "{0}"'.format(speed)) + if pitch not in range(0, 99): + raise ValueError('pitch should be in 0 to 99, not "{0}"'.format(pitch)) + self._amp = amp + self._speed = speed + self._gap = gap + self._pitch = pitch diff --git a/examples/robot_hat/utils.py b/examples/robot_hat/utils.py new file mode 100644 index 0000000..ce5875c --- /dev/null +++ b/examples/robot_hat/utils.py @@ -0,0 +1,44 @@ +import time +import os +import re +import math + +def delay(ms): + time.sleep(ms/1000) + +def set_volume(value): + value = min(100, max(0, value)) + cmd = "sudo amixer -M sset 'PCM' %d%%" % value + os.system(cmd) + +def run_command(cmd): + import subprocess + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + result = p.stdout.read().decode('utf-8') + status = p.poll() + return status, result + +def is_installed(cmd): + status, _ = run_command("%s -v"%cmd) + # 0 only tested under "espeak -v" + if status in [0,]: + return True + else: + return False + +def mapping(x, in_min, in_max, out_min, out_max): + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min + +def getIP(ifaces=['wlan0', 'eth0']): + if isinstance(ifaces, str): + ifaces = [ifaces] + for iface in list(ifaces): + search_str = 'ip addr show {}'.format(iface) + result = os.popen(search_str).read() + com = re.compile(r'(?<=inet )(.*)(?=\/)', re.M) + ipv4 = re.search(com, result) + if ipv4: + ipv4 = ipv4.groups()[0] + return ipv4 + return False diff --git a/examples/robot_hat/vilib.py b/examples/robot_hat/vilib.py new file mode 100644 index 0000000..75b841f --- /dev/null +++ b/examples/robot_hat/vilib.py @@ -0,0 +1,1600 @@ +import numpy as np +import cv2 +import threading + +from importlib import import_module +import os +from flask import Flask, render_template, Response +from multiprocessing import Process, Manager +import time +import tflite_runtime.interpreter as tflite +from pyzbar import pyzbar +import datetime + +from picamera.array import PiRGBArray +from picamera import PiCamera +from PIL import Image, ImageDraw, ImageFont +import threading +from .utils import run_command + +# face_recognizer = cv2.face.LBPHFaceRecognizer_create() +# face_recognizer.read("/home/pi/face_recognizer.yml") +# master_name = ["","chentao","zhangguoliang"] + +traffic_num_list = [i for i in range(4)] +ges_num_list = [i for i in range(3)] +# ges_list = [chr(i) for i in range(97,101)] +# ges_list.remove('j') + +traffic_list = ['stop','right','left','forward'] +gesture_list = ["paper","scissor","rock"] +# rock, scissor, paper + +traffic_dict = dict(zip(traffic_num_list,traffic_list)) +ges_dict = dict(zip(ges_num_list,gesture_list)) + + +# test_image_dir = './ges_pic/' +# traffic_sign_model_path = "/home/pi/sport_camera/example/tf_150_dr0.2.tflite" +# gesture_model_path = "/home/pi/sport_camera/example/3bak_ges_200_dr0.2.tflite" + +traffic_sign_model_path = "/home/pi/sport_camera/picarx_python_example/tf_150_dr0.2.tflite" +gesture_model_path = "/home/pi/sport_camera/picarx_python_example/3bak_ges_200_dr0.2.tflite" +# gesture_model_path = "/home/pi/sport_camera/example/mb1_gesture_200_dr0.2.tflite" + + + +interpreter_1 = tflite.Interpreter(model_path=traffic_sign_model_path) +interpreter_1.allocate_tensors() + +interpreter_2 = tflite.Interpreter(model_path=gesture_model_path) +interpreter_2.allocate_tensors() + +# Get input and output tensors. +input_details_1 = interpreter_1.get_input_details() +# print(str(input_details_1)) +output_details_1 = interpreter_1.get_output_details() +# print(str(output_details_1)) + + +# Get input and output tensors. +input_details_2 = interpreter_2.get_input_details() +# print(str(input_details_2)) +output_details_2 = interpreter_2.get_output_details() +# print(str(output_details_2)) + + +app = Flask(__name__) +@app.route('/') +def index(): + """Video streaming home page.""" + return render_template('index.html') + +def get_frame(): + return cv2.imencode('.jpg', Vilib.img_array[0])[1].tobytes() + + +def get_qrcode_pictrue(): + return cv2.imencode('.jpg', Vilib.img_array[1])[1].tobytes() + +def get_png_frame(): + return cv2.imencode('.png', Vilib.img_array[0])[1].tobytes() + +def gen(): + """Video streaming generator function.""" + while True: + # start_time = time.time() + # frame = cv2.imread("123.jpeg")Vilib.q.get() + # print("1") + # if Vilib.conn2.recv() + # frame = cv2.imencode('.jpg', Vilib.conn2.recv())[1].tobytes() + # rt_img = np.ones((320,240),np.uint8) + # print("2") + frame = get_frame() + yield (b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') + time.sleep(0.03) + # end_time = time.time() - start_time + # print(int(1/end_time)) + +@app.route('/mjpg') ## video +def video_feed(): + # from camera import Camera + """Video streaming route. Put this in the src attribute of an img tag.""" + response = Response(gen(), + mimetype='multipart/x-mixed-replace; boundary=frame') + response.headers.add("Access-Control-Allow-Origin", "*") + return response + +@app.route('/mjpg.jpg') ##picture +def video_feed_jpg(): + # from camera import Camera + """Video streaming route. Put this in the src attribute of an img tag.""" + # path = "/home/pi/sport_camera/example/cali.jpg" + response = Response(get_frame(), mimetype="image/jpeg") + response.headers.add("Access-Control-Allow-Origin", "*") + return response + +@app.route('/mjpg.png') ##picture +def video_feed_png(): + # from camera import Camera + """Video streaming route. Put this in the src attribute of an img tag.""" + # path = "/home/pi/sport_camera/example/cali.jpg" + response = Response(get_png_frame(), mimetype="image/png") + response.headers.add("Access-Control-Allow-Origin", "*") + return response + +# @app.route('/mjpg.jpg') ##picture +# def video_feed_jpg(): + # from camera import Camera + """Video streaming route. Put this in the src attribute of an img tag.""" + # path = "/home/pi/sport_camera/example/cali.jpg" + # return Response(get_qrcode_pictrue(), mimetype="image/jpeg") +# @app.route('/mjpg.jpg', methods=['post', 'get']) +# def video_feed_jpg(): +# path = request.args.get('path') +# print(path) +# path = "/home/pi/http.jpg/%s" % path + +# resp = Response(open(path, 'rb'), mimetype="image/jpeg") +# return resp + + +def web_camera_start(): + app.run(host='0.0.0.0', port=9000,threaded=True) + +EFFECTS = [ + "none", + "negative",# + "solarize",# + # "sketch", + # "denoise", + "emboss",# + # "oilpaint", + # "hatch", + # "gpen", + # "pastel", + # "watercolor", + # "film", + # "blur", + # "saturation", + # "colorswap", + # "washedout", + "posterise",# + # "colorpoint", + # "colorbalance", + "cartoon",# + # "deinterlace1", + # "deinterlace2", +] + +Camera_SETTING = [ + "resolution", #max(4056,3040) + #"framerate + "rotation", #(0 90 180 270) + # "shutter_speed", + "brightness", # 0~100 default 50 + "sharpness", # -100~100 default 0 + "contrast", # -100~100 default 0 + "saturation", # -100~100 default 0 + "iso", #Vaild value:0(auto) 100,200,320,400,500,640,800 + "exposure_compensation", # -25~25 default 0 + "exposure_mode", #Valid values are: 'off', 'auto' (default),'night', 'nightpreview', 'backlight', 'spotlight', 'sports', 'snow', 'beach','verylong', 'fixedfps', 'antishake', or 'fireworks' + "meter_mode", #Valid values are: 'average' (default),'spot', 'backlit', 'matrix'. + "awb_mode", #'off', 'auto' (default), ‘sunlight', 'cloudy', 'shade', 'tungsten', 'fluorescent','incandescent', 'flash', or 'horizon'. + "hflip", # Default:False ,True + "vflip", # Default:False ,True + # "crop", #Retrieves or sets the zoom applied to the camera’s input, as a tuple (x, y, w, h) of floating point + #values ranging from 0.0 to 1.0, indicating the proportion of the image to include in the output + #(the ‘region of interest’). The default value is (0.0, 0.0, 1.0, 1.0), which indicates that everything + #should be included. +] + +time_font = lambda x: ImageFont.truetype('/home/pi/sport_camera/picarx_python_example/Roboto-Light-2.ttf', int(x / 320.0 * 6)) +text_font = lambda x: ImageFont.truetype('/home/pi/sport_camera/picarx_python_example/Roboto-Light-2.ttf', int(x / 320.0 * 10)) +company_font = lambda x: ImageFont.truetype('/home/pi/sport_camera/picarx_python_example/Roboto-Light-2.ttf', int(x / 320.0 * 8)) + + +def add_text_to_image(name, text_1): + # rgba_image = image.convert('RGB') + # text_overlay = Image.new('RGB', rgba_image.size, (255, 255, 255)) + image_target = Image.open(name) + + image_draw = ImageDraw.Draw(image_target) + + + time_text = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + time_size_x, time_size_y = image_draw.textsize(time_text, font=time_font(image_target.size[0])) + text_size_x, text_size_y = image_draw.textsize(text_1, font=text_font(image_target.size[0])) + + # 设置文本文字位置 + # print(rgba_image) + time_xy = (image_target.size[0] - time_size_x - time_size_y, image_target.size[1] - int(1.5*time_size_y)) + text_xy = (text_size_y, image_target.size[1] - int(1.5*text_size_y)) + company_xy = (text_size_y, image_target.size[1] - int(1.5*text_size_y) - text_size_y) + + # 设置文本颜色和透明度 + image_draw.text(time_xy, time_text, font=time_font(image_target.size[0]), fill=(255, 255, 255)) + image_draw.text(company_xy, text_1, font=text_font(image_target.size[0]), fill=(255, 255, 255)) + # image_draw.text(text_xy, text_2, font=company_font(image_target.size[0]), fill=(255, 255, 255)) + # run_command("sudo rm " + str(name)) + image_target.save(name,quality=95,subsampling=0)# + + +class Vilib(object): + + video_flag = False + # video_path = './video_file/tst.avi' + + # picture_path = './picture_file' + # video_recorder = cv2.VideoWriter(video_path, fourcc, 20.0, (320, 240)) + + face_cascade = cv2.CascadeClassifier('/home/pi/sport_camera/picarx_python_example/haarcascade_frontalface_default.xml') + kernel_5 = np.ones((5,5),np.uint8)#4x4的卷积核 + # color_default = 'blue' + # color_dict = {'red':[0,4],'orange':[5,18],'yellow':[22,37],'green':[42,85],'blue':[92,110],'purple':[115,165],'red_2':[166,180]} + # lower_color = np.array([min(color_dict[detect_obj_parameter['color_default']]), 60, 60]) + # upper_color = np.array([max(color_dict[detect_obj_parameter['color_default']]), 255, 255]) + # hdf_flag = False + # cdf_flag = False + # stf_flag = False + video_source = 0 + roi = cv2.imread("/home/pi/sport_camera/picarx_python_example/cali.jpg") + roi_hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) + + # obj_roi = cv2.imread("/home/pi/sport_camera/example/object.jpg") + # h,w = obj_roi.shape[:2] + + # obj_hsv = cv2.cvtColor(obj_roi, cv2.COLOR_BGR2HSV) + + # track_window = (0, 0, w, h) + + # roi_hist = cv2.calcHist([obj_hsv],[0,1],None,[180,256],[0,180,0,256]) #计算直方图 + # cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX) + + + # human_object_counter = 0 + # detect_obj_parameter = np.array([0,0]) + # human_object_size = np.array([0,0]) + + # color_object_counter = 0 + detect_obj_parameter = Manager().dict() + img_array = Manager().list(range(2)) +#Color_obj_parameter + detect_obj_parameter['color_default'] = 'red' + + color_dict = {'red':[0,4],'orange':[5,18],'yellow':[22,37],'green':[42,85],'blue':[92,110],'purple':[115,165],'red_2':[165,180]} + # lower_color = np.array([min(color_dict[detect_obj_parameter['color_default']]), 60, 60]) + # upper_color = np.array([max(color_dict[detect_obj_parameter['color_default']]), 255, 255]) + + detect_obj_parameter['color_x'] = 320 + detect_obj_parameter['color_y'] = 240 + detect_obj_parameter['color_w'] = 0 + detect_obj_parameter['color_h'] = 0 + detect_obj_parameter['color_n'] = 0 + detect_obj_parameter['lower_color'] = np.array([min(color_dict[detect_obj_parameter['color_default']]), 60, 60]) + detect_obj_parameter['upper_color'] = np.array([max(color_dict[detect_obj_parameter['color_default']]), 255, 255]) + + +#Human_obj_parameter + detect_obj_parameter['human_x'] = 320 + detect_obj_parameter['human_y'] = 240 + detect_obj_parameter['human_w'] = 0 + detect_obj_parameter['human_h'] = 0 + detect_obj_parameter['human_n'] = 0 + +#traffic_sign_obj_parameter + detect_obj_parameter['traffic_sign_x'] = 320 + detect_obj_parameter['traffic_sign_y'] = 240 + detect_obj_parameter['traffic_sign_w'] = 0 + detect_obj_parameter['traffic_sign_h'] = 0 + detect_obj_parameter['traffic_sign_t'] = 'None' + detect_obj_parameter['traffic_sign_acc'] = 0 + +#gesture_obj_parameter + detect_obj_parameter['gesture_x'] = 320 + detect_obj_parameter['gesture_y'] = 240 + detect_obj_parameter['gesture_w'] = 0 + detect_obj_parameter['gesture_h'] = 0 + detect_obj_parameter['gesture_t'] = 'None' + detect_obj_parameter['gesture_acc'] = 0 + # detect_obj_parameter['human_n'] = 0 + + +#detect_switch + detect_obj_parameter['hdf_flag'] = False + detect_obj_parameter['cdf_flag'] = False + detect_obj_parameter['ts_flag'] = False + detect_obj_parameter['gs_flag'] = False + detect_obj_parameter['calibrate_flag'] = False + detect_obj_parameter['object_follow_flag'] = False + detect_obj_parameter['qr_flag'] = False + +#QR_code + detect_obj_parameter['qr_data'] = "None" + detect_obj_parameter['qr_x'] = 320 + detect_obj_parameter['qr_y'] = 240 + detect_obj_parameter['qr_w'] = 0 + detect_obj_parameter['qr_h'] = 0 +#video + # detect_obj_parameter['vi_fps'] = 20 + # detect_obj_parameter['video_flag'] = False + # detect_obj_parameter['video_path'] = './video_file/1.avi' + + # detect_obj_parameter['new_video'] = False + # detect_obj_parameter['process_video'] = True + +#picture + detect_obj_parameter['picture_flag'] = False + detect_obj_parameter['process_picture'] = True + detect_obj_parameter['picture_path'] = '/home/pi/picture_file/' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')+ '.jpg' + # detect_obj_parameter['color_default'] = 'red' + + # color_dict = {'red':[0,4],'orange':[5,18],'yellow':[22,37],'green':[42,85],'blue':[92,110],'purple':[115,165],'red_2':[166,180]} + # lower_color = np.array([min(color_dict[detect_obj_parameter['color_default']]), 60, 60]) + # upper_color = np.array([max(color_dict[detect_obj_parameter['color_default']]), 255, 255]) + detect_obj_parameter['video_flag'] = None + + detect_obj_parameter['ensure_flag'] = False + detect_obj_parameter['clarity_val'] = 0 + +#diy + detect_obj_parameter['human_n'] = 0 + # detect_obj_parameter['hdf_flag'] = False + +#picture + detect_obj_parameter['eff'] = 0 + detect_obj_parameter['setting'] = 0 + detect_obj_parameter['setting_flag'] = False + detect_obj_parameter['setting_val'] = 0 + # detect_obj_parameter['current_setting_val'] = None + detect_obj_parameter['setting_resolution'] = (3840,2880) + detect_obj_parameter['change_setting_flag'] = False + detect_obj_parameter['change_setting_type'] = 'None' + detect_obj_parameter['change_setting_val'] = 0 + + detect_obj_parameter['photo_button_flag'] = False + detect_obj_parameter['content_length'] = 0 + detect_obj_parameter['content_num'] = 0 + detect_obj_parameter['process_content_1'] = [] + detect_obj_parameter['process_si'] = [] + # detect_obj_parameter['process_dict'] = {} + + detect_obj_parameter['watermark_flag'] = True + detect_obj_parameter['camera_flip'] = False + detect_obj_parameter['watermark'] = "Shot by Picar-x" + # detect_obj_parameter['google_upload_flag'] = False + + rt_img = np.ones((320,240),np.uint8) + front_view_img = np.zeros((240,320,3), np.uint8) +# 使用白色填充图片区域,默认为黑色 + # front_view_img.fill(255) + img_array[0] = rt_img + img_array[1] = rt_img + # img_array = rt_img + vi_img = np.ones((320,240),np.uint8) + + + # @staticmethod + # def clarity_val(): + # return Vilib.detect_obj_parameter['clarity_val'] + + + # @staticmethod + # def camera_start(): + # from multiprocessing import Process + + + # worker_2 = Process(name='worker 2',target=Ras_Cam.camera_clone) + # worker_1 = Process(name='worker 1',target=dis) + # worker_3 = Process(name='worker 3',target=web_camera_start) + # worker_1.start() + # worker_2.start() + # worker_3.start() + + # @staticmethod + # def gamma_method(img): + # (b, g, r) = cv2.split(img) + # b_mean = np.mean(b) + # g_mean = np.mean(g) + # r_mean = np.mean(r) + + # b_gamma_val = math.log10(0.5)/math.log10(b_mean/255) + # g_gamma_val = math.log10(0.5)/math.log10(g_mean/255) + # r_gamma_val = math.log10(0.5)/math.log10(r_mean/255) + + # b_gamma_table = [np.power(x / 255.0, b_gamma_val) * 255.0 for x in range(256)] # 建立映射表 + # g_gamma_table = [np.power(x / 255.0, g_gamma_val) * 255.0 for x in range(256)] # 建立映射表 + # r_gamma_table = [np.power(x / 255.0, r_gamma_val) * 255.0 for x in range(256)] # 建立映射表 + + # b_gamma_table = np.round(np.array(b_gamma_table)).astype(np.uint8) # 颜色值为整数 + # g_gamma_table = np.round(np.array(g_gamma_table)).astype(np.uint8) # 颜色值为整数 + # r_gamma_table = np.round(np.array(r_gamma_table)).astype(np.uint8) # 颜色值为整数 + + # bh = cv2.LUT(b, b_gamma_table) # 图片颜色查表。另外可以根据光强(颜色)均匀化原则设计自适应算法。 + # gh = cv2.LUT(g, g_gamma_table) # 图片颜色查表。另外可以根据光强(颜色)均匀化原则设计自适应算法。 + # rh = cv2.LUT(r, r_gamma_table) # 图片颜色查表。另外可以根据光强(颜色)均匀化原则设计自适应算法。 + + + # # cv2.normalize(b,b, 0, 255, cv2.NORM_MINMAX) + # # cv2.normalize(g,g, 0, 255, cv2.NORM_MINMAX) + # # cv2.normalize(r,r, 0, 255, cv2.NORM_MINMAX) + # # #归一化 + # # bh= cv2.convertScaleAbs(b) + # # gh= cv2.convertScaleAbs(g) + # # rh= cv2.convertScaleAbs(r) + # # #将格式从uint16转为uint8 + + # # fgamma = 120.0 + # # img_gamma = np.power((bh/255.0),1/fgamma)*255.0 + # # img_gamma = np.power((gh/255.0),1/fgamma)*255.0 + # # img_gamma = np.power((rh/255.0),1/fgamma)*255.0 + + # img = cv2.merge((bh, gh, rh)) + + # return img + + @staticmethod + def photo_effect(shirt_way = 'Shift_left'): + print(shirt_way) + shirt_way = str(shirt_way) + if shirt_way == 'Shift_left': + Vilib.detect_obj_parameter['eff'] += 1 + if Vilib.detect_obj_parameter['eff'] >= len(EFFECTS): + Vilib.detect_obj_parameter['eff'] = 0 + elif shirt_way == 'Shift_right': + Vilib.detect_obj_parameter['eff'] -= 1 + if Vilib.detect_obj_parameter['eff'] < 0: + Vilib.detect_obj_parameter['eff'] = len(EFFECTS) - 1 + else: + raise Exception("parameter error!") + + + # @staticmethod + # def change_show_setting(shirt_way = 'None'): + # global button_motion + # if shirt_way == 'Shift_left': + # Vilib.detect_obj_parameter['setting'] += 1 + # if Vilib.detect_obj_parameter['setting'] >= len(Camera_SETTING): + # Vilib.detect_obj_parameter['setting'] = 0 + + # elif shirt_way == 'Shift_right': + # Vilib.detect_obj_parameter['setting'] -= 1 + # if Vilib.detect_obj_parameter['setting'] < 0: + # Vilib.detect_obj_parameter['setting'] = len(Camera_SETTING) - 1 + + # elif shirt_way == 'None': + # pass + + # else: + # raise Exception("parameter error!") + + + # # print(Camera_SETTING[Ras_Cam.detect_obj_parameter['setting']]) + # if type(Vilib.detect_obj_parameter['setting_val']) == str: + # Vilib.detect_obj_parameter['setting_val'] = "'" + Vilib.detect_obj_parameter['setting_val'] + "'" + # return Camera_SETTING[Vilib.detect_obj_parameter['setting']], Vilib.detect_obj_parameter['setting_val'] + + + + # @staticmethod + # def google_upload(flag): + # # global button_motion + # Ras_Cam.detect_obj_parameter['google_upload_flag'] = flag + + @staticmethod + def video_flag(flag): + # global button_motion + Vilib.detect_obj_parameter['video_flag'] = flag + + # @staticmethod + # def show_content(id_num,content,content_coordinate,content_color,font_size): + + # cmd_test = "Vilib.detect_obj_parameter['process_content_" + str(id_num) + "'" + "] = [" + "'" + str(content) + "'" + "," + str(content_coordinate)+","+str(content_color)+","+str(font_size)+"]" + # exec(cmd_test) + # if id_num > Vilib.detect_obj_parameter['content_num']: + # Vilib.detect_obj_parameter['content_num'] = id_num + + @staticmethod + def watermark(watermark = "Shot by Picar-x"): + # global button_motion + watermark = str(watermark) + Vilib.detect_obj_parameter['watermark_flag'] = True + Vilib.detect_obj_parameter['watermark'] = watermark + + @staticmethod + def show_setting(flag): + # global button_motion + + Vilib.detect_obj_parameter['setting_flag'] = flag + # button_motion = 'free' + + @staticmethod + def change_setting_type_val(setting_type,setting_val): + # global button_motion + if setting_type == 'resolution': + Vilib.detect_obj_parameter['setting_resolution'] = setting_val + else: + Vilib.detect_obj_parameter['change_setting_type'] = setting_type + Vilib.detect_obj_parameter['change_setting_val'] = setting_val + Vilib.detect_obj_parameter['change_setting_flag'] = True + + + @staticmethod + def shuttle_button(): + # global button_motion + Vilib.detect_obj_parameter['photo_button_flag'] = True + # button_motion = 'free' + + + @staticmethod + def make_qrcode_picture(data): + # data = 'hello' + # 生成二维码 + Vilib.img_array = qrcode.make(data=data) + + + @staticmethod + def color_detect_object(obj_parameter): + if obj_parameter == 'x': + # print(Vilib.detect_obj_parameter['x']) + return int(Vilib.detect_obj_parameter['color_x']/214.0)-1 + elif obj_parameter == 'y': + # print(Vilib.detect_obj_parameter['y']) + return -1*(int(Vilib.detect_obj_parameter['color_y']/160.2)-1) #max_size_object_coordinate_y + elif obj_parameter == 'width': + return Vilib.detect_obj_parameter['color_w'] #objects_max_width + elif obj_parameter == 'height': + return Vilib.detect_obj_parameter['color_h'] #objects_max_height + elif obj_parameter == 'number': + return Vilib.detect_obj_parameter['color_n'] #objects_count + return None + + @staticmethod + def human_detect_object(obj_parameter): + if obj_parameter == 'x': + # print(Vilib.detect_obj_parameter['x']) + return int(Vilib.detect_obj_parameter['human_x']/214.0)-1 + elif obj_parameter == 'y': + # print(Vilib.detect_obj_parameter['y']) + return -1*(int(Vilib.detect_obj_parameter['human_y']/160.2)-1) #max_size_object_coordinate_y + elif obj_parameter == 'width': + return Vilib.detect_obj_parameter['human_w'] #objects_max_width + elif obj_parameter == 'height': + return Vilib.detect_obj_parameter['human_h'] #objects_max_height + elif obj_parameter == 'number': + return Vilib.detect_obj_parameter['human_n'] #objects_count + return None + + @staticmethod + def traffic_sign_detect_object(obj_parameter): + if obj_parameter == 'x': + # print(Vilib.detect_obj_parameter['x']) + return int(Vilib.detect_obj_parameter['traffic_sign_x']/214.0)-1 + elif obj_parameter == 'y': + # print(Vilib.detect_obj_parameter['y']) + return -1*(int(Vilib.detect_obj_parameter['traffic_sign_y']/160.2)-1) #max_size_object_coordinate_y + elif obj_parameter == 'width': + return Vilib.detect_obj_parameter['traffic_sign_w'] #objects_max_width + elif obj_parameter == 'height': + return Vilib.detect_obj_parameter['traffic_sign_h'] #objects_max_height + # elif obj_parameter == 'number': + # return Vilib.detect_obj_parameter['traffic_sign_n'] #objects_count + elif obj_parameter == 'type': + return Vilib.detect_obj_parameter['traffic_sign_t'] #objects_type + elif obj_parameter == 'accuracy': + return Vilib.detect_obj_parameter['traffic_sign_acc'] #objects_type + return 'none' + + @staticmethod + def gesture_detect_object(obj_parameter): + if obj_parameter == 'x': + # print(Vilib.detect_obj_parameter['x']) + return int(Vilib.detect_obj_parameter['gesture_x']/214.0)-1 + elif obj_parameter == 'y': + # print(Vilib.detect_obj_parameter['y']) + return -1*(int(Vilib.detect_obj_parameter['gesture_y']/160.2)-1) #max_size_object_coordinate_y + elif obj_parameter == 'width': + return Vilib.detect_obj_parameter['gesture_w'] #objects_max_width + elif obj_parameter == 'height': + return Vilib.detect_obj_parameter['gesture_h'] #objects_max_height + elif obj_parameter == 'type': + return Vilib.detect_obj_parameter['gesture_t'] #objects_type + elif obj_parameter == 'accuracy': + return Vilib.detect_obj_parameter['gesture_acc'] #objects_type + return 'none' + + @staticmethod + def qrcode_detect_object(obj_parameter = 'data'): + if obj_parameter == 'x': + # print(Vilib.detect_obj_parameter['x']) + return int(Vilib.detect_obj_parameter['qr_x']/214.0)-1 + elif obj_parameter == 'y': + # print(Vilib.detect_obj_parameter['y']) + return -1*(int(Vilib.detect_obj_parameter['qr_y']/160.2)-1) #max_size_object_coordinate_y + elif obj_parameter == 'width': + return Vilib.detect_obj_parameter['qr_w'] #objects_max_width + elif obj_parameter == 'height': + return Vilib.detect_obj_parameter['qr_h'] #objects_max_height + elif obj_parameter == 'data': + return Vilib.detect_obj_parameter['qr_data'] #objects_count + return 'none' + # return Vilib.detect_obj_parameter['qr_data'] + + @staticmethod + def detect_color_name(color_name): + Vilib.detect_obj_parameter['color_default'] = color_name + Vilib.detect_obj_parameter['lower_color'] = np.array([min(Vilib.color_dict[Vilib.detect_obj_parameter['color_default']]), 60, 60]) + Vilib.detect_obj_parameter['upper_color'] = np.array([max(Vilib.color_dict[Vilib.detect_obj_parameter['color_default']]), 255, 255]) + Vilib.detect_obj_parameter['cdf_flag'] = True + # Vilib.detect_obj_parameter['color_x'] = 160 + # Vilib.detect_obj_parameter['color_y'] = 120 + # Vilib.detect_obj_parameter['color_w'] = 0 + # Vilib.detect_obj_parameter['color_h'] = 0 + # Vilib.detect_obj_parameter['color_n'] = 0 + + + + @staticmethod + def camera_start(web_func = True,inverted_flag = False): + # from multiprocessing import Process + + if inverted_flag == True: + Vilib.detect_obj_parameter['camera_flip'] = True + else: + Vilib.detect_obj_parameter['camera_flip'] = False + + worker_2 = threading.Thread(target=Vilib.camera_clone, name="Thread1") + # worker_2.setDaemon(True) + if web_func == True: + worker_1 = threading.Thread(name='worker 1',target=web_camera_start) + # worker_1.setDaemon(True) + worker_1.start() + # print("worker_1:",worker_1.pid) + worker_2.start() + # # print("worker_2:",worker_2.pid) + + + # if inverted_flag == True: + # Vilib.detect_obj_parameter['camera_flip'] = True + # else: + # Vilib.detect_obj_parameter['camera_flip'] = False + + # worker_2 = Process(target=Vilib.camera_clone, name="Thread1") + # worker_2.daemon = True + # if web_func == True: + # worker_1 = Process(name='worker 1',target=web_camera_start) + # worker_1.daemon = True + # worker_1.start() + # print("worker_1:",worker_1.pid) + # worker_2.start() + # print("worker_2:",worker_2.pid) + # timer.start() + + # worker_2 = Process(name='worker 2',target=Vilib.camera_clone) + # if web_func == True: + # worker_1 = Process(name='worker 1',target=web_camera_start) + # worker_1.start() + # worker_2.start() + # if web_func == True: + # print("1") + # # from flask_camera import web_camera_start + # t2 = threading.Thread(target=web_camera_start) #Thread是一个类,实例化产生t1对象,这里就是创建了一个线程对象t1 + # print("2") + # t2.start() #线程执行 + # print('cam') + # t1 = threading.Thread(target=Vilib.camera_clone) #Thread是一个类,实例化产生t1对象,这里就是创建了一个线程对象t1 + # t1.start() #线程执行 + # print('yes') + + @staticmethod + def human_detect_switch(flag=False): + Vilib.detect_obj_parameter['hdf_flag'] = flag + + @staticmethod + def color_detect_switch(flag=False): + Vilib.detect_obj_parameter['cdf_flag'] = flag + + @staticmethod + def gesture_detect_switch(flag=False): + Vilib.detect_obj_parameter['gs_flag'] = flag + + @staticmethod + def traffic_sign_detect_switch(flag=False): + Vilib.detect_obj_parameter['ts_flag'] = flag + + @staticmethod + def gesture_calibrate_switch(flag=False): + Vilib.detect_obj_parameter['calibrate_flag'] = flag + + @staticmethod + def object_follow_switch(flag=False): + Vilib.detect_obj_parameter['object_follow_flag'] = flag + + @staticmethod + def qrcode_detect_switch(flag=False): + Vilib.detect_obj_parameter['qr_flag'] = flag + + + @staticmethod + def camera_clone(): + Vilib.camera() + + @staticmethod + def camera(): + global effect + camera = PiCamera() + camera.resolution = (640, 480) + camera.image_effect = EFFECTS[Vilib.detect_obj_parameter['eff']] + camera.framerate = 24 + camera.rotation = 0 + # camera.rotation = 180 + camera.brightness = 50 #(0 to 100) + camera.sharpness = 0 #(-100 to 100) + camera.contrast = 0 #(-100 to 100) + camera.saturation = 0 #(-100 to 100) + camera.iso = 0 #(automatic)(100 to 800) + camera.exposure_compensation = 0 #(-25 to 25) + camera.exposure_mode = 'auto' + camera.meter_mode = 'average' + camera.awb_mode = 'auto' + camera.hflip = False + camera.vflip = Vilib.detect_obj_parameter['camera_flip'] + camera.crop = (0.0, 0.0, 1.0, 1.0) + rawCapture = PiRGBArray(camera, size=camera.resolution) + last_e ='none' + camera_val = 0 + last_show_content_list = [] + show_content_list = [] + change_type_val = [] + change_type_dict = {"shutter_speed":0,"resolution":[2592,1944], "brightness":50, "contrast":0, "sharpness":0, "saturation":0, "iso":0, "exposure_compensation":0, "exposure_mode":'auto', \ + "meter_mode":'average' ,"rotation":0 ,"awb_mode":'auto',"drc_strength":'off',"hflip":False,"vflip":True} + start_time = 0 + end_time = 0 + # camera.framerate = 10 + # + try: + while True: + + + for frame in camera.capture_continuous(rawCapture, format="bgr",use_video_port=True):# use_video_port=True + + start_time = time.time() + img = frame.array + # img2gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + # Vilib.detect_obj_parameter['clarity_val'] = round(cv2.Laplacian(img2gray, cv2.CV_64F).var(),2) + # img = Vilib.human_detect_func(img) + # img = Vilib.gamma_method(img) + img = Vilib.gesture_calibrate(img) + img = Vilib.traffic_detect(img) + img = Vilib.color_detect_func(img) + img = Vilib.human_detect_func(img) + img = Vilib.gesture_recognition(img) + img = Vilib.qrcode_detect_func(img) + # cv2.rectangle(img, (280,10), (310,20), (255,255,255)) + # cv2.rectangle(img, (310,13), (311,17), (255,255,255)) + # cv2.rectangle(img, (282,12), (int((1-round(4.3 - power_val(),3)) / 1 * 26 + 282),18), (0,255,0),thickness=-1) + + # change_camera_setting + if Vilib.detect_obj_parameter['change_setting_flag'] == True: + Vilib.detect_obj_parameter['change_setting_flag'] = False + + change_setting_cmd = "camera." + Vilib.detect_obj_parameter['change_setting_type'] + '=' + str(Vilib.detect_obj_parameter['change_setting_val']) + print(change_setting_cmd) + exec(change_setting_cmd) + # change_type_dict[Vilib.detect_obj_parameter['change_setting_type']] = Vilib.detect_obj_parameter['change_setting_val'] + # change_type_val.append(change_setting_cmd) + change_type_dict[Vilib.detect_obj_parameter['change_setting_type']] = Vilib.detect_obj_parameter['change_setting_val'] + if Vilib.detect_obj_parameter['content_num'] != 0: + + for i in range(Vilib.detect_obj_parameter['content_num']): + exec("Vilib.detect_obj_parameter['process_si'] = Vilib.detect_obj_parameter['process_content_" + str(i+1) + "'" + "]") + cv2.putText(img, str(Vilib.detect_obj_parameter['process_si'][0]),Vilib.detect_obj_parameter['process_si'][1],cv2.FONT_HERSHEY_SIMPLEX,Vilib.detect_obj_parameter['process_si'][3],Vilib.detect_obj_parameter['process_si'][2],2) + + if Vilib.detect_obj_parameter['setting_flag'] == True: + setting_type = Camera_SETTING[Vilib.detect_obj_parameter['setting']] + if setting_type == "resolution": + Vilib.detect_obj_parameter['setting_val'] = Vilib.detect_obj_parameter['setting_resolution'] + # print(Vilib.detect_obj_parameter['change_setting_type']) + # print(list(Vilib.detect_obj_parameter['setting_resolution'])) + change_type_dict["resolution"] = list(Vilib.detect_obj_parameter['setting_resolution']) + cv2.putText(img, 'resolution:' + str(Vilib.detect_obj_parameter['setting_resolution']),(10,20),cv2.FONT_HERSHEY_SIMPLEX,0.6,(255,255,255),2) + elif setting_type == "shutter_speed": + change_type_dict["shutter_speed"] = Vilib.detect_obj_parameter['change_setting_val'] + cv2.putText(img, 'shutter_speed:' + str(Vilib.detect_obj_parameter['change_setting_val']),(10,20),cv2.FONT_HERSHEY_SIMPLEX,0.6,(255,255,255),2) + else: + cmd_text = "Vilib.detect_obj_parameter['setting_val'] = camera." + Camera_SETTING[Vilib.detect_obj_parameter['setting']] + # print('mennu:',Ras_Cam.detect_obj_parameter['setting_val']) + exec(cmd_text) + cv2.putText(img, setting_type + ': ' + str(Vilib.detect_obj_parameter['setting_val']),(10,20),cv2.FONT_HERSHEY_SIMPLEX,0.6,(255,255,255),2) + + + e = EFFECTS[Vilib.detect_obj_parameter['eff']] + + + if last_e != e: + camera.image_effect = e + last_e = e + if last_e != 'none': + cv2.putText(img, str(last_e),(0,15),cv2.FONT_HERSHEY_SIMPLEX,0.6,(204,209,72),2) + + + if Vilib.detect_obj_parameter['photo_button_flag'] == True: + camera.close() + break + + + Vilib.img_array[0] = img + rawCapture.truncate(0) + end_time = time.time() + end_time = end_time - start_time + # print(int(1/end_time)) + # print("FPS:",round(time.time() - s_time,2),camera.framerate) + + + # camera = PiCamera() + + + # imu_x,imu_y = imu_rotate() + # # print("change_type_val:",change_type_val) + # for i in change_type_val: + # exec(i) + # if imu_y < 35 and imu_y >-35 and imu_x <= 90 and imu_x > 45: + # # if Vilib.detect_obj_parameter['setting_resolution'][0] < 3040: + # # camera.resolution = (Vilib.detect_obj_parameter['setting_resolution'][1],Vilib.detect_obj_parameter['setting_resolution'][0]) + # # else: + # # camera.resolution = (Vilib.detect_obj_parameter['setting_resolution'][1],Vilib.detect_obj_parameter['setting_resolution'][0]) + # # camera.rotation = 270 + # change_type_dict['rotation'] = 270 + # image_width, image_height = change_type_dict['resolution'][1],change_type_dict['resolution'][0] + # elif imu_y < 35 and imu_y >-35 and imu_x < -45 and imu_x >= -90: + # # if Vilib.detect_obj_parameter['setting_resolution'][0] < 3040: + # # camera.resolution = (Vilib.detect_obj_parameter['setting_resolution'][1],Vilib.detect_obj_parameter['setting_resolution'][0]) + # # else: + # # camera.resolution = (Vilib.detect_obj_parameter['setting_resolution'][1],Vilib.detect_obj_parameter['setting_resolution'][0]) + # # camera.rotation = 90 + # image_width, image_height = change_type_dict['resolution'][1],change_type_dict['resolution'][0] + # change_type_dict['rotation'] = 90 + # elif imu_y < -65 and imu_y >=-90 and imu_x < 45 and imu_x >= -45: + # # camera.resolution = Vilib.detect_obj_parameter['setting_resolution'] + # # camera.rotation = 180 + # image_width, image_height = change_type_dict['resolution'][0],change_type_dict['resolution'][1] + # change_type_dict['rotation'] = 180 + # else: + # image_width, image_height = change_type_dict['resolution'][0],change_type_dict['resolution'][1] + # change_type_dict['rotation'] = 0 + # # camera.resolution = Vilib.detect_obj_parameter['setting_resolution'] + + # camera.image_effect = e + # rawCapture = PiRGBArray(camera, size=camera.resolution) + # print("12") + picture_time = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') + Vilib.detect_obj_parameter['picture_path'] = '/home/pi/picture_file/' + picture_time + '.jpg' + # print(Vilib.detect_obj_parameter['picture_path']) + + + # camera.close() + # print(camera.brightness,camera.sharpness,camera.contrast,camera.saturation,camera.iso,camera.exposure_compensation,camera.exposure_mode,camera.meter_mode,camera.awb_mode,camera.shutter_speed) + a_t = "sudo raspistill -t 250 -w 2592 -h 1944 -vf" + " -rot " + str(change_type_dict['rotation']) + " -ifx " + str(EFFECTS[Vilib.detect_obj_parameter['eff']]) +" -o " + Vilib.detect_obj_parameter['picture_path'] + + + print(a_t) + run_command(a_t) + # camera.capture(Vilib.detect_obj_parameter['picture_path']) + # cv2.imread() + if Vilib.detect_obj_parameter['watermark_flag'] == True: + add_text_to_image(Vilib.detect_obj_parameter['picture_path'],Vilib.detect_obj_parameter['watermark']) + + # if Vilib.detect_obj_parameter['google_upload_flag'] == True: + # upload(file_path='/home/pi/Pictures/rascam_picture_file/', file_name=picture_time + '.jpg') + + #init again + # camera.close() + camera = PiCamera() + camera.resolution = (640,480) + camera.vflip = Vilib.detect_obj_parameter['camera_flip'] + # camera.rotation = Vilib.detect_obj_parameter['camera_rot'] + camera.image_effect = e + rawCapture = PiRGBArray(camera, size=camera.resolution) + Vilib.detect_obj_parameter['photo_button_flag'] = False + + finally: + camera.close() + + + + @staticmethod + def gesture_calibrate(img): + if Vilib.detect_obj_parameter['calibrate_flag'] == True: + # cv2.VideoWriter("./video_file/tt.avi", fourcc, 20.0, (640, 480)) + # roi_hsv = roi_hsv + cv2.imwrite('/home/pi/sport_camera/picarx_python_example/cali.jpg', img[190:290,270:370]) + # cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) + cv2.rectangle(img,(270,190),(370,290),(255,255,255),2) + + return img + + + + @staticmethod + def get_picture(process_picture): + Vilib.detect_obj_parameter['picture_flag'] = True + Vilib.detect_obj_parameter['process_picture'] = process_picture + # if Vilib.detect_obj_parameter['picture_flag'] == True: + # Vilib.detect_obj_parameter['picture_path'] = '/home/pi/picture_file/' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + '.jpg' + Vilib.detect_obj_parameter['picture_path'] = '/home/pi/picture_file/' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + '.jpg' + # cv2.VideoWriter("./video_file/tt.avi", fourcc, 20.0, (640, 480)) + # cv2.imwrite(pic_path, Vilib.img_array[0]) + + @staticmethod + def take_photo(img): + if img is not None: + # if Vilib.detect_obj_parameter['picture_flag'] == True: + # print(Vilib.detect_obj_parameter['picture_path']) + cv2.imwrite(Vilib.detect_obj_parameter['picture_path'], img) + Vilib.detect_obj_parameter['picture_flag'] = False + + + @staticmethod + def cnt_area(cnt): + x,y,w,h = cv2.boundingRect(cnt) + return w*h + + + + @staticmethod + def traffic_predict(input_img,x,y,w,h): + # new_x = x + # new_y = y + # new_w = w + # new_h = h + x1 = int(x) + x2 = int(x + w) + y1 = int(y) + y2 = int(y + h) + + # print(x1,x2,y1,y2) + new_img = input_img[y1:y2,x1:x2] + # new_img = cv2.cvtColor(new_img,cv2.COLOR_BGR2GRAY) + # cv2.imwrite(str(x)+str(y)+'.jpg',new_img) + new_img = (new_img / 255.0) + # img = img / 255. + new_img = (new_img - 0.5) * 2.0 + + resize_img = cv2.resize(new_img, (96,96), interpolation=cv2.INTER_LINEAR) + flatten_img = np.reshape(resize_img, (96,96,3)) + im5 = np.expand_dims(flatten_img,axis = 0) + + # Perform the actual detection by running the model with the image as input + image_np_expanded = im5.astype('float32') # 类型也要满足要求 + + interpreter_1.set_tensor(input_details_2[0]['index'],image_np_expanded) + interpreter_1.invoke() + output_data_2 = interpreter_1.get_tensor(output_details_2[0]['index']) + + # # 出来的结果去掉没用的维度 np.where(result==np.max(result)))[0][0] + result = np.squeeze(output_data_2) + result_accuracy = round(np.max(result),2) + ges_class = np.where(result==np.max(result))[0][0] + + + return result_accuracy,ges_class + + +### detection + @staticmethod + def gesture_predict(input_img,x,y,w,h): + # new_x = x + # new_y = y + # new_w = w + # new_h = h + x1 = int(x) + x2 = int(x + w) + y1 = int(y) + y2 = int(y + h) + + if x1 <= 0: + x1 = 0 + elif x2 >= 640: + x2 = 640 + if y1 <= 0: + y1 = 0 + elif y2 >= 640: + y2 = 640 + + # print(x1,x2,y1,y2) + new_img = input_img[y1:y2,x1:x2] + # new_img = cv2.cvtColor(new_img,cv2.COLOR_BGR2GRAY) + # cv2.imwrite(str(x)+str(y)+'.jpg',new_img) + new_img = (new_img / 255.0) + # img = img / 255. + new_img = (new_img - 0.5) * 2.0 + + resize_img = cv2.resize(new_img, (96,96), interpolation=cv2.INTER_LINEAR) + flatten_img = np.reshape(resize_img, (96,96,3)) + im5 = np.expand_dims(flatten_img,axis = 0) + + # Perform the actual detection by running the model with the image as input + image_np_expanded = im5.astype('float32') # 类型也要满足要求 + + interpreter_2.set_tensor(input_details_2[0]['index'],image_np_expanded) + interpreter_2.invoke() + output_data_2 = interpreter_2.get_tensor(output_details_2[0]['index']) + + # # 出来的结果去掉没用的维度 np.where(result==np.max(result)))[0][0] + result = np.squeeze(output_data_2) + result_accuracy = round(np.max(result),2) + ges_class = np.where(result==np.max(result))[0][0] + + # if result_accuracy >= 0.95: + # interpreter_1.set_tensor(input_details_1[0]['index'],image_np_expanded) + # interpreter_1.invoke() + # output_data_1 = interpreter_2.get_tensor(output_details_1[0]['index']) + # result_1 = np.squeeze(output_data_1) + + # result_accuracy_1 = round(np.max(result_1),2) + # ges_class_1 = np.where(result_1==np.max(result_1))[0][0] + + # if (ges_class_1 == 0 and result_accuracy_1 >= 0.95) or (ges_class_1 == 1 and result_accuracy_1 >= 0.95): + # return result_accuracy_1,ges_class_1 + + + return result_accuracy,ges_class + + @staticmethod + def traffic_detect(img): + + if Vilib.detect_obj_parameter['ts_flag'] == True: + # resize_img = cv2.resize(img, (160,120), interpolation=cv2.INTER_LINEAR) + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 2.从BGR转换到HSV + cv2.circle(img, (160,120), 1, (255,255,255), -1) + # print(hsv[160,120]) + # print(Vilib.lower_color) + + ### red + mask_red_1 = cv2.inRange(hsv,(157,20,20), (180,255,255)) + mask_red_2 = cv2.inRange(hsv,(0,20,20), (10,255,255)) + # mask_red_2 = cv2.inRange(hsv, (175,50,20), (180,255,255)) + + ### blue + mask_blue = cv2.inRange(hsv,(92,10,10), (125,255,255)) + + ### all + mask_all = cv2.bitwise_or(mask_red_1, mask_blue) + + mask_all = cv2.bitwise_or(mask_red_2, mask_all) + + + # color_dict = {'red':[0,4],'orange':[5,18],'yellow':[22,37],'green':[42,85],'blue':[92,110],'purple':[115,165],'red_2':[166,180]} + + open_img = cv2.morphologyEx(mask_all, cv2.MORPH_OPEN,Vilib.kernel_5,iterations=1) #开运算 + # open_img = cv2.dilate(open_img, Vilib.kernel_5,iterations=5) + + contours, hierarchy = cv2.findContours(open_img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) ####在binary中发现轮廓,轮廓按照面积从小到大排列 + # p=0 + contours = sorted(contours,key = Vilib.cnt_area, reverse=False) + traffic_n = len(contours) + max_area = 0 + traffic_sign_num = 0 + + if traffic_n > 0: + for i in contours: #遍历所有的轮廓 + x,y,w,h = cv2.boundingRect(i) #将轮廓分解为识别对象的左上角坐标和宽、高 + + #在图像上画上矩形(图片、左上角坐标、右下角坐标、颜色、线条宽度) + if w > 32 and h > 32: + # cv2.drawContours(img,i,0,(0,0,255),3) + + # if corners == 3: + # count = self.shapes['triangle'] + # count = count+1 + # self.shapes['triangle'] = count + # shape_type = "三角形" + # if corners == 4: + # count = self.shapes['rectangle'] + # count = count + 1 + # self.shapes['rectangle'] = count + # shape_type = "矩形" + + # self.shapes['circles'] = count + # shape_type = "圆形" + # else if 4 < corners < 10: + # count = self.shapes['polygons'] + # count = count + 1 + # self.shapes['polygons'] = count + # shape_type = "多边形" + # x = x*2 + # y = y*2 + # w = w*2 + # h = h*2 + acc_val, traffic_type = Vilib.traffic_predict(img,x,y,w,h) + # print(traffic_type,acc_val) + acc_val = round(acc_val*100) + if acc_val >= 75: + + if traffic_type == 1 or traffic_type == 2 or traffic_type == 3: + # hsv = cv2.cvtColor(resize_img, cv2.COLOR_BGR2HSV) # 2.从BGR转换到HSV 'blue':[92,110] + # print(Vilib.lower_color) + # mask = cv2.inRange(hsv, (92,50,20), (110,255,255)) # 3.inRange():介于lower/upper之间的为白色,其余黑色 + + simple_gray = cv2.cvtColor(img[y:y+h,x:x+w], cv2.COLOR_BGR2GRAY) + # new_mask_blue = cv2.inRange(hsv[y:y+h,x:x+w],(92,70,50), (118,255,255)) + circles = cv2.HoughCircles(simple_gray,cv2.HOUGH_GRADIENT,1,32,\ + param1=140,param2=70,minRadius=int(w/4.0),maxRadius=max(w,h)) + + if circles is not None: + for i in circles[0,:]: + # cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) + traffic_sign_coor = (int(x+i[0]),int(y+i[1])) + cv2.circle(img,traffic_sign_coor,i[2],(255,0,255),2) + cv2.putText(img,str(traffic_dict[traffic_type]) +': ' + str(round(acc_val)),(int(x+i[0]-i[2]),int(y+i[1]-i[2])), cv2.FONT_HERSHEY_SIMPLEX, 1,(255,0,255),2)#加减10是调整字符位置 + if w * h > max_area: + max_area = w * h + max_obj_x = x + max_obj_y = y + max_obj_w = w + max_obj_h = h + max_obj_t = traffic_type + max_obj_acc = acc_val + traffic_sign_num += 1 + + elif traffic_type == 0: + # small_hsv = cv2.cvtColor(resize_img, cv2.COLOR_BGR2HSV) + red_mask_1 = cv2.inRange(hsv[y:y+h,x:x+w],(0,50,20), (4,255,255)) # 3.inRange():介于lower/upper之间的为白色,其余黑色 + red_mask_2 = cv2.inRange(hsv[y:y+h,x:x+w],(163,50,20), (180,255,255)) + red_mask_all = cv2.bitwise_or(red_mask_1,red_mask_2) + + + # circles = np.uint16(np.around(circles)) + + # ret, new_binary = cv2.threshold(simple_gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) + new_binary = cv2.GaussianBlur(red_mask_all, (5, 5), 0) + + open_img = cv2.morphologyEx(red_mask_all, cv2.MORPH_OPEN,Vilib.kernel_5,iterations=1) #开运算 + open_img = cv2.dilate(open_img, Vilib.kernel_5,iterations=5) + + blue_contours, hierarchy = cv2.findContours(open_img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) ####在binary中发现轮廓,轮廓按照面积从小到大排列 + + contours_count = len(blue_contours) + if contours_count >=1: + # print("contours:",contours_count) + blue_contours = sorted(blue_contours,key = Vilib.cnt_area, reverse=True) + + # cv2.drawContours(img,contours,0,(0,0,255),3) + # print(len(blue_contours[0])) + + epsilon = 0.025 * cv2.arcLength(blue_contours[0], True) + approx = cv2.approxPolyDP(blue_contours[0], epsilon, True) + + # # 分析几何形状 + corners = len(approx) + + # # shape_type = "" + # cv2.drawContours(img,blue_contours,0,(0,0,255),1) + # print(corners) + if corners >= 0: + # print("corners:",corners) + # print("eight") + traffic_sign_coor = (int(x+w/2),int(y+h/2)) + cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,255),2) + cv2.putText(img,str(traffic_dict[traffic_type]) +': ' + str(round(acc_val)),(x,y), cv2.FONT_HERSHEY_SIMPLEX, 1,(255,0,255),2)#加减10是调整字符位置 + if w * h > max_area: + max_area = w * h + max_obj_x = x + max_obj_y = y + max_obj_w = w + max_obj_h = h + max_obj_t = traffic_type + max_obj_acc = acc_val + traffic_sign_num += 1 + + + # print("traffic_sign_num:",traffic_sign_num) + if traffic_sign_num > 0: + + Vilib.detect_obj_parameter['traffic_sign_x'] = int(max_obj_x + max_obj_w/2) + Vilib.detect_obj_parameter['traffic_sign_y'] = int(max_obj_y + max_obj_h/2) + Vilib.detect_obj_parameter['traffic_sign_w'] = max_obj_w + Vilib.detect_obj_parameter['traffic_sign_h'] = max_obj_h + # print("traffic_sign_type:",) + Vilib.detect_obj_parameter['traffic_sign_t'] = traffic_dict[max_obj_t] + Vilib.detect_obj_parameter['traffic_sign_acc'] = max_obj_acc + else: + Vilib.detect_obj_parameter['traffic_sign_x'] = 320 + Vilib.detect_obj_parameter['traffic_sign_y'] = 240 + Vilib.detect_obj_parameter['traffic_sign_w'] = 0 + Vilib.detect_obj_parameter['traffic_sign_h'] = 0 + Vilib.detect_obj_parameter['traffic_sign_t'] = 'none' + Vilib.detect_obj_parameter['traffic_sign_acc'] = 0 + # cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) + + + # cv2.putText(img,str(ges_dict[ges_type]) +': ' + str(round(acc_val*100)),(x,y), cv2.FONT_HERSHEY_SIMPLEX, 1,(0,0,255),2)#加减10是调整字符位置 + + # object_area = w*h + # if object_area > max_area: + # max_area = object_area + # Vilib.detect_obj_parameter['color_x'] = int(x + w/2) + # Vilib.detect_obj_parameter['color_y'] = int(y + h/2) + # Vilib.detect_obj_parameter['color_w'] = w + # Vilib.detect_obj_parameter['color_h'] = h + # # print() + # else: + # Vilib.detect_obj_parameter['color_x'] = 160 + # Vilib.detect_obj_parameter['color_y'] = 120 + # Vilib.detect_obj_parameter['color_w'] = 0 + # Vilib.detect_obj_parameter['color_h'] = 0 + # Vilib.detect_obj_parameter['color_n'] = 0 + # return img + # else: + else: + Vilib.detect_obj_parameter['traffic_sign_x'] = 320 + Vilib.detect_obj_parameter['traffic_sign_y'] = 240 + Vilib.detect_obj_parameter['traffic_sign_w'] = 0 + Vilib.detect_obj_parameter['traffic_sign_h'] = 0 + Vilib.detect_obj_parameter['traffic_sign_t'] = 'none' + Vilib.detect_obj_parameter['traffic_sign_acc'] = 0 + + return img + + + @staticmethod + def gesture_recognition(img): + if Vilib.detect_obj_parameter['gs_flag'] == True: + + ###肤色部分 + + target_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + # 首先对样本图像计算2D直方图 + roi_hsv_hist = cv2.calcHist([Vilib.roi_hsv], [0, 1], None, [180, 256], [0, 180, 0, 255]) + # 对得到的样本2D直方图进行归一化 + # 这样可以方便显示,归一化后的直方图就变成0-255之间的数了 + # cv2.NORM_MINMAX表示对数组所有值进行转换,线性映射到最大最小值之间 + cv2.normalize(roi_hsv_hist, roi_hsv_hist, 0, 255, cv2.NORM_MINMAX) + # 对待检测图像进行反向投影 + # 最后一个参数为尺度参数 + dst = cv2.calcBackProject([target_hsv], [0, 1], roi_hsv_hist, [0, 180, 0, 256], 1) + # 构建一个圆形卷积核,用于对图像进行平滑,连接分散的像素 + disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) + dst = cv2.filter2D(dst, -1, disc,dst) + ret, thresh = cv2.threshold(dst, 1, 255, 0) + dilate = cv2.dilate(thresh, Vilib.kernel_5, iterations=3) + # 注意由于原图是三通道BGR图像,因此在进行位运算之前,先要把thresh转成三通道 + # thresh = cv2.merge((dilate, dilate, dilate)) + # 对原图与二值化后的阈值图像进行位运算,得到结果 + # res = cv2.bitwise_and(img, thresh) + + # ycrcb=cv2.cvtColor(img,cv2.COLOR_BGR2YCR_CB) + + # cr_skin = cv2.inRange(ycrcb, (85,124,121), (111,131,128)) + + # open_img = cv2.morphologyEx(cr_skin, cv2.MORPH_OPEN,Vilib.kernel_5,iterations=1) + + contours, hierarchy = cv2.findContours(dilate,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) + ges_num = len(contours) + # max_area = 0 + # max_x = 0 + # max_y = 0 + # max_w = 0 + # max_h = 0 + # point_size = 1 + # point_color = (0, 0, 255) # BGR + # thickness = 4 # 可以为 0 、4、8 + + # acc_val,ges_type = Vilib.gesture_predict(img,x,y,w,h) + # print(ycrcb[160,120]) + # cv2.rectangle(img,(160-96,120-96),(160+96, 120+96),(0,125,0),2, cv2.LINE_AA) + # acc_val,ges_type = Vilib.gesture_predict(img,160-96,120-96,192,192) + + # # cv2.rectangle(img,(160-96,120-96),(160+96, 120+96),(0,125,125),2, cv2.LINE_AA) + # cv2.putText(img,str(ges_type)+': '+str(round(acc_val*100)) + '%',(160-96,120-96),cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,97,240),2) + + if ges_num > 0: + contours = sorted(contours,key = Vilib.cnt_area, reverse=True) + # for i in range(0,len(contours)): #遍历所有的轮廓 + x,y,w,h = cv2.boundingRect(contours[0]) #将轮廓分解为识别对象的左上角坐标和宽、高 + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + faces = Vilib.face_cascade.detectMultiScale(gray[y:y+h,x:x+w], 1.3, 2) + # print(len(faces)) + face_len = len(faces) + + + #在图像上画上矩形(图片、左上角坐标、右下角坐标、颜色、线条宽度) + + + if w >= 60 and h >= 60 and face_len == 0: + # acc_val,ges_type = Vilib.gesture_predict(img,x-2.2*w,y-2.8*h,4.4*w,5.6*h) + acc_val,ges_type = Vilib.gesture_predict(img,x-0.1*w,y-0.2*h,1.1*w,1.2*h) + # x = x*2 + # y = y*2 + # w = w*2 + # h = h*2 + acc_val = round(acc_val*100,3) + if acc_val >= 75: + # print(x,y,w,h) + cv2.rectangle(img,(int(x-0.1*w),int(y-0.2*h)),(int(x+1.1*w), int(y+1.2*h)),(0,125,0),2, cv2.LINE_AA) + cv2.rectangle(img,(0,0),(125,27),(204,209,72),-1, cv2.LINE_AA) + cv2.putText(img,ges_dict[ges_type]+': '+str(acc_val) + '%',(0,17),cv2.FONT_HERSHEY_SIMPLEX,0.6,(255,255,255),2) ##(0,97,240) + + # object_area = w*h + # if object_area > max_area: + # max_area = object_area + + # max_x = int(x + w/2) + # max_y = int(y + h/2) + # max_w = w + # max_h = h + + Vilib.detect_obj_parameter['gesture_x'] = int(x + w/2) + Vilib.detect_obj_parameter['gesture_y'] = int(y + h/2) + Vilib.detect_obj_parameter['gesture_w'] = w + Vilib.detect_obj_parameter['gesture_h'] = h + Vilib.detect_obj_parameter['gesture_t'] = ges_dict[ges_type] + Vilib.detect_obj_parameter['gesture_acc'] = acc_val + # print() + else: + Vilib.detect_obj_parameter['gesture_x'] = 320 + Vilib.detect_obj_parameter['gesture_y'] = 240 + Vilib.detect_obj_parameter['gesture_w'] = 0 + Vilib.detect_obj_parameter['gesture_h'] = 0 + Vilib.detect_obj_parameter['gesture_t'] = 'none' + Vilib.detect_obj_parameter['gesture_acc'] = 0 + + # else: + # # cv2.rectangle(img,(55,35),(210,160),(255,0,0),2, cv2.LINE_AA) + # return img + # # cv2.rectangle(img,(55,35),(210,160),(255,0,0),2, cv2.LINE_AA) + else: + Vilib.detect_obj_parameter['gesture_x'] = 320 + Vilib.detect_obj_parameter['gesture_y'] = 240 + Vilib.detect_obj_parameter['gesture_w'] = 0 + Vilib.detect_obj_parameter['gesture_h'] = 0 + Vilib.detect_obj_parameter['gesture_t'] = 'none' + Vilib.detect_obj_parameter['gesture_acc'] = 0 + + else: + Vilib.detect_obj_parameter['gesture_x'] = 320 + Vilib.detect_obj_parameter['gesture_y'] = 240 + Vilib.detect_obj_parameter['gesture_w'] = 0 + Vilib.detect_obj_parameter['gesture_h'] = 0 + Vilib.detect_obj_parameter['gesture_t'] = 'none' + Vilib.detect_obj_parameter['gesture_acc'] = 0 + + return img + + @staticmethod + def human_detect_func(img): + if Vilib.detect_obj_parameter['hdf_flag'] == True: + resize_img = cv2.resize(img, (320,240), interpolation=cv2.INTER_LINEAR) # 2.从BGR转换到RAY + gray = cv2.cvtColor(resize_img, cv2.COLOR_BGR2GRAY) + faces = Vilib.face_cascade.detectMultiScale(gray, 1.3, 2) + # print(len(faces)) + Vilib.detect_obj_parameter['human_n'] = len(faces) + max_area = 0 + if Vilib.detect_obj_parameter['human_n'] > 0: + for (x,y,w,h) in faces: + + x = x*2 + y = y*2 + w = w*2 + h = h*2 + # face_gray = cv2.cvtColor(img[y:y + w, x:x + h], cv2.COLOR_BGR2GRAY) + # label = face_recognizer.predict(face_gray) + # if round(label[1],2) > 80: + # cv2.putText(img, master_name[label[0]] + ": " + str(round(label[1],2)), (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (128, 128, 0), 2) + cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) + object_area = w*h + if object_area > max_area: + object_area = max_area + Vilib.detect_obj_parameter['human_x'] = int(x + w/2) + Vilib.detect_obj_parameter['human_y'] = int(y + h/2) + Vilib.detect_obj_parameter['human_w'] = w + Vilib.detect_obj_parameter['human_h'] = h + + else: + Vilib.detect_obj_parameter['human_x'] = 320 + Vilib.detect_obj_parameter['human_y'] = 240 + Vilib.detect_obj_parameter['human_w'] = 0 + Vilib.detect_obj_parameter['human_h'] = 0 + Vilib.detect_obj_parameter['human_n'] = 0 + return img + else: + return img + + + # @staticmethod + # def new_color_detect(img): + # # resize_img = cv2.resize(img, (160,120), interpolation=cv2.INTER_LINEAR) + # brightLAB = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) + + # bgr = [40, 158, 16] + # thresh = 40 + # lab = cv2.cvtColor( np.uint8([[bgr]] ), cv2.COLOR_BGR2LAB)[0][0] + + # minLAB = np.array([lab[0] - thresh, lab[1] - thresh, lab[2] - thresh]) + # maxLAB = np.array([lab[0] + thresh, lab[1] + thresh, lab[2] + thresh]) + + # maskLAB = cv2.inRange(brightLAB, minLAB, maxLAB) + # resultLAB = cv2.bitwise_and(brightLAB, brightLAB, mask = maskLAB) + + # return resultLAB + + + + @staticmethod + def color_detect_func(img): + + # 蓝色的范围,不同光照条件下不一样,可灵活调整 H:色度,S:饱和度 v:明度 + if Vilib.detect_obj_parameter['cdf_flag'] == True: + resize_img = cv2.resize(img, (160,120), interpolation=cv2.INTER_LINEAR) + hsv = cv2.cvtColor(resize_img, cv2.COLOR_BGR2HSV) # 2.从BGR转换到HSV + # print(Vilib.lower_color) + color_type = Vilib.detect_obj_parameter['color_default'] + + mask = cv2.inRange(hsv,np.array([min(Vilib.color_dict[color_type]), 60, 60]), np.array([max(Vilib.color_dict[color_type]), 255, 255]) ) # 3.inRange():介于lower/upper之间的为白色,其余黑色 + if color_type == 'red': + mask_2 = cv2.inRange(hsv, (167,0,0), (180,255,255)) + mask = cv2.bitwise_or(mask, mask_2) + + open_img = cv2.morphologyEx(mask, cv2.MORPH_OPEN,Vilib.kernel_5,iterations=1) #开运算 + + contours, hierarchy = cv2.findContours(open_img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) ####在binary中发现轮廓,轮廓按照面积从小到大排列 + # p=0 + Vilib.detect_obj_parameter['color_n'] = len(contours) + max_area = 0 + + if Vilib.detect_obj_parameter['color_n'] > 0: + for i in contours: #遍历所有的轮廓 + x,y,w,h = cv2.boundingRect(i) #将轮廓分解为识别对象的左上角坐标和宽、高 + + #在图像上画上矩形(图片、左上角坐标、右下角坐标、颜色、线条宽度) + if w >= 8 and h >= 8: + x = x*4 + y = y*4 + w = w*4 + h = h*4 + cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) + #给识别对象写上标号 + cv2.putText(img,color_type,(x,y), cv2.FONT_HERSHEY_SIMPLEX, 1,(0,0,255),2)#加减10是调整字符位置 + + object_area = w*h + if object_area > max_area: + max_area = object_area + Vilib.detect_obj_parameter['color_x'] = int(x + w/2) + Vilib.detect_obj_parameter['color_y'] = int(y + h/2) + Vilib.detect_obj_parameter['color_w'] = w + Vilib.detect_obj_parameter['color_h'] = h + # print() + else: + Vilib.detect_obj_parameter['color_x'] = 320 + Vilib.detect_obj_parameter['color_y'] = 240 + Vilib.detect_obj_parameter['color_w'] = 0 + Vilib.detect_obj_parameter['color_h'] = 0 + Vilib.detect_obj_parameter['color_n'] = 0 + return img + else: + return img + + + @staticmethod + def qrcode_detect_func(img): + if Vilib.detect_obj_parameter['qr_flag'] == True: + barcodes = pyzbar.decode(img) + # 循环检测到的条形码 + if len(barcodes) > 0: + for barcode in barcodes: + # 提取条形码的边界框的位置 + # 画出图像中条形码的边界框 + (x, y, w, h) = barcode.rect + cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2) + + # 条形码数据为字节对象,所以如果我们想在输出图像上 + # 画出来,就需要先将它转换成字符串 + barcodeData = barcode.data.decode("utf-8") + # barcodeType = barcode.type + + # 绘出图像上条形码的数据和条形码类型 + # text = "{} ({})".format(barcodeData, barcodeType) + text = "{}".format(barcodeData) + if len(text) > 0: + Vilib.detect_obj_parameter['qr_data'] = text + Vilib.detect_obj_parameter['qr_h'] = h + Vilib.detect_obj_parameter['qr_w'] = w + Vilib.detect_obj_parameter['qr_x'] = x + Vilib.detect_obj_parameter['qr_y'] = y + # print("Vilib.qr_date:%s"%Vilib.qr_date) + cv2.putText(img, text, (x - 20, y - 10), cv2.FONT_HERSHEY_SIMPLEX, + 0.5, (0, 0, 255), 2) + else: + Vilib.detect_obj_parameter['qr_data'] = "None" + Vilib.detect_obj_parameter['qr_x'] = 320 + Vilib.detect_obj_parameter['qr_y'] = 240 + Vilib.detect_obj_parameter['qr_w'] = 0 + Vilib.detect_obj_parameter['qr_h'] = 0 + return img + else: + return img + + @staticmethod + def new_color_detect_func(img,color): + Vilib.detect_color_name(color) + + # 蓝色的范围,不同光照条件下不一样,可灵活调整 H:色度,S:饱和度 v:明度 + if Vilib.detect_obj_parameter['cdf_flag'] == True: + resize_img = cv2.resize(img, (160,120), interpolation=cv2.INTER_LINEAR) + hsv = cv2.cvtColor(resize_img, cv2.COLOR_BGR2HSV) # 2.从BGR转换到HSV + # print(Vilib.lower_color) + color_type = Vilib.detect_obj_parameter['color_default'] + + mask = cv2.inRange(hsv,np.array([min(Vilib.color_dict[color_type]), 60, 60]), np.array([max(Vilib.color_dict[color_type]), 255, 255]) ) # 3.inRange():介于lower/upper之间的为白色,其余黑色 + if color_type == 'red': + mask_2 = cv2.inRange(hsv, (167,0,0), (180,255,255)) + mask = cv2.bitwise_or(mask, mask_2) + + open_img = cv2.morphologyEx(mask, cv2.MORPH_OPEN,Vilib.kernel_5,iterations=1) #开运算 + + contours, hierarchy = cv2.findContours(open_img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) ####在binary中发现轮廓,轮廓按照面积从小到大排列 + # p=0 + Vilib.detect_obj_parameter['color_n'] = len(contours) + max_area = 0 + + if Vilib.detect_obj_parameter['color_n'] > 0: + for i in contours: #遍历所有的轮廓 + x,y,w,h = cv2.boundingRect(i) #将轮廓分解为识别对象的左上角坐标和宽、高 + + #在图像上画上矩形(图片、左上角坐标、右下角坐标、颜色、线条宽度) + if w >= 8 and h >= 8: + x = x*2 + y = y*2 + w = w*2 + h = h*2 + cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) + #给识别对象写上标号 + cv2.putText(img,color_type,(x,y), cv2.FONT_HERSHEY_SIMPLEX, 1,(0,0,255),2)#加减10是调整字符位置 + + object_area = w*h + if object_area > max_area: + max_area = object_area + Vilib.detect_obj_parameter['color_x'] = int(x + w/2) + Vilib.detect_obj_parameter['color_y'] = int(y + h/2) + Vilib.detect_obj_parameter['color_w'] = w + Vilib.detect_obj_parameter['color_h'] = h + # print() + else: + Vilib.detect_obj_parameter['color_x'] = 320 + Vilib.detect_obj_parameter['color_y'] = 240 + Vilib.detect_obj_parameter['color_w'] = 0 + Vilib.detect_obj_parameter['color_h'] = 0 + Vilib.detect_obj_parameter['color_n'] = 0 + return img + else: + return img + + + # @staticmethod + # def object_follow(img): + # if Vilib.detect_obj_parameter['object_follow_flag'] == True: + # hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + # dst = cv2.calcBackProject([hsv],[0,1],Vilib.roi_hist,[0,180,0,256],1)#反向投影 + # disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5)) + # dst = cv2.filter2D(dst,-1,disc,dst) + # ret,thresh = cv2.threshold(dst,1,255,0) + # # dilate = cv2.dilate(thresh,kernel_5,iterations=3) + + # #使用 meanshift获得新位置 + # ret, track_window = cv2.CamShift(thresh,Vilib.track_window, term_crit) + # # print(ret) + + # #显示标记 + # print(ret) + # pts = cv2.boxPoints(ret) + # pts = np.int0(pts) + # img = cv2.polylines(img,[pts],True, (255,0,0),2) + # return img + # else: + # return img + +if __name__ == "__main__": + Vilib.camera_start() + while True: + pass \ No newline at end of file diff --git a/examples/sound_effect.py b/examples/sound_effect.py new file mode 100644 index 0000000..0b8c95b --- /dev/null +++ b/examples/sound_effect.py @@ -0,0 +1,25 @@ +from robot_hat import TTS, Music +import time +import os + +tts = TTS() +music = Music() + +def main(): + # # tts.say('Oh, hello there') + # # tts.say("Here are all the sound effects i can do") + for file in os.listdir("./sounds"): + name = file.split(".")[0] + print(name) + # tts.say(name) + time.sleep(1) + try: + music.sound_effect_play('./sounds/%s' % file) + except Exception as e: + print(e) + time.sleep(2) + # music.sound_effect_play('./sounds/happy2.wav') + # music.sound_effect_play('./sounds/happy.wav') + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/sounds/battle.wav b/examples/sounds/battle.wav new file mode 100644 index 0000000..7996925 Binary files /dev/null and b/examples/sounds/battle.wav differ diff --git a/examples/sounds/bell.wav b/examples/sounds/bell.wav new file mode 100644 index 0000000..bf38f0c Binary files /dev/null and b/examples/sounds/bell.wav differ diff --git a/examples/sounds/depress.wav b/examples/sounds/depress.wav new file mode 100644 index 0000000..57c8275 Binary files /dev/null and b/examples/sounds/depress.wav differ diff --git a/examples/sounds/depress2.wav b/examples/sounds/depress2.wav new file mode 100644 index 0000000..796ccb6 Binary files /dev/null and b/examples/sounds/depress2.wav differ diff --git a/examples/sounds/error.wav b/examples/sounds/error.wav new file mode 100644 index 0000000..6784ef0 Binary files /dev/null and b/examples/sounds/error.wav differ diff --git a/examples/sounds/happy.wav b/examples/sounds/happy.wav new file mode 100644 index 0000000..962e347 Binary files /dev/null and b/examples/sounds/happy.wav differ diff --git a/examples/sounds/happy2.wav b/examples/sounds/happy2.wav new file mode 100644 index 0000000..6e64608 Binary files /dev/null and b/examples/sounds/happy2.wav differ diff --git a/examples/sounds/sign.wav b/examples/sounds/sign.wav new file mode 100644 index 0000000..54c9359 Binary files /dev/null and b/examples/sounds/sign.wav differ diff --git a/examples/sounds/talk1.wav b/examples/sounds/talk1.wav new file mode 100644 index 0000000..8c71bcb Binary files /dev/null and b/examples/sounds/talk1.wav differ diff --git a/examples/sounds/talk2.wav b/examples/sounds/talk2.wav new file mode 100644 index 0000000..6dd8716 Binary files /dev/null and b/examples/sounds/talk2.wav differ diff --git a/examples/sounds/talk3.wav b/examples/sounds/talk3.wav new file mode 100644 index 0000000..8654cde Binary files /dev/null and b/examples/sounds/talk3.wav differ diff --git a/examples/sounds/vigilance.wav b/examples/sounds/vigilance.wav new file mode 100644 index 0000000..e3b8139 Binary files /dev/null and b/examples/sounds/vigilance.wav differ diff --git a/examples/sounds/warning.wav b/examples/sounds/warning.wav new file mode 100644 index 0000000..514d2d2 Binary files /dev/null and b/examples/sounds/warning.wav differ diff --git a/pisloth/__init__.py b/pisloth/__init__.py new file mode 100644 index 0000000..e09372a --- /dev/null +++ b/pisloth/__init__.py @@ -0,0 +1 @@ +from .sloth import Sloth \ No newline at end of file diff --git a/pisloth/__pycache__/__init__.cpython-37.pyc b/pisloth/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..b0c3d49 Binary files /dev/null and b/pisloth/__pycache__/__init__.cpython-37.pyc differ diff --git a/pisloth/__pycache__/sloth.cpython-37.pyc b/pisloth/__pycache__/sloth.cpython-37.pyc new file mode 100644 index 0000000..92ef53d Binary files /dev/null and b/pisloth/__pycache__/sloth.cpython-37.pyc differ diff --git a/pisloth/sloth.py b/pisloth/sloth.py new file mode 100644 index 0000000..a4cf9c0 --- /dev/null +++ b/pisloth/sloth.py @@ -0,0 +1,182 @@ +from robot_hat import Robot +from robot_hat import mapping + +class Sloth(Robot): + move_list = { + "forward":[ + [0, 40, 0, 15], + [-30, 40, -30, 15], + [-30, 0, -30, 0], + + [0, -15, 0, -40], + [30, -15, 30, -40], + [30, 0, 30, 0], + ], + + "turn right":[ + [0, -20, 0, -40], + [-20, -20, 0, -40], + [-20, 0, 0, 0], + + [0, 40, 0, 20], + [20, 40, 0, 20], + [20, 0, 0, 0], + ], + + "turn left":[ + [0, 40, 0, 20], + [0, 40, 20, 20], + [0, 0, 20, 0], + + [0, -20, 0, -40], + [0, -20, -20, -40], + [0, 0, -20, 0], + ], + "backward":[ + [0, 40, 0, 15], + [30, 40, 30, 15], + [30, 0, 30, 0], + + [0, -15, 0, -40], + [-30, -15, -30, -40], + [-30, 0, -30, 0], + ], + + "stand":[ + [0,0,0,0], + ], + + "moon walk left": [ + [0, 0, 0, -30], + [0, 30, 0, -60], + [0, 60, 0, -30], + [0, 30, 0, 0], + [0, 0, 0, 0] + ], + "moon walk right": [ + [0, 30, 0, 0], + [0, 60, 0, -30], + [0, 30, 0, -60], + [0, 0, 0, -30], + [0, 0, 0, 0] + ], + + + "hook": [ + [0, 50, 0, -50], + ], + + "big swing": [ + [0, -90, 0, 90], + ], + + "swing": [ + [0, -40, 0, 40], + ], + + "walk boldly": [ + [-15, -15, 15, -40], + [10, -30, 40, -40], + [10, 0, 40, 0], + + [-15, 40, 15, 15], + [-40, 40, -10, 30], + [-40, 0, -10, 0], + ], + + "walk backward boldly": [ + [-15, -15, 15, -40], + [-40, -30, -10, -40], + [-40, 0, -10, 0], + + [-15, 40, 15, 15], + [10, 40, 40, 30], + [10, 0, 40, 0], + ], + + "walk shyly": [ + [10, -15, -10, -40], + [25, -30, -5, -40], + [25, 0, -5, 0], + + [10, 40, -10, 15], + [5, 40, -25, 30], + [5, 0, -25, 0], + ], + + "walk backward shyly": [ + [10, -15, -10, -40], + [5, -30, -25, -40], + [5, 0, -25, 0], + + [10, 40, -10, 15], + [25, 40, -5, 30], + [25, 0, -5, 0], + ], + + + "stomp rihgt": [ + [0, 15, 0, 0], + [0, 30, 0, -15], + [0, 15, 0, -30], + [0, 0, 0, -15], + [0, 0, 0, 0], + ], + "stomp left": [ + [0, 0, 0, -15], + [0, 15, 0, -30], + [0, 30, 0, -15], + [0, 15, 0, 0], + [0, 0, 0, 0] + ], + + "close": [ + [30, 0, -30, 0], + # [0, 0, 0, 0] + ], + "open": [ + [-30, 0, 30, 0], + # [0, 0, 0, 0] + ], + + "tiptoe left":[ + [-20, 35, -20, 15], + [-20, 15, -20, 15], + ], + + "tiptoe right":[ + [20, -15, 20, -35], + [20, -15, 20, -15], + ], + + "fall left": [ + [-40, 70, -40, 30], + [-40, 30, -40, 30], + ], + "fall right": [ + [40, -30, 40, -70], + [40, -30, 40, -30], + ], + + } + + def do_action(self,motion_name, step=1, speed=None, bpm=None): + if bpm == None: + speed = 50 if speed == None else speed + # speed = mapping(speed, 0, 100, 0, 80) + for _ in range(step): + for motion in self.move_list[motion_name]: + if bpm != None: + self.servo_move(motion, bpm=bpm) + else: + self.servo_move(motion, speed=speed) + + def add_action(self,action_name,action_list): + if action_name not in self.move_list.keys(): + self.move_list[action_name] = action_list + +if __name__=="__main__": + a = Sloth([1,2,3,4]) + while 1: + for i in a.move_list: + a.do_action(i,step=2,speed=100) \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..79bc678 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +# This flag says that the code is written to work on both Python 2 and Python +# 3. If at all possible, it is good practice to do this. If you cannot, you +# will need to generate wheels for each Python version that you support. +universal=1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..caec583 --- /dev/null +++ b/setup.py @@ -0,0 +1,75 @@ +# Always prefer setuptools over distutils +from setuptools import setup, find_packages +# To use a consistent encoding +from codecs import open +from os import path + +here = path.abspath(path.dirname(__file__)) + +# Get the long description from the relevant file +with open(path.join(here, 'DESCRIPTION.rst'), encoding='utf-8') as f: + long_description = f.read() + +setup( + name='pisloth', + + # Versions should comply with PEP440. For a discussion on single-sourcing + # the version across setup.py and the project code, see + # https://packaging.python.org/en/latest/single_source_version.html + version="1.0.0", + + description='Library for SunFounder Pi Sloth', + long_description=long_description, + + # The project's main homepage. + url='https://github.com/sunfounder/pisloth', + + # Author details + author='SunFounder', + author_email='service@sunfounder.com', + + # Choose your license + license='GNU', + zip_safe=False, + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + 'Development Status :: 3 - Alpha', + + # Indicate who your project is intended for + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Build Tools', + + # Pick your license as you wish (should match "license" above) + 'License :: OSI Approved :: GNU License', + + # Specify the Python versions you support here. In particular, ensure + # that you indicate whether you support Python 2, Python 3 or both. + 'Programming Language :: Python :: 3', + ], + + # What does your project relate to? + keywords='python raspberry pi GPIO sunfounder', + + # You can just specify the packages manually here if your project is + # simple. Or you can use find_packages(). + packages=find_packages(exclude=['tests', 'docs']), + + # List run-time dependencies here. These will be installed by pip when + # your project is installed. For an analysis of "install_requires" vs pip's + # requirements files see: + # https://packaging.python.org/en/latest/requirements.html + install_requires=['RPi.GPIO', 'smbus', 'spidev', 'pyserial'], + + # To provide executable scripts, use entry points in preference to the + # "scripts" keyword. Entry points provide cross-platform support and allow + # pip to create the appropriate form of executable for the target platform. + # entry_points={ + # 'console_scripts': [ + # 'pisloth=pisloth:__main__', + # ], + # }, +)