From 8539bc973840a012105ed6020e18391a18ac4619 Mon Sep 17 00:00:00 2001 From: ayin21 Date: Sat, 19 Apr 2025 12:39:02 +0000 Subject: [PATCH 001/159] add packge and rosbridge --- bitbots_misc/bitbots_education/LICENSE | 202 ++++++++++++++++++ .../bitbots_education/__init__.py | 0 .../bitbots_education/index.html | 50 +++++ bitbots_misc/bitbots_education/package.xml | 18 ++ .../resource/bitbots_education | 0 bitbots_misc/bitbots_education/setup.cfg | 4 + bitbots_misc/bitbots_education/setup.py | 25 +++ .../bitbots_education/test/test_copyright.py | 25 +++ .../bitbots_education/test/test_flake8.py | 25 +++ .../bitbots_education/test/test_pep257.py | 23 ++ 10 files changed, 372 insertions(+) create mode 100644 bitbots_misc/bitbots_education/LICENSE create mode 100644 bitbots_misc/bitbots_education/bitbots_education/__init__.py create mode 100644 bitbots_misc/bitbots_education/bitbots_education/index.html create mode 100644 bitbots_misc/bitbots_education/package.xml create mode 100644 bitbots_misc/bitbots_education/resource/bitbots_education create mode 100644 bitbots_misc/bitbots_education/setup.cfg create mode 100644 bitbots_misc/bitbots_education/setup.py create mode 100644 bitbots_misc/bitbots_education/test/test_copyright.py create mode 100644 bitbots_misc/bitbots_education/test/test_flake8.py create mode 100644 bitbots_misc/bitbots_education/test/test_pep257.py diff --git a/bitbots_misc/bitbots_education/LICENSE b/bitbots_misc/bitbots_education/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/bitbots_misc/bitbots_education/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/bitbots_misc/bitbots_education/bitbots_education/__init__.py b/bitbots_misc/bitbots_education/bitbots_education/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/bitbots_misc/bitbots_education/bitbots_education/index.html b/bitbots_misc/bitbots_education/bitbots_education/index.html new file mode 100644 index 000000000..1e6529bc1 --- /dev/null +++ b/bitbots_misc/bitbots_education/bitbots_education/index.html @@ -0,0 +1,50 @@ + + + + + + + + + + + + +

Simple roslib Example

+

Check your Web Console for output.

+ + diff --git a/bitbots_misc/bitbots_education/package.xml b/bitbots_misc/bitbots_education/package.xml new file mode 100644 index 000000000..569588d82 --- /dev/null +++ b/bitbots_misc/bitbots_education/package.xml @@ -0,0 +1,18 @@ + + + + bitbots_education + 0.0.0 + TODO: Package description + root + Apache-2.0 + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/bitbots_misc/bitbots_education/resource/bitbots_education b/bitbots_misc/bitbots_education/resource/bitbots_education new file mode 100644 index 000000000..e69de29bb diff --git a/bitbots_misc/bitbots_education/setup.cfg b/bitbots_misc/bitbots_education/setup.cfg new file mode 100644 index 000000000..70c036156 --- /dev/null +++ b/bitbots_misc/bitbots_education/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/bitbots_education +[install] +install_scripts=$base/lib/bitbots_education diff --git a/bitbots_misc/bitbots_education/setup.py b/bitbots_misc/bitbots_education/setup.py new file mode 100644 index 000000000..5c27dc4e6 --- /dev/null +++ b/bitbots_misc/bitbots_education/setup.py @@ -0,0 +1,25 @@ +from setuptools import find_packages, setup + +package_name = 'bitbots_education' + +setup( + name=package_name, + version='0.0.0', + packages=find_packages(exclude=['test']), + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + package_name]), + ('share/' + package_name, ['package.xml']), + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='root', + maintainer_email='wedmann.lea@gmail.com', + description='TODO: Package description', + license='Apache-2.0', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + ], + }, +) diff --git a/bitbots_misc/bitbots_education/test/test_copyright.py b/bitbots_misc/bitbots_education/test/test_copyright.py new file mode 100644 index 000000000..97a39196e --- /dev/null +++ b/bitbots_misc/bitbots_education/test/test_copyright.py @@ -0,0 +1,25 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +# Remove the `skip` decorator once the source file(s) have a copyright header +@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.') +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found errors' diff --git a/bitbots_misc/bitbots_education/test/test_flake8.py b/bitbots_misc/bitbots_education/test/test_flake8.py new file mode 100644 index 000000000..27ee1078f --- /dev/null +++ b/bitbots_misc/bitbots_education/test/test_flake8.py @@ -0,0 +1,25 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc, errors = main_with_errors(argv=[]) + assert rc == 0, \ + 'Found %d code style errors / warnings:\n' % len(errors) + \ + '\n'.join(errors) diff --git a/bitbots_misc/bitbots_education/test/test_pep257.py b/bitbots_misc/bitbots_education/test/test_pep257.py new file mode 100644 index 000000000..b234a3840 --- /dev/null +++ b/bitbots_misc/bitbots_education/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found code style errors / warnings' From 1936fce9f430460bb088207dc3b6fc9e03c2ebb4 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Thu, 24 Apr 2025 22:37:12 +0200 Subject: [PATCH 002/159] imu and vision integration and slight styling --- .../bitbots_education/bitBot_color.svg | 1043 +++++++++++++++++ .../bitbots_education/index.html | 65 +- 2 files changed, 1099 insertions(+), 9 deletions(-) create mode 100644 bitbots_misc/bitbots_education/bitbots_education/bitBot_color.svg diff --git a/bitbots_misc/bitbots_education/bitbots_education/bitBot_color.svg b/bitbots_misc/bitbots_education/bitbots_education/bitBot_color.svg new file mode 100644 index 000000000..7e6063d00 --- /dev/null +++ b/bitbots_misc/bitbots_education/bitbots_education/bitBot_color.svg @@ -0,0 +1,1043 @@ + + + + + BitBot + + + + image/svg+xml + + BitBot + 2011 + + + Timon Giese + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bitbots_misc/bitbots_education/bitbots_education/index.html b/bitbots_misc/bitbots_education/bitbots_education/index.html index 1e6529bc1..b7f2ff15b 100644 --- a/bitbots_misc/bitbots_education/bitbots_education/index.html +++ b/bitbots_misc/bitbots_education/bitbots_education/index.html @@ -2,16 +2,44 @@ - + - + -

Simple roslib Example

-

Check your Web Console for output.

+
+

IMU

+
+
+
+ +
+

informative text about imu

+
+
+

Vision

+
+ +

informative text about vision

From 1ea84e5baa9f1bc1cd2b3f7ada8e9a2a460869b6 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Tue, 29 Apr 2025 16:43:05 +0200 Subject: [PATCH 003/159] add launch files --- .../bitbots_education/launch/education.launch | 9 ++++++ .../launch/education_simulation.launch | 10 +++++++ bitbots_misc/bitbots_education/setup.py | 29 ++++++++++--------- .../bitbots_education/test/test_copyright.py | 8 ++--- .../bitbots_education/test/test_flake8.py | 6 ++-- .../bitbots_education/test/test_pep257.py | 6 ++-- 6 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 bitbots_misc/bitbots_education/launch/education.launch create mode 100644 bitbots_misc/bitbots_education/launch/education_simulation.launch diff --git a/bitbots_misc/bitbots_education/launch/education.launch b/bitbots_misc/bitbots_education/launch/education.launch new file mode 100644 index 000000000..15b97aef2 --- /dev/null +++ b/bitbots_misc/bitbots_education/launch/education.launch @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/bitbots_misc/bitbots_education/launch/education_simulation.launch b/bitbots_misc/bitbots_education/launch/education_simulation.launch new file mode 100644 index 000000000..44222759b --- /dev/null +++ b/bitbots_misc/bitbots_education/launch/education_simulation.launch @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/bitbots_misc/bitbots_education/setup.py b/bitbots_misc/bitbots_education/setup.py index 5c27dc4e6..e9bf9a7cb 100644 --- a/bitbots_misc/bitbots_education/setup.py +++ b/bitbots_misc/bitbots_education/setup.py @@ -1,25 +1,26 @@ +import glob + from setuptools import find_packages, setup -package_name = 'bitbots_education' +package_name = "bitbots_education" setup( name=package_name, - version='0.0.0', - packages=find_packages(exclude=['test']), + version="0.0.0", + packages=find_packages(exclude=["test"]), data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + package_name]), - ('share/' + package_name, ['package.xml']), + ("share/ament_index/resource_index/packages", ["resource/" + package_name]), + ("share/" + package_name, ["package.xml"]), + ("share/" + package_name + "/launch", glob.glob("launch/*.launch")), ], - install_requires=['setuptools'], + install_requires=["setuptools"], zip_safe=True, - maintainer='root', - maintainer_email='wedmann.lea@gmail.com', - description='TODO: Package description', - license='Apache-2.0', - tests_require=['pytest'], + maintainer="root", + maintainer_email="wedmann.lea@gmail.com", + description="TODO: Package description", + license="Apache-2.0", + tests_require=["pytest"], entry_points={ - 'console_scripts': [ - ], + "console_scripts": [], }, ) diff --git a/bitbots_misc/bitbots_education/test/test_copyright.py b/bitbots_misc/bitbots_education/test/test_copyright.py index 97a39196e..60c2d1e68 100644 --- a/bitbots_misc/bitbots_education/test/test_copyright.py +++ b/bitbots_misc/bitbots_education/test/test_copyright.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_copyright.main import main import pytest +from ament_copyright.main import main # Remove the `skip` decorator once the source file(s) have a copyright header -@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.') +@pytest.mark.skip(reason="No copyright header has been placed in the generated source file.") @pytest.mark.copyright @pytest.mark.linter def test_copyright(): - rc = main(argv=['.', 'test']) - assert rc == 0, 'Found errors' + rc = main(argv=[".", "test"]) + assert rc == 0, "Found errors" diff --git a/bitbots_misc/bitbots_education/test/test_flake8.py b/bitbots_misc/bitbots_education/test/test_flake8.py index 27ee1078f..22fffcb86 100644 --- a/bitbots_misc/bitbots_education/test/test_flake8.py +++ b/bitbots_misc/bitbots_education/test/test_flake8.py @@ -12,14 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @pytest.mark.linter def test_flake8(): rc, errors = main_with_errors(argv=[]) - assert rc == 0, \ - 'Found %d code style errors / warnings:\n' % len(errors) + \ - '\n'.join(errors) + assert rc == 0, "Found %d code style errors / warnings:\n" % len(errors) + "\n".join(errors) diff --git a/bitbots_misc/bitbots_education/test/test_pep257.py b/bitbots_misc/bitbots_education/test/test_pep257.py index b234a3840..4eddb46ed 100644 --- a/bitbots_misc/bitbots_education/test/test_pep257.py +++ b/bitbots_misc/bitbots_education/test/test_pep257.py @@ -12,12 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_pep257.main import main import pytest +from ament_pep257.main import main @pytest.mark.linter @pytest.mark.pep257 def test_pep257(): - rc = main(argv=['.', 'test']) - assert rc == 0, 'Found code style errors / warnings' + rc = main(argv=[".", "test"]) + assert rc == 0, "Found code style errors / warnings" From c7abfd7fc399e13673c025c5e8d198d50c9c1155 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Thu, 1 May 2025 18:24:47 +0200 Subject: [PATCH 004/159] Fix roslaunch and change vision topic --- bitbots_misc/bitbots_education/launch/education.launch | 2 +- bitbots_misc/bitbots_education/setup.py | 1 + .../{bitbots_education => web}/bitBot_color.svg | 0 .../bitbots_education/{bitbots_education => web}/index.html | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) rename bitbots_misc/bitbots_education/{bitbots_education => web}/bitBot_color.svg (100%) rename bitbots_misc/bitbots_education/{bitbots_education => web}/index.html (98%) diff --git a/bitbots_misc/bitbots_education/launch/education.launch b/bitbots_misc/bitbots_education/launch/education.launch index 15b97aef2..30ce06a44 100644 --- a/bitbots_misc/bitbots_education/launch/education.launch +++ b/bitbots_misc/bitbots_education/launch/education.launch @@ -5,5 +5,5 @@ - + diff --git a/bitbots_misc/bitbots_education/setup.py b/bitbots_misc/bitbots_education/setup.py index e9bf9a7cb..65f59f410 100644 --- a/bitbots_misc/bitbots_education/setup.py +++ b/bitbots_misc/bitbots_education/setup.py @@ -12,6 +12,7 @@ ("share/ament_index/resource_index/packages", ["resource/" + package_name]), ("share/" + package_name, ["package.xml"]), ("share/" + package_name + "/launch", glob.glob("launch/*.launch")), + ("share/" + package_name + "/web", glob.glob("web/*")), ], install_requires=["setuptools"], zip_safe=True, diff --git a/bitbots_misc/bitbots_education/bitbots_education/bitBot_color.svg b/bitbots_misc/bitbots_education/web/bitBot_color.svg similarity index 100% rename from bitbots_misc/bitbots_education/bitbots_education/bitBot_color.svg rename to bitbots_misc/bitbots_education/web/bitBot_color.svg diff --git a/bitbots_misc/bitbots_education/bitbots_education/index.html b/bitbots_misc/bitbots_education/web/index.html similarity index 98% rename from bitbots_misc/bitbots_education/bitbots_education/index.html rename to bitbots_misc/bitbots_education/web/index.html index b7f2ff15b..3ed4b9a17 100644 --- a/bitbots_misc/bitbots_education/bitbots_education/index.html +++ b/bitbots_misc/bitbots_education/web/index.html @@ -33,7 +33,7 @@ return { roll, pitch, yaw }; } window.onload = function() { - document.getElementById("videofeed").src=`http://${window.location.hostname}:8081/stream?topic=/camera/image_proc`; + document.getElementById("videofeed").src=`http://${window.location.hostname}:8081/stream?topic=/debug_image`; }; // Connecting to ROS // ----------------- From 552d10e7f31636d93de721b94eb672466ebf019c Mon Sep 17 00:00:00 2001 From: ayin21 Date: Sat, 3 May 2025 09:14:40 +0000 Subject: [PATCH 005/159] add dependenies --- bitbots_misc/bitbots_education/package.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bitbots_misc/bitbots_education/package.xml b/bitbots_misc/bitbots_education/package.xml index 569588d82..0c881ac70 100644 --- a/bitbots_misc/bitbots_education/package.xml +++ b/bitbots_misc/bitbots_education/package.xml @@ -7,6 +7,8 @@ root Apache-2.0 + web_video_server + rosbridge_suite ament_copyright ament_flake8 ament_pep257 From 7570fbdb6a8d5abfc095af40e74b8aeb10f65ccc Mon Sep 17 00:00:00 2001 From: ayin21 Date: Sat, 3 May 2025 13:30:30 +0000 Subject: [PATCH 006/159] add dashboard --- bitbots_misc/bitbots_education/setup.py | 13 +- .../web/{ => assets}/bitBot_color.svg | 0 .../bitbots_education/web/components/imu.html | 74 +++++++++++ .../web/components/vision.html | 11 ++ bitbots_misc/bitbots_education/web/index.html | 115 +++++------------- .../bitbots_education/web/pages/imu.html | 22 ++++ .../bitbots_education/web/pages/vision.html | 20 +++ 7 files changed, 170 insertions(+), 85 deletions(-) rename bitbots_misc/bitbots_education/web/{ => assets}/bitBot_color.svg (100%) create mode 100644 bitbots_misc/bitbots_education/web/components/imu.html create mode 100644 bitbots_misc/bitbots_education/web/components/vision.html create mode 100644 bitbots_misc/bitbots_education/web/pages/imu.html create mode 100644 bitbots_misc/bitbots_education/web/pages/vision.html diff --git a/bitbots_misc/bitbots_education/setup.py b/bitbots_misc/bitbots_education/setup.py index 65f59f410..ad177e65a 100644 --- a/bitbots_misc/bitbots_education/setup.py +++ b/bitbots_misc/bitbots_education/setup.py @@ -1,9 +1,19 @@ import glob +import os from setuptools import find_packages, setup package_name = "bitbots_education" +def generate_data_files(share_path, dir): + data_files = [] + + for path, _, files in os.walk(dir): + list_entry = (share_path + path, [os.path.join(path, f) for f in files if not f.startswith('.')]) + data_files.append(list_entry) + + return data_files + setup( name=package_name, version="0.0.0", @@ -12,8 +22,7 @@ ("share/ament_index/resource_index/packages", ["resource/" + package_name]), ("share/" + package_name, ["package.xml"]), ("share/" + package_name + "/launch", glob.glob("launch/*.launch")), - ("share/" + package_name + "/web", glob.glob("web/*")), - ], + ] + generate_data_files('share/' + package_name + '/', 'web'), install_requires=["setuptools"], zip_safe=True, maintainer="root", diff --git a/bitbots_misc/bitbots_education/web/bitBot_color.svg b/bitbots_misc/bitbots_education/web/assets/bitBot_color.svg similarity index 100% rename from bitbots_misc/bitbots_education/web/bitBot_color.svg rename to bitbots_misc/bitbots_education/web/assets/bitBot_color.svg diff --git a/bitbots_misc/bitbots_education/web/components/imu.html b/bitbots_misc/bitbots_education/web/components/imu.html new file mode 100644 index 000000000..47d678e46 --- /dev/null +++ b/bitbots_misc/bitbots_education/web/components/imu.html @@ -0,0 +1,74 @@ + + + + +
+ +
diff --git a/bitbots_misc/bitbots_education/web/components/vision.html b/bitbots_misc/bitbots_education/web/components/vision.html new file mode 100644 index 000000000..4cc8f26ae --- /dev/null +++ b/bitbots_misc/bitbots_education/web/components/vision.html @@ -0,0 +1,11 @@ + + + + + + diff --git a/bitbots_misc/bitbots_education/web/index.html b/bitbots_misc/bitbots_education/web/index.html index 3ed4b9a17..7696bd3e5 100644 --- a/bitbots_misc/bitbots_education/web/index.html +++ b/bitbots_misc/bitbots_education/web/index.html @@ -1,97 +1,46 @@ - + - + - -
-

IMU

+ +
+

Inner workings of robot

-
-
- +
+ +
+
+
+
+

imu

+
+
+
+
+
+

vision

+
+
+
+
+
+
+
+

motor heat

+
-

informative text about imu

-
-
-

Vision

+
+
+
+

behavior

+
- -

informative text about vision

diff --git a/bitbots_misc/bitbots_education/web/pages/imu.html b/bitbots_misc/bitbots_education/web/pages/imu.html new file mode 100644 index 000000000..d262129d3 --- /dev/null +++ b/bitbots_misc/bitbots_education/web/pages/imu.html @@ -0,0 +1,22 @@ + + + + + + + + + + + + +
+

IMU

+
+
+
+
+

informative text about imu

+
+ + diff --git a/bitbots_misc/bitbots_education/web/pages/vision.html b/bitbots_misc/bitbots_education/web/pages/vision.html new file mode 100644 index 000000000..c03e0410c --- /dev/null +++ b/bitbots_misc/bitbots_education/web/pages/vision.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + +
+

Vision

+
+
+
+

informative text about vision

+ + From 205ce66e5475aedc6764dee767b2a0bcd2ea3b3d Mon Sep 17 00:00:00 2001 From: ayin21 Date: Sun, 4 May 2025 11:04:35 +0000 Subject: [PATCH 007/159] add behavior and motor heat page and links to them --- bitbots_misc/bitbots_education/web/index.html | 10 ++++----- .../bitbots_education/web/pages/behavior.html | 20 +++++++++++++++++ .../web/pages/motor_heat.html | 22 +++++++++++++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 bitbots_misc/bitbots_education/web/pages/behavior.html create mode 100644 bitbots_misc/bitbots_education/web/pages/motor_heat.html diff --git a/bitbots_misc/bitbots_education/web/index.html b/bitbots_misc/bitbots_education/web/index.html index 7696bd3e5..b98a0aff6 100644 --- a/bitbots_misc/bitbots_education/web/index.html +++ b/bitbots_misc/bitbots_education/web/index.html @@ -22,21 +22,21 @@

Inner workings of robot

imu

- - -
+ +

behavior

diff --git a/bitbots_misc/bitbots_education/web/pages/behavior.html b/bitbots_misc/bitbots_education/web/pages/behavior.html new file mode 100644 index 000000000..a0ad29b0b --- /dev/null +++ b/bitbots_misc/bitbots_education/web/pages/behavior.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + +
+

Verhalten

+
+
+
+

Informationen über verhalten

+ + diff --git a/bitbots_misc/bitbots_education/web/pages/motor_heat.html b/bitbots_misc/bitbots_education/web/pages/motor_heat.html new file mode 100644 index 000000000..c152f37bc --- /dev/null +++ b/bitbots_misc/bitbots_education/web/pages/motor_heat.html @@ -0,0 +1,22 @@ + + + + + + + + + + + + +
+

Motor heat

+
+
+
+
+

informative text about motor heat

+
+ + From 62272fcf93992b92906a8308ce5e71ee81e31a82 Mon Sep 17 00:00:00 2001 From: ayin21 Date: Sun, 4 May 2025 12:21:00 +0000 Subject: [PATCH 008/159] unified design --- bitbots_misc/bitbots_education/web/components/imu.html | 2 +- bitbots_misc/bitbots_education/web/components/vision.html | 2 +- bitbots_misc/bitbots_education/web/index.html | 5 +++-- bitbots_misc/bitbots_education/web/pages/behavior.html | 6 +++--- bitbots_misc/bitbots_education/web/pages/imu.html | 6 +++--- bitbots_misc/bitbots_education/web/pages/motor_heat.html | 6 +++--- bitbots_misc/bitbots_education/web/pages/vision.html | 6 +++--- 7 files changed, 17 insertions(+), 16 deletions(-) diff --git a/bitbots_misc/bitbots_education/web/components/imu.html b/bitbots_misc/bitbots_education/web/components/imu.html index 47d678e46..56e07a111 100644 --- a/bitbots_misc/bitbots_education/web/components/imu.html +++ b/bitbots_misc/bitbots_education/web/components/imu.html @@ -70,5 +70,5 @@
- +
diff --git a/bitbots_misc/bitbots_education/web/components/vision.html b/bitbots_misc/bitbots_education/web/components/vision.html index 4cc8f26ae..fd524479f 100644 --- a/bitbots_misc/bitbots_education/web/components/vision.html +++ b/bitbots_misc/bitbots_education/web/components/vision.html @@ -6,6 +6,6 @@ }; - + diff --git a/bitbots_misc/bitbots_education/web/index.html b/bitbots_misc/bitbots_education/web/index.html index b98a0aff6..51ce4a557 100644 --- a/bitbots_misc/bitbots_education/web/index.html +++ b/bitbots_misc/bitbots_education/web/index.html @@ -22,9 +22,9 @@

Inner workings of robot

imu

- +
-
+

vision

@@ -41,6 +41,7 @@

Inner workings of robot

behavior

+
diff --git a/bitbots_misc/bitbots_education/web/pages/behavior.html b/bitbots_misc/bitbots_education/web/pages/behavior.html index a0ad29b0b..e78112f8f 100644 --- a/bitbots_misc/bitbots_education/web/pages/behavior.html +++ b/bitbots_misc/bitbots_education/web/pages/behavior.html @@ -1,5 +1,5 @@ - + @@ -9,8 +9,8 @@ - -
+ +

Verhalten

diff --git a/bitbots_misc/bitbots_education/web/pages/imu.html b/bitbots_misc/bitbots_education/web/pages/imu.html index d262129d3..d13b742cd 100644 --- a/bitbots_misc/bitbots_education/web/pages/imu.html +++ b/bitbots_misc/bitbots_education/web/pages/imu.html @@ -1,5 +1,5 @@ - + @@ -9,8 +9,8 @@ - -
+ +

IMU

diff --git a/bitbots_misc/bitbots_education/web/pages/motor_heat.html b/bitbots_misc/bitbots_education/web/pages/motor_heat.html index c152f37bc..a37ba8472 100644 --- a/bitbots_misc/bitbots_education/web/pages/motor_heat.html +++ b/bitbots_misc/bitbots_education/web/pages/motor_heat.html @@ -1,5 +1,5 @@ - + @@ -9,8 +9,8 @@ - -
+ +

Motor heat

diff --git a/bitbots_misc/bitbots_education/web/pages/vision.html b/bitbots_misc/bitbots_education/web/pages/vision.html index c03e0410c..6f43a2d5d 100644 --- a/bitbots_misc/bitbots_education/web/pages/vision.html +++ b/bitbots_misc/bitbots_education/web/pages/vision.html @@ -1,5 +1,5 @@ - + @@ -9,8 +9,8 @@ - -
+ +

Vision

From 5e13c6ea4a1c6af3fce6103a69ed728b0788f3bc Mon Sep 17 00:00:00 2001 From: ayin21 Date: Sun, 4 May 2025 13:09:54 +0000 Subject: [PATCH 009/159] rework to single page application --- .../web/components/vision.html | 4 +-- bitbots_misc/bitbots_education/web/index.html | 32 ++----------------- .../web/pages/dashboard.html | 32 +++++++++++++++++++ .../bitbots_education/web/pages/vision.html | 23 +++---------- 4 files changed, 40 insertions(+), 51 deletions(-) create mode 100644 bitbots_misc/bitbots_education/web/pages/dashboard.html diff --git a/bitbots_misc/bitbots_education/web/components/vision.html b/bitbots_misc/bitbots_education/web/components/vision.html index fd524479f..85acd9eeb 100644 --- a/bitbots_misc/bitbots_education/web/components/vision.html +++ b/bitbots_misc/bitbots_education/web/components/vision.html @@ -1,9 +1,9 @@ diff --git a/bitbots_misc/bitbots_education/web/index.html b/bitbots_misc/bitbots_education/web/index.html index 51ce4a557..4d314f8e6 100644 --- a/bitbots_misc/bitbots_education/web/index.html +++ b/bitbots_misc/bitbots_education/web/index.html @@ -11,37 +11,9 @@
-

Inner workings of robot

+

-
- -
-
-
-
-

imu

-
-
- -
-
-

vision

-
-
- -
-
-
-
-

motor heat

-
-
- -
-
-

behavior

-
-
+
diff --git a/bitbots_misc/bitbots_education/web/pages/dashboard.html b/bitbots_misc/bitbots_education/web/pages/dashboard.html new file mode 100644 index 000000000..43b07d6de --- /dev/null +++ b/bitbots_misc/bitbots_education/web/pages/dashboard.html @@ -0,0 +1,32 @@ +

Hamburg Bit-Bots

+ + diff --git a/bitbots_misc/bitbots_education/web/pages/vision.html b/bitbots_misc/bitbots_education/web/pages/vision.html index 6f43a2d5d..53a982ebd 100644 --- a/bitbots_misc/bitbots_education/web/pages/vision.html +++ b/bitbots_misc/bitbots_education/web/pages/vision.html @@ -1,20 +1,5 @@ - - - - - - - - - - +

Vision

- -
-

Vision

-
-
-
-

informative text about vision

- - +
+
+

informative text about vision

From 0dd86843c8fc780187b3e90e53c14c8260c2b973 Mon Sep 17 00:00:00 2001 From: ayin21 Date: Sun, 4 May 2025 14:56:16 +0000 Subject: [PATCH 010/159] Add navbar --- .../bitbots_education/web/components/imu.html | 4 +- bitbots_misc/bitbots_education/web/index.html | 46 +++++++++++++++++-- .../bitbots_education/web/pages/behavior.html | 23 ++-------- .../web/pages/dashboard.html | 4 +- .../bitbots_education/web/pages/imu.html | 27 +++-------- .../web/pages/motor_heat.html | 29 ++++-------- 6 files changed, 64 insertions(+), 69 deletions(-) diff --git a/bitbots_misc/bitbots_education/web/components/imu.html b/bitbots_misc/bitbots_education/web/components/imu.html index 56e07a111..0c1ba9174 100644 --- a/bitbots_misc/bitbots_education/web/components/imu.html +++ b/bitbots_misc/bitbots_education/web/components/imu.html @@ -69,6 +69,6 @@ -
- +
+
diff --git a/bitbots_misc/bitbots_education/web/index.html b/bitbots_misc/bitbots_education/web/index.html index 4d314f8e6..c1e1340c9 100644 --- a/bitbots_misc/bitbots_education/web/index.html +++ b/bitbots_misc/bitbots_education/web/index.html @@ -10,10 +10,48 @@ -
-

-
-
+ + diff --git a/bitbots_misc/bitbots_education/web/pages/behavior.html b/bitbots_misc/bitbots_education/web/pages/behavior.html index e78112f8f..0e463848d 100644 --- a/bitbots_misc/bitbots_education/web/pages/behavior.html +++ b/bitbots_misc/bitbots_education/web/pages/behavior.html @@ -1,20 +1,5 @@ - - - - - - - - - - +

Behavior

+
+
+

Informationen über verhalten

- -
-

Verhalten

-
-
-
-

Informationen über verhalten

- - diff --git a/bitbots_misc/bitbots_education/web/pages/dashboard.html b/bitbots_misc/bitbots_education/web/pages/dashboard.html index 43b07d6de..1cddaadd6 100644 --- a/bitbots_misc/bitbots_education/web/pages/dashboard.html +++ b/bitbots_misc/bitbots_education/web/pages/dashboard.html @@ -1,6 +1,6 @@

Hamburg Bit-Bots

-
+ diff --git a/bitbots_misc/bitbots_education/web/pages/imu.html b/bitbots_misc/bitbots_education/web/pages/imu.html index d13b742cd..d211455e5 100644 --- a/bitbots_misc/bitbots_education/web/pages/imu.html +++ b/bitbots_misc/bitbots_education/web/pages/imu.html @@ -1,22 +1,7 @@ - - - - - - - - - - - - -
-

IMU

-
-
-
-
-

informative text about imu

+

IMU

+
+
- - +

informative text about imu

+
+ diff --git a/bitbots_misc/bitbots_education/web/pages/motor_heat.html b/bitbots_misc/bitbots_education/web/pages/motor_heat.html index a37ba8472..905610e06 100644 --- a/bitbots_misc/bitbots_education/web/pages/motor_heat.html +++ b/bitbots_misc/bitbots_education/web/pages/motor_heat.html @@ -1,22 +1,9 @@ - - - - - - - - - - +

Motor Heat

- -
-

Motor heat

-
-
-
-
-

informative text about motor heat

-
- - +
+
+
+

informative text about motor heat

+
+ + From 7e363a69c611452f5f01ab980e5fe73dcf23de6c Mon Sep 17 00:00:00 2001 From: ayin21 Date: Sun, 4 May 2025 15:38:24 +0000 Subject: [PATCH 011/159] add current action to behavior --- bitbots_misc/bitbots_education/web/index.html | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/bitbots_misc/bitbots_education/web/index.html b/bitbots_misc/bitbots_education/web/index.html index c1e1340c9..c1846448e 100644 --- a/bitbots_misc/bitbots_education/web/index.html +++ b/bitbots_misc/bitbots_education/web/index.html @@ -7,8 +7,28 @@ - + + + + + +
+

+
diff --git a/bitbots_misc/bitbots_education/web/components/imu.html b/bitbots_misc/bitbots_education/web/components/imu.html index 0c1ba9174..9612bad44 100644 --- a/bitbots_misc/bitbots_education/web/components/imu.html +++ b/bitbots_misc/bitbots_education/web/components/imu.html @@ -1,5 +1,5 @@ - + + +
+

+
diff --git a/bitbots_misc/bitbots_education/web/pages/behavior.html b/bitbots_misc/bitbots_education/web/pages/behavior.html index ef8f56fbc..5493be80e 100644 --- a/bitbots_misc/bitbots_education/web/pages/behavior.html +++ b/bitbots_misc/bitbots_education/web/pages/behavior.html @@ -1,5 +1,5 @@

Behavior

-
+

Informationen über verhalten

From 71c1f42a0a7891298005d18d295b1cfab8051d39 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Mon, 5 May 2025 15:46:49 +0200 Subject: [PATCH 016/159] fix null pointer errors --- .../web/components/current_action.html | 6 ++++++ bitbots_misc/bitbots_education/web/components/imu.html | 10 ++++++++-- .../bitbots_education/web/components/vision.html | 6 +++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/bitbots_misc/bitbots_education/web/components/current_action.html b/bitbots_misc/bitbots_education/web/components/current_action.html index 44a592e94..91b5b9871 100644 --- a/bitbots_misc/bitbots_education/web/components/current_action.html +++ b/bitbots_misc/bitbots_education/web/components/current_action.html @@ -6,6 +6,12 @@ }); action_listener.subscribe(function(message) { + let element = document.getElementById("action_label"); + if (element === null) + { + action_listener.unsubscribe(); + return; + } document.getElementById("action_label").innerHTML = message.data; }); diff --git a/bitbots_misc/bitbots_education/web/components/imu.html b/bitbots_misc/bitbots_education/web/components/imu.html index 9612bad44..fc312cd23 100644 --- a/bitbots_misc/bitbots_education/web/components/imu.html +++ b/bitbots_misc/bitbots_education/web/components/imu.html @@ -29,18 +29,24 @@ // Subscribing to a Topic // ---------------------- - var listener = new ROSLIB.Topic({ + var imu_listener = new ROSLIB.Topic({ ros : ros, name : '/imu/data', messageType : 'sensor_msgs/msg/Imu' }); - listener.subscribe(function(message) { + imu_listener.subscribe(function(message) { const quaternion = message.orientation; const euler = quaternionToEuler(quaternion); euler.roll = euler.roll/(2*Math.PI)*360 euler.pitch = euler.pitch/(2*Math.PI)*360 //document.getElementById("imu").innerHTML = String(euler.roll); + let element = document.getElementById("logo"); + if (element === null) + { + imu_listener.unsubscribe(); + return; + } document.getElementById("logo").style.transform = `rotate(${-euler.roll}deg)`; }); diff --git a/bitbots_misc/bitbots_education/web/components/vision.html b/bitbots_misc/bitbots_education/web/components/vision.html index 90f8a442d..41290a66c 100644 --- a/bitbots_misc/bitbots_education/web/components/vision.html +++ b/bitbots_misc/bitbots_education/web/components/vision.html @@ -2,7 +2,11 @@ From c78ceda4a9a44f0aca6a0b6507be5519810a698b Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Mon, 5 May 2025 15:49:44 +0200 Subject: [PATCH 017/159] make logo smaller --- bitbots_misc/bitbots_education/web/pages/imu.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bitbots_misc/bitbots_education/web/pages/imu.html b/bitbots_misc/bitbots_education/web/pages/imu.html index 12126c99a..71d8207f3 100644 --- a/bitbots_misc/bitbots_education/web/pages/imu.html +++ b/bitbots_misc/bitbots_education/web/pages/imu.html @@ -1,6 +1,6 @@

IMU

-
-
+
+

An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.

The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.

From 3d09fca3dc83f17f30aac1811def67e25b9b3599 Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Mon, 5 May 2025 16:49:26 +0200 Subject: [PATCH 018/159] Add static templating --- bitbots_misc/bitbots_education/.gitignore | 1 + bitbots_misc/bitbots_education/setup.py | 48 +++++++++++++++++-- .../assets/bitBot_color.svg | 0 .../components/current_action.html | 4 +- .../{web => templates}/components/imu.html | 13 ++--- .../templates/components/textbox.html | 6 +++ .../{web => templates}/components/vision.html | 5 +- .../{web => templates}/index.html | 0 .../templates/pages/behavior.html | 6 +++ .../{web => templates}/pages/dashboard.html | 24 +++++++--- .../{web => templates}/pages/imu.html | 5 +- .../templates/pages/motor_heat.html | 10 ++++ .../{web => templates}/pages/vision.html | 7 ++- .../bitbots_education/web/pages/behavior.html | 5 -- .../web/pages/motor_heat.html | 9 ---- 15 files changed, 104 insertions(+), 39 deletions(-) create mode 100644 bitbots_misc/bitbots_education/.gitignore rename bitbots_misc/bitbots_education/{web => templates}/assets/bitBot_color.svg (100%) rename bitbots_misc/bitbots_education/{web => templates}/components/current_action.html (88%) rename bitbots_misc/bitbots_education/{web => templates}/components/imu.html (96%) create mode 100644 bitbots_misc/bitbots_education/templates/components/textbox.html rename bitbots_misc/bitbots_education/{web => templates}/components/vision.html (82%) rename bitbots_misc/bitbots_education/{web => templates}/index.html (100%) create mode 100644 bitbots_misc/bitbots_education/templates/pages/behavior.html rename bitbots_misc/bitbots_education/{web => templates}/pages/dashboard.html (70%) rename bitbots_misc/bitbots_education/{web => templates}/pages/imu.html (62%) create mode 100644 bitbots_misc/bitbots_education/templates/pages/motor_heat.html rename bitbots_misc/bitbots_education/{web => templates}/pages/vision.html (50%) delete mode 100644 bitbots_misc/bitbots_education/web/pages/behavior.html delete mode 100644 bitbots_misc/bitbots_education/web/pages/motor_heat.html diff --git a/bitbots_misc/bitbots_education/.gitignore b/bitbots_misc/bitbots_education/.gitignore new file mode 100644 index 000000000..89f9ac04a --- /dev/null +++ b/bitbots_misc/bitbots_education/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/bitbots_misc/bitbots_education/setup.py b/bitbots_misc/bitbots_education/setup.py index ad177e65a..531f89d24 100644 --- a/bitbots_misc/bitbots_education/setup.py +++ b/bitbots_misc/bitbots_education/setup.py @@ -1,28 +1,68 @@ import glob +import inspect import os +import shutil +import sys +import tempfile from setuptools import find_packages, setup +from setuptools.command.develop import develop +from setuptools.command.install import install +import distutils.command.install as distutils_install +from jinja2 import Environment, FileSystemLoader package_name = "bitbots_education" def generate_data_files(share_path, dir): data_files = [] - + for path, _, files in os.walk(dir): list_entry = (share_path + path, [os.path.join(path, f) for f in files if not f.startswith('.')]) data_files.append(list_entry) return data_files +def build_html(): + + output_path = "out" + components_path = os.path.join(output_path, "components") + template_path = "templates" + + env = Environment(loader=FileSystemLoader(template_path)) + + # Render all templates excluding the ones in the components folder + for root, dirs, files in os.walk(template_path): + for file in files: + if os.path.abspath(root) != os.path.abspath(components_path): + if file.endswith(".html"): + template_file = os.path.relpath(os.path.join(root, file), template_path) + template = env.get_template(template_file) + output = template.render() + + # Get install path + output_file_path = os.path.join(output_path, template_file) + os.makedirs(os.path.dirname(output_file_path), exist_ok=True) + + with open(output_file_path, "w") as f: + f.write(output) + else: + # Copy all other files + src = os.path.join(root, file) + dst = os.path.join(output_path, os.path.relpath(src, template_path)) + os.makedirs(os.path.dirname(dst), exist_ok=True) + shutil.copy2(src, dst) + +build_html() + setup( name=package_name, - version="0.0.0", - packages=find_packages(exclude=["test"]), data_files=[ ("share/ament_index/resource_index/packages", ["resource/" + package_name]), ("share/" + package_name, ["package.xml"]), ("share/" + package_name + "/launch", glob.glob("launch/*.launch")), - ] + generate_data_files('share/' + package_name + '/', 'web'), + ] + generate_data_files("share/" + package_name + "/", "out/"), + version="0.0.0", + packages=find_packages(exclude=["test"]), install_requires=["setuptools"], zip_safe=True, maintainer="root", diff --git a/bitbots_misc/bitbots_education/web/assets/bitBot_color.svg b/bitbots_misc/bitbots_education/templates/assets/bitBot_color.svg similarity index 100% rename from bitbots_misc/bitbots_education/web/assets/bitBot_color.svg rename to bitbots_misc/bitbots_education/templates/assets/bitBot_color.svg diff --git a/bitbots_misc/bitbots_education/web/components/current_action.html b/bitbots_misc/bitbots_education/templates/components/current_action.html similarity index 88% rename from bitbots_misc/bitbots_education/web/components/current_action.html rename to bitbots_misc/bitbots_education/templates/components/current_action.html index 44a592e94..833595426 100644 --- a/bitbots_misc/bitbots_education/web/components/current_action.html +++ b/bitbots_misc/bitbots_education/templates/components/current_action.html @@ -1,10 +1,11 @@ +{% macro current_action_component() %} - - +{% endmacro %} diff --git a/bitbots_misc/bitbots_education/web/index.html b/bitbots_misc/bitbots_education/templates/index.html similarity index 100% rename from bitbots_misc/bitbots_education/web/index.html rename to bitbots_misc/bitbots_education/templates/index.html diff --git a/bitbots_misc/bitbots_education/templates/pages/behavior.html b/bitbots_misc/bitbots_education/templates/pages/behavior.html new file mode 100644 index 000000000..1cb1f95ee --- /dev/null +++ b/bitbots_misc/bitbots_education/templates/pages/behavior.html @@ -0,0 +1,6 @@ +{% import "components/current_action.html" as current_action_components %} + +

Behavior

+{{ current_action_components.current_action_component() }} +

Informationen über verhalten

+ diff --git a/bitbots_misc/bitbots_education/web/pages/dashboard.html b/bitbots_misc/bitbots_education/templates/pages/dashboard.html similarity index 70% rename from bitbots_misc/bitbots_education/web/pages/dashboard.html rename to bitbots_misc/bitbots_education/templates/pages/dashboard.html index 31f0cb3d8..b0ae5128c 100644 --- a/bitbots_misc/bitbots_education/web/pages/dashboard.html +++ b/bitbots_misc/bitbots_education/templates/pages/dashboard.html @@ -1,24 +1,32 @@ +{% import "components/imu.html" as imu_components %} +{% import "components/vision.html" as vision_components %} +{% import "components/current_action.html" as current_action_components %} +

Hamburg Bit-Bots

+
diff --git a/bitbots_misc/bitbots_education/web/pages/imu.html b/bitbots_misc/bitbots_education/templates/pages/imu.html similarity index 62% rename from bitbots_misc/bitbots_education/web/pages/imu.html rename to bitbots_misc/bitbots_education/templates/pages/imu.html index d211455e5..fc7afe644 100644 --- a/bitbots_misc/bitbots_education/web/pages/imu.html +++ b/bitbots_misc/bitbots_education/templates/pages/imu.html @@ -1,7 +1,8 @@ +{% import "components/imu.html" as imu_components %} +

IMU

-
-
+ {{ imu_components.imu_component() }}

informative text about imu

diff --git a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html new file mode 100644 index 000000000..3e7ccc059 --- /dev/null +++ b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html @@ -0,0 +1,10 @@ +{% import "components/imu.html" as imu_components %} + +

Motor Heat

+ +
+ {{ imu_components.imu_component() }} +

informative text about motor heat

+
+ + diff --git a/bitbots_misc/bitbots_education/web/pages/vision.html b/bitbots_misc/bitbots_education/templates/pages/vision.html similarity index 50% rename from bitbots_misc/bitbots_education/web/pages/vision.html rename to bitbots_misc/bitbots_education/templates/pages/vision.html index 53a982ebd..d693a6e5c 100644 --- a/bitbots_misc/bitbots_education/web/pages/vision.html +++ b/bitbots_misc/bitbots_education/templates/pages/vision.html @@ -1,5 +1,8 @@ +{% import "components/vision.html" as vision_components %} +

Vision

-
-
+{{ vision_components.vision_component() }} +

informative text about vision

+ diff --git a/bitbots_misc/bitbots_education/web/pages/behavior.html b/bitbots_misc/bitbots_education/web/pages/behavior.html deleted file mode 100644 index ef8f56fbc..000000000 --- a/bitbots_misc/bitbots_education/web/pages/behavior.html +++ /dev/null @@ -1,5 +0,0 @@ -

Behavior

-
-
-

Informationen über verhalten

- diff --git a/bitbots_misc/bitbots_education/web/pages/motor_heat.html b/bitbots_misc/bitbots_education/web/pages/motor_heat.html deleted file mode 100644 index 905610e06..000000000 --- a/bitbots_misc/bitbots_education/web/pages/motor_heat.html +++ /dev/null @@ -1,9 +0,0 @@ -

Motor Heat

- -
-
-
-

informative text about motor heat

-
- - From 38e19fdb124e5551a5b06f51b02bb72d0f3a276d Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Mon, 5 May 2025 16:55:02 +0200 Subject: [PATCH 019/159] Adapt launch file to new location --- bitbots_misc/bitbots_education/launch/education.launch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bitbots_misc/bitbots_education/launch/education.launch b/bitbots_misc/bitbots_education/launch/education.launch index 30ce06a44..f2a636063 100644 --- a/bitbots_misc/bitbots_education/launch/education.launch +++ b/bitbots_misc/bitbots_education/launch/education.launch @@ -2,8 +2,8 @@ - + - + From c4afabb38103b5450f471f3855ef1ff6541043e1 Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Mon, 5 May 2025 17:27:13 +0200 Subject: [PATCH 020/159] Cleanup --- bitbots_misc/bitbots_education/setup.py | 6 ------ .../templates/components/behavior_stack.html | 3 ++- .../templates/components/current_action.html | 7 ++++++- .../bitbots_education/templates/components/textbox.html | 7 ++++--- bitbots_misc/bitbots_education/templates/pages/imu.html | 3 +++ 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/bitbots_misc/bitbots_education/setup.py b/bitbots_misc/bitbots_education/setup.py index 531f89d24..1d57021d4 100644 --- a/bitbots_misc/bitbots_education/setup.py +++ b/bitbots_misc/bitbots_education/setup.py @@ -1,14 +1,8 @@ import glob -import inspect import os import shutil -import sys -import tempfile from setuptools import find_packages, setup -from setuptools.command.develop import develop -from setuptools.command.install import install -import distutils.command.install as distutils_install from jinja2 import Environment, FileSystemLoader package_name = "bitbots_education" diff --git a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html index 1ae59d275..91153152d 100644 --- a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html +++ b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html @@ -43,7 +43,8 @@
-

+

+

{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/components/current_action.html b/bitbots_misc/bitbots_education/templates/components/current_action.html index c52b818c8..d8d006fc8 100644 --- a/bitbots_misc/bitbots_education/templates/components/current_action.html +++ b/bitbots_misc/bitbots_education/templates/components/current_action.html @@ -19,6 +19,11 @@
-

+

+ +

{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/components/textbox.html b/bitbots_misc/bitbots_education/templates/components/textbox.html index 55416f753..f4bcd35d1 100644 --- a/bitbots_misc/bitbots_education/templates/components/textbox.html +++ b/bitbots_misc/bitbots_education/templates/components/textbox.html @@ -1,6 +1,7 @@ -{% macro textbox(label, content) %} -
+{% macro textbox(label, content, color) %} +
- + +

{{ content }}

{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/pages/imu.html b/bitbots_misc/bitbots_education/templates/pages/imu.html index 768567e2a..98f79dbfc 100644 --- a/bitbots_misc/bitbots_education/templates/pages/imu.html +++ b/bitbots_misc/bitbots_education/templates/pages/imu.html @@ -1,10 +1,13 @@ {% import "components/imu.html" as imu_components %} +{% import "components/textbox.html" as components %}

IMU

{{ imu_components.imu_component() }}
+ + {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", "sky") }}

An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.

The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.

IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.

From 853cc5bcc5372f95def92676d1fa91e75420ec86 Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Mon, 5 May 2025 17:30:40 +0200 Subject: [PATCH 021/159] Add missing dep --- bitbots_misc/bitbots_education/package.xml | 1 + bitbots_misc/bitbots_education/setup.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/bitbots_misc/bitbots_education/package.xml b/bitbots_misc/bitbots_education/package.xml index 0c881ac70..a8a229ec2 100644 --- a/bitbots_misc/bitbots_education/package.xml +++ b/bitbots_misc/bitbots_education/package.xml @@ -9,6 +9,7 @@ web_video_server rosbridge_suite + python3-jinja2 ament_copyright ament_flake8 ament_pep257 diff --git a/bitbots_misc/bitbots_education/setup.py b/bitbots_misc/bitbots_education/setup.py index 1d57021d4..b78ab9db7 100644 --- a/bitbots_misc/bitbots_education/setup.py +++ b/bitbots_misc/bitbots_education/setup.py @@ -2,22 +2,23 @@ import os import shutil -from setuptools import find_packages, setup from jinja2 import Environment, FileSystemLoader +from setuptools import find_packages, setup package_name = "bitbots_education" + def generate_data_files(share_path, dir): data_files = [] for path, _, files in os.walk(dir): - list_entry = (share_path + path, [os.path.join(path, f) for f in files if not f.startswith('.')]) + list_entry = (share_path + path, [os.path.join(path, f) for f in files if not f.startswith(".")]) data_files.append(list_entry) return data_files -def build_html(): +def build_html(): output_path = "out" components_path = os.path.join(output_path, "components") template_path = "templates" @@ -25,7 +26,7 @@ def build_html(): env = Environment(loader=FileSystemLoader(template_path)) # Render all templates excluding the ones in the components folder - for root, dirs, files in os.walk(template_path): + for root, _, files in os.walk(template_path): for file in files: if os.path.abspath(root) != os.path.abspath(components_path): if file.endswith(".html"): @@ -46,6 +47,7 @@ def build_html(): os.makedirs(os.path.dirname(dst), exist_ok=True) shutil.copy2(src, dst) + build_html() setup( @@ -54,7 +56,8 @@ def build_html(): ("share/ament_index/resource_index/packages", ["resource/" + package_name]), ("share/" + package_name, ["package.xml"]), ("share/" + package_name + "/launch", glob.glob("launch/*.launch")), - ] + generate_data_files("share/" + package_name + "/", "out/"), + ] + + generate_data_files("share/" + package_name + "/", "out/"), version="0.0.0", packages=find_packages(exclude=["test"]), install_requires=["setuptools"], From 5f21cfa0068645cacf358e46fe0622312ee326bc Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Mon, 5 May 2025 19:25:50 +0200 Subject: [PATCH 022/159] Add back btn --- .../templates/components/textbox.html | 6 +-- .../bitbots_education/templates/index.html | 47 ++++++++++++------- .../templates/pages/dashboard.html | 22 ++++----- 3 files changed, 44 insertions(+), 31 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/textbox.html b/bitbots_misc/bitbots_education/templates/components/textbox.html index f4bcd35d1..b02f5c7fa 100644 --- a/bitbots_misc/bitbots_education/templates/components/textbox.html +++ b/bitbots_misc/bitbots_education/templates/components/textbox.html @@ -1,7 +1,7 @@ {% macro textbox(label, content, color) %} -
- +
+ -

{{ content }}

+

{{ content }}

{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/index.html b/bitbots_misc/bitbots_education/templates/index.html index c1846448e..5d27fbfa2 100644 --- a/bitbots_misc/bitbots_education/templates/index.html +++ b/bitbots_misc/bitbots_education/templates/index.html @@ -6,7 +6,8 @@ - + + - + + }); + + // This enables us to use the htmx with both partial and full page loads without a dynamic backend + // It loads the page for a full page load dynamically by injecting the partial page into the index.html + document.addEventListener("DOMContentLoaded", function () { + // Get url parameter + const urlParams = new URLSearchParams(window.location.search); + const page = urlParams.get('page') || 'dashboard'; // Default to 'dashboard' if no page is specified + htmx.ajax('GET', `pages/${page}.html`, { + target: '#content', + swap: 'innerHTML' + }); + }); +
-
-

+
+

-
+
- diff --git a/bitbots_misc/bitbots_education/templates/pages/dashboard.html b/bitbots_misc/bitbots_education/templates/pages/dashboard.html index b0ae5128c..df7cb4e61 100644 --- a/bitbots_misc/bitbots_education/templates/pages/dashboard.html +++ b/bitbots_misc/bitbots_education/templates/pages/dashboard.html @@ -4,24 +4,24 @@

Hamburg Bit-Bots

-
- -
-
-
+ From bb0a97c693fcc08d89be9288b675b4e842eea85f Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Tue, 6 May 2025 10:55:03 +0200 Subject: [PATCH 023/159] rework design --- .../templates/components/textbox.html | 8 ++--- .../bitbots_education/templates/index.html | 8 ++--- .../templates/pages/behavior.html | 13 ++++++-- .../templates/pages/dashboard.html | 33 +++++++++++-------- .../templates/pages/imu.html | 15 ++++----- .../templates/pages/motor_heat.html | 12 ++++++- .../templates/pages/vision.html | 10 +++++- 7 files changed, 65 insertions(+), 34 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/textbox.html b/bitbots_misc/bitbots_education/templates/components/textbox.html index b02f5c7fa..8b4a9d798 100644 --- a/bitbots_misc/bitbots_education/templates/components/textbox.html +++ b/bitbots_misc/bitbots_education/templates/components/textbox.html @@ -1,7 +1,7 @@ -{% macro textbox(label, content, color) %} -
- +{% macro textbox(label, content, color, textcolor) %} +
+ -

{{ content }}

+

{{ content }}

{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/index.html b/bitbots_misc/bitbots_education/templates/index.html index 5d27fbfa2..4ed828d0d 100644 --- a/bitbots_misc/bitbots_education/templates/index.html +++ b/bitbots_misc/bitbots_education/templates/index.html @@ -30,7 +30,7 @@ - +
-
+

-
+
-
+
diff --git a/bitbots_misc/bitbots_education/templates/pages/behavior.html b/bitbots_misc/bitbots_education/templates/pages/behavior.html index d32ac68de..0c1247935 100644 --- a/bitbots_misc/bitbots_education/templates/pages/behavior.html +++ b/bitbots_misc/bitbots_education/templates/pages/behavior.html @@ -1,8 +1,15 @@ {% import "components/current_action.html" as current_action_components %} {% import "components/behavior_stack.html" as behavior_stack_component %} +{% import "components/textbox.html" as components %}

Behavior

-{{ current_action_components.current_action_component() }} -{{ behavior_stack_component.behavior_stack_component() }} -

Informationen über Verhalten

+
+ {{ behavior_stack_component.behavior_stack_component() }} + {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", "sky-100", "zinc-900") }} + {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", "sky-100", "zinc-900")}} + {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", "sky-100", "zinc-900")}} + {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", "sky-800", "white")}} + {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", "sky-800", "white")}} + {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", "sky-800", "white")}} +
diff --git a/bitbots_misc/bitbots_education/templates/pages/dashboard.html b/bitbots_misc/bitbots_education/templates/pages/dashboard.html index df7cb4e61..a4b7c7fc9 100644 --- a/bitbots_misc/bitbots_education/templates/pages/dashboard.html +++ b/bitbots_misc/bitbots_education/templates/pages/dashboard.html @@ -1,27 +1,32 @@ {% import "components/imu.html" as imu_components %} {% import "components/vision.html" as vision_components %} {% import "components/current_action.html" as current_action_components %} +{% import "components/textbox.html" as components %}

Hamburg Bit-Bots

-
- -
-
-
+
+
+ {{ components.textbox("Welcome", "Click on a vizualisation below to get to know more about it.", "sky-800", "white")}} +
+
+ +
+
+
{{ imu_components.imu_component() }}

IMU

- +
{{ vision_components.vision_component() }}

Vision

- +
+ diff --git a/bitbots_misc/bitbots_education/templates/pages/imu.html b/bitbots_misc/bitbots_education/templates/pages/imu.html index 98f79dbfc..01e204bcd 100644 --- a/bitbots_misc/bitbots_education/templates/pages/imu.html +++ b/bitbots_misc/bitbots_education/templates/pages/imu.html @@ -2,16 +2,15 @@ {% import "components/textbox.html" as components %}

IMU

-
+
{{ imu_components.imu_component() }}
- - {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", "sky") }} -

An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.

-

The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.

-

IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.

-

The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.

-

In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.

+ {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", "sky-100", "zinc-900") }} + {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", "sky-100", "zinc-900")}} + {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", "sky-100", "zinc-900")}} + {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", "sky-800", "white")}} + {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", "sky-800", "white")}} + {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", "sky-800", "white")}}
diff --git a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html index 3e7ccc059..ad11a566a 100644 --- a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html +++ b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html @@ -1,10 +1,20 @@ {% import "components/imu.html" as imu_components %} +{% import "components/textbox.html" as components %}

Motor Heat

-
+
{{ imu_components.imu_component() }}

informative text about motor heat

+ + {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", "sky-100", "zinc-900") }} + {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", "sky-100", "zinc-900")}} + {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", "sky-100", "zinc-900")}} + {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", "sky-800", "white")}} + {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", "sky-800", "white")}} + {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", "sky-800", "white")}}
+ + diff --git a/bitbots_misc/bitbots_education/templates/pages/vision.html b/bitbots_misc/bitbots_education/templates/pages/vision.html index d693a6e5c..f2e238f36 100644 --- a/bitbots_misc/bitbots_education/templates/pages/vision.html +++ b/bitbots_misc/bitbots_education/templates/pages/vision.html @@ -1,8 +1,16 @@ {% import "components/vision.html" as vision_components %} +{% import "components/textbox.html" as components %}

Vision

{{ vision_components.vision_component() }} -

informative text about vision

+
+ {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", "sky-100", "zinc-900") }} + {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", "sky-100", "zinc-900")}} + {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", "sky-100", "zinc-900")}} + {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", "sky-800", "white")}} + {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", "sky-800", "white")}} + {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", "sky-800", "white")}} +
From 05ae604aee9443facbdf23b6bd4599722abb4763 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Tue, 6 May 2025 12:03:54 +0200 Subject: [PATCH 024/159] stlye light and dark in textbox component --- .../templates/components/textbox.html | 8 ++++---- .../bitbots_education/templates/pages/behavior.html | 12 ++++++------ .../bitbots_education/templates/pages/dashboard.html | 2 +- .../bitbots_education/templates/pages/imu.html | 12 ++++++------ .../templates/pages/motor_heat.html | 12 ++++++------ .../bitbots_education/templates/pages/vision.html | 12 ++++++------ 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/textbox.html b/bitbots_misc/bitbots_education/templates/components/textbox.html index 8b4a9d798..52089709d 100644 --- a/bitbots_misc/bitbots_education/templates/components/textbox.html +++ b/bitbots_misc/bitbots_education/templates/components/textbox.html @@ -1,7 +1,7 @@ -{% macro textbox(label, content, color, textcolor) %} -
- +{% macro textbox(label, content, style) %} +
+ -

{{ content }}

+

{{ content }}

{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/pages/behavior.html b/bitbots_misc/bitbots_education/templates/pages/behavior.html index 0c1247935..f05d8c224 100644 --- a/bitbots_misc/bitbots_education/templates/pages/behavior.html +++ b/bitbots_misc/bitbots_education/templates/pages/behavior.html @@ -5,11 +5,11 @@

Behavior

{{ behavior_stack_component.behavior_stack_component() }} - {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", "sky-100", "zinc-900") }} - {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", "sky-100", "zinc-900")}} - {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", "sky-100", "zinc-900")}} - {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", "sky-800", "white")}} - {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", "sky-800", "white")}} - {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", "sky-800", "white")}} + {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", true) }} + {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", true)}} + {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", true)}} + {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", false)}} + {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", false)}} + {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", false)}}
diff --git a/bitbots_misc/bitbots_education/templates/pages/dashboard.html b/bitbots_misc/bitbots_education/templates/pages/dashboard.html index a4b7c7fc9..0102bb2c6 100644 --- a/bitbots_misc/bitbots_education/templates/pages/dashboard.html +++ b/bitbots_misc/bitbots_education/templates/pages/dashboard.html @@ -7,7 +7,7 @@

Hamburg Bit-Bots

- {{ components.textbox("Welcome", "Click on a vizualisation below to get to know more about it.", "sky-800", "white")}} + {{ components.textbox("Welcome", "Click on a vizualisation below to get to know more about it.", "dark")}}
diff --git a/bitbots_misc/bitbots_education/templates/pages/imu.html b/bitbots_misc/bitbots_education/templates/pages/imu.html index 01e204bcd..b0ca0aa7f 100644 --- a/bitbots_misc/bitbots_education/templates/pages/imu.html +++ b/bitbots_misc/bitbots_education/templates/pages/imu.html @@ -6,11 +6,11 @@

IMU

{{ imu_components.imu_component() }}
- {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", "sky-100", "zinc-900") }} - {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", "sky-100", "zinc-900")}} - {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", "sky-100", "zinc-900")}} - {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", "sky-800", "white")}} - {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", "sky-800", "white")}} - {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", "sky-800", "white")}} + {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", true) }} + {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", true)}} + {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", true)}} + {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", false)}} + {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", false)}} + {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", false)}}
diff --git a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html index ad11a566a..0a5e38102 100644 --- a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html +++ b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html @@ -6,13 +6,13 @@

Motor Heat

{{ imu_components.imu_component() }}

informative text about motor heat

+ {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", true) }} + {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", true)}} + {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", true)}} + {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", false)}} + {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", false)}} + {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", false)}} - {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", "sky-100", "zinc-900") }} - {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", "sky-100", "zinc-900")}} - {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", "sky-100", "zinc-900")}} - {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", "sky-800", "white")}} - {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", "sky-800", "white")}} - {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", "sky-800", "white")}}
diff --git a/bitbots_misc/bitbots_education/templates/pages/vision.html b/bitbots_misc/bitbots_education/templates/pages/vision.html index f2e238f36..2f80870b6 100644 --- a/bitbots_misc/bitbots_education/templates/pages/vision.html +++ b/bitbots_misc/bitbots_education/templates/pages/vision.html @@ -6,11 +6,11 @@

Vision

{{ vision_components.vision_component() }}
- {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", "sky-100", "zinc-900") }} - {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", "sky-100", "zinc-900")}} - {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", "sky-100", "zinc-900")}} - {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", "sky-800", "white")}} - {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", "sky-800", "white")}} - {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", "sky-800", "white")}} + {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", true) }} + {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", true)}} + {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", true)}} + {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", false)}} + {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", false)}} + {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", false)}}
From c38518f2fa1d0559b8e90b4a68529ee77fd93570 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Thu, 8 May 2025 15:04:03 +0200 Subject: [PATCH 025/159] make behavior stack prettier --- .../templates/components/behavior_stack.html | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html index 91153152d..d8a4c9b59 100644 --- a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html +++ b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html @@ -16,16 +16,16 @@ { if(subtree.type === "sequence") { - output_string += ` ${subtree.type} ${subtree.current_action.type} ${subtree.current_action.name}`; + output_string += `
${subtree.type}
${subtree.current_action.type}
${subtree.current_action.name}
`; } else if (subtree.next === null) { - output_string += ` ${subtree.type} ${subtree.name}`; + output_string += `
${subtree.type}
${subtree.name}
`; } else { - output_string += ` ${subtree.type} ${subtree.name} --> ${subtree.next.activation_reason}
`; + output_string += `
${subtree.type}
${subtree.name} --> ${subtree.next.activation_reason}

`; } } subtree = subtree.next; @@ -37,14 +37,13 @@ stack_listener.unsubscribe(); return; } - document.getElementById("stack_label").innerHTML = output_string; + document.getElementById("stack_label").innerHTML = `
${output_string}
`; }); -
-

-

+
+
{% endmacro %} From 1265596cac30b4738f826206f73222d7652a3718 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Thu, 8 May 2025 23:22:26 +0200 Subject: [PATCH 026/159] Change texts --- .../bitbots_education/templates/pages/imu.html | 15 +++++++++------ .../bitbots_education/templates/pages/vision.html | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/pages/imu.html b/bitbots_misc/bitbots_education/templates/pages/imu.html index b0ca0aa7f..8282f7c17 100644 --- a/bitbots_misc/bitbots_education/templates/pages/imu.html +++ b/bitbots_misc/bitbots_education/templates/pages/imu.html @@ -6,11 +6,14 @@

IMU

{{ imu_components.imu_component() }}
- {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", true) }} - {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", true)}} - {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", true)}} - {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", false)}} - {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", false)}} - {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", false)}} + {{ components.textbox(" + Warum wackelt der Bit-Bot da so?", + "Die Roboter Abbildung zeigt die Daten, + welche durch eine inertiale Messeinheit (im englischen intertial measurement unit, kurz IMU) gemessen wurde. + Sie ist ein kleiner Sensor, welcher Drehungen und Beschleunigungen misst. + Wenn der echte Roboter also zur Seite kippt, dann merkt die IMU dies und der kleine Bit-Bot hier oben kippt sich ebenfalls.", true) }} + {{ components.textbox("Verlgeich zum Menschen", "Die IMU ist wie der menschliche Gleichgewichtssinn.", true)}} + {{ components.textbox("Nutzen im Roboter", "Die Daten, welche die IMU aufnimmt, können zur Laufstabilisierung beitragen. Kippt der Roboter zu weit in die eine Richtung, kann die Software darauf reagieren und Maßnahmen vornehmen, die ein Fallen verhindern. Sollte der Roboter trotzdem hinfallen, zeigt das die IMU ebenfalls an und der Roboter kann in eine Fallpose gehen, um möglichst Hardware schonend zu landen. Zudem wird sie genutzt um festzustellen auf welche Seite wir gefallen sind und was wir machen müssen um wieder aufzustehen.", true)}} + {{ components.textbox("Tiefergehendes...", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", false)}}
diff --git a/bitbots_misc/bitbots_education/templates/pages/vision.html b/bitbots_misc/bitbots_education/templates/pages/vision.html index 2f80870b6..9f01b3f98 100644 --- a/bitbots_misc/bitbots_education/templates/pages/vision.html +++ b/bitbots_misc/bitbots_education/templates/pages/vision.html @@ -6,11 +6,14 @@

Vision

{{ vision_components.vision_component() }}
- {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", true) }} - {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", true)}} - {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", true)}} - {{ components.textbox("IMU in robotics", "IMUs are widely used in robots for navigation and localization, especially in environments where GPS signals are unavailable or unreliable. By tracking changes in orientation and motion, an IMU helps a robot understand how it has moved relative to its previous position. Robots that rely on dynamic balance, such as humanoid robots or drones, use IMU data to continuously adjust their posture and maintain stability. IMUs also support precise motion tracking, making them valuable in applications where robotic arms, wheels, or legs need to move with accuracy and control. Often, IMU data is combined with information from other sensors such as cameras or LiDAR in a process called sensor fusion, which enhances the robot’s understanding of its environment and improves navigation performance.", false)}} - {{ components.textbox("Advantage of IMUs", "The main advantages of IMUs include their compact size, lightweight design, low power consumption, and ability to deliver real-time motion data. However, they are not without limitations. One common issue is drift, especially with gyroscopes, where small errors accumulate over time and can lead to incorrect position or orientation readings. Additionally, magnetometers can be affected by external magnetic interference, reducing the accuracy of heading measurements.", false)}} - {{ components.textbox("Bringing it all together", "In summary, IMUs play a fundamental role in modern robotics by enabling robots to perceive and react to their motion and orientation. When integrated effectively, they significantly enhance a robot’s ability to move, navigate, and interact with the physical world.", false)}} + {{ components.textbox( + "Was ist das für ein Kreis um dem Ball herum?", + "Das hier ist nicht einfach nur der Lifestream der Kamera des Roboters. + Er beinhaltet zudem was der Roboter in dem Bild zu sehen glaubt. + Dabei werden Bälle mit einem grünen Kreis versehen, andere Roboter eingerahmt und Linien in blau markiert.", + true) }} + {{ components.textbox("Vergleich zum Menschen", "Die Vision gleicht der Objekterkennung des Menschen. Sie schaut auf ein Bild und sagt: An der Stelle ist ein Ball. Ähnlich wie beim Menschen wird sie nicht etwa manuell einprogrammiert, sondern erlernt.", true)}} + {{ components.textbox("Nutzen im Roboter", "Viele Komponenten des Roboters brauchen die Daten aus der Vision: Das Wissen über die Position des Balles ist für Verhaltensentscheidungen sehr spannend, erkannte Feldlinien dienen zur Orientierung auf dem Feld und Roboter werden in der Strategie, sowie der Pfadplanung berücksichtigt. Letzters vor allem damit es möglicht wenig Zusammenstöße gibt.", true)}} + {{ components.textbox("Woher weiß die Vision dass da ein Ball ist?", "Sie hat es durch Maschinelles Lernen ( Methode für Künstliche Intelligenz) gelernt. Ein Mensch lernt z.B. von seinen Eltern was ein Ball ist. Zuerst zeigt ein Elternteil auf einen Ball und sagt: Das ist ein Ball. Später zeigt das Kind selber auf Gegenstände, die wie ein Ball aussehen und der Erwachsene bestätigt die Annahme oder berichtigt. Bei dem machienellen lernernen fünktioniert es ähnlich. Während des Lernens versucht das neuronale Netz immer wieder zu raten ob und wo auf einem Bild ein Ball ist. Das wird dann abgeglichen mit einem vorher von Menschen annotierten Bild, wo der Ball an die richtige Stelle markiert wurde. Das Netzt ändert daraufhin die Gewichte seiner Neuronen und wird dadurch über die Zeit besser, bis es gut genug ist und das Training beendet wird. Das fertig gelerte Netz wird dann noch mit neuen Bildern getestet um Schummeln durch Außwendiglernen zu vermeiden und schließlich verbaut. Ab diesem Punkt wird nicht weiter gelernt, sondern nur bereits gelerntes auf neue Bilder anwendet.", false)}}
From 5f04051c0efcc720ccebb7f23bbade577262de77 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Thu, 8 May 2025 23:22:49 +0200 Subject: [PATCH 027/159] Add hot stuff --- .../templates/assets/abstract_robot.svg | 13 +++++++++++++ .../templates/assets/abstract_robot.webp | Bin 0 -> 81686 bytes .../templates/pages/dashboard.html | 6 ++---- .../templates/pages/motor_heat.html | 11 +++++++++-- 4 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 bitbots_misc/bitbots_education/templates/assets/abstract_robot.svg create mode 100644 bitbots_misc/bitbots_education/templates/assets/abstract_robot.webp diff --git a/bitbots_misc/bitbots_education/templates/assets/abstract_robot.svg b/bitbots_misc/bitbots_education/templates/assets/abstract_robot.svg new file mode 100644 index 000000000..fcb47a275 --- /dev/null +++ b/bitbots_misc/bitbots_education/templates/assets/abstract_robot.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/bitbots_misc/bitbots_education/templates/assets/abstract_robot.webp b/bitbots_misc/bitbots_education/templates/assets/abstract_robot.webp new file mode 100644 index 0000000000000000000000000000000000000000..a22e1fb11488d5530f6cf4cc493cb8be33bace9b GIT binary patch literal 81686 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 literal 0 HcmV?d00001 diff --git a/bitbots_misc/bitbots_education/templates/pages/dashboard.html b/bitbots_misc/bitbots_education/templates/pages/dashboard.html index 0102bb2c6..3b79bdc7b 100644 --- a/bitbots_misc/bitbots_education/templates/pages/dashboard.html +++ b/bitbots_misc/bitbots_education/templates/pages/dashboard.html @@ -28,10 +28,8 @@

Hamburg Bit-Bots

-
-
- {{ imu_components.imu_component() }} -
+
+
42°C

Motor Heat

diff --git a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html index 0a5e38102..ed7cfcdcf 100644 --- a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html +++ b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html @@ -4,8 +4,15 @@

Motor Heat

- {{ imu_components.imu_component() }} -

informative text about motor heat

+ +
+
+ +
+
+
+
+ {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", true) }} {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", true)}} {{ components.textbox("Accerelometer", "The accelerometer in an IMU detects linear acceleration along the X, Y, and Z axes, helping the robot sense movements such as tilting, sudden changes in speed, or falls. The gyroscope measures angular velocity, or how quickly the robot is rotating around each axis, which is crucial for maintaining balance and stable orientation during motion. In some cases, a magnetometer is included to measure magnetic fields, allowing the robot to determine its heading relative to the Earth's magnetic north, similar to how a digital compass works.", true)}} From 5ded97b45709ad054e561e4149f91014170b3dbe Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Fri, 9 May 2025 00:13:01 +0200 Subject: [PATCH 028/159] Use higher compression ratio --- bitbots_misc/bitbots_education/templates/components/vision.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitbots_misc/bitbots_education/templates/components/vision.html b/bitbots_misc/bitbots_education/templates/components/vision.html index 96dbbbf3f..85b80054f 100644 --- a/bitbots_misc/bitbots_education/templates/components/vision.html +++ b/bitbots_misc/bitbots_education/templates/components/vision.html @@ -5,7 +5,7 @@ let element = document.getElementById("videofeed"); if (element != null) { - document.getElementById("videofeed").src=`http://${window.location.hostname}:8081/stream?topic=/debug_image`; + document.getElementById("videofeed").src=`http://${window.location.hostname}:8081/stream?topic=/debug_image&quality=10`; } }); From 9e20deb55b4595451f922ba1ab0151ae454293f1 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Fri, 9 May 2025 00:13:11 +0200 Subject: [PATCH 029/159] Rename video server --- bitbots_misc/bitbots_education/launch/education.launch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitbots_misc/bitbots_education/launch/education.launch b/bitbots_misc/bitbots_education/launch/education.launch index f2a636063..81c6f166b 100644 --- a/bitbots_misc/bitbots_education/launch/education.launch +++ b/bitbots_misc/bitbots_education/launch/education.launch @@ -2,7 +2,7 @@ - + From f8f70a0065c5766585c638431386bcfff3e42cc3 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Fri, 9 May 2025 00:13:25 +0200 Subject: [PATCH 030/159] Format behavior stack viz --- .../templates/components/behavior_stack.html | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html index d8a4c9b59..ca1895a86 100644 --- a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html +++ b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html @@ -16,16 +16,23 @@ { if(subtree.type === "sequence") { - output_string += `
${subtree.type}
${subtree.current_action.type}
${subtree.current_action.name}
`; + output_string += `
${subtree.current_action.name}
`; } - else if (subtree.next === null) { - output_string += `
${subtree.type}
${subtree.name}
`; + output_string += `
${subtree.name}
`; } else { - output_string += `
${subtree.type}
${subtree.name} --> ${subtree.next.activation_reason}

`; + output_string += ` + +
${subtree.name}
+
+ + + + ${subtree.next.activation_reason} +
`; } } subtree = subtree.next; @@ -42,8 +49,8 @@ -
-
+
+
{% endmacro %} From ca86fb91839f6789c453ba8a04ea62e7b4c29508 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Sat, 10 May 2025 11:23:28 +0200 Subject: [PATCH 031/159] adjust motor heat --- .../templates/assets/bitBot_whiteboard.svg | 1155 +++++++++++++++++ .../templates/pages/motor_heat.html | 23 +- 2 files changed, 1173 insertions(+), 5 deletions(-) create mode 100644 bitbots_misc/bitbots_education/templates/assets/bitBot_whiteboard.svg diff --git a/bitbots_misc/bitbots_education/templates/assets/bitBot_whiteboard.svg b/bitbots_misc/bitbots_education/templates/assets/bitBot_whiteboard.svg new file mode 100644 index 000000000..c872c5334 --- /dev/null +++ b/bitbots_misc/bitbots_education/templates/assets/bitBot_whiteboard.svg @@ -0,0 +1,1155 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Strategie: + + + diff --git a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html index ed7cfcdcf..c0abe0f30 100644 --- a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html +++ b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html @@ -3,13 +3,26 @@

Motor Heat

-
+
-
-
+
+
-
-
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
From 96d24131601fb0e78dfb87abcc0cf0dab7eeb2b2 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Sat, 10 May 2025 12:23:27 +0200 Subject: [PATCH 032/159] add start/stop button to behavior stack --- .../templates/components/behavior_stack.html | 34 ++++++++++++++++--- .../bitbots_education/templates/index.html | 2 ++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html index ca1895a86..25117a90a 100644 --- a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html +++ b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html @@ -7,6 +7,24 @@ messageType : 'std_msgs/msg/String' }); + function toggleBehavior() + { + if (behavior_changes) + { + behavior_changes = false; + document.getElementById("play_button").innerHTML =` + + `; + } + else + { + behavior_changes = true; + document.getElementById("play_button").innerHTML = ` + + `; + } + } + stack_listener.subscribe(function(message) { const behavior_stack = JSON.parse(message.data); let subtree =behavior_stack; @@ -16,17 +34,17 @@ { if(subtree.type === "sequence") { - output_string += `
${subtree.current_action.name}
`; + output_string += `
${subtree.current_action.name}
`; } else if (subtree.next === null) { - output_string += `
${subtree.name}
`; + output_string += `
${subtree.name}
`; } else { output_string += ` -
${subtree.name}
+
${subtree.name}
@@ -44,13 +62,19 @@ stack_listener.unsubscribe(); return; } - document.getElementById("stack_label").innerHTML = `
${output_string}
`; + if (behavior_changes) + { + document.getElementById("stack_label").innerHTML = `
${output_string}
`; + } }); -
+
+
{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/index.html b/bitbots_misc/bitbots_education/templates/index.html index 4ed828d0d..0d3ba9ec4 100644 --- a/bitbots_misc/bitbots_education/templates/index.html +++ b/bitbots_misc/bitbots_education/templates/index.html @@ -27,6 +27,8 @@ ros.on('close', function() { console.log('Connection to websocket server closed.'); }); + + let behavior_changes = true; From 81765448115440b5839d0baf6225c82c89b88e5a Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Sat, 10 May 2025 15:01:06 +0200 Subject: [PATCH 033/159] add diagnostic subcribtion and logic to moter heat --- .../templates/pages/motor_heat.html | 179 ++++++++++++++++-- 1 file changed, 164 insertions(+), 15 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html index c0abe0f30..edbf84ce7 100644 --- a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html +++ b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html @@ -1,6 +1,169 @@ {% import "components/imu.html" as imu_components %} {% import "components/textbox.html" as components %} + +

Motor Heat

@@ -8,21 +171,7 @@

Motor Heat

-
-
- -
-
- -
-
- -
-
- -
-
- +
From b1533e61f54315dddd68e1937f7e24c190bb1ee3 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Mon, 12 May 2025 14:35:33 +0200 Subject: [PATCH 034/159] add motor heat componend and implementation for max heat on dashboard --- .../templates/components/motor_heat.html | 185 ++++++++++++++++++ .../templates/pages/dashboard.html | 3 +- .../templates/pages/motor_heat.html | 174 +--------------- 3 files changed, 191 insertions(+), 171 deletions(-) create mode 100644 bitbots_misc/bitbots_education/templates/components/motor_heat.html diff --git a/bitbots_misc/bitbots_education/templates/components/motor_heat.html b/bitbots_misc/bitbots_education/templates/components/motor_heat.html new file mode 100644 index 000000000..5391e7f81 --- /dev/null +++ b/bitbots_misc/bitbots_education/templates/components/motor_heat.html @@ -0,0 +1,185 @@ +{% macro motor_heat_component(max_temperature) %} + + + +{% if max_temperature == True %} +
+{% else %} +
+
+ +
+
+
+{% endif %} +{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/pages/dashboard.html b/bitbots_misc/bitbots_education/templates/pages/dashboard.html index 3b79bdc7b..16d4e4600 100644 --- a/bitbots_misc/bitbots_education/templates/pages/dashboard.html +++ b/bitbots_misc/bitbots_education/templates/pages/dashboard.html @@ -2,6 +2,7 @@ {% import "components/vision.html" as vision_components %} {% import "components/current_action.html" as current_action_components %} {% import "components/textbox.html" as components %} +{% import "components/motor_heat.html" as heat_components %}

Hamburg Bit-Bots

@@ -29,7 +30,7 @@

Hamburg Bit-Bots

-
42°C
+ {{ heat_components.motor_heat_component(true) }}

Motor Heat

diff --git a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html index edbf84ce7..e0c0f3581 100644 --- a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html +++ b/bitbots_misc/bitbots_education/templates/pages/motor_heat.html @@ -1,179 +1,13 @@ -{% import "components/imu.html" as imu_components %} -{% import "components/textbox.html" as components %} - -

Motor Heat

- -
-
- -
-
-
+ + {{ heat_components.motor_heat_component(false) }} {{ components.textbox("Explanation Text", "An Inertial Measurement Unit, or IMU, is an essential sensor commonly used in robotics to measure a robot’s orientation, velocity, and acceleration. It typically combines multiple sensing elements—most commonly accelerometers, gyroscopes, and sometimes magnetometers—to provide real-time data about the robot’s motion and position in three-dimensional space.", true) }} {{ components.textbox("Comparison to humans", "The IMU is like the sense of balance in humans", true)}} From 2d4b7a46a12c0c1497e109f2015999701712e03e Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Mon, 12 May 2025 14:39:25 +0200 Subject: [PATCH 035/159] change color of camera heat --- .../bitbots_education/templates/components/motor_heat.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitbots_misc/bitbots_education/templates/components/motor_heat.html b/bitbots_misc/bitbots_education/templates/components/motor_heat.html index 5391e7f81..c4f10d18e 100644 --- a/bitbots_misc/bitbots_education/templates/components/motor_heat.html +++ b/bitbots_misc/bitbots_education/templates/components/motor_heat.html @@ -124,7 +124,7 @@ output_string += `
` let camera_temperature = camera; - output_string += `
` + output_string += `
` r_hip_temperature = Math.max(r_hip_pitch,r_hip_roll,r_hip_yaw); let r_knee_temperature = r_knee From c8116a57a39101623623b6d40b295cccadae40c2 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Mon, 12 May 2025 15:36:00 +0200 Subject: [PATCH 036/159] add pitch to imu rotation --- bitbots_misc/bitbots_education/templates/components/imu.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitbots_misc/bitbots_education/templates/components/imu.html b/bitbots_misc/bitbots_education/templates/components/imu.html index 02664c249..51e47b31c 100644 --- a/bitbots_misc/bitbots_education/templates/components/imu.html +++ b/bitbots_misc/bitbots_education/templates/components/imu.html @@ -47,7 +47,7 @@ imu_listener.unsubscribe(); return; } - document.getElementById("logo").style.transform = `rotate(${-euler.roll}deg)`; + document.getElementById("logo").style.transform = `rotateZ(${- euler.roll}deg) rotateX(${-euler.pitch}deg)`; }); From c078fe206f9241e75c16818a5abe54ebf00be819 Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Sat, 17 May 2025 12:36:13 +0200 Subject: [PATCH 037/159] paint backgrund of icon to show current page --- .../templates/components/icon_footer.html | 31 ++++++++++++++++++ .../templates/components/motor_heat.html | 22 ++++++------- .../bitbots_education/templates/index.html | 32 ++----------------- .../templates/pages/behavior.html | 4 +++ .../templates/pages/dashboard.html | 4 +++ .../templates/pages/imu.html | 4 +++ .../templates/pages/motor_heat.html | 4 +++ .../templates/pages/vision.html | 4 +++ 8 files changed, 64 insertions(+), 41 deletions(-) create mode 100644 bitbots_misc/bitbots_education/templates/components/icon_footer.html diff --git a/bitbots_misc/bitbots_education/templates/components/icon_footer.html b/bitbots_misc/bitbots_education/templates/components/icon_footer.html new file mode 100644 index 000000000..412b140c5 --- /dev/null +++ b/bitbots_misc/bitbots_education/templates/components/icon_footer.html @@ -0,0 +1,31 @@ +{% macro icons(site) %} +
+{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/components/motor_heat.html b/bitbots_misc/bitbots_education/templates/components/motor_heat.html index c4f10d18e..ef6fd66d2 100644 --- a/bitbots_misc/bitbots_education/templates/components/motor_heat.html +++ b/bitbots_misc/bitbots_education/templates/components/motor_heat.html @@ -121,33 +121,33 @@ } let head_temperature = Math.max(head_pen,head_tilt); - output_string += `
` + output_string += `
` let camera_temperature = camera; - output_string += `
` + output_string += `
` r_hip_temperature = Math.max(r_hip_pitch,r_hip_roll,r_hip_yaw); let r_knee_temperature = r_knee let r_ankle_temperature = Math.max(r_ancle_pitch,r_ancle_roll); output_string += ` -
-
-
` +
+
+
` let l_hip_temperature = Math.max(l_hip_pitch,l_hip_roll,l_hip_yaw); let l_knee_temperature = l_knee; let l_ankle_temperature = Math.max(l_ancle_pitch,l_ancle_roll); output_string += ` -
-
-
` +
+
+
` let r_shoulder_temperature = Math.max(r_shoulder_pitch,r_shoulder_roll); - output_string += `
` + output_string += `
` let l_shoulder_temperature = Math.max(l_shoulder_pitch,l_shoulder_roll); - output_string += `
` - + output_string += `
` + console.log(head_temperature, l_hip_temperature, r_hip_temperature, l_knee_temperature, r_knee_temperature, l_ankle_temperature, r_ankle_temperature, l_shoulder_temperature, r_shoulder_temperature) let max_temperature = Math.max(head_temperature, l_hip_temperature, r_hip_temperature, l_knee_temperature, r_knee_temperature, l_ankle_temperature, r_ankle_temperature, l_shoulder_temperature, r_shoulder_temperature); let element = document.getElementById("temperature_points"); diff --git a/bitbots_misc/bitbots_education/templates/index.html b/bitbots_misc/bitbots_education/templates/index.html index 0d3ba9ec4..4447443aa 100644 --- a/bitbots_misc/bitbots_education/templates/index.html +++ b/bitbots_misc/bitbots_education/templates/index.html @@ -7,7 +7,7 @@ - +{% import "components/icon_footer.html" as icon_components %} -
-
-
diff --git a/bitbots_misc/bitbots_education/templates/components/current_action.html b/bitbots_misc/bitbots_education/templates/components/current_action.html index d8d006fc8..383f684f5 100644 --- a/bitbots_misc/bitbots_education/templates/components/current_action.html +++ b/bitbots_misc/bitbots_education/templates/components/current_action.html @@ -1,29 +1,29 @@ {% macro current_action_component() %} - - - -
-

- -

+
+

+

+ +

{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/index.html b/bitbots_misc/bitbots_education/templates/index.html index 4447443aa..202b87ae3 100644 --- a/bitbots_misc/bitbots_education/templates/index.html +++ b/bitbots_misc/bitbots_education/templates/index.html @@ -7,7 +7,8 @@ -{% import "components/icon_footer.html" as icon_components %} + + From f33fd6fa17e8d5350094d7eff48e6b8adb40cc86 Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Sun, 18 May 2025 13:10:01 +0200 Subject: [PATCH 042/159] Add translations --- .../templates/components/behavior_stack.html | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html index 77c5803c6..2fdc7724f 100644 --- a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html +++ b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html @@ -1,6 +1,47 @@ {% macro behavior_stack_component() %} - - -
-
-

-
-
+ +
+
+

+
+
+
+
-
From 3f7c70a7d16d1a25f8f9c89577c506d7b72f3e71 Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Sun, 18 May 2025 14:00:28 +0200 Subject: [PATCH 047/159] Use alpine for IMU --- .../templates/components/imu.html | 96 +++++++++---------- 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/imu.html b/bitbots_misc/bitbots_education/templates/components/imu.html index 51e47b31c..401df160e 100644 --- a/bitbots_misc/bitbots_education/templates/components/imu.html +++ b/bitbots_misc/bitbots_education/templates/components/imu.html @@ -1,61 +1,53 @@ {% macro imu_component() %} -
- +
+
{% endmacro %} From ff314729f39feaa7981187fb5d8205921839297e Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Sun, 18 May 2025 14:14:06 +0200 Subject: [PATCH 048/159] Use better angle representation --- .../templates/components/imu.html | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/imu.html b/bitbots_misc/bitbots_education/templates/components/imu.html index 401df160e..1810a48ec 100644 --- a/bitbots_misc/bitbots_education/templates/components/imu.html +++ b/bitbots_misc/bitbots_education/templates/components/imu.html @@ -1,28 +1,37 @@ {% macro imu_component() %} @@ -38,10 +47,10 @@ }); this.listener.subscribe((message) => { const quaternion = message.orientation; - const euler = quaternionToEuler(quaternion); - euler.roll = euler.roll/(2*Math.PI)*360 - euler.pitch = euler.pitch/(2*Math.PI)*360 - $refs.logo.style.transform = `rotateZ(${- euler.roll}deg) rotateX(${-euler.pitch}deg)`; + // Convert quaternion to fused angles + const fused_angles = quaternionToFusedAngles(quaternion); + // Update the logo rotation + $refs.logo.style.transform = `rotateZ(${- fused_angles.fusedRoll}rad) rotateX(${-fused_angles.fusedPitch}rad)`; }); }, destroy() { From 7ca432753d00ce216fa811eadd982ab1b83eb7c6 Mon Sep 17 00:00:00 2001 From: Lea Date: Wed, 21 May 2025 12:11:06 +0200 Subject: [PATCH 049/159] fix typos --- .../bitbots_education/templates/pages/behavior.html | 2 +- .../bitbots_education/templates/pages/imu.html | 12 ++++++------ .../bitbots_education/templates/pages/vision.html | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/pages/behavior.html b/bitbots_misc/bitbots_education/templates/pages/behavior.html index 7bc2e3642..1de404f76 100644 --- a/bitbots_misc/bitbots_education/templates/pages/behavior.html +++ b/bitbots_misc/bitbots_education/templates/pages/behavior.html @@ -21,7 +21,7 @@

Behavior

Hier sind Strategien für das Spiel gespeichert, also Spielzüge, wer wann zum Ball stürmt oder als Verteidiger zurück bleibt. Um diese Entscheidungen treffen zu können, bekommt das Verhalten sehr viele Informationen von anderen Softwarekomponenten, wie zum Beispiel die Position des Balls von der Bildverarbeitung.", true)}} {{ components.textbox("Wie funktioniert das Verhalten?", "Im Vergleich zur Bildverarbeitung ist das Verhalten nicht gelernt, sondern besteht aus vielen Abwägungen ähnlich zu: wenn X, dann Y. - Wobei X und Y durch einen Informatiker im Code festgelegt worden sind. Es gibt verschiedene Strukturen in denen man diese Abwägungen ordnen kann. Darunter Finite Zustands Maschinen oder Entscheidungsbäume. + Wobei X und Y durch einen Informatiker im Code festgelegt worden sind. Es gibt verschiedene Strukturen in denen man diese Abwägungen ordnen kann. Darunter Endliche Automaten oder Entscheidungsbäume. Doch die Bit-Bots haben sich eine eigene Struktur überlegt: den Dynamic Stack Decider (kurz DSD, in Deutsch so was wie dynamischer Stapel Entscheider). Der DSD ist zunächst sehr ähnlich zu einem Entscheidungsbaum. Die Baumstruktur entsteht dabei durch die verschiedenen Antwortmöglichkeiten auf eine Entscheidung. Oben in der Visualisierung sehen sie nur die gewählten Antworten, aber sie können sich vorstellen, dass jede Entscheidung eine Astgabelung darstellt und je nach Antwort der eine oder der andere Ast ausgewählt wird. Was der DSD nun besonders macht, diff --git a/bitbots_misc/bitbots_education/templates/pages/imu.html b/bitbots_misc/bitbots_education/templates/pages/imu.html index dee8d69a2..2835d97b4 100644 --- a/bitbots_misc/bitbots_education/templates/pages/imu.html +++ b/bitbots_misc/bitbots_education/templates/pages/imu.html @@ -12,14 +12,14 @@

IMU

{{ components.textbox(" Warum wackelt der Bit-Bot da so?", - "Die Roboter Abbildung zeigt die Daten, - welche durch eine inertiale Messeinheit (im englischen intertial measurement unit, kurz IMU) gemessen wurde. - Sie ist ein kleiner Sensor, welcher Drehungen und Beschleunigungen misst. - Wenn der echte Roboter also zur Seite kippt, dann merkt die IMU dies und der kleine Bit-Bot hier oben kippt sich ebenfalls.", true) }} - {{ components.textbox("Vergleich zum Menschen", "Die IMU ist wie der menschliche Gleichgewichtssinn.", true)}} + "Die Roboter Abbildung visualisiert die Daten, + welche durch eine inertiale Messeinheit (im englischen intertial measurement unit, kurz IMU) gemessen wurden. + Sie ist ein kleiner Sensor, welcher Drehgeschwindigkeiten und Beschleunigungen misst. + Wenn der echte Roboter also zur Seite kippt, dann misst die IMU dies und der kleine Bit-Bot hier oben kippt sich ebenfalls.", true) }} + {{ components.textbox("Vergleich zum Menschen", "Die IMU ist vergleichbar zum menschlichen Gleichgewichtssinn.", true)}} {{ components.textbox("Nutzen im Roboter", "Die Daten, welche die IMU aufnimmt, können zur Laufstabilisierung beitragen. Kippt der Roboter zu weit in die eine Richtung, kann die Software darauf reagieren und Maßnahmen vornehmen, die ein Fallen verhindern. Sollte der Roboter trotzdem hinfallen, zeigt das die IMU ebenfalls an und der Roboter kann in eine Fallpose gehen, um möglichst Hardware schonend zu landen. Zudem wird sie genutzt um festzustellen auf welche Seite wir gefallen sind und was wir machen müssen um wieder aufzustehen.", true)}} {{ components.textbox("Wie funktioniert die IMU?", "Unsere IMU besitzt zwei Hauptbestandteile: ein Gyroskop, welches die Rotationsgeschwindigkeit der IMU misst und ein Beschleunigungssensor, welcher Beschleunigung der IMU in verschiedene Richtungen misst. Meist nimmt eine IMU nicht nur Daten auf, sondern verarbeitet diese auch gleich ein wenig. Hierbei wird zum Beispiel die Schräglage des Roboters über kurze Zeit - durch integrieren(vereinfacht: aufsummieren von Werten) des Gyrometers errechnet. Über lange Zeit wird dieser Wert dann jedoch über die von den Beschleunigungssensoren gemessene Erdbeschleunigung korrigiert, ", false)}} + durch integrieren (vereinfacht: aufsummieren von Werten) des Gyroskop errechnet. Über lange Zeit wird dieser Wert dann jedoch über die von den Beschleunigungssensoren gemessene Erdbeschleunigung korrigiert. ", false)}}
diff --git a/bitbots_misc/bitbots_education/templates/pages/vision.html b/bitbots_misc/bitbots_education/templates/pages/vision.html index 864651158..b790ff43e 100644 --- a/bitbots_misc/bitbots_education/templates/pages/vision.html +++ b/bitbots_misc/bitbots_education/templates/pages/vision.html @@ -13,11 +13,11 @@

Vision

{{ components.textbox( "Was ist das für ein Kreis um dem Ball herum?", "Das hier ist nicht einfach nur der Lifestream der Kamera des Roboters. - Er beinhaltet zudem was der Roboter in dem Bild zu sehen glaubt. + Er beinhaltet zudem Markierungen der Dinge, die der Roboter in dem Bild zu sehen glaubt. Dabei werden Bälle mit einem grünen Kreis versehen, andere Roboter eingerahmt und Linien in blau markiert.", true) }} {{ components.textbox("Vergleich zum Menschen", "Die Vision gleicht der Objekterkennung des Menschen. Sie schaut auf ein Bild und sagt: An der Stelle ist ein Ball. Ähnlich wie beim Menschen wird sie nicht etwa manuell einprogrammiert, sondern erlernt.", true)}} - {{ components.textbox("Nutzen im Roboter", "Viele Komponenten des Roboters brauchen die Daten aus der Vision: Das Wissen über die Position des Balles ist für Verhaltensentscheidungen sehr spannend, erkannte Feldlinien dienen zur Orientierung auf dem Feld und Roboter werden in der Strategie, sowie der Pfadplanung berücksichtigt. Letzters vor allem damit es möglicht wenig Zusammenstöße gibt.", true)}} - {{ components.textbox("Woher weiß die Vision dass da ein Ball ist?", "Sie hat es durch Maschinelles Lernen ( Methode für Künstliche Intelligenz) gelernt. Ein Mensch lernt z.B. von seinen Eltern was ein Ball ist. Zuerst zeigt ein Elternteil auf einen Ball und sagt: Das ist ein Ball. Später zeigt das Kind selber auf Gegenstände, die wie ein Ball aussehen und der Erwachsene bestätigt die Annahme oder berichtigt. Bei dem machienellen lernernen fünktioniert es ähnlich. Während des Lernens versucht das neuronale Netz immer wieder zu raten ob und wo auf einem Bild ein Ball ist. Das wird dann abgeglichen mit einem vorher von Menschen annotierten Bild, wo der Ball an die richtige Stelle markiert wurde. Das Netzt ändert daraufhin die Gewichte seiner Neuronen und wird dadurch über die Zeit besser, bis es gut genug ist und das Training beendet wird. Das fertig gelerte Netz wird dann noch mit neuen Bildern getestet um Schummeln durch Außwendiglernen zu vermeiden und schließlich verbaut. Ab diesem Punkt wird nicht weiter gelernt, sondern nur bereits gelerntes auf neue Bilder anwendet.", false)}} + {{ components.textbox("Nutzen im Roboter", "Viele Komponenten des Roboters brauchen die Daten aus der Vision: Das Wissen über die Position des Balles ist für Verhaltensentscheidungen sehr spannend, erkannte Feldlinien dienen zur Orientierung auf dem Feld und erkannte Roboter werden in der Strategie, sowie der Pfadplanung berücksichtigt. Letzters vor allem damit es möglicht wenig Zusammenstöße gibt.", true)}} + {{ components.textbox("Woher weiß die Vision dass da ein Ball ist?", "Sie hat es durch Maschinelles Lernen (Methode für Künstliche Intelligenz) gelernt. Ein Mensch lernt z.B. von seinen Eltern was ein Ball ist. Zuerst zeigt ein Elternteil auf einen Ball und sagt: Das ist ein Ball. Später zeigt das Kind selber auf Gegenstände, die wie ein Ball aussehen und der Erwachsene bestätigt die Annahme oder berichtigt. Bei dem Machienellen Lernen funktioniert es ähnlich. Während des Lernens versucht ein künsliches neuronales Netz immer wieder zu raten ob und wo auf einem Bild ein Ball ist. Das wird dann abgeglichen mit einem vorher von Menschen annotierten Bild, wo der Ball an der richtigen Stelle markiert wurde. Das Netz ändert daraufhin die Gewichte seiner Neuronen und wird dadurch über die Zeit besser, bis es gut genug ist und das Training beendet wird. Das fertig gelerte Netz wird dann noch mit neuen Bildern getestet um Schummeln durch Außwendiglernen zu vermeiden und schließlich verbaut. Ab diesem Punkt wird nicht weiter gelernt, sondern nur bereits gelerntes auf neue Bilder anwendet.", false)}}
From c3fa05d1df62801db370385a917a4d3cad69f5a8 Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Mon, 26 May 2025 18:48:54 +0200 Subject: [PATCH 050/159] Move to server side flask rendering --- .../bitbots_education/app.py | 35 ++++++++++++++++ .../bitbots_education/launch/education.launch | 3 +- bitbots_misc/bitbots_education/package.xml | 1 + bitbots_misc/bitbots_education/setup.py | 40 ++----------------- .../templates/{index.html => base.html} | 28 ++++--------- .../templates/components/behavior_stack.html | 2 + .../templates/components/icon_footer.html | 10 ++--- .../templates/pages/behavior.html | 14 +++++-- .../templates/pages/dashboard.html | 22 ++++++---- .../templates/pages/imu.html | 14 +++++-- .../pages/{motor_heat.html => motors.html} | 13 ++++-- .../templates/pages/vision.html | 19 +++++---- 12 files changed, 111 insertions(+), 90 deletions(-) create mode 100644 bitbots_misc/bitbots_education/bitbots_education/app.py rename bitbots_misc/bitbots_education/templates/{index.html => base.html} (62%) rename bitbots_misc/bitbots_education/templates/pages/{motor_heat.html => motors.html} (94%) diff --git a/bitbots_misc/bitbots_education/bitbots_education/app.py b/bitbots_misc/bitbots_education/bitbots_education/app.py new file mode 100644 index 000000000..42f93d3b6 --- /dev/null +++ b/bitbots_misc/bitbots_education/bitbots_education/app.py @@ -0,0 +1,35 @@ +import os +from flask import Flask, render_template +from ament_index_python import get_package_share_directory + + +template_dir = os.path.join( + get_package_share_directory("bitbots_education"), "templates" +) + +app = Flask(__name__, template_folder=template_dir) + + +@app.route("/") +def index(): + return render_template("pages/dashboard.html") + +@app.route("/imu") +def imu(): + return render_template("pages/imu.html") + +@app.route("/vision") +def vision(): + return render_template("pages/vision.html") + +@app.route("/motors") +def motors(): + return render_template("pages/motors.html") + +@app.route("/behavior") +def behavior(): + return render_template("pages/behavior.html") + + +def main(): + app.run(host="0.0.0.0", port=8000) diff --git a/bitbots_misc/bitbots_education/launch/education.launch b/bitbots_misc/bitbots_education/launch/education.launch index 81c6f166b..89f604ae2 100644 --- a/bitbots_misc/bitbots_education/launch/education.launch +++ b/bitbots_misc/bitbots_education/launch/education.launch @@ -5,5 +5,6 @@ - + + diff --git a/bitbots_misc/bitbots_education/package.xml b/bitbots_misc/bitbots_education/package.xml index a8a229ec2..7e1636352 100644 --- a/bitbots_misc/bitbots_education/package.xml +++ b/bitbots_misc/bitbots_education/package.xml @@ -10,6 +10,7 @@ web_video_server rosbridge_suite python3-jinja2 + python3-flask ament_copyright ament_flake8 ament_pep257 diff --git a/bitbots_misc/bitbots_education/setup.py b/bitbots_misc/bitbots_education/setup.py index b78ab9db7..8071a55aa 100644 --- a/bitbots_misc/bitbots_education/setup.py +++ b/bitbots_misc/bitbots_education/setup.py @@ -7,7 +7,6 @@ package_name = "bitbots_education" - def generate_data_files(share_path, dir): data_files = [] @@ -17,39 +16,6 @@ def generate_data_files(share_path, dir): return data_files - -def build_html(): - output_path = "out" - components_path = os.path.join(output_path, "components") - template_path = "templates" - - env = Environment(loader=FileSystemLoader(template_path)) - - # Render all templates excluding the ones in the components folder - for root, _, files in os.walk(template_path): - for file in files: - if os.path.abspath(root) != os.path.abspath(components_path): - if file.endswith(".html"): - template_file = os.path.relpath(os.path.join(root, file), template_path) - template = env.get_template(template_file) - output = template.render() - - # Get install path - output_file_path = os.path.join(output_path, template_file) - os.makedirs(os.path.dirname(output_file_path), exist_ok=True) - - with open(output_file_path, "w") as f: - f.write(output) - else: - # Copy all other files - src = os.path.join(root, file) - dst = os.path.join(output_path, os.path.relpath(src, template_path)) - os.makedirs(os.path.dirname(dst), exist_ok=True) - shutil.copy2(src, dst) - - -build_html() - setup( name=package_name, data_files=[ @@ -57,7 +23,7 @@ def build_html(): ("share/" + package_name, ["package.xml"]), ("share/" + package_name + "/launch", glob.glob("launch/*.launch")), ] - + generate_data_files("share/" + package_name + "/", "out/"), + + generate_data_files("share/" + package_name + "/", "templates/"), version="0.0.0", packages=find_packages(exclude=["test"]), install_requires=["setuptools"], @@ -68,6 +34,8 @@ def build_html(): license="Apache-2.0", tests_require=["pytest"], entry_points={ - "console_scripts": [], + "console_scripts": [ + "webserver = bitbots_education.app:main", + ], }, ) diff --git a/bitbots_misc/bitbots_education/templates/index.html b/bitbots_misc/bitbots_education/templates/base.html similarity index 62% rename from bitbots_misc/bitbots_education/templates/index.html rename to bitbots_misc/bitbots_education/templates/base.html index 8e202667f..46a522d72 100644 --- a/bitbots_misc/bitbots_education/templates/index.html +++ b/bitbots_misc/bitbots_education/templates/base.html @@ -36,32 +36,18 @@
-
-

+

+ {% block banner %}{% endblock %} +

+ {% block content %}{% endblock %} +
+
+ {% block icons %}{% endblock %}
-
diff --git a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html index 2fdc7724f..2bf32ced3 100644 --- a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html +++ b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html @@ -74,6 +74,8 @@ } while (subtree != null) document.getElementById("stack_label").innerHTML = output_string; }; + + diff --git a/bitbots_misc/bitbots_education/templates/components/icon_footer.html b/bitbots_misc/bitbots_education/templates/components/icon_footer.html index 412b140c5..ea8be5089 100644 --- a/bitbots_misc/bitbots_education/templates/components/icon_footer.html +++ b/bitbots_misc/bitbots_education/templates/components/icon_footer.html @@ -1,28 +1,28 @@ {% macro icons(site) %}
- + - + - + - + - + diff --git a/bitbots_misc/bitbots_education/templates/pages/behavior.html b/bitbots_misc/bitbots_education/templates/pages/behavior.html index 1de404f76..9b173fc40 100644 --- a/bitbots_misc/bitbots_education/templates/pages/behavior.html +++ b/bitbots_misc/bitbots_education/templates/pages/behavior.html @@ -3,10 +3,11 @@ {% import "components/textbox.html" as components %} {% import "components/icon_footer.html" as icon_components %} -

Behavior

-
- {{ icon_components.icons("behavior") }} -
+{% extends "base.html" %} + +{% block banner %}Behavior{% endblock %} + +{% block content %}
{{ behavior_stack_component.behavior_stack_component() }} {{ components.textbox("Warum ändern sich die Boxen und wieso haben sie unterschiedliche Farben?", @@ -29,4 +30,9 @@

Behavior

weil ein Entscheidung sich an einer Stelle aufgrund einer veränderten Spielsituation ändert, ändert sich auch der ganze Stapel da drüber und alles was sich die Software dort gemerkt hat, wird zurückgesetzt.", false)}}
+{% endblock %} + +{% block icons %} + {{ icon_components.icons("behavior") }} +{% endblock %} diff --git a/bitbots_misc/bitbots_education/templates/pages/dashboard.html b/bitbots_misc/bitbots_education/templates/pages/dashboard.html index 74a05abc3..0e7bde8e6 100644 --- a/bitbots_misc/bitbots_education/templates/pages/dashboard.html +++ b/bitbots_misc/bitbots_education/templates/pages/dashboard.html @@ -5,14 +5,14 @@ {% import "components/motor_heat.html" as heat_components %} {% import "components/icon_footer.html" as icon_components %} -

Hamburg Bit-Bots

-
- {{ icon_components.icons("dashboard") }} -
+{% extends "base.html" %} + +{% block banner %}Hamburg Bit-Bots{% endblock %} +{% block content %}
- +
@@ -22,13 +22,13 @@

Hamburg Bit-Bots

IMU

- +
{{ vision_components.vision_component() }}

Vision

- +
{{ heat_components.motor_heat_component(true) }} @@ -36,7 +36,7 @@

Hamburg Bit-Bots

Motor Heat

- +
@@ -53,3 +53,9 @@

Hamburg Bit-Bots

{{ components.textbox("Was sind das für Boxen da unten? Kann man da drauf drücken?", "Jeder der Boxen geht auf eine andere Komponente des Roboters ein. Wenn du mehr über eine Komponente erfahren möchtest, kannst du auf die jeweilige Box drücken und wirst zu einer anderen Seite weitergeleitet, die mehr detaillierte Information bereit hält.", true)}} +{% endblock %} + +{% block icons %} + {{ icon_components.icons("dashboard") }} +{% endblock %} + diff --git a/bitbots_misc/bitbots_education/templates/pages/imu.html b/bitbots_misc/bitbots_education/templates/pages/imu.html index 2835d97b4..ba2b9be43 100644 --- a/bitbots_misc/bitbots_education/templates/pages/imu.html +++ b/bitbots_misc/bitbots_education/templates/pages/imu.html @@ -2,10 +2,11 @@ {% import "components/textbox.html" as components %} {% import "components/icon_footer.html" as icon_components %} -

IMU

-
- {{ icon_components.icons("imu") }} -
+{% extends "base.html" %} + +{% block banner %}IMU{% endblock %} + +{% block content %}
{{ imu_components.imu_component() }} @@ -22,4 +23,9 @@

IMU

Richtungen misst. Meist nimmt eine IMU nicht nur Daten auf, sondern verarbeitet diese auch gleich ein wenig. Hierbei wird zum Beispiel die Schräglage des Roboters über kurze Zeit durch integrieren (vereinfacht: aufsummieren von Werten) des Gyroskop errechnet. Über lange Zeit wird dieser Wert dann jedoch über die von den Beschleunigungssensoren gemessene Erdbeschleunigung korrigiert. ", false)}}
+{% endblock %} + +{% block icons %} + {{ icon_components.icons("imu") }} +{% endblock %} diff --git a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html b/bitbots_misc/bitbots_education/templates/pages/motors.html similarity index 94% rename from bitbots_misc/bitbots_education/templates/pages/motor_heat.html rename to bitbots_misc/bitbots_education/templates/pages/motors.html index b9d3a8a8c..34857ec19 100644 --- a/bitbots_misc/bitbots_education/templates/pages/motor_heat.html +++ b/bitbots_misc/bitbots_education/templates/pages/motors.html @@ -4,11 +4,11 @@ {% import "components/icon_footer.html" as icon_components %} -

Motor Heat

-
- {{ icon_components.icons("heat") }} -
+{% extends "base.html" %} + +{% block banner %}Motors{% endblock %} +{% block content %}
{{ heat_components.motor_heat_component(false) }} @@ -32,6 +32,11 @@

Motor Heat

belohnt, schafft er es nicht, wird ihm das auch mitgeteilt und er versucht einen neuen Ansatz. Meist ist das Laufen lernen hier auch ein Prozess, der über viele Wiederholungen stattfindet, dann aber auch bessere Ergebnisse liefert, als herkömmliche Algorithmen.", false)}}
+{% endblock %} + +{% block icons %} +{{ icon_components.icons("motors_heat") }} +{% endblock %} diff --git a/bitbots_misc/bitbots_education/templates/pages/vision.html b/bitbots_misc/bitbots_education/templates/pages/vision.html index b790ff43e..1753d429c 100644 --- a/bitbots_misc/bitbots_education/templates/pages/vision.html +++ b/bitbots_misc/bitbots_education/templates/pages/vision.html @@ -2,22 +2,27 @@ {% import "components/textbox.html" as components %} {% import "components/icon_footer.html" as icon_components %} -

Vision

-
- {{ icon_components.icons("vision") }} -
+{% extends "base.html" %} + +{% block banner %}Vision{% endblock %} +{% block content %} {{ vision_components.vision_component() }}
{{ components.textbox( - "Was ist das für ein Kreis um dem Ball herum?", - "Das hier ist nicht einfach nur der Lifestream der Kamera des Roboters. - Er beinhaltet zudem Markierungen der Dinge, die der Roboter in dem Bild zu sehen glaubt. + "Was ist das für ein Kreis um dem Ball herum?", + "Das hier ist nicht einfach nur der Lifestream der Kamera des Roboters. + Er beinhaltet zudem Markierungen der Dinge, die der Roboter in dem Bild zu sehen glaubt. Dabei werden Bälle mit einem grünen Kreis versehen, andere Roboter eingerahmt und Linien in blau markiert.", true) }} {{ components.textbox("Vergleich zum Menschen", "Die Vision gleicht der Objekterkennung des Menschen. Sie schaut auf ein Bild und sagt: An der Stelle ist ein Ball. Ähnlich wie beim Menschen wird sie nicht etwa manuell einprogrammiert, sondern erlernt.", true)}} {{ components.textbox("Nutzen im Roboter", "Viele Komponenten des Roboters brauchen die Daten aus der Vision: Das Wissen über die Position des Balles ist für Verhaltensentscheidungen sehr spannend, erkannte Feldlinien dienen zur Orientierung auf dem Feld und erkannte Roboter werden in der Strategie, sowie der Pfadplanung berücksichtigt. Letzters vor allem damit es möglicht wenig Zusammenstöße gibt.", true)}} {{ components.textbox("Woher weiß die Vision dass da ein Ball ist?", "Sie hat es durch Maschinelles Lernen (Methode für Künstliche Intelligenz) gelernt. Ein Mensch lernt z.B. von seinen Eltern was ein Ball ist. Zuerst zeigt ein Elternteil auf einen Ball und sagt: Das ist ein Ball. Später zeigt das Kind selber auf Gegenstände, die wie ein Ball aussehen und der Erwachsene bestätigt die Annahme oder berichtigt. Bei dem Machienellen Lernen funktioniert es ähnlich. Während des Lernens versucht ein künsliches neuronales Netz immer wieder zu raten ob und wo auf einem Bild ein Ball ist. Das wird dann abgeglichen mit einem vorher von Menschen annotierten Bild, wo der Ball an der richtigen Stelle markiert wurde. Das Netz ändert daraufhin die Gewichte seiner Neuronen und wird dadurch über die Zeit besser, bis es gut genug ist und das Training beendet wird. Das fertig gelerte Netz wird dann noch mit neuen Bildern getestet um Schummeln durch Außwendiglernen zu vermeiden und schließlich verbaut. Ab diesem Punkt wird nicht weiter gelernt, sondern nur bereits gelerntes auf neue Bilder anwendet.", false)}}
+{% endblock %} + +{% block icons %} +{{ icon_components.icons("vision") }} +{% endblock %} From 3f898a9e8dc7655abdf52d844b218472f6747dbb Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Mon, 26 May 2025 19:03:02 +0200 Subject: [PATCH 051/159] Fix static content --- .../bitbots_education/bitbots_education/app.py | 5 ++++- bitbots_misc/bitbots_education/setup.py | 3 ++- .../{templates/assets => static}/abstract_robot.svg | 0 .../assets => static}/abstract_robot.webp | Bin .../{templates/assets => static}/bitBot_color.svg | 0 .../assets => static}/bitBot_whiteboard.svg | 0 .../bitbots_education/templates/components/imu.html | 2 +- .../templates/components/motor_heat.html | 2 +- 8 files changed, 8 insertions(+), 4 deletions(-) rename bitbots_misc/bitbots_education/{templates/assets => static}/abstract_robot.svg (100%) rename bitbots_misc/bitbots_education/{templates/assets => static}/abstract_robot.webp (100%) rename bitbots_misc/bitbots_education/{templates/assets => static}/bitBot_color.svg (100%) rename bitbots_misc/bitbots_education/{templates/assets => static}/bitBot_whiteboard.svg (100%) diff --git a/bitbots_misc/bitbots_education/bitbots_education/app.py b/bitbots_misc/bitbots_education/bitbots_education/app.py index 42f93d3b6..028013f0b 100644 --- a/bitbots_misc/bitbots_education/bitbots_education/app.py +++ b/bitbots_misc/bitbots_education/bitbots_education/app.py @@ -6,8 +6,11 @@ template_dir = os.path.join( get_package_share_directory("bitbots_education"), "templates" ) +static_folder = os.path.join( + get_package_share_directory("bitbots_education"), "static" +) -app = Flask(__name__, template_folder=template_dir) +app = Flask(__name__, template_folder=template_dir, static_folder=static_folder) @app.route("/") diff --git a/bitbots_misc/bitbots_education/setup.py b/bitbots_misc/bitbots_education/setup.py index 8071a55aa..748ac1cbf 100644 --- a/bitbots_misc/bitbots_education/setup.py +++ b/bitbots_misc/bitbots_education/setup.py @@ -23,7 +23,8 @@ def generate_data_files(share_path, dir): ("share/" + package_name, ["package.xml"]), ("share/" + package_name + "/launch", glob.glob("launch/*.launch")), ] - + generate_data_files("share/" + package_name + "/", "templates/"), + + generate_data_files("share/" + package_name + "/", "templates/") + + generate_data_files("share/" + package_name + "/", "static/"), version="0.0.0", packages=find_packages(exclude=["test"]), install_requires=["setuptools"], diff --git a/bitbots_misc/bitbots_education/templates/assets/abstract_robot.svg b/bitbots_misc/bitbots_education/static/abstract_robot.svg similarity index 100% rename from bitbots_misc/bitbots_education/templates/assets/abstract_robot.svg rename to bitbots_misc/bitbots_education/static/abstract_robot.svg diff --git a/bitbots_misc/bitbots_education/templates/assets/abstract_robot.webp b/bitbots_misc/bitbots_education/static/abstract_robot.webp similarity index 100% rename from bitbots_misc/bitbots_education/templates/assets/abstract_robot.webp rename to bitbots_misc/bitbots_education/static/abstract_robot.webp diff --git a/bitbots_misc/bitbots_education/templates/assets/bitBot_color.svg b/bitbots_misc/bitbots_education/static/bitBot_color.svg similarity index 100% rename from bitbots_misc/bitbots_education/templates/assets/bitBot_color.svg rename to bitbots_misc/bitbots_education/static/bitBot_color.svg diff --git a/bitbots_misc/bitbots_education/templates/assets/bitBot_whiteboard.svg b/bitbots_misc/bitbots_education/static/bitBot_whiteboard.svg similarity index 100% rename from bitbots_misc/bitbots_education/templates/assets/bitBot_whiteboard.svg rename to bitbots_misc/bitbots_education/static/bitBot_whiteboard.svg diff --git a/bitbots_misc/bitbots_education/templates/components/imu.html b/bitbots_misc/bitbots_education/templates/components/imu.html index 1810a48ec..af060e791 100644 --- a/bitbots_misc/bitbots_education/templates/components/imu.html +++ b/bitbots_misc/bitbots_education/templates/components/imu.html @@ -57,6 +57,6 @@ this.listener.unsubscribe(); }, }" > - +
{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/components/motor_heat.html b/bitbots_misc/bitbots_education/templates/components/motor_heat.html index ab27afda0..868cdac40 100644 --- a/bitbots_misc/bitbots_education/templates/components/motor_heat.html +++ b/bitbots_misc/bitbots_education/templates/components/motor_heat.html @@ -179,7 +179,7 @@
- +
From 06720caef07c9cded44918bf5a75b9bb3e7437b4 Mon Sep 17 00:00:00 2001 From: Florian Vahl Date: Mon, 26 May 2025 22:33:25 +0200 Subject: [PATCH 052/159] Split alpine state into script tag --- .../bitbots_education/templates/base.html | 2 +- .../templates/components/behavior_stack.html | 51 ++++++++++--------- .../templates/components/current_action.html | 41 ++++++++------- .../templates/components/imu.html | 15 +++--- .../templates/components/vision.html | 19 +++---- .../templates/pages/motors.html | 2 +- 6 files changed, 70 insertions(+), 60 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/base.html b/bitbots_misc/bitbots_education/templates/base.html index 46a522d72..e30cbad31 100644 --- a/bitbots_misc/bitbots_education/templates/base.html +++ b/bitbots_misc/bitbots_education/templates/base.html @@ -3,7 +3,7 @@ - + diff --git a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html index 2bf32ced3..d92d03015 100644 --- a/bitbots_misc/bitbots_education/templates/components/behavior_stack.html +++ b/bitbots_misc/bitbots_education/templates/components/behavior_stack.html @@ -75,34 +75,35 @@ document.getElementById("stack_label").innerHTML = output_string; }; - + function behavior_stack_state() { + return { + listener: null, + run: true, + init() { + this.listener = new ROSLIB.Topic({ + ros : ros, + name : '/debug/dsd/body_behavior/dsd_stack', + messageType : 'std_msgs/msg/String' + }); + this.listener.subscribe(behavior_callback); + }, + toggle() { + this.run = !this.run; + if (this.run) { + this.listener.subscribe(behavior_callback); + } else { + this.listener.unsubscribe(); + } + }, + destroy() { + this.listener.unsubscribe(); + }, + }; + } -
+
diff --git a/bitbots_misc/bitbots_education/templates/components/current_action.html b/bitbots_misc/bitbots_education/templates/components/current_action.html index bc34f8fcb..d80de0503 100644 --- a/bitbots_misc/bitbots_education/templates/components/current_action.html +++ b/bitbots_misc/bitbots_education/templates/components/current_action.html @@ -1,23 +1,28 @@ {% macro current_action_component() %} -
+ + +

+ }; + } + + +
{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/components/vision.html b/bitbots_misc/bitbots_education/templates/components/vision.html index 85b80054f..8a9bc1b98 100644 --- a/bitbots_misc/bitbots_education/templates/components/vision.html +++ b/bitbots_misc/bitbots_education/templates/components/vision.html @@ -1,14 +1,15 @@ {% macro vision_component() %} - +
+ +
{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/pages/motors.html b/bitbots_misc/bitbots_education/templates/pages/motors.html index 34857ec19..9c84ce9fb 100644 --- a/bitbots_misc/bitbots_education/templates/pages/motors.html +++ b/bitbots_misc/bitbots_education/templates/pages/motors.html @@ -35,7 +35,7 @@ {% endblock %} {% block icons %} -{{ icon_components.icons("motors_heat") }} +{{ icon_components.icons("heat") }} {% endblock %} From 38fd35032f975ab167b7267d3cc47729b95b4edc Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Tue, 27 May 2025 22:06:14 +0200 Subject: [PATCH 053/159] get current action from behavior stack --- .../templates/components/current_action.html | 63 ++++++++++++++++++- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/current_action.html b/bitbots_misc/bitbots_education/templates/components/current_action.html index d80de0503..bb8226527 100644 --- a/bitbots_misc/bitbots_education/templates/components/current_action.html +++ b/bitbots_misc/bitbots_education/templates/components/current_action.html @@ -1,6 +1,48 @@ {% macro current_action_component() %} + + + +
+
    +
    + + + + +
    + +{% endmacro %} diff --git a/bitbots_misc/bitbots_education/templates/pages/behavior.html b/bitbots_misc/bitbots_education/templates/pages/behavior.html index 9b173fc40..ec82bd0c9 100644 --- a/bitbots_misc/bitbots_education/templates/pages/behavior.html +++ b/bitbots_misc/bitbots_education/templates/pages/behavior.html @@ -1,5 +1,6 @@ {% import "components/current_action.html" as current_action_components %} {% import "components/behavior_stack.html" as behavior_stack_component %} +{% import "components/behavior_tree.html" as behavior_tree_component %} {% import "components/textbox.html" as components %} {% import "components/icon_footer.html" as icon_components %} @@ -9,7 +10,7 @@ {% block content %}
    - {{ behavior_stack_component.behavior_stack_component() }} + {{ behavior_tree_component.behavior_tree_component() }} {{ components.textbox("Warum ändern sich die Boxen und wieso haben sie unterschiedliche Farben?", "Jede Box steht für eine Entscheidung oder eine Aktion, die der Roboter macht. Dabei macht er meist viele unterschiedliche Abwägungen (blaue Boxen), bis er sich am Ende für eine Aktion entscheidet, die er dann ausführt (helle Box). Zusätzlich steht auch unter jeder Entscheidung neben dem Pfeil nach unten, From ee36b7fc2218f555cf4d85bbb432ac93d6e9094d Mon Sep 17 00:00:00 2001 From: Lea Date: Wed, 28 May 2025 15:58:49 +0200 Subject: [PATCH 056/159] include bitbot_education in syncronisation to rorobt --- sync_includes_wolfgang_nuc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/sync_includes_wolfgang_nuc.yaml b/sync_includes_wolfgang_nuc.yaml index 9f8005637..f4e5da20d 100644 --- a/sync_includes_wolfgang_nuc.yaml +++ b/sync_includes_wolfgang_nuc.yaml @@ -10,6 +10,7 @@ include: - bitbots_bringup - bitbots_diagnostic - bitbots_docs + - bitbots_education - bitbots_extrinsic_calibration - bitbots_ipm - bitbots_parameter_blackboard From 6c308cfb8457a3cafd4854d020ea93e7d62bd84f Mon Sep 17 00:00:00 2001 From: Lea Wedmann Date: Thu, 29 May 2025 21:12:05 +0200 Subject: [PATCH 057/159] make behavior tree scrollable and change colors --- .../templates/components/behavior_tree.html | 137 ++++++++++-------- .../templates/pages/behavior.html | 2 +- 2 files changed, 76 insertions(+), 63 deletions(-) diff --git a/bitbots_misc/bitbots_education/templates/components/behavior_tree.html b/bitbots_misc/bitbots_education/templates/components/behavior_tree.html index 9f1fe13de..300e21718 100644 --- a/bitbots_misc/bitbots_education/templates/components/behavior_tree.html +++ b/bitbots_misc/bitbots_education/templates/components/behavior_tree.html @@ -53,7 +53,7 @@ console.log(tree); if (tree.children !== undefined) { - let string = `
  • ${reason} --> ${tree.name} + let string = `
  • ${reason} --> ${tree.name}