first commit
parent
979d9521a5
commit
b7d16e74a5
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="PyDocumentationSettings">
|
||||||
|
<option name="format" value="PLAIN" />
|
||||||
|
<option name="myDocStringFormat" value="Plain" />
|
||||||
|
</component>
|
||||||
|
</module>
|
@ -0,0 +1,19 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredErrors">
|
||||||
|
<list>
|
||||||
|
<option value="N806" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredIdentifiers">
|
||||||
|
<list>
|
||||||
|
<option value="list.copy" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="fcp_env" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="FCPCodebase" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/FCPCodebase.iml" filepath="$PROJECT_DIR$/.idea/FCPCodebase.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
@ -0,0 +1,36 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent as Agent
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
from scripts.commons.Script import Script
|
||||||
|
|
||||||
|
script = Script()
|
||||||
|
a = script.args
|
||||||
|
|
||||||
|
# Args: Server IP, Agent Port, Monitor Port, Uniform No., Robot Type, Team Name
|
||||||
|
p1 = Agent(a.i, a.p, a.m, a.u, a.r, a.t)
|
||||||
|
p2 = Agent(a.i, a.p, a.m, a.u, a.r, "Opponent")
|
||||||
|
players = [p1,p2]
|
||||||
|
|
||||||
|
p1.scom.unofficial_beam((-3,0,p1.world.robot.beam_height), 0)
|
||||||
|
p2.scom.unofficial_beam((-3,0,p2.world.robot.beam_height), 0)
|
||||||
|
|
||||||
|
getting_up = [False]*2
|
||||||
|
|
||||||
|
while True:
|
||||||
|
for i in range(len(players)):
|
||||||
|
p = players[i]
|
||||||
|
w = p.world
|
||||||
|
|
||||||
|
player_2d = w.robot.loc_head_position[:2]
|
||||||
|
ball_2d = w.ball_abs_pos[:2]
|
||||||
|
goal_dir = M.vector_angle( (15,0)-player_2d ) # Goal direction
|
||||||
|
|
||||||
|
if p.behavior.is_ready("Get_Up") or getting_up[i]:
|
||||||
|
getting_up[i] = not p.behavior.execute("Get_Up") # True on completion
|
||||||
|
else:
|
||||||
|
p.behavior.execute("Basic_Kick", goal_dir)
|
||||||
|
|
||||||
|
p.scom.commit_and_send( w.robot.get_command() )
|
||||||
|
|
||||||
|
# all players must commit and send before the server updates
|
||||||
|
p1.scom.receive()
|
||||||
|
p2.scom.receive()
|
@ -0,0 +1,13 @@
|
|||||||
|
from scripts.commons.Script import Script
|
||||||
|
script = Script() # Initialize: load config file, parse arguments, build cpp modules
|
||||||
|
a = script.args
|
||||||
|
|
||||||
|
from agent.Agent import Agent
|
||||||
|
|
||||||
|
# Args: Server IP, Agent Port, Monitor Port, Uniform No., Team name, Enable Log, Enable Draw
|
||||||
|
team_args = ((a.i, a.p, a.m, u, a.t, True, True) for u in range(1,12))
|
||||||
|
script.batch_create(Agent,team_args)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
script.batch_execute_agent()
|
||||||
|
script.batch_receive()
|
@ -0,0 +1,14 @@
|
|||||||
|
from scripts.commons.Script import Script
|
||||||
|
script = Script() # Initialize: load config file, parse arguments, build cpp modules
|
||||||
|
a = script.args
|
||||||
|
|
||||||
|
from agent.Agent import Agent
|
||||||
|
|
||||||
|
# Args: Server IP, Agent Port, Monitor Port, Uniform No., Team name, Enable Log, Enable Draw
|
||||||
|
script.batch_create(Agent, ((a.i, a.p, a.m, a.u, a.t, True, True),)) #one player for home team
|
||||||
|
script.batch_create(Agent, ((a.i, a.p, a.m, a.u, "Opponent", True, True),)) #one player for away team
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
script.batch_execute_agent()
|
||||||
|
script.batch_receive()
|
@ -0,0 +1,18 @@
|
|||||||
|
from scripts.commons.Script import Script
|
||||||
|
script = Script(cpp_builder_unum=1) # Initialize: load config file, parse arguments, build cpp modules
|
||||||
|
a = script.args
|
||||||
|
|
||||||
|
if a.P: # penalty shootout
|
||||||
|
from agent.Agent_Penalty import Agent
|
||||||
|
else: # normal agent
|
||||||
|
from agent.Agent import Agent
|
||||||
|
|
||||||
|
# Args: Server IP, Agent Port, Monitor Port, Uniform No., Team name, Enable Log, Enable Draw, Wait for Server, is magmaFatProxy
|
||||||
|
if a.D: # debug mode
|
||||||
|
player = Agent(a.i, a.p, a.m, a.u, a.t, True, True, False, a.F)
|
||||||
|
else:
|
||||||
|
player = Agent(a.i, a.p, None, a.u, a.t, False, False, False, a.F)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
player.think_and_send()
|
||||||
|
player.scom.receive()
|
@ -0,0 +1,96 @@
|
|||||||
|
def main():
|
||||||
|
|
||||||
|
from scripts.commons.Script import Script
|
||||||
|
script = Script() #Initialize: load config file, parse arguments, build cpp modules (warns the user about inconsistencies before choosing a test script)
|
||||||
|
|
||||||
|
# Allows using local version of StableBaselines3 (e.g. https://github.com/m-abr/Adaptive-Symmetry-Learning)
|
||||||
|
# place the 'stable-baselines3' folder in the parent directory of this project
|
||||||
|
import sys
|
||||||
|
from os.path import dirname, abspath, join
|
||||||
|
sys.path.insert( 0, join( dirname(dirname( abspath(__file__) )), "stable-baselines3") )
|
||||||
|
|
||||||
|
from scripts.commons.UI import UI
|
||||||
|
from os.path import isfile, join, realpath, dirname
|
||||||
|
from os import listdir, getcwd
|
||||||
|
from importlib import import_module
|
||||||
|
|
||||||
|
_cwd = realpath( join(getcwd(), dirname(__file__)))
|
||||||
|
gyms_path = _cwd + "/scripts/gyms/"
|
||||||
|
utils_path = _cwd + "/scripts/utils/"
|
||||||
|
exclusions = ["__init__.py"]
|
||||||
|
|
||||||
|
utils = sorted([f[:-3] for f in listdir(utils_path) if isfile(join(utils_path, f)) and f.endswith(".py") and f not in exclusions], key=lambda x: (x != "Server", x))
|
||||||
|
gyms = sorted([f[:-3] for f in listdir(gyms_path ) if isfile(join(gyms_path , f)) and f.endswith(".py") and f not in exclusions])
|
||||||
|
|
||||||
|
while True:
|
||||||
|
_, col_idx, col = UI.print_table( [utils, gyms], ["Demos & Tests & Utils","Gyms"], cols_per_title=[2,1], numbering=[True]*2, prompt='Choose script (ctrl+c to exit): ' )
|
||||||
|
|
||||||
|
is_gym = False
|
||||||
|
if col == 0:
|
||||||
|
chosen = ("scripts.utils." , utils[col_idx])
|
||||||
|
elif col == 1:
|
||||||
|
chosen = ("scripts.gyms." , gyms[col_idx])
|
||||||
|
is_gym = True
|
||||||
|
|
||||||
|
cls_name = chosen[1]
|
||||||
|
mod = import_module(chosen[0]+chosen[1])
|
||||||
|
|
||||||
|
'''
|
||||||
|
An imported script should not automatically execute the main code because:
|
||||||
|
- Multiprocessing methods, such as 'forkserver' and 'spawn', will execute the main code in every child
|
||||||
|
- The script can only be called once unless it is reloaded
|
||||||
|
'''
|
||||||
|
if not is_gym:
|
||||||
|
'''
|
||||||
|
Utils receive a script support object with user-defined arguments and useful methods
|
||||||
|
Each util must implement an execute() method, which may or may not return
|
||||||
|
'''
|
||||||
|
from world.commons.Draw import Draw
|
||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
obj = getattr(mod,cls_name)(script)
|
||||||
|
try:
|
||||||
|
obj.execute() # Util may return normally or through KeyboardInterrupt
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nctrl+c pressed, returning...\n")
|
||||||
|
Draw.clear_all() # clear all drawings
|
||||||
|
Base_Agent.terminate_all() # close all server sockets + monitor socket
|
||||||
|
script.players = [] # clear list of players created through batch commands
|
||||||
|
del obj
|
||||||
|
|
||||||
|
else:
|
||||||
|
'''
|
||||||
|
Gyms must implement a class Train() which is initialized with user-defined arguments and implements:
|
||||||
|
train() - method to run the optimization and save a new model
|
||||||
|
test(folder_dir, folder_name, model_file) - method to load an existing model and test it
|
||||||
|
'''
|
||||||
|
from scripts.commons.Train_Base import Train_Base
|
||||||
|
|
||||||
|
print("\nBefore using GYMS, make sure all server parameters are set correctly")
|
||||||
|
print("(sync mode should be 'On', real time should be 'Off', cheats should be 'On', ...)")
|
||||||
|
print("To change these parameters go to the previous menu, and select Server\n")
|
||||||
|
print("Also, GYMS start their own servers, so don't run any server manually")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
idx = UI.print_table([["Train","Test","Retrain"]], numbering=[True], prompt='Choose option (ctrl+c to return): ')[0]
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print()
|
||||||
|
break
|
||||||
|
|
||||||
|
if idx == 0:
|
||||||
|
mod.Train(script).train(dict())
|
||||||
|
else:
|
||||||
|
model_info = Train_Base.prompt_user_for_model()
|
||||||
|
if model_info is not None and idx == 1:
|
||||||
|
mod.Train(script).test(model_info)
|
||||||
|
elif model_info is not None:
|
||||||
|
mod.Train(script).train(model_info)
|
||||||
|
|
||||||
|
|
||||||
|
# allow child processes to bypass this file
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nctrl+c pressed, exiting...")
|
||||||
|
exit()
|
@ -0,0 +1,35 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent as Agent
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
from scripts.commons.Script import Script
|
||||||
|
|
||||||
|
script = Script()
|
||||||
|
a = script.args
|
||||||
|
|
||||||
|
# Args: Server IP, Agent Port, Monitor Port, Uniform No., Robot Type, Team Name
|
||||||
|
# This could be done in 1 line, but calling batch_create twice enhances readability
|
||||||
|
|
||||||
|
script.batch_create(Agent, ((a.i,a.p,a.m,u+1,a.r,"Home") for u in range(3)) )
|
||||||
|
script.batch_create(Agent, ((a.i,a.p,a.m,u+1,a.r,"Away") for u in range(3)) )
|
||||||
|
|
||||||
|
players = script.players
|
||||||
|
p_num = len(players) # total number of players
|
||||||
|
script.batch_unofficial_beam( (-3, 4.5-abs(3*i-7.5), 0.5, 0) for i in range(p_num) )
|
||||||
|
|
||||||
|
getting_up = [False]*p_num
|
||||||
|
|
||||||
|
while True:
|
||||||
|
for i in range(p_num):
|
||||||
|
p = players[i]
|
||||||
|
w = p.world
|
||||||
|
|
||||||
|
player_2d = w.robot.loc_head_position[:2]
|
||||||
|
ball_2d = w.ball_abs_pos[:2]
|
||||||
|
goal_dir = M.vector_angle( (15,0)-player_2d ) # Goal direction
|
||||||
|
|
||||||
|
if p.behavior.is_ready("Get_Up") or getting_up[i]:
|
||||||
|
getting_up[i] = not p.behavior.execute("Get_Up") # True on completion
|
||||||
|
else:
|
||||||
|
p.behavior.execute("Basic_Kick", goal_dir)
|
||||||
|
|
||||||
|
script.batch_commit_and_send()
|
||||||
|
script.batch_receive()
|
@ -0,0 +1,271 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class Agent(Base_Agent):
|
||||||
|
def __init__(self, host:str, agent_port:int, monitor_port:int, unum:int,
|
||||||
|
team_name:str, enable_log, enable_draw, wait_for_server=True, is_fat_proxy=False) -> None:
|
||||||
|
|
||||||
|
# define robot type
|
||||||
|
robot_type = (0,1,1,1,2,3,3,3,4,4,4)[unum-1]
|
||||||
|
|
||||||
|
# Initialize base agent
|
||||||
|
# Args: Server IP, Agent Port, Monitor Port, Uniform No., Robot Type, Team Name, Enable Log, Enable Draw, play mode correction, Wait for Server, Hear Callback
|
||||||
|
super().__init__(host, agent_port, monitor_port, unum, robot_type, team_name, enable_log, enable_draw, True, wait_for_server, None)
|
||||||
|
|
||||||
|
self.enable_draw = enable_draw
|
||||||
|
self.state = 0 # 0-Normal, 1-Getting up, 2-Kicking
|
||||||
|
self.kick_direction = 0
|
||||||
|
self.kick_distance = 0
|
||||||
|
self.fat_proxy_cmd = "" if is_fat_proxy else None
|
||||||
|
self.fat_proxy_walk = np.zeros(3) # filtered walk parameters for fat proxy
|
||||||
|
|
||||||
|
self.init_pos = ([-14,0],[-9,-5],[-9,0],[-9,5],[-5,-5],[-5,0],[-5,5],[-1,-6],[-1,-2.5],[-1,2.5],[-1,6])[unum-1] # initial formation
|
||||||
|
|
||||||
|
|
||||||
|
def beam(self, avoid_center_circle=False):
|
||||||
|
r = self.world.robot
|
||||||
|
pos = self.init_pos[:] # copy position list
|
||||||
|
self.state = 0
|
||||||
|
|
||||||
|
# Avoid center circle by moving the player back
|
||||||
|
if avoid_center_circle and np.linalg.norm(self.init_pos) < 2.5:
|
||||||
|
pos[0] = -2.3
|
||||||
|
|
||||||
|
if np.linalg.norm(pos - r.loc_head_position[:2]) > 0.1 or self.behavior.is_ready("Get_Up"):
|
||||||
|
self.scom.commit_beam(pos, M.vector_angle((-pos[0],-pos[1]))) # beam to initial position, face coordinate (0,0)
|
||||||
|
else:
|
||||||
|
if self.fat_proxy_cmd is None: # normal behavior
|
||||||
|
self.behavior.execute("Zero_Bent_Knees_Auto_Head")
|
||||||
|
else: # fat proxy behavior
|
||||||
|
self.fat_proxy_cmd += "(proxy dash 0 0 0)"
|
||||||
|
self.fat_proxy_walk = np.zeros(3) # reset fat proxy walk
|
||||||
|
|
||||||
|
|
||||||
|
def move(self, target_2d=(0,0), orientation=None, is_orientation_absolute=True,
|
||||||
|
avoid_obstacles=True, priority_unums=[], is_aggressive=False, timeout=3000):
|
||||||
|
'''
|
||||||
|
Walk to target position
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
target_2d : array_like
|
||||||
|
2D target in absolute coordinates
|
||||||
|
orientation : float
|
||||||
|
absolute or relative orientation of torso, in degrees
|
||||||
|
set to None to go towards the target (is_orientation_absolute is ignored)
|
||||||
|
is_orientation_absolute : bool
|
||||||
|
True if orientation is relative to the field, False if relative to the robot's torso
|
||||||
|
avoid_obstacles : bool
|
||||||
|
True to avoid obstacles using path planning (maybe reduce timeout arg if this function is called multiple times per simulation cycle)
|
||||||
|
priority_unums : list
|
||||||
|
list of teammates to avoid (since their role is more important)
|
||||||
|
is_aggressive : bool
|
||||||
|
if True, safety margins are reduced for opponents
|
||||||
|
timeout : float
|
||||||
|
restrict path planning to a maximum duration (in microseconds)
|
||||||
|
'''
|
||||||
|
r = self.world.robot
|
||||||
|
|
||||||
|
if self.fat_proxy_cmd is not None: # fat proxy behavior
|
||||||
|
self.fat_proxy_move(target_2d, orientation, is_orientation_absolute) # ignore obstacles
|
||||||
|
return
|
||||||
|
|
||||||
|
if avoid_obstacles:
|
||||||
|
target_2d, _, distance_to_final_target = self.path_manager.get_path_to_target(
|
||||||
|
target_2d, priority_unums=priority_unums, is_aggressive=is_aggressive, timeout=timeout)
|
||||||
|
else:
|
||||||
|
distance_to_final_target = np.linalg.norm(target_2d - r.loc_head_position[:2])
|
||||||
|
|
||||||
|
self.behavior.execute("Walk", target_2d, True, orientation, is_orientation_absolute, distance_to_final_target) # Args: target, is_target_abs, ori, is_ori_abs, distance
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def kick(self, kick_direction=None, kick_distance=None, abort=False, enable_pass_command=False):
|
||||||
|
'''
|
||||||
|
Walk to ball and kick
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
kick_direction : float
|
||||||
|
kick direction, in degrees, relative to the field
|
||||||
|
kick_distance : float
|
||||||
|
kick distance in meters
|
||||||
|
abort : bool
|
||||||
|
True to abort.
|
||||||
|
The method returns True upon successful abortion, which is immediate while the robot is aligning itself.
|
||||||
|
However, if the abortion is requested during the kick, it is delayed until the kick is completed.
|
||||||
|
avoid_pass_command : bool
|
||||||
|
When False, the pass command will be used when at least one opponent is near the ball
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
finished : bool
|
||||||
|
Returns True if the behavior finished or was successfully aborted.
|
||||||
|
'''
|
||||||
|
|
||||||
|
if self.min_opponent_ball_dist < 1.45 and enable_pass_command:
|
||||||
|
self.scom.commit_pass_command()
|
||||||
|
|
||||||
|
self.kick_direction = self.kick_direction if kick_direction is None else kick_direction
|
||||||
|
self.kick_distance = self.kick_distance if kick_distance is None else kick_distance
|
||||||
|
|
||||||
|
if self.fat_proxy_cmd is None: # normal behavior
|
||||||
|
return self.behavior.execute("Basic_Kick", self.kick_direction, abort) # Basic_Kick has no kick distance control
|
||||||
|
else: # fat proxy behavior
|
||||||
|
return self.fat_proxy_kick()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def think_and_send(self):
|
||||||
|
w = self.world
|
||||||
|
r = self.world.robot
|
||||||
|
my_head_pos_2d = r.loc_head_position[:2]
|
||||||
|
my_ori = r.imu_torso_orientation
|
||||||
|
ball_2d = w.ball_abs_pos[:2]
|
||||||
|
ball_vec = ball_2d - my_head_pos_2d
|
||||||
|
ball_dir = M.vector_angle(ball_vec)
|
||||||
|
ball_dist = np.linalg.norm(ball_vec)
|
||||||
|
ball_sq_dist = ball_dist * ball_dist # for faster comparisons
|
||||||
|
ball_speed = np.linalg.norm(w.get_ball_abs_vel(6)[:2])
|
||||||
|
behavior = self.behavior
|
||||||
|
goal_dir = M.target_abs_angle(ball_2d,(15.05,0))
|
||||||
|
path_draw_options = self.path_manager.draw_options
|
||||||
|
PM = w.play_mode
|
||||||
|
PM_GROUP = w.play_mode_group
|
||||||
|
|
||||||
|
#--------------------------------------- 1. Preprocessing
|
||||||
|
|
||||||
|
slow_ball_pos = w.get_predicted_ball_pos(0.5) # predicted future 2D ball position when ball speed <= 0.5 m/s
|
||||||
|
|
||||||
|
# list of squared distances between teammates (including self) and slow ball (sq distance is set to 1000 in some conditions)
|
||||||
|
teammates_ball_sq_dist = [np.sum((p.state_abs_pos[:2] - slow_ball_pos) ** 2) # squared distance between teammate and ball
|
||||||
|
if p.state_last_update != 0 and (w.time_local_ms - p.state_last_update <= 360 or p.is_self) and not p.state_fallen
|
||||||
|
else 1000 # force large distance if teammate does not exist, or its state info is not recent (360 ms), or it has fallen
|
||||||
|
for p in w.teammates ]
|
||||||
|
|
||||||
|
# list of squared distances between opponents and slow ball (sq distance is set to 1000 in some conditions)
|
||||||
|
opponents_ball_sq_dist = [np.sum((p.state_abs_pos[:2] - slow_ball_pos) ** 2) # squared distance between teammate and ball
|
||||||
|
if p.state_last_update != 0 and w.time_local_ms - p.state_last_update <= 360 and not p.state_fallen
|
||||||
|
else 1000 # force large distance if opponent does not exist, or its state info is not recent (360 ms), or it has fallen
|
||||||
|
for p in w.opponents ]
|
||||||
|
|
||||||
|
min_teammate_ball_sq_dist = min(teammates_ball_sq_dist)
|
||||||
|
self.min_teammate_ball_dist = math.sqrt(min_teammate_ball_sq_dist) # distance between ball and closest teammate
|
||||||
|
self.min_opponent_ball_dist = math.sqrt(min(opponents_ball_sq_dist)) # distance between ball and closest opponent
|
||||||
|
|
||||||
|
active_player_unum = teammates_ball_sq_dist.index(min_teammate_ball_sq_dist) + 1
|
||||||
|
|
||||||
|
|
||||||
|
#--------------------------------------- 2. Decide action
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if PM == w.M_GAME_OVER:
|
||||||
|
pass
|
||||||
|
elif PM_GROUP == w.MG_ACTIVE_BEAM:
|
||||||
|
self.beam()
|
||||||
|
elif PM_GROUP == w.MG_PASSIVE_BEAM:
|
||||||
|
self.beam(True) # avoid center circle
|
||||||
|
elif self.state == 1 or (behavior.is_ready("Get_Up") and self.fat_proxy_cmd is None):
|
||||||
|
self.state = 0 if behavior.execute("Get_Up") else 1 # return to normal state if get up behavior has finished
|
||||||
|
elif PM == w.M_OUR_KICKOFF:
|
||||||
|
if r.unum == 9:
|
||||||
|
self.kick(120,3) # no need to change the state when PM is not Play On
|
||||||
|
else:
|
||||||
|
self.move(self.init_pos, orientation=ball_dir) # walk in place
|
||||||
|
elif PM == w.M_THEIR_KICKOFF:
|
||||||
|
self.move(self.init_pos, orientation=ball_dir) # walk in place
|
||||||
|
elif active_player_unum != r.unum: # I am not the active player
|
||||||
|
if r.unum == 1: # I am the goalkeeper
|
||||||
|
self.move(self.init_pos, orientation=ball_dir) # walk in place
|
||||||
|
else:
|
||||||
|
# compute basic formation position based on ball position
|
||||||
|
new_x = max(0.5,(ball_2d[0]+15)/15) * (self.init_pos[0]+15) - 15
|
||||||
|
if self.min_teammate_ball_dist < self.min_opponent_ball_dist:
|
||||||
|
new_x = min(new_x + 3.5, 13) # advance if team has possession
|
||||||
|
self.move((new_x,self.init_pos[1]), orientation=ball_dir, priority_unums=[active_player_unum])
|
||||||
|
|
||||||
|
else: # I am the active player
|
||||||
|
path_draw_options(enable_obstacles=True, enable_path=True, use_team_drawing_channel=True) # enable path drawings for active player (ignored if self.enable_draw is False)
|
||||||
|
enable_pass_command = (PM == w.M_PLAY_ON and ball_2d[0]<6)
|
||||||
|
|
||||||
|
if r.unum == 1 and PM_GROUP == w.MG_THEIR_KICK: # goalkeeper during their kick
|
||||||
|
self.move(self.init_pos, orientation=ball_dir) # walk in place
|
||||||
|
if PM == w.M_OUR_CORNER_KICK:
|
||||||
|
self.kick( -np.sign(ball_2d[1])*95, 5.5) # kick the ball into the space in front of the opponent's goal
|
||||||
|
# no need to change the state when PM is not Play On
|
||||||
|
elif self.min_opponent_ball_dist + 0.5 < self.min_teammate_ball_dist: # defend if opponent is considerably closer to the ball
|
||||||
|
if self.state == 2: # commit to kick while aborting
|
||||||
|
self.state = 0 if self.kick(abort=True) else 2
|
||||||
|
else: # move towards ball, but position myself between ball and our goal
|
||||||
|
self.move(slow_ball_pos + M.normalize_vec((-16,0) - slow_ball_pos) * 0.2, is_aggressive=True)
|
||||||
|
else:
|
||||||
|
self.state = 0 if self.kick(goal_dir,9,False,enable_pass_command) else 2
|
||||||
|
|
||||||
|
path_draw_options(enable_obstacles=False, enable_path=False) # disable path drawings
|
||||||
|
|
||||||
|
#--------------------------------------- 3. Broadcast
|
||||||
|
self.radio.broadcast()
|
||||||
|
|
||||||
|
#--------------------------------------- 4. Send to server
|
||||||
|
if self.fat_proxy_cmd is None: # normal behavior
|
||||||
|
self.scom.commit_and_send( r.get_command() )
|
||||||
|
else: # fat proxy behavior
|
||||||
|
self.scom.commit_and_send( self.fat_proxy_cmd.encode() )
|
||||||
|
self.fat_proxy_cmd = ""
|
||||||
|
|
||||||
|
#---------------------- annotations for debugging
|
||||||
|
if self.enable_draw:
|
||||||
|
d = w.draw
|
||||||
|
if active_player_unum == r.unum:
|
||||||
|
d.point(slow_ball_pos, 3, d.Color.pink, "status", False) # predicted future 2D ball position when ball speed <= 0.5 m/s
|
||||||
|
d.point(w.ball_2d_pred_pos[-1], 5, d.Color.pink, "status", False) # last ball prediction
|
||||||
|
d.annotation((*my_head_pos_2d, 0.6), "I've got it!" , d.Color.yellow, "status")
|
||||||
|
else:
|
||||||
|
d.clear("status")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#--------------------------------------- Fat proxy auxiliary methods
|
||||||
|
|
||||||
|
|
||||||
|
def fat_proxy_kick(self):
|
||||||
|
w = self.world
|
||||||
|
r = self.world.robot
|
||||||
|
ball_2d = w.ball_abs_pos[:2]
|
||||||
|
my_head_pos_2d = r.loc_head_position[:2]
|
||||||
|
|
||||||
|
if np.linalg.norm(ball_2d - my_head_pos_2d) < 0.25:
|
||||||
|
# fat proxy kick arguments: power [0,10]; relative horizontal angle [-180,180]; vertical angle [0,70]
|
||||||
|
self.fat_proxy_cmd += f"(proxy kick 10 {M.normalize_deg( self.kick_direction - r.imu_torso_orientation ):.2f} 20)"
|
||||||
|
self.fat_proxy_walk = np.zeros(3) # reset fat proxy walk
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.fat_proxy_move(ball_2d-(-0.1,0), None, True) # ignore obstacles
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def fat_proxy_move(self, target_2d, orientation, is_orientation_absolute):
|
||||||
|
r = self.world.robot
|
||||||
|
|
||||||
|
target_dist = np.linalg.norm(target_2d - r.loc_head_position[:2])
|
||||||
|
target_dir = M.target_rel_angle(r.loc_head_position[:2], r.imu_torso_orientation, target_2d)
|
||||||
|
|
||||||
|
if target_dist > 0.1 and abs(target_dir) < 8:
|
||||||
|
self.fat_proxy_cmd += (f"(proxy dash {100} {0} {0})")
|
||||||
|
return
|
||||||
|
|
||||||
|
if target_dist < 0.1:
|
||||||
|
if is_orientation_absolute:
|
||||||
|
orientation = M.normalize_deg( orientation - r.imu_torso_orientation )
|
||||||
|
target_dir = np.clip(orientation, -60, 60)
|
||||||
|
self.fat_proxy_cmd += (f"(proxy dash {0} {0} {target_dir:.1f})")
|
||||||
|
else:
|
||||||
|
self.fat_proxy_cmd += (f"(proxy dash {20} {0} {target_dir:.1f})")
|
@ -0,0 +1,88 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
import numpy as np
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
class Agent(Base_Agent):
|
||||||
|
def __init__(self, host:str, agent_port:int, monitor_port:int, unum:int,
|
||||||
|
team_name:str, enable_log, enable_draw, wait_for_server=True, is_fat_proxy=False) -> None:
|
||||||
|
|
||||||
|
# define robot type
|
||||||
|
robot_type = 0 if unum == 1 else 4 # assume the goalkeeper uses uniform number 1 and the kicker uses any other number
|
||||||
|
|
||||||
|
# Initialize base agent
|
||||||
|
# Args: Server IP, Agent Port, Monitor Port, Uniform No., Robot Type, Team Name, Enable Log, Enable Draw, play mode correction, Wait for Server, Hear Callback
|
||||||
|
super().__init__(host, agent_port, monitor_port, unum, robot_type, team_name, enable_log, enable_draw, False, wait_for_server, None)
|
||||||
|
|
||||||
|
self.enable_draw = enable_draw
|
||||||
|
self.state = 0 # 0-Normal, 1-Getting up, 2-Dive Left, 3-Dive Right, 4-Wait
|
||||||
|
|
||||||
|
self.kick_dir = 0 # kick direction
|
||||||
|
self.reset_kick = True # when True, a new random kick direction is generated
|
||||||
|
|
||||||
|
|
||||||
|
def think_and_send(self):
|
||||||
|
w = self.world
|
||||||
|
r = self.world.robot
|
||||||
|
my_head_pos_2d = r.loc_head_position[:2]
|
||||||
|
my_ori = r.imu_torso_orientation
|
||||||
|
ball_2d = w.ball_abs_pos[:2]
|
||||||
|
ball_vec = ball_2d - my_head_pos_2d
|
||||||
|
ball_dir = M.vector_angle(ball_vec)
|
||||||
|
ball_dist = np.linalg.norm(ball_vec)
|
||||||
|
ball_speed = np.linalg.norm(w.get_ball_abs_vel(6)[:2])
|
||||||
|
behavior = self.behavior
|
||||||
|
PM = w.play_mode
|
||||||
|
|
||||||
|
#--------------------------------------- 1. Decide action
|
||||||
|
|
||||||
|
if PM in [w.M_BEFORE_KICKOFF, w.M_THEIR_GOAL, w.M_OUR_GOAL]: # beam to initial position and wait
|
||||||
|
self.state = 0
|
||||||
|
self.reset_kick = True
|
||||||
|
pos = (-14,0) if r.unum == 1 else (4.9,0)
|
||||||
|
if np.linalg.norm(pos - r.loc_head_position[:2]) > 0.1 or behavior.is_ready("Get_Up"):
|
||||||
|
self.scom.commit_beam(pos, 0) # beam to initial position
|
||||||
|
else:
|
||||||
|
behavior.execute("Zero_Bent_Knees") # wait
|
||||||
|
elif self.state == 2: # dive left
|
||||||
|
self.state = 4 if behavior.execute("Dive_Left") else 2 # change state to wait after skill has finished
|
||||||
|
elif self.state == 3: # dive right
|
||||||
|
self.state = 4 if behavior.execute("Dive_Right") else 3 # change state to wait after skill has finished
|
||||||
|
elif self.state == 4: # wait (after diving or during opposing kick)
|
||||||
|
pass
|
||||||
|
elif self.state == 1 or behavior.is_ready("Get_Up"): # if getting up or fallen
|
||||||
|
self.state = 0 if behavior.execute("Get_Up") else 1 # return to normal state if get up behavior has finished
|
||||||
|
elif PM == w.M_OUR_KICKOFF and r.unum == 1 or PM == w.M_THEIR_KICKOFF and r.unum != 1:
|
||||||
|
self.state = 4 # wait until next beam
|
||||||
|
elif r.unum == 1: # goalkeeper
|
||||||
|
y_coordinate = np.clip(ball_2d[1], -1.1, 1.1)
|
||||||
|
behavior.execute("Walk", (-14,y_coordinate), True, 0, True, None) # Args: target, is_target_abs, ori, is_ori_abs, distance
|
||||||
|
if ball_2d[0] < -10:
|
||||||
|
self.state = 2 if ball_2d[1] > 0 else 3 # dive to defend
|
||||||
|
else: # kicker
|
||||||
|
if PM == w.M_OUR_KICKOFF and ball_2d[0] > 5: # check ball position to make sure I see it
|
||||||
|
if self.reset_kick:
|
||||||
|
self.kick_dir = random.choice([-7.5,7.5])
|
||||||
|
self.reset_kick = False
|
||||||
|
behavior.execute("Basic_Kick", self.kick_dir)
|
||||||
|
else:
|
||||||
|
behavior.execute("Zero_Bent_Knees") # wait
|
||||||
|
|
||||||
|
#--------------------------------------- 2. Broadcast
|
||||||
|
self.radio.broadcast()
|
||||||
|
|
||||||
|
#--------------------------------------- 3. Send to server
|
||||||
|
self.scom.commit_and_send( r.get_command() )
|
||||||
|
|
||||||
|
#---------------------- annotations for debugging
|
||||||
|
if self.enable_draw:
|
||||||
|
d = w.draw
|
||||||
|
if r.unum == 1:
|
||||||
|
d.annotation((*my_head_pos_2d, 0.8), "Goalkeeper" , d.Color.yellow, "status")
|
||||||
|
else:
|
||||||
|
d.annotation((*my_head_pos_2d, 0.8), "Kicker" , d.Color.yellow, "status")
|
||||||
|
if PM == w.M_OUR_KICKOFF: # draw arrow to indicate kick direction
|
||||||
|
d.arrow(ball_2d, ball_2d + 5*M.vector_from_angle(self.kick_dir), 0.4, 3, d.Color.cyan_light, "Target")
|
||||||
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
|||||||
|
from abc import abstractmethod
|
||||||
|
from behaviors.Behavior import Behavior
|
||||||
|
from communication.Radio import Radio
|
||||||
|
from communication.Server_Comm import Server_Comm
|
||||||
|
from communication.World_Parser import World_Parser
|
||||||
|
from logs.Logger import Logger
|
||||||
|
from math_ops.Inverse_Kinematics import Inverse_Kinematics
|
||||||
|
from world.commons.Path_Manager import Path_Manager
|
||||||
|
from world.World import World
|
||||||
|
|
||||||
|
class Base_Agent():
|
||||||
|
all_agents = []
|
||||||
|
|
||||||
|
def __init__(self, host:str, agent_port:int, monitor_port:int, unum:int, robot_type:int, team_name:str, enable_log:bool=True,
|
||||||
|
enable_draw:bool=True, apply_play_mode_correction:bool=True, wait_for_server:bool=True, hear_callback=None) -> None:
|
||||||
|
|
||||||
|
self.radio = None # hear_message may be called during Server_Comm instantiation
|
||||||
|
self.logger = Logger(enable_log, f"{team_name}_{unum}")
|
||||||
|
self.world = World(robot_type, team_name, unum, apply_play_mode_correction, enable_draw, self.logger, host)
|
||||||
|
self.world_parser = World_Parser(self.world, self.hear_message if hear_callback is None else hear_callback)
|
||||||
|
self.scom = Server_Comm(host,agent_port,monitor_port,unum,robot_type,team_name,self.world_parser,self.world,Base_Agent.all_agents,wait_for_server)
|
||||||
|
self.inv_kinematics = Inverse_Kinematics(self.world.robot)
|
||||||
|
self.behavior = Behavior(self)
|
||||||
|
self.path_manager = Path_Manager(self.world)
|
||||||
|
self.radio = Radio(self.world, self.scom.commit_announcement)
|
||||||
|
self.behavior.create_behaviors()
|
||||||
|
Base_Agent.all_agents.append(self)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def think_and_send(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def hear_message(self, msg:bytearray, direction, timestamp:float) -> None:
|
||||||
|
if direction != "self" and self.radio is not None:
|
||||||
|
self.radio.receive(msg)
|
||||||
|
|
||||||
|
def terminate(self):
|
||||||
|
# close shared monitor socket if this is the last agent on this thread
|
||||||
|
self.scom.close(close_monitor_socket=(len(Base_Agent.all_agents)==1))
|
||||||
|
Base_Agent.all_agents.remove(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def terminate_all():
|
||||||
|
for o in Base_Agent.all_agents:
|
||||||
|
o.scom.close(True) # close shared monitor socket, if it exists
|
||||||
|
Base_Agent.all_agents = []
|
||||||
|
|
@ -0,0 +1,183 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class Behavior():
|
||||||
|
|
||||||
|
def __init__(self, base_agent) -> None:
|
||||||
|
from agent.Base_Agent import Base_Agent # for type hinting
|
||||||
|
self.base_agent : Base_Agent = base_agent
|
||||||
|
self.world = self.base_agent.world
|
||||||
|
self.state_behavior_name = None
|
||||||
|
self.state_behavior_init_ms = 0
|
||||||
|
self.previous_behavior = None
|
||||||
|
self.previous_behavior_duration = None
|
||||||
|
|
||||||
|
#Initialize standard behaviors
|
||||||
|
from behaviors.Poses import Poses
|
||||||
|
from behaviors.Slot_Engine import Slot_Engine
|
||||||
|
from behaviors.Head import Head
|
||||||
|
|
||||||
|
self.poses = Poses(self.world)
|
||||||
|
self.slot_engine = Slot_Engine(self.world)
|
||||||
|
self.head = Head(self.world)
|
||||||
|
|
||||||
|
|
||||||
|
def create_behaviors(self):
|
||||||
|
'''
|
||||||
|
Behaviors dictionary:
|
||||||
|
creation: key: ( description, auto_head, lambda reset[,a,b,c,..]: self.execute(...), lambda: self.is_ready(...) )
|
||||||
|
usage: key: ( description, auto_head, execute_func(reset, *args), is_ready_func )
|
||||||
|
'''
|
||||||
|
self.behaviors = self.poses.get_behaviors_callbacks()
|
||||||
|
self.behaviors.update(self.slot_engine.get_behaviors_callbacks())
|
||||||
|
self.behaviors.update(self.get_custom_callbacks())
|
||||||
|
|
||||||
|
|
||||||
|
def get_custom_callbacks(self):
|
||||||
|
'''
|
||||||
|
Searching custom behaviors could be implemented automatically
|
||||||
|
However, for code distribution, loading code dynamically is not ideal (unless we load byte code or some other import solution)
|
||||||
|
Currently, adding custom behaviors is a manual process:
|
||||||
|
1. Add import statement below
|
||||||
|
2. Add class to 'classes' list
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Declaration of behaviors
|
||||||
|
from behaviors.custom.Basic_Kick.Basic_Kick import Basic_Kick
|
||||||
|
from behaviors.custom.Dribble.Dribble import Dribble
|
||||||
|
from behaviors.custom.Fall.Fall import Fall
|
||||||
|
from behaviors.custom.Get_Up.Get_Up import Get_Up
|
||||||
|
from behaviors.custom.Step.Step import Step
|
||||||
|
from behaviors.custom.Walk.Walk import Walk
|
||||||
|
classes = [Basic_Kick,Dribble,Fall,Get_Up,Step,Walk]
|
||||||
|
|
||||||
|
'''---- End of manual declarations ----'''
|
||||||
|
|
||||||
|
# Prepare callbacks
|
||||||
|
self.objects = {cls.__name__ : cls(self.base_agent) for cls in classes}
|
||||||
|
|
||||||
|
return {name: (o.description,o.auto_head,
|
||||||
|
lambda reset,*args,o=o: o.execute(reset,*args), lambda *args,o=o: o.is_ready(*args)) for name, o in self.objects.items()}
|
||||||
|
|
||||||
|
|
||||||
|
def get_custom_behavior_object(self, name):
|
||||||
|
''' Get unique object from class "name" ("name" must represent a custom behavior) '''
|
||||||
|
assert name in self.objects, f"There is no custom behavior called {name}"
|
||||||
|
return self.objects[name]
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_behaviors(self):
|
||||||
|
''' Get name and description of all behaviors '''
|
||||||
|
return [ key for key in self.behaviors ], [ val[0] for val in self.behaviors.values() ]
|
||||||
|
|
||||||
|
|
||||||
|
def get_current(self):
|
||||||
|
''' Get name and duration (in seconds) of current behavior '''
|
||||||
|
duration = (self.world.time_local_ms - self.state_behavior_init_ms) / 1000.0
|
||||||
|
return self.state_behavior_name, duration
|
||||||
|
|
||||||
|
|
||||||
|
def get_previous(self):
|
||||||
|
''' Get name and duration (in seconds) of previous behavior '''
|
||||||
|
return self.previous_behavior, self.previous_behavior_duration
|
||||||
|
|
||||||
|
|
||||||
|
def force_reset(self):
|
||||||
|
''' Force reset next executed behavior '''
|
||||||
|
self.state_behavior_name = None
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, name, *args) -> bool:
|
||||||
|
'''
|
||||||
|
Execute one step of behavior `name` with arguments `*args`
|
||||||
|
- Automatically resets behavior on first call
|
||||||
|
- Call get_current() to get the current behavior (and its duration)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
finished : bool
|
||||||
|
True if behavior has finished
|
||||||
|
'''
|
||||||
|
|
||||||
|
assert name in self.behaviors, f"Behavior {name} does not exist!"
|
||||||
|
|
||||||
|
# Check if transitioning from other behavior
|
||||||
|
reset = bool(self.state_behavior_name != name)
|
||||||
|
if reset:
|
||||||
|
if self.state_behavior_name is not None:
|
||||||
|
self.previous_behavior = self.state_behavior_name # Previous behavior was interrupted (did not finish)
|
||||||
|
self.previous_behavior_duration = (self.world.time_local_ms - self.state_behavior_init_ms) / 1000.0
|
||||||
|
self.state_behavior_name = name
|
||||||
|
self.state_behavior_init_ms = self.world.time_local_ms
|
||||||
|
|
||||||
|
# Control head orientation if behavior allows it
|
||||||
|
if self.behaviors[name][1]:
|
||||||
|
self.head.execute()
|
||||||
|
|
||||||
|
# Execute behavior
|
||||||
|
if not self.behaviors[name][2](reset,*args):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# The behavior has finished
|
||||||
|
self.previous_behavior = self.state_behavior_name # Store current behavior name
|
||||||
|
self.state_behavior_name = None
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def execute_sub_behavior(self, name, reset, *args):
|
||||||
|
'''
|
||||||
|
Execute one step of behavior `name` with arguments `*args`
|
||||||
|
Useful for custom behaviors that call other behaviors
|
||||||
|
- Behavior reset is performed manually
|
||||||
|
- Calling get_current() will return the main behavior (not the sub behavior)
|
||||||
|
- Poses ignore the reset argument
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
finished : bool
|
||||||
|
True if behavior has finished
|
||||||
|
'''
|
||||||
|
|
||||||
|
assert name in self.behaviors, f"Behavior {name} does not exist!"
|
||||||
|
|
||||||
|
# Control head orientation if behavior allows it
|
||||||
|
if self.behaviors[name][1]:
|
||||||
|
self.head.execute()
|
||||||
|
|
||||||
|
# Execute behavior
|
||||||
|
return self.behaviors[name][2](reset,*args)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_to_completion(self, name, *args):
|
||||||
|
'''
|
||||||
|
Execute steps and communicate with server until completion
|
||||||
|
- Slot behaviors indicate that the behavior has finished when sending the last command (which is promptly sent)
|
||||||
|
- Poses are finished when the server returns the desired robot state (so the last command is irrelevant)
|
||||||
|
- For custom behaviors, we assume the same logic, and so, the last command is ignored
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
- Before exiting, the `Robot.joints_target_speed` array is reset to avoid polluting the next command
|
||||||
|
- For poses and custom behaviors that indicate a finished behavior on the 1st call, nothing is committed or sent
|
||||||
|
- Warning: this function may get stuck in an infinite loop if the behavior never ends
|
||||||
|
'''
|
||||||
|
|
||||||
|
r = self.world.robot
|
||||||
|
skip_last = name not in self.slot_engine.behaviors
|
||||||
|
|
||||||
|
while True:
|
||||||
|
done = self.execute(name, *args)
|
||||||
|
if done and skip_last: break # Exit here if last command is irrelevant
|
||||||
|
self.base_agent.scom.commit_and_send( r.get_command() )
|
||||||
|
self.base_agent.scom.receive()
|
||||||
|
if done: break # Exit here if last command is part of the behavior
|
||||||
|
|
||||||
|
# reset to avoid polluting the next command
|
||||||
|
r.joints_target_speed = np.zeros_like(r.joints_target_speed)
|
||||||
|
|
||||||
|
|
||||||
|
def is_ready(self, name, *args) -> bool:
|
||||||
|
''' Checks if behavior is ready to start under current game/robot conditions '''
|
||||||
|
|
||||||
|
assert name in self.behaviors, f"Behavior {name} does not exist!"
|
||||||
|
return self.behaviors[name][3](*args)
|
@ -0,0 +1,105 @@
|
|||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
from world.World import World
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class Head():
|
||||||
|
FIELD_FLAGS = World.FLAGS_CORNERS_POS + World.FLAGS_POSTS_POS
|
||||||
|
HEAD_PITCH = -35
|
||||||
|
|
||||||
|
def __init__(self, world : World) -> None:
|
||||||
|
self.world = world
|
||||||
|
self.look_left = True
|
||||||
|
self.state = 0
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
'''
|
||||||
|
Try to compute best head orientation if possible, otherwise look around
|
||||||
|
|
||||||
|
state:
|
||||||
|
0 - Adjust position - ball is in FOV and robot can self-locate
|
||||||
|
1..TIMEOUT-1 - Guided search - attempt to use recent visual/radio information to guide the search
|
||||||
|
TIMEOUT - Random search - look around (default mode after guided search fails by timeout)
|
||||||
|
'''
|
||||||
|
TIMEOUT = 30
|
||||||
|
w = self.world
|
||||||
|
r = w.robot
|
||||||
|
can_self_locate = r.loc_last_update > w.time_local_ms - w.VISUALSTEP_MS
|
||||||
|
|
||||||
|
#--------------------------------------- A. Ball is in FOV and robot can self-locate
|
||||||
|
|
||||||
|
if w.ball_last_seen > w.time_local_ms - w.VISUALSTEP_MS: # ball is in FOV
|
||||||
|
if can_self_locate:
|
||||||
|
best_dir = self.compute_best_direction(can_self_locate, use_ball_from_vision=True)
|
||||||
|
self.state = 0
|
||||||
|
elif self.state < TIMEOUT:
|
||||||
|
#--------------------------------------- B. Ball is in FOV but robot cannot currently self-locate
|
||||||
|
best_dir = self.compute_best_direction(can_self_locate, use_ball_from_vision=True)
|
||||||
|
self.state += 1 # change to guided search and increment time
|
||||||
|
elif self.state < TIMEOUT:
|
||||||
|
#--------------------------------------- C. Ball is not in FOV
|
||||||
|
best_dir = self.compute_best_direction(can_self_locate)
|
||||||
|
self.state += 1 # change to guided search and increment time
|
||||||
|
|
||||||
|
|
||||||
|
if self.state == TIMEOUT: # Random search
|
||||||
|
|
||||||
|
if w.ball_last_seen > w.time_local_ms - w.VISUALSTEP_MS: # Ball is in FOV (search 45 deg to both sides of the ball)
|
||||||
|
ball_dir = M.vector_angle(w.ball_rel_torso_cart_pos[:2])
|
||||||
|
targ = np.clip(ball_dir + (45 if self.look_left else -45), -119, 119)
|
||||||
|
else: # Ball is not in FOV (search 119 deg to both sides)
|
||||||
|
targ = 119 if self.look_left else -119
|
||||||
|
|
||||||
|
if r.set_joints_target_position_direct([0,1], np.array([targ,Head.HEAD_PITCH]), False) <= 0:
|
||||||
|
self.look_left = not self.look_left
|
||||||
|
|
||||||
|
else: # Adjust position or guided search
|
||||||
|
r.set_joints_target_position_direct([0,1], np.array([best_dir,Head.HEAD_PITCH]), False)
|
||||||
|
|
||||||
|
|
||||||
|
def compute_best_direction(self, can_self_locate, use_ball_from_vision=False):
|
||||||
|
FOV_MARGIN = 15 # safety margin, avoid margin horizontally
|
||||||
|
SAFE_RANGE = 120 - FOV_MARGIN*2
|
||||||
|
HALF_RANGE = SAFE_RANGE / 2
|
||||||
|
|
||||||
|
w = self.world
|
||||||
|
r = w.robot
|
||||||
|
|
||||||
|
if use_ball_from_vision:
|
||||||
|
ball_2d_dist = np.linalg.norm(w.ball_rel_torso_cart_pos[:2])
|
||||||
|
else:
|
||||||
|
ball_2d_dist = np.linalg.norm(w.ball_abs_pos[:2]-r.loc_head_position[:2])
|
||||||
|
|
||||||
|
if ball_2d_dist > 0.12:
|
||||||
|
if use_ball_from_vision:
|
||||||
|
ball_dir = M.vector_angle(w.ball_rel_torso_cart_pos[:2])
|
||||||
|
else:
|
||||||
|
ball_dir = M.target_rel_angle(r.loc_head_position, r.imu_torso_orientation, w.ball_abs_pos)
|
||||||
|
else: # ball is very close to robot
|
||||||
|
ball_dir = 0
|
||||||
|
|
||||||
|
flags_diff = dict()
|
||||||
|
|
||||||
|
# iterate flags
|
||||||
|
for f in Head.FIELD_FLAGS:
|
||||||
|
flag_dir = M.target_rel_angle(r.loc_head_position, r.imu_torso_orientation, f)
|
||||||
|
diff = M.normalize_deg( flag_dir - ball_dir )
|
||||||
|
if abs(diff) < HALF_RANGE and can_self_locate:
|
||||||
|
return ball_dir # return ball direction if robot can self-locate
|
||||||
|
flags_diff[f] = diff
|
||||||
|
|
||||||
|
|
||||||
|
closest_flag = min( flags_diff, key=lambda k: abs(flags_diff[k]) )
|
||||||
|
closest_diff = flags_diff[closest_flag]
|
||||||
|
|
||||||
|
if can_self_locate: # at this point, if it can self-locate, then abs(closest_diff) > HALF_RANGE
|
||||||
|
# return position that centers the ball as much as possible in the FOV, including the nearest flag if possible
|
||||||
|
final_diff = min( abs(closest_diff) - HALF_RANGE, SAFE_RANGE ) * np.sign(closest_diff)
|
||||||
|
else:
|
||||||
|
# position that centers the flag as much as possible, until it is seen, while keeping the ball in the FOV
|
||||||
|
final_diff = np.clip( closest_diff, -SAFE_RANGE, SAFE_RANGE )
|
||||||
|
# saturate instead of normalizing angle to avoid a complete neck rotation
|
||||||
|
return np.clip(ball_dir + final_diff, -119, 119)
|
||||||
|
|
||||||
|
|
||||||
|
return M.normalize_deg( ball_dir + final_diff )
|
@ -0,0 +1,97 @@
|
|||||||
|
'''
|
||||||
|
Pose - angles in degrees for the specified joints
|
||||||
|
Note: toes positions are ignored by robots that have no toes
|
||||||
|
|
||||||
|
Poses may control all joints or just a subgroup defined by the "indices" variable
|
||||||
|
'''
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from world.World import World
|
||||||
|
|
||||||
|
|
||||||
|
class Poses():
|
||||||
|
def __init__(self, world : World) -> None:
|
||||||
|
self.world = world
|
||||||
|
self.tolerance = 0.05 # angle error tolerance to consider that behavior is finished
|
||||||
|
|
||||||
|
'''
|
||||||
|
Instruction to add new pose:
|
||||||
|
1. add new entry to the following dictionary, using a unique behavior name
|
||||||
|
2. that's it
|
||||||
|
'''
|
||||||
|
self.poses = {
|
||||||
|
"Zero":(
|
||||||
|
"Neutral pose, including head", # description
|
||||||
|
False, # disable automatic head orientation
|
||||||
|
np.array([0,1,2,3,4,5,6,7,8,9,10,11,12,13, 14, 15,16,17,18,19,20,21,22,23]), # indices
|
||||||
|
np.array([0,0,0,0,0,0,0,0,0,0, 0, 0, 0, 0,-90,-90, 0, 0,90,90, 0, 0, 0, 0]) # values
|
||||||
|
),
|
||||||
|
"Zero_Legacy":(
|
||||||
|
"Neutral pose, including head, elbows cause collision (legacy)", # description
|
||||||
|
False, # disable automatic head orientation
|
||||||
|
np.array([0,1,2,3,4,5,6,7,8,9,10,11,12,13, 14, 15,16,17,18,19,20,21,22,23]), # indices
|
||||||
|
np.array([0,0,0,0,0,0,0,0,0,0, 0, 0, 0, 0,-90,-90, 0, 0, 0, 0, 0, 0, 0, 0]) # values
|
||||||
|
),
|
||||||
|
"Zero_Bent_Knees":(
|
||||||
|
"Neutral pose, including head, bent knees", # description
|
||||||
|
False, # disable automatic head orientation
|
||||||
|
np.array([0,1,2,3,4,5,6, 7, 8, 9,10,11,12,13, 14, 15,16,17,18,19,20,21,22,23]), # indices
|
||||||
|
np.array([0,0,0,0,0,0,30,30,-60,-60,30,30, 0, 0,-90,-90, 0, 0,90,90, 0, 0, 0, 0]) # values
|
||||||
|
),
|
||||||
|
"Zero_Bent_Knees_Auto_Head":(
|
||||||
|
"Neutral pose, automatic head, bent knees", # description
|
||||||
|
True, # enable automatic head orientation
|
||||||
|
np.array([2,3,4,5,6, 7, 8, 9,10,11,12,13, 14, 15,16,17,18,19,20,21,22,23]), # indices
|
||||||
|
np.array([0,0,0,0,30,30,-60,-60,30,30, 0, 0,-90,-90, 0, 0,90,90, 0, 0, 0, 0]) # values
|
||||||
|
),
|
||||||
|
"Fall_Back":(
|
||||||
|
"Incline feet to fall back", # description
|
||||||
|
True, # enable automatic head orientation
|
||||||
|
np.array([ 10, 11]), # indices
|
||||||
|
np.array([-20,-20]) # values
|
||||||
|
),
|
||||||
|
"Fall_Front":(
|
||||||
|
"Incline feet to fall forward", # description
|
||||||
|
True, # enable automatic head orientation
|
||||||
|
np.array([10,11]), # indices
|
||||||
|
np.array([45,45]) # values
|
||||||
|
),
|
||||||
|
"Fall_Left":(
|
||||||
|
"Incline legs to fall to left", # description
|
||||||
|
True, # enable automatic head orientation
|
||||||
|
np.array([ 4, 5]), # indices
|
||||||
|
np.array([-20,20]) # values
|
||||||
|
),
|
||||||
|
"Fall_Right":(
|
||||||
|
"Incline legs to fall to right", # description
|
||||||
|
True, # enable automatic head orientation
|
||||||
|
np.array([ 4, 5]), # indices
|
||||||
|
np.array([20,-20]) # values
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove toes if not robot 4
|
||||||
|
if world.robot.type != 4:
|
||||||
|
for key, val in self.poses.items():
|
||||||
|
idxs = np.where(val[2] >= 22)[0] # search for joint 22 & 23
|
||||||
|
if len(idxs) > 0:
|
||||||
|
self.poses[key] = (val[0], val[1], np.delete(val[2],idxs), np.delete(val[3],idxs)) # remove those joints
|
||||||
|
|
||||||
|
|
||||||
|
def get_behaviors_callbacks(self):
|
||||||
|
'''
|
||||||
|
Returns callbacks for each pose behavior (used internally)
|
||||||
|
|
||||||
|
Implementation note:
|
||||||
|
--------------------
|
||||||
|
Using dummy default parameters because lambda expression will remember the scope and var name.
|
||||||
|
In the loop, the scope does not change, nor does the var name.
|
||||||
|
However, default parameters are evaluated when the lambda is defined.
|
||||||
|
'''
|
||||||
|
return {key: (val[0], val[1], lambda reset, key=key: self.execute(key), lambda: True) for key, val in self.poses.items()}
|
||||||
|
|
||||||
|
def execute(self,name) -> bool:
|
||||||
|
_, _, indices, values = self.poses[name]
|
||||||
|
remaining_steps = self.world.robot.set_joints_target_position_direct(indices,values,True,tolerance=self.tolerance)
|
||||||
|
return bool(remaining_steps == -1)
|
||||||
|
|
@ -0,0 +1,110 @@
|
|||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
from os import listdir
|
||||||
|
from os.path import isfile, join
|
||||||
|
from world.World import World
|
||||||
|
import numpy as np
|
||||||
|
import xml.etree.ElementTree as xmlp
|
||||||
|
|
||||||
|
class Slot_Engine():
|
||||||
|
|
||||||
|
def __init__(self, world : World) -> None:
|
||||||
|
self.world = world
|
||||||
|
self.state_slot_number = 0
|
||||||
|
self.state_slot_start_time = 0
|
||||||
|
self.state_slot_start_angles = None
|
||||||
|
self.state_init_zero = True
|
||||||
|
|
||||||
|
# ------------- Parse slot behaviors
|
||||||
|
|
||||||
|
dir = M.get_active_directory("/behaviors/slot/")
|
||||||
|
|
||||||
|
common_dir = f"{dir}common/"
|
||||||
|
files = [(f,join(common_dir, f)) for f in listdir(common_dir) if isfile(join(common_dir, f)) and f.endswith(".xml")]
|
||||||
|
robot_dir = f"{dir}r{world.robot.type}"
|
||||||
|
files += [(f,join(robot_dir, f)) for f in listdir(robot_dir) if isfile(join(robot_dir, f)) and f.endswith(".xml")]
|
||||||
|
|
||||||
|
self.behaviors = dict()
|
||||||
|
self.descriptions = dict()
|
||||||
|
self.auto_head_flags = dict()
|
||||||
|
|
||||||
|
for fname, file in files:
|
||||||
|
robot_xml_root = xmlp.parse(file).getroot()
|
||||||
|
slots = []
|
||||||
|
bname = fname[:-4] # remove extension ".xml"
|
||||||
|
|
||||||
|
for xml_slot in robot_xml_root:
|
||||||
|
assert xml_slot.tag == 'slot', f"Unexpected XML element in slot behavior {fname}: '{xml_slot.tag}'"
|
||||||
|
indices, angles = [],[]
|
||||||
|
|
||||||
|
for action in xml_slot:
|
||||||
|
indices.append( int(action.attrib['id']) )
|
||||||
|
angles.append( float(action.attrib['angle']) )
|
||||||
|
|
||||||
|
delta_ms = float(xml_slot.attrib['delta']) * 1000
|
||||||
|
assert delta_ms > 0, f"Invalid delta <=0 found in Slot Behavior {fname}"
|
||||||
|
slots.append((delta_ms, indices, angles))
|
||||||
|
|
||||||
|
assert bname not in self.behaviors, f"Found at least 2 slot behaviors with same name: {fname}"
|
||||||
|
|
||||||
|
self.descriptions[bname] = robot_xml_root.attrib["description"] if "description" in robot_xml_root.attrib else bname
|
||||||
|
self.auto_head_flags[bname] = (robot_xml_root.attrib["auto_head"] == "1")
|
||||||
|
self.behaviors[bname] = slots
|
||||||
|
|
||||||
|
|
||||||
|
def get_behaviors_callbacks(self):
|
||||||
|
'''
|
||||||
|
Returns callbacks for each slot behavior (used internally)
|
||||||
|
|
||||||
|
Implementation note:
|
||||||
|
--------------------
|
||||||
|
Using dummy default parameters because lambda expression will remember the scope and var name.
|
||||||
|
In the loop, the scope does not change, nor does the var name.
|
||||||
|
However, default parameters are evaluated when the lambda is defined.
|
||||||
|
'''
|
||||||
|
return {key: (self.descriptions[key],self.auto_head_flags[key],
|
||||||
|
lambda reset,key=key: self.execute(key,reset), lambda key=key: self.is_ready(key)) for key in self.behaviors}
|
||||||
|
|
||||||
|
|
||||||
|
def is_ready(self,name) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def reset(self, name):
|
||||||
|
''' Initialize/Reset slot behavior '''
|
||||||
|
|
||||||
|
self.state_slot_number = 0
|
||||||
|
self.state_slot_start_time_ms = self.world.time_local_ms
|
||||||
|
self.state_slot_start_angles = np.copy(self.world.robot.joints_position)
|
||||||
|
assert name in self.behaviors, f"Requested slot behavior does not exist: {name}"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self,name,reset) -> bool:
|
||||||
|
''' Execute one step '''
|
||||||
|
|
||||||
|
if reset: self.reset(name)
|
||||||
|
|
||||||
|
elapsed_ms = self.world.time_local_ms - self.state_slot_start_time_ms
|
||||||
|
delta_ms, indices, angles = self.behaviors[name][self.state_slot_number]
|
||||||
|
|
||||||
|
# Check slot progression
|
||||||
|
if elapsed_ms >= delta_ms:
|
||||||
|
self.state_slot_start_angles[indices] = angles #update start angles based on last target
|
||||||
|
|
||||||
|
# Prevent 2 rare scenarios:
|
||||||
|
# 1 - this function is called after the behavior is finished & reset==False
|
||||||
|
# 2 - we are in the last slot, syncmode is not active, and we lost the last step
|
||||||
|
if self.state_slot_number+1 == len(self.behaviors[name]):
|
||||||
|
return True # So, the return indicates a finished behavior until a reset is sent via the arguments
|
||||||
|
|
||||||
|
self.state_slot_number += 1
|
||||||
|
elapsed_ms = 0
|
||||||
|
self.state_slot_start_time_ms = self.world.time_local_ms
|
||||||
|
delta_ms, indices, angles = self.behaviors[name][self.state_slot_number]
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
progress = (elapsed_ms+20) / delta_ms
|
||||||
|
target = (angles - self.state_slot_start_angles[indices]) * progress + self.state_slot_start_angles[indices]
|
||||||
|
self.world.robot.set_joints_target_position_direct(indices,target,False)
|
||||||
|
|
||||||
|
# Return True if finished (this is the last step)
|
||||||
|
return bool(elapsed_ms+20 >= delta_ms and self.state_slot_number + 1 == len(self.behaviors[name])) # true if next step (now+20ms) is out of bounds
|
@ -0,0 +1,74 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from behaviors.custom.Step.Step_Generator import Step_Generator
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
|
||||||
|
|
||||||
|
class Basic_Kick():
|
||||||
|
|
||||||
|
def __init__(self, base_agent : Base_Agent) -> None:
|
||||||
|
self.behavior = base_agent.behavior
|
||||||
|
self.path_manager = base_agent.path_manager
|
||||||
|
self.world = base_agent.world
|
||||||
|
self.description = "Walk to ball and perform a basic kick"
|
||||||
|
self.auto_head = True
|
||||||
|
|
||||||
|
r_type = self.world.robot.type
|
||||||
|
self.bias_dir = [22,29,26,29,22][self.world.robot.type]
|
||||||
|
self.ball_x_limits = ((0.19,0.215), (0.2,0.22), (0.19,0.22), (0.2,0.215), (0.2,0.215))[r_type]
|
||||||
|
self.ball_y_limits = ((-0.115,-0.1), (-0.125,-0.095), (-0.12,-0.1), (-0.13,-0.105), (-0.09,-0.06))[r_type]
|
||||||
|
self.ball_x_center = (self.ball_x_limits[0] + self.ball_x_limits[1])/2
|
||||||
|
self.ball_y_center = (self.ball_y_limits[0] + self.ball_y_limits[1])/2
|
||||||
|
|
||||||
|
def execute(self,reset, direction, abort=False) -> bool: # You can add more arguments
|
||||||
|
'''
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
direction : float
|
||||||
|
kick direction relative to field, in degrees
|
||||||
|
abort : bool
|
||||||
|
True to abort.
|
||||||
|
The method returns True upon successful abortion, which is immediate while the robot is aligning itself.
|
||||||
|
However, if the abortion is requested during the kick, it is delayed until the kick is completed.
|
||||||
|
'''
|
||||||
|
|
||||||
|
w = self.world
|
||||||
|
r = self.world.robot
|
||||||
|
b = w.ball_rel_torso_cart_pos
|
||||||
|
t = w.time_local_ms
|
||||||
|
gait : Step_Generator = self.behavior.get_custom_behavior_object("Walk").env.step_generator
|
||||||
|
|
||||||
|
if reset:
|
||||||
|
self.phase = 0
|
||||||
|
self.reset_time = t
|
||||||
|
|
||||||
|
if self.phase == 0:
|
||||||
|
biased_dir = M.normalize_deg(direction + self.bias_dir) # add bias to rectify direction
|
||||||
|
ang_diff = abs(M.normalize_deg( biased_dir - r.loc_torso_orientation )) # the reset was learned with loc, not IMU
|
||||||
|
|
||||||
|
next_pos, next_ori, dist_to_final_target = self.path_manager.get_path_to_ball(
|
||||||
|
x_ori=biased_dir, x_dev=-self.ball_x_center, y_dev=-self.ball_y_center, torso_ori=biased_dir)
|
||||||
|
|
||||||
|
if (w.ball_last_seen > t - w.VISUALSTEP_MS and ang_diff < 5 and # ball is visible & aligned
|
||||||
|
self.ball_x_limits[0] < b[0] < self.ball_x_limits[1] and # ball is in kick area (x)
|
||||||
|
self.ball_y_limits[0] < b[1] < self.ball_y_limits[1] and # ball is in kick area (y)
|
||||||
|
t - w.ball_abs_pos_last_update < 100 and # ball absolute location is recent
|
||||||
|
dist_to_final_target < 0.03 and # if absolute ball position is updated
|
||||||
|
not gait.state_is_left_active and gait.state_current_ts == 2 and # walk gait phase is adequate
|
||||||
|
t - self.reset_time > 500): # to avoid kicking immediately without preparation & stability
|
||||||
|
|
||||||
|
self.phase += 1
|
||||||
|
|
||||||
|
return self.behavior.execute_sub_behavior("Kick_Motion", True)
|
||||||
|
else:
|
||||||
|
dist = max(0.07, dist_to_final_target)
|
||||||
|
reset_walk = reset and self.behavior.previous_behavior != "Walk" # reset walk if it wasn't the previous behavior
|
||||||
|
self.behavior.execute_sub_behavior("Walk", reset_walk, next_pos, True, next_ori, True, dist) # target, is_target_abs, ori, is_ori_abs, distance
|
||||||
|
return abort # abort only if self.phase == 0
|
||||||
|
|
||||||
|
else: # define kick parameters and execute
|
||||||
|
return self.behavior.execute_sub_behavior("Kick_Motion", False)
|
||||||
|
|
||||||
|
|
||||||
|
def is_ready(self) -> any: # You can add more arguments
|
||||||
|
''' Returns True if this behavior is ready to start/continue under current game/robot conditions '''
|
||||||
|
return True
|
@ -0,0 +1,207 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from behaviors.custom.Dribble.Env import Env
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
from math_ops.Neural_Network import run_mlp
|
||||||
|
import numpy as np
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
|
||||||
|
class Dribble():
|
||||||
|
|
||||||
|
def __init__(self, base_agent : Base_Agent) -> None:
|
||||||
|
self.behavior = base_agent.behavior
|
||||||
|
self.path_manager = base_agent.path_manager
|
||||||
|
self.world = base_agent.world
|
||||||
|
self.description = "RL dribble"
|
||||||
|
self.auto_head = True
|
||||||
|
self.env = Env(base_agent, 0.9 if self.world.robot.type == 3 else 1.2)
|
||||||
|
|
||||||
|
with open(M.get_active_directory([
|
||||||
|
"/behaviors/custom/Dribble/dribble_R0.pkl",
|
||||||
|
"/behaviors/custom/Dribble/dribble_R1.pkl",
|
||||||
|
"/behaviors/custom/Dribble/dribble_R2.pkl",
|
||||||
|
"/behaviors/custom/Dribble/dribble_R3.pkl",
|
||||||
|
"/behaviors/custom/Dribble/dribble_R4.pkl"
|
||||||
|
][self.world.robot.type]), 'rb') as f:
|
||||||
|
self.model = pickle.load(f)
|
||||||
|
|
||||||
|
def define_approach_orientation(self):
|
||||||
|
|
||||||
|
w = self.world
|
||||||
|
b = w.ball_abs_pos[:2]
|
||||||
|
me = w.robot.loc_head_position[:2]
|
||||||
|
|
||||||
|
self.approach_orientation = None
|
||||||
|
|
||||||
|
MARGIN = 0.8 # safety margin (if ball is near the field limits by this amount, the approach orientation is considered)
|
||||||
|
M90 = 90/MARGIN # auxiliary variable
|
||||||
|
DEV = 25 # when standing on top of sidelines or endlines, the approach direction deviates from that line by this amount
|
||||||
|
MDEV = (90+DEV)/MARGIN # auxiliary variable
|
||||||
|
|
||||||
|
a1 = -180 # angle range start (counterclockwise rotation)
|
||||||
|
a2 = 180 # angle range end (counterclockwise rotation)
|
||||||
|
|
||||||
|
if b[1] < -10 + MARGIN:
|
||||||
|
if b[0] < -15 + MARGIN:
|
||||||
|
a1 = DEV - M90 * (b[1]+10)
|
||||||
|
a2 = 90 - DEV + M90 * (b[0]+15)
|
||||||
|
elif b[0] > 15 - MARGIN:
|
||||||
|
a1 = 90 + DEV - M90 * (15-b[0])
|
||||||
|
a2 = 180 - DEV + M90 * (b[1]+10)
|
||||||
|
else:
|
||||||
|
a1 = DEV - MDEV * (b[1]+10)
|
||||||
|
a2 = 180 - DEV + MDEV * (b[1]+10)
|
||||||
|
elif b[1] > 10 - MARGIN:
|
||||||
|
if b[0] < -15 + MARGIN:
|
||||||
|
a1 = -90 + DEV - M90 * (b[0]+15)
|
||||||
|
a2 = -DEV + M90 * (10-b[1])
|
||||||
|
elif b[0] > 15 - MARGIN:
|
||||||
|
a1 = 180 + DEV - M90 * (10-b[1])
|
||||||
|
a2 = 270 - DEV + M90 * (15-b[0])
|
||||||
|
else:
|
||||||
|
a1 = -180 + DEV - MDEV * (10-b[1])
|
||||||
|
a2 = -DEV + MDEV * (10-b[1])
|
||||||
|
elif b[0] < -15 + MARGIN:
|
||||||
|
a1 = -90 + DEV - MDEV * (b[0]+15)
|
||||||
|
a2 = 90 - DEV + MDEV * (b[0]+15)
|
||||||
|
elif b[0] > 15 - MARGIN and abs(b[1]) > 1.2:
|
||||||
|
a1 = 90 + DEV - MDEV * (15-b[0])
|
||||||
|
a2 = 270 - DEV + MDEV * (15-b[0])
|
||||||
|
|
||||||
|
|
||||||
|
cad = M.vector_angle(b - me) # current approach direction
|
||||||
|
|
||||||
|
a1 = M.normalize_deg(a1)
|
||||||
|
a2 = M.normalize_deg(a2)
|
||||||
|
|
||||||
|
if a1<a2:
|
||||||
|
if a1 <= cad <= a2:
|
||||||
|
return # current approach orientation is within accepted range
|
||||||
|
else:
|
||||||
|
if a1 <= cad or cad <= a2:
|
||||||
|
return # current approach orientation is within accepted range
|
||||||
|
|
||||||
|
a1_diff = abs(M.normalize_deg(a1 - cad))
|
||||||
|
a2_diff = abs(M.normalize_deg(a2 - cad))
|
||||||
|
|
||||||
|
self.approach_orientation = a1 if a1_diff < a2_diff else a2 # fixed normalized orientation
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, reset, orientation, is_orientation_absolute, speed=1, stop=False):
|
||||||
|
'''
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
orientation : float
|
||||||
|
absolute or relative orientation of torso (relative to imu_torso_orientation), in degrees
|
||||||
|
set to None to dribble towards the opponent's goal (is_orientation_absolute is ignored)
|
||||||
|
is_orientation_absolute : bool
|
||||||
|
True if orientation is relative to the field, False if relative to the robot's torso
|
||||||
|
speed : float
|
||||||
|
speed from 0 to 1 (scale is not linear)
|
||||||
|
stop : bool
|
||||||
|
return True immediately if walking, wind down if dribbling, and return True when possible
|
||||||
|
'''
|
||||||
|
w = self.world
|
||||||
|
r = self.world.robot
|
||||||
|
me = r.loc_head_position[:2]
|
||||||
|
b = w.ball_abs_pos[:2]
|
||||||
|
b_rel = w.ball_rel_torso_cart_pos[:2]
|
||||||
|
b_dist = np.linalg.norm(b-me)
|
||||||
|
behavior = self.behavior
|
||||||
|
reset_dribble = False
|
||||||
|
lost_ball = (w.ball_last_seen <= w.time_local_ms - w.VISUALSTEP_MS) or np.linalg.norm(b_rel)>0.4
|
||||||
|
|
||||||
|
if reset:
|
||||||
|
self.phase = 0
|
||||||
|
if behavior.previous_behavior == "Push_RL" and 0<b_rel[0]<0.25 and abs(b_rel[1])<0.07:
|
||||||
|
self.phase = 1
|
||||||
|
reset_dribble = True
|
||||||
|
|
||||||
|
if self.phase == 0: # walk to ball
|
||||||
|
reset_walk = reset and behavior.previous_behavior != "Walk" and behavior.previous_behavior != "Push_RL" # reset walk if it wasn't the previous behavior
|
||||||
|
|
||||||
|
#------------------------ 1. Decide if a better approach orientation is needed (when ball is nearly out of bounds)
|
||||||
|
if reset or b_dist > 0.4: # stop defining orientation after getting near the ball to avoid noise
|
||||||
|
self.define_approach_orientation()
|
||||||
|
|
||||||
|
#------------------------ 2A. A better approach orientation is needed (ball is almost out of bounds)
|
||||||
|
if self.approach_orientation is not None:
|
||||||
|
next_pos, next_ori, dist_to_final_target = self.path_manager.get_path_to_ball(
|
||||||
|
x_ori=self.approach_orientation, x_dev=-0.24, torso_ori=self.approach_orientation, safety_margin=0.4)
|
||||||
|
|
||||||
|
if b_rel[0]<0.26 and b_rel[0]>0.18 and abs(b_rel[1])<0.04 and w.ball_is_visible: # ready to start dribble
|
||||||
|
self.phase += 1
|
||||||
|
reset_dribble = True
|
||||||
|
else:
|
||||||
|
dist = max(0.08, dist_to_final_target*0.7)
|
||||||
|
behavior.execute_sub_behavior("Walk", reset_walk, next_pos, True, next_ori, True, dist) # target, is_target_abs, ori, is_ori_abs, distance
|
||||||
|
|
||||||
|
#------------------------ 2B. A better approach orientation is not needed but the robot cannot see the ball
|
||||||
|
elif w.time_local_ms - w.ball_last_seen > 200: # walk to absolute target if ball was not seen
|
||||||
|
abs_ori = M.vector_angle( b - me )
|
||||||
|
behavior.execute_sub_behavior("Walk", reset_walk, b, True, abs_ori, True, None) # target, is_target_abs, ori, is_ori_abs, distance
|
||||||
|
|
||||||
|
#------------------------ 2C. A better approach orientation is not needed and the robot can see the ball
|
||||||
|
else: # walk to relative target
|
||||||
|
if 0.18<b_rel[0]<0.25 and abs(b_rel[1])<0.05 and w.ball_is_visible: # ready to start dribble
|
||||||
|
self.phase += 1
|
||||||
|
reset_dribble = True
|
||||||
|
else:
|
||||||
|
rel_target = b_rel+(-0.23,0) # relative target is a circle (center: ball, radius:0.23m)
|
||||||
|
rel_ori = M.vector_angle(b_rel) # relative orientation of ball, NOT the target!
|
||||||
|
dist = max(0.08, np.linalg.norm(rel_target)*0.7) # slow approach
|
||||||
|
behavior.execute_sub_behavior("Walk", reset_walk, rel_target, False, rel_ori, False, dist) # target, is_target_abs, ori, is_ori_abs, distance
|
||||||
|
|
||||||
|
if stop:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.phase == 1 and (stop or (b_dist > 0.5 and lost_ball)): # go back to walking
|
||||||
|
self.phase += 1
|
||||||
|
elif self.phase == 1: # dribble
|
||||||
|
#------------------------ 1. Define dribble parameters
|
||||||
|
self.env.dribble_speed = speed
|
||||||
|
|
||||||
|
# Relative orientation values are decreased to avoid overshoot
|
||||||
|
if orientation is None:
|
||||||
|
if b[0] < 0: # dribble to the sides
|
||||||
|
if b[1] > 0:
|
||||||
|
dribble_target = (15,5)
|
||||||
|
else:
|
||||||
|
dribble_target = (15,-5)
|
||||||
|
else:
|
||||||
|
dribble_target = None # go to goal
|
||||||
|
self.env.dribble_rel_orientation = self.path_manager.get_dribble_path(optional_2d_target=dribble_target)[1]
|
||||||
|
elif is_orientation_absolute:
|
||||||
|
self.env.dribble_rel_orientation = M.normalize_deg( orientation - r.imu_torso_orientation )
|
||||||
|
else:
|
||||||
|
self.env.dribble_rel_orientation = float(orientation) # copy if numpy float
|
||||||
|
|
||||||
|
#------------------------ 2. Execute behavior
|
||||||
|
obs = self.env.observe(reset_dribble)
|
||||||
|
action = run_mlp(obs, self.model)
|
||||||
|
self.env.execute(action)
|
||||||
|
|
||||||
|
# wind down dribbling, and then reset phase
|
||||||
|
if self.phase > 1:
|
||||||
|
WIND_DOWN_STEPS = 60
|
||||||
|
#------------------------ 1. Define dribble wind down parameters
|
||||||
|
self.env.dribble_speed = 1 - self.phase/WIND_DOWN_STEPS
|
||||||
|
self.env.dribble_rel_orientation = 0
|
||||||
|
|
||||||
|
#------------------------ 2. Execute behavior
|
||||||
|
obs = self.env.observe(reset_dribble, virtual_ball=True)
|
||||||
|
action = run_mlp(obs, self.model)
|
||||||
|
self.env.execute(action)
|
||||||
|
|
||||||
|
#------------------------ 3. Reset behavior
|
||||||
|
self.phase += 1
|
||||||
|
if self.phase >= WIND_DOWN_STEPS - 5:
|
||||||
|
self.phase = 0
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
''' Returns True if this behavior is ready to start/continue under current game/robot conditions '''
|
||||||
|
return True
|
@ -0,0 +1,181 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from behaviors.custom.Step.Step_Generator import Step_Generator
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class Env():
|
||||||
|
def __init__(self, base_agent : Base_Agent, step_width) -> None:
|
||||||
|
|
||||||
|
self.world = base_agent.world
|
||||||
|
self.ik = base_agent.inv_kinematics
|
||||||
|
|
||||||
|
# State space
|
||||||
|
self.obs = np.zeros(76, np.float32)
|
||||||
|
|
||||||
|
# Step behavior defaults
|
||||||
|
self.STEP_DUR = 8
|
||||||
|
self.STEP_Z_SPAN = 0.02
|
||||||
|
self.STEP_Z_MAX = 0.70
|
||||||
|
|
||||||
|
# IK
|
||||||
|
r = self.world.robot
|
||||||
|
nao_specs = self.ik.NAO_SPECS
|
||||||
|
self.leg_length = nao_specs[1] + nao_specs[3] # upper leg height + lower leg height
|
||||||
|
feet_y_dev = nao_specs[0] * step_width # wider step
|
||||||
|
sample_time = r.STEPTIME
|
||||||
|
max_ankle_z = nao_specs[5]
|
||||||
|
|
||||||
|
self.step_generator = Step_Generator(feet_y_dev, sample_time, max_ankle_z)
|
||||||
|
self.DEFAULT_ARMS = np.array([-90,-90,8,8,90,90,70,70],np.float32)
|
||||||
|
|
||||||
|
self.dribble_rel_orientation = None # relative to imu_torso_orientation (in degrees)
|
||||||
|
self.dribble_speed = 1
|
||||||
|
|
||||||
|
|
||||||
|
def observe(self, init=False, virtual_ball=False):
|
||||||
|
|
||||||
|
w = self.world
|
||||||
|
r = self.world.robot
|
||||||
|
|
||||||
|
if init: # reset variables
|
||||||
|
self.step_counter = 0
|
||||||
|
self.act = np.zeros(16, np.float32) # memory variable
|
||||||
|
|
||||||
|
# index observation naive normalization
|
||||||
|
self.obs[0] = min(self.step_counter,12*8) /100 # simple counter: 0,1,2,3...
|
||||||
|
self.obs[1] = r.loc_head_z *3 # z coordinate (torso)
|
||||||
|
self.obs[2] = r.loc_head_z_vel /2 # z velocity (torso)
|
||||||
|
self.obs[3] = r.imu_torso_roll /15 # absolute torso roll in deg
|
||||||
|
self.obs[4] = r.imu_torso_pitch /15 # absolute torso pitch in deg
|
||||||
|
self.obs[5:8] = r.gyro /100 # gyroscope
|
||||||
|
self.obs[8:11] = r.acc /10 # accelerometer
|
||||||
|
|
||||||
|
self.obs[11:17] = r.frp.get('lf', np.zeros(6)) * (10,10,10,0.01,0.01,0.01) # left foot: relative point of origin (p) and force vector (f) -> (px,py,pz,fx,fy,fz)*
|
||||||
|
self.obs[17:23] = r.frp.get('rf', np.zeros(6)) * (10,10,10,0.01,0.01,0.01) # right foot: relative point of origin (p) and force vector (f) -> (px,py,pz,fx,fy,fz)*
|
||||||
|
# *if foot is not touching the ground, then (px=0,py=0,pz=0,fx=0,fy=0,fz=0)
|
||||||
|
|
||||||
|
self.obs[23:43] = r.joints_position[2:22] /100 # position of all joints except head & toes (for robot type 4)
|
||||||
|
self.obs[43:63] = r.joints_speed[2:22] /6.1395 # speed of all joints except head & toes (for robot type 4)
|
||||||
|
|
||||||
|
'''
|
||||||
|
Expected observations for walking state:
|
||||||
|
Time step R 0 1 2 3 4 5 6 7 0
|
||||||
|
Progress 1 0 .14 .28 .43 .57 .71 .86 1 0
|
||||||
|
Left leg active T F F F F F F F F T
|
||||||
|
'''
|
||||||
|
|
||||||
|
if init: # the walking parameters refer to the last parameters in effect (after a reset, they are pointless)
|
||||||
|
self.obs[63] = 1 # step progress
|
||||||
|
self.obs[64] = 1 # 1 if left leg is active
|
||||||
|
self.obs[65] = 0 # 1 if right leg is active
|
||||||
|
self.obs[66] = 0
|
||||||
|
else:
|
||||||
|
self.obs[63] = self.step_generator.external_progress # step progress
|
||||||
|
self.obs[64] = float(self.step_generator.state_is_left_active) # 1 if left leg is active
|
||||||
|
self.obs[65] = float(not self.step_generator.state_is_left_active) # 1 if right leg is active
|
||||||
|
self.obs[66] = math.sin(self.step_generator.state_current_ts / self.step_generator.ts_per_step * math.pi)
|
||||||
|
|
||||||
|
# Ball
|
||||||
|
ball_rel_hip_center = self.ik.torso_to_hip_transform(w.ball_rel_torso_cart_pos)
|
||||||
|
ball_dist_hip_center = np.linalg.norm( ball_rel_hip_center )
|
||||||
|
|
||||||
|
if init:
|
||||||
|
self.obs[67:70] = (0,0,0) # Initial velocity is 0
|
||||||
|
elif w.ball_is_visible:
|
||||||
|
self.obs[67:70] = (ball_rel_hip_center - self.obs[70:73]) * 10 # Ball velocity, relative to ankle's midpoint
|
||||||
|
|
||||||
|
self.obs[70:73] = ball_rel_hip_center # Ball position, relative to hip
|
||||||
|
self.obs[73] = ball_dist_hip_center * 2
|
||||||
|
|
||||||
|
if virtual_ball: # simulate the ball between the robot's feet
|
||||||
|
self.obs[67:74] = (0,0,0,0.05,0,-0.175,0.36)
|
||||||
|
|
||||||
|
'''
|
||||||
|
Create internal target with a smoother variation
|
||||||
|
'''
|
||||||
|
|
||||||
|
MAX_ROTATION_DIFF = 20 # max difference (degrees) per visual step
|
||||||
|
MAX_ROTATION_DIST = 80
|
||||||
|
|
||||||
|
if init:
|
||||||
|
self.internal_rel_orientation = 0
|
||||||
|
self.internal_target_vel = 0
|
||||||
|
self.gym_last_internal_abs_ori = r.imu_torso_orientation # for training purposes (reward)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------- compute internal target
|
||||||
|
|
||||||
|
if w.vision_is_up_to_date:
|
||||||
|
|
||||||
|
previous_internal_rel_orientation = np.copy(self.internal_rel_orientation)
|
||||||
|
|
||||||
|
internal_ori_diff = np.clip( M.normalize_deg( self.dribble_rel_orientation - self.internal_rel_orientation ), -MAX_ROTATION_DIFF, MAX_ROTATION_DIFF)
|
||||||
|
self.internal_rel_orientation = np.clip(M.normalize_deg( self.internal_rel_orientation + internal_ori_diff ), -MAX_ROTATION_DIST, MAX_ROTATION_DIST)
|
||||||
|
|
||||||
|
# Observations
|
||||||
|
self.internal_target_vel = self.internal_rel_orientation - previous_internal_rel_orientation
|
||||||
|
|
||||||
|
self.gym_last_internal_abs_ori = self.internal_rel_orientation + r.imu_torso_orientation
|
||||||
|
|
||||||
|
#----------------------------------------------------------------- observations
|
||||||
|
|
||||||
|
self.obs[74] = self.internal_rel_orientation / MAX_ROTATION_DIST
|
||||||
|
self.obs[75] = self.internal_target_vel / MAX_ROTATION_DIFF
|
||||||
|
|
||||||
|
return self.obs
|
||||||
|
|
||||||
|
|
||||||
|
def execute_ik(self, l_pos, l_rot, r_pos, r_rot):
|
||||||
|
r = self.world.robot
|
||||||
|
# Apply IK to each leg + Set joint targets
|
||||||
|
|
||||||
|
# Left leg
|
||||||
|
indices, self.values_l, error_codes = self.ik.leg(l_pos, l_rot, True, dynamic_pose=False)
|
||||||
|
|
||||||
|
r.set_joints_target_position_direct(indices, self.values_l, harmonize=False)
|
||||||
|
|
||||||
|
# Right leg
|
||||||
|
indices, self.values_r, error_codes = self.ik.leg(r_pos, r_rot, False, dynamic_pose=False)
|
||||||
|
|
||||||
|
r.set_joints_target_position_direct(indices, self.values_r, harmonize=False)
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, action):
|
||||||
|
|
||||||
|
r = self.world.robot
|
||||||
|
|
||||||
|
# Actions:
|
||||||
|
# 0,1,2 left ankle pos
|
||||||
|
# 3,4,5 right ankle pos
|
||||||
|
# 6,7,8 left foot rotation
|
||||||
|
# 9,10,11 right foot rotation
|
||||||
|
# 12,13 left/right arm pitch
|
||||||
|
# 14,15 left/right arm roll
|
||||||
|
|
||||||
|
# exponential moving average
|
||||||
|
self.act = 0.85 * self.act + 0.15 * action * 0.7 * 0.95 * self.dribble_speed
|
||||||
|
|
||||||
|
# execute Step behavior to extract the target positions of each leg (we will override these targets)
|
||||||
|
lfy,lfz,rfy,rfz = self.step_generator.get_target_positions(self.step_counter == 0, self.STEP_DUR, self.STEP_Z_SPAN, self.leg_length * self.STEP_Z_MAX)
|
||||||
|
|
||||||
|
# Leg IK
|
||||||
|
a = self.act
|
||||||
|
l_ankle_pos = (a[0]*0.025-0.01, a[1]*0.01 + lfy, a[2]*0.01 + lfz)
|
||||||
|
r_ankle_pos = (a[3]*0.025-0.01, a[4]*0.01 + rfy, a[5]*0.01 + rfz)
|
||||||
|
l_foot_rot = a[6:9] * (2,2,3)
|
||||||
|
r_foot_rot = a[9:12] * (2,2,3)
|
||||||
|
|
||||||
|
# Limit leg yaw/pitch (and add bias)
|
||||||
|
l_foot_rot[2] = max(0,l_foot_rot[2] + 18.3)
|
||||||
|
r_foot_rot[2] = min(0,r_foot_rot[2] - 18.3)
|
||||||
|
|
||||||
|
# Arms actions
|
||||||
|
arms = np.copy(self.DEFAULT_ARMS) # default arms pose
|
||||||
|
arms[0:4] += a[12:16]*4 # arms pitch+roll
|
||||||
|
|
||||||
|
# Set target positions
|
||||||
|
self.execute_ik(l_ankle_pos, l_foot_rot, r_ankle_pos, r_foot_rot) # legs
|
||||||
|
r.set_joints_target_position_direct( slice(14,22), arms, harmonize=False ) # arms
|
||||||
|
|
||||||
|
self.step_counter += 1
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,43 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
from math_ops.Neural_Network import run_mlp
|
||||||
|
import pickle, numpy as np
|
||||||
|
|
||||||
|
class Fall():
|
||||||
|
|
||||||
|
def __init__(self, base_agent : Base_Agent) -> None:
|
||||||
|
self.world = base_agent.world
|
||||||
|
self.description = "Fall example"
|
||||||
|
self.auto_head = False
|
||||||
|
|
||||||
|
with open(M.get_active_directory("/behaviors/custom/Fall/fall.pkl"), 'rb') as f:
|
||||||
|
self.model = pickle.load(f)
|
||||||
|
|
||||||
|
self.action_size = len(self.model[-1][0]) # extracted from size of Neural Network's last layer bias
|
||||||
|
self.obs = np.zeros(self.action_size+1, np.float32)
|
||||||
|
|
||||||
|
self.controllable_joints = min(self.world.robot.no_of_joints, self.action_size) # compatibility between different robot types
|
||||||
|
|
||||||
|
def observe(self):
|
||||||
|
r = self.world.robot
|
||||||
|
|
||||||
|
for i in range(self.action_size):
|
||||||
|
self.obs[i] = r.joints_position[i] / 100 # naive scale normalization
|
||||||
|
|
||||||
|
self.obs[self.action_size] = r.cheat_abs_pos[2] # head.z (alternative: r.loc_head_z)
|
||||||
|
|
||||||
|
def execute(self,reset) -> bool:
|
||||||
|
self.observe()
|
||||||
|
action = run_mlp(self.obs, self.model)
|
||||||
|
|
||||||
|
self.world.robot.set_joints_target_position_direct( # commit actions:
|
||||||
|
slice(self.controllable_joints), # act on trained joints
|
||||||
|
action*10, # scale actions up to motivate early exploration
|
||||||
|
harmonize=False # there is no point in harmonizing actions if the targets change at every step
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.world.robot.loc_head_z < 0.15 # finished when head height < 0.15 m
|
||||||
|
|
||||||
|
def is_ready(self) -> any:
|
||||||
|
''' Returns True if this behavior is ready to start/continue under current game/robot conditions '''
|
||||||
|
return True
|
Binary file not shown.
@ -0,0 +1,68 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from collections import deque
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class Get_Up():
|
||||||
|
|
||||||
|
def __init__(self, base_agent : Base_Agent) -> None:
|
||||||
|
self.behavior = base_agent.behavior
|
||||||
|
self.world = base_agent.world
|
||||||
|
self.description = "Get Up using the most appropriate skills"
|
||||||
|
self.auto_head = False
|
||||||
|
self.MIN_HEIGHT = 0.3 # minimum value for the head's height
|
||||||
|
self.MAX_INCLIN = 50 # maximum torso inclination in degrees
|
||||||
|
self.STABILITY_THRESHOLD = 4
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.state = 0
|
||||||
|
self.gyro_queue = deque(maxlen=self.STABILITY_THRESHOLD)
|
||||||
|
self.watchdog = 0 # when player has the shaking bug, it is never stable enough to get up
|
||||||
|
|
||||||
|
def execute(self,reset):
|
||||||
|
|
||||||
|
r = self.world.robot
|
||||||
|
execute_sub_behavior = self.behavior.execute_sub_behavior
|
||||||
|
|
||||||
|
if reset:
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
if self.state == 0: # State 0: go to pose "Zero"
|
||||||
|
|
||||||
|
self.watchdog += 1
|
||||||
|
self.gyro_queue.append( max(abs(r.gyro)) ) # log last STABILITY_THRESHOLD values
|
||||||
|
|
||||||
|
# advance to next state if behavior is complete & robot is stable
|
||||||
|
if (execute_sub_behavior("Zero",None) and len(self.gyro_queue) == self.STABILITY_THRESHOLD
|
||||||
|
and all(g < 10 for g in self.gyro_queue)) or self.watchdog > 100:
|
||||||
|
|
||||||
|
# determine how to get up
|
||||||
|
if r.acc[0] < -4 and abs(r.acc[1]) < 2 and abs(r.acc[2]) < 3:
|
||||||
|
execute_sub_behavior("Get_Up_Front", True) # reset behavior
|
||||||
|
self.state = 1
|
||||||
|
elif r.acc[0] > 4 and abs(r.acc[1]) < 2 and abs(r.acc[2]) < 3:
|
||||||
|
execute_sub_behavior("Get_Up_Back", True) # reset behavior
|
||||||
|
self.state = 2
|
||||||
|
elif r.acc[2] > 8: # fail-safe if vision is not up to date: if pose is 'Zero' and torso is upright, the robot is already up
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
execute_sub_behavior("Flip", True) # reset behavior
|
||||||
|
self.state = 3
|
||||||
|
|
||||||
|
elif self.state == 1:
|
||||||
|
if execute_sub_behavior("Get_Up_Front", False):
|
||||||
|
return True
|
||||||
|
elif self.state == 2:
|
||||||
|
if execute_sub_behavior("Get_Up_Back", False):
|
||||||
|
return True
|
||||||
|
elif self.state == 3:
|
||||||
|
if execute_sub_behavior("Flip", False):
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
''' Returns True if the Get Up behavior is ready (= robot is down) '''
|
||||||
|
r = self.world.robot
|
||||||
|
# check if z < 5 and acc magnitude > 8 and any visual indicator says we fell
|
||||||
|
return r.acc[2] < 5 and np.dot(r.acc,r.acc) > 64 and (r.loc_head_z < self.MIN_HEIGHT or r.imu_torso_inclination > self.MAX_INCLIN)
|
@ -0,0 +1,75 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from behaviors.custom.Step.Step_Generator import Step_Generator
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
|
||||||
|
|
||||||
|
class Kick_8():
|
||||||
|
|
||||||
|
def __init__(self, base_agent: Base_Agent) -> None:
|
||||||
|
self.behavior = base_agent.behavior
|
||||||
|
self.path_manager = base_agent.path_manager
|
||||||
|
self.world = base_agent.world
|
||||||
|
self.description = "Walk to ball and perform a Kick_8"
|
||||||
|
self.auto_head = True
|
||||||
|
|
||||||
|
r_type = self.world.robot.type
|
||||||
|
self.bias_dir = [22, 29, 26, 29, 22][self.world.robot.type]
|
||||||
|
self.ball_x_limits = ((0.19, 0.215), (0.2, 0.22), (0.19, 0.22), (0.2, 0.215), (0.2, 0.215))[r_type]
|
||||||
|
self.ball_y_limits = ((-0.115, -0.1), (-0.125, -0.095), (-0.12, -0.1), (-0.13, -0.105), (-0.09, -0.06))[r_type]
|
||||||
|
self.ball_x_center = (self.ball_x_limits[0] + self.ball_x_limits[1]) / 2
|
||||||
|
self.ball_y_center = (self.ball_y_limits[0] + self.ball_y_limits[1]) / 2
|
||||||
|
|
||||||
|
def execute(self, reset, direction, distance, abort=False) -> bool: # You can add more arguments
|
||||||
|
'''
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
direction : float
|
||||||
|
kick direction relative to field, in degrees
|
||||||
|
abort : bool
|
||||||
|
True to abort.
|
||||||
|
The method returns True upon successful abortion, which is immediate while the robot is aligning itself.
|
||||||
|
However, if the abortion is requested during the kick, it is delayed until the kick is completed.
|
||||||
|
'''
|
||||||
|
|
||||||
|
w = self.world
|
||||||
|
r = self.world.robot
|
||||||
|
b = w.ball_rel_torso_cart_pos
|
||||||
|
t = w.time_local_ms
|
||||||
|
gait: Step_Generator = self.behavior.get_custom_behavior_object("Walk").env.step_generator
|
||||||
|
|
||||||
|
if reset:
|
||||||
|
self.phase = 0
|
||||||
|
self.reset_time = t
|
||||||
|
|
||||||
|
if self.phase == 0:
|
||||||
|
biased_dir = M.normalize_deg(direction + self.bias_dir) # add bias to rectify direction
|
||||||
|
ang_diff = abs(
|
||||||
|
M.normalize_deg(biased_dir - r.loc_torso_orientation)) # the reset was learned with loc, not IMU
|
||||||
|
|
||||||
|
next_pos, next_ori, dist_to_final_target = self.path_manager.get_path_to_ball(
|
||||||
|
x_ori=biased_dir, x_dev=-self.ball_x_center, y_dev=-self.ball_y_center, torso_ori=biased_dir)
|
||||||
|
|
||||||
|
if (w.ball_last_seen > t - w.VISUALSTEP_MS and ang_diff < 5 and # ball is visible & aligned
|
||||||
|
self.ball_x_limits[0] < b[0] < self.ball_x_limits[1] and # ball is in kick area (x)
|
||||||
|
self.ball_y_limits[0] < b[1] < self.ball_y_limits[1] and # ball is in kick area (y)
|
||||||
|
t - w.ball_abs_pos_last_update < 100 and # ball absolute location is recent
|
||||||
|
dist_to_final_target < 0.03 and # if absolute ball position is updated
|
||||||
|
not gait.state_is_left_active and gait.state_current_ts == 2 and # walk gait phase is adequate
|
||||||
|
t - self.reset_time > 500): # to avoid kicking immediately without preparation & stability
|
||||||
|
|
||||||
|
self.phase += 1
|
||||||
|
|
||||||
|
return self.behavior.execute_sub_behavior("Kick_Motion", True)
|
||||||
|
else:
|
||||||
|
dist = max(0.07, dist_to_final_target)
|
||||||
|
reset_walk = reset and self.behavior.previous_behavior != "Walk" # reset walk if it wasn't the previous behavior
|
||||||
|
self.behavior.execute_sub_behavior("Walk", reset_walk, next_pos, True, next_ori, True,
|
||||||
|
dist) # target, is_target_abs, ori, is_ori_abs, distance
|
||||||
|
return abort # abort only if self.phase == 0
|
||||||
|
|
||||||
|
else: # define kick parameters and execute
|
||||||
|
return self.behavior.execute_sub_behavior("Kick_Motion", False)
|
||||||
|
|
||||||
|
def is_ready(self) -> any: # You can add more arguments
|
||||||
|
''' Returns True if this behavior is ready to start/continue under current game/robot conditions '''
|
||||||
|
return True
|
@ -0,0 +1,59 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from behaviors.custom.Step.Step_Generator import Step_Generator
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class Step():
|
||||||
|
|
||||||
|
def __init__(self, base_agent : Base_Agent) -> None:
|
||||||
|
self.world = base_agent.world
|
||||||
|
self.ik = base_agent.inv_kinematics
|
||||||
|
self.description = "Step (Skill-Set-Primitive)"
|
||||||
|
self.auto_head = True
|
||||||
|
|
||||||
|
nao_specs = self.ik.NAO_SPECS
|
||||||
|
self.leg_length = nao_specs[1] + nao_specs[3] # upper leg height + lower leg height
|
||||||
|
|
||||||
|
feet_y_dev = nao_specs[0] * 1.2 # wider step
|
||||||
|
sample_time = self.world.robot.STEPTIME
|
||||||
|
max_ankle_z = nao_specs[5]
|
||||||
|
|
||||||
|
# Initialize step generator with constants
|
||||||
|
self.step_generator = Step_Generator(feet_y_dev, sample_time, max_ankle_z)
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self,reset, ts_per_step=7, z_span=0.03, z_max=0.8):
|
||||||
|
|
||||||
|
lfy,lfz,rfy,rfz = self.step_generator.get_target_positions(reset, ts_per_step, z_span, self.leg_length * z_max)
|
||||||
|
|
||||||
|
#----------------- Apply IK to each leg + Set joint targets
|
||||||
|
|
||||||
|
# Left leg
|
||||||
|
indices, self.values_l, error_codes = self.ik.leg((0,lfy,lfz), (0,0,0), True, dynamic_pose=False)
|
||||||
|
for i in error_codes:
|
||||||
|
print(f"Joint {i} is out of range!" if i!=-1 else "Position is out of reach!")
|
||||||
|
|
||||||
|
self.world.robot.set_joints_target_position_direct(indices, self.values_l)
|
||||||
|
|
||||||
|
# Right leg
|
||||||
|
indices, self.values_r, error_codes = self.ik.leg((0,rfy,rfz), (0,0,0), False, dynamic_pose=False)
|
||||||
|
for i in error_codes:
|
||||||
|
print(f"Joint {i} is out of range!" if i!=-1 else "Position is out of reach!")
|
||||||
|
|
||||||
|
self.world.robot.set_joints_target_position_direct(indices, self.values_r)
|
||||||
|
|
||||||
|
# ----------------- Fixed arms
|
||||||
|
|
||||||
|
indices = [14,16,18,20]
|
||||||
|
values = np.array([-80,20,90,0])
|
||||||
|
self.world.robot.set_joints_target_position_direct(indices,values)
|
||||||
|
|
||||||
|
indices = [15,17,19,21]
|
||||||
|
values = np.array([-80,20,90,0])
|
||||||
|
self.world.robot.set_joints_target_position_direct(indices,values)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
''' Returns True if Step Behavior is ready to start under current game/robot conditions '''
|
||||||
|
return True
|
@ -0,0 +1,75 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
class Step_Generator():
|
||||||
|
GRAVITY = 9.81
|
||||||
|
Z0 = 0.2
|
||||||
|
|
||||||
|
def __init__(self, feet_y_dev, sample_time, max_ankle_z) -> None:
|
||||||
|
self.feet_y_dev = feet_y_dev
|
||||||
|
self.sample_time = sample_time
|
||||||
|
self.state_is_left_active = False
|
||||||
|
self.state_current_ts = 0
|
||||||
|
self.switch = False # switch legs
|
||||||
|
self.external_progress = 0 # non-overlaped progress
|
||||||
|
self.max_ankle_z = max_ankle_z
|
||||||
|
|
||||||
|
|
||||||
|
def get_target_positions(self, reset, ts_per_step, z_span, z_extension):
|
||||||
|
'''
|
||||||
|
Get target positions for each foot
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
target : `tuple`
|
||||||
|
(Left leg y, Left leg z, Right leg y, Right leg z)
|
||||||
|
'''
|
||||||
|
|
||||||
|
assert type(ts_per_step)==int and ts_per_step > 0, "ts_per_step must be a positive integer!"
|
||||||
|
|
||||||
|
#-------------------------- Advance 1ts
|
||||||
|
if reset:
|
||||||
|
self.ts_per_step = ts_per_step # step duration in time steps
|
||||||
|
self.swing_height = z_span
|
||||||
|
self.max_leg_extension = z_extension # maximum distance between ankle to center of both hip joints
|
||||||
|
self.state_current_ts = 0
|
||||||
|
self.state_is_left_active = False
|
||||||
|
self.switch = False
|
||||||
|
elif self.switch:
|
||||||
|
self.state_current_ts = 0
|
||||||
|
self.state_is_left_active = not self.state_is_left_active # switch leg
|
||||||
|
self.switch = False
|
||||||
|
else:
|
||||||
|
self.state_current_ts += 1
|
||||||
|
|
||||||
|
#-------------------------- Compute COM.y
|
||||||
|
W = math.sqrt(self.Z0/self.GRAVITY)
|
||||||
|
|
||||||
|
step_time = self.ts_per_step * self.sample_time
|
||||||
|
time_delta = self.state_current_ts * self.sample_time
|
||||||
|
|
||||||
|
y0 = self.feet_y_dev # absolute initial y value
|
||||||
|
y_swing = y0 + y0 * ( math.sinh((step_time - time_delta)/W) + math.sinh(time_delta/W) ) / math.sinh(-step_time/W)
|
||||||
|
|
||||||
|
#-------------------------- Cap maximum extension and swing height
|
||||||
|
z0 = min(-self.max_leg_extension, self.max_ankle_z) # capped initial z value
|
||||||
|
zh = min(self.swing_height, self.max_ankle_z - z0) # capped swing height
|
||||||
|
|
||||||
|
#-------------------------- Compute Z Swing
|
||||||
|
progress = self.state_current_ts / self.ts_per_step
|
||||||
|
self.external_progress = self.state_current_ts / (self.ts_per_step-1)
|
||||||
|
active_z_swing = zh * math.sin(math.pi * progress)
|
||||||
|
|
||||||
|
#-------------------------- Accept new parameters after final step
|
||||||
|
if self.state_current_ts + 1 >= self.ts_per_step:
|
||||||
|
self.ts_per_step = ts_per_step # step duration in time steps
|
||||||
|
self.swing_height = z_span
|
||||||
|
self.max_leg_extension = z_extension # maximum distance between ankle to center of both hip joints
|
||||||
|
self.switch = True
|
||||||
|
|
||||||
|
#-------------------------- Distinguish active leg
|
||||||
|
if self.state_is_left_active:
|
||||||
|
return y0+y_swing, active_z_swing+z0, -y0+y_swing, z0
|
||||||
|
else:
|
||||||
|
return y0-y_swing, z0, -y0-y_swing, active_z_swing+z0
|
||||||
|
|
@ -0,0 +1,201 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from behaviors.custom.Step.Step_Generator import Step_Generator
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class Env():
|
||||||
|
def __init__(self, base_agent : Base_Agent) -> None:
|
||||||
|
|
||||||
|
self.world = base_agent.world
|
||||||
|
self.ik = base_agent.inv_kinematics
|
||||||
|
|
||||||
|
# State space
|
||||||
|
self.obs = np.zeros(63, np.float32)
|
||||||
|
|
||||||
|
# Step behavior defaults
|
||||||
|
self.STEP_DUR = 8
|
||||||
|
self.STEP_Z_SPAN = 0.02
|
||||||
|
self.STEP_Z_MAX = 0.70
|
||||||
|
|
||||||
|
# IK
|
||||||
|
nao_specs = self.ik.NAO_SPECS
|
||||||
|
self.leg_length = nao_specs[1] + nao_specs[3] # upper leg height + lower leg height
|
||||||
|
feet_y_dev = nao_specs[0] * 1.12 # wider step
|
||||||
|
sample_time = self.world.robot.STEPTIME
|
||||||
|
max_ankle_z = nao_specs[5]
|
||||||
|
|
||||||
|
self.step_generator = Step_Generator(feet_y_dev, sample_time, max_ankle_z)
|
||||||
|
self.DEFAULT_ARMS = np.array([-90,-90,8,8,90,90,70,70],np.float32)
|
||||||
|
|
||||||
|
self.walk_rel_orientation = None
|
||||||
|
self.walk_rel_target = None
|
||||||
|
self.walk_distance = None
|
||||||
|
|
||||||
|
|
||||||
|
def observe(self, init=False):
|
||||||
|
|
||||||
|
r = self.world.robot
|
||||||
|
|
||||||
|
if init: # reset variables
|
||||||
|
self.act = np.zeros(16, np.float32) # memory variable
|
||||||
|
self.step_counter = 0
|
||||||
|
|
||||||
|
# index observation naive normalization
|
||||||
|
self.obs[0] = min(self.step_counter,15*8) /100 # simple counter: 0,1,2,3...
|
||||||
|
self.obs[1] = r.loc_head_z *3 # z coordinate (torso)
|
||||||
|
self.obs[2] = r.loc_head_z_vel /2 # z velocity (torso)
|
||||||
|
self.obs[3] = r.imu_torso_roll /15 # absolute torso roll in deg
|
||||||
|
self.obs[4] = r.imu_torso_pitch /15 # absolute torso pitch in deg
|
||||||
|
self.obs[5:8] = r.gyro /100 # gyroscope
|
||||||
|
self.obs[8:11] = r.acc /10 # accelerometer
|
||||||
|
|
||||||
|
self.obs[11:17] = r.frp.get('lf', np.zeros(6)) * (10,10,10,0.01,0.01,0.01) # left foot: relative point of origin (p) and force vector (f) -> (px,py,pz,fx,fy,fz)*
|
||||||
|
self.obs[17:23] = r.frp.get('rf', np.zeros(6)) * (10,10,10,0.01,0.01,0.01) # right foot: relative point of origin (p) and force vector (f) -> (px,py,pz,fx,fy,fz)*
|
||||||
|
# *if foot is not touching the ground, then (px=0,py=0,pz=0,fx=0,fy=0,fz=0)
|
||||||
|
|
||||||
|
# Joints: Forward kinematics for ankles + feet rotation + arms (pitch + roll)
|
||||||
|
rel_lankle = self.ik.get_body_part_pos_relative_to_hip("lankle") # ankle position relative to center of both hip joints
|
||||||
|
rel_rankle = self.ik.get_body_part_pos_relative_to_hip("rankle") # ankle position relative to center of both hip joints
|
||||||
|
lf = r.head_to_body_part_transform("torso", r.body_parts['lfoot'].transform ) # foot transform relative to torso
|
||||||
|
rf = r.head_to_body_part_transform("torso", r.body_parts['rfoot'].transform ) # foot transform relative to torso
|
||||||
|
lf_rot_rel_torso = np.array( [lf.get_roll_deg(), lf.get_pitch_deg(), lf.get_yaw_deg()] ) # foot rotation relative to torso
|
||||||
|
rf_rot_rel_torso = np.array( [rf.get_roll_deg(), rf.get_pitch_deg(), rf.get_yaw_deg()] ) # foot rotation relative to torso
|
||||||
|
|
||||||
|
# pose
|
||||||
|
self.obs[23:26] = rel_lankle * (8,8,5)
|
||||||
|
self.obs[26:29] = rel_rankle * (8,8,5)
|
||||||
|
self.obs[29:32] = lf_rot_rel_torso / 20
|
||||||
|
self.obs[32:35] = rf_rot_rel_torso / 20
|
||||||
|
self.obs[35:39] = r.joints_position[14:18] /100 # arms (pitch + roll)
|
||||||
|
|
||||||
|
# velocity
|
||||||
|
self.obs[39:55] = r.joints_target_last_speed[2:18] # predictions == last action
|
||||||
|
|
||||||
|
'''
|
||||||
|
Expected observations for walking state:
|
||||||
|
Time step R 0 1 2 3 4 5 6 7 0
|
||||||
|
Progress 1 0 .14 .28 .43 .57 .71 .86 1 0
|
||||||
|
Left leg active T F F F F F F F F T
|
||||||
|
'''
|
||||||
|
|
||||||
|
if init: # the walking parameters refer to the last parameters in effect (after a reset, they are pointless)
|
||||||
|
self.obs[55] = 1 # step progress
|
||||||
|
self.obs[56] = 1 # 1 if left leg is active
|
||||||
|
self.obs[57] = 0 # 1 if right leg is active
|
||||||
|
else:
|
||||||
|
self.obs[55] = self.step_generator.external_progress # step progress
|
||||||
|
self.obs[56] = float(self.step_generator.state_is_left_active) # 1 if left leg is active
|
||||||
|
self.obs[57] = float(not self.step_generator.state_is_left_active) # 1 if right leg is active
|
||||||
|
|
||||||
|
'''
|
||||||
|
Create internal target with a smoother variation
|
||||||
|
'''
|
||||||
|
|
||||||
|
MAX_LINEAR_DIST = 0.5
|
||||||
|
MAX_LINEAR_DIFF = 0.014 # max difference (meters) per step
|
||||||
|
MAX_ROTATION_DIFF = 1.6 # max difference (degrees) per step
|
||||||
|
MAX_ROTATION_DIST = 45
|
||||||
|
|
||||||
|
|
||||||
|
if init:
|
||||||
|
self.internal_rel_orientation = 0
|
||||||
|
self.internal_target = np.zeros(2)
|
||||||
|
|
||||||
|
previous_internal_target = np.copy(self.internal_target)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------- compute internal linear target
|
||||||
|
|
||||||
|
rel_raw_target_size = np.linalg.norm(self.walk_rel_target)
|
||||||
|
|
||||||
|
if rel_raw_target_size == 0:
|
||||||
|
rel_target = self.walk_rel_target
|
||||||
|
else:
|
||||||
|
rel_target = self.walk_rel_target / rel_raw_target_size * min(self.walk_distance, MAX_LINEAR_DIST)
|
||||||
|
|
||||||
|
internal_diff = rel_target - self.internal_target
|
||||||
|
internal_diff_size = np.linalg.norm(internal_diff)
|
||||||
|
|
||||||
|
if internal_diff_size > MAX_LINEAR_DIFF:
|
||||||
|
self.internal_target += internal_diff * (MAX_LINEAR_DIFF / internal_diff_size)
|
||||||
|
else:
|
||||||
|
self.internal_target[:] = rel_target
|
||||||
|
|
||||||
|
#---------------------------------------------------------------- compute internal rotation target
|
||||||
|
|
||||||
|
internal_ori_diff = np.clip( M.normalize_deg( self.walk_rel_orientation - self.internal_rel_orientation ), -MAX_ROTATION_DIFF, MAX_ROTATION_DIFF)
|
||||||
|
self.internal_rel_orientation = np.clip(M.normalize_deg( self.internal_rel_orientation + internal_ori_diff ), -MAX_ROTATION_DIST, MAX_ROTATION_DIST)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------- observations
|
||||||
|
|
||||||
|
internal_target_vel = self.internal_target - previous_internal_target
|
||||||
|
|
||||||
|
self.obs[58] = self.internal_target[0] / MAX_LINEAR_DIST
|
||||||
|
self.obs[59] = self.internal_target[1] / MAX_LINEAR_DIST
|
||||||
|
self.obs[60] = self.internal_rel_orientation / MAX_ROTATION_DIST
|
||||||
|
self.obs[61] = internal_target_vel[0] / MAX_LINEAR_DIFF
|
||||||
|
self.obs[62] = internal_target_vel[0] / MAX_LINEAR_DIFF
|
||||||
|
|
||||||
|
return self.obs
|
||||||
|
|
||||||
|
|
||||||
|
def execute_ik(self, l_pos, l_rot, r_pos, r_rot):
|
||||||
|
r = self.world.robot
|
||||||
|
# Apply IK to each leg + Set joint targets
|
||||||
|
|
||||||
|
# Left leg
|
||||||
|
indices, self.values_l, error_codes = self.ik.leg(l_pos, l_rot, True, dynamic_pose=False)
|
||||||
|
|
||||||
|
r.set_joints_target_position_direct(indices, self.values_l, harmonize=False)
|
||||||
|
|
||||||
|
# Right leg
|
||||||
|
indices, self.values_r, error_codes = self.ik.leg(r_pos, r_rot, False, dynamic_pose=False)
|
||||||
|
|
||||||
|
r.set_joints_target_position_direct(indices, self.values_r, harmonize=False)
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, action):
|
||||||
|
|
||||||
|
r = self.world.robot
|
||||||
|
|
||||||
|
# Actions:
|
||||||
|
# 0,1,2 left ankle pos
|
||||||
|
# 3,4,5 right ankle pos
|
||||||
|
# 6,7,8 left foot rotation
|
||||||
|
# 9,10,11 right foot rotation
|
||||||
|
# 12,13 left/right arm pitch
|
||||||
|
# 14,15 left/right arm roll
|
||||||
|
|
||||||
|
internal_dist = np.linalg.norm( self.internal_target )
|
||||||
|
action_mult = 1 if internal_dist > 0.2 else (0.7/0.2) * internal_dist + 0.3
|
||||||
|
|
||||||
|
# exponential moving average
|
||||||
|
self.act = 0.8 * self.act + 0.2 * action * action_mult * 0.7
|
||||||
|
|
||||||
|
# execute Step behavior to extract the target positions of each leg (we will override these targets)
|
||||||
|
lfy,lfz,rfy,rfz = self.step_generator.get_target_positions(self.step_counter == 0, self.STEP_DUR, self.STEP_Z_SPAN, self.leg_length * self.STEP_Z_MAX)
|
||||||
|
|
||||||
|
|
||||||
|
# Leg IK
|
||||||
|
a = self.act
|
||||||
|
l_ankle_pos = (a[0]*0.02, max(0.01, a[1]*0.02 + lfy), a[2]*0.01 + lfz) # limit y to avoid self collision
|
||||||
|
r_ankle_pos = (a[3]*0.02, min(a[4]*0.02 + rfy, -0.01), a[5]*0.01 + rfz) # limit y to avoid self collision
|
||||||
|
l_foot_rot = a[6:9] * (3,3,5)
|
||||||
|
r_foot_rot = a[9:12] * (3,3,5)
|
||||||
|
|
||||||
|
# Limit leg yaw/pitch
|
||||||
|
l_foot_rot[2] = max(0,l_foot_rot[2] + 7)
|
||||||
|
r_foot_rot[2] = min(0,r_foot_rot[2] - 7)
|
||||||
|
|
||||||
|
# Arms actions
|
||||||
|
arms = np.copy(self.DEFAULT_ARMS) # default arms pose
|
||||||
|
arm_swing = math.sin(self.step_generator.state_current_ts / self.STEP_DUR * math.pi) * 6
|
||||||
|
inv = 1 if self.step_generator.state_is_left_active else -1
|
||||||
|
arms[0:4] += a[12:16]*4 + (-arm_swing*inv,arm_swing*inv,0,0) # arms pitch+roll
|
||||||
|
|
||||||
|
# Set target positions
|
||||||
|
self.execute_ik(l_ankle_pos, l_foot_rot, r_ankle_pos, r_foot_rot) # legs
|
||||||
|
r.set_joints_target_position_direct( slice(14,22), arms, harmonize=False ) # arms
|
||||||
|
|
||||||
|
self.step_counter += 1
|
@ -0,0 +1,83 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent
|
||||||
|
from behaviors.custom.Walk.Env import Env
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
from math_ops.Neural_Network import run_mlp
|
||||||
|
import numpy as np
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
class Walk():
|
||||||
|
|
||||||
|
def __init__(self, base_agent : Base_Agent) -> None:
|
||||||
|
self.world = base_agent.world
|
||||||
|
self.description = "Omnidirectional RL walk"
|
||||||
|
self.auto_head = True
|
||||||
|
self.env = Env(base_agent)
|
||||||
|
self.last_executed = 0
|
||||||
|
|
||||||
|
with open(M.get_active_directory([
|
||||||
|
"/behaviors/custom/Walk/walk_R0.pkl",
|
||||||
|
"/behaviors/custom/Walk/walk_R1_R3.pkl",
|
||||||
|
"/behaviors/custom/Walk/walk_R2.pkl",
|
||||||
|
"/behaviors/custom/Walk/walk_R1_R3.pkl",
|
||||||
|
"/behaviors/custom/Walk/walk_R4.pkl"
|
||||||
|
][self.world.robot.type]), 'rb') as f:
|
||||||
|
self.model = pickle.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, reset, target_2d, is_target_absolute, orientation, is_orientation_absolute, distance):
|
||||||
|
'''
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
target_2d : array_like
|
||||||
|
2D target in absolute or relative coordinates (use is_target_absolute to specify)
|
||||||
|
is_target_absolute : bool
|
||||||
|
True if target_2d is in absolute coordinates, False if relative to robot's torso
|
||||||
|
orientation : float
|
||||||
|
absolute or relative orientation of torso, in degrees
|
||||||
|
set to None to go towards the target (is_orientation_absolute is ignored)
|
||||||
|
is_orientation_absolute : bool
|
||||||
|
True if orientation is relative to the field, False if relative to the robot's torso
|
||||||
|
distance : float
|
||||||
|
distance to final target [0,0.5] (influences walk speed when approaching the final target)
|
||||||
|
set to None to consider target_2d the final target
|
||||||
|
'''
|
||||||
|
r = self.world.robot
|
||||||
|
|
||||||
|
#------------------------ 0. Override reset (since some behaviors use this as a sub-behavior)
|
||||||
|
if reset and self.world.time_local_ms - self.last_executed == 20:
|
||||||
|
reset = False
|
||||||
|
self.last_executed = self.world.time_local_ms
|
||||||
|
|
||||||
|
#------------------------ 1. Define walk parameters
|
||||||
|
|
||||||
|
if is_target_absolute: # convert to target relative to (head position + torso orientation)
|
||||||
|
raw_target = target_2d - r.loc_head_position[:2]
|
||||||
|
self.env.walk_rel_target = M.rotate_2d_vec(raw_target, -r.imu_torso_orientation)
|
||||||
|
else:
|
||||||
|
self.env.walk_rel_target = target_2d
|
||||||
|
|
||||||
|
if distance is None:
|
||||||
|
self.env.walk_distance = np.linalg.norm(self.env.walk_rel_target)
|
||||||
|
else:
|
||||||
|
self.env.walk_distance = distance # MAX_LINEAR_DIST = 0.5
|
||||||
|
|
||||||
|
# Relative orientation values are decreased to avoid overshoot
|
||||||
|
if orientation is None:
|
||||||
|
self.env.walk_rel_orientation = M.vector_angle(self.env.walk_rel_target) * 0.3
|
||||||
|
elif is_orientation_absolute:
|
||||||
|
self.env.walk_rel_orientation = M.normalize_deg( orientation - r.imu_torso_orientation )
|
||||||
|
else:
|
||||||
|
self.env.walk_rel_orientation = orientation * 0.3
|
||||||
|
|
||||||
|
#------------------------ 2. Execute behavior
|
||||||
|
|
||||||
|
obs = self.env.observe(reset)
|
||||||
|
action = run_mlp(obs, self.model)
|
||||||
|
self.env.execute(action)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
''' Returns True if Walk Behavior is ready to start under current game/robot conditions '''
|
||||||
|
return True
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Dive left (goalkeeper)" auto_head="1">
|
||||||
|
<slot delta="0.28">
|
||||||
|
<move id="2" angle="0"/>
|
||||||
|
<move id="3" angle="0"/>
|
||||||
|
<move id="4" angle="-25"/>
|
||||||
|
<move id="5" angle="45"/>
|
||||||
|
<move id="6" angle="60"/>
|
||||||
|
<move id="7" angle="0"/>
|
||||||
|
<move id="8" angle="-120"/>
|
||||||
|
<move id="9" angle="0"/>
|
||||||
|
<move id="10" angle="60"/>
|
||||||
|
<move id="11" angle="0"/>
|
||||||
|
<move id="12" angle="25"/>
|
||||||
|
<move id="13" angle="-45"/>
|
||||||
|
<move id="14" angle="90"/>
|
||||||
|
<move id="15" angle="90"/>
|
||||||
|
<move id="16" angle="0"/>
|
||||||
|
<move id="17" angle="0"/>
|
||||||
|
<move id="18" angle="90"/>
|
||||||
|
<move id="19" angle="90"/>
|
||||||
|
<move id="20" angle="0"/>
|
||||||
|
<move id="21" angle="0"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.18">
|
||||||
|
<move id="2" angle="0"/>
|
||||||
|
<move id="3" angle="0"/>
|
||||||
|
<move id="4" angle="-25"/>
|
||||||
|
<move id="5" angle="45"/>
|
||||||
|
<move id="6" angle="60"/>
|
||||||
|
<move id="7" angle="0"/>
|
||||||
|
<move id="8" angle="-120"/>
|
||||||
|
<move id="9" angle="0"/>
|
||||||
|
<move id="10" angle="60"/>
|
||||||
|
<move id="11" angle="0"/>
|
||||||
|
<move id="12" angle="25"/>
|
||||||
|
<move id="13" angle="-45"/>
|
||||||
|
<move id="14" angle="90"/>
|
||||||
|
<move id="15" angle="90"/>
|
||||||
|
<move id="16" angle="0"/>
|
||||||
|
<move id="17" angle="0"/>
|
||||||
|
<move id="18" angle="90"/>
|
||||||
|
<move id="19" angle="90"/>
|
||||||
|
<move id="20" angle="0"/>
|
||||||
|
<move id="21" angle="0"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.32">
|
||||||
|
<move id="2" angle="0"/>
|
||||||
|
<move id="3" angle="0"/>
|
||||||
|
<move id="4" angle="0"/>
|
||||||
|
<move id="5" angle="0"/>
|
||||||
|
<move id="6" angle="0"/>
|
||||||
|
<move id="7" angle="0"/>
|
||||||
|
<move id="8" angle="0"/>
|
||||||
|
<move id="9" angle="0"/>
|
||||||
|
<move id="10" angle="0"/>
|
||||||
|
<move id="11" angle="0"/>
|
||||||
|
<move id="12" angle="0"/>
|
||||||
|
<move id="13" angle="0"/>
|
||||||
|
<move id="14" angle="90"/>
|
||||||
|
<move id="15" angle="90"/>
|
||||||
|
<move id="16" angle="0"/>
|
||||||
|
<move id="17" angle="0"/>
|
||||||
|
<move id="18" angle="90"/>
|
||||||
|
<move id="19" angle="90"/>
|
||||||
|
<move id="20" angle="0"/>
|
||||||
|
<move id="21" angle="0"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
||||||
|
|
@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Dive right (goalkeeper)" auto_head="1">
|
||||||
|
<slot delta="0.28">
|
||||||
|
<move id="2" angle="0"/>
|
||||||
|
<move id="3" angle="0"/>
|
||||||
|
<move id="4" angle="45"/>
|
||||||
|
<move id="5" angle="-25"/>
|
||||||
|
<move id="6" angle="0"/>
|
||||||
|
<move id="7" angle="60"/>
|
||||||
|
<move id="8" angle="0"/>
|
||||||
|
<move id="9" angle="-120"/>
|
||||||
|
<move id="10" angle="0"/>
|
||||||
|
<move id="11" angle="60"/>
|
||||||
|
<move id="12" angle="-45"/>
|
||||||
|
<move id="13" angle="25"/>
|
||||||
|
<move id="14" angle="90"/>
|
||||||
|
<move id="15" angle="90"/>
|
||||||
|
<move id="16" angle="0"/>
|
||||||
|
<move id="17" angle="0"/>
|
||||||
|
<move id="18" angle="90"/>
|
||||||
|
<move id="19" angle="90"/>
|
||||||
|
<move id="20" angle="0"/>
|
||||||
|
<move id="21" angle="0"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.18">
|
||||||
|
<move id="2" angle="0"/>
|
||||||
|
<move id="3" angle="0"/>
|
||||||
|
<move id="4" angle="45"/>
|
||||||
|
<move id="5" angle="-25"/>
|
||||||
|
<move id="6" angle="0"/>
|
||||||
|
<move id="7" angle="60"/>
|
||||||
|
<move id="8" angle="0"/>
|
||||||
|
<move id="9" angle="-120"/>
|
||||||
|
<move id="10" angle="0"/>
|
||||||
|
<move id="11" angle="60"/>
|
||||||
|
<move id="12" angle="-45"/>
|
||||||
|
<move id="13" angle="25"/>
|
||||||
|
<move id="14" angle="90"/>
|
||||||
|
<move id="15" angle="90"/>
|
||||||
|
<move id="16" angle="0"/>
|
||||||
|
<move id="17" angle="0"/>
|
||||||
|
<move id="18" angle="90"/>
|
||||||
|
<move id="19" angle="90"/>
|
||||||
|
<move id="20" angle="0"/>
|
||||||
|
<move id="21" angle="0"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.32">
|
||||||
|
<move id="2" angle="0"/>
|
||||||
|
<move id="3" angle="0"/>
|
||||||
|
<move id="4" angle="0"/>
|
||||||
|
<move id="5" angle="0"/>
|
||||||
|
<move id="6" angle="0"/>
|
||||||
|
<move id="7" angle="0"/>
|
||||||
|
<move id="8" angle="0"/>
|
||||||
|
<move id="9" angle="0"/>
|
||||||
|
<move id="10" angle="0"/>
|
||||||
|
<move id="11" angle="0"/>
|
||||||
|
<move id="12" angle="0"/>
|
||||||
|
<move id="13" angle="0"/>
|
||||||
|
<move id="14" angle="90"/>
|
||||||
|
<move id="15" angle="90"/>
|
||||||
|
<move id="16" angle="0"/>
|
||||||
|
<move id="17" angle="0"/>
|
||||||
|
<move id="18" angle="90"/>
|
||||||
|
<move id="19" angle="90"/>
|
||||||
|
<move id="20" angle="0"/>
|
||||||
|
<move id="21" angle="0"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
||||||
|
|
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Flip from sides before getting up" auto_head="0">
|
||||||
|
<slot delta="0.30">
|
||||||
|
<move id="14" angle="-120"/>
|
||||||
|
<move id="15" angle="-120"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.30">
|
||||||
|
<move id="16" angle="80"/>
|
||||||
|
<move id="17" angle="80"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Squat for demonstrations" auto_head="0">
|
||||||
|
<slot delta="1.0"> <!-- GO DOWN -->
|
||||||
|
<move id="6" angle="40"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="40"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="-80"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="-80"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="40"/> <!-- Left foot pitch -->
|
||||||
|
<move id="11" angle="40"/> <!-- Right foot pitch -->
|
||||||
|
<move id="16" angle="30"/> <!-- Left arm roll -->
|
||||||
|
<move id="17" angle="30"/> <!-- Right arm roll -->
|
||||||
|
</slot>
|
||||||
|
<slot delta="1.0"> <!-- GO UP -->
|
||||||
|
<move id="6" angle="0"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="0"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="0"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="0"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="0"/> <!-- Left foot pitch -->
|
||||||
|
<move id="11" angle="0"/> <!-- Right foot pitch -->
|
||||||
|
<move id="16" angle="0"/> <!-- Left arm roll -->
|
||||||
|
<move id="17" angle="0"/> <!-- Right arm roll -->
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,148 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Get up from robot's back starting from Zero pose" auto_head="0">
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-35.454876"/>
|
||||||
|
<move id="3" angle="-35.454876"/>
|
||||||
|
<move id="4" angle="9.4003105"/>
|
||||||
|
<move id="5" angle="9.4003105"/>
|
||||||
|
<move id="6" angle="106.73567"/>
|
||||||
|
<move id="7" angle="106.73567"/>
|
||||||
|
<move id="8" angle="23.08675"/>
|
||||||
|
<move id="9" angle="23.08675"/>
|
||||||
|
<move id="10" angle="-19.941034"/>
|
||||||
|
<move id="11" angle="-19.941034"/>
|
||||||
|
<move id="12" angle="8.216015"/>
|
||||||
|
<move id="13" angle="8.216015"/>
|
||||||
|
<move id="14" angle="57.021896"/>
|
||||||
|
<move id="15" angle="57.021896"/>
|
||||||
|
<move id="16" angle="-64.761406"/>
|
||||||
|
<move id="17" angle="-64.761406"/>
|
||||||
|
<move id="18" angle="-120.64462"/>
|
||||||
|
<move id="19" angle="-120.64462"/>
|
||||||
|
<move id="20" angle="85.76495"/>
|
||||||
|
<move id="21" angle="85.76495"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.23555405990408101">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-62.22426"/>
|
||||||
|
<move id="3" angle="-62.22426"/>
|
||||||
|
<move id="4" angle="-11.9137745"/>
|
||||||
|
<move id="5" angle="-11.9137745"/>
|
||||||
|
<move id="6" angle="24.511059"/>
|
||||||
|
<move id="7" angle="24.511059"/>
|
||||||
|
<move id="8" angle="14.4840355"/>
|
||||||
|
<move id="9" angle="14.4840355"/>
|
||||||
|
<move id="10" angle="53.158936"/>
|
||||||
|
<move id="11" angle="53.158936"/>
|
||||||
|
<move id="12" angle="-14.684006"/>
|
||||||
|
<move id="13" angle="-14.684006"/>
|
||||||
|
<move id="14" angle="-7.403747"/>
|
||||||
|
<move id="15" angle="-7.403747"/>
|
||||||
|
<move id="16" angle="60.993023"/>
|
||||||
|
<move id="17" angle="60.993023"/>
|
||||||
|
<move id="18" angle="106.169014"/>
|
||||||
|
<move id="19" angle="106.169014"/>
|
||||||
|
<move id="20" angle="47.9693"/>
|
||||||
|
<move id="21" angle="47.9693"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.32551713739126387">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-47.479836"/>
|
||||||
|
<move id="3" angle="-47.479836"/>
|
||||||
|
<move id="4" angle="24.034796"/>
|
||||||
|
<move id="5" angle="24.034796"/>
|
||||||
|
<move id="6" angle="143.53075"/>
|
||||||
|
<move id="7" angle="143.53075"/>
|
||||||
|
<move id="8" angle="-44.24652"/>
|
||||||
|
<move id="9" angle="-44.24652"/>
|
||||||
|
<move id="10" angle="-65.46661"/>
|
||||||
|
<move id="11" angle="-65.46661"/>
|
||||||
|
<move id="12" angle="-34.403675"/>
|
||||||
|
<move id="13" angle="-34.403675"/>
|
||||||
|
<move id="14" angle="63.59447"/>
|
||||||
|
<move id="15" angle="63.59447"/>
|
||||||
|
<move id="16" angle="-49.916084"/>
|
||||||
|
<move id="17" angle="-49.916084"/>
|
||||||
|
<move id="18" angle="-98.035194"/>
|
||||||
|
<move id="19" angle="-98.035194"/>
|
||||||
|
<move id="20" angle="-17.168068"/>
|
||||||
|
<move id="21" angle="-17.168068"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-66.561325"/>
|
||||||
|
<move id="3" angle="-66.561325"/>
|
||||||
|
<move id="4" angle="37.05487"/>
|
||||||
|
<move id="5" angle="37.05487"/>
|
||||||
|
<move id="6" angle="29.69468"/>
|
||||||
|
<move id="7" angle="29.69468"/>
|
||||||
|
<move id="8" angle="-54.39078"/>
|
||||||
|
<move id="9" angle="-54.39078"/>
|
||||||
|
<move id="10" angle="-60.952106"/>
|
||||||
|
<move id="11" angle="-60.952106"/>
|
||||||
|
<move id="12" angle="-29.30997"/>
|
||||||
|
<move id="13" angle="-29.30997"/>
|
||||||
|
<move id="14" angle="46.348843"/>
|
||||||
|
<move id="15" angle="46.348843"/>
|
||||||
|
<move id="16" angle="127.48256"/>
|
||||||
|
<move id="17" angle="127.48256"/>
|
||||||
|
<move id="18" angle="126.34747"/>
|
||||||
|
<move id="19" angle="126.34747"/>
|
||||||
|
<move id="20" angle="32.387558"/>
|
||||||
|
<move id="21" angle="32.387558"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.04259131836792499">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-20.94111"/>
|
||||||
|
<move id="3" angle="-20.94111"/>
|
||||||
|
<move id="4" angle="45.91111"/>
|
||||||
|
<move id="5" angle="45.91111"/>
|
||||||
|
<move id="6" angle="112.662"/>
|
||||||
|
<move id="7" angle="112.662"/>
|
||||||
|
<move id="8" angle="-134.07819"/>
|
||||||
|
<move id="9" angle="-134.07819"/>
|
||||||
|
<move id="10" angle="39.1108"/>
|
||||||
|
<move id="11" angle="39.1108"/>
|
||||||
|
<move id="12" angle="-41.35012"/>
|
||||||
|
<move id="13" angle="-41.35012"/>
|
||||||
|
<move id="14" angle="-27.910278"/>
|
||||||
|
<move id="15" angle="-27.910278"/>
|
||||||
|
<move id="16" angle="16.615406"/>
|
||||||
|
<move id="17" angle="16.615406"/>
|
||||||
|
<move id="18" angle="-107.39476"/>
|
||||||
|
<move id="19" angle="-107.39476"/>
|
||||||
|
<move id="20" angle="75.266754"/>
|
||||||
|
<move id="21" angle="75.266754"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.14964306950470477">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="2.0762901"/>
|
||||||
|
<move id="3" angle="2.0762901"/>
|
||||||
|
<move id="4" angle="53.06344"/>
|
||||||
|
<move id="5" angle="53.06344"/>
|
||||||
|
<move id="6" angle="138.88753"/>
|
||||||
|
<move id="7" angle="138.88753"/>
|
||||||
|
<move id="8" angle="-131.63484"/>
|
||||||
|
<move id="9" angle="-131.63484"/>
|
||||||
|
<move id="10" angle="31.228016"/>
|
||||||
|
<move id="11" angle="31.228016"/>
|
||||||
|
<move id="12" angle="-13.720424"/>
|
||||||
|
<move id="13" angle="-13.720424"/>
|
||||||
|
<move id="14" angle="43.981934"/>
|
||||||
|
<move id="15" angle="43.981934"/>
|
||||||
|
<move id="16" angle="-0.4473343"/>
|
||||||
|
<move id="17" angle="-0.4473343"/>
|
||||||
|
<move id="18" angle="13.684681"/>
|
||||||
|
<move id="19" angle="13.684681"/>
|
||||||
|
<move id="20" angle="-15.960043"/>
|
||||||
|
<move id="21" angle="-15.960043"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,220 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Get up from robot's front starting from Zero pose" auto_head="0">
|
||||||
|
<slot delta="0.2">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-4.976922"/>
|
||||||
|
<move id="3" angle="-4.976922"/>
|
||||||
|
<move id="4" angle="14.641327"/>
|
||||||
|
<move id="5" angle="14.641327"/>
|
||||||
|
<move id="6" angle="6.7912245"/>
|
||||||
|
<move id="7" angle="6.7912245"/>
|
||||||
|
<move id="8" angle="-0.386917"/>
|
||||||
|
<move id="9" angle="-0.386917"/>
|
||||||
|
<move id="10" angle="0.23039937"/>
|
||||||
|
<move id="11" angle="0.23039937"/>
|
||||||
|
<move id="12" angle="-5.228052"/>
|
||||||
|
<move id="13" angle="-5.228052"/>
|
||||||
|
<move id="14" angle="23.919197"/>
|
||||||
|
<move id="15" angle="23.919197"/>
|
||||||
|
<move id="16" angle="9.847563"/>
|
||||||
|
<move id="17" angle="9.847563"/>
|
||||||
|
<move id="18" angle="-6.9379196"/>
|
||||||
|
<move id="19" angle="-6.9379196"/>
|
||||||
|
<move id="20" angle="-16.700315"/>
|
||||||
|
<move id="21" angle="-16.700315"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.08">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="1.0232245"/>
|
||||||
|
<move id="3" angle="1.0232245"/>
|
||||||
|
<move id="4" angle="-22.36497"/>
|
||||||
|
<move id="5" angle="-22.36497"/>
|
||||||
|
<move id="6" angle="265.17767"/>
|
||||||
|
<move id="7" angle="265.17767"/>
|
||||||
|
<move id="8" angle="-133.80704"/>
|
||||||
|
<move id="9" angle="-133.80704"/>
|
||||||
|
<move id="10" angle="118.27272"/>
|
||||||
|
<move id="11" angle="118.27272"/>
|
||||||
|
<move id="12" angle="-7.289217"/>
|
||||||
|
<move id="13" angle="-7.289217"/>
|
||||||
|
<move id="14" angle="2.7956364"/>
|
||||||
|
<move id="15" angle="2.7956364"/>
|
||||||
|
<move id="16" angle="17.118933"/>
|
||||||
|
<move id="17" angle="17.118933"/>
|
||||||
|
<move id="18" angle="-27.083485"/>
|
||||||
|
<move id="19" angle="-27.083485"/>
|
||||||
|
<move id="20" angle="-8.698996"/>
|
||||||
|
<move id="21" angle="-8.698996"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.08">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-14.808299"/>
|
||||||
|
<move id="3" angle="-14.808299"/>
|
||||||
|
<move id="4" angle="37.39434"/>
|
||||||
|
<move id="5" angle="37.39434"/>
|
||||||
|
<move id="6" angle="7.6876955"/>
|
||||||
|
<move id="7" angle="7.6876955"/>
|
||||||
|
<move id="8" angle="-91.4461"/>
|
||||||
|
<move id="9" angle="-91.4461"/>
|
||||||
|
<move id="10" angle="-0.40869182"/>
|
||||||
|
<move id="11" angle="-0.40869182"/>
|
||||||
|
<move id="12" angle="-24.007051"/>
|
||||||
|
<move id="13" angle="-24.007051"/>
|
||||||
|
<move id="14" angle="8.485344"/>
|
||||||
|
<move id="15" angle="8.485344"/>
|
||||||
|
<move id="16" angle="7.828232"/>
|
||||||
|
<move id="17" angle="7.828232"/>
|
||||||
|
<move id="18" angle="5.688171"/>
|
||||||
|
<move id="19" angle="5.688171"/>
|
||||||
|
<move id="20" angle="-17.660221"/>
|
||||||
|
<move id="21" angle="-17.660221"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-89.41741"/>
|
||||||
|
<move id="3" angle="-89.41741"/>
|
||||||
|
<move id="4" angle="14.217276"/>
|
||||||
|
<move id="5" angle="14.217276"/>
|
||||||
|
<move id="6" angle="-3.3899987"/>
|
||||||
|
<move id="7" angle="-3.3899987"/>
|
||||||
|
<move id="8" angle="-103.90058"/>
|
||||||
|
<move id="9" angle="-103.90058"/>
|
||||||
|
<move id="10" angle="-48.087368"/>
|
||||||
|
<move id="11" angle="-48.087368"/>
|
||||||
|
<move id="12" angle="-21.949076"/>
|
||||||
|
<move id="13" angle="-21.949076"/>
|
||||||
|
<move id="14" angle="-0.5262981"/>
|
||||||
|
<move id="15" angle="-0.5262981"/>
|
||||||
|
<move id="16" angle="-9.913716"/>
|
||||||
|
<move id="17" angle="-9.913716"/>
|
||||||
|
<move id="18" angle="-0.202469"/>
|
||||||
|
<move id="19" angle="-0.202469"/>
|
||||||
|
<move id="20" angle="-12.194956"/>
|
||||||
|
<move id="21" angle="-12.194956"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-0.9640895"/>
|
||||||
|
<move id="3" angle="-0.9640895"/>
|
||||||
|
<move id="4" angle="-4.2894506"/>
|
||||||
|
<move id="5" angle="-4.2894506"/>
|
||||||
|
<move id="6" angle="-1.5769005"/>
|
||||||
|
<move id="7" angle="-1.5769005"/>
|
||||||
|
<move id="8" angle="5.6831093"/>
|
||||||
|
<move id="9" angle="5.6831093"/>
|
||||||
|
<move id="10" angle="4.317226"/>
|
||||||
|
<move id="11" angle="4.317226"/>
|
||||||
|
<move id="12" angle="-3.2686572"/>
|
||||||
|
<move id="13" angle="-3.2686572"/>
|
||||||
|
<move id="14" angle="50.114185"/>
|
||||||
|
<move id="15" angle="50.114185"/>
|
||||||
|
<move id="16" angle="-11.285558"/>
|
||||||
|
<move id="17" angle="-11.285558"/>
|
||||||
|
<move id="18" angle="-6.0098124"/>
|
||||||
|
<move id="19" angle="-6.0098124"/>
|
||||||
|
<move id="20" angle="-3.112287"/>
|
||||||
|
<move id="21" angle="-3.112287"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-60.664845"/>
|
||||||
|
<move id="3" angle="-60.664845"/>
|
||||||
|
<move id="4" angle="15.481741"/>
|
||||||
|
<move id="5" angle="15.481741"/>
|
||||||
|
<move id="6" angle="-1.4592165"/>
|
||||||
|
<move id="7" angle="-1.4592165"/>
|
||||||
|
<move id="8" angle="-121.534065"/>
|
||||||
|
<move id="9" angle="-121.534065"/>
|
||||||
|
<move id="10" angle="44.071327"/>
|
||||||
|
<move id="11" angle="44.071327"/>
|
||||||
|
<move id="12" angle="-10.756018"/>
|
||||||
|
<move id="13" angle="-10.756018"/>
|
||||||
|
<move id="14" angle="-4.711259"/>
|
||||||
|
<move id="15" angle="-4.711259"/>
|
||||||
|
<move id="16" angle="-7.3120046"/>
|
||||||
|
<move id="17" angle="-7.3120046"/>
|
||||||
|
<move id="18" angle="3.982705"/>
|
||||||
|
<move id="19" angle="3.982705"/>
|
||||||
|
<move id="20" angle="-11.348281"/>
|
||||||
|
<move id="21" angle="-11.348281"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.04">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-58.631565"/>
|
||||||
|
<move id="3" angle="-58.631565"/>
|
||||||
|
<move id="4" angle="31.81041"/>
|
||||||
|
<move id="5" angle="31.81041"/>
|
||||||
|
<move id="6" angle="100.52844"/>
|
||||||
|
<move id="7" angle="100.52844"/>
|
||||||
|
<move id="8" angle="-123.12153"/>
|
||||||
|
<move id="9" angle="-123.12153"/>
|
||||||
|
<move id="10" angle="38.84296"/>
|
||||||
|
<move id="11" angle="38.84296"/>
|
||||||
|
<move id="12" angle="-37.32096"/>
|
||||||
|
<move id="13" angle="-37.32096"/>
|
||||||
|
<move id="14" angle="-4.5341907"/>
|
||||||
|
<move id="15" angle="-4.5341907"/>
|
||||||
|
<move id="16" angle="6.23005"/>
|
||||||
|
<move id="17" angle="6.23005"/>
|
||||||
|
<move id="18" angle="12.395767"/>
|
||||||
|
<move id="19" angle="12.395767"/>
|
||||||
|
<move id="20" angle="-4.4955177"/>
|
||||||
|
<move id="21" angle="-4.4955177"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.14">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-62.06845"/>
|
||||||
|
<move id="3" angle="-62.06845"/>
|
||||||
|
<move id="4" angle="69.89282"/>
|
||||||
|
<move id="5" angle="69.89282"/>
|
||||||
|
<move id="6" angle="104.76725"/>
|
||||||
|
<move id="7" angle="104.76725"/>
|
||||||
|
<move id="8" angle="-132.93317"/>
|
||||||
|
<move id="9" angle="-132.93317"/>
|
||||||
|
<move id="10" angle="35.730618"/>
|
||||||
|
<move id="11" angle="35.730618"/>
|
||||||
|
<move id="12" angle="-33.17914"/>
|
||||||
|
<move id="13" angle="-33.17914"/>
|
||||||
|
<move id="14" angle="-19.49921"/>
|
||||||
|
<move id="15" angle="-19.49921"/>
|
||||||
|
<move id="16" angle="0.18734631"/>
|
||||||
|
<move id="17" angle="0.18734631"/>
|
||||||
|
<move id="18" angle="20.604797"/>
|
||||||
|
<move id="19" angle="20.604797"/>
|
||||||
|
<move id="20" angle="8.590274"/>
|
||||||
|
<move id="21" angle="8.590274"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.4">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-0.702901"/>
|
||||||
|
<move id="3" angle="-0.702901"/>
|
||||||
|
<move id="4" angle="7.3007526"/>
|
||||||
|
<move id="5" angle="7.3007526"/>
|
||||||
|
<move id="6" angle="36.762917"/>
|
||||||
|
<move id="7" angle="36.762917"/>
|
||||||
|
<move id="8" angle="-102.52497"/>
|
||||||
|
<move id="9" angle="-102.52497"/>
|
||||||
|
<move id="10" angle="65.24182"/>
|
||||||
|
<move id="11" angle="65.24182"/>
|
||||||
|
<move id="12" angle="7.8809857"/>
|
||||||
|
<move id="13" angle="7.8809857"/>
|
||||||
|
<move id="14" angle="-117.089615"/>
|
||||||
|
<move id="15" angle="-117.089615"/>
|
||||||
|
<move id="16" angle="37.752632"/>
|
||||||
|
<move id="17" angle="37.752632"/>
|
||||||
|
<move id="18" angle="47.93907"/>
|
||||||
|
<move id="19" angle="47.93907"/>
|
||||||
|
<move id="20" angle="-8.804468"/>
|
||||||
|
<move id="21" angle="-8.804468"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Kick motion with right leg" auto_head="1">
|
||||||
|
<slot delta="0.22"> <!-- Lean -->
|
||||||
|
<move id="5" angle="-10"/> <!-- Left leg roll -->
|
||||||
|
<move id="6" angle="40"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="65"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="-60"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="-115"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="60"/> <!-- Left foot pitch -->
|
||||||
|
<move id="11" angle="10"/> <!-- Right foot pitch -->
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.12"> <!-- Kick -->
|
||||||
|
<move id="3" angle="-45"/> <!-- Right leg yaw/pitch -->
|
||||||
|
<move id="6" angle="-25"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="80"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="0"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="0"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="30"/> <!-- Left foot pitch -->
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,148 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Get up from robot's back starting from Zero pose" auto_head="0">
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-33.468"/>
|
||||||
|
<move id="3" angle="-33.468"/>
|
||||||
|
<move id="4" angle="32.57773"/>
|
||||||
|
<move id="5" angle="32.57773"/>
|
||||||
|
<move id="6" angle="59.169636"/>
|
||||||
|
<move id="7" angle="59.169636"/>
|
||||||
|
<move id="8" angle="9.965677"/>
|
||||||
|
<move id="9" angle="9.965677"/>
|
||||||
|
<move id="10" angle="-2.0611076"/>
|
||||||
|
<move id="11" angle="-2.0611076"/>
|
||||||
|
<move id="12" angle="0.4622302"/>
|
||||||
|
<move id="13" angle="0.4622302"/>
|
||||||
|
<move id="14" angle="59.513363"/>
|
||||||
|
<move id="15" angle="59.513363"/>
|
||||||
|
<move id="16" angle="-65.481674"/>
|
||||||
|
<move id="17" angle="-65.481674"/>
|
||||||
|
<move id="18" angle="-40.493507"/>
|
||||||
|
<move id="19" angle="-40.493507"/>
|
||||||
|
<move id="20" angle="173.76538"/>
|
||||||
|
<move id="21" angle="173.76538"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.24">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-53.970947"/>
|
||||||
|
<move id="3" angle="-53.970947"/>
|
||||||
|
<move id="4" angle="-31.631035"/>
|
||||||
|
<move id="5" angle="-31.631035"/>
|
||||||
|
<move id="6" angle="19.487873"/>
|
||||||
|
<move id="7" angle="19.487873"/>
|
||||||
|
<move id="8" angle="16.130121"/>
|
||||||
|
<move id="9" angle="16.130121"/>
|
||||||
|
<move id="10" angle="48.766438"/>
|
||||||
|
<move id="11" angle="48.766438"/>
|
||||||
|
<move id="12" angle="-43.63519"/>
|
||||||
|
<move id="13" angle="-43.63519"/>
|
||||||
|
<move id="14" angle="8.0750675"/>
|
||||||
|
<move id="15" angle="8.0750675"/>
|
||||||
|
<move id="16" angle="26.09803"/>
|
||||||
|
<move id="17" angle="26.09803"/>
|
||||||
|
<move id="18" angle="76.53693"/>
|
||||||
|
<move id="19" angle="76.53693"/>
|
||||||
|
<move id="20" angle="166.2738"/>
|
||||||
|
<move id="21" angle="166.2738"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.32">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-58.35547"/>
|
||||||
|
<move id="3" angle="-58.35547"/>
|
||||||
|
<move id="4" angle="4.337753"/>
|
||||||
|
<move id="5" angle="4.337753"/>
|
||||||
|
<move id="6" angle="119.925354"/>
|
||||||
|
<move id="7" angle="119.925354"/>
|
||||||
|
<move id="8" angle="-24.74408"/>
|
||||||
|
<move id="9" angle="-24.74408"/>
|
||||||
|
<move id="10" angle="-96.68003"/>
|
||||||
|
<move id="11" angle="-96.68003"/>
|
||||||
|
<move id="12" angle="-38.341896"/>
|
||||||
|
<move id="13" angle="-38.341896"/>
|
||||||
|
<move id="14" angle="61.39873"/>
|
||||||
|
<move id="15" angle="61.39873"/>
|
||||||
|
<move id="16" angle="-69.773056"/>
|
||||||
|
<move id="17" angle="-69.773056"/>
|
||||||
|
<move id="18" angle="-76.832436"/>
|
||||||
|
<move id="19" angle="-76.832436"/>
|
||||||
|
<move id="20" angle="-33.894478"/>
|
||||||
|
<move id="21" angle="-33.894478"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-82.921234"/>
|
||||||
|
<move id="3" angle="-82.921234"/>
|
||||||
|
<move id="4" angle="62.38533"/>
|
||||||
|
<move id="5" angle="62.38533"/>
|
||||||
|
<move id="6" angle="-25.176954"/>
|
||||||
|
<move id="7" angle="-25.176954"/>
|
||||||
|
<move id="8" angle="-48.75859"/>
|
||||||
|
<move id="9" angle="-48.75859"/>
|
||||||
|
<move id="10" angle="-65.601974"/>
|
||||||
|
<move id="11" angle="-65.601974"/>
|
||||||
|
<move id="12" angle="-22.979483"/>
|
||||||
|
<move id="13" angle="-22.979483"/>
|
||||||
|
<move id="14" angle="5.9137707"/>
|
||||||
|
<move id="15" angle="5.9137707"/>
|
||||||
|
<move id="16" angle="21.222649"/>
|
||||||
|
<move id="17" angle="21.222649"/>
|
||||||
|
<move id="18" angle="-84.464584"/>
|
||||||
|
<move id="19" angle="-84.464584"/>
|
||||||
|
<move id="20" angle="38.63583"/>
|
||||||
|
<move id="21" angle="38.63583"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.06">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-28.479536"/>
|
||||||
|
<move id="3" angle="-28.479536"/>
|
||||||
|
<move id="4" angle="63.585136"/>
|
||||||
|
<move id="5" angle="63.585136"/>
|
||||||
|
<move id="6" angle="111.310234"/>
|
||||||
|
<move id="7" angle="111.310234"/>
|
||||||
|
<move id="8" angle="-159.30522"/>
|
||||||
|
<move id="9" angle="-159.30522"/>
|
||||||
|
<move id="10" angle="56.21947"/>
|
||||||
|
<move id="11" angle="56.21947"/>
|
||||||
|
<move id="12" angle="-46.503235"/>
|
||||||
|
<move id="13" angle="-46.503235"/>
|
||||||
|
<move id="14" angle="11.373715"/>
|
||||||
|
<move id="15" angle="11.373715"/>
|
||||||
|
<move id="16" angle="-54.145515"/>
|
||||||
|
<move id="17" angle="-54.145515"/>
|
||||||
|
<move id="18" angle="63.853237"/>
|
||||||
|
<move id="19" angle="63.853237"/>
|
||||||
|
<move id="20" angle="-32.70381"/>
|
||||||
|
<move id="21" angle="-32.70381"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.16">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-3.9309306"/>
|
||||||
|
<move id="3" angle="-3.9309306"/>
|
||||||
|
<move id="4" angle="28.753582"/>
|
||||||
|
<move id="5" angle="28.753582"/>
|
||||||
|
<move id="6" angle="73.12465"/>
|
||||||
|
<move id="7" angle="73.12465"/>
|
||||||
|
<move id="8" angle="-133.79837"/>
|
||||||
|
<move id="9" angle="-133.79837"/>
|
||||||
|
<move id="10" angle="37.535084"/>
|
||||||
|
<move id="11" angle="37.535084"/>
|
||||||
|
<move id="12" angle="5.768486"/>
|
||||||
|
<move id="13" angle="5.768486"/>
|
||||||
|
<move id="14" angle="-69.01422"/>
|
||||||
|
<move id="15" angle="-69.01422"/>
|
||||||
|
<move id="16" angle="50.282738"/>
|
||||||
|
<move id="17" angle="50.282738"/>
|
||||||
|
<move id="18" angle="51.603962"/>
|
||||||
|
<move id="19" angle="51.603962"/>
|
||||||
|
<move id="20" angle="80.4043"/>
|
||||||
|
<move id="21" angle="80.4043"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,220 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Get up from robot's front starting from Zero pose" auto_head="0">
|
||||||
|
<slot delta="0.04">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="0.03765949"/>
|
||||||
|
<move id="3" angle="0.03765949"/>
|
||||||
|
<move id="4" angle="6.7361"/>
|
||||||
|
<move id="5" angle="6.7361"/>
|
||||||
|
<move id="6" angle="7.144582"/>
|
||||||
|
<move id="7" angle="7.144582"/>
|
||||||
|
<move id="8" angle="3.2917228"/>
|
||||||
|
<move id="9" angle="3.2917228"/>
|
||||||
|
<move id="10" angle="0.2270207"/>
|
||||||
|
<move id="11" angle="0.2270207"/>
|
||||||
|
<move id="12" angle="-0.62398136"/>
|
||||||
|
<move id="13" angle="-0.62398136"/>
|
||||||
|
<move id="14" angle="11.943922"/>
|
||||||
|
<move id="15" angle="11.943922"/>
|
||||||
|
<move id="16" angle="36.128757"/>
|
||||||
|
<move id="17" angle="36.128757"/>
|
||||||
|
<move id="18" angle="1.7096568"/>
|
||||||
|
<move id="19" angle="1.7096568"/>
|
||||||
|
<move id="20" angle="-10.422138"/>
|
||||||
|
<move id="21" angle="-10.422138"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.18">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="1.2124634"/>
|
||||||
|
<move id="3" angle="1.2124634"/>
|
||||||
|
<move id="4" angle="-16.6976"/>
|
||||||
|
<move id="5" angle="-16.6976"/>
|
||||||
|
<move id="6" angle="12.002005"/>
|
||||||
|
<move id="7" angle="12.002005"/>
|
||||||
|
<move id="8" angle="-3.8902621"/>
|
||||||
|
<move id="9" angle="-3.8902621"/>
|
||||||
|
<move id="10" angle="2.2115564"/>
|
||||||
|
<move id="11" angle="2.2115564"/>
|
||||||
|
<move id="12" angle="18.24385"/>
|
||||||
|
<move id="13" angle="18.24385"/>
|
||||||
|
<move id="14" angle="17.7995"/>
|
||||||
|
<move id="15" angle="17.7995"/>
|
||||||
|
<move id="16" angle="46.32759"/>
|
||||||
|
<move id="17" angle="46.32759"/>
|
||||||
|
<move id="18" angle="-5.752133"/>
|
||||||
|
<move id="19" angle="-5.752133"/>
|
||||||
|
<move id="20" angle="-23.737434"/>
|
||||||
|
<move id="21" angle="-23.737434"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.06">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="0.4612013"/>
|
||||||
|
<move id="3" angle="0.4612013"/>
|
||||||
|
<move id="4" angle="-10.992544"/>
|
||||||
|
<move id="5" angle="-10.992544"/>
|
||||||
|
<move id="6" angle="256.44278"/>
|
||||||
|
<move id="7" angle="256.44278"/>
|
||||||
|
<move id="8" angle="-130.99982"/>
|
||||||
|
<move id="9" angle="-130.99982"/>
|
||||||
|
<move id="10" angle="125.019295"/>
|
||||||
|
<move id="11" angle="125.019295"/>
|
||||||
|
<move id="12" angle="-4.830559"/>
|
||||||
|
<move id="13" angle="-4.830559"/>
|
||||||
|
<move id="14" angle="-3.4743867"/>
|
||||||
|
<move id="15" angle="-3.4743867"/>
|
||||||
|
<move id="16" angle="-17.395947"/>
|
||||||
|
<move id="17" angle="-17.395947"/>
|
||||||
|
<move id="18" angle="-7.197071"/>
|
||||||
|
<move id="19" angle="-7.197071"/>
|
||||||
|
<move id="20" angle="-18.81015"/>
|
||||||
|
<move id="21" angle="-18.81015"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.06">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-11.333593"/>
|
||||||
|
<move id="3" angle="-11.333593"/>
|
||||||
|
<move id="4" angle="44.738525"/>
|
||||||
|
<move id="5" angle="44.738525"/>
|
||||||
|
<move id="6" angle="6.1589823"/>
|
||||||
|
<move id="7" angle="6.1589823"/>
|
||||||
|
<move id="8" angle="-91.90975"/>
|
||||||
|
<move id="9" angle="-91.90975"/>
|
||||||
|
<move id="10" angle="3.823912"/>
|
||||||
|
<move id="11" angle="3.823912"/>
|
||||||
|
<move id="12" angle="-38.074776"/>
|
||||||
|
<move id="13" angle="-38.074776"/>
|
||||||
|
<move id="14" angle="15.952488"/>
|
||||||
|
<move id="15" angle="15.952488"/>
|
||||||
|
<move id="16" angle="-7.0655346"/>
|
||||||
|
<move id="17" angle="-7.0655346"/>
|
||||||
|
<move id="18" angle="-22.311245"/>
|
||||||
|
<move id="19" angle="-22.311245"/>
|
||||||
|
<move id="20" angle="-18.258038"/>
|
||||||
|
<move id="21" angle="-18.258038"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-89.49788"/>
|
||||||
|
<move id="3" angle="-89.49788"/>
|
||||||
|
<move id="4" angle="-3.2911317"/>
|
||||||
|
<move id="5" angle="-3.2911317"/>
|
||||||
|
<move id="6" angle="-2.9935064"/>
|
||||||
|
<move id="7" angle="-2.9935064"/>
|
||||||
|
<move id="8" angle="-97.67326"/>
|
||||||
|
<move id="9" angle="-97.67326"/>
|
||||||
|
<move id="10" angle="-45.384357"/>
|
||||||
|
<move id="11" angle="-45.384357"/>
|
||||||
|
<move id="12" angle="-23.928814"/>
|
||||||
|
<move id="13" angle="-23.928814"/>
|
||||||
|
<move id="14" angle="-8.738391"/>
|
||||||
|
<move id="15" angle="-8.738391"/>
|
||||||
|
<move id="16" angle="-5.594632"/>
|
||||||
|
<move id="17" angle="-5.594632"/>
|
||||||
|
<move id="18" angle="12.174933"/>
|
||||||
|
<move id="19" angle="12.174933"/>
|
||||||
|
<move id="20" angle="9.707994"/>
|
||||||
|
<move id="21" angle="9.707994"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-3.2422483"/>
|
||||||
|
<move id="3" angle="-3.2422483"/>
|
||||||
|
<move id="4" angle="10.094491"/>
|
||||||
|
<move id="5" angle="10.094491"/>
|
||||||
|
<move id="6" angle="0.105650306"/>
|
||||||
|
<move id="7" angle="0.105650306"/>
|
||||||
|
<move id="8" angle="3.6602314"/>
|
||||||
|
<move id="9" angle="3.6602314"/>
|
||||||
|
<move id="10" angle="4.2006454"/>
|
||||||
|
<move id="11" angle="4.2006454"/>
|
||||||
|
<move id="12" angle="12.860165"/>
|
||||||
|
<move id="13" angle="12.860165"/>
|
||||||
|
<move id="14" angle="41.729637"/>
|
||||||
|
<move id="15" angle="41.729637"/>
|
||||||
|
<move id="16" angle="4.5020647"/>
|
||||||
|
<move id="17" angle="4.5020647"/>
|
||||||
|
<move id="18" angle="0.37317705"/>
|
||||||
|
<move id="19" angle="0.37317705"/>
|
||||||
|
<move id="20" angle="-17.251738"/>
|
||||||
|
<move id="21" angle="-17.251738"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.04">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-54.546894"/>
|
||||||
|
<move id="3" angle="-54.546894"/>
|
||||||
|
<move id="4" angle="47.746372"/>
|
||||||
|
<move id="5" angle="47.746372"/>
|
||||||
|
<move id="6" angle="102.24254"/>
|
||||||
|
<move id="7" angle="102.24254"/>
|
||||||
|
<move id="8" angle="-124.605934"/>
|
||||||
|
<move id="9" angle="-124.605934"/>
|
||||||
|
<move id="10" angle="48.503883"/>
|
||||||
|
<move id="11" angle="48.503883"/>
|
||||||
|
<move id="12" angle="-61.207714"/>
|
||||||
|
<move id="13" angle="-61.207714"/>
|
||||||
|
<move id="14" angle="3.2614079"/>
|
||||||
|
<move id="15" angle="3.2614079"/>
|
||||||
|
<move id="16" angle="14.854502"/>
|
||||||
|
<move id="17" angle="14.854502"/>
|
||||||
|
<move id="18" angle="-23.213947"/>
|
||||||
|
<move id="19" angle="-23.213947"/>
|
||||||
|
<move id="20" angle="6.2566423"/>
|
||||||
|
<move id="21" angle="6.2566423"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.14">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-49.910076"/>
|
||||||
|
<move id="3" angle="-49.910076"/>
|
||||||
|
<move id="4" angle="41.691715"/>
|
||||||
|
<move id="5" angle="41.691715"/>
|
||||||
|
<move id="6" angle="115.67641"/>
|
||||||
|
<move id="7" angle="115.67641"/>
|
||||||
|
<move id="8" angle="-130.50008"/>
|
||||||
|
<move id="9" angle="-130.50008"/>
|
||||||
|
<move id="10" angle="45.15649"/>
|
||||||
|
<move id="11" angle="45.15649"/>
|
||||||
|
<move id="12" angle="-63.601254"/>
|
||||||
|
<move id="13" angle="-63.601254"/>
|
||||||
|
<move id="14" angle="-23.61024"/>
|
||||||
|
<move id="15" angle="-23.61024"/>
|
||||||
|
<move id="16" angle="-22.336332"/>
|
||||||
|
<move id="17" angle="-22.336332"/>
|
||||||
|
<move id="18" angle="9.258426"/>
|
||||||
|
<move id="19" angle="9.258426"/>
|
||||||
|
<move id="20" angle="75"/> <!-- avoid self collision -->
|
||||||
|
<move id="21" angle="75"/> <!-- avoid self collision -->
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.38">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-1.9032228"/>
|
||||||
|
<move id="3" angle="-1.9032228"/>
|
||||||
|
<move id="4" angle="25.761885"/>
|
||||||
|
<move id="5" angle="25.761885"/>
|
||||||
|
<move id="6" angle="37.294437"/>
|
||||||
|
<move id="7" angle="37.294437"/>
|
||||||
|
<move id="8" angle="-92.91941"/>
|
||||||
|
<move id="9" angle="-92.91941"/>
|
||||||
|
<move id="10" angle="54.069553"/>
|
||||||
|
<move id="11" angle="54.069553"/>
|
||||||
|
<move id="12" angle="3.6394153"/>
|
||||||
|
<move id="13" angle="3.6394153"/>
|
||||||
|
<move id="14" angle="-115.9809"/>
|
||||||
|
<move id="15" angle="-115.9809"/>
|
||||||
|
<move id="16" angle="30.516266"/>
|
||||||
|
<move id="17" angle="30.516266"/>
|
||||||
|
<move id="18" angle="67.11856"/>
|
||||||
|
<move id="19" angle="67.11856"/>
|
||||||
|
<move id="20" angle="75"/> <!-- avoid self collision -->
|
||||||
|
<move id="21" angle="75"/> <!-- avoid self collision -->
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Kick motion with right leg" auto_head="1">
|
||||||
|
<slot delta="0.22"> <!-- Lean -->
|
||||||
|
<move id="5" angle="-10"/> <!-- Left leg roll -->
|
||||||
|
<move id="6" angle="40"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="65"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="-60"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="-115"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="60"/> <!-- Left foot pitch -->
|
||||||
|
<move id="11" angle="0"/> <!-- Right foot pitch -->
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.12"> <!-- Kick -->
|
||||||
|
<move id="3" angle="-45"/> <!-- Right leg yaw/pitch -->
|
||||||
|
<move id="6" angle="-25"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="80"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="0"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="0"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="30"/> <!-- Left foot pitch -->
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,148 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Get up from robot's back starting from Zero pose" auto_head="0">
|
||||||
|
<slot delta="0.028081112120805277">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-24.101269"/>
|
||||||
|
<move id="3" angle="-24.101269"/>
|
||||||
|
<move id="4" angle="32.993332"/>
|
||||||
|
<move id="5" angle="32.993332"/>
|
||||||
|
<move id="6" angle="110.35986"/>
|
||||||
|
<move id="7" angle="110.35986"/>
|
||||||
|
<move id="8" angle="1.8788892"/>
|
||||||
|
<move id="9" angle="1.8788892"/>
|
||||||
|
<move id="10" angle="80.01187"/>
|
||||||
|
<move id="11" angle="80.01187"/>
|
||||||
|
<move id="12" angle="9.717135"/>
|
||||||
|
<move id="13" angle="9.717135"/>
|
||||||
|
<move id="14" angle="51.12714"/>
|
||||||
|
<move id="15" angle="51.12714"/>
|
||||||
|
<move id="16" angle="42.79977"/>
|
||||||
|
<move id="17" angle="42.79977"/>
|
||||||
|
<move id="18" angle="-86.264915"/>
|
||||||
|
<move id="19" angle="-86.264915"/>
|
||||||
|
<move id="20" angle="33.84708"/>
|
||||||
|
<move id="21" angle="33.84708"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.23351845363901297">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-45.0749"/>
|
||||||
|
<move id="3" angle="-45.0749"/>
|
||||||
|
<move id="4" angle="-14.139231"/>
|
||||||
|
<move id="5" angle="-14.139231"/>
|
||||||
|
<move id="6" angle="45.828407"/>
|
||||||
|
<move id="7" angle="45.828407"/>
|
||||||
|
<move id="8" angle="12.7746315"/>
|
||||||
|
<move id="9" angle="12.7746315"/>
|
||||||
|
<move id="10" angle="39.730194"/>
|
||||||
|
<move id="11" angle="39.730194"/>
|
||||||
|
<move id="12" angle="-41.445404"/>
|
||||||
|
<move id="13" angle="-41.445404"/>
|
||||||
|
<move id="14" angle="-20.96023"/>
|
||||||
|
<move id="15" angle="-20.96023"/>
|
||||||
|
<move id="16" angle="67.418335"/>
|
||||||
|
<move id="17" angle="67.418335"/>
|
||||||
|
<move id="18" angle="136.93271"/>
|
||||||
|
<move id="19" angle="136.93271"/>
|
||||||
|
<move id="20" angle="79.51946"/>
|
||||||
|
<move id="21" angle="79.51946"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.3334680044481105">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-47.639645"/>
|
||||||
|
<move id="3" angle="-47.639645"/>
|
||||||
|
<move id="4" angle="36.16746"/>
|
||||||
|
<move id="5" angle="36.16746"/>
|
||||||
|
<move id="6" angle="106.46471"/>
|
||||||
|
<move id="7" angle="106.46471"/>
|
||||||
|
<move id="8" angle="-39.363796"/>
|
||||||
|
<move id="9" angle="-39.363796"/>
|
||||||
|
<move id="10" angle="-72.647545"/>
|
||||||
|
<move id="11" angle="-72.647545"/>
|
||||||
|
<move id="12" angle="-25.91456"/>
|
||||||
|
<move id="13" angle="-25.91456"/>
|
||||||
|
<move id="14" angle="72.665886"/>
|
||||||
|
<move id="15" angle="72.665886"/>
|
||||||
|
<move id="16" angle="-46.327827"/>
|
||||||
|
<move id="17" angle="-46.327827"/>
|
||||||
|
<move id="18" angle="-66.61269"/>
|
||||||
|
<move id="19" angle="-66.61269"/>
|
||||||
|
<move id="20" angle="-31.427937"/>
|
||||||
|
<move id="21" angle="-31.427937"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-75.75934"/>
|
||||||
|
<move id="3" angle="-75.75934"/>
|
||||||
|
<move id="4" angle="22.589397"/>
|
||||||
|
<move id="5" angle="22.589397"/>
|
||||||
|
<move id="6" angle="29.006308"/>
|
||||||
|
<move id="7" angle="29.006308"/>
|
||||||
|
<move id="8" angle="-38.235386"/>
|
||||||
|
<move id="9" angle="-38.235386"/>
|
||||||
|
<move id="10" angle="-60.07112"/>
|
||||||
|
<move id="11" angle="-60.07112"/>
|
||||||
|
<move id="12" angle="-7.1391"/>
|
||||||
|
<move id="13" angle="-7.1391"/>
|
||||||
|
<move id="14" angle="-4.2825775"/>
|
||||||
|
<move id="15" angle="-4.2825775"/>
|
||||||
|
<move id="16" angle="134.62912"/>
|
||||||
|
<move id="17" angle="134.62912"/>
|
||||||
|
<move id="18" angle="131.15475"/>
|
||||||
|
<move id="19" angle="131.15475"/>
|
||||||
|
<move id="20" angle="73.77145"/>
|
||||||
|
<move id="21" angle="73.77145"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.05570955407520801">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-19.975779"/>
|
||||||
|
<move id="3" angle="-19.975779"/>
|
||||||
|
<move id="4" angle="41.732513"/>
|
||||||
|
<move id="5" angle="41.732513"/>
|
||||||
|
<move id="6" angle="102.82768"/>
|
||||||
|
<move id="7" angle="102.82768"/>
|
||||||
|
<move id="8" angle="-136.81155"/>
|
||||||
|
<move id="9" angle="-136.81155"/>
|
||||||
|
<move id="10" angle="14.452017"/>
|
||||||
|
<move id="11" angle="14.452017"/>
|
||||||
|
<move id="12" angle="-36.594322"/>
|
||||||
|
<move id="13" angle="-36.594322"/>
|
||||||
|
<move id="14" angle="-10.283749"/>
|
||||||
|
<move id="15" angle="-10.283749"/>
|
||||||
|
<move id="16" angle="46.129036"/>
|
||||||
|
<move id="17" angle="46.129036"/>
|
||||||
|
<move id="18" angle="-29.25866"/>
|
||||||
|
<move id="19" angle="-29.25866"/>
|
||||||
|
<move id="20" angle="-28.004427"/>
|
||||||
|
<move id="21" angle="-28.004427"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.15631983125111132">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-1.2392063"/>
|
||||||
|
<move id="3" angle="-1.2392063"/>
|
||||||
|
<move id="4" angle="50.825813"/>
|
||||||
|
<move id="5" angle="50.825813"/>
|
||||||
|
<move id="6" angle="81.91005"/>
|
||||||
|
<move id="7" angle="81.91005"/>
|
||||||
|
<move id="8" angle="-114.5607"/>
|
||||||
|
<move id="9" angle="-114.5607"/>
|
||||||
|
<move id="10" angle="46.413334"/>
|
||||||
|
<move id="11" angle="46.413334"/>
|
||||||
|
<move id="12" angle="-11.515432"/>
|
||||||
|
<move id="13" angle="-11.515432"/>
|
||||||
|
<move id="14" angle="-17.704185"/>
|
||||||
|
<move id="15" angle="-17.704185"/>
|
||||||
|
<move id="16" angle="11.881702"/>
|
||||||
|
<move id="17" angle="11.881702"/>
|
||||||
|
<move id="18" angle="116.02219"/>
|
||||||
|
<move id="19" angle="116.02219"/>
|
||||||
|
<move id="20" angle="85.50837"/>
|
||||||
|
<move id="21" angle="85.50837"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,220 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Get up from robot's front starting from Zero pose" auto_head="0">
|
||||||
|
<slot delta="0.16">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="4.6313915"/>
|
||||||
|
<move id="3" angle="4.6313915"/>
|
||||||
|
<move id="4" angle="-11.846246"/>
|
||||||
|
<move id="5" angle="-11.846246"/>
|
||||||
|
<move id="6" angle="8.745557"/>
|
||||||
|
<move id="7" angle="8.745557"/>
|
||||||
|
<move id="8" angle="-1.8197118"/>
|
||||||
|
<move id="9" angle="-1.8197118"/>
|
||||||
|
<move id="10" angle="1.359597"/>
|
||||||
|
<move id="11" angle="1.359597"/>
|
||||||
|
<move id="12" angle="16.82137"/>
|
||||||
|
<move id="13" angle="16.82137"/>
|
||||||
|
<move id="14" angle="21.83775"/>
|
||||||
|
<move id="15" angle="21.83775"/>
|
||||||
|
<move id="16" angle="-27.174564"/>
|
||||||
|
<move id="17" angle="-27.174564"/>
|
||||||
|
<move id="18" angle="-12.889872"/>
|
||||||
|
<move id="19" angle="-12.889872"/>
|
||||||
|
<move id="20" angle="-14.989797"/>
|
||||||
|
<move id="21" angle="-14.989797"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.08">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="1.0406766"/>
|
||||||
|
<move id="3" angle="1.0406766"/>
|
||||||
|
<move id="4" angle="-7.939904"/>
|
||||||
|
<move id="5" angle="-7.939904"/>
|
||||||
|
<move id="6" angle="260.59564"/>
|
||||||
|
<move id="7" angle="260.59564"/>
|
||||||
|
<move id="8" angle="-131.98882"/>
|
||||||
|
<move id="9" angle="-131.98882"/>
|
||||||
|
<move id="10" angle="123.69923"/>
|
||||||
|
<move id="11" angle="123.69923"/>
|
||||||
|
<move id="12" angle="-8.521371"/>
|
||||||
|
<move id="13" angle="-8.521371"/>
|
||||||
|
<move id="14" angle="3.173905"/>
|
||||||
|
<move id="15" angle="3.173905"/>
|
||||||
|
<move id="16" angle="-19.583878"/>
|
||||||
|
<move id="17" angle="-19.583878"/>
|
||||||
|
<move id="18" angle="-13.1733055"/>
|
||||||
|
<move id="19" angle="-13.1733055"/>
|
||||||
|
<move id="20" angle="-17.131882"/>
|
||||||
|
<move id="21" angle="-17.131882"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.06">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-19.195496"/>
|
||||||
|
<move id="3" angle="-19.195496"/>
|
||||||
|
<move id="4" angle="42.399166"/>
|
||||||
|
<move id="5" angle="42.399166"/>
|
||||||
|
<move id="6" angle="4.278442"/>
|
||||||
|
<move id="7" angle="4.278442"/>
|
||||||
|
<move id="8" angle="-93.31827"/>
|
||||||
|
<move id="9" angle="-93.31827"/>
|
||||||
|
<move id="10" angle="2.5259771"/>
|
||||||
|
<move id="11" angle="2.5259771"/>
|
||||||
|
<move id="12" angle="-33.76639"/>
|
||||||
|
<move id="13" angle="-33.76639"/>
|
||||||
|
<move id="14" angle="23.958351"/>
|
||||||
|
<move id="15" angle="23.958351"/>
|
||||||
|
<move id="16" angle="-7.903243"/>
|
||||||
|
<move id="17" angle="-7.903243"/>
|
||||||
|
<move id="18" angle="-18.376505"/>
|
||||||
|
<move id="19" angle="-18.376505"/>
|
||||||
|
<move id="20" angle="-20.77221"/>
|
||||||
|
<move id="21" angle="-20.77221"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-87.61395"/>
|
||||||
|
<move id="3" angle="-87.61395"/>
|
||||||
|
<move id="4" angle="-3.732545"/>
|
||||||
|
<move id="5" angle="-3.732545"/>
|
||||||
|
<move id="6" angle="-3.1527936"/>
|
||||||
|
<move id="7" angle="-3.1527936"/>
|
||||||
|
<move id="8" angle="-97.287674"/>
|
||||||
|
<move id="9" angle="-97.287674"/>
|
||||||
|
<move id="10" angle="-46.131386"/>
|
||||||
|
<move id="11" angle="-46.131386"/>
|
||||||
|
<move id="12" angle="-25.069555"/>
|
||||||
|
<move id="13" angle="-25.069555"/>
|
||||||
|
<move id="14" angle="-7.6755543"/>
|
||||||
|
<move id="15" angle="-7.6755543"/>
|
||||||
|
<move id="16" angle="-9.066617"/>
|
||||||
|
<move id="17" angle="-9.066617"/>
|
||||||
|
<move id="18" angle="9.437028"/>
|
||||||
|
<move id="19" angle="9.437028"/>
|
||||||
|
<move id="20" angle="0.19799754"/>
|
||||||
|
<move id="21" angle="0.19799754"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-4.7361717"/>
|
||||||
|
<move id="3" angle="-4.7361717"/>
|
||||||
|
<move id="4" angle="4.523938"/>
|
||||||
|
<move id="5" angle="4.523938"/>
|
||||||
|
<move id="6" angle="1.5581197"/>
|
||||||
|
<move id="7" angle="1.5581197"/>
|
||||||
|
<move id="8" angle="0.91717005"/>
|
||||||
|
<move id="9" angle="0.91717005"/>
|
||||||
|
<move id="10" angle="-0.73723245"/>
|
||||||
|
<move id="11" angle="-0.73723245"/>
|
||||||
|
<move id="12" angle="7.8827386"/>
|
||||||
|
<move id="13" angle="7.8827386"/>
|
||||||
|
<move id="14" angle="42.908276"/>
|
||||||
|
<move id="15" angle="42.908276"/>
|
||||||
|
<move id="16" angle="6.8883567"/>
|
||||||
|
<move id="17" angle="6.8883567"/>
|
||||||
|
<move id="18" angle="4.924833"/>
|
||||||
|
<move id="19" angle="4.924833"/>
|
||||||
|
<move id="20" angle="-0.006686151"/>
|
||||||
|
<move id="21" angle="-0.006686151"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-62.821877"/>
|
||||||
|
<move id="3" angle="-62.821877"/>
|
||||||
|
<move id="4" angle="1.0078714"/>
|
||||||
|
<move id="5" angle="1.0078714"/>
|
||||||
|
<move id="6" angle="1.994555"/>
|
||||||
|
<move id="7" angle="1.994555"/>
|
||||||
|
<move id="8" angle="-124.40493"/>
|
||||||
|
<move id="9" angle="-124.40493"/>
|
||||||
|
<move id="10" angle="46.153664"/>
|
||||||
|
<move id="11" angle="46.153664"/>
|
||||||
|
<move id="12" angle="30.022522"/>
|
||||||
|
<move id="13" angle="30.022522"/>
|
||||||
|
<move id="14" angle="3.9094872"/>
|
||||||
|
<move id="15" angle="3.9094872"/>
|
||||||
|
<move id="16" angle="12.2626095"/>
|
||||||
|
<move id="17" angle="12.2626095"/>
|
||||||
|
<move id="18" angle="12.774431"/>
|
||||||
|
<move id="19" angle="12.774431"/>
|
||||||
|
<move id="20" angle="10.435047"/>
|
||||||
|
<move id="21" angle="10.435047"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.06">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-55.784325"/>
|
||||||
|
<move id="3" angle="-55.784325"/>
|
||||||
|
<move id="4" angle="47.548805"/>
|
||||||
|
<move id="5" angle="47.548805"/>
|
||||||
|
<move id="6" angle="101.01688"/>
|
||||||
|
<move id="7" angle="101.01688"/>
|
||||||
|
<move id="8" angle="-122.21617"/>
|
||||||
|
<move id="9" angle="-122.21617"/>
|
||||||
|
<move id="10" angle="46.225307"/>
|
||||||
|
<move id="11" angle="46.225307"/>
|
||||||
|
<move id="12" angle="-56.009483"/>
|
||||||
|
<move id="13" angle="-56.009483"/>
|
||||||
|
<move id="14" angle="16.3225"/>
|
||||||
|
<move id="15" angle="16.3225"/>
|
||||||
|
<move id="16" angle="4.7965903"/>
|
||||||
|
<move id="17" angle="4.7965903"/>
|
||||||
|
<move id="18" angle="-25.914183"/>
|
||||||
|
<move id="19" angle="-25.914183"/>
|
||||||
|
<move id="20" angle="2.689302"/>
|
||||||
|
<move id="21" angle="2.689302"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.14">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-58.72785"/>
|
||||||
|
<move id="3" angle="-58.72785"/>
|
||||||
|
<move id="4" angle="43.85045"/>
|
||||||
|
<move id="5" angle="43.85045"/>
|
||||||
|
<move id="6" angle="108.34738"/>
|
||||||
|
<move id="7" angle="108.34738"/>
|
||||||
|
<move id="8" angle="-131.66202"/>
|
||||||
|
<move id="9" angle="-131.66202"/>
|
||||||
|
<move id="10" angle="44.46511"/>
|
||||||
|
<move id="11" angle="44.46511"/>
|
||||||
|
<move id="12" angle="-53.461926"/>
|
||||||
|
<move id="13" angle="-53.461926"/>
|
||||||
|
<move id="14" angle="-17.58822"/>
|
||||||
|
<move id="15" angle="-17.58822"/>
|
||||||
|
<move id="16" angle="-16.696602"/>
|
||||||
|
<move id="17" angle="-16.696602"/>
|
||||||
|
<move id="18" angle="0.24892278"/>
|
||||||
|
<move id="19" angle="0.24892278"/>
|
||||||
|
<move id="20" angle="14.703634"/>
|
||||||
|
<move id="21" angle="14.703634"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.42">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-1.136161"/>
|
||||||
|
<move id="3" angle="-1.136161"/>
|
||||||
|
<move id="4" angle="28.925074"/>
|
||||||
|
<move id="5" angle="28.925074"/>
|
||||||
|
<move id="6" angle="35.894753"/>
|
||||||
|
<move id="7" angle="35.894753"/>
|
||||||
|
<move id="8" angle="-94.40036"/>
|
||||||
|
<move id="9" angle="-94.40036"/>
|
||||||
|
<move id="10" angle="57.54773"/>
|
||||||
|
<move id="11" angle="57.54773"/>
|
||||||
|
<move id="12" angle="-4.952044"/>
|
||||||
|
<move id="13" angle="-4.952044"/>
|
||||||
|
<move id="14" angle="-123.39889"/>
|
||||||
|
<move id="15" angle="-123.39889"/>
|
||||||
|
<move id="16" angle="18.326159"/>
|
||||||
|
<move id="17" angle="18.326159"/>
|
||||||
|
<move id="18" angle="65.677376"/>
|
||||||
|
<move id="19" angle="65.677376"/>
|
||||||
|
<move id="20" angle="-21.766216"/>
|
||||||
|
<move id="21" angle="-21.766216"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Kick motion with right leg" auto_head="1">
|
||||||
|
<slot delta="0.22"> <!-- Lean -->
|
||||||
|
<move id="5" angle="-10"/> <!-- Left leg roll -->
|
||||||
|
<move id="6" angle="40"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="65"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="-60"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="-115"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="60"/> <!-- Left foot pitch -->
|
||||||
|
<move id="11" angle="25"/> <!-- Right foot pitch -->
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.12"> <!-- Kick -->
|
||||||
|
<move id="3" angle="-45"/> <!-- Right leg yaw/pitch -->
|
||||||
|
<move id="6" angle="-25"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="80"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="0"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="0"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="30"/> <!-- Left foot pitch -->
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,148 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Get up from robot's back starting from Zero pose" auto_head="0">
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-38.572456"/>
|
||||||
|
<move id="3" angle="-38.572456"/>
|
||||||
|
<move id="4" angle="10.999639"/>
|
||||||
|
<move id="5" angle="10.999639"/>
|
||||||
|
<move id="6" angle="77.6285"/>
|
||||||
|
<move id="7" angle="77.6285"/>
|
||||||
|
<move id="8" angle="-15.158332"/>
|
||||||
|
<move id="9" angle="-15.158332"/>
|
||||||
|
<move id="10" angle="-35.486084"/>
|
||||||
|
<move id="11" angle="-35.486084"/>
|
||||||
|
<move id="12" angle="2.6835575"/>
|
||||||
|
<move id="13" angle="2.6835575"/>
|
||||||
|
<move id="14" angle="73.84404"/>
|
||||||
|
<move id="15" angle="73.84404"/>
|
||||||
|
<move id="16" angle="-54.934566"/>
|
||||||
|
<move id="17" angle="-54.934566"/>
|
||||||
|
<move id="18" angle="-113.41597"/>
|
||||||
|
<move id="19" angle="-113.41597"/>
|
||||||
|
<move id="20" angle="78.12353"/>
|
||||||
|
<move id="21" angle="78.12353"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.24">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-65.98044"/>
|
||||||
|
<move id="3" angle="-65.98044"/>
|
||||||
|
<move id="4" angle="-50.182472"/>
|
||||||
|
<move id="5" angle="-50.182472"/>
|
||||||
|
<move id="6" angle="30.041952"/>
|
||||||
|
<move id="7" angle="30.041952"/>
|
||||||
|
<move id="8" angle="34.80442"/>
|
||||||
|
<move id="9" angle="34.80442"/>
|
||||||
|
<move id="10" angle="49.432697"/>
|
||||||
|
<move id="11" angle="49.432697"/>
|
||||||
|
<move id="12" angle="-77.979546"/>
|
||||||
|
<move id="13" angle="-77.979546"/>
|
||||||
|
<move id="14" angle="-0.92560226"/>
|
||||||
|
<move id="15" angle="-0.92560226"/>
|
||||||
|
<move id="16" angle="52.747627"/>
|
||||||
|
<move id="17" angle="52.747627"/>
|
||||||
|
<move id="18" angle="127.680466"/>
|
||||||
|
<move id="19" angle="127.680466"/>
|
||||||
|
<move id="20" angle="57.550724"/>
|
||||||
|
<move id="21" angle="57.550724"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.32">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-62.44547"/>
|
||||||
|
<move id="3" angle="-62.44547"/>
|
||||||
|
<move id="4" angle="9.1681"/>
|
||||||
|
<move id="5" angle="9.1681"/>
|
||||||
|
<move id="6" angle="134.78099"/>
|
||||||
|
<move id="7" angle="134.78099"/>
|
||||||
|
<move id="8" angle="-27.68505"/>
|
||||||
|
<move id="9" angle="-27.68505"/>
|
||||||
|
<move id="10" angle="-115.14692"/>
|
||||||
|
<move id="11" angle="-115.14692"/>
|
||||||
|
<move id="12" angle="-44.91786"/>
|
||||||
|
<move id="13" angle="-44.91786"/>
|
||||||
|
<move id="14" angle="56.92144"/>
|
||||||
|
<move id="15" angle="56.92144"/>
|
||||||
|
<move id="16" angle="-52.54726"/>
|
||||||
|
<move id="17" angle="-52.54726"/>
|
||||||
|
<move id="18" angle="-54.15026"/>
|
||||||
|
<move id="19" angle="-54.15026"/>
|
||||||
|
<move id="20" angle="-9.677674"/>
|
||||||
|
<move id="21" angle="-9.677674"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-58.012352"/>
|
||||||
|
<move id="3" angle="-58.012352"/>
|
||||||
|
<move id="4" angle="43.231464"/>
|
||||||
|
<move id="5" angle="43.231464"/>
|
||||||
|
<move id="6" angle="-44.049873"/>
|
||||||
|
<move id="7" angle="-44.049873"/>
|
||||||
|
<move id="8" angle="-63.7723"/>
|
||||||
|
<move id="9" angle="-63.7723"/>
|
||||||
|
<move id="10" angle="-56.622414"/>
|
||||||
|
<move id="11" angle="-56.622414"/>
|
||||||
|
<move id="12" angle="-28.760904"/>
|
||||||
|
<move id="13" angle="-28.760904"/>
|
||||||
|
<move id="14" angle="-14.939745"/>
|
||||||
|
<move id="15" angle="-14.939745"/>
|
||||||
|
<move id="16" angle="23.633577"/>
|
||||||
|
<move id="17" angle="23.633577"/>
|
||||||
|
<move id="18" angle="-85.10401"/>
|
||||||
|
<move id="19" angle="-85.10401"/>
|
||||||
|
<move id="20" angle="17.66445"/>
|
||||||
|
<move id="21" angle="17.66445"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.06">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-24.942818"/>
|
||||||
|
<move id="3" angle="-24.942818"/>
|
||||||
|
<move id="4" angle="78.45739"/>
|
||||||
|
<move id="5" angle="78.45739"/>
|
||||||
|
<move id="6" angle="106.30913"/>
|
||||||
|
<move id="7" angle="106.30913"/>
|
||||||
|
<move id="8" angle="-152.12027"/>
|
||||||
|
<move id="9" angle="-152.12027"/>
|
||||||
|
<move id="10" angle="42.071213"/>
|
||||||
|
<move id="11" angle="42.071213"/>
|
||||||
|
<move id="12" angle="-65.846405"/>
|
||||||
|
<move id="13" angle="-65.846405"/>
|
||||||
|
<move id="14" angle="-57.11586"/>
|
||||||
|
<move id="15" angle="-57.11586"/>
|
||||||
|
<move id="16" angle="-81.987076"/>
|
||||||
|
<move id="17" angle="-81.987076"/>
|
||||||
|
<move id="18" angle="11.453229"/>
|
||||||
|
<move id="19" angle="11.453229"/>
|
||||||
|
<move id="20" angle="-39.00894"/>
|
||||||
|
<move id="21" angle="-39.00894"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.18">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-0.6814623"/>
|
||||||
|
<move id="3" angle="-0.6814623"/>
|
||||||
|
<move id="4" angle="35.28643"/>
|
||||||
|
<move id="5" angle="35.28643"/>
|
||||||
|
<move id="6" angle="75.47946"/>
|
||||||
|
<move id="7" angle="75.47946"/>
|
||||||
|
<move id="8" angle="-132.41306"/>
|
||||||
|
<move id="9" angle="-132.41306"/>
|
||||||
|
<move id="10" angle="46.746563"/>
|
||||||
|
<move id="11" angle="46.746563"/>
|
||||||
|
<move id="12" angle="-8.374908"/>
|
||||||
|
<move id="13" angle="-8.374908"/>
|
||||||
|
<move id="14" angle="-20.005192"/>
|
||||||
|
<move id="15" angle="-20.005192"/>
|
||||||
|
<move id="16" angle="9.418695"/>
|
||||||
|
<move id="17" angle="9.418695"/>
|
||||||
|
<move id="18" angle="186.1502"/>
|
||||||
|
<move id="19" angle="186.1502"/>
|
||||||
|
<move id="20" angle="133.16562"/>
|
||||||
|
<move id="21" angle="133.16562"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,220 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Get up from robot's front starting from Zero pose" auto_head="0">
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-12.640889"/>
|
||||||
|
<move id="3" angle="-12.640889"/>
|
||||||
|
<move id="4" angle="4.646634"/>
|
||||||
|
<move id="5" angle="4.646634"/>
|
||||||
|
<move id="6" angle="17.342707"/>
|
||||||
|
<move id="7" angle="17.342707"/>
|
||||||
|
<move id="8" angle="3.5387506"/>
|
||||||
|
<move id="9" angle="3.5387506"/>
|
||||||
|
<move id="10" angle="3.3997679"/>
|
||||||
|
<move id="11" angle="3.3997679"/>
|
||||||
|
<move id="12" angle="0.6578274"/>
|
||||||
|
<move id="13" angle="0.6578274"/>
|
||||||
|
<move id="14" angle="-8.817135"/>
|
||||||
|
<move id="15" angle="-8.817135"/>
|
||||||
|
<move id="16" angle="61.96675"/>
|
||||||
|
<move id="17" angle="61.96675"/>
|
||||||
|
<move id="18" angle="1.5812168"/>
|
||||||
|
<move id="19" angle="1.5812168"/>
|
||||||
|
<move id="20" angle="-8.415477"/>
|
||||||
|
<move id="21" angle="-8.415477"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.18">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="5.4146767"/>
|
||||||
|
<move id="3" angle="5.4146767"/>
|
||||||
|
<move id="4" angle="-11.824036"/>
|
||||||
|
<move id="5" angle="-11.824036"/>
|
||||||
|
<move id="6" angle="8.015917"/>
|
||||||
|
<move id="7" angle="8.015917"/>
|
||||||
|
<move id="8" angle="-1.4786508"/>
|
||||||
|
<move id="9" angle="-1.4786508"/>
|
||||||
|
<move id="10" angle="6.2315826"/>
|
||||||
|
<move id="11" angle="6.2315826"/>
|
||||||
|
<move id="12" angle="11.371917"/>
|
||||||
|
<move id="13" angle="11.371917"/>
|
||||||
|
<move id="14" angle="6.024707"/>
|
||||||
|
<move id="15" angle="6.024707"/>
|
||||||
|
<move id="16" angle="61.55641"/>
|
||||||
|
<move id="17" angle="61.55641"/>
|
||||||
|
<move id="18" angle="-6.719823"/>
|
||||||
|
<move id="19" angle="-6.719823"/>
|
||||||
|
<move id="20" angle="-19.866516"/>
|
||||||
|
<move id="21" angle="-19.866516"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.06">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-2.2857966"/>
|
||||||
|
<move id="3" angle="-2.2857966"/>
|
||||||
|
<move id="4" angle="-5.441041"/>
|
||||||
|
<move id="5" angle="-5.441041"/>
|
||||||
|
<move id="6" angle="270.0556"/>
|
||||||
|
<move id="7" angle="270.0556"/>
|
||||||
|
<move id="8" angle="-127.3845"/>
|
||||||
|
<move id="9" angle="-127.3845"/>
|
||||||
|
<move id="10" angle="123.612"/>
|
||||||
|
<move id="11" angle="123.612"/>
|
||||||
|
<move id="12" angle="-6.389326"/>
|
||||||
|
<move id="13" angle="-6.389326"/>
|
||||||
|
<move id="14" angle="-2.5006394"/>
|
||||||
|
<move id="15" angle="-2.5006394"/>
|
||||||
|
<move id="16" angle="-8.59928"/>
|
||||||
|
<move id="17" angle="-8.59928"/>
|
||||||
|
<move id="18" angle="-10.413264"/>
|
||||||
|
<move id="19" angle="-10.413264"/>
|
||||||
|
<move id="20" angle="-10.369881"/>
|
||||||
|
<move id="21" angle="-10.369881"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.06">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-15.020902"/>
|
||||||
|
<move id="3" angle="-15.020902"/>
|
||||||
|
<move id="4" angle="39.801743"/>
|
||||||
|
<move id="5" angle="39.801743"/>
|
||||||
|
<move id="6" angle="7.683368"/>
|
||||||
|
<move id="7" angle="7.683368"/>
|
||||||
|
<move id="8" angle="-96.00759"/>
|
||||||
|
<move id="9" angle="-96.00759"/>
|
||||||
|
<move id="10" angle="2.5151913"/>
|
||||||
|
<move id="11" angle="2.5151913"/>
|
||||||
|
<move id="12" angle="-44.600082"/>
|
||||||
|
<move id="13" angle="-44.600082"/>
|
||||||
|
<move id="14" angle="28.740438"/>
|
||||||
|
<move id="15" angle="28.740438"/>
|
||||||
|
<move id="16" angle="-7.8307633"/>
|
||||||
|
<move id="17" angle="-7.8307633"/>
|
||||||
|
<move id="18" angle="-25.14883"/>
|
||||||
|
<move id="19" angle="-25.14883"/>
|
||||||
|
<move id="20" angle="-21.899431"/>
|
||||||
|
<move id="21" angle="-21.899431"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-91.19676"/>
|
||||||
|
<move id="3" angle="-91.19676"/>
|
||||||
|
<move id="4" angle="-1.6109102"/>
|
||||||
|
<move id="5" angle="-1.6109102"/>
|
||||||
|
<move id="6" angle="-3.0909305"/>
|
||||||
|
<move id="7" angle="-3.0909305"/>
|
||||||
|
<move id="8" angle="-97.02908"/>
|
||||||
|
<move id="9" angle="-97.02908"/>
|
||||||
|
<move id="10" angle="-44.9949"/>
|
||||||
|
<move id="11" angle="-44.9949"/>
|
||||||
|
<move id="12" angle="-24.864588"/>
|
||||||
|
<move id="13" angle="-24.864588"/>
|
||||||
|
<move id="14" angle="-10.729849"/>
|
||||||
|
<move id="15" angle="-10.729849"/>
|
||||||
|
<move id="16" angle="-12.871218"/>
|
||||||
|
<move id="17" angle="-12.871218"/>
|
||||||
|
<move id="18" angle="10.0304985"/>
|
||||||
|
<move id="19" angle="10.0304985"/>
|
||||||
|
<move id="20" angle="10.550584"/>
|
||||||
|
<move id="21" angle="10.550584"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-3.6664605"/>
|
||||||
|
<move id="3" angle="-3.6664605"/>
|
||||||
|
<move id="4" angle="2.7512116"/>
|
||||||
|
<move id="5" angle="2.7512116"/>
|
||||||
|
<move id="6" angle="4.2976775"/>
|
||||||
|
<move id="7" angle="4.2976775"/>
|
||||||
|
<move id="8" angle="-1.8928692"/>
|
||||||
|
<move id="9" angle="-1.8928692"/>
|
||||||
|
<move id="10" angle="-1.3523235"/>
|
||||||
|
<move id="11" angle="-1.3523235"/>
|
||||||
|
<move id="12" angle="-4.7275023"/>
|
||||||
|
<move id="13" angle="-4.7275023"/>
|
||||||
|
<move id="14" angle="44.758335"/>
|
||||||
|
<move id="15" angle="44.758335"/>
|
||||||
|
<move id="16" angle="17.067863"/>
|
||||||
|
<move id="17" angle="17.067863"/>
|
||||||
|
<move id="18" angle="-7.7519336"/>
|
||||||
|
<move id="19" angle="-7.7519336"/>
|
||||||
|
<move id="20" angle="-4.652607"/>
|
||||||
|
<move id="21" angle="-4.652607"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.04">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-59.41754"/>
|
||||||
|
<move id="3" angle="-59.41754"/>
|
||||||
|
<move id="4" angle="49.316"/>
|
||||||
|
<move id="5" angle="49.316"/>
|
||||||
|
<move id="6" angle="100.66388"/>
|
||||||
|
<move id="7" angle="100.66388"/>
|
||||||
|
<move id="8" angle="-121.67775"/>
|
||||||
|
<move id="9" angle="-121.67775"/>
|
||||||
|
<move id="10" angle="46.480865"/>
|
||||||
|
<move id="11" angle="46.480865"/>
|
||||||
|
<move id="12" angle="-57.7324"/>
|
||||||
|
<move id="13" angle="-57.7324"/>
|
||||||
|
<move id="14" angle="5.5761175"/>
|
||||||
|
<move id="15" angle="5.5761175"/>
|
||||||
|
<move id="16" angle="18.378542"/>
|
||||||
|
<move id="17" angle="18.378542"/>
|
||||||
|
<move id="18" angle="-27.80392"/>
|
||||||
|
<move id="19" angle="-27.80392"/>
|
||||||
|
<move id="20" angle="10.984678"/>
|
||||||
|
<move id="21" angle="10.984678"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.12">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-47.20175"/>
|
||||||
|
<move id="3" angle="-47.20175"/>
|
||||||
|
<move id="4" angle="47.4648"/>
|
||||||
|
<move id="5" angle="47.4648"/>
|
||||||
|
<move id="6" angle="115.67783"/>
|
||||||
|
<move id="7" angle="115.67783"/>
|
||||||
|
<move id="8" angle="-135.32626"/>
|
||||||
|
<move id="9" angle="-135.32626"/>
|
||||||
|
<move id="10" angle="42.217873"/>
|
||||||
|
<move id="11" angle="42.217873"/>
|
||||||
|
<move id="12" angle="-65.463715"/>
|
||||||
|
<move id="13" angle="-65.463715"/>
|
||||||
|
<move id="14" angle="-19.853582"/>
|
||||||
|
<move id="15" angle="-19.853582"/>
|
||||||
|
<move id="16" angle="-26.5262"/>
|
||||||
|
<move id="17" angle="-26.5262"/>
|
||||||
|
<move id="18" angle="17.335098"/>
|
||||||
|
<move id="19" angle="17.335098"/>
|
||||||
|
<move id="20" angle="7.6011276"/>
|
||||||
|
<move id="21" angle="7.6011276"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.4">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-1.6223584"/>
|
||||||
|
<move id="3" angle="-1.6223584"/>
|
||||||
|
<move id="4" angle="24.222506"/>
|
||||||
|
<move id="5" angle="24.222506"/>
|
||||||
|
<move id="6" angle="38.50319"/>
|
||||||
|
<move id="7" angle="38.50319"/>
|
||||||
|
<move id="8" angle="-93.59837"/>
|
||||||
|
<move id="9" angle="-93.59837"/>
|
||||||
|
<move id="10" angle="53.80671"/>
|
||||||
|
<move id="11" angle="53.80671"/>
|
||||||
|
<move id="12" angle="-2.3789244"/>
|
||||||
|
<move id="13" angle="-2.3789244"/>
|
||||||
|
<move id="14" angle="-119.351814"/>
|
||||||
|
<move id="15" angle="-119.351814"/>
|
||||||
|
<move id="16" angle="0.0"/>
|
||||||
|
<move id="17" angle="0.0"/>
|
||||||
|
<move id="18" angle="67.36445"/>
|
||||||
|
<move id="19" angle="67.36445"/>
|
||||||
|
<move id="20" angle="90"/>
|
||||||
|
<move id="21" angle="90"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Kick motion with right leg" auto_head="1">
|
||||||
|
<slot delta="0.22"> <!-- Lean -->
|
||||||
|
<move id="5" angle="-10"/> <!-- Left leg roll -->
|
||||||
|
<move id="6" angle="40"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="65"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="-60"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="-115"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="60"/> <!-- Left foot pitch -->
|
||||||
|
<move id="11" angle="-5"/> <!-- Right foot pitch -->
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.12"> <!-- Kick -->
|
||||||
|
<move id="3" angle="-45"/> <!-- Right leg yaw/pitch -->
|
||||||
|
<move id="6" angle="-25"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="80"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="0"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="0"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="30"/> <!-- Left foot pitch -->
|
||||||
|
<move id="17" angle="40"/> <!-- Right arm roll -->
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,148 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Get up from robot's back starting from Zero pose" auto_head="0">
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-48.405083"/>
|
||||||
|
<move id="3" angle="-48.405083"/>
|
||||||
|
<move id="4" angle="33.774643"/>
|
||||||
|
<move id="5" angle="33.774643"/>
|
||||||
|
<move id="6" angle="100.784706"/>
|
||||||
|
<move id="7" angle="100.784706"/>
|
||||||
|
<move id="8" angle="7.756978"/>
|
||||||
|
<move id="9" angle="7.756978"/>
|
||||||
|
<move id="10" angle="-23.10689"/>
|
||||||
|
<move id="11" angle="-23.10689"/>
|
||||||
|
<move id="12" angle="85.49136"/>
|
||||||
|
<move id="13" angle="85.49136"/>
|
||||||
|
<move id="14" angle="38.31756"/>
|
||||||
|
<move id="15" angle="38.31756"/>
|
||||||
|
<move id="16" angle="-67.331825"/>
|
||||||
|
<move id="17" angle="-67.331825"/>
|
||||||
|
<move id="18" angle="22.26954"/>
|
||||||
|
<move id="19" angle="22.26954"/>
|
||||||
|
<move id="20" angle="74.803024"/>
|
||||||
|
<move id="21" angle="74.803024"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.23336456513404846">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-70.5122"/>
|
||||||
|
<move id="3" angle="-70.5122"/>
|
||||||
|
<move id="4" angle="-33.731937"/>
|
||||||
|
<move id="5" angle="-33.731937"/>
|
||||||
|
<move id="6" angle="24.08416"/>
|
||||||
|
<move id="7" angle="24.08416"/>
|
||||||
|
<move id="8" angle="43.438618"/>
|
||||||
|
<move id="9" angle="43.438618"/>
|
||||||
|
<move id="10" angle="58.60381"/>
|
||||||
|
<move id="11" angle="58.60381"/>
|
||||||
|
<move id="12" angle="-85.92165"/>
|
||||||
|
<move id="13" angle="-85.92165"/>
|
||||||
|
<move id="14" angle="5.59869"/>
|
||||||
|
<move id="15" angle="5.59869"/>
|
||||||
|
<move id="16" angle="122.71095"/>
|
||||||
|
<move id="17" angle="122.71095"/>
|
||||||
|
<move id="18" angle="55.634182"/>
|
||||||
|
<move id="19" angle="55.634182"/>
|
||||||
|
<move id="20" angle="85.27185"/>
|
||||||
|
<move id="21" angle="85.27185"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.33675475766658775">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-38.059784"/>
|
||||||
|
<move id="3" angle="-38.059784"/>
|
||||||
|
<move id="4" angle="46.120163"/>
|
||||||
|
<move id="5" angle="46.120163"/>
|
||||||
|
<move id="6" angle="120.91053"/>
|
||||||
|
<move id="7" angle="120.91053"/>
|
||||||
|
<move id="8" angle="-37.19999"/>
|
||||||
|
<move id="9" angle="-37.19999"/>
|
||||||
|
<move id="10" angle="-60.143646"/>
|
||||||
|
<move id="11" angle="-60.143646"/>
|
||||||
|
<move id="12" angle="-81.65832"/>
|
||||||
|
<move id="13" angle="-81.65832"/>
|
||||||
|
<move id="14" angle="35.256054"/>
|
||||||
|
<move id="15" angle="35.256054"/>
|
||||||
|
<move id="16" angle="-17.517155"/>
|
||||||
|
<move id="17" angle="-17.517155"/>
|
||||||
|
<move id="18" angle="18.431877"/>
|
||||||
|
<move id="19" angle="18.431877"/>
|
||||||
|
<move id="20" angle="-25.010399"/>
|
||||||
|
<move id="21" angle="-25.010399"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-69.23109"/>
|
||||||
|
<move id="3" angle="-69.23109"/>
|
||||||
|
<move id="4" angle="15.320035"/>
|
||||||
|
<move id="5" angle="15.320035"/>
|
||||||
|
<move id="6" angle="30.714127"/>
|
||||||
|
<move id="7" angle="30.714127"/>
|
||||||
|
<move id="8" angle="-47.507515"/>
|
||||||
|
<move id="9" angle="-47.507515"/>
|
||||||
|
<move id="10" angle="-61.518223"/>
|
||||||
|
<move id="11" angle="-61.518223"/>
|
||||||
|
<move id="12" angle="-27.325377"/>
|
||||||
|
<move id="13" angle="-27.325377"/>
|
||||||
|
<move id="14" angle="45.20161"/>
|
||||||
|
<move id="15" angle="45.20161"/>
|
||||||
|
<move id="16" angle="71.49224"/>
|
||||||
|
<move id="17" angle="71.49224"/>
|
||||||
|
<move id="18" angle="37.682297"/>
|
||||||
|
<move id="19" angle="37.682297"/>
|
||||||
|
<move id="20" angle="10.35512"/>
|
||||||
|
<move id="21" angle="10.35512"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.05201605577468872">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-28.16217"/>
|
||||||
|
<move id="3" angle="-28.16217"/>
|
||||||
|
<move id="4" angle="52.758217"/>
|
||||||
|
<move id="5" angle="52.758217"/>
|
||||||
|
<move id="6" angle="111.2359"/>
|
||||||
|
<move id="7" angle="111.2359"/>
|
||||||
|
<move id="8" angle="-118.02957"/>
|
||||||
|
<move id="9" angle="-118.02957"/>
|
||||||
|
<move id="10" angle="54.038685"/>
|
||||||
|
<move id="11" angle="54.038685"/>
|
||||||
|
<move id="12" angle="-123.210915"/>
|
||||||
|
<move id="13" angle="-123.210915"/>
|
||||||
|
<move id="14" angle="-26.220789"/>
|
||||||
|
<move id="15" angle="-26.220789"/>
|
||||||
|
<move id="16" angle="28.817947"/>
|
||||||
|
<move id="17" angle="28.817947"/>
|
||||||
|
<move id="18" angle="-14.253399"/>
|
||||||
|
<move id="19" angle="-14.253399"/>
|
||||||
|
<move id="20" angle="17.878008"/>
|
||||||
|
<move id="21" angle="17.878008"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.15276842055320738">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-5.5297728"/>
|
||||||
|
<move id="3" angle="-5.5297728"/>
|
||||||
|
<move id="4" angle="90.15973"/>
|
||||||
|
<move id="5" angle="90.15973"/>
|
||||||
|
<move id="6" angle="50.418648"/>
|
||||||
|
<move id="7" angle="50.418648"/>
|
||||||
|
<move id="8" angle="-90.911835"/>
|
||||||
|
<move id="9" angle="-90.911835"/>
|
||||||
|
<move id="10" angle="50.15513"/>
|
||||||
|
<move id="11" angle="50.15513"/>
|
||||||
|
<move id="12" angle="-45.83729"/>
|
||||||
|
<move id="13" angle="-45.83729"/>
|
||||||
|
<move id="14" angle="-39.350525"/>
|
||||||
|
<move id="15" angle="-39.350525"/>
|
||||||
|
<move id="16" angle="-23.518953"/>
|
||||||
|
<move id="17" angle="-23.518953"/>
|
||||||
|
<move id="18" angle="72.38919"/>
|
||||||
|
<move id="19" angle="72.38919"/>
|
||||||
|
<move id="20" angle="-77.339966"/>
|
||||||
|
<move id="21" angle="-77.339966"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,220 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Get up from robot's front starting from Zero pose" auto_head="0">
|
||||||
|
<slot delta="0.16">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="4.6313915"/>
|
||||||
|
<move id="3" angle="4.6313915"/>
|
||||||
|
<move id="4" angle="-11.846246"/>
|
||||||
|
<move id="5" angle="-11.846246"/>
|
||||||
|
<move id="6" angle="8.745557"/>
|
||||||
|
<move id="7" angle="8.745557"/>
|
||||||
|
<move id="8" angle="-1.8197118"/>
|
||||||
|
<move id="9" angle="-1.8197118"/>
|
||||||
|
<move id="10" angle="1.359597"/>
|
||||||
|
<move id="11" angle="1.359597"/>
|
||||||
|
<move id="12" angle="16.82137"/>
|
||||||
|
<move id="13" angle="16.82137"/>
|
||||||
|
<move id="14" angle="21.83775"/>
|
||||||
|
<move id="15" angle="21.83775"/>
|
||||||
|
<move id="16" angle="-27.174564"/>
|
||||||
|
<move id="17" angle="-27.174564"/>
|
||||||
|
<move id="18" angle="-12.889872"/>
|
||||||
|
<move id="19" angle="-12.889872"/>
|
||||||
|
<move id="20" angle="-14.989797"/>
|
||||||
|
<move id="21" angle="-14.989797"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.08">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="1.0406766"/>
|
||||||
|
<move id="3" angle="1.0406766"/>
|
||||||
|
<move id="4" angle="-7.939904"/>
|
||||||
|
<move id="5" angle="-7.939904"/>
|
||||||
|
<move id="6" angle="260.59564"/>
|
||||||
|
<move id="7" angle="260.59564"/>
|
||||||
|
<move id="8" angle="-131.98882"/>
|
||||||
|
<move id="9" angle="-131.98882"/>
|
||||||
|
<move id="10" angle="123.69923"/>
|
||||||
|
<move id="11" angle="123.69923"/>
|
||||||
|
<move id="12" angle="-8.521371"/>
|
||||||
|
<move id="13" angle="-8.521371"/>
|
||||||
|
<move id="14" angle="3.173905"/>
|
||||||
|
<move id="15" angle="3.173905"/>
|
||||||
|
<move id="16" angle="-19.583878"/>
|
||||||
|
<move id="17" angle="-19.583878"/>
|
||||||
|
<move id="18" angle="-13.1733055"/>
|
||||||
|
<move id="19" angle="-13.1733055"/>
|
||||||
|
<move id="20" angle="-17.131882"/>
|
||||||
|
<move id="21" angle="-17.131882"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.06">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-19.195496"/>
|
||||||
|
<move id="3" angle="-19.195496"/>
|
||||||
|
<move id="4" angle="42.399166"/>
|
||||||
|
<move id="5" angle="42.399166"/>
|
||||||
|
<move id="6" angle="4.278442"/>
|
||||||
|
<move id="7" angle="4.278442"/>
|
||||||
|
<move id="8" angle="-93.31827"/>
|
||||||
|
<move id="9" angle="-93.31827"/>
|
||||||
|
<move id="10" angle="2.5259771"/>
|
||||||
|
<move id="11" angle="2.5259771"/>
|
||||||
|
<move id="12" angle="-33.76639"/>
|
||||||
|
<move id="13" angle="-33.76639"/>
|
||||||
|
<move id="14" angle="23.958351"/>
|
||||||
|
<move id="15" angle="23.958351"/>
|
||||||
|
<move id="16" angle="-7.903243"/>
|
||||||
|
<move id="17" angle="-7.903243"/>
|
||||||
|
<move id="18" angle="-18.376505"/>
|
||||||
|
<move id="19" angle="-18.376505"/>
|
||||||
|
<move id="20" angle="-20.77221"/>
|
||||||
|
<move id="21" angle="-20.77221"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-87.61395"/>
|
||||||
|
<move id="3" angle="-87.61395"/>
|
||||||
|
<move id="4" angle="-3.732545"/>
|
||||||
|
<move id="5" angle="-3.732545"/>
|
||||||
|
<move id="6" angle="-3.1527936"/>
|
||||||
|
<move id="7" angle="-3.1527936"/>
|
||||||
|
<move id="8" angle="-97.287674"/>
|
||||||
|
<move id="9" angle="-97.287674"/>
|
||||||
|
<move id="10" angle="-46.131386"/>
|
||||||
|
<move id="11" angle="-46.131386"/>
|
||||||
|
<move id="12" angle="-25.069555"/>
|
||||||
|
<move id="13" angle="-25.069555"/>
|
||||||
|
<move id="14" angle="-7.6755543"/>
|
||||||
|
<move id="15" angle="-7.6755543"/>
|
||||||
|
<move id="16" angle="-9.066617"/>
|
||||||
|
<move id="17" angle="-9.066617"/>
|
||||||
|
<move id="18" angle="9.437028"/>
|
||||||
|
<move id="19" angle="9.437028"/>
|
||||||
|
<move id="20" angle="0.19799754"/>
|
||||||
|
<move id="21" angle="0.19799754"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-4.7361717"/>
|
||||||
|
<move id="3" angle="-4.7361717"/>
|
||||||
|
<move id="4" angle="4.523938"/>
|
||||||
|
<move id="5" angle="4.523938"/>
|
||||||
|
<move id="6" angle="1.5581197"/>
|
||||||
|
<move id="7" angle="1.5581197"/>
|
||||||
|
<move id="8" angle="0.91717005"/>
|
||||||
|
<move id="9" angle="0.91717005"/>
|
||||||
|
<move id="10" angle="-0.73723245"/>
|
||||||
|
<move id="11" angle="-0.73723245"/>
|
||||||
|
<move id="12" angle="7.8827386"/>
|
||||||
|
<move id="13" angle="7.8827386"/>
|
||||||
|
<move id="14" angle="42.908276"/>
|
||||||
|
<move id="15" angle="42.908276"/>
|
||||||
|
<move id="16" angle="6.8883567"/>
|
||||||
|
<move id="17" angle="6.8883567"/>
|
||||||
|
<move id="18" angle="4.924833"/>
|
||||||
|
<move id="19" angle="4.924833"/>
|
||||||
|
<move id="20" angle="-0.006686151"/>
|
||||||
|
<move id="21" angle="-0.006686151"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.02">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-62.821877"/>
|
||||||
|
<move id="3" angle="-62.821877"/>
|
||||||
|
<move id="4" angle="1.0078714"/>
|
||||||
|
<move id="5" angle="1.0078714"/>
|
||||||
|
<move id="6" angle="1.994555"/>
|
||||||
|
<move id="7" angle="1.994555"/>
|
||||||
|
<move id="8" angle="-124.40493"/>
|
||||||
|
<move id="9" angle="-124.40493"/>
|
||||||
|
<move id="10" angle="46.153664"/>
|
||||||
|
<move id="11" angle="46.153664"/>
|
||||||
|
<move id="12" angle="30.022522"/>
|
||||||
|
<move id="13" angle="30.022522"/>
|
||||||
|
<move id="14" angle="3.9094872"/>
|
||||||
|
<move id="15" angle="3.9094872"/>
|
||||||
|
<move id="16" angle="12.2626095"/>
|
||||||
|
<move id="17" angle="12.2626095"/>
|
||||||
|
<move id="18" angle="12.774431"/>
|
||||||
|
<move id="19" angle="12.774431"/>
|
||||||
|
<move id="20" angle="10.435047"/>
|
||||||
|
<move id="21" angle="10.435047"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.06">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-55.784325"/>
|
||||||
|
<move id="3" angle="-55.784325"/>
|
||||||
|
<move id="4" angle="47.548805"/>
|
||||||
|
<move id="5" angle="47.548805"/>
|
||||||
|
<move id="6" angle="101.01688"/>
|
||||||
|
<move id="7" angle="101.01688"/>
|
||||||
|
<move id="8" angle="-122.21617"/>
|
||||||
|
<move id="9" angle="-122.21617"/>
|
||||||
|
<move id="10" angle="46.225307"/>
|
||||||
|
<move id="11" angle="46.225307"/>
|
||||||
|
<move id="12" angle="-56.009483"/>
|
||||||
|
<move id="13" angle="-56.009483"/>
|
||||||
|
<move id="14" angle="16.3225"/>
|
||||||
|
<move id="15" angle="16.3225"/>
|
||||||
|
<move id="16" angle="4.7965903"/>
|
||||||
|
<move id="17" angle="4.7965903"/>
|
||||||
|
<move id="18" angle="-25.914183"/>
|
||||||
|
<move id="19" angle="-25.914183"/>
|
||||||
|
<move id="20" angle="2.689302"/>
|
||||||
|
<move id="21" angle="2.689302"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.14">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-58.72785"/>
|
||||||
|
<move id="3" angle="-58.72785"/>
|
||||||
|
<move id="4" angle="43.85045"/>
|
||||||
|
<move id="5" angle="43.85045"/>
|
||||||
|
<move id="6" angle="108.34738"/>
|
||||||
|
<move id="7" angle="108.34738"/>
|
||||||
|
<move id="8" angle="-131.66202"/>
|
||||||
|
<move id="9" angle="-131.66202"/>
|
||||||
|
<move id="10" angle="44.46511"/>
|
||||||
|
<move id="11" angle="44.46511"/>
|
||||||
|
<move id="12" angle="-53.461926"/>
|
||||||
|
<move id="13" angle="-53.461926"/>
|
||||||
|
<move id="14" angle="-17.58822"/>
|
||||||
|
<move id="15" angle="-17.58822"/>
|
||||||
|
<move id="16" angle="-16.696602"/>
|
||||||
|
<move id="17" angle="-16.696602"/>
|
||||||
|
<move id="18" angle="0.24892278"/>
|
||||||
|
<move id="19" angle="0.24892278"/>
|
||||||
|
<move id="20" angle="14.703634"/>
|
||||||
|
<move id="21" angle="14.703634"/>
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.42">
|
||||||
|
<move id="0" angle="0.0"/>
|
||||||
|
<move id="1" angle="0.0"/>
|
||||||
|
<move id="2" angle="-1.136161"/>
|
||||||
|
<move id="3" angle="-1.136161"/>
|
||||||
|
<move id="4" angle="28.925074"/>
|
||||||
|
<move id="5" angle="28.925074"/>
|
||||||
|
<move id="6" angle="35.894753"/>
|
||||||
|
<move id="7" angle="35.894753"/>
|
||||||
|
<move id="8" angle="-94.40036"/>
|
||||||
|
<move id="9" angle="-94.40036"/>
|
||||||
|
<move id="10" angle="57.54773"/>
|
||||||
|
<move id="11" angle="57.54773"/>
|
||||||
|
<move id="12" angle="-4.952044"/>
|
||||||
|
<move id="13" angle="-4.952044"/>
|
||||||
|
<move id="14" angle="-123.39889"/>
|
||||||
|
<move id="15" angle="-123.39889"/>
|
||||||
|
<move id="16" angle="18.326159"/>
|
||||||
|
<move id="17" angle="18.326159"/>
|
||||||
|
<move id="18" angle="65.677376"/>
|
||||||
|
<move id="19" angle="65.677376"/>
|
||||||
|
<move id="20" angle="-21.766216"/>
|
||||||
|
<move id="21" angle="-21.766216"/>
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<behavior description="Kick motion with right leg" auto_head="1">
|
||||||
|
<slot delta="0.18"> <!-- Lean -->
|
||||||
|
<move id="5" angle="-5"/> <!-- Left leg roll -->
|
||||||
|
<move id="7" angle="65"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="-70"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="-130"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="60"/> <!-- Left foot pitch -->
|
||||||
|
<move id="11" angle="30"/> <!-- Right foot pitch -->
|
||||||
|
</slot>
|
||||||
|
<slot delta="0.1"> <!-- Kick -->
|
||||||
|
<move id="3" angle="-45"/> <!-- Right leg yaw/pitch -->
|
||||||
|
<move id="6" angle="-25"/> <!-- Left leg pitch -->
|
||||||
|
<move id="7" angle="100"/> <!-- Right leg pitch -->
|
||||||
|
<move id="8" angle="0"/> <!-- Left knee -->
|
||||||
|
<move id="9" angle="0"/> <!-- Right knee -->
|
||||||
|
<move id="10" angle="30"/> <!-- Left foot pitch -->
|
||||||
|
<move id="11" angle="25"/> <!-- Right foot pitch -->
|
||||||
|
</slot>
|
||||||
|
</behavior>
|
@ -0,0 +1,83 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Call this script from any directory
|
||||||
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||||
|
|
||||||
|
# cd to main folder
|
||||||
|
cd "${SCRIPT_DIR}/.."
|
||||||
|
|
||||||
|
rm -rf ./bundle/build
|
||||||
|
rm -rf ./bundle/dist
|
||||||
|
|
||||||
|
onefile="--onefile"
|
||||||
|
|
||||||
|
# bundle app, dependencies and data files into single executable
|
||||||
|
|
||||||
|
pyinstaller \
|
||||||
|
--add-data './world/commons/robots:world/commons/robots' \
|
||||||
|
--add-data './behaviors/slot/common:behaviors/slot/common' \
|
||||||
|
--add-data './behaviors/slot/r0:behaviors/slot/r0' \
|
||||||
|
--add-data './behaviors/slot/r1:behaviors/slot/r1' \
|
||||||
|
--add-data './behaviors/slot/r2:behaviors/slot/r2' \
|
||||||
|
--add-data './behaviors/slot/r3:behaviors/slot/r3' \
|
||||||
|
--add-data './behaviors/slot/r4:behaviors/slot/r4' \
|
||||||
|
--add-data './behaviors/custom/Dribble/*.pkl:behaviors/custom/Dribble' \
|
||||||
|
--add-data './behaviors/custom/Walk/*.pkl:behaviors/custom/Walk' \
|
||||||
|
--add-data './behaviors/custom/Fall/*.pkl:behaviors/custom/Fall' \
|
||||||
|
${onefile} --distpath ./bundle/dist/ --workpath ./bundle/build/ --noconfirm --name fcp Run_Player.py
|
||||||
|
|
||||||
|
# start.sh
|
||||||
|
|
||||||
|
cat > ./bundle/dist/start.sh << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
export OMP_NUM_THREADS=1
|
||||||
|
|
||||||
|
host=\${1:-localhost}
|
||||||
|
port=\${2:-3100}
|
||||||
|
|
||||||
|
for i in {1..11}; do
|
||||||
|
./fcp -i \$host -p \$port -u \$i -t FCPortugal &
|
||||||
|
done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# start_penalty.sh
|
||||||
|
|
||||||
|
cat > ./bundle/dist/start_penalty.sh << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
export OMP_NUM_THREADS=1
|
||||||
|
|
||||||
|
host=\${1:-localhost}
|
||||||
|
port=\${2:-3100}
|
||||||
|
|
||||||
|
./fcp -i \$host -p \$port -u 1 -t FCPortugal -P 1 &
|
||||||
|
./fcp -i \$host -p \$port -u 11 -t FCPortugal -P 1 &
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# start_fat_proxy.sh
|
||||||
|
|
||||||
|
cat > ./bundle/dist/start_fat_proxy.sh << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
export OMP_NUM_THREADS=1
|
||||||
|
|
||||||
|
host=\${1:-localhost}
|
||||||
|
port=\${2:-3100}
|
||||||
|
|
||||||
|
for i in {1..11}; do
|
||||||
|
./fcp -i \$host -p \$port -u \$i -t FCPortugal -F 1 &
|
||||||
|
done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# kill.sh
|
||||||
|
|
||||||
|
cat > ./bundle/dist/kill.sh << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
pkill -9 -e fcp
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# execution permission
|
||||||
|
|
||||||
|
chmod a+x ./bundle/dist/start.sh
|
||||||
|
chmod a+x ./bundle/dist/start_penalty.sh
|
||||||
|
chmod a+x ./bundle/dist/start_fat_proxy.sh
|
||||||
|
chmod a+x ./bundle/dist/kill.sh
|
||||||
|
|
@ -0,0 +1,306 @@
|
|||||||
|
from typing import List
|
||||||
|
from world.commons.Other_Robot import Other_Robot
|
||||||
|
from world.World import World
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class Radio():
|
||||||
|
'''
|
||||||
|
map limits are hardcoded:
|
||||||
|
teammates/opponents positions (x,y) in ([-16,16],[-11,11])
|
||||||
|
ball position (x,y) in ([-15,15],[-10,10])
|
||||||
|
known server limitations:
|
||||||
|
claimed: all ascii from 0x20 to 0x7E except ' ', '(', ')'
|
||||||
|
bugs:
|
||||||
|
- ' or " clip the message
|
||||||
|
- '\' at the end or near another '\'
|
||||||
|
- ';' at beginning of message
|
||||||
|
'''
|
||||||
|
# map limits are hardcoded:
|
||||||
|
|
||||||
|
# lines, columns, half lines index, half cols index, (lines-1)/x_span, (cols-1)/y_span, combinations, combinations*2states,
|
||||||
|
TP = 321,221,160,110,10, 10,70941,141882 # teammate position
|
||||||
|
OP = 201,111,100,55, 6.25,5, 22311,44622 # opponent position
|
||||||
|
BP = 301,201,150,100,10, 10,60501 # ball position
|
||||||
|
SYMB = "!#$%&*+,-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~;"
|
||||||
|
SLEN = len(SYMB)
|
||||||
|
SYMB_TO_IDX = {ord(s):i for i,s in enumerate(SYMB)}
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, world : World, commit_announcement) -> None:
|
||||||
|
self.world = world
|
||||||
|
self.commit_announcement = commit_announcement
|
||||||
|
r = world.robot
|
||||||
|
t = world.teammates
|
||||||
|
o = world.opponents
|
||||||
|
self.groups = ( # player team/unum, group has ball?, self in group?
|
||||||
|
[(t[9],t[10],o[6],o[7],o[8],o[9],o[10]), True ], # 2 teammates, 5 opponents, ball
|
||||||
|
[(t[0],t[1], t[2],t[3],t[4],t[5],t[6] ), False], # 7 teammates
|
||||||
|
[(t[7],t[8], o[0],o[1],o[2],o[3],o[4],o[5]), False] # 2 teammates, 6 opponents
|
||||||
|
)
|
||||||
|
for g in self.groups: # add 'self in group?'
|
||||||
|
g.append(any(i.is_self for i in g[0]))
|
||||||
|
|
||||||
|
def get_player_combination(self, pos, is_unknown, is_down, info):
|
||||||
|
''' Returns combination (0-based) and number of possible combinations '''
|
||||||
|
|
||||||
|
if is_unknown:
|
||||||
|
return info[7]+1, info[7]+2 # return unknown combination
|
||||||
|
|
||||||
|
x,y = pos[:2]
|
||||||
|
|
||||||
|
if x < -17 or x > 17 or y < -12 or y > 12:
|
||||||
|
return info[7], info[7]+2 # return out of bounds combination (if it exceeds 1m in any axis)
|
||||||
|
|
||||||
|
# convert to int to avoid overflow later
|
||||||
|
l = int(np.clip( round(info[4]*x+info[2]), 0, info[0]-1 )) # absorb out of bounds positions (up to 1m in each axis)
|
||||||
|
c = int(np.clip( round(info[5]*y+info[3]), 0, info[1]-1 ))
|
||||||
|
|
||||||
|
return (l*info[1]+c)+(info[6] if is_down else 0), info[7]+2 # return valid combination
|
||||||
|
|
||||||
|
|
||||||
|
def get_ball_combination(self, x, y):
|
||||||
|
''' Returns combination (0-based) and number of possible combinations '''
|
||||||
|
|
||||||
|
# if ball is out of bounds, we force it in
|
||||||
|
l = int(np.clip( round(Radio.BP[4]*x+Radio.BP[2]), 0, Radio.BP[0]-1 ))
|
||||||
|
c = int(np.clip( round(Radio.BP[5]*y+Radio.BP[3]), 0, Radio.BP[1]-1 ))
|
||||||
|
|
||||||
|
return l*Radio.BP[1]+c, Radio.BP[6] # return valid combination
|
||||||
|
|
||||||
|
def get_ball_position(self,comb):
|
||||||
|
|
||||||
|
l = comb // Radio.BP[1]
|
||||||
|
c = comb % Radio.BP[1]
|
||||||
|
|
||||||
|
return np.array([l/Radio.BP[4]-15, c/Radio.BP[5]-10, 0.042]) # assume ball is on ground
|
||||||
|
|
||||||
|
def get_player_position(self,comb, info):
|
||||||
|
|
||||||
|
if comb == info[7]: return -1 # player is out of bounds
|
||||||
|
if comb == info[7]+1: return -2 # player is in unknown location
|
||||||
|
|
||||||
|
is_down = comb >= info[6]
|
||||||
|
if is_down:
|
||||||
|
comb -= info[6]
|
||||||
|
|
||||||
|
l = comb // info[1]
|
||||||
|
c = comb % info[1]
|
||||||
|
|
||||||
|
return l/info[4]-16, c/info[5]-11, is_down
|
||||||
|
|
||||||
|
|
||||||
|
def check_broadcast_requirements(self):
|
||||||
|
'''
|
||||||
|
Check if broadcast group is valid
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
ready : bool
|
||||||
|
True if all requirements are met
|
||||||
|
|
||||||
|
Sequence: g0,g1,g2, ig0,ig1,ig2, iig0,iig1,iig2 (whole cycle: 0.36s)
|
||||||
|
igx means 'incomplete group', where <=1 element can be MIA recently
|
||||||
|
iigx means 'very incomplete group', where <=2 elements can be MIA recently
|
||||||
|
Rationale: prevent incomplete messages from monopolizing the broadcast space
|
||||||
|
|
||||||
|
However:
|
||||||
|
- 1st round: when 0 group members are missing, that group will update 3 times every 0.36s
|
||||||
|
- 2nd round: when 1 group member is recently missing, that group will update 2 times every 0.36s
|
||||||
|
- 3rd round: when 2 group members are recently missing, that group will update 1 time every 0.36s
|
||||||
|
- when >2 group members are recently missing, that group will not be updated
|
||||||
|
|
||||||
|
Players that have never been seen or heard are not considered for the 'recently missing'.
|
||||||
|
If there is only 1 group member since the beginning, the respective group can be updated, except in the 1st round.
|
||||||
|
In this way, the 1st round cannot be monopolized by clueless agents, which is important during games with 22 players.
|
||||||
|
'''
|
||||||
|
|
||||||
|
w = self.world
|
||||||
|
r = w.robot
|
||||||
|
ago40ms = w.time_local_ms - 40
|
||||||
|
ago370ms = w.time_local_ms - 370 # maximum delay (up to 2 MIAs) is 360ms because radio has a delay of 20ms (otherwise max delay would be 340ms)
|
||||||
|
group : List[Other_Robot]
|
||||||
|
|
||||||
|
idx9 = int((w.time_server * 25)+0.1) % 9 # sequence of 9 phases
|
||||||
|
max_MIA = idx9 // 3 # maximum number of MIA players (based on server time)
|
||||||
|
group_idx = idx9 % 3 # group number (based on server time)
|
||||||
|
group, has_ball, is_self_included = self.groups[group_idx]
|
||||||
|
|
||||||
|
#============================================ 0. check if group is valid
|
||||||
|
|
||||||
|
if has_ball and w.ball_abs_pos_last_update < ago40ms: # Ball is included and not up to date
|
||||||
|
return False
|
||||||
|
|
||||||
|
if is_self_included and r.loc_last_update < ago40ms: # Oneself is included and unable to self-locate
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Get players that have been previously seen or heard but not recently
|
||||||
|
MIAs = [not ot.is_self and ot.state_last_update < ago370ms and ot.state_last_update > 0 for ot in group]
|
||||||
|
self.MIAs = [ot.state_last_update == 0 or MIAs[i] for i,ot in enumerate(group)] # add players that have never been seen
|
||||||
|
|
||||||
|
if sum(MIAs) > max_MIA: # checking if number of recently missing members is not above threshold
|
||||||
|
return False
|
||||||
|
|
||||||
|
# never seen before players are always ignored except when:
|
||||||
|
# - this is the 0 MIAs round (see explanation above)
|
||||||
|
# - all are MIA
|
||||||
|
if (max_MIA == 0 and any(self.MIAs)) or all(self.MIAs):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check for invalid members. Conditions:
|
||||||
|
# - Player is other and not MIA and:
|
||||||
|
# - last update was >40ms ago OR
|
||||||
|
# - last update did not include the head (head is important to provide state and accurate position)
|
||||||
|
|
||||||
|
if any(
|
||||||
|
(not ot.is_self and not self.MIAs[i] and
|
||||||
|
(ot.state_last_update < ago40ms or ot.state_last_update==0 or len(ot.state_abs_pos)<3)# (last update: has no head or is old)
|
||||||
|
) for i,ot in enumerate(group)
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def broadcast(self):
|
||||||
|
'''
|
||||||
|
Commit messages to teammates if certain conditions are met
|
||||||
|
Messages contain: positions/states of every moving entity
|
||||||
|
'''
|
||||||
|
|
||||||
|
if not self.check_broadcast_requirements():
|
||||||
|
return
|
||||||
|
|
||||||
|
w = self.world
|
||||||
|
ot : Other_Robot
|
||||||
|
|
||||||
|
group_idx = int((w.time_server * 25)+0.1) % 3 # group number based on server time
|
||||||
|
group, has_ball, _ = self.groups[group_idx]
|
||||||
|
|
||||||
|
#============================================ 1. create combination
|
||||||
|
|
||||||
|
# add message number
|
||||||
|
combination = group_idx
|
||||||
|
no_of_combinations = 3
|
||||||
|
|
||||||
|
# add ball combination
|
||||||
|
if has_ball:
|
||||||
|
c, n = self.get_ball_combination(w.ball_abs_pos[0], w.ball_abs_pos[1])
|
||||||
|
combination += c * no_of_combinations
|
||||||
|
no_of_combinations *= n
|
||||||
|
|
||||||
|
|
||||||
|
# add group combinations
|
||||||
|
for i,ot in enumerate(group):
|
||||||
|
c, n = self.get_player_combination(ot.state_abs_pos, # player position
|
||||||
|
self.MIAs[i], ot.state_fallen, # is unknown, is down
|
||||||
|
Radio.TP if ot.is_teammate else Radio.OP) # is teammate
|
||||||
|
combination += c * no_of_combinations
|
||||||
|
no_of_combinations *= n
|
||||||
|
|
||||||
|
|
||||||
|
assert(no_of_combinations < 9.61e38) # 88*89^19 (first character cannot be ';')
|
||||||
|
|
||||||
|
#============================================ 2. create message
|
||||||
|
|
||||||
|
# 1st msg symbol: ignore ';' due to server bug
|
||||||
|
msg = Radio.SYMB[combination % (Radio.SLEN-1)]
|
||||||
|
combination //= (Radio.SLEN-1)
|
||||||
|
|
||||||
|
# following msg symbols
|
||||||
|
while combination:
|
||||||
|
msg += Radio.SYMB[combination % Radio.SLEN]
|
||||||
|
combination //= Radio.SLEN
|
||||||
|
|
||||||
|
#============================================ 3. commit message
|
||||||
|
|
||||||
|
self.commit_announcement(msg.encode()) # commit message
|
||||||
|
|
||||||
|
|
||||||
|
def receive(self, msg:bytearray):
|
||||||
|
w = self.world
|
||||||
|
r = w.robot
|
||||||
|
ago40ms = w.time_local_ms - 40
|
||||||
|
ago110ms = w.time_local_ms - 110
|
||||||
|
msg_time = w.time_local_ms - 20 # message was sent in the last step
|
||||||
|
|
||||||
|
#============================================ 1. get combination
|
||||||
|
|
||||||
|
# read first symbol, which cannot be ';' due to server bug
|
||||||
|
combination = Radio.SYMB_TO_IDX[msg[0]]
|
||||||
|
total_combinations = Radio.SLEN-1
|
||||||
|
|
||||||
|
if len(msg)>1:
|
||||||
|
for m in msg[1:]:
|
||||||
|
combination += total_combinations * Radio.SYMB_TO_IDX[m]
|
||||||
|
total_combinations *= Radio.SLEN
|
||||||
|
|
||||||
|
#============================================ 2. get msg ID
|
||||||
|
|
||||||
|
message_no = combination % 3
|
||||||
|
combination //= 3
|
||||||
|
group, has_ball, _ = self.groups[message_no]
|
||||||
|
|
||||||
|
#============================================ 3. get data
|
||||||
|
|
||||||
|
if has_ball:
|
||||||
|
ball_comb = combination % Radio.BP[6]
|
||||||
|
combination //= Radio.BP[6]
|
||||||
|
|
||||||
|
players_combs = []
|
||||||
|
ot : Other_Robot
|
||||||
|
for ot in group:
|
||||||
|
info = Radio.TP if ot.is_teammate else Radio.OP
|
||||||
|
players_combs.append( combination % (info[7]+2) )
|
||||||
|
combination //= info[7]+2
|
||||||
|
|
||||||
|
#============================================ 4. update world
|
||||||
|
|
||||||
|
if has_ball and w.ball_abs_pos_last_update < ago40ms: # update ball if it was not seen
|
||||||
|
time_diff = (msg_time - w.ball_abs_pos_last_update) / 1000
|
||||||
|
ball = self.get_ball_position(ball_comb)
|
||||||
|
w.ball_abs_vel = (ball - w.ball_abs_pos) / time_diff
|
||||||
|
w.ball_abs_speed = np.linalg.norm(w.ball_abs_vel)
|
||||||
|
w.ball_abs_pos_last_update = msg_time # (error: 0-40 ms)
|
||||||
|
w.ball_abs_pos = ball
|
||||||
|
w.is_ball_abs_pos_from_vision = False
|
||||||
|
|
||||||
|
for c, ot in zip(players_combs, group):
|
||||||
|
|
||||||
|
# handle oneself case
|
||||||
|
if ot.is_self:
|
||||||
|
# the ball's position has a fair amount of noise, whether seen by us or other players
|
||||||
|
# but our self-locatization mechanism is usually much better than how others perceive us
|
||||||
|
if r.loc_last_update < ago110ms: # so we wait until we miss 2 visual steps
|
||||||
|
data = self.get_player_position(c, Radio.TP)
|
||||||
|
if type(data)==tuple:
|
||||||
|
x,y,is_down = data
|
||||||
|
r.loc_head_position[:2] = x,y # z is kept unchanged
|
||||||
|
r.loc_head_position_last_update = msg_time
|
||||||
|
r.radio_fallen_state = is_down
|
||||||
|
r.radio_last_update = msg_time
|
||||||
|
continue
|
||||||
|
|
||||||
|
# do not update if other robot was recently seen
|
||||||
|
if ot.state_last_update >= ago40ms:
|
||||||
|
continue
|
||||||
|
|
||||||
|
info = Radio.TP if ot.is_teammate else Radio.OP
|
||||||
|
data = self.get_player_position(c, info)
|
||||||
|
if type(data)==tuple:
|
||||||
|
x,y,is_down = data
|
||||||
|
p = np.array([x,y])
|
||||||
|
|
||||||
|
if ot.state_abs_pos is not None: # update the x & y components of the velocity
|
||||||
|
time_diff = (msg_time - ot.state_last_update) / 1000
|
||||||
|
velocity = np.append( (p - ot.state_abs_pos[:2]) / time_diff, 0) # v.z = 0
|
||||||
|
vel_diff = velocity - ot.state_filtered_velocity
|
||||||
|
if np.linalg.norm(vel_diff) < 4: # otherwise assume it was beamed
|
||||||
|
ot.state_filtered_velocity /= (ot.vel_decay,ot.vel_decay,1) # neutralize decay (except in the z-axis)
|
||||||
|
ot.state_filtered_velocity += ot.vel_filter * vel_diff
|
||||||
|
|
||||||
|
ot.state_fallen = is_down
|
||||||
|
ot.state_last_update = msg_time
|
||||||
|
ot.state_body_parts_abs_pos = {"head":p}
|
||||||
|
ot.state_abs_pos = p
|
||||||
|
ot.state_horizontal_dist = np.linalg.norm(p - r.loc_head_position[:2])
|
||||||
|
ot.state_ground_area = (p, 0.3 if is_down else 0.2) # not very precise, but we cannot see the robot
|
@ -0,0 +1,285 @@
|
|||||||
|
from communication.World_Parser import World_Parser
|
||||||
|
from itertools import count
|
||||||
|
from select import select
|
||||||
|
from sys import exit
|
||||||
|
from world.World import World
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
|
||||||
|
class Server_Comm():
|
||||||
|
monitor_socket = None
|
||||||
|
|
||||||
|
def __init__(self, host:str, agent_port:int, monitor_port:int, unum:int, robot_type:int, team_name:str,
|
||||||
|
world_parser:World_Parser, world:World, other_players, wait_for_server=True) -> None:
|
||||||
|
|
||||||
|
self.BUFFER_SIZE = 8192
|
||||||
|
self.rcv_buff = bytearray(self.BUFFER_SIZE)
|
||||||
|
self.send_buff = []
|
||||||
|
self.world_parser = world_parser
|
||||||
|
self.unum = unum
|
||||||
|
|
||||||
|
# During initialization, it's not clear whether we are on the left or right side
|
||||||
|
self._unofficial_beam_msg_left = "(agent (unum " + str(unum) + ") (team Left) (move "
|
||||||
|
self._unofficial_beam_msg_right = "(agent (unum " + str(unum) + ") (team Right) (move "
|
||||||
|
self.world = world
|
||||||
|
|
||||||
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM )
|
||||||
|
|
||||||
|
if wait_for_server: print("Waiting for server at ", host, ":", agent_port, sep="",end=".",flush=True)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
self.socket.connect((host, agent_port))
|
||||||
|
print(end=" ")
|
||||||
|
break
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
if not wait_for_server:
|
||||||
|
print("Server is down. Closing...")
|
||||||
|
exit()
|
||||||
|
time.sleep(1)
|
||||||
|
print(".",end="",flush=True)
|
||||||
|
print("Connected agent", unum, self.socket.getsockname())
|
||||||
|
|
||||||
|
self.send_immediate(b'(scene rsg/agent/nao/nao_hetero.rsg ' + str(robot_type).encode() + b')')
|
||||||
|
self._receive_async(other_players, True)
|
||||||
|
|
||||||
|
self.send_immediate(b'(init (unum '+ str(unum).encode() + b') (teamname '+ team_name.encode() + b'))')
|
||||||
|
self._receive_async(other_players, False)
|
||||||
|
|
||||||
|
# Repeat to guarantee that team side information is received
|
||||||
|
for _ in range(3):
|
||||||
|
# Eliminate advanced step by changing syn order (rcssserver3d protocol bug, usually seen for player 11)
|
||||||
|
self.send_immediate(b'(syn)') #if this syn is not needed, it will be discarded by the server
|
||||||
|
for p in other_players:
|
||||||
|
p.scom.send_immediate(b'(syn)')
|
||||||
|
for p in other_players:
|
||||||
|
p.scom.receive()
|
||||||
|
self.receive()
|
||||||
|
|
||||||
|
if world.team_side_is_left == None:
|
||||||
|
print("\nError: server did not return a team side! Check server terminal!")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# Monitor socket is shared by all agents on the same thread
|
||||||
|
if Server_Comm.monitor_socket is None and monitor_port is not None:
|
||||||
|
print("Connecting to server's monitor port at ", host, ":", monitor_port, sep="",end=".",flush=True)
|
||||||
|
Server_Comm.monitor_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM )
|
||||||
|
Server_Comm.monitor_socket.connect((host, monitor_port))
|
||||||
|
print("Done!")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _receive_async(self, other_players, first_pass) -> None:
|
||||||
|
'''Private function that receives asynchronous information during the initialization'''
|
||||||
|
|
||||||
|
if not other_players:
|
||||||
|
self.receive()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.socket.setblocking(0)
|
||||||
|
if first_pass: print("Async agent",self.unum,"initialization", end="", flush=True)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
print(".",end="",flush=True)
|
||||||
|
self.receive()
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
for p in other_players:
|
||||||
|
p.scom.send_immediate(b'(syn)')
|
||||||
|
for p in other_players:
|
||||||
|
p.scom.receive()
|
||||||
|
|
||||||
|
self.socket.setblocking(1)
|
||||||
|
if not first_pass: print("Done!")
|
||||||
|
|
||||||
|
|
||||||
|
def receive(self, update=True):
|
||||||
|
|
||||||
|
for i in count(): # parse all messages and perform value updates, but heavy computation is only done once at the end
|
||||||
|
try:
|
||||||
|
if self.socket.recv_into(self.rcv_buff, nbytes=4) != 4: raise ConnectionResetError()
|
||||||
|
msg_size = int.from_bytes(self.rcv_buff[:4], byteorder='big', signed=False)
|
||||||
|
if self.socket.recv_into(self.rcv_buff, nbytes=msg_size, flags=socket.MSG_WAITALL) != msg_size: raise ConnectionResetError()
|
||||||
|
except ConnectionResetError:
|
||||||
|
print("\nError: socket was closed by rcssserver3d!")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
self.world_parser.parse(self.rcv_buff[:msg_size])
|
||||||
|
if len(select([self.socket],[],[], 0.0)[0]) == 0: break
|
||||||
|
|
||||||
|
if update:
|
||||||
|
if i==1: self.world.log( "Server_Comm.py: The agent lost 1 packet! Is syncmode enabled?")
|
||||||
|
if i>1: self.world.log(f"Server_Comm.py: The agent lost {i} consecutive packets! Is syncmode disabled?")
|
||||||
|
self.world.update()
|
||||||
|
|
||||||
|
if len(select([self.socket],[],[], 0.0)[0]) != 0:
|
||||||
|
self.world.log("Server_Comm.py: Received a new packet while on world.update()!")
|
||||||
|
self.receive()
|
||||||
|
|
||||||
|
|
||||||
|
def send_immediate(self, msg:bytes) -> None:
|
||||||
|
''' Commit and send immediately '''
|
||||||
|
try:
|
||||||
|
self.socket.send( (len(msg)).to_bytes(4,byteorder='big') + msg ) #Add message length in the first 4 bytes
|
||||||
|
except BrokenPipeError:
|
||||||
|
print("\nError: socket was closed by rcssserver3d!")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
def send(self) -> None:
|
||||||
|
''' Send all committed messages '''
|
||||||
|
if len(select([self.socket],[],[], 0.0)[0]) == 0:
|
||||||
|
self.send_buff.append(b'(syn)')
|
||||||
|
self.send_immediate( b''.join(self.send_buff) )
|
||||||
|
else:
|
||||||
|
self.world.log("Server_Comm.py: Received a new packet while thinking!")
|
||||||
|
self.send_buff = [] #clear buffer
|
||||||
|
|
||||||
|
def commit(self, msg:bytes) -> None:
|
||||||
|
assert type(msg) == bytes, "Message must be of type Bytes!"
|
||||||
|
self.send_buff.append(msg)
|
||||||
|
|
||||||
|
def commit_and_send(self, msg:bytes = b'') -> None:
|
||||||
|
self.commit(msg)
|
||||||
|
self.send()
|
||||||
|
|
||||||
|
def clear_buffer(self) -> None:
|
||||||
|
self.send_buff = []
|
||||||
|
|
||||||
|
def commit_announcement(self, msg:bytes) -> None:
|
||||||
|
'''
|
||||||
|
Say something to every player on the field.
|
||||||
|
Maximum 20 characters, ascii between 0x20, 0x7E except ' ', '(', ')'
|
||||||
|
Accepted: letters+numbers+symbols: !"#$%&'*+,-./:;<=>?@[\]^_`{|}~
|
||||||
|
Message range: 50m (the field is 36m diagonally, so ignore this limitation)
|
||||||
|
A player can only hear a teammate's message every 2 steps (0.04s)
|
||||||
|
This ability exists independetly for messages from both teams
|
||||||
|
(i.e. our team cannot spam the other team to block their messages)
|
||||||
|
Messages from oneself are always heard
|
||||||
|
'''
|
||||||
|
assert len(msg) <= 20 and type(msg) == bytes
|
||||||
|
self.commit(b'(say ' + msg + b')')
|
||||||
|
|
||||||
|
def commit_pass_command(self) -> None:
|
||||||
|
'''
|
||||||
|
Issue a pass command:
|
||||||
|
Conditions:
|
||||||
|
- The current playmode is PlayOn
|
||||||
|
- The agent is near the ball (default 0.5m)
|
||||||
|
- No opponents are near the ball (default 1m)
|
||||||
|
- The ball is stationary (default <0.05m/s)
|
||||||
|
- A certain amount of time has passed between pass commands
|
||||||
|
'''
|
||||||
|
self.commit(b'(pass)')
|
||||||
|
|
||||||
|
def commit_beam(self, pos2d, rot) -> None:
|
||||||
|
'''
|
||||||
|
Official beam command that can be used in-game
|
||||||
|
This beam is affected by noise (unless it is disabled in the server configuration)
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pos2d : array_like
|
||||||
|
Absolute 2D position (negative X is always our half of the field, no matter our side)
|
||||||
|
rot : `int`/`float`
|
||||||
|
Player angle in degrees (0 points forward)
|
||||||
|
'''
|
||||||
|
assert len(pos2d)==2, "The official beam command accepts only 2D positions!"
|
||||||
|
self.commit( f"(beam {pos2d[0]} {pos2d[1]} {rot})".encode() )
|
||||||
|
|
||||||
|
|
||||||
|
def unofficial_beam(self, pos3d, rot) -> None:
|
||||||
|
'''
|
||||||
|
Unofficial beam - it cannot be used in official matches
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pos3d : array_like
|
||||||
|
Absolute 3D position (negative X is always our half of the field, no matter our side)
|
||||||
|
rot : `int`/`float`
|
||||||
|
Player angle in degrees (0 points forward)
|
||||||
|
'''
|
||||||
|
assert len(pos3d)==3, "The unofficial beam command accepts only 3D positions!"
|
||||||
|
|
||||||
|
# there is no need to normalize the angle, the server accepts any angle
|
||||||
|
if self.world.team_side_is_left:
|
||||||
|
msg = f"{self._unofficial_beam_msg_left }{ pos3d[0]} { pos3d[1]} {pos3d[2]} {rot-90}))".encode()
|
||||||
|
else:
|
||||||
|
msg = f"{self._unofficial_beam_msg_right}{-pos3d[0]} {-pos3d[1]} {pos3d[2]} {rot+90}))".encode()
|
||||||
|
|
||||||
|
self.monitor_socket.send( (len(msg)).to_bytes(4,byteorder='big') + msg )
|
||||||
|
|
||||||
|
def unofficial_kill_sim(self) -> None:
|
||||||
|
''' Unofficial kill simulator command '''
|
||||||
|
msg = b'(killsim)'
|
||||||
|
self.monitor_socket.send( (len(msg)).to_bytes(4,byteorder='big') + msg )
|
||||||
|
|
||||||
|
def unofficial_move_ball(self, pos3d, vel3d=(0,0,0)) -> None:
|
||||||
|
'''
|
||||||
|
Unofficial command to move ball
|
||||||
|
info: ball radius = 0.042m
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pos3d : array_like
|
||||||
|
Absolute 3D position (negative X is always our half of the field, no matter our side)
|
||||||
|
vel3d : array_like
|
||||||
|
Absolute 3D velocity (negative X is always our half of the field, no matter our side)
|
||||||
|
'''
|
||||||
|
assert len(pos3d)==3 and len(vel3d)==3, "To move the ball we need a 3D position and velocity"
|
||||||
|
|
||||||
|
if self.world.team_side_is_left:
|
||||||
|
msg = f"(ball (pos { pos3d[0]} { pos3d[1]} {pos3d[2]}) (vel { vel3d[0]} { vel3d[1]} {vel3d[2]}))".encode()
|
||||||
|
else:
|
||||||
|
msg = f"(ball (pos {-pos3d[0]} {-pos3d[1]} {pos3d[2]}) (vel {-vel3d[0]} {-vel3d[1]} {vel3d[2]}))".encode()
|
||||||
|
|
||||||
|
self.monitor_socket.send( (len(msg)).to_bytes(4,byteorder='big') + msg )
|
||||||
|
|
||||||
|
def unofficial_set_game_time(self, time_in_s : float) -> None:
|
||||||
|
'''
|
||||||
|
Unofficial command to set the game time
|
||||||
|
e.g. unofficial_set_game_time(68.78)
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
time_in_s : float
|
||||||
|
Game time in seconds
|
||||||
|
'''
|
||||||
|
msg = f"(time {time_in_s})".encode()
|
||||||
|
self.monitor_socket.send( (len(msg)).to_bytes(4,byteorder='big') + msg )
|
||||||
|
|
||||||
|
def unofficial_set_play_mode(self, play_mode : str) -> None:
|
||||||
|
'''
|
||||||
|
Unofficial command to set the play mode
|
||||||
|
e.g. unofficial_set_play_mode("PlayOn")
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
play_mode : str
|
||||||
|
Play mode
|
||||||
|
'''
|
||||||
|
msg = f"(playMode {play_mode})".encode()
|
||||||
|
self.monitor_socket.send( (len(msg)).to_bytes(4,byteorder='big') + msg )
|
||||||
|
|
||||||
|
def unofficial_kill_player(self, unum : int, team_side_is_left : bool) -> None:
|
||||||
|
'''
|
||||||
|
Unofficial command to kill specific player
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
unum : int
|
||||||
|
Uniform number
|
||||||
|
team_side_is_left : bool
|
||||||
|
True if player to kill belongs to left team
|
||||||
|
'''
|
||||||
|
msg = f"(kill (unum {unum}) (team {'Left' if team_side_is_left else 'Right'}))".encode()
|
||||||
|
self.monitor_socket.send( (len(msg)).to_bytes(4,byteorder='big') + msg )
|
||||||
|
|
||||||
|
def close(self, close_monitor_socket = False):
|
||||||
|
''' Close agent socket, and optionally the monitor socket (shared by players running on the same thread) '''
|
||||||
|
self.socket.close()
|
||||||
|
if close_monitor_socket and Server_Comm.monitor_socket is not None:
|
||||||
|
Server_Comm.monitor_socket.close()
|
||||||
|
Server_Comm.monitor_socket = None
|
||||||
|
|
@ -0,0 +1,430 @@
|
|||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
from world.Robot import Robot
|
||||||
|
from world.World import World
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class World_Parser():
|
||||||
|
def __init__(self, world:World, hear_callback) -> None:
|
||||||
|
self.LOG_PREFIX = "World_Parser.py: "
|
||||||
|
self.world = world
|
||||||
|
self.hear_callback = hear_callback
|
||||||
|
self.exp = None
|
||||||
|
self.depth = None
|
||||||
|
self.LEFT_SIDE_FLAGS = {b'F2L':(-15,-10,0),
|
||||||
|
b'F1L':(-15,+10,0),
|
||||||
|
b'F2R':(+15,-10,0),
|
||||||
|
b'F1R':(+15,+10,0),
|
||||||
|
b'G2L':(-15,-1.05,0.8),
|
||||||
|
b'G1L':(-15,+1.05,0.8),
|
||||||
|
b'G2R':(+15,-1.05,0.8),
|
||||||
|
b'G1R':(+15,+1.05,0.8)} #mapping between flag names and their corrected location, when playing on the left side
|
||||||
|
self.RIGHT_SIDE_FLAGS = {b'F2L':(+15,+10,0),
|
||||||
|
b'F1L':(+15,-10,0),
|
||||||
|
b'F2R':(-15,+10,0),
|
||||||
|
b'F1R':(-15,-10,0),
|
||||||
|
b'G2L':(+15,+1.05,0.8),
|
||||||
|
b'G1L':(+15,-1.05,0.8),
|
||||||
|
b'G2R':(-15,+1.05,0.8),
|
||||||
|
b'G1R':(-15,-1.05,0.8)}
|
||||||
|
self.play_mode_to_id = None
|
||||||
|
self.LEFT_PLAY_MODE_TO_ID = {"KickOff_Left":World.M_OUR_KICKOFF, "KickIn_Left":World.M_OUR_KICK_IN, "corner_kick_left":World.M_OUR_CORNER_KICK,
|
||||||
|
"goal_kick_left":World.M_OUR_GOAL_KICK, "free_kick_left":World.M_OUR_FREE_KICK, "pass_left":World.M_OUR_PASS,
|
||||||
|
"direct_free_kick_left": World.M_OUR_DIR_FREE_KICK, "Goal_Left": World.M_OUR_GOAL, "offside_left": World.M_OUR_OFFSIDE,
|
||||||
|
"KickOff_Right":World.M_THEIR_KICKOFF, "KickIn_Right":World.M_THEIR_KICK_IN, "corner_kick_right":World.M_THEIR_CORNER_KICK,
|
||||||
|
"goal_kick_right":World.M_THEIR_GOAL_KICK, "free_kick_right":World.M_THEIR_FREE_KICK, "pass_right":World.M_THEIR_PASS,
|
||||||
|
"direct_free_kick_right": World.M_THEIR_DIR_FREE_KICK, "Goal_Right": World.M_THEIR_GOAL, "offside_right": World.M_THEIR_OFFSIDE,
|
||||||
|
"BeforeKickOff": World.M_BEFORE_KICKOFF, "GameOver": World.M_GAME_OVER, "PlayOn": World.M_PLAY_ON }
|
||||||
|
self.RIGHT_PLAY_MODE_TO_ID = {"KickOff_Left":World.M_THEIR_KICKOFF, "KickIn_Left":World.M_THEIR_KICK_IN, "corner_kick_left":World.M_THEIR_CORNER_KICK,
|
||||||
|
"goal_kick_left":World.M_THEIR_GOAL_KICK, "free_kick_left":World.M_THEIR_FREE_KICK, "pass_left":World.M_THEIR_PASS,
|
||||||
|
"direct_free_kick_left": World.M_THEIR_DIR_FREE_KICK, "Goal_Left": World.M_THEIR_GOAL, "offside_left": World.M_THEIR_OFFSIDE,
|
||||||
|
"KickOff_Right":World.M_OUR_KICKOFF, "KickIn_Right":World.M_OUR_KICK_IN, "corner_kick_right":World.M_OUR_CORNER_KICK,
|
||||||
|
"goal_kick_right":World.M_OUR_GOAL_KICK, "free_kick_right":World.M_OUR_FREE_KICK, "pass_right":World.M_OUR_PASS,
|
||||||
|
"direct_free_kick_right": World.M_OUR_DIR_FREE_KICK, "Goal_Right": World.M_OUR_GOAL, "offside_right": World.M_OUR_OFFSIDE,
|
||||||
|
"BeforeKickOff": World.M_BEFORE_KICKOFF, "GameOver": World.M_GAME_OVER, "PlayOn": World.M_PLAY_ON }
|
||||||
|
|
||||||
|
|
||||||
|
def find_non_digit(self,start):
|
||||||
|
while True:
|
||||||
|
if (self.exp[start] < ord('0') or self.exp[start] > ord('9')) and self.exp[start] != ord('.'): return start
|
||||||
|
start+=1
|
||||||
|
|
||||||
|
def find_char(self,start,char):
|
||||||
|
while True:
|
||||||
|
if self.exp[start] == char : return start
|
||||||
|
start+=1
|
||||||
|
|
||||||
|
def read_float(self, start):
|
||||||
|
if self.exp[start:start+3] == b'nan': return float('nan'), start+3 #handle nan values (they exist)
|
||||||
|
end = self.find_non_digit(start+1) #we assume the first one is a digit or minus sign
|
||||||
|
try:
|
||||||
|
retval = float(self.exp[start:end])
|
||||||
|
except:
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}String to float conversion failed: {self.exp[start:end]} at msg[{start},{end}], \nMsg: {self.exp.decode()}")
|
||||||
|
retval = 0
|
||||||
|
return retval, end
|
||||||
|
|
||||||
|
def read_int(self, start):
|
||||||
|
end = self.find_non_digit(start+1) #we assume the first one is a digit or minus sign
|
||||||
|
return int(self.exp[start:end]), end
|
||||||
|
|
||||||
|
def read_bytes(self, start):
|
||||||
|
end = start
|
||||||
|
while True:
|
||||||
|
if self.exp[end] == ord(' ') or self.exp[end] == ord(')'): break
|
||||||
|
end+=1
|
||||||
|
|
||||||
|
return self.exp[start:end], end
|
||||||
|
|
||||||
|
def read_str(self, start):
|
||||||
|
b, end = self.read_bytes(start)
|
||||||
|
return b.decode(), end
|
||||||
|
|
||||||
|
def get_next_tag(self, start):
|
||||||
|
min_depth = self.depth
|
||||||
|
while True:
|
||||||
|
if self.exp[start] == ord(")") : #monitor xml element depth
|
||||||
|
self.depth -= 1
|
||||||
|
if min_depth > self.depth: min_depth = self.depth
|
||||||
|
elif self.exp[start] == ord("(") : break
|
||||||
|
start+=1
|
||||||
|
if start >= len(self.exp): return None, start, 0
|
||||||
|
|
||||||
|
self.depth += 1
|
||||||
|
start += 1
|
||||||
|
end = self.find_char(start, ord(" "))
|
||||||
|
return self.exp[start:end], end, min_depth
|
||||||
|
|
||||||
|
|
||||||
|
def parse(self, exp):
|
||||||
|
|
||||||
|
self.exp = exp #used by other member functions
|
||||||
|
self.depth = 0 #xml element depth
|
||||||
|
self.world.step += 1
|
||||||
|
self.world.line_count = 0
|
||||||
|
self.world.robot.frp = dict()
|
||||||
|
self.world.flags_posts = dict()
|
||||||
|
self.world.flags_corners = dict()
|
||||||
|
self.world.vision_is_up_to_date = False
|
||||||
|
self.world.ball_is_visible = False
|
||||||
|
self.world.robot.feet_toes_are_touching = dict.fromkeys(self.world.robot.feet_toes_are_touching, False)
|
||||||
|
self.world.time_local_ms += World.STEPTIME_MS
|
||||||
|
|
||||||
|
for p in self.world.teammates: p.is_visible = False
|
||||||
|
for p in self.world.opponents: p.is_visible = False
|
||||||
|
|
||||||
|
tag, end, _ = self.get_next_tag(0)
|
||||||
|
|
||||||
|
while end < len(exp):
|
||||||
|
|
||||||
|
if tag==b'time':
|
||||||
|
while True:
|
||||||
|
tag, end, min_depth = self.get_next_tag(end)
|
||||||
|
if min_depth == 0: break
|
||||||
|
|
||||||
|
if tag==b'now':
|
||||||
|
#last_time = self.world.time_server
|
||||||
|
self.world.time_server, end = self.read_float(end+1)
|
||||||
|
|
||||||
|
#Test server time reliability
|
||||||
|
#increment = self.world.time_server - last_time
|
||||||
|
#if increment < 0.019: print ("down",last_time,self.world.time_server)
|
||||||
|
#if increment > 0.021: print ("up",last_time,self.world.time_server)
|
||||||
|
else:
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}Unknown tag inside 'time': {tag} at {end}, \nMsg: {exp.decode()}")
|
||||||
|
|
||||||
|
|
||||||
|
elif tag==b'GS':
|
||||||
|
while True:
|
||||||
|
tag, end, min_depth = self.get_next_tag(end)
|
||||||
|
if min_depth == 0: break
|
||||||
|
|
||||||
|
if tag==b'unum':
|
||||||
|
_, end = self.read_int(end+1) #We already know our unum
|
||||||
|
elif tag==b'team':
|
||||||
|
aux, end = self.read_str(end+1)
|
||||||
|
is_left = bool(aux == "left")
|
||||||
|
if self.world.team_side_is_left != is_left:
|
||||||
|
self.world.team_side_is_left = is_left
|
||||||
|
self.play_mode_to_id = self.LEFT_PLAY_MODE_TO_ID if is_left else self.RIGHT_PLAY_MODE_TO_ID
|
||||||
|
self.world.draw.set_team_side(not is_left)
|
||||||
|
self.world.team_draw.set_team_side(not is_left)
|
||||||
|
elif tag==b'sl':
|
||||||
|
if self.world.team_side_is_left:
|
||||||
|
self.world.goals_scored, end = self.read_int(end+1)
|
||||||
|
else:
|
||||||
|
self.world.goals_conceded, end = self.read_int(end+1)
|
||||||
|
elif tag==b'sr':
|
||||||
|
if self.world.team_side_is_left:
|
||||||
|
self.world.goals_conceded, end = self.read_int(end+1)
|
||||||
|
else:
|
||||||
|
self.world.goals_scored, end = self.read_int(end+1)
|
||||||
|
elif tag==b't':
|
||||||
|
self.world.time_game, end = self.read_float(end+1)
|
||||||
|
elif tag==b'pm':
|
||||||
|
aux, end = self.read_str(end+1)
|
||||||
|
if self.play_mode_to_id is not None:
|
||||||
|
self.world.play_mode = self.play_mode_to_id[aux]
|
||||||
|
else:
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}Unknown tag inside 'GS': {tag} at {end}, \nMsg: {exp.decode()}")
|
||||||
|
|
||||||
|
|
||||||
|
elif tag==b'GYR':
|
||||||
|
while True:
|
||||||
|
tag, end, min_depth = self.get_next_tag(end)
|
||||||
|
if min_depth == 0: break
|
||||||
|
|
||||||
|
'''
|
||||||
|
The gyroscope measures the robot's torso angular velocity (rotation rate vector)
|
||||||
|
The angular velocity's orientation is given by the right-hand rule.
|
||||||
|
|
||||||
|
Original reference frame:
|
||||||
|
X:left(-)/right(+) Y:back(-)/front(+) Z:down(-)/up(+)
|
||||||
|
|
||||||
|
New reference frame:
|
||||||
|
X:back(-)/front(+) Y:right(-)/left(+) Z:down(-)/up(+)
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
if tag==b'n':
|
||||||
|
pass
|
||||||
|
elif tag==b'rt':
|
||||||
|
self.world.robot.gyro[1], end = self.read_float(end+1)
|
||||||
|
self.world.robot.gyro[0], end = self.read_float(end+1)
|
||||||
|
self.world.robot.gyro[2], end = self.read_float(end+1)
|
||||||
|
self.world.robot.gyro[1] *= -1
|
||||||
|
else:
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}Unknown tag inside 'GYR': {tag} at {end}, \nMsg: {exp.decode()}")
|
||||||
|
|
||||||
|
|
||||||
|
elif tag==b'ACC':
|
||||||
|
while True:
|
||||||
|
tag, end, min_depth = self.get_next_tag(end)
|
||||||
|
if min_depth == 0: break
|
||||||
|
|
||||||
|
'''
|
||||||
|
The accelerometer measures the acceleration relative to freefall. It will read zero during any type of free fall.
|
||||||
|
When at rest relative to the Earth's surface, it will indicate an upwards acceleration of 9.81m/s^2 (in SimSpark).
|
||||||
|
|
||||||
|
Original reference frame:
|
||||||
|
X:left(-)/right(+) Y:back(-)/front(+) Z:down(-)/up(+)
|
||||||
|
|
||||||
|
New reference frame:
|
||||||
|
X:back(-)/front(+) Y:right(-)/left(+) Z:down(-)/up(+)
|
||||||
|
'''
|
||||||
|
|
||||||
|
if tag==b'n':
|
||||||
|
pass
|
||||||
|
elif tag==b'a':
|
||||||
|
self.world.robot.acc[1], end = self.read_float(end+1)
|
||||||
|
self.world.robot.acc[0], end = self.read_float(end+1)
|
||||||
|
self.world.robot.acc[2], end = self.read_float(end+1)
|
||||||
|
self.world.robot.acc[1] *= -1
|
||||||
|
else:
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}Unknown tag inside 'ACC': {tag} at {end}, \nMsg: {exp.decode()}")
|
||||||
|
|
||||||
|
|
||||||
|
elif tag==b'HJ':
|
||||||
|
while True:
|
||||||
|
tag, end, min_depth = self.get_next_tag(end)
|
||||||
|
if min_depth == 0: break
|
||||||
|
|
||||||
|
if tag==b'n':
|
||||||
|
joint_name, end = self.read_str(end+1)
|
||||||
|
joint_index = Robot.MAP_PERCEPTOR_TO_INDEX[joint_name]
|
||||||
|
elif tag==b'ax':
|
||||||
|
joint_angle, end = self.read_float(end+1)
|
||||||
|
|
||||||
|
#Fix symmetry issues 2/4 (perceptors)
|
||||||
|
if joint_name in Robot.FIX_PERCEPTOR_SET: joint_angle = -joint_angle
|
||||||
|
|
||||||
|
old_angle = self.world.robot.joints_position[joint_index]
|
||||||
|
self.world.robot.joints_speed[joint_index] = (joint_angle - old_angle) / World.STEPTIME * math.pi / 180
|
||||||
|
self.world.robot.joints_position[joint_index] = joint_angle
|
||||||
|
else:
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}Unknown tag inside 'HJ': {tag} at {end}, \nMsg: {exp.decode()}")
|
||||||
|
|
||||||
|
elif tag==b'FRP':
|
||||||
|
while True:
|
||||||
|
tag, end, min_depth = self.get_next_tag(end)
|
||||||
|
if min_depth == 0: break
|
||||||
|
|
||||||
|
'''
|
||||||
|
The reference frame is used for the contact point and force vector applied to that point
|
||||||
|
Note: The force vector is applied to the foot, so it usually points up
|
||||||
|
|
||||||
|
Original reference frame:
|
||||||
|
X:left(-)/right(+) Y:back(-)/front(+) Z:down(-)/up(+)
|
||||||
|
|
||||||
|
New reference frame:
|
||||||
|
X:back(-)/front(+) Y:right(-)/left(+) Z:down(-)/up(+)
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
if tag==b'n':
|
||||||
|
foot_toe_id, end = self.read_str(end+1)
|
||||||
|
self.world.robot.frp[foot_toe_id] = foot_toe_ref = np.empty(6)
|
||||||
|
self.world.robot.feet_toes_last_touch[foot_toe_id] = self.world.time_local_ms
|
||||||
|
self.world.robot.feet_toes_are_touching[foot_toe_id] = True
|
||||||
|
elif tag==b'c':
|
||||||
|
foot_toe_ref[1], end = self.read_float(end+1)
|
||||||
|
foot_toe_ref[0], end = self.read_float(end+1)
|
||||||
|
foot_toe_ref[2], end = self.read_float(end+1)
|
||||||
|
foot_toe_ref[1] *= -1
|
||||||
|
elif tag==b'f':
|
||||||
|
foot_toe_ref[4], end = self.read_float(end+1)
|
||||||
|
foot_toe_ref[3], end = self.read_float(end+1)
|
||||||
|
foot_toe_ref[5], end = self.read_float(end+1)
|
||||||
|
foot_toe_ref[4] *= -1
|
||||||
|
else:
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}Unknown tag inside 'FRP': {tag} at {end}, \nMsg: {exp.decode()}")
|
||||||
|
|
||||||
|
|
||||||
|
elif tag==b'See':
|
||||||
|
self.world.vision_is_up_to_date = True
|
||||||
|
self.world.vision_last_update = self.world.time_local_ms
|
||||||
|
|
||||||
|
while True:
|
||||||
|
tag, end, min_depth = self.get_next_tag(end)
|
||||||
|
if min_depth == 0: break
|
||||||
|
|
||||||
|
tag_bytes = bytes(tag) #since bytearray is not hashable, it cannot be used as key for dictionaries
|
||||||
|
|
||||||
|
if tag==b'G1R' or tag==b'G2R' or tag==b'G1L' or tag==b'G2L':
|
||||||
|
_, end, _ = self.get_next_tag(end)
|
||||||
|
|
||||||
|
c1, end = self.read_float(end+1)
|
||||||
|
c2, end = self.read_float(end+1)
|
||||||
|
c3, end = self.read_float(end+1)
|
||||||
|
|
||||||
|
aux = self.LEFT_SIDE_FLAGS[tag_bytes] if self.world.team_side_is_left else self.RIGHT_SIDE_FLAGS[tag_bytes]
|
||||||
|
self.world.flags_posts[aux] = (c1,c2,c3)
|
||||||
|
|
||||||
|
elif tag==b'F1R' or tag==b'F2R' or tag==b'F1L' or tag==b'F2L':
|
||||||
|
_, end, _ = self.get_next_tag(end)
|
||||||
|
|
||||||
|
c1, end = self.read_float(end+1)
|
||||||
|
c2, end = self.read_float(end+1)
|
||||||
|
c3, end = self.read_float(end+1)
|
||||||
|
|
||||||
|
aux = self.LEFT_SIDE_FLAGS[tag_bytes] if self.world.team_side_is_left else self.RIGHT_SIDE_FLAGS[tag_bytes]
|
||||||
|
self.world.flags_corners[aux] = (c1,c2,c3)
|
||||||
|
|
||||||
|
elif tag==b'B':
|
||||||
|
_, end, _ = self.get_next_tag(end)
|
||||||
|
|
||||||
|
self.world.ball_rel_head_sph_pos[0], end = self.read_float(end+1)
|
||||||
|
self.world.ball_rel_head_sph_pos[1], end = self.read_float(end+1)
|
||||||
|
self.world.ball_rel_head_sph_pos[2], end = self.read_float(end+1)
|
||||||
|
self.world.ball_rel_head_cart_pos = M.deg_sph2cart(self.world.ball_rel_head_sph_pos)
|
||||||
|
self.world.ball_is_visible = True
|
||||||
|
self.world.ball_last_seen = self.world.time_local_ms
|
||||||
|
|
||||||
|
elif tag==b'mypos':
|
||||||
|
|
||||||
|
self.world.robot.cheat_abs_pos[0], end = self.read_float(end+1)
|
||||||
|
self.world.robot.cheat_abs_pos[1], end = self.read_float(end+1)
|
||||||
|
self.world.robot.cheat_abs_pos[2], end = self.read_float(end+1)
|
||||||
|
|
||||||
|
elif tag==b'myorien':
|
||||||
|
|
||||||
|
self.world.robot.cheat_ori, end = self.read_float(end+1)
|
||||||
|
|
||||||
|
elif tag==b'ballpos':
|
||||||
|
|
||||||
|
c1, end = self.read_float(end+1)
|
||||||
|
c2, end = self.read_float(end+1)
|
||||||
|
c3, end = self.read_float(end+1)
|
||||||
|
|
||||||
|
self.world.ball_cheat_abs_vel[0] = (c1 - self.world.ball_cheat_abs_pos[0]) / World.VISUALSTEP
|
||||||
|
self.world.ball_cheat_abs_vel[1] = (c2 - self.world.ball_cheat_abs_pos[1]) / World.VISUALSTEP
|
||||||
|
self.world.ball_cheat_abs_vel[2] = (c3 - self.world.ball_cheat_abs_pos[2]) / World.VISUALSTEP
|
||||||
|
|
||||||
|
self.world.ball_cheat_abs_pos[0] = c1
|
||||||
|
self.world.ball_cheat_abs_pos[1] = c2
|
||||||
|
self.world.ball_cheat_abs_pos[2] = c3
|
||||||
|
|
||||||
|
elif tag==b'P':
|
||||||
|
|
||||||
|
while True:
|
||||||
|
previous_depth = self.depth
|
||||||
|
previous_end = end
|
||||||
|
tag, end, min_depth = self.get_next_tag(end)
|
||||||
|
if min_depth < 2: #if =1 we are still inside 'See', if =0 we are already outside 'See'
|
||||||
|
end = previous_end #The "P" tag is special because it's the only variable particle inside 'See'
|
||||||
|
self.depth = previous_depth
|
||||||
|
break # we restore the previous tag, and let 'See' handle it
|
||||||
|
|
||||||
|
if tag==b'team':
|
||||||
|
player_team, end = self.read_str(end+1)
|
||||||
|
is_teammate = bool(player_team == self.world.team_name)
|
||||||
|
if self.world.team_name_opponent is None and not is_teammate: #register opponent team name
|
||||||
|
self.world.team_name_opponent = player_team
|
||||||
|
elif tag==b'id':
|
||||||
|
player_id, end = self.read_int(end+1)
|
||||||
|
player = self.world.teammates[player_id-1] if is_teammate else self.world.opponents[player_id-1]
|
||||||
|
player.body_parts_cart_rel_pos = dict() #reset seen body parts
|
||||||
|
player.is_visible = True
|
||||||
|
elif tag==b'llowerarm' or tag==b'rlowerarm' or tag==b'lfoot' or tag==b'rfoot' or tag==b'head':
|
||||||
|
tag_str = tag.decode()
|
||||||
|
_, end, _ = self.get_next_tag(end)
|
||||||
|
|
||||||
|
c1, end = self.read_float(end+1)
|
||||||
|
c2, end = self.read_float(end+1)
|
||||||
|
c3, end = self.read_float(end+1)
|
||||||
|
|
||||||
|
if is_teammate:
|
||||||
|
self.world.teammates[player_id-1].body_parts_sph_rel_pos[tag_str] = (c1,c2,c3)
|
||||||
|
self.world.teammates[player_id-1].body_parts_cart_rel_pos[tag_str] = M.deg_sph2cart((c1,c2,c3))
|
||||||
|
else:
|
||||||
|
self.world.opponents[player_id-1].body_parts_sph_rel_pos[tag_str] = (c1,c2,c3)
|
||||||
|
self.world.opponents[player_id-1].body_parts_cart_rel_pos[tag_str] = M.deg_sph2cart((c1,c2,c3))
|
||||||
|
else:
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}Unknown tag inside 'P': {tag} at {end}, \nMsg: {exp.decode()}")
|
||||||
|
|
||||||
|
elif tag==b'L':
|
||||||
|
l = self.world.lines[self.world.line_count]
|
||||||
|
|
||||||
|
_, end, _ = self.get_next_tag(end)
|
||||||
|
l[0], end = self.read_float(end+1)
|
||||||
|
l[1], end = self.read_float(end+1)
|
||||||
|
l[2], end = self.read_float(end+1)
|
||||||
|
_, end, _ = self.get_next_tag(end)
|
||||||
|
l[3], end = self.read_float(end+1)
|
||||||
|
l[4], end = self.read_float(end+1)
|
||||||
|
l[5], end = self.read_float(end+1)
|
||||||
|
|
||||||
|
if np.isnan(l).any():
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}Received field line with NaNs {l}")
|
||||||
|
else:
|
||||||
|
self.world.line_count += 1 #accept field line if there are no NaNs
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}Unknown tag inside 'see': {tag} at {end}, \nMsg: {exp.decode()}")
|
||||||
|
|
||||||
|
|
||||||
|
elif tag==b'hear':
|
||||||
|
|
||||||
|
team_name, end = self.read_str(end+1)
|
||||||
|
|
||||||
|
if team_name == self.world.team_name: # discard message if it's not from our team
|
||||||
|
|
||||||
|
timestamp, end = self.read_float(end+1)
|
||||||
|
|
||||||
|
if self.exp[end+1] == ord('s'): # this message was sent by oneself
|
||||||
|
direction, end = "self", end+5
|
||||||
|
else: # this message was sent by teammate
|
||||||
|
direction, end = self.read_float(end+1)
|
||||||
|
|
||||||
|
msg, end = self.read_bytes(end+1)
|
||||||
|
self.hear_callback(msg, direction, timestamp)
|
||||||
|
|
||||||
|
|
||||||
|
tag, end, _ = self.get_next_tag(end)
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.world.log(f"{self.LOG_PREFIX}Unknown root tag: {tag} at {end}, \nMsg: {exp.decode()}")
|
||||||
|
tag, end, min_depth = self.get_next_tag(end)
|
||||||
|
|
@ -0,0 +1,14 @@
|
|||||||
|
src = $(wildcard *.cpp)
|
||||||
|
obj = $(src:.c=.o)
|
||||||
|
|
||||||
|
CFLAGS = -O3 -shared -std=c++11 -fPIC -Wall $(PYBIND_INCLUDES)
|
||||||
|
|
||||||
|
all: $(obj)
|
||||||
|
g++ $(CFLAGS) -o a_star.so $^
|
||||||
|
|
||||||
|
debug: $(filter-out lib_main.cpp,$(obj))
|
||||||
|
g++ -O0 -std=c++14 -Wall -g -o debug.bin debug_main.cc $^
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -f $(obj) all
|
@ -0,0 +1,918 @@
|
|||||||
|
#include "a_star.h"
|
||||||
|
#include "expansion_groups.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#define SQRT2 1.414213562373095f
|
||||||
|
#define LINES 321
|
||||||
|
#define COLS 221
|
||||||
|
#define MAX_RADIUS 5 // obstacle max radius in meters
|
||||||
|
#define IN_GOAL_LINE 312 // target line when go_to_goal is 'true' (312 -> 15.2m)
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
using std::chrono::duration_cast;
|
||||||
|
using std::chrono::microseconds;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Map dimensions: 32m*22m
|
||||||
|
col 0 ... col 220
|
||||||
|
line 0
|
||||||
|
--- our goal --- line 1
|
||||||
|
| | line 2
|
||||||
|
| | line 3
|
||||||
|
|--------------| ...
|
||||||
|
| | line 317
|
||||||
|
| | line 318
|
||||||
|
-- their goal -- line 319
|
||||||
|
line 320
|
||||||
|
|
||||||
|
[(H)ard wall: -3, (S)oft wall: -2, (E)mpty: 0, 0 < Cost < inf]
|
||||||
|
*/
|
||||||
|
|
||||||
|
// build board cost statically
|
||||||
|
#define H27 -3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3
|
||||||
|
#define S11 -2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2
|
||||||
|
#define S19 S11,-2,-2,-2,-2,-2,-2,-2,-2
|
||||||
|
#define S97 S11,S11,S11,S11,S11,S11,S11,S11,-2,-2,-2,-2,-2,-2,-2,-2,-2
|
||||||
|
#define S98 S11,S11,S11,S11,S11,S11,S11,S11,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2
|
||||||
|
#define S221 S98,S98,S11,S11,-2,-2,-2
|
||||||
|
#define E19 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
#define E197 E19,E19,E19,E19,E19,E19,E19,E19,E19,E19,0,0,0,0,0,0,0
|
||||||
|
#define L0 S221 // Line 0: soft W
|
||||||
|
#define L0_1 L0,L0
|
||||||
|
#define L2 S97,H27,S97 // Line 2: soft W, (goal post, back net, goal post), soft W
|
||||||
|
#define L2_5 L2,L2,L2,L2
|
||||||
|
#define L6 S98,-3,-3,-3,S19,-3,-3,-3,S98 // Line 6: soft W, goal post, soft W, goal post, soft W
|
||||||
|
#define L6_10 L6,L6,L6,L6,L6
|
||||||
|
#define L11 S98,-2,-3,-3,S19,-3,-3,-2,S98 // Line 11:soft W, empty field, goal post, soft W, goal post,empty field, soft W
|
||||||
|
#define L12 S11,-2,E197,-2,S11 // Line 12:soft W, empty field, soft W
|
||||||
|
|
||||||
|
#define L12x33 L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12
|
||||||
|
#define LIN12_308 L12x33,L12x33,L12x33,L12x33,L12x33,L12x33,L12x33,L12x33,L12x33
|
||||||
|
|
||||||
|
#define L309 S98,-2,-3,-3,E19,-3,-3,-2,S98 // Line 309: soft W, empty field, goal post, empty field, goal post,empty field, soft W
|
||||||
|
#define L310 S98,-3,-3,-3,E19,-3,-3,-3,S98 // Line 310: soft W, goal post, inside goal, goal post, soft W
|
||||||
|
#define L310_314 L310,L310,L310,L310,L310
|
||||||
|
|
||||||
|
using std::min;
|
||||||
|
using std::max;
|
||||||
|
|
||||||
|
|
||||||
|
#define MIN min_node
|
||||||
|
Node* min_node; // non-expanded node with lowest predicted total cost (f)
|
||||||
|
|
||||||
|
namespace open{
|
||||||
|
|
||||||
|
Node* insert(Node* new_node, Node* root) {
|
||||||
|
|
||||||
|
new_node->left = nullptr;
|
||||||
|
new_node->right = nullptr;
|
||||||
|
|
||||||
|
// Empty BST, return without saving min_node
|
||||||
|
if(root == nullptr){
|
||||||
|
new_node->up = nullptr;
|
||||||
|
MIN = new_node; // save min node for fast access
|
||||||
|
return new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If new_node is the new min node
|
||||||
|
if(new_node->f < MIN->f){
|
||||||
|
MIN->left = new_node;
|
||||||
|
new_node->up = MIN;
|
||||||
|
MIN = new_node;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* node = root;
|
||||||
|
float key = new_node->f;
|
||||||
|
|
||||||
|
while(true){
|
||||||
|
if (key < node->f)
|
||||||
|
if(node->left == nullptr){
|
||||||
|
node->left = new_node;
|
||||||
|
break;
|
||||||
|
}else{
|
||||||
|
node = node->left;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if(node->right == nullptr){
|
||||||
|
node->right = new_node;
|
||||||
|
break;
|
||||||
|
}else{
|
||||||
|
node = node->right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_node->up = node;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Remove min node
|
||||||
|
Node* pop(Node* root) {
|
||||||
|
|
||||||
|
// Minimum node can have right child, but not left child
|
||||||
|
if (MIN->right == nullptr){
|
||||||
|
if(MIN == root){ //------(A)------ min node is root and has no children
|
||||||
|
return nullptr; // BST is empty
|
||||||
|
}
|
||||||
|
MIN->up->left = nullptr; //------(B)------ min node has no children but has parent
|
||||||
|
MIN = MIN->up;
|
||||||
|
}else{
|
||||||
|
if(MIN == root){ //------(C)------ min node is root and has right child
|
||||||
|
MIN = MIN->right;
|
||||||
|
root = MIN;
|
||||||
|
root->up = nullptr;
|
||||||
|
|
||||||
|
while(MIN->left != nullptr){ // update new min node
|
||||||
|
MIN = MIN->left;
|
||||||
|
}
|
||||||
|
|
||||||
|
return root; // right child is now root
|
||||||
|
}
|
||||||
|
MIN->right->up = MIN->up; //------(D)------ min node has right child and parent
|
||||||
|
MIN->up->left = MIN->right;
|
||||||
|
|
||||||
|
MIN = MIN->right;
|
||||||
|
while(MIN->left != nullptr){ // update new min node
|
||||||
|
MIN = MIN->left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Remove specific node
|
||||||
|
Node* delete_node(Node* node, Node* root) {
|
||||||
|
|
||||||
|
if(node == MIN){ // remove min node
|
||||||
|
return pop(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(node->left==nullptr and node->right==nullptr){ //------(A)------ node has no children (it can't be root, otherwise it would be min node)
|
||||||
|
// Redirect incoming connection
|
||||||
|
if(node->up->left == node){ node->up->left = nullptr; }
|
||||||
|
else{ node->up->right = nullptr; }
|
||||||
|
|
||||||
|
}else if(node->left==nullptr){ //------(B)------ node has right child (it can't be root, otherwise it would be min node)
|
||||||
|
// Redirect incoming connections
|
||||||
|
node->right->up = node->up;
|
||||||
|
if(node->up->left == node){ node->up->left = node->right; }
|
||||||
|
else{ node->up->right = node->right; }
|
||||||
|
|
||||||
|
}else if(node->right==nullptr){ //------(C)------ node has left child
|
||||||
|
if(node == root){
|
||||||
|
node->left->up = nullptr;
|
||||||
|
return node->left; // left child becomes root
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect incoming connections (if not root)
|
||||||
|
node->left->up = node->up;
|
||||||
|
if(node->up->left == node){ node->up->left = node->left; }
|
||||||
|
else{ node->up->right = node->left; }
|
||||||
|
|
||||||
|
}else{ //------(D)------ node has 2 children
|
||||||
|
Node *successor = node->right;
|
||||||
|
if(successor->left == nullptr){ //----- if successor is the node's right child (successor has no left child)
|
||||||
|
|
||||||
|
//-------------- successor replaces node
|
||||||
|
|
||||||
|
// Outgoing connections (successor's right child is not changed)
|
||||||
|
successor->left = node->left;
|
||||||
|
successor->up = node->up; // if node is root this is also ok
|
||||||
|
|
||||||
|
// Incoming connections
|
||||||
|
node->left->up = successor;
|
||||||
|
|
||||||
|
if(node == root){ return successor; } // successor becomes root
|
||||||
|
|
||||||
|
// Incoming connections (if not root)
|
||||||
|
if(node->up->left == node){ node->up->left = successor; }
|
||||||
|
else{ node->up->right = successor; }
|
||||||
|
|
||||||
|
}else{ //------ if successor is deeper (successor has no left child, and is itself a left child)
|
||||||
|
do{
|
||||||
|
successor = successor->left;
|
||||||
|
}while(successor->left != nullptr);
|
||||||
|
|
||||||
|
//-------------- Remove successor by redirecting its incoming connections
|
||||||
|
if(successor->right==nullptr){ // no children
|
||||||
|
successor->up->left = nullptr;
|
||||||
|
}else{
|
||||||
|
successor->up->left = successor->right;
|
||||||
|
successor->right->up = successor->up;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------- successor replaces node
|
||||||
|
|
||||||
|
// Outgoing connections
|
||||||
|
successor->left = node->left;
|
||||||
|
successor->right = node->right;
|
||||||
|
successor->up = node->up; // if node is root this is also ok
|
||||||
|
|
||||||
|
// Incoming connections
|
||||||
|
node->left->up = successor;
|
||||||
|
node->right->up = successor;
|
||||||
|
|
||||||
|
if(node == root){ return successor; } // successor becomes root
|
||||||
|
|
||||||
|
// Incoming connections (if not root)
|
||||||
|
if(node->up->left == node){ node->up->left = successor; }
|
||||||
|
else{ node->up->right = successor; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inorder Traversal
|
||||||
|
// void inorder(Node* root, Node* board) {
|
||||||
|
// if (root != nullptr) {
|
||||||
|
// // Traverse left
|
||||||
|
// inorder(root->left, board);
|
||||||
|
|
||||||
|
// // Traverse root
|
||||||
|
// std::cout << (root-board)/COLS << " " << (root-board)%COLS << " -> ";
|
||||||
|
|
||||||
|
// // Traverse right
|
||||||
|
// inorder(root->right, board);
|
||||||
|
|
||||||
|
// }
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline int x_to_line(float x){
|
||||||
|
return int(fmaxf(0.f, fminf(10*x+160, 320.f)) + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int y_to_col(float y){
|
||||||
|
return int(fmaxf(0.f, fminf(10*y+110, 220.f)) + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float diagonal_distance(bool go_to_goal, int line, int col, int end_l, int end_c){
|
||||||
|
// diagonal distance - adapted from http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
|
||||||
|
int dl, dc;
|
||||||
|
if( go_to_goal ){
|
||||||
|
dl = abs(IN_GOAL_LINE - line);
|
||||||
|
if (col>119) { dc = col-119; }
|
||||||
|
else if (col<101) { dc = 101-col; }
|
||||||
|
else { dc = 0; }
|
||||||
|
}else{
|
||||||
|
dl = abs(line - end_l);
|
||||||
|
dc = abs(col - end_c);
|
||||||
|
}
|
||||||
|
return (dl + dc) - 0.585786437626905f * min(dl,dc);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Node* expand_child(Node* open_root, float cost, float wall_index, Node* curr_node, Node* board, int pos, int state,
|
||||||
|
bool go_to_goal, int line, int col, int end_l, int end_c, unsigned int* node_state, float extra ){
|
||||||
|
// child can be as inaccessible as current pos (but there is a cost penalty to avoid inaccessible paths)
|
||||||
|
if(cost <= wall_index){
|
||||||
|
cost = 100.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// g (min cost from start to n)
|
||||||
|
float g = curr_node->g + extra + std::fmaxf(0.f,cost); // current cost + child distance
|
||||||
|
|
||||||
|
Node* child = &board[pos];
|
||||||
|
|
||||||
|
// if child is already in the open set
|
||||||
|
if (state){
|
||||||
|
if (g >= child->g){
|
||||||
|
return open_root; // if not an improvement, we discard the new child
|
||||||
|
}else{
|
||||||
|
open_root = open::delete_node(child, open_root); // if it is an improvement: remove reference, update it, add it again in correct order
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
node_state[pos] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// f (prediction of min total cost passing through n)
|
||||||
|
float f = g + diagonal_distance(go_to_goal,line,col,end_l,end_c);
|
||||||
|
|
||||||
|
child->g = g;
|
||||||
|
child->f = f;
|
||||||
|
child->parent = curr_node;
|
||||||
|
return open::insert(child, open_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float final_path[2050];
|
||||||
|
int final_path_size;
|
||||||
|
|
||||||
|
inline void build_final_path(Node* const best_node, const Node* board, float status, const bool override_end=false, const float end_x=0, const float end_y=0){
|
||||||
|
// Node* pt = best_node;
|
||||||
|
// while( pt != nullptr ){
|
||||||
|
// int pos = pt - board;
|
||||||
|
// board_cost[pos] = -4;
|
||||||
|
// pt = pt->parent;
|
||||||
|
// }
|
||||||
|
// std::cout << "\n";
|
||||||
|
// for(int l=l_min; l<=l_max; l++){
|
||||||
|
// for(int c=c_min; c<=c_max; c++){
|
||||||
|
// //[(H)ard wall: -3, (S)oft wall: -2, (E)mpty: 0, 0 < Cost < inf]
|
||||||
|
// //if (board[l][c].closed) std::cout << "o";
|
||||||
|
// if (board_cost[l*COLS+c] == -3) std::cout << "h";
|
||||||
|
// else if (board_cost[l*COLS+c] == -2) std::cout << "s";
|
||||||
|
// else if (board_cost[l*COLS+c] == -4) std::cout << ".";
|
||||||
|
// else if (board_cost[l*COLS+c] == -1) std::cout << "g";
|
||||||
|
// else if (board_cost[l*COLS+c] == 0 and node_state[l*COLS+c]==2) std::cout << "o";
|
||||||
|
// else if (board_cost[l*COLS+c] == 0) std::cout << " ";
|
||||||
|
// //ele ainda nao sabe ler hard walls
|
||||||
|
// else std::cout << int(board_cost[l*COLS+c]+0.5f);
|
||||||
|
// }
|
||||||
|
// std::cout << "\n";
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Using 'current_node' would suffice if A* reaches the objective (but 'best_node' works with impossible paths or timeout)
|
||||||
|
Node* ptr = best_node;
|
||||||
|
int counter=0;
|
||||||
|
do{
|
||||||
|
ptr = ptr->parent;
|
||||||
|
counter++;
|
||||||
|
}while( ptr != nullptr );
|
||||||
|
|
||||||
|
final_path_size = min(counter*2,2048);
|
||||||
|
|
||||||
|
ptr = best_node;
|
||||||
|
int i = final_path_size-1;
|
||||||
|
|
||||||
|
// if enabled, replace end point with correct coordinates instead of discrete version
|
||||||
|
if(override_end){
|
||||||
|
final_path[i--] = end_y;
|
||||||
|
final_path[i--] = end_x;
|
||||||
|
ptr = ptr->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(; i>0;){
|
||||||
|
final_path[i--] = ((ptr-board) % COLS)/10.f-11.f; // y
|
||||||
|
final_path[i--] = ((ptr-board) / COLS)/10.f-16.f; // x
|
||||||
|
ptr = ptr->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add status (& increment path size)
|
||||||
|
final_path[final_path_size++] = status; // 0-success, 1-timeout, 2-impossible, 3-no obstacles(this one is not done in this function)
|
||||||
|
|
||||||
|
// add cost (& increment path size)
|
||||||
|
final_path[final_path_size++] = best_node->g / 10.f; // min. A* cost from start to best_node
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if line segment 'ab' intersects either goal (considering the unreachable area)
|
||||||
|
* - This function assumes that 'a' and 'b' are two points outside the unreachable goal area
|
||||||
|
* - Therefore, 'ab' must enter and exit the goal unreachable area
|
||||||
|
* - To detect this, we consider only the intersection of 'ab' and the goal outer borders (back+sides)
|
||||||
|
* - The front should already be covered by independent goal posts checks
|
||||||
|
*/
|
||||||
|
inline bool does_intersect_any_goal(float a_x, float a_y, float b_x, float b_y){
|
||||||
|
|
||||||
|
float ab_x = b_x - a_x;
|
||||||
|
float ab_y = b_y - a_y;
|
||||||
|
float k;
|
||||||
|
|
||||||
|
if(ab_x != 0){ // Check if 'ab' and goal back is noncollinear (collinear intersections are ignored)
|
||||||
|
|
||||||
|
k = (15.75-a_x) / ab_x; // a_x + ab_x*k = 15.75
|
||||||
|
if (k >= 0 and k <= 1 and fabsf(a_y + ab_y * k) <= 1.25){ // collision (intersection_y = a_y + ab_y*k)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
k = (-15.75-a_x) / ab_x; // a_x + ab_x*k = -15.75
|
||||||
|
if (k >= 0 and k <= 1 and fabsf(a_y + ab_y * k) <= 1.25){ // collision (intersection_y = a_y + ab_y*k)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ab_y != 0){ // Check if 'ab' and goal sides are noncollinear (collinear intersections are ignored)
|
||||||
|
|
||||||
|
k = (1.25-a_y) / ab_y; // a_y + ab_y*k = 1.25
|
||||||
|
if( k >= 0 and k <= 1){
|
||||||
|
float intersection_x_abs = fabsf(a_x + ab_x * k);
|
||||||
|
if( intersection_x_abs >= 15 and intersection_x_abs <= 15.75 ){ // check one side for both goals at same time
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k = (-1.25-a_y) / ab_y; // a_y + ab_y*k = -1.25
|
||||||
|
if( k >= 0 and k <= 1){
|
||||||
|
float intersection_x_abs = fabsf(a_x + ab_x * k);
|
||||||
|
if( intersection_x_abs >= 15 and intersection_x_abs <= 15.75 ){ // check one side for both goals at same time
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add space cushion near the midlines and endlines
|
||||||
|
*/
|
||||||
|
inline void add_space_cushion(float board_cost[]){
|
||||||
|
|
||||||
|
#define CUSHION_WIDTH 6
|
||||||
|
|
||||||
|
// opponent goal line
|
||||||
|
for(int i=0; i<CUSHION_WIDTH; i++){
|
||||||
|
int ii = (i+12)*COLS;
|
||||||
|
for(int j=12+i; j<209-i; j++){
|
||||||
|
board_cost[ii+j] = CUSHION_WIDTH-i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// our goal line
|
||||||
|
for(int i=0; i<CUSHION_WIDTH; i++){
|
||||||
|
int ii = (308-i)*COLS;
|
||||||
|
for(int j=12+i; j<99; j++){
|
||||||
|
board_cost[ii+j] = CUSHION_WIDTH-i;
|
||||||
|
board_cost[ii+220-j] = CUSHION_WIDTH-i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sidelines
|
||||||
|
for(int i=0; i<CUSHION_WIDTH; i++){
|
||||||
|
for(int j=(13+i)*COLS; j<(308-i)*COLS; j+=COLS){
|
||||||
|
board_cost[j+i+12] = CUSHION_WIDTH-i;
|
||||||
|
board_cost[j-i+208] = CUSHION_WIDTH-i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function checks if a straight path from start to end is obstructed by obstacles
|
||||||
|
* Returning 'false' means that we do not need A* to solve this problem
|
||||||
|
*
|
||||||
|
* Normal case (start != end):
|
||||||
|
* The path is obstructed if it intersects any hard or soft circumference
|
||||||
|
* Special case (start == end):
|
||||||
|
* The path is obstructed if start is inside any hard circumference
|
||||||
|
*/
|
||||||
|
bool is_path_obstructed(float start_x, float start_y, float end_x, float end_y, float given_obstacles[],
|
||||||
|
int given_obst_size, bool go_to_goal, int wall_index, float board_cost[]){
|
||||||
|
|
||||||
|
|
||||||
|
// Restrict start coordinates to map
|
||||||
|
start_x = max(-16.f, min(start_x, 16.f));
|
||||||
|
start_y = max(-11.f, min(start_y, 11.f));
|
||||||
|
|
||||||
|
int s_lin = x_to_line(start_x);
|
||||||
|
int e_lin = x_to_line(end_x);
|
||||||
|
int s_col = y_to_col(start_y);
|
||||||
|
int e_col = y_to_col(end_y);
|
||||||
|
float s_cost = board_cost[s_lin*COLS+s_col];
|
||||||
|
float e_cost = board_cost[e_lin*COLS+e_col];
|
||||||
|
|
||||||
|
// Path is obvious if start/end are is same or adjacent cells
|
||||||
|
bool is_near = abs(s_lin - e_lin) <= 1 and abs(s_col - e_col) <= 1;
|
||||||
|
|
||||||
|
// Let A* handle it if the start position is unreachable or (out of bounds is not allowed && is nearly out of bounds && not near end)
|
||||||
|
if(s_cost <= wall_index or (!is_near and s_cost > 0)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (go_to_goal){ // This is a safe target. If it generates a collision with any goal post, we use A* instead.
|
||||||
|
end_x = 15.2;
|
||||||
|
end_y = max(-0.8f, min(start_y, 0.8f));
|
||||||
|
}else{ // Restrict end coordinates to map
|
||||||
|
end_x = max(-16.f, min(end_x, 16.f));
|
||||||
|
end_y = max(-11.f, min(end_y, 11.f));
|
||||||
|
|
||||||
|
// Let A* handle it if the end position is unreachable or (is nearly out of bounds && out of bounds is not allowed && not near end)
|
||||||
|
if(e_cost <= wall_index or (!is_near and e_cost > 0)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if path intersects either goal (considering the unreachable area)
|
||||||
|
* - at this point we know that 'start' and 'end' are reachable
|
||||||
|
* - Therefore, the path must enter and exit the goal unreachable area for an intersection to exist
|
||||||
|
* - To detect this, we consider only the intersection of the path and the goal outer borders (back+sides)
|
||||||
|
* - The front is covered next by goal posts checks
|
||||||
|
*/
|
||||||
|
if (does_intersect_any_goal(start_x, start_y, end_x, end_y)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ----------------------- List all obstacles: given obstacles + goal posts
|
||||||
|
* note that including the goal posts in the given obstacles is not a bad idea since the default
|
||||||
|
* goal posts we add here provide no space cushion (which may be needed if the robot is not precise)
|
||||||
|
* values explanation:
|
||||||
|
* - goal post location (tested in simulator, collision with robot, robot is sideways to the goal post and arms are close to body)
|
||||||
|
* - hard radius (tested in the same way, robot collides when closer than 0.15m from goal post border)
|
||||||
|
* post radius 0.02 + agent radius 0.15 = 0.17
|
||||||
|
* - largest radius (equivalent to hard radius, since there is no soft radius)
|
||||||
|
*/
|
||||||
|
|
||||||
|
float obst[given_obst_size*4/5+16] = {
|
||||||
|
15.02, 1.07, 0.17, 0.17,
|
||||||
|
15.02, -1.07, 0.17, 0.17,
|
||||||
|
-15.02, 1.07, 0.17, 0.17,
|
||||||
|
-15.02, -1.07, 0.17, 0.17
|
||||||
|
}; // x, y, hard radius, largest radius
|
||||||
|
|
||||||
|
|
||||||
|
int obst_size = 16;
|
||||||
|
|
||||||
|
for(int i=0; i<given_obst_size; i+=5){
|
||||||
|
obst[obst_size++] = given_obstacles[i]; // x
|
||||||
|
obst[obst_size++] = given_obstacles[i+1]; // y
|
||||||
|
obst[obst_size++] = fmaxf( 0, fminf(given_obstacles[i+2], MAX_RADIUS) ); // hard radius
|
||||||
|
obst[obst_size++] = fmaxf( 0, fminf(max(given_obstacles[i+2], given_obstacles[i+3]), MAX_RADIUS) ); // largest radius
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------ Special case (start ~= end): the path is obstructed if start or end are inside any hard circumference
|
||||||
|
|
||||||
|
if( is_near ){
|
||||||
|
for(int ob=0; ob<obst_size; ob+=4){
|
||||||
|
float c_x = obst[ob]; // obstacle center
|
||||||
|
float c_y = obst[ob+1]; // obstacle center
|
||||||
|
float hard_radius = obst[ob+2]; // hard radius
|
||||||
|
float r_sq = hard_radius * hard_radius; // squared radius
|
||||||
|
|
||||||
|
float sc_x = c_x - start_x;
|
||||||
|
float sc_y = c_y - start_y;
|
||||||
|
float ec_x = c_x - end_x;
|
||||||
|
float ec_y = c_y - end_y;
|
||||||
|
|
||||||
|
if(sc_x*sc_x + sc_y*sc_y <= r_sq or ec_x*ec_x + ec_y*ec_y <= r_sq){ // check distance: center<->start center<->end
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
|
||||||
|
//-------------------- Normal case (start !~= end): the path is obstructed if it intersects any hard or soft circumference
|
||||||
|
|
||||||
|
// for each obstacle: check if circle intersects line segment (start-end)
|
||||||
|
for(int ob=0; ob<obst_size; ob+=4){
|
||||||
|
float c_x = obst[ob]; // obstacle center
|
||||||
|
float c_y = obst[ob+1]; // obstacle center
|
||||||
|
float largest_radius = obst[ob+3]; // largest radius
|
||||||
|
float r_sq = largest_radius * largest_radius; // squared radius
|
||||||
|
|
||||||
|
float sc_x = c_x - start_x;
|
||||||
|
float sc_y = c_y - start_y;
|
||||||
|
float se_x = end_x - start_x;
|
||||||
|
float se_y = end_y - start_y;
|
||||||
|
|
||||||
|
float sc_proj_scale = (sc_x*se_x + sc_y*se_y) / (se_x*se_x + se_y*se_y); // scale = projection length / target vector length
|
||||||
|
float sc_proj_x = se_x * sc_proj_scale; // projection of start->center onto start->end
|
||||||
|
float sc_proj_y = se_y * sc_proj_scale; // projection of start->center onto start->end
|
||||||
|
|
||||||
|
// check if projection falls on top of trajectory (start->projection = k * start->end)
|
||||||
|
float k = abs(se_x)>abs(se_y) ? sc_proj_x/se_x : sc_proj_y/se_y; // we use the largest dimension of start->end to avoid division by 0
|
||||||
|
|
||||||
|
if(k <= 0){
|
||||||
|
if(sc_x*sc_x + sc_y*sc_y <= r_sq){ // check distance: center<->start
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}else if(k >= 1){
|
||||||
|
float ec_x = c_x - end_x;
|
||||||
|
float ec_y = c_y - end_y;
|
||||||
|
if(ec_x*ec_x + ec_y*ec_y <= r_sq){ // check distance: center<->end
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
float proj_c_x = c_x - (sc_proj_x + start_x);
|
||||||
|
float proj_c_y = c_y - (sc_proj_y + start_y);
|
||||||
|
if(proj_c_x*proj_c_x + proj_c_y*proj_c_y <= r_sq){ // check distance: center<->projection
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float path_x = end_x - start_x;
|
||||||
|
float path_y = end_y - start_y;
|
||||||
|
|
||||||
|
final_path_size = 6;
|
||||||
|
final_path[0] = start_x;
|
||||||
|
final_path[1] = start_y;
|
||||||
|
final_path[2] = end_x;
|
||||||
|
final_path[3] = end_y;
|
||||||
|
final_path[4] = 3; // status: 3-no obstacles
|
||||||
|
final_path[5] = sqrtf(path_x*path_x+path_y*path_y) + max(0.f, e_cost/10.f); // min. A* cost from start to end (e_cost is added even if start==end to help debug cell costs)
|
||||||
|
|
||||||
|
return false; // no obstruction was found
|
||||||
|
}
|
||||||
|
|
||||||
|
// opponent players + active player + restricted areas (from referee)
|
||||||
|
// data:
|
||||||
|
// [start x][start y]
|
||||||
|
// [allow out of bounds?][go to goal?]
|
||||||
|
// [optional target x][optional target y]
|
||||||
|
// [timeout]
|
||||||
|
// [x][y][hard radius][soft radius][force]
|
||||||
|
void astar(float params[], int params_size){
|
||||||
|
|
||||||
|
auto t1 = high_resolution_clock::now();
|
||||||
|
|
||||||
|
const float s_x = params[0]; // start x
|
||||||
|
const float s_y = params[1]; // start y
|
||||||
|
const bool allow_out_of_bounds = params[2];
|
||||||
|
const int wall_index = allow_out_of_bounds ? -3 : -2; // (cost <= wall_index) means 'unreachable'
|
||||||
|
const bool go_to_goal = params[3];
|
||||||
|
const float opt_t_x = params[4]; // optional target x
|
||||||
|
const float opt_t_y = params[5]; // optional target y
|
||||||
|
const int timeout_us = params[6];
|
||||||
|
float* obstacles = ¶ms[7];
|
||||||
|
int obst_size = params_size-7; // size of obstacles array
|
||||||
|
|
||||||
|
//======================================================== Populate board 0: add field layout
|
||||||
|
float board_cost[LINES*COLS] = {L0_1,L2_5,L6_10,L11,LIN12_308,L309,L310_314,L2_5,L0_1};
|
||||||
|
|
||||||
|
if (!allow_out_of_bounds){ // add cost to getting near sideline or endline (except near goal)
|
||||||
|
add_space_cushion(board_cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================================== Check if path is obstructed
|
||||||
|
|
||||||
|
if (!is_path_obstructed(s_x, s_y, opt_t_x, opt_t_y, obstacles, obst_size, go_to_goal, wall_index, board_cost)){
|
||||||
|
return; // return if path is not obstructed
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================================== Define board basics (start, end, limits)
|
||||||
|
|
||||||
|
// if the start point is out of field, it is brought in
|
||||||
|
const int start_l = x_to_line(s_x);
|
||||||
|
const int start_c = y_to_col(s_y);
|
||||||
|
const int start_pos = start_l * COLS + start_c;
|
||||||
|
|
||||||
|
// define objective (go to goal or a specific point)
|
||||||
|
int end_l, end_c;
|
||||||
|
if(!go_to_goal){
|
||||||
|
end_l = x_to_line(opt_t_x);
|
||||||
|
end_c = y_to_col(opt_t_y);
|
||||||
|
}else{
|
||||||
|
end_l = IN_GOAL_LINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// define board limits considering the initial and final positions (and obstacles in the next section, and goals after that)
|
||||||
|
int l_min = min(start_l, end_l);
|
||||||
|
int l_max = max(start_l, end_l);
|
||||||
|
int c_min, c_max;
|
||||||
|
if(go_to_goal){
|
||||||
|
c_min = min(start_c,119);
|
||||||
|
c_max = max(start_c,101);
|
||||||
|
}else{
|
||||||
|
c_min = min(start_c, end_c);
|
||||||
|
c_max = max(start_c, end_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allow_out_of_bounds){ // workspace must contain a bit of empty field if out of bounds is not allowed
|
||||||
|
l_min = min(l_min, 306);
|
||||||
|
l_max = max(14, l_max);
|
||||||
|
c_min = min(c_min, 206);
|
||||||
|
c_max = max(14, c_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================================== Initialize A*
|
||||||
|
|
||||||
|
Node* open_root = nullptr;
|
||||||
|
Node board[LINES*COLS];
|
||||||
|
unsigned int node_state[LINES*COLS] = {0}; //0-unknown, 1-open, 2-closed
|
||||||
|
|
||||||
|
//======================================================== Populate board 1: convert obstacles to cost
|
||||||
|
for(int ob=0; ob<obst_size; ob+=5){
|
||||||
|
int lin = x_to_line(obstacles[ob]);
|
||||||
|
int col = y_to_col(obstacles[ob+1]);
|
||||||
|
float hard_radius = fmaxf( 0, fminf(obstacles[ob+2], MAX_RADIUS) );
|
||||||
|
float soft_radius = fmaxf( 0, fminf(obstacles[ob+3], MAX_RADIUS) );
|
||||||
|
float force = obstacles[ob+4];
|
||||||
|
float f_per_m = force / soft_radius; // force per meter
|
||||||
|
|
||||||
|
int max_r = int( fmaxf(hard_radius, soft_radius)*10.f+1e-4 ); // add epsilon to avoid potential rounding error in expansion groups
|
||||||
|
l_min = min(l_min, lin - max_r - 1 );
|
||||||
|
l_max = max(l_max, lin + max_r + 1 );
|
||||||
|
c_min = min(c_min, col - max_r - 1 );
|
||||||
|
c_max = max(c_max, col + max_r + 1 );
|
||||||
|
|
||||||
|
//=============================================================== hard radius
|
||||||
|
int i=0;
|
||||||
|
for(; i<expansion_positions_no and expansion_pos_dist[i] <= hard_radius; i++){
|
||||||
|
int l = lin + expansion_pos_l[i];
|
||||||
|
int c = col + expansion_pos_c[i];
|
||||||
|
if(l>=0 and c>=0 and l<LINES and c<COLS){
|
||||||
|
board_cost[l*COLS+c] = -3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================== soft radius
|
||||||
|
|
||||||
|
for(; i<expansion_positions_no and expansion_pos_dist[i] <= soft_radius; i++){
|
||||||
|
int l = lin + expansion_pos_l[i];
|
||||||
|
int c = col + expansion_pos_c[i];
|
||||||
|
int p = l*COLS+c;
|
||||||
|
float cost = board_cost[p];
|
||||||
|
float fr = force-(f_per_m * expansion_pos_dist[i]);
|
||||||
|
|
||||||
|
if(l>=0 and c>=0 and l<LINES and c<COLS and cost > wall_index and cost < fr){
|
||||||
|
board_cost[p] = fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust board limits if working area overlaps goal area (which includes walking margin)
|
||||||
|
if (c_max > 96 and c_min < 124){ // Otherwise it does not overlap any goal
|
||||||
|
if (l_max > 1 and l_min < 12 ){ // Overlaps our goal
|
||||||
|
l_max = max(12,l_max); // Extend working area to include our goal
|
||||||
|
l_min = min(l_min,1);
|
||||||
|
c_max = max(124,c_max);
|
||||||
|
c_min = min(c_min,96);
|
||||||
|
}
|
||||||
|
if (l_max > 308 and l_min < 319 ){ // Overlaps their goal
|
||||||
|
l_max = max(319,l_max); // Extend working area to include their goal
|
||||||
|
l_min = min(l_min,308);
|
||||||
|
c_max = max(124,c_max);
|
||||||
|
c_min = min(c_min,96);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//======================================================== Populate board 2: add objective
|
||||||
|
// Explanation: if objective is not accessible we do not add it to the map, although its reference still exists.
|
||||||
|
// Therefore, we know how far we are from that reference, but it cannot be reached.
|
||||||
|
// Even if we are on top of the objective, the idea is to get away, to the nearest valid position.
|
||||||
|
if(!go_to_goal){
|
||||||
|
float *end_cost = &board_cost[end_l*COLS+end_c];
|
||||||
|
if(*end_cost > wall_index){
|
||||||
|
*end_cost = -1;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
for(int i=IN_GOAL_LINE*COLS+101; i<=IN_GOAL_LINE*COLS+119; i++){
|
||||||
|
if(board_cost[i] > wall_index){
|
||||||
|
board_cost[i] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add board limits as an additional restriction to workspace
|
||||||
|
l_min = max(0, l_min);
|
||||||
|
l_max = min(l_max, 320);
|
||||||
|
c_min = max(0, c_min);
|
||||||
|
c_max = min(c_max, 220);
|
||||||
|
|
||||||
|
// add start node to open list (it will be closed right away, so there is not need to set it as open)
|
||||||
|
board[start_pos].g = 0; // This is needed to compute the cost of child nodes, but f is not needed because there are no comparisons with other nodes in the open BST
|
||||||
|
board[start_pos].parent = nullptr; //This is where the path ends
|
||||||
|
open_root = open::insert(&board[start_pos], open_root);
|
||||||
|
int measure_timeout=0;
|
||||||
|
Node* best_node = &board[start_pos]; // save best node based on distance to goal (useful if impossible/timeout to get best path)
|
||||||
|
|
||||||
|
float best_node_dist = std::numeric_limits<float>::max(); // infinite distance if start is itself unreachable
|
||||||
|
if(board_cost[start_pos] > wall_index){
|
||||||
|
best_node_dist = diagonal_distance(go_to_goal,start_l,start_c,end_l,end_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//======================================================== A* algorithm
|
||||||
|
while (open_root != nullptr){
|
||||||
|
|
||||||
|
// Get next best node (lowest predicted total cost (f))
|
||||||
|
Node* curr_node = min_node;
|
||||||
|
const int curr_pos = curr_node - board;
|
||||||
|
const int curr_line = curr_pos / COLS;
|
||||||
|
const int curr_col = curr_pos % COLS;
|
||||||
|
const float curr_cost = board_cost[curr_pos];
|
||||||
|
measure_timeout = (measure_timeout+1) & 31; // check timeout at every 32 iterations
|
||||||
|
|
||||||
|
// save best node based on distance to goal (useful if impossible/timeout)
|
||||||
|
if(curr_cost > wall_index){
|
||||||
|
float dd = diagonal_distance(go_to_goal,curr_line,curr_col,end_l,end_c);
|
||||||
|
if(best_node_dist > dd){
|
||||||
|
best_node = curr_node;
|
||||||
|
best_node_dist = dd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open_root = open::pop(open_root);
|
||||||
|
node_state[curr_pos] = 2;
|
||||||
|
|
||||||
|
// Check if we reached objective
|
||||||
|
if( curr_cost == -1 ){
|
||||||
|
// replace end point with correct coordinates instead of discrete version if the optional target was defined (not going to goal)
|
||||||
|
build_final_path(best_node, board, 0, !go_to_goal, opt_t_x, opt_t_y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if( measure_timeout==0 and duration_cast<microseconds>(high_resolution_clock::now() - t1).count() > timeout_us ){
|
||||||
|
build_final_path(best_node, board, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand child nodes
|
||||||
|
bool rcol_ok = curr_col < c_max;
|
||||||
|
bool lcol_ok = curr_col > c_min;
|
||||||
|
|
||||||
|
if(curr_line > l_min){
|
||||||
|
int line = curr_line - 1;
|
||||||
|
int col = curr_col - 1;
|
||||||
|
int pos = curr_pos - COLS - 1;
|
||||||
|
float cost = board_cost[pos];
|
||||||
|
int state = node_state[pos];
|
||||||
|
|
||||||
|
// check if not an obstacle and if node is not closed (child can be as inaccessible as current pos)
|
||||||
|
if (state!=2 and !(cost <= wall_index and cost < curr_cost) and lcol_ok){
|
||||||
|
open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,SQRT2);
|
||||||
|
}
|
||||||
|
|
||||||
|
col++;
|
||||||
|
pos++;
|
||||||
|
cost = board_cost[pos];
|
||||||
|
state = node_state[pos];
|
||||||
|
|
||||||
|
// check if not an obstacle and if node is not closed (child can be as inaccessible as current pos)
|
||||||
|
if (state!=2 and !(cost <= wall_index and cost < curr_cost)){
|
||||||
|
open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
col++;
|
||||||
|
pos++;
|
||||||
|
cost = board_cost[pos];
|
||||||
|
state = node_state[pos];
|
||||||
|
|
||||||
|
// check if not an obstacle and if node is not closed (child can be as inaccessible as current pos)
|
||||||
|
if (state!=2 and !(cost <= wall_index and cost < curr_cost) and rcol_ok){
|
||||||
|
open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,SQRT2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(curr_line < l_max){
|
||||||
|
int line = curr_line + 1;
|
||||||
|
int col = curr_col - 1;
|
||||||
|
int pos = curr_pos + COLS - 1;
|
||||||
|
float cost = board_cost[pos];
|
||||||
|
int state = node_state[pos];
|
||||||
|
|
||||||
|
// check if not an obstacle and if node is not closed (child can be as inaccessible as current pos)
|
||||||
|
if (state!=2 and !(cost <= wall_index and cost < curr_cost) and lcol_ok){
|
||||||
|
open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,SQRT2);
|
||||||
|
}
|
||||||
|
|
||||||
|
col++;
|
||||||
|
pos++;
|
||||||
|
cost = board_cost[pos];
|
||||||
|
state = node_state[pos];
|
||||||
|
|
||||||
|
// check if not an obstacle and if node is not closed (child can be as inaccessible as current pos)
|
||||||
|
if (state!=2 and !(cost <= wall_index and cost < curr_cost)){
|
||||||
|
open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
col++;
|
||||||
|
pos++;
|
||||||
|
cost = board_cost[pos];
|
||||||
|
state = node_state[pos];
|
||||||
|
|
||||||
|
// check if not an obstacle and if node is not closed (child can be as inaccessible as current pos)
|
||||||
|
if (state!=2 and !(cost <= wall_index and cost < curr_cost) and rcol_ok){
|
||||||
|
open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,SQRT2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
int col = curr_col - 1;
|
||||||
|
int pos = curr_pos - 1;
|
||||||
|
float cost = board_cost[pos];
|
||||||
|
int state = node_state[pos];
|
||||||
|
|
||||||
|
// check if not an obstacle and if node is not closed (child can be as inaccessible as current pos)
|
||||||
|
if (state!=2 and !(cost <= wall_index and cost < curr_cost) and lcol_ok){
|
||||||
|
open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,curr_line,col,end_l,end_c,node_state,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
col+=2;
|
||||||
|
pos+=2;
|
||||||
|
cost = board_cost[pos];
|
||||||
|
state = node_state[pos];
|
||||||
|
|
||||||
|
|
||||||
|
// check if not an obstacle and if node is not closed (child can be as inaccessible as current pos)
|
||||||
|
if (state!=2 and !(cost <= wall_index and cost < curr_cost) and rcol_ok){
|
||||||
|
open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,curr_line,col,end_l,end_c,node_state,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
build_final_path(best_node, board, 2);
|
||||||
|
return;
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FILENAME: a_star.h
|
||||||
|
* DESCRIPTION: custom A* pathfinding implementation, optimized for the soccer environment
|
||||||
|
* AUTHOR: Miguel Abreu (m.abreu@fe.up.pt)
|
||||||
|
* DATE: 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct Node{
|
||||||
|
|
||||||
|
//------------- BST parameters
|
||||||
|
Node* left;
|
||||||
|
Node* right;
|
||||||
|
Node* up;
|
||||||
|
|
||||||
|
//------------- A* parameters
|
||||||
|
Node* parent;
|
||||||
|
float g;
|
||||||
|
float f;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void astar(float params[], int params_size);
|
||||||
|
extern float final_path[2050];
|
||||||
|
extern int final_path_size;
|
@ -0,0 +1,37 @@
|
|||||||
|
#include "a_star.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
using std::chrono::duration_cast;
|
||||||
|
using std::chrono::microseconds;
|
||||||
|
|
||||||
|
std::chrono::_V2::system_clock::time_point t1,t2;
|
||||||
|
|
||||||
|
float params[] = {
|
||||||
|
15.78,-0.07, //start
|
||||||
|
1,1, //out of bounds? go to goal?
|
||||||
|
0,0, //target (if not go to goal)
|
||||||
|
500000, // timeout
|
||||||
|
-10,0,1,5,5,
|
||||||
|
-10,1,1,7,10,
|
||||||
|
-10,-7,0,5,1
|
||||||
|
};
|
||||||
|
int params_size = sizeof(params)/sizeof(params[0]);
|
||||||
|
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
|
||||||
|
t1 = high_resolution_clock::now();
|
||||||
|
astar(params, params_size);
|
||||||
|
t2 = high_resolution_clock::now();
|
||||||
|
|
||||||
|
std::cout << duration_cast<microseconds>(t2 - t1).count() << "us (includes initialization)\n";
|
||||||
|
|
||||||
|
t1 = high_resolution_clock::now();
|
||||||
|
astar(params, params_size);
|
||||||
|
t2 = high_resolution_clock::now();
|
||||||
|
|
||||||
|
std::cout << duration_cast<microseconds>(t2 - t1).count() << "us\n";
|
||||||
|
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,60 @@
|
|||||||
|
from os import getcwd
|
||||||
|
from os.path import join, dirname
|
||||||
|
from math import sqrt
|
||||||
|
|
||||||
|
MAX_RADIUS = 5
|
||||||
|
LINES = 321
|
||||||
|
COLS = 221
|
||||||
|
|
||||||
|
expansion_groups = dict()
|
||||||
|
CWD = join(getcwd(), dirname(__file__))
|
||||||
|
|
||||||
|
for l in range(MAX_RADIUS*10+1):
|
||||||
|
for c in range(MAX_RADIUS*10+1):
|
||||||
|
dist = sqrt(l*l+c*c)*0.1
|
||||||
|
if dist <= MAX_RADIUS:
|
||||||
|
if not dist in expansion_groups:
|
||||||
|
expansion_groups[dist] = []
|
||||||
|
expansion_groups[dist].append((l,c))
|
||||||
|
|
||||||
|
if c>0: expansion_groups[dist].append((l,-c))
|
||||||
|
if l>0: expansion_groups[dist].append((-l,c))
|
||||||
|
if c>0 and l>0: expansion_groups[dist].append((-l,-c))
|
||||||
|
|
||||||
|
ascending_dist = sorted(expansion_groups)
|
||||||
|
groups_no = len(expansion_groups)
|
||||||
|
|
||||||
|
#============================================= prepare text to file
|
||||||
|
|
||||||
|
no_of_pos = 0
|
||||||
|
for g in expansion_groups.values():
|
||||||
|
no_of_pos += len(g)
|
||||||
|
|
||||||
|
file = []
|
||||||
|
file.append(f"const int expansion_positions_no = {no_of_pos};\n")
|
||||||
|
file.append(f"const float expansion_pos_dist[{no_of_pos}] = {{")
|
||||||
|
|
||||||
|
for dist in ascending_dist:
|
||||||
|
for p in expansion_groups[dist]:
|
||||||
|
file.extend([f"{dist}",","])
|
||||||
|
file.pop()
|
||||||
|
|
||||||
|
file.append(f"}};\n\nconst int expansion_pos_l[{no_of_pos}] = {{")
|
||||||
|
|
||||||
|
for dist in ascending_dist:
|
||||||
|
for p in expansion_groups[dist]:
|
||||||
|
file.extend([f"{p[0]}",","])
|
||||||
|
file.pop()
|
||||||
|
|
||||||
|
file.append(f"}};\n\nconst int expansion_pos_c[{no_of_pos}] = {{")
|
||||||
|
|
||||||
|
for dist in ascending_dist:
|
||||||
|
for p in expansion_groups[dist]:
|
||||||
|
file.extend([f"{p[1]}",","])
|
||||||
|
file.pop()
|
||||||
|
file.append("};\n")
|
||||||
|
|
||||||
|
#============================================= write to file
|
||||||
|
|
||||||
|
with open(join(CWD, "expansion_groups.h"), 'w') as f:
|
||||||
|
f.write(''.join(file))
|
@ -0,0 +1,43 @@
|
|||||||
|
#include "a_star.h"
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/numpy.h>
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
py::array_t<float> compute( py::array_t<float> parameters ){
|
||||||
|
|
||||||
|
// ================================================= 1. Parse data
|
||||||
|
|
||||||
|
py::buffer_info parameters_buf = parameters.request();
|
||||||
|
int params_len = parameters_buf.shape[0];
|
||||||
|
|
||||||
|
// ================================================= 2. Compute path
|
||||||
|
|
||||||
|
astar( (float*)parameters_buf.ptr, params_len );
|
||||||
|
|
||||||
|
// ================================================= 3. Prepare data to return
|
||||||
|
|
||||||
|
py::array_t<float> retval = py::array_t<float>(final_path_size); //allocate
|
||||||
|
py::buffer_info buff = retval.request();
|
||||||
|
float *ptr = (float *) buff.ptr;
|
||||||
|
|
||||||
|
for(int i=0; i<final_path_size; i++){
|
||||||
|
ptr[i] = final_path[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using namespace pybind11::literals; // to add informative argument names as -> "argname"_a
|
||||||
|
|
||||||
|
PYBIND11_MODULE(a_star, m) { // the python module name, m is the interface to create bindings
|
||||||
|
m.doc() = "Custom A-star implementation"; // optional module docstring
|
||||||
|
|
||||||
|
// optional arguments names
|
||||||
|
m.def("compute", &compute, "Compute the best path", "parameters"_a);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
src = $(wildcard *.cpp)
|
||||||
|
obj = $(src:.c=.o)
|
||||||
|
|
||||||
|
CFLAGS = -O3 -shared -std=c++11 -fPIC -Wall $(PYBIND_INCLUDES)
|
||||||
|
|
||||||
|
all: $(obj)
|
||||||
|
g++ $(CFLAGS) -o ball_predictor.so $^
|
||||||
|
|
||||||
|
debug: $(filter-out lib_main.cpp,$(obj))
|
||||||
|
g++ -O0 -std=c++14 -Wall -g -o debug.bin debug_main.cc $^
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -f $(obj) all
|
@ -0,0 +1,104 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include "ball_predictor.h"
|
||||||
|
|
||||||
|
|
||||||
|
float ball_pos_pred[600]; // ball position (x,y) prediction for 300*0.02s = 6s
|
||||||
|
float ball_vel_pred[600]; // ball velocity (x,y) prediction for 300*0.02s = 6s
|
||||||
|
float ball_spd_pred[300]; // ball linear speed (s) prediction for 300*0.02s = 6s
|
||||||
|
int pos_pred_len=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get intersection with moving ball (intersection point and distance)
|
||||||
|
* @param x robot position (x)
|
||||||
|
* @param y robot position (y)
|
||||||
|
* @param max_robot_sp_per_step maximum speed per step
|
||||||
|
* @param ball_pos imported ball positions (possibly modified version of ball_pos_pred)
|
||||||
|
* @param ball_pos_len length of ball_pos
|
||||||
|
* @param ret_x returned position (x) of intersection point
|
||||||
|
* @param ret_y returned position (y) of intersection point
|
||||||
|
* @param ret_d returned distance between robot and intersection point
|
||||||
|
*/
|
||||||
|
void get_intersection_with_ball(float x, float y, float max_robot_sp_per_step, float ball_pos[], float ball_pos_len,
|
||||||
|
float &ret_x, float &ret_y, float &ret_d){
|
||||||
|
|
||||||
|
float robot_max_displacement = 0.2; // robot has an immediate reach radius of 0.2m
|
||||||
|
int j=0;
|
||||||
|
|
||||||
|
while(1){
|
||||||
|
float vec_x = ball_pos[j++] - x;
|
||||||
|
float vec_y = ball_pos[j++] - y;
|
||||||
|
float b_dist_sq = vec_x*vec_x + vec_y*vec_y; // squared ball distance
|
||||||
|
|
||||||
|
// If robot has reached the ball, or the ball has stopped but the robot is still not there
|
||||||
|
if (b_dist_sq <= robot_max_displacement*robot_max_displacement or j>=ball_pos_len){
|
||||||
|
float d = sqrtf(b_dist_sq);
|
||||||
|
ret_d = d;
|
||||||
|
ret_x = ball_pos[j-2];
|
||||||
|
ret_y = ball_pos[j-1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
robot_max_displacement += max_robot_sp_per_step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Predict ball position/velocity until the ball stops or gets out of bounds (up to 6s)
|
||||||
|
* Adequate when the ball is rolling on the ground
|
||||||
|
* @param bx ball position (x)
|
||||||
|
* @param by ball position (y)
|
||||||
|
* @param vx ball velocity (x)
|
||||||
|
* @param vy ball velocity (y)
|
||||||
|
*/
|
||||||
|
void predict_rolling_ball_pos_vel_spd(double bx, double by, double vx, double vy){
|
||||||
|
|
||||||
|
// acceleration = Rolling Drag Force * mass (constant = 0.026 kg)
|
||||||
|
// acceleration = k1 * velocity^2 + k2 * velocity
|
||||||
|
const double k1 = -0.01;
|
||||||
|
const double k2 = -1;
|
||||||
|
|
||||||
|
const double k1_x = (vx < 0) ? -k1 : k1; // invert k1 if vx is negative, because vx^2 absorbs the sign
|
||||||
|
const double k1_y = (vy < 0) ? -k1 : k1; // invert k1 if vy is negative, because vy^2 absorbs the sign
|
||||||
|
|
||||||
|
ball_pos_pred[0] = bx; // current ball position
|
||||||
|
ball_pos_pred[1] = by;
|
||||||
|
ball_vel_pred[0] = vx; // current ball velocity
|
||||||
|
ball_vel_pred[1] = vy;
|
||||||
|
ball_spd_pred[0] = sqrt(vx*vx+vy*vy);
|
||||||
|
|
||||||
|
int counter = 2;
|
||||||
|
|
||||||
|
while(counter < 600){
|
||||||
|
|
||||||
|
// acceleration
|
||||||
|
double acc_x = vx*vx*k1_x + vx*k2;
|
||||||
|
double acc_y = vy*vy*k1_y + vy*k2;
|
||||||
|
|
||||||
|
// second equation of motion: displacement = v0*t + 0.5*a*t^2
|
||||||
|
double dx = vx*0.02 + acc_x*0.0002; // 0.5*0.02^2 = 0.0002
|
||||||
|
double dy = vy*0.02 + acc_y*0.0002; // 0.5*0.02^2 = 0.0002
|
||||||
|
|
||||||
|
// position
|
||||||
|
bx += dx;
|
||||||
|
by += dy;
|
||||||
|
|
||||||
|
// abort when displacement is low or ball is out of bounds
|
||||||
|
if ((fabs(dx) < 0.0005 and fabs(dy) < 0.0005) or fabs(bx) > 15 or fabs(by) > 10){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// velocity
|
||||||
|
vx += acc_x*0.02;
|
||||||
|
vy += acc_y*0.02;
|
||||||
|
|
||||||
|
// store as 32b
|
||||||
|
ball_spd_pred[counter/2] = sqrt(vx*vx+vy*vy);
|
||||||
|
ball_vel_pred[counter] = vx;
|
||||||
|
ball_pos_pred[counter++] = bx;
|
||||||
|
ball_vel_pred[counter] = vy;
|
||||||
|
ball_pos_pred[counter++] = by;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pos_pred_len = counter;
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
extern float ball_pos_pred[600]; // ball position (x,y) prediction for 300*0.02s = 6s
|
||||||
|
extern float ball_vel_pred[600]; // ball velocoty (x,y) prediction for 300*0.02s = 6s
|
||||||
|
extern float ball_spd_pred[300]; // ball linear speed (s) prediction for 300*0.02s = 6s
|
||||||
|
extern int pos_pred_len;
|
||||||
|
|
||||||
|
extern void get_intersection_with_ball(float x, float y, float max_robot_sp_per_step, float ball_pos[], float ball_pos_len,
|
||||||
|
float &ret_x, float &ret_y, float &ret_d);
|
||||||
|
extern void predict_rolling_ball_pos_vel_spd(double bx, double by, double vx, double vy);
|
@ -0,0 +1,54 @@
|
|||||||
|
#include "ball_predictor.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
using std::cout;
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
using std::chrono::duration_cast;
|
||||||
|
using std::chrono::microseconds;
|
||||||
|
|
||||||
|
std::chrono::_V2::system_clock::time_point t1,t2;
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
|
||||||
|
// ================================================= 1. Generate data
|
||||||
|
|
||||||
|
float px = 3;
|
||||||
|
float py = 4;
|
||||||
|
float vx = -5;
|
||||||
|
float vy = -1;
|
||||||
|
|
||||||
|
// ================================================= 2. Compute prediction
|
||||||
|
|
||||||
|
t1 = high_resolution_clock::now();
|
||||||
|
predict_rolling_ball_pos_vel_spd(px, py, vx, vy);
|
||||||
|
t2 = high_resolution_clock::now();
|
||||||
|
|
||||||
|
cout << std::fixed << std::setprecision(8);
|
||||||
|
|
||||||
|
for(int i=0; i<pos_pred_len; i+=2){
|
||||||
|
cout << i/2 << " pos:" << ball_pos_pred[i] << "," << ball_pos_pred[i+1] <<
|
||||||
|
" vel:" << ball_vel_pred[i] << "," << ball_vel_pred[i+1] <<
|
||||||
|
" spd:" << ball_spd_pred[i/2] << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "\n\n" << duration_cast<microseconds>(t2 - t1).count() << "us for prediction\n";
|
||||||
|
|
||||||
|
// ================================================= 3. Generate data
|
||||||
|
|
||||||
|
float robot_x = -1;
|
||||||
|
float robot_y = 1;
|
||||||
|
float max_speed_per_step = 0.7*0.02;
|
||||||
|
float ret_x, ret_y, ret_d;
|
||||||
|
|
||||||
|
// ================================================= 4. Compute intersection
|
||||||
|
|
||||||
|
t1 = high_resolution_clock::now();
|
||||||
|
get_intersection_with_ball(robot_x, robot_y, max_speed_per_step, ball_pos_pred, pos_pred_len, ret_x, ret_y, ret_d);
|
||||||
|
t2 = high_resolution_clock::now();
|
||||||
|
|
||||||
|
cout << duration_cast<microseconds>(t2 - t1).count() << "us for intersection\n\n";
|
||||||
|
cout << "Intersection: " << ret_x << "," << ret_y << " dist: " << ret_d << "\n\n";
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
#include "ball_predictor.h"
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/numpy.h>
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Predict rolling ball position, velocity, linear speed
|
||||||
|
*
|
||||||
|
* @param parameters
|
||||||
|
* ball_x, ball_y, ball_vel_x, ball_vel_y
|
||||||
|
* @return ball_pos_pred, ball_vel_pred, ball_spd_pred
|
||||||
|
*/
|
||||||
|
py::array_t<float> predict_rolling_ball( py::array_t<float> parameters ){
|
||||||
|
|
||||||
|
// ================================================= 1. Parse data
|
||||||
|
|
||||||
|
py::buffer_info parameters_buf = parameters.request();
|
||||||
|
float* parameters_ptr = (float*)parameters_buf.ptr;
|
||||||
|
|
||||||
|
float px = parameters_ptr[0];
|
||||||
|
float py = parameters_ptr[1];
|
||||||
|
float vx = parameters_ptr[2];
|
||||||
|
float vy = parameters_ptr[3];
|
||||||
|
|
||||||
|
// ================================================= 2. Compute path
|
||||||
|
|
||||||
|
predict_rolling_ball_pos_vel_spd(px, py, vx, vy);
|
||||||
|
|
||||||
|
// ================================================= 3. Prepare data to return
|
||||||
|
|
||||||
|
py::array_t<float> retval = py::array_t<float>(pos_pred_len+pos_pred_len+pos_pred_len/2); //allocate
|
||||||
|
py::buffer_info buff = retval.request();
|
||||||
|
float *ptr = (float *) buff.ptr;
|
||||||
|
|
||||||
|
for(int i=0; i<pos_pred_len; i++){
|
||||||
|
ptr[i] = ball_pos_pred[i];
|
||||||
|
}
|
||||||
|
ptr+=pos_pred_len;
|
||||||
|
for(int i=0; i<pos_pred_len; i++){
|
||||||
|
ptr[i] = ball_vel_pred[i];
|
||||||
|
}
|
||||||
|
ptr+=pos_pred_len;
|
||||||
|
for(int i=0; i<pos_pred_len/2; i++){
|
||||||
|
ptr[i] = ball_spd_pred[i];
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get point of intersection with moving ball
|
||||||
|
*
|
||||||
|
* @param parameters
|
||||||
|
* robot_x, robot_y, robot_max_speed_per_step
|
||||||
|
* @return intersection_x, intersection_y, intersection_distance
|
||||||
|
*/
|
||||||
|
py::array_t<float> get_intersection( py::array_t<float> parameters ){
|
||||||
|
|
||||||
|
// ================================================= 1. Parse data
|
||||||
|
|
||||||
|
py::buffer_info parameters_buf = parameters.request();
|
||||||
|
float* parameters_ptr = (float*)parameters_buf.ptr;
|
||||||
|
int params_len = parameters_buf.shape[0];
|
||||||
|
|
||||||
|
float x = parameters_ptr[0];
|
||||||
|
float y = parameters_ptr[1];
|
||||||
|
float max_sp = parameters_ptr[2];
|
||||||
|
float* ball_pos = parameters_ptr + 3;
|
||||||
|
float ret_x, ret_y, ret_d;
|
||||||
|
|
||||||
|
// ================================================= 2. Compute path
|
||||||
|
|
||||||
|
get_intersection_with_ball(x, y, max_sp, ball_pos, params_len-3, ret_x, ret_y, ret_d);
|
||||||
|
|
||||||
|
// ================================================= 3. Prepare data to return
|
||||||
|
|
||||||
|
py::array_t<float> retval = py::array_t<float>(3); //allocate
|
||||||
|
py::buffer_info buff = retval.request();
|
||||||
|
float *ptr = (float *) buff.ptr;
|
||||||
|
|
||||||
|
ptr[0] = ret_x;
|
||||||
|
ptr[1] = ret_y;
|
||||||
|
ptr[2] = ret_d;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
using namespace pybind11::literals; // to add informative argument names as -> "argname"_a
|
||||||
|
|
||||||
|
PYBIND11_MODULE(ball_predictor, m) { // the python module name, m is the interface to create bindings
|
||||||
|
m.doc() = "Ball predictor"; // optional module docstring
|
||||||
|
|
||||||
|
// optional arguments names
|
||||||
|
m.def("predict_rolling_ball", &predict_rolling_ball, "Predict rolling ball", "parameters"_a);
|
||||||
|
m.def("get_intersection", &get_intersection, "Get point of intersection with moving ball", "parameters"_a);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,532 @@
|
|||||||
|
#include "Field.h"
|
||||||
|
#include "RobovizLogger.h"
|
||||||
|
#include "World.h"
|
||||||
|
|
||||||
|
|
||||||
|
static World& world = SWorld::getInstance();
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//=========================================================================== constexpr definitions
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
decltype(Field::cRingLineLength) constexpr Field::cRingLineLength;
|
||||||
|
decltype(Field::cPenaltyBoxDistX) constexpr Field::cPenaltyBoxDistX;
|
||||||
|
decltype(Field::cHalfPenaltyWidth) constexpr Field::cHalfPenaltyWidth;
|
||||||
|
decltype(Field::cHalfGoalWidth) constexpr Field::cHalfGoalWidth;
|
||||||
|
decltype(Field::cHalfFielfLength) constexpr Field::cHalfFielfLength;
|
||||||
|
decltype(Field::cGoalWidth) constexpr Field::cGoalWidth;
|
||||||
|
decltype(Field::cGoalDepth) constexpr Field::cGoalDepth;
|
||||||
|
decltype(Field::cGoalHeight) constexpr Field::cGoalHeight;
|
||||||
|
decltype(Field::cFieldLength) constexpr Field::cFieldLength;
|
||||||
|
decltype(Field::cFieldWidth) constexpr Field::cFieldWidth;
|
||||||
|
decltype(Field::cPenaltyLength) constexpr Field::cPenaltyLength;
|
||||||
|
decltype(Field::cPenaltyWidth) constexpr Field::cPenaltyWidth;
|
||||||
|
decltype(Field::cFieldLineSegments::list) constexpr Field::cFieldLineSegments::list;
|
||||||
|
decltype(Field::cFieldPoints::list) constexpr Field::cFieldPoints::list;
|
||||||
|
|
||||||
|
//non-constexpr definitions
|
||||||
|
decltype(Field::list_8_landmarks::list) Field::list_8_landmarks::list;
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//=============================================================================== Drawing utilities
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw estimates of all visible lines, markers, self position and ball
|
||||||
|
* */
|
||||||
|
|
||||||
|
void Field::draw_visible(const Matrix4D& headToFieldT, bool is_right_side) const{
|
||||||
|
|
||||||
|
if(is_right_side){
|
||||||
|
return draw_visible_switch(headToFieldT);
|
||||||
|
}
|
||||||
|
|
||||||
|
string draw_name = "localization";
|
||||||
|
|
||||||
|
RobovizLogger* roboviz = RobovizLogger::Instance();
|
||||||
|
roboviz->init(); // only initialized when draw_visible is called (only happens once)
|
||||||
|
|
||||||
|
//--------------------------------- Print all lines, whether they were identified or not
|
||||||
|
for(const Line6f& l : list_segments) {
|
||||||
|
|
||||||
|
Vector3f s = headToFieldT * l.startc;
|
||||||
|
Vector3f e = headToFieldT * l.endc;
|
||||||
|
|
||||||
|
roboviz->drawLine(s.x, s.y, s.z, e.x, e.y, e.z, 1, 0.8,0,0, &draw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------- Print identified line segments, with their fixed abs coordinates
|
||||||
|
|
||||||
|
for(const auto& s : list_known_segments){
|
||||||
|
|
||||||
|
Vector3f mid = Vector3f::determineMidpoint(s.point[0].absPos.get_vector(), s.point[1].absPos.get_vector());
|
||||||
|
|
||||||
|
string line_name(s.fieldSegment->name);
|
||||||
|
roboviz->drawAnnotation(&line_name,mid.x,mid.y,mid.z, 0,1,0,&draw_name);
|
||||||
|
roboviz->drawLine(s.point[0].absPos.x, s.point[0].absPos.y, s.point[0].absPos.z,
|
||||||
|
s.point[1].absPos.x, s.point[1].absPos.y, s.point[1].absPos.z, 3, 0,0.8,0, &draw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& m : list_known_markers){
|
||||||
|
string line_name(m.fieldPt->name);
|
||||||
|
roboviz->drawAnnotation(&line_name,m.absPos.x, m.absPos.y, m.absPos.z+1, 1,0,0,&draw_name);
|
||||||
|
roboviz->drawLine(m.absPos.x, m.absPos.y, m.absPos.z,
|
||||||
|
m.absPos.x, m.absPos.y, m.absPos.z+0.5, 1, 0.8,0.8,0.8, &draw_name);
|
||||||
|
}
|
||||||
|
for(const auto& m : list_unknown_markers){
|
||||||
|
string line_name = "!";
|
||||||
|
roboviz->drawAnnotation(&line_name,m.absPos.x, m.absPos.y, m.absPos.z+1, 1,0,0,&draw_name);
|
||||||
|
roboviz->drawLine(m.absPos.x, m.absPos.y, m.absPos.z,
|
||||||
|
m.absPos.x, m.absPos.y, m.absPos.z+0.5, 1, 0.8,0.8,0.8, &draw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------- Draw player and ball arrows
|
||||||
|
|
||||||
|
Vector3f me = headToFieldT.toVector3f();
|
||||||
|
|
||||||
|
roboviz->drawLine(me.x, me.y, me.z, me.x, me.y, me.z+0.5, 2,1,0,0,&draw_name);
|
||||||
|
roboviz->drawLine(me.x, me.y, me.z, me.x-0.2, me.y, me.z+0.2, 2,1,0,0,&draw_name);
|
||||||
|
roboviz->drawLine(me.x, me.y, me.z, me.x+0.2, me.y, me.z+0.2, 2,1,0,0,&draw_name);
|
||||||
|
|
||||||
|
//There is no need to draw the ball position here (but it works well)
|
||||||
|
|
||||||
|
/*static Vector3f last_known_ball_pos = Vector3f();
|
||||||
|
if(world.ball_seen){
|
||||||
|
last_known_ball_pos = headToFieldT * world.ball_rel_pos_cart;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f &b = last_known_ball_pos;
|
||||||
|
|
||||||
|
roboviz->drawLine(b.x, b.y, b.z, b.x, b.y, b.z+0.5, 2,1,1,0,&draw_name);
|
||||||
|
roboviz->drawLine(b.x, b.y, b.z, b.x-0.2, b.y, b.z+0.2, 2,1,1,0,&draw_name);
|
||||||
|
roboviz->drawLine(b.x, b.y, b.z, b.x+0.2, b.y, b.z+0.2, 2,1,1,0,&draw_name);*/
|
||||||
|
|
||||||
|
roboviz->swapBuffers(&draw_name);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw estimates of all visible lines, markers, self position and ball, but switch field sides
|
||||||
|
* */
|
||||||
|
|
||||||
|
void Field::draw_visible_switch(const Matrix4D& headToFieldT) const{
|
||||||
|
|
||||||
|
string draw_name = "localization";
|
||||||
|
|
||||||
|
RobovizLogger* roboviz = RobovizLogger::Instance();
|
||||||
|
roboviz->init(); // only initialized when draw_visible is called (only happens once)
|
||||||
|
|
||||||
|
//--------------------------------- Print all lines, whether they were identified or not
|
||||||
|
for(const Line6f& l : list_segments) {
|
||||||
|
|
||||||
|
Vector3f s = headToFieldT * l.startc;
|
||||||
|
Vector3f e = headToFieldT * l.endc;
|
||||||
|
|
||||||
|
roboviz->drawLine(-s.x, -s.y, s.z, -e.x, -e.y, e.z, 1, 0.8,0,0, &draw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------- Print identified line segments, with their fixed abs coordinates
|
||||||
|
|
||||||
|
for(const auto& s : list_known_segments){
|
||||||
|
|
||||||
|
Vector3f mid = Vector3f::determineMidpoint(s.point[0].absPos.get_vector(), s.point[1].absPos.get_vector());
|
||||||
|
|
||||||
|
string line_name(s.fieldSegment->name);
|
||||||
|
roboviz->drawAnnotation(&line_name,-mid.x,-mid.y,mid.z, 0,1,0,&draw_name);
|
||||||
|
roboviz->drawLine(-s.point[0].absPos.x, -s.point[0].absPos.y, s.point[0].absPos.z,
|
||||||
|
-s.point[1].absPos.x, -s.point[1].absPos.y, s.point[1].absPos.z, 3, 0,0.8,0, &draw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& m : list_known_markers){
|
||||||
|
string line_name(m.fieldPt->name);
|
||||||
|
roboviz->drawAnnotation(&line_name,-m.absPos.x, -m.absPos.y, m.absPos.z+1, 1,0,0,&draw_name);
|
||||||
|
roboviz->drawLine(-m.absPos.x, -m.absPos.y, m.absPos.z,
|
||||||
|
-m.absPos.x, -m.absPos.y, m.absPos.z+0.5, 1, 0.8,0.8,0.8, &draw_name);
|
||||||
|
}
|
||||||
|
for(const auto& m : list_unknown_markers){
|
||||||
|
string line_name = "!";
|
||||||
|
roboviz->drawAnnotation(&line_name,-m.absPos.x, -m.absPos.y, m.absPos.z+1, 1,0,0,&draw_name);
|
||||||
|
roboviz->drawLine(-m.absPos.x, -m.absPos.y, m.absPos.z,
|
||||||
|
-m.absPos.x, -m.absPos.y, m.absPos.z+0.5, 1, 0.8,0.8,0.8, &draw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------- Draw player and ball arrows
|
||||||
|
|
||||||
|
Vector3f me = headToFieldT.toVector3f();
|
||||||
|
|
||||||
|
roboviz->drawLine(-me.x, -me.y, me.z, -me.x, -me.y, me.z+0.5, 2,1,0,0,&draw_name);
|
||||||
|
roboviz->drawLine(-me.x, -me.y, me.z, -me.x+0.2, -me.y, me.z+0.2, 2,1,0,0,&draw_name);
|
||||||
|
roboviz->drawLine(-me.x, -me.y, me.z, -me.x-0.2, -me.y, me.z+0.2, 2,1,0,0,&draw_name);
|
||||||
|
|
||||||
|
//There is no need to draw the ball position here (but it works well)
|
||||||
|
|
||||||
|
/*static Vector3f last_known_ball_pos = Vector3f();
|
||||||
|
if(world.ball_seen){
|
||||||
|
last_known_ball_pos = headToFieldT * world.ball_rel_pos_cart;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f &b = last_known_ball_pos;
|
||||||
|
|
||||||
|
roboviz->drawLine(b.x, b.y, b.z, b.x, b.y, b.z+0.5, 2,1,1,0,&draw_name);
|
||||||
|
roboviz->drawLine(b.x, b.y, b.z, b.x-0.2, b.y, b.z+0.2, 2,1,1,0,&draw_name);
|
||||||
|
roboviz->drawLine(b.x, b.y, b.z, b.x+0.2, b.y, b.z+0.2, 2,1,1,0,&draw_name);*/
|
||||||
|
|
||||||
|
roboviz->swapBuffers(&draw_name);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//==================================================================== Refresh / Identify / Collect
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gather markers with known absolute z: line endpoints + foot contact points + [toe contact points]
|
||||||
|
**/
|
||||||
|
void Field::gather_ground_markers(){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add NAO's feet ground contact points to zmarks
|
||||||
|
* Dependency: the agent's feet must be touching the ground
|
||||||
|
* Flaws:
|
||||||
|
* - if the feet are touching other players or the ball (may be solved if it is problematic)
|
||||||
|
* - robot 4 may be touching the ground with its toes and they are currently ignored
|
||||||
|
**/
|
||||||
|
|
||||||
|
for(int i=0; i<2; i++){
|
||||||
|
if( world.foot_touch[i] ){ //if this foot is touching the ground
|
||||||
|
|
||||||
|
//Vector3f contactAux = world.foot_contact_pt[i]; //contact point using strange coordinate system
|
||||||
|
//Vector3f contactpt = Vector3f(contactAux.y,-contactAux.x,contactAux.z); // fix coordinate system for both feet
|
||||||
|
|
||||||
|
Vector3f relPos = world.foot_contact_rel_pos[i];
|
||||||
|
list_feet_contact_points.emplace_back( sVector3d({0,0,0}), relPos.toPolar(), relPos);
|
||||||
|
list_ground_markers.emplace_back( sVector3d({0,0,0}), relPos.toPolar(), relPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Deactivated since it did not produce better results, even when both feet are floating (it was only better when the robot fell)
|
||||||
|
/*const Types::BodyParts toePart[2] = {Types::ilLToe, Types::ilRToe};
|
||||||
|
for(int i=0; i<2; i++){
|
||||||
|
if( agent::model->getToeTouch(feetSide[i]) ){ //if this foot is touching the ground
|
||||||
|
|
||||||
|
Vector3f contactAux = agent::model->getToeContact(feetSide[i]); //contact point using strange coordinate system
|
||||||
|
Vector3f contactpt = Vector3f(contactAux.y,-contactAux.x,contactAux.z); // fix coordinate system for both feet
|
||||||
|
Vector3f relPos = agent::model->getRelPositionOfBodyPoint(agent::model->getBodyPart(toePart[i]),contactpt);
|
||||||
|
|
||||||
|
if(agent::model->getFootTouch(Types::iLeft) == false && agent::model->getFootTouch(Types::iRight) == false){
|
||||||
|
zmarks.emplace_back( 0,0,0,relPos );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
//Add all line endings to ground markers
|
||||||
|
for(const Line6f& l : list_segments){
|
||||||
|
list_ground_markers.emplace_back( sVector3d({0,0,0}), l.startp, l.startc);
|
||||||
|
list_ground_markers.emplace_back( sVector3d({0,0,0}), l.endp, l.endc);
|
||||||
|
}
|
||||||
|
|
||||||
|
non_collinear_ground_markers = list_ground_markers.size(); //Excludes corner flags
|
||||||
|
|
||||||
|
//Add corner flags
|
||||||
|
for(const auto& c : list_landmarks_corners){
|
||||||
|
list_ground_markers.emplace_back( sVector3d({0,0,0}), c.relPosPolar, c.relPosCart);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All the polar coordinates' errors are dependent on the distance
|
||||||
|
* Var[distance error] = Var[ed*d/100] + Var[er] (ed-error distance, er-error rounding)
|
||||||
|
* Var[distance error] = (d/100)^2 * Var[ed] + Var[er]
|
||||||
|
* Their importance will be given by Inverse-variance weighting
|
||||||
|
*
|
||||||
|
* Application:
|
||||||
|
* repetition = max(int(k*(1/var)),1), where k=1/1500
|
||||||
|
* repetitions for 1 meter: 71
|
||||||
|
* repetitions for 2 meters: 55
|
||||||
|
* repetitions for >=19 meters: 1
|
||||||
|
*/
|
||||||
|
|
||||||
|
for(const auto& g : list_ground_markers){
|
||||||
|
float var = pow(g.relPosPolar.x / 100.f,2) * var_distance + var_round_hundredth;
|
||||||
|
float w = 1.f/(1500.f*var); //weight = (1/var)*k where k is a constant to transform the large weights into number of repetitions
|
||||||
|
int repetitions = max(int(w),1);
|
||||||
|
|
||||||
|
list_weighted_ground_markers.insert(list_weighted_ground_markers.end(), repetitions, g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update markers after visual step
|
||||||
|
* Marks attributes: abs(x,y,z), rel(r,h,v)
|
||||||
|
* Possible markers:
|
||||||
|
* - 8 landmarks
|
||||||
|
* - line endings:
|
||||||
|
* - 8 @ field corners
|
||||||
|
* - 12 @ penalty box corners
|
||||||
|
* - 20 @ center ring
|
||||||
|
* - 2 @ halfway line
|
||||||
|
* - 8 noisy estimates from corner-originated lines
|
||||||
|
* */
|
||||||
|
void Field::update(){
|
||||||
|
|
||||||
|
//no need to reserve space since these vectors will expand mostly in the first cycles
|
||||||
|
list_segments.clear();
|
||||||
|
list_landmarks.clear();
|
||||||
|
list_landmarks_corners.clear();
|
||||||
|
list_landmarks_goalposts.clear();
|
||||||
|
list_feet_contact_points.clear();
|
||||||
|
list_known_markers.clear();
|
||||||
|
list_unknown_markers.clear();
|
||||||
|
list_known_segments.clear();
|
||||||
|
list_ground_markers.clear();
|
||||||
|
list_weighted_ground_markers.clear();
|
||||||
|
|
||||||
|
//----------------------------------------- Pre-processing: prepare landmark lists
|
||||||
|
|
||||||
|
for(int i=0; i<8; i++){
|
||||||
|
sFixedMarker *l8;
|
||||||
|
const sFieldPoint *fp;
|
||||||
|
World::sLMark *l = &world.landmark[i];
|
||||||
|
if (l->pos.x == -15 && l->pos.y == -10) {l8 = &list_8_landmarks::_corner_mm; fp = &cFieldPoints::corner_mm;}
|
||||||
|
else if(l->pos.x == -15 && l->pos.y == +10) {l8 = &list_8_landmarks::_corner_mp; fp = &cFieldPoints::corner_mp;}
|
||||||
|
else if(l->pos.x == +15 && l->pos.y == -10) {l8 = &list_8_landmarks::_corner_pm; fp = &cFieldPoints::corner_pm;}
|
||||||
|
else if(l->pos.x == +15 && l->pos.y == +10) {l8 = &list_8_landmarks::_corner_pp; fp = &cFieldPoints::corner_pp;}
|
||||||
|
else if(l->pos.x == -15 && l->pos.y < 0) {l8 = &list_8_landmarks::_goal_mm; fp = &cFieldPoints::goal_mm; }
|
||||||
|
else if(l->pos.x == -15 && l->pos.y > 0) {l8 = &list_8_landmarks::_goal_mp; fp = &cFieldPoints::goal_mp; }
|
||||||
|
else if(l->pos.x == +15 && l->pos.y < 0) {l8 = &list_8_landmarks::_goal_pm; fp = &cFieldPoints::goal_pm; }
|
||||||
|
else if(l->pos.x == +15 && l->pos.y > 0) {l8 = &list_8_landmarks::_goal_pp; fp = &cFieldPoints::goal_pp; }
|
||||||
|
else{ return; }
|
||||||
|
|
||||||
|
if(l->seen){
|
||||||
|
l8->set_relPos(l->rel_pos);
|
||||||
|
l8->visible = true;
|
||||||
|
|
||||||
|
sMarker seen_mark(fp, l->rel_pos);
|
||||||
|
list_landmarks.push_back(seen_mark);
|
||||||
|
list_known_markers.push_back(seen_mark);
|
||||||
|
|
||||||
|
if (l->isCorner){ list_landmarks_corners.push_back( seen_mark); }
|
||||||
|
else { list_landmarks_goalposts.push_back(seen_mark); }
|
||||||
|
}else{
|
||||||
|
l8->visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------- Pre-processing: prepare lines and landmarks' coordinates sign
|
||||||
|
|
||||||
|
for(const auto& l : world.lines_polar) {
|
||||||
|
list_segments.emplace_back(l.start, l.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------- Gather markers with known absolute z: line endpoints + foot contact points
|
||||||
|
|
||||||
|
gather_ground_markers();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Field::update_from_transformation(const Matrix4D& tmatrix){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify segments based on transformation matrix
|
||||||
|
*
|
||||||
|
* Identification starts from longest to shortest line
|
||||||
|
* The visible line segment is identified if there is only 1 close field line
|
||||||
|
* If there is more than 1 close field line and all but 1 were already taken, it is still identified
|
||||||
|
*/
|
||||||
|
|
||||||
|
//----------------------------------------------- get lines ordered from largest to shortest
|
||||||
|
vector<Line6f*> lines_descending_length;
|
||||||
|
for(auto& l : list_segments){
|
||||||
|
lines_descending_length.push_back(&l);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sort from largest to smallest radius
|
||||||
|
sort(lines_descending_length.begin(),lines_descending_length.end(),
|
||||||
|
[](const Line6f* a, const Line6f* b) { return (a->length > b->length); });
|
||||||
|
|
||||||
|
//----------------------------------------------- identify lines
|
||||||
|
|
||||||
|
for(const Line6f* l : lines_descending_length){
|
||||||
|
Vector3f l_abs[2] = {tmatrix * l->startc, tmatrix * l->endc};
|
||||||
|
|
||||||
|
float l_angle = atan2f(l_abs[1].y - l_abs[0].y, l_abs[1].x - l_abs[0].x);
|
||||||
|
|
||||||
|
const float min_err = 0.3; //maximum allowed distance (startdist + enddist < 0.3m)
|
||||||
|
const sFieldSegment* best_line = nullptr;
|
||||||
|
for(const auto& s : cFieldLineSegments::list){ //find distance to closest field line
|
||||||
|
|
||||||
|
//Skip field line if seen line is substantially larger
|
||||||
|
if( l->length > (s.length + 0.7) ){ continue; }
|
||||||
|
|
||||||
|
//Skip field line if orientation does not match
|
||||||
|
float line_angle_difference = normalize_line_angle_rad(l_angle - s.angle);
|
||||||
|
if(line_angle_difference > 0.26) continue; //tolerance 15deg
|
||||||
|
|
||||||
|
//Skip field line if it was already identified
|
||||||
|
bool already_identified = false;
|
||||||
|
for(const auto& k : list_known_segments){
|
||||||
|
if(k.fieldSegment == &s){ already_identified=true; break; }
|
||||||
|
}
|
||||||
|
if(already_identified) continue;
|
||||||
|
|
||||||
|
//Error is the sum of the distance of a single line segment to both endpoints of seen line
|
||||||
|
float err = fieldLineSegmentDistToCart2DPoint(s,l_abs[0].to2d());
|
||||||
|
if(err < min_err) err += fieldLineSegmentDistToCart2DPoint(s,l_abs[1].to2d());
|
||||||
|
|
||||||
|
if(err < min_err){
|
||||||
|
if(best_line == nullptr){ best_line = &s; } //Save the field line for now (others may emerge)
|
||||||
|
else{
|
||||||
|
best_line = nullptr; //Two close field lines, none of which was taken yet, so abort
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(best_line != nullptr){
|
||||||
|
|
||||||
|
//-------------- Fix the seen line's start<->end order to match the corresponding field line
|
||||||
|
|
||||||
|
int l_index[2] = {0,1}; //line index of [0]start and [1]end points
|
||||||
|
|
||||||
|
if(normalize_vector_angle_rad(l_angle - best_line->angle) > 1.57079633f){ // they point in opposite directions
|
||||||
|
l_index[0] = 1;
|
||||||
|
l_index[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector3f *lAbs[2] = {&l_abs[l_index[0]], &l_abs[l_index[1]]};
|
||||||
|
const Vector3f *lRelP[2] = {&l->get_polar_pt(l_index[0]), &l->get_polar_pt(l_index[1])};
|
||||||
|
const Vector3f *lRelC[2] = {&l->get_cart_pt(l_index[0]), &l->get_cart_pt(l_index[1])};
|
||||||
|
|
||||||
|
//-------------- Fix the absolute coordinates with field information
|
||||||
|
|
||||||
|
bool isInFoV[2] = {false,false};
|
||||||
|
|
||||||
|
//1st: recognize endpoints as field points (& save known markers)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* //---------------------------------------------- General solution
|
||||||
|
* All points reasonably within the FoV are identified
|
||||||
|
* Noise applied horizontally sigma=0.1225, Pr[-0.5<x<0.5]=0.99996
|
||||||
|
* Noise applied vertically sigma=0.1480, Pr[-0.5<x<0.5]=0.99928
|
||||||
|
*
|
||||||
|
* Warning: the server limits the focal distance to 10cm (which is enforced in the x cartesian coordinate)
|
||||||
|
* current solution: lRelC[i].x > 0.2
|
||||||
|
*
|
||||||
|
* Warning 2: sometimes the error of phi and theta is larger than expected, producing points like
|
||||||
|
* (rel polar: 0.57,-36.36,-54.41) (rel cart: 0.267,-0.1967,-0.4635)
|
||||||
|
* which goes with the theory that the FoV is actually defined as a cone (a bit unrealistic though)
|
||||||
|
* current solution: cone_angle < hor_FoV-5
|
||||||
|
*/
|
||||||
|
|
||||||
|
for( int i=0; i<2; i++){
|
||||||
|
float cone_angle = acosf(lRelC[i]->x / lRelP[i]->x); //angle between vector and (1,0,0)
|
||||||
|
const float max_cone_angle = (cHalfHorizontalFoV-5)*M_PI/180;
|
||||||
|
|
||||||
|
if(cone_angle < max_cone_angle && lRelC[i]->x > 0.2){
|
||||||
|
list_known_markers.emplace_back(best_line->point[i], *lRelP[i], *lRelC[i]);
|
||||||
|
isInFoV[i] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//2nd: use real coordinates if point was recognized, otherwise push it to a valid position (& save segment and unknown markers)
|
||||||
|
|
||||||
|
const Line6f field_line(best_line->point[0]->get_vector(), best_line->point[1]->get_vector(), best_line->length);
|
||||||
|
|
||||||
|
sVector3d l_pt_d[2]; //final line segment points (double precision floating-points)
|
||||||
|
|
||||||
|
for( int i=0; i<2; i++){
|
||||||
|
if(isInFoV[i]){
|
||||||
|
l_pt_d[i] = best_line->point[i]->pt; //set recognized point's abs coordinates
|
||||||
|
}else{
|
||||||
|
Vector3f p = field_line.segmentPointClosestToCartPoint(*lAbs[i]); //push point to closest valid position
|
||||||
|
l_pt_d[i].set(p); //set unknown point's estimated coordinates
|
||||||
|
list_unknown_markers.emplace_back(best_line, l_pt_d[i], *lRelP[i], *lRelC[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------- Save identified line segment
|
||||||
|
list_known_segments.emplace_back(sMarker(l_pt_d[0],*lRelP[0],*lRelC[0]),sMarker(l_pt_d[1],*lRelP[1],*lRelC[1]), l->length, best_line);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Field::update_unknown_markers(const Matrix4D& tmatrix){
|
||||||
|
|
||||||
|
for(auto& u : list_unknown_markers){
|
||||||
|
|
||||||
|
//Transform marker to world frame
|
||||||
|
Vector3f raw_abs_pos = tmatrix * u.relPosCart;
|
||||||
|
|
||||||
|
//Push marker to existing field segment
|
||||||
|
const Line6f field_seg( u.fieldSeg->point[0]->get_vector(), u.fieldSeg->point[1]->get_vector(), u.fieldSeg->length);
|
||||||
|
Vector3f fixed_abs_pos = field_seg.segmentPointClosestToCartPoint(raw_abs_pos); //push point to closest valid position
|
||||||
|
|
||||||
|
u.absPos = sVector3d({fixed_abs_pos.x, fixed_abs_pos.y, fixed_abs_pos.z});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//================================================================================== Math utilities
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field lines are on the ground (z=0), so the method is simplified
|
||||||
|
*/
|
||||||
|
float Field::fieldLineSegmentDistToCartPoint(const sFieldSegment& fLine, const Vector3f& cp){
|
||||||
|
|
||||||
|
//Line segment vector (start -> end)
|
||||||
|
float vx = fLine.point[1]->pt.x - fLine.point[0]->pt.x;
|
||||||
|
float vy = fLine.point[1]->pt.y - fLine.point[0]->pt.y;
|
||||||
|
|
||||||
|
Vector3f w1(cp.x - fLine.point[0]->pt.x, cp.y - fLine.point[0]->pt.y, cp.z); // vector: (segment start -> point)
|
||||||
|
if (w1.x * vx + w1.y * vy <= 0)
|
||||||
|
return w1.length();// if angle between vectors is >=90deg, we return the distance to segment start
|
||||||
|
|
||||||
|
Vector3f w2(cp.x - fLine.point[1]->pt.x, cp.y - fLine.point[1]->pt.y, cp.z); // vector: (segment end -> point)
|
||||||
|
if (w2.x * vx + w2.y * vy >= 0)
|
||||||
|
return w2.length(); //if angle between vectors is <=90deg, we return the distance to segment end
|
||||||
|
|
||||||
|
Vector3f v_cross_w1(vy * w1.z, - vx * w1.z, vx * w1.y - vy * w1.x);
|
||||||
|
return v_cross_w1.length() / fLine.length; //distance line to point (area of parallelogram divided by base gives height)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field lines are on the ground (z=0), so the method is simplified
|
||||||
|
*/
|
||||||
|
float Field::fieldLineSegmentDistToCart2DPoint(const sFieldSegment& fLine, const Vector& cp){
|
||||||
|
|
||||||
|
const Vector segment_start(fLine.point[0]->pt.x, fLine.point[0]->pt.y);
|
||||||
|
const Vector segment_end( fLine.point[1]->pt.x, fLine.point[1]->pt.y );
|
||||||
|
|
||||||
|
//Line segment vector (start -> end)
|
||||||
|
Vector v(segment_end-segment_start);
|
||||||
|
|
||||||
|
Vector w1(cp - segment_start);// vector: (segment start -> point)
|
||||||
|
|
||||||
|
if(w1.innerProduct(v) <= 0)
|
||||||
|
return w1.length();// if angle between vectors is >=90deg, we return the distance to segment start
|
||||||
|
|
||||||
|
Vector w2(cp - segment_end); // vector: (segment end -> point)
|
||||||
|
if(w2.innerProduct(v) >= 0)
|
||||||
|
return w2.length(); //if angle between vectors is <=90deg, we return the distance to segment end
|
||||||
|
|
||||||
|
return fabsf(v.crossProduct(w1)) / fLine.length; //distance line to point (area of parallelogram divided by base gives height)
|
||||||
|
}
|
@ -0,0 +1,503 @@
|
|||||||
|
/**
|
||||||
|
* FILENAME: Field
|
||||||
|
* DESCRIPTION: Field map
|
||||||
|
* AUTHOR: Miguel Abreu (m.abreu@fe.up.pt)
|
||||||
|
* DATE: 2021
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "Vector3f.h"
|
||||||
|
#include "Singleton.h"
|
||||||
|
#include "Matrix4D.h"
|
||||||
|
#include "Line6f.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
class Field {
|
||||||
|
friend class Singleton<Field>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Field(){};
|
||||||
|
void gather_ground_markers();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//====================================================================================== Structures
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
struct sVector3d {
|
||||||
|
double x,y,z;
|
||||||
|
|
||||||
|
//sVector3d(const Vector3f& v) : x(v.x), y(v.y), z(v.z) {}
|
||||||
|
|
||||||
|
Vector3f get_vector() const {
|
||||||
|
return Vector3f(x,y,z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(const sVector3d &pt){
|
||||||
|
x=pt.x; y=pt.y; z=pt.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(const Vector3f &pt){
|
||||||
|
x=pt.x; y=pt.y; z=pt.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dist(const Vector3f &other) const{
|
||||||
|
float dx = x-other.x;
|
||||||
|
float dy = y-other.y;
|
||||||
|
float dz = z-other.z;
|
||||||
|
return sqrtf(dx*dx+dy*dy+dz*dz);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sFieldPoint {
|
||||||
|
const sVector3d pt;
|
||||||
|
const char name[10];
|
||||||
|
|
||||||
|
Vector3f get_vector() const {
|
||||||
|
return Vector3f(pt.x,pt.y,pt.z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sFieldSegment {
|
||||||
|
const sFieldPoint * const point[2];
|
||||||
|
const double length;
|
||||||
|
const double angle;
|
||||||
|
const char name[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct sMarker {
|
||||||
|
/**
|
||||||
|
* Estimated absolute position based on the transformation matrix and field knowledge
|
||||||
|
*/
|
||||||
|
sVector3d absPos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer to corresponding field point (if reasonably inside the FoV)
|
||||||
|
* The coordinates are the same as "absPos" but it provides other features:
|
||||||
|
* - Name of the field point
|
||||||
|
* - Knowledge that this marker corresponds to a field point (nullptr otherwise)
|
||||||
|
* - The address of the fieldPt may be compared with field segment endpoints
|
||||||
|
*/
|
||||||
|
const sFieldPoint *fieldPt = nullptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer to corresponding field segment
|
||||||
|
* This variable is currently set only for unknown markers
|
||||||
|
* (i.e. those which are known to belong to a field line, but whose field point is unknown)
|
||||||
|
*/
|
||||||
|
const sFieldSegment *fieldSeg = nullptr;
|
||||||
|
|
||||||
|
Vector3f relPosPolar;
|
||||||
|
Vector3f relPosCart;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor
|
||||||
|
*/
|
||||||
|
sMarker() : absPos({0,0,0}), relPosPolar(Vector3f()), relPosCart(Vector3f()) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with absolute position and relative polar coordinates (the cartesian version is computed)
|
||||||
|
*/
|
||||||
|
sMarker(const sVector3d& absPos_, const Vector3f& relPosPolar_)
|
||||||
|
: absPos(absPos_), relPosPolar(relPosPolar_), relPosCart(relPosPolar_.toCartesian()) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with field point and relative polar coordinates (the cartesian version is computed)
|
||||||
|
*/
|
||||||
|
sMarker(const sFieldPoint* fieldPt_, const Vector3f& relPosPolar_)
|
||||||
|
: absPos(fieldPt_->pt), fieldPt(fieldPt_), relPosPolar(relPosPolar_), relPosCart(relPosPolar_.toCartesian()) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with float absolute position and relative polar coordinates (the cartesian version is computed)
|
||||||
|
*/
|
||||||
|
sMarker(const Vector3f& absPos_, const Vector3f& relPosPolar_)
|
||||||
|
: absPos(sVector3d({absPos_.x,absPos_.y,absPos_.z})), relPosPolar(relPosPolar_), relPosCart(relPosPolar_.toCartesian()) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with absolute position, relative polar & cartesian coordinates
|
||||||
|
*/
|
||||||
|
sMarker(const sVector3d& absPos_, const Vector3f& relPosPolar_, const Vector3f& relPosCart_)
|
||||||
|
: absPos(absPos_), relPosPolar(relPosPolar_), relPosCart(relPosCart_) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with field segment, absolute position, relative polar & cartesian coordinates (e.g. unknown marker)
|
||||||
|
*/
|
||||||
|
sMarker(const sFieldSegment* fieldSeg_, const sVector3d& absPos_, const Vector3f& relPosPolar_, const Vector3f& relPosCart_)
|
||||||
|
: fieldSeg(fieldSeg_), absPos(absPos_), relPosPolar(relPosPolar_), relPosCart(relPosCart_) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor with field point, relative polar & cartesian coordinates
|
||||||
|
*/
|
||||||
|
sMarker(const sFieldPoint* fieldPt_, const Vector3f& relPosPolar_, const Vector3f& relPosCart_)
|
||||||
|
: absPos(fieldPt_->pt), fieldPt(fieldPt_), relPosPolar(relPosPolar_), relPosCart(relPosCart_) {};
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sSegment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order of start and end is the same as the corresponding fieldSegment
|
||||||
|
* [0]-start, [1]-end
|
||||||
|
*/
|
||||||
|
sMarker point[2];
|
||||||
|
|
||||||
|
float length; //visible segment length
|
||||||
|
const sFieldSegment* fieldSegment; //Corresponding field segment if we had full visibility
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
sSegment(const sMarker& start, const sMarker& end, float length_, const sFieldSegment* fieldSegment_)
|
||||||
|
: point{start,end}, length(length_), fieldSegment(fieldSegment_) {};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sFixedMarker {
|
||||||
|
bool visible;
|
||||||
|
Vector3f relPosPolar;
|
||||||
|
Vector3f relPosCart;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor
|
||||||
|
*/
|
||||||
|
sFixedMarker() : relPosPolar(Vector3f()), relPosCart(Vector3f()), visible(false) {};
|
||||||
|
|
||||||
|
void set_relPos(Vector3f relPosPolar_){
|
||||||
|
relPosPolar = relPosPolar_;
|
||||||
|
relPosCart = relPosPolar_.toCartesian();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//================================================================================= Field constants
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant field dimensions
|
||||||
|
*/
|
||||||
|
static constexpr double cFieldLength = 30.0, cFieldWidth = 20.0, cPenaltyLength = 1.8, cPenaltyWidth = 6.0;
|
||||||
|
static constexpr double cGoalWidth = 2.1, cGoalDepth = 0.6, cGoalHeight = 0.8;
|
||||||
|
|
||||||
|
static constexpr double cHalfFielfLength = cFieldLength/2.0, cHalfFieldWidth = cFieldWidth/2.0;
|
||||||
|
static constexpr double cHalfGoalWidth = cGoalWidth/2.0, cHalfPenaltyLength = cPenaltyLength/2.0;
|
||||||
|
static constexpr double cHalfPenaltyWidth = cPenaltyWidth/2.0;
|
||||||
|
|
||||||
|
static constexpr double cPenaltyBoxDistX = cHalfFielfLength-cPenaltyLength;
|
||||||
|
static constexpr double cRingLineLength = 1.2360679774997897;
|
||||||
|
|
||||||
|
static constexpr float cHalfHorizontalFoV = 60;
|
||||||
|
static constexpr float cHalfVerticalFoV = 60;
|
||||||
|
|
||||||
|
static constexpr float stdev_distance = 0.0965; //st. deviation of error ed (distance error=d/100*ed)
|
||||||
|
static constexpr float var_distance = 0.00931225; // variance of error ed (distance error=d/100*ed)
|
||||||
|
static constexpr float var_round_hundredth = 0.01*0.01/12; //variance of uniformly distributed random variable [-0.005,0.005]
|
||||||
|
|
||||||
|
|
||||||
|
class cFieldPoints{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constant list of field points
|
||||||
|
* Notation
|
||||||
|
* "PT1-.-PT2" midpoint between PT1 and PT2 (2D/3D)
|
||||||
|
* "PT1-PT2" point between PT1 and PT2 (in 2D only)
|
||||||
|
*/
|
||||||
|
static constexpr std::array<sFieldPoint,28> list {{
|
||||||
|
{-cHalfFielfLength,-cHalfGoalWidth, cGoalHeight, "post--"}, {-cHalfFielfLength, cHalfGoalWidth, cGoalHeight, "post-+"}, //Goalposts x<0
|
||||||
|
{ cHalfFielfLength,-cHalfGoalWidth, cGoalHeight, "post+-"}, { cHalfFielfLength, cHalfGoalWidth, cGoalHeight, "post++"}, //Goalposts x>0
|
||||||
|
{-cHalfFielfLength,-cHalfFieldWidth,0, "corner--"}, {-cHalfFielfLength, cHalfFieldWidth,0, "corner-+"}, //Corners x<0
|
||||||
|
{ cHalfFielfLength,-cHalfFieldWidth,0, "corner+-"}, { cHalfFielfLength, cHalfFieldWidth,0, "corner++"}, //Corners x>0
|
||||||
|
{0,-cHalfFieldWidth, 0, "halfway-"}, // Halfway line ending y<0
|
||||||
|
{0, cHalfFieldWidth, 0, "halfway+"}, // Halfway line ending y>0
|
||||||
|
{-cHalfFielfLength, -cHalfPenaltyWidth, 0, "boxBack--"}, {-cHalfFielfLength, cHalfPenaltyWidth, 0, "boxBack-+"}, //Penalty box goal line corner x<0
|
||||||
|
{ cHalfFielfLength, -cHalfPenaltyWidth, 0, "boxBack+-"}, { cHalfFielfLength, cHalfPenaltyWidth, 0, "boxBack++"}, //Penalty box goal line corner x>0
|
||||||
|
{-cPenaltyBoxDistX, -cHalfPenaltyWidth, 0, "boxFrnt--"}, {-cPenaltyBoxDistX, cHalfPenaltyWidth, 0, "boxFrnt-+"}, //Penalty box front corner x<0
|
||||||
|
{ cPenaltyBoxDistX, -cHalfPenaltyWidth, 0, "boxFrnt+-"}, { cPenaltyBoxDistX, cHalfPenaltyWidth, 0, "boxFrnt++"}, //Penalty box front corner x>0
|
||||||
|
{2, 0, 0, "r0"}, { 1.6180339887498948, 1.1755705045849463, 0, "r36" }, //(18,19) Ring 0/36 deg
|
||||||
|
{0.61803398874989485, 1.9021130325903071, 0, "r72" }, {-0.61803398874989485, 1.9021130325903071, 0, "r108"}, //(20,21) Ring 72/108 deg
|
||||||
|
{-1.6180339887498948, 1.1755705045849463, 0, "r144"}, {-2, 0, 0, "r180"}, //(22,23) Ring 144/180 deg
|
||||||
|
{-1.6180339887498948, -1.1755705045849463, 0, "r216"}, {-0.61803398874989485, -1.9021130325903071, 0, "r252"}, //(24,25) Ring 216/252 deg
|
||||||
|
{0.61803398874989485, -1.9021130325903071, 0, "r288"}, { 1.6180339887498948, -1.1755705045849463, 0, "r324"} //(26,27) Ring 288/324 deg
|
||||||
|
}};
|
||||||
|
|
||||||
|
static constexpr const sFieldPoint &goal_mm = list[0]; //Goalpost x<0 y<0
|
||||||
|
static constexpr const sFieldPoint &goal_mp = list[1]; //Goalpost x<0 y>0
|
||||||
|
static constexpr const sFieldPoint &goal_pm = list[2]; //Goalpost x>0 y<0
|
||||||
|
static constexpr const sFieldPoint &goal_pp = list[3]; //Goalpost x>0 y>0
|
||||||
|
|
||||||
|
static constexpr const sFieldPoint &corner_mm = list[4]; //Corner x<0 y<0
|
||||||
|
static constexpr const sFieldPoint &corner_mp = list[5]; //Corner x<0 y>0
|
||||||
|
static constexpr const sFieldPoint &corner_pm = list[6]; //Corner x>0 y<0
|
||||||
|
static constexpr const sFieldPoint &corner_pp = list[7]; //Corner x>0 y>0
|
||||||
|
|
||||||
|
static constexpr const sFieldPoint &halfway_m = list[8]; //Halfway line ending y<0
|
||||||
|
static constexpr const sFieldPoint &halfway_p = list[9]; //Halfway line ending y>0
|
||||||
|
|
||||||
|
static constexpr const sFieldPoint &boxgoal_mm = list[10]; //Penalty box goal line corner x<0 y<0
|
||||||
|
static constexpr const sFieldPoint &boxgoal_mp = list[11]; //Penalty box goal line corner x<0 y>0
|
||||||
|
static constexpr const sFieldPoint &boxgoal_pm = list[12]; //Penalty box goal line corner x>0 y<0
|
||||||
|
static constexpr const sFieldPoint &boxgoal_pp = list[13]; //Penalty box goal line corner x>0 y>0
|
||||||
|
|
||||||
|
static constexpr const sFieldPoint &box_mm = list[14]; //Penalty box front corner x<0 y<0
|
||||||
|
static constexpr const sFieldPoint &box_mp = list[15]; //Penalty box front corner x<0 y>0
|
||||||
|
static constexpr const sFieldPoint &box_pm = list[16]; //Penalty box front corner x>0 y<0
|
||||||
|
static constexpr const sFieldPoint &box_pp = list[17]; //Penalty box front corner x>0 y>0
|
||||||
|
|
||||||
|
static constexpr const sFieldPoint *rings = &list[18]; //iterator for 10 ring points
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant list of field line segments
|
||||||
|
* Each line segment has 3 characteristics: {startc, endc, length, angle, print name},
|
||||||
|
* The angle is always positive, in [0,180[, and corresponds to the vector defined by (end-start)
|
||||||
|
* The print name should be used for printing purposes only,
|
||||||
|
* since the line segment can be identified by its constant index or address
|
||||||
|
*/
|
||||||
|
|
||||||
|
class cFieldLineSegments{
|
||||||
|
public:
|
||||||
|
static constexpr double c0deg = 0, c36deg = 0.62831853071795865, c72deg = 1.2566370614359173;
|
||||||
|
static constexpr double c90deg = 1.5707963267948966, c108deg = 1.8849555921538759, c144deg = 2.5132741228718346;
|
||||||
|
|
||||||
|
static constexpr std::array<sFieldSegment,21> list {{
|
||||||
|
{&cFieldPoints::corner_mm, &cFieldPoints::corner_pm, cFieldLength, c0deg , "side-"}, // Sideline y<0
|
||||||
|
{&cFieldPoints::corner_mp, &cFieldPoints::corner_pp, cFieldLength, c0deg , "side+"}, // Sideline y>0
|
||||||
|
{&cFieldPoints::corner_mm, &cFieldPoints::corner_mp, cFieldWidth, c90deg , "goal-"}, // Goal line x<0
|
||||||
|
{&cFieldPoints::corner_pm, &cFieldPoints::corner_pp, cFieldWidth, c90deg , "goal+"}, // Goal line x>0
|
||||||
|
{&cFieldPoints::halfway_m, &cFieldPoints::halfway_p, cFieldWidth, c90deg , "halfway"},// Halfway line
|
||||||
|
{&cFieldPoints::boxgoal_mm, &cFieldPoints::box_mm, cPenaltyLength, c0deg , "box--"}, // Penalty box sideline x<0 y<0
|
||||||
|
{&cFieldPoints::boxgoal_mp, &cFieldPoints::box_mp, cPenaltyLength, c0deg , "box-+"}, // Penalty box sideline x<0 y>0
|
||||||
|
{&cFieldPoints::box_pm, &cFieldPoints::boxgoal_pm, cPenaltyLength, c0deg , "box+-"}, // Penalty box sideline x>0 y<0
|
||||||
|
{&cFieldPoints::box_pp, &cFieldPoints::boxgoal_pp, cPenaltyLength, c0deg , "box++"}, // Penalty box sideline x>0 y>0
|
||||||
|
{&cFieldPoints::box_mm, &cFieldPoints::box_mp, cPenaltyWidth, c90deg , "box-"}, // Penalty box front line x<0
|
||||||
|
{&cFieldPoints::box_pm, &cFieldPoints::box_pp, cPenaltyWidth, c90deg , "box+"}, // Penalty box front line x>0
|
||||||
|
{&cFieldPoints::rings[0], &cFieldPoints::rings[1], cRingLineLength, c108deg, "rL0"}, // Ring line 0 -> 36
|
||||||
|
{&cFieldPoints::rings[1], &cFieldPoints::rings[2], cRingLineLength, c144deg, "rL1"}, // Ring line 36 -> 72
|
||||||
|
{&cFieldPoints::rings[3], &cFieldPoints::rings[2], cRingLineLength, c0deg , "rL2"}, // Ring line 72 <- 108
|
||||||
|
{&cFieldPoints::rings[4], &cFieldPoints::rings[3], cRingLineLength, c36deg , "rL3"}, // Ring line 108 <- 144
|
||||||
|
{&cFieldPoints::rings[5], &cFieldPoints::rings[4], cRingLineLength, c72deg , "rL4"}, // Ring line 144 <- 180
|
||||||
|
{&cFieldPoints::rings[6], &cFieldPoints::rings[5], cRingLineLength, c108deg, "rL5"}, // Ring line 180 <- 216
|
||||||
|
{&cFieldPoints::rings[7], &cFieldPoints::rings[6], cRingLineLength, c144deg, "rL6"}, // Ring line 216 <- 252
|
||||||
|
{&cFieldPoints::rings[7], &cFieldPoints::rings[8], cRingLineLength, c0deg , "rL7"}, // Ring line 252 -> 288
|
||||||
|
{&cFieldPoints::rings[8], &cFieldPoints::rings[9], cRingLineLength, c36deg , "rL8"}, // Ring line 288 -> 324
|
||||||
|
{&cFieldPoints::rings[9], &cFieldPoints::rings[0], cRingLineLength, c72deg , "rL9"} // Ring line 324 -> 0
|
||||||
|
}};
|
||||||
|
|
||||||
|
static constexpr const sFieldSegment &side_m = list[0]; // Sideline y<0
|
||||||
|
static constexpr const sFieldSegment &side_p = list[1]; // Sideline y>0
|
||||||
|
static constexpr const sFieldSegment &goal_m = list[2]; // Goal line x<0
|
||||||
|
static constexpr const sFieldSegment &goal_p = list[3]; // Goal line x>0
|
||||||
|
|
||||||
|
static constexpr const sFieldSegment &halfway = list[4]; //Halfway line
|
||||||
|
|
||||||
|
static constexpr const sFieldSegment &box_mm = list[5]; // Penalty box sideline x<0 y<0
|
||||||
|
static constexpr const sFieldSegment &box_mp = list[6]; // Penalty box sideline x<0 y>0
|
||||||
|
static constexpr const sFieldSegment &box_pm = list[7]; // Penalty box sideline x>0 y<0
|
||||||
|
static constexpr const sFieldSegment &box_pp = list[8]; // Penalty box sideline x>0 y>0
|
||||||
|
|
||||||
|
static constexpr const sFieldSegment &box_m = list[9]; // Penalty box front line x<0
|
||||||
|
static constexpr const sFieldSegment &box_p = list[10]; // Penalty box front line x>0
|
||||||
|
|
||||||
|
static constexpr const sFieldSegment *rings = &list[11]; //iterator for 10 ring lines
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sSegment* get_known_segment(const sFieldSegment &id){
|
||||||
|
for( auto& s : list_known_segments){
|
||||||
|
if(s.fieldSegment == &id) return &s;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//================================================================================= Control methods
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update markers, based on existing landmarks and lines
|
||||||
|
*/
|
||||||
|
void update();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update markers, based on transformation matrix and existing lines
|
||||||
|
*/
|
||||||
|
void update_from_transformation(const Matrix4D& tmatrix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the absolute position of unknown markers, based on transformation matrix and existing lines
|
||||||
|
*/
|
||||||
|
void update_unknown_markers(const Matrix4D& tmatrix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw estimates of all visible lines, markers, self position and ball
|
||||||
|
*/
|
||||||
|
void draw_visible(const Matrix4D& headToFieldT, bool is_right_side) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw estimates of all visible lines, markers, self position and ball, but switch field sides
|
||||||
|
*/
|
||||||
|
void draw_visible_switch(const Matrix4D& headToFieldT) const;
|
||||||
|
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//============================================================================= Visible collections
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible landmarks: corners + goalposts
|
||||||
|
*/
|
||||||
|
vector<sMarker> list_landmarks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible corners
|
||||||
|
*/
|
||||||
|
vector<sMarker> list_landmarks_corners;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible goalposts
|
||||||
|
*/
|
||||||
|
vector<sMarker> list_landmarks_goalposts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identified visible line segments
|
||||||
|
* Their start and endpoints' order is the same as the corresponding field segment to which they point
|
||||||
|
*/
|
||||||
|
vector<sSegment> list_known_segments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identified visible line segment endpoints + landmarks
|
||||||
|
* Each marker has a reference to the corresponding field point
|
||||||
|
*/
|
||||||
|
vector<sMarker> list_known_markers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoints (of identified visible line segments) whose corresponding field point is unknown
|
||||||
|
* Each marker has a reference to the corresponding field segment
|
||||||
|
* Note: list_known_markers + list_unknown_markers excludes points from unknown line segments
|
||||||
|
*/
|
||||||
|
vector<sMarker> list_unknown_markers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible line endpoints + foot contact points + corner flags (the absolute position is always (0,0,0))
|
||||||
|
*/
|
||||||
|
vector<sMarker> list_ground_markers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of visible non-collinear (line endpoints + foot contact points)
|
||||||
|
* Note: collinearity between lines is impossible; between feet<->lines it is possible but unlikely
|
||||||
|
*/
|
||||||
|
int non_collinear_ground_markers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as list_ground_markers but closer points are repeated more often (proportional to distance)
|
||||||
|
*/
|
||||||
|
vector<sMarker> list_weighted_ground_markers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feet contact points
|
||||||
|
*/
|
||||||
|
vector<sMarker> list_feet_contact_points;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible line segments
|
||||||
|
*/
|
||||||
|
vector<Line6f> list_segments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redundant list of all 8 landmarks' relative cartesian coordinates (to speed up lookups)
|
||||||
|
* It's different from world.landmarks since it is ordered by position, not by name, and it holds the cartesian relPos
|
||||||
|
* (this ordering difference is important when the teams switch sides)
|
||||||
|
*/
|
||||||
|
|
||||||
|
class list_8_landmarks{
|
||||||
|
friend class Field;
|
||||||
|
private:
|
||||||
|
static sFixedMarker list[8];
|
||||||
|
//static std::array<sFixedMarker,8> list;
|
||||||
|
static constexpr sFixedMarker &_corner_mm = list[0];
|
||||||
|
static constexpr sFixedMarker &_corner_mp = list[1];
|
||||||
|
static constexpr sFixedMarker &_corner_pm = list[2];
|
||||||
|
static constexpr sFixedMarker &_corner_pp = list[3];
|
||||||
|
static constexpr sFixedMarker &_goal_mm = list[4];
|
||||||
|
static constexpr sFixedMarker &_goal_mp = list[5];
|
||||||
|
static constexpr sFixedMarker &_goal_pm = list[6];
|
||||||
|
static constexpr sFixedMarker &_goal_pp = list[7];
|
||||||
|
public:
|
||||||
|
static constexpr const sFixedMarker &corner_mm = list[0];
|
||||||
|
static constexpr const sFixedMarker &corner_mp = list[1];
|
||||||
|
static constexpr const sFixedMarker &corner_pm = list[2];
|
||||||
|
static constexpr const sFixedMarker &corner_pp = list[3];
|
||||||
|
static constexpr const sFixedMarker &goal_mm = list[4];
|
||||||
|
static constexpr const sFixedMarker &goal_mp = list[5];
|
||||||
|
static constexpr const sFixedMarker &goal_pm = list[6];
|
||||||
|
static constexpr const sFixedMarker &goal_pp = list[7];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//================================================================================== Math Utilities
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute 3D distance between field line segment and cartesian point
|
||||||
|
* Field lines are on the ground (z=0), so the method is simplified
|
||||||
|
*/
|
||||||
|
static float fieldLineSegmentDistToCartPoint(const sFieldSegment& fLine, const Vector3f& cp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute 2D distance between field line segment and cartesian point
|
||||||
|
* Field lines are on the ground (z=0), so the method is simplified
|
||||||
|
*/
|
||||||
|
static float fieldLineSegmentDistToCart2DPoint(const sFieldSegment& fLine, const Vector& cp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize angle between 2 lines
|
||||||
|
* @return angle between 0 and 90 deg
|
||||||
|
*/
|
||||||
|
static inline float normalize_line_angle_deg(float deg){
|
||||||
|
return 90.f-fabsf(fmodf(fabsf(deg), 180.f) - 90.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize angle between 2 lines
|
||||||
|
* @return angle between 0 and pi/2 rad
|
||||||
|
*/
|
||||||
|
static inline float normalize_line_angle_rad(float rad){
|
||||||
|
return 1.57079633f-fabsf(fmod(fabsf(rad), 3.14159265f) - 1.57079633f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize angle between 2 vectors
|
||||||
|
* @return angle between 0 and 180 deg
|
||||||
|
*/
|
||||||
|
static inline float normalize_vector_angle_deg(float deg){
|
||||||
|
return 180.f-fabsf(fmodf(fabsf(deg), 360.f) - 180.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize angle between 2 vectors
|
||||||
|
* @return angle between 0 and pi rad
|
||||||
|
*/
|
||||||
|
static inline float normalize_vector_angle_rad(float rad){
|
||||||
|
return 3.14159265f-fabsf(fmod(fabsf(rad), 6.28318531f) - 3.14159265f);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Singleton<Field> SField;
|
@ -0,0 +1,104 @@
|
|||||||
|
#include "FieldNoise.h"
|
||||||
|
|
||||||
|
double FieldNoise::log_prob_r(double d, double r){
|
||||||
|
double c1 = 100.0 * ((r-0.005)/d - 1);
|
||||||
|
double c2 = 100.0 * ((r+0.005)/d - 1);
|
||||||
|
return log_prob_normal_distribution(0, 0.0965, c1, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
double FieldNoise::log_prob_h(double h, double phi){
|
||||||
|
double c1 = phi - 0.005 - h;
|
||||||
|
double c2 = phi + 0.005 - h;
|
||||||
|
return log_prob_normal_distribution(0, 0.1225, c1, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
double FieldNoise::log_prob_v(double v, double theta){
|
||||||
|
double c1 = theta - 0.005 - v;
|
||||||
|
double c2 = theta + 0.005 - v;
|
||||||
|
return log_prob_normal_distribution(0, 0.1480, c1, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
double FieldNoise::log_prob_normal_distribution(double mean, double std, double interval1, double interval2){
|
||||||
|
const double std2 = std * sqrt(2);
|
||||||
|
double erf1_x = (mean - interval1)/std2; //lowest interval, highest expression
|
||||||
|
double erf2_x = (mean - interval2)/std2; //highest interval, lowest expression
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computing erf(erf_x1) - erf(erf_x2) is the same as erf(erf_x1) + erf(-erf_x2).
|
||||||
|
* Intuitively, the former seems more natural.
|
||||||
|
* So, computationally, the former expression is accurate in the following situations:
|
||||||
|
* - erf_x1 * erf_x2 <= 0
|
||||||
|
* - |erf_x1| < 3 _or_ |erf_x2| < 3 ('3' is just a ballpark figure, not really relevant)
|
||||||
|
*
|
||||||
|
* Known issues: erf(6.5)-erf(6.0) = 1-1 = 0 (actual result: 2.148e-17)
|
||||||
|
* Using 128b functions only mitigates the issue, which is quite common actually.
|
||||||
|
*
|
||||||
|
* For these cases, erf_aux(x) is used, although it is not precise for |x|<1.
|
||||||
|
* - erf_aux(x) allows the computation of erf(6.5)-erf(6.0) with 7 digits of precision
|
||||||
|
* - erf_aux(x) allows the computation of erf(8.5)-erf(8.0) with 3 digits of precision
|
||||||
|
* - erf(12.5)-erf(12) = 0 (which is not good for probability comparison)
|
||||||
|
* - erf_aux(x) allows the computation of log(erf(6.5)-erf(6.0)) with 8 digits of precision
|
||||||
|
* - erf_aux(x) allows the computation of log(erf(8.5)-erf(8.0)) with 5 digits of precision
|
||||||
|
* - log(erf(12.5)-erf(12)) = -4647 (real: -147) (not accurate but good for comparisons)
|
||||||
|
*
|
||||||
|
* The complete algorithm below that uses erf_aux(x) is almost as fast as the one which uses erf() from math.h (+30% runtime)
|
||||||
|
*/
|
||||||
|
|
||||||
|
const double log05 = log(0.5);
|
||||||
|
|
||||||
|
//If they have different sign or |erf1_x|<1 || |erf2_x|<1
|
||||||
|
if( fabs(erf1_x) < 2 || fabs(erf2_x) < 2 || ((erf1_x > 0) ^ (erf2_x > 0))){
|
||||||
|
return log( erf(erf1_x) - erf(erf2_x) ) + log05; // same but faster than log( 0.5 * (erf(erf1_x) - erf(erf2_x)) )
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise use erf_aux(x)
|
||||||
|
//At this point, erf1_x and erf2_x have the same sign and are both distant from 0
|
||||||
|
|
||||||
|
double erf1 = erf_aux(erf1_x);
|
||||||
|
double erf2 = erf_aux(erf2_x);
|
||||||
|
|
||||||
|
//These operations are described in the documentation of erf_aux()
|
||||||
|
if(erf1_x > 0){ //both are positive
|
||||||
|
return log( 1.0 - exp(erf1-erf2) ) + erf2 + log05;
|
||||||
|
}else{ //both are negative
|
||||||
|
return log( 1.0 - exp(erf2-erf1) ) + erf1 + log05;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
double FieldNoise::erf_aux(double a){
|
||||||
|
double r, s, t, u;
|
||||||
|
|
||||||
|
t = fabs (a);
|
||||||
|
s = a * a;
|
||||||
|
|
||||||
|
r = fma (-5.6271698458222802e-018, t, 4.8565951833159269e-016);
|
||||||
|
u = fma (-1.9912968279795284e-014, t, 5.1614612430130285e-013);
|
||||||
|
r = fma (r, s, u);
|
||||||
|
r = fma (r, t, -9.4934693735334407e-012);
|
||||||
|
r = fma (r, t, 1.3183034417266867e-010);
|
||||||
|
r = fma (r, t, -1.4354030030124722e-009);
|
||||||
|
r = fma (r, t, 1.2558925114367386e-008);
|
||||||
|
r = fma (r, t, -8.9719702096026844e-008);
|
||||||
|
r = fma (r, t, 5.2832013824236141e-007);
|
||||||
|
r = fma (r, t, -2.5730580226095829e-006);
|
||||||
|
r = fma (r, t, 1.0322052949682532e-005);
|
||||||
|
r = fma (r, t, -3.3555264836704290e-005);
|
||||||
|
r = fma (r, t, 8.4667486930270974e-005);
|
||||||
|
r = fma (r, t, -1.4570926486272249e-004);
|
||||||
|
r = fma (r, t, 7.1877160107951816e-005);
|
||||||
|
r = fma (r, t, 4.9486959714660115e-004);
|
||||||
|
r = fma (r, t, -1.6221099717135142e-003);
|
||||||
|
r = fma (r, t, 1.6425707149019371e-004);
|
||||||
|
r = fma (r, t, 1.9148914196620626e-002);
|
||||||
|
r = fma (r, t, -1.0277918343487556e-001);
|
||||||
|
r = fma (r, t, -6.3661844223699315e-001);
|
||||||
|
r = fma (r, t, -1.2837929411398119e-001);
|
||||||
|
r = fma (r, t, -t);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* FILENAME: FieldNoise
|
||||||
|
* DESCRIPTION: efficient computation of relative probabilities (for the noise model of the RoboCup 3DSSL)
|
||||||
|
* AUTHOR: Miguel Abreu (m.abreu@fe.up.pt)
|
||||||
|
* DATE: 2021
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "math.h"
|
||||||
|
|
||||||
|
class FieldNoise{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log probability of real distance d, given noisy radius r
|
||||||
|
*/
|
||||||
|
static double log_prob_r(double d, double r);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log probability of real horizontal angle h, given noisy angle phi
|
||||||
|
*/
|
||||||
|
static double log_prob_h(double h, double phi);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log probability of real vertical angle v, given noisy angle theta
|
||||||
|
*/
|
||||||
|
static double log_prob_v(double v, double theta);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
FieldNoise(){}; //Disable construction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log probability of normally distributed random variable X from interval1 to interval2:
|
||||||
|
* Log Pr[interval1 < X < interval2]
|
||||||
|
* @param mean mean of random variable
|
||||||
|
* @param std standard deviation of random variable
|
||||||
|
* @param interval1 minimum value of random variable
|
||||||
|
* @param interval2 maximum value of random variable
|
||||||
|
*/
|
||||||
|
static double log_prob_normal_distribution(double mean, double std, double interval1, double interval2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns ln(1-sgn(a)*erf(a)), but sgn(a)*erf(a) = |erf(a)|, because sgn(a) == sgn(erf(a))
|
||||||
|
* So, it returns: ln(1-|erf(a)|), which is <=0
|
||||||
|
*
|
||||||
|
* NOTE: condition to guarantee high precision: |a|>= 1
|
||||||
|
*
|
||||||
|
* how to compute erf(a) ?
|
||||||
|
* erf(a) = sgn(a)(1-e^erf_aux(a))
|
||||||
|
*
|
||||||
|
* how to compute erf(a)+erf(b) ?
|
||||||
|
* erf(a)+erf(b) = sgn(a)(1-e^erf_aux(a)) + sgn(b)(1-e^erf_aux(b))
|
||||||
|
* assuming a<0 and b>0:
|
||||||
|
* = e^erf_aux(a) -1 + 1 - e^erf_aux(b)
|
||||||
|
* = e^erf_aux(a) - e^erf_aux(b)
|
||||||
|
*
|
||||||
|
* example: erf(-7)+erf(7.1)
|
||||||
|
* if we computed it directly:
|
||||||
|
* erf(-7)+erf(7.1) = -0.9999999(...) + 0.9999999(...) = -1+1 = 0 (due to lack of precision, even if using double)
|
||||||
|
* if we use the proposed method:
|
||||||
|
* e^erf_aux(-7) - e^erf_aux(7.1) = -1.007340e-23 - -4.183826e-23 = 3.176486E-23
|
||||||
|
*
|
||||||
|
* how to compute ln(erf(a)+erf(b)) ?
|
||||||
|
* assuming a<0 and b>0:
|
||||||
|
* ln(erf(a)+erf(b)) = ln( exp(erf_aux(a)) - exp(erf_aux(b)) )
|
||||||
|
* = ln( exp(erf_aux(a)-k) - exp(erf_aux(b)-k) ) + k
|
||||||
|
* where k = min(erf_aux(a), erf_aux(b))
|
||||||
|
*
|
||||||
|
* how to compute ln(erf(a)-erf(b)) ? (the difference is just the assumption)
|
||||||
|
* assuming a*b >= 0
|
||||||
|
*
|
||||||
|
* ln(erf(a)-erf(b)) = ln( sgn(a)(1-e^erf_aux(a)) - sgn(a)(1-e^erf_aux(b)) ), note that sgn(a)=sgn(b)
|
||||||
|
*
|
||||||
|
* rule: log( exp(a) - exp(b) ) = log( exp(a-k) - exp(b-k) ) + k
|
||||||
|
*
|
||||||
|
* if(a>0)
|
||||||
|
* ln(erf(a)-erf(b)) = ln( 1 - e^erf_aux(a) - 1 + e^erf_aux(b))
|
||||||
|
* = ln( exp(erf_aux(b)) - exp(erf_aux(a)) )
|
||||||
|
* = ln( exp(erf_aux(b)-erf_aux(b)) - exp(erf_aux(a)-erf_aux(b)) ) + erf_aux(b)
|
||||||
|
* = ln( 1 - exp(erf_aux(a)-erf_aux(b)) ) + erf_aux(b)
|
||||||
|
* if(a<0)
|
||||||
|
* ln(erf(a)-erf(b)) = ln( -1 + e^erf_aux(a) + 1 - e^erf_aux(b))
|
||||||
|
* = ln( exp(erf_aux(a)) - exp(erf_aux(b)) )
|
||||||
|
* = ln( exp(erf_aux(a)-erf_aux(a)) - exp(erf_aux(b)-erf_aux(a)) ) + erf_aux(a)
|
||||||
|
* = ln( 1 - exp(erf_aux(b)-erf_aux(a)) ) + erf_aux(a)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static double erf_aux(double a);
|
||||||
|
};
|
@ -0,0 +1,344 @@
|
|||||||
|
#include "Geometry.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns the cosine of a given angle in degrees using the
|
||||||
|
* built-in cosine function that works with angles in radians.
|
||||||
|
*
|
||||||
|
* @param x an angle in degrees
|
||||||
|
* @return the cosine of the given angle
|
||||||
|
*/
|
||||||
|
float Cos(float x) {
|
||||||
|
return ( cos(x * M_PI / 180));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns the sine of a given angle in degrees using the
|
||||||
|
* built-in sine function that works with angles in radians.
|
||||||
|
*
|
||||||
|
* @param x an angle in degrees
|
||||||
|
* @return the sine of the given angle
|
||||||
|
*/
|
||||||
|
float Sin(float x) {
|
||||||
|
return ( sin(x * M_PI / 180));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns the principal value of the arc tangent of y/x in
|
||||||
|
* degrees using the signs of both arguments to determine the quadrant of the
|
||||||
|
* return value. For this the built-in 'atan2' function is used which returns
|
||||||
|
* this value in radians.
|
||||||
|
*
|
||||||
|
* @param x a float value
|
||||||
|
* @param y a float value
|
||||||
|
* @return the arc tangent of y/x in degrees taking the signs of x and y into
|
||||||
|
* account
|
||||||
|
*/
|
||||||
|
float ATan2(float x, float y) {
|
||||||
|
if (fabs(x) < EPSILON && fabs(y) < EPSILON)
|
||||||
|
return ( 0.0);
|
||||||
|
|
||||||
|
return ( atan2(x, y) * 180 / M_PI );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/******************* CLASS VECTOR ***********************************/
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
/*! Constructor for the Vector class. Arguments x and y
|
||||||
|
denote the x- and y-coordinates of the new position.
|
||||||
|
\param x the x-coordinate of the new position
|
||||||
|
\param y the y-coordinate of the new position
|
||||||
|
\return the Vector corresponding to the given arguments */
|
||||||
|
Vector::Vector(float vx, float vy) : x(vx), y(vy) {}
|
||||||
|
|
||||||
|
/*! Overloaded version of unary minus operator for Vectors. It returns the
|
||||||
|
negative Vector, i.e. both the x- and y-coordinates are multiplied by
|
||||||
|
-1. The current Vector itself is left unchanged.
|
||||||
|
\return a negated version of the current Vector */
|
||||||
|
Vector Vector::operator-() const{
|
||||||
|
return ( Vector(-x, -y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the binary plus operator for adding a given float
|
||||||
|
value to a Vector. The float value is added to both the x- and
|
||||||
|
y-coordinates of the current Vector. The current Vector itself is
|
||||||
|
left unchanged.
|
||||||
|
\param d a float value which has to be added to both the x- and
|
||||||
|
y-coordinates of the current Vector
|
||||||
|
\return the result of adding the given float value to the current
|
||||||
|
Vector */
|
||||||
|
Vector Vector::operator+(const float &d) const{
|
||||||
|
return ( Vector(x + d, y + d));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the binary plus operator for Vectors. It returns
|
||||||
|
the sum of the current Vector and the given Vector by adding their
|
||||||
|
x- and y-coordinates. The Vectors themselves are left unchanged.
|
||||||
|
\param p a Vector
|
||||||
|
\return the sum of the current Vector and the given Vector */
|
||||||
|
Vector Vector::operator+(const Vector &p) const{
|
||||||
|
return ( Vector(x + p.x, y + p.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the binary minus operator for subtracting a
|
||||||
|
given float value from a Vector. The float value is
|
||||||
|
subtracted from both the x- and y-coordinates of the current
|
||||||
|
Vector. The current Vector itself is left unchanged.
|
||||||
|
\param d a float value which has to be subtracted from both the x- and
|
||||||
|
y-coordinates of the current Vector
|
||||||
|
\return the result of subtracting the given float value from the current
|
||||||
|
Vector */
|
||||||
|
Vector Vector::operator-(const float &d) const{
|
||||||
|
return ( Vector(x - d, y - d));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the binary minus operator for
|
||||||
|
Vectors. It returns the difference between the current
|
||||||
|
Vector and the given Vector by subtracting their x- and
|
||||||
|
y-coordinates. The Vectors themselves are left unchanged.
|
||||||
|
|
||||||
|
\param p a Vector
|
||||||
|
\return the difference between the current Vector and the given
|
||||||
|
Vector */
|
||||||
|
Vector Vector::operator-(const Vector &p) const {
|
||||||
|
return ( Vector(x - p.x, y - p.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the multiplication operator for multiplying a
|
||||||
|
Vector by a given float value. Both the x- and y-coordinates of the
|
||||||
|
current Vector are multiplied by this value. The current Vector
|
||||||
|
itself is left unchanged.
|
||||||
|
\param d the multiplication factor
|
||||||
|
\return the result of multiplying the current Vector by the given
|
||||||
|
float value */
|
||||||
|
Vector Vector::operator*(const float &d) const{
|
||||||
|
return ( Vector(x * d, y * d));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the multiplication operator for
|
||||||
|
Vectors. It returns the product of the current Vector
|
||||||
|
and the given Vector by multiplying their x- and
|
||||||
|
y-coordinates. The Vectors themselves are left unchanged.
|
||||||
|
|
||||||
|
\param p a Vector
|
||||||
|
\return the product of the current Vector and the given Vector */
|
||||||
|
Vector Vector::operator*(const Vector &p) const{
|
||||||
|
return ( Vector(x * p.x, y * p.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the division operator for dividing a
|
||||||
|
Vector by a given float value. Both the x- and y-coordinates
|
||||||
|
of the current Vector are divided by this value. The current
|
||||||
|
Vector itself is left unchanged.
|
||||||
|
|
||||||
|
\param d the division factor
|
||||||
|
\return the result of dividing the current Vector by the given float
|
||||||
|
value */
|
||||||
|
Vector Vector::operator/(const float &d) const{
|
||||||
|
return ( Vector(x / d, y / d));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the division operator for Vectors. It
|
||||||
|
returns the quotient of the current Vector and the given
|
||||||
|
Vector by dividing their x- and y-coordinates. The
|
||||||
|
Vectors themselves are left unchanged.
|
||||||
|
|
||||||
|
\param p a Vector
|
||||||
|
\return the quotient of the current Vector and the given one */
|
||||||
|
Vector Vector::operator/(const Vector &p) const{
|
||||||
|
return ( Vector(x / p.x, y / p.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the assignment operator for assigning a given float
|
||||||
|
value to both the x- and y-coordinates of the current Vector. This
|
||||||
|
changes the current Vector itself.
|
||||||
|
\param d a float value which has to be assigned to both the x- and
|
||||||
|
y-coordinates of the current Vector */
|
||||||
|
void Vector::operator=(const float &d) {
|
||||||
|
x = d;
|
||||||
|
y = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the sum-assignment operator for Vectors. It
|
||||||
|
returns the sum of the current Vector and the given Vector by
|
||||||
|
adding their x- and y-coordinates. This changes the current Vector
|
||||||
|
itself.
|
||||||
|
\param p a Vector which has to be added to the current Vector */
|
||||||
|
void Vector::operator+=(const Vector &p) {
|
||||||
|
x += p.x;
|
||||||
|
y += p.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the sum-assignment operator for adding a given float
|
||||||
|
value to a Vector. The float value is added to both the x- and
|
||||||
|
y-coordinates of the current Vector. This changes the current
|
||||||
|
Vector itself.
|
||||||
|
\param d a float value which has to be added to both the x- and
|
||||||
|
y-coordinates of the current Vector */
|
||||||
|
void Vector::operator+=(const float &d) {
|
||||||
|
x += d;
|
||||||
|
y += d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the difference-assignment operator for
|
||||||
|
Vectors. It returns the difference between the current
|
||||||
|
Vector and the given Vector by subtracting their x- and
|
||||||
|
y-coordinates. This changes the current Vector itself.
|
||||||
|
|
||||||
|
\param p a Vector which has to be subtracted from the current
|
||||||
|
Vector */
|
||||||
|
void Vector::operator-=(const Vector &p) {
|
||||||
|
x -= p.x;
|
||||||
|
y -= p.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the difference-assignment operator for
|
||||||
|
subtracting a given float value from a Vector. The float
|
||||||
|
value is subtracted from both the x- and y-coordinates of the
|
||||||
|
current Vector. This changes the current Vector itself.
|
||||||
|
|
||||||
|
\param d a float value which has to be subtracted from both the x- and
|
||||||
|
y-coordinates of the current Vector */
|
||||||
|
void Vector::operator-=(const float &d) {
|
||||||
|
x -= d;
|
||||||
|
y -= d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the multiplication-assignment operator for
|
||||||
|
Vectors. It returns the product of the current Vector
|
||||||
|
and the given Vector by multiplying their x- and
|
||||||
|
y-coordinates. This changes the current Vector itself.
|
||||||
|
|
||||||
|
\param p a Vector by which the current Vector has to be
|
||||||
|
multiplied */
|
||||||
|
void Vector::operator*=(const Vector &p) {
|
||||||
|
x *= p.x;
|
||||||
|
y *= p.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the multiplication-assignment operator for
|
||||||
|
multiplying a Vector by a given float value. Both the x- and
|
||||||
|
y-coordinates of the current Vector are multiplied by this
|
||||||
|
value. This changes the current Vector itself.
|
||||||
|
|
||||||
|
\param d a float value by which both the x- and y-coordinates of the
|
||||||
|
current Vector have to be multiplied */
|
||||||
|
void Vector::operator*=(const float &d) {
|
||||||
|
x *= d;
|
||||||
|
y *= d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the division-assignment operator for
|
||||||
|
Vectors. It returns the quotient of the current Vector
|
||||||
|
and the given Vector by dividing their x- and
|
||||||
|
y-coordinates. This changes the current Vector itself.
|
||||||
|
|
||||||
|
\param p a Vector by which the current Vector is divided */
|
||||||
|
void Vector::operator/=(const Vector &p) {
|
||||||
|
x /= p.x;
|
||||||
|
y /= p.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the division-assignment operator for
|
||||||
|
dividing a Vector by a given float value. Both the x- and
|
||||||
|
y-coordinates of the current Vector are divided by this
|
||||||
|
value. This changes the current Vector itself.
|
||||||
|
|
||||||
|
\param d a float value by which both the x- and y-coordinates of the
|
||||||
|
current Vector have to be divided */
|
||||||
|
void Vector::operator/=(const float &d) {
|
||||||
|
x /= d;
|
||||||
|
y /= d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the inequality operator for Vectors. It
|
||||||
|
determines whether the current Vector is unequal to the given
|
||||||
|
Vector by comparing their x- and y-coordinates.
|
||||||
|
|
||||||
|
\param p a Vector
|
||||||
|
\return true when either the x- or y-coordinates of the given Vector
|
||||||
|
and the current Vector are different; false otherwise */
|
||||||
|
bool Vector::operator!=(const Vector &p) {
|
||||||
|
return ( (x != p.x) || (y != p.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the inequality operator for comparing a
|
||||||
|
Vector to a float value. It determines whether either the x-
|
||||||
|
or y-coordinate of the current Vector is unequal to the given
|
||||||
|
float value.
|
||||||
|
|
||||||
|
\param d a float value with which both the x- and y-coordinates of the
|
||||||
|
current Vector have to be compared.
|
||||||
|
\return true when either the x- or y-coordinate of the current Vector
|
||||||
|
is unequal to the given float value; false otherwise */
|
||||||
|
bool Vector::operator!=(const float &d) {
|
||||||
|
return ( (x != d) || (y != d));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the equality operator for Vectors. It
|
||||||
|
determines whether the current Vector is equal to the given
|
||||||
|
Vector by comparing their x- and y-coordinates.
|
||||||
|
|
||||||
|
\param p a Vector
|
||||||
|
\return true when both the x- and y-coordinates of the given
|
||||||
|
Vector and the current Vector are equal; false
|
||||||
|
otherwise */
|
||||||
|
bool Vector::operator==(const Vector &p) {
|
||||||
|
return ( (x == p.x) && (y == p.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Overloaded version of the equality operator for comparing a
|
||||||
|
Vector to a float value. It determines whether both the x-
|
||||||
|
and y-coordinates of the current Vector are equal to the
|
||||||
|
given float value.
|
||||||
|
|
||||||
|
\param d a float value with which both the x- and y-coordinates of the
|
||||||
|
current Vector have to be compared.
|
||||||
|
\return true when both the x- and y-coordinates of the current Vector
|
||||||
|
are equal to the given float value; false otherwise */
|
||||||
|
bool Vector::operator==(const float &d) {
|
||||||
|
return ( (x == d) && (y == d));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! This method determines the distance between the current
|
||||||
|
Vector and a given Vector. This is equal to the
|
||||||
|
magnitude (length) of the vector connecting the two positions
|
||||||
|
which is the difference vector between them.
|
||||||
|
|
||||||
|
\param p a Vecposition
|
||||||
|
\return the distance between the current Vector and the given
|
||||||
|
Vector */
|
||||||
|
float Vector::getDistanceTo(const Vector p) {
|
||||||
|
return ( (*this -p).length());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! This method determines the magnitude (length) of the vector
|
||||||
|
corresponding with the current Vector using the formula of
|
||||||
|
Pythagoras.
|
||||||
|
|
||||||
|
\return the length of the vector corresponding with the current
|
||||||
|
Vector */
|
||||||
|
float Vector::length() const {
|
||||||
|
return ( sqrt(x * x + y * y));
|
||||||
|
}
|
||||||
|
|
||||||
|
float Vector::crossProduct(const Vector p) {
|
||||||
|
return this->x*p.y - this->y*p.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This methods returns the inner product of this vector with another
|
||||||
|
*
|
||||||
|
* @param other the other vector
|
||||||
|
* @return inner product
|
||||||
|
*/
|
||||||
|
float Vector::innerProduct(const Vector& other) const {
|
||||||
|
return x * other.x + y * other.y;
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
#ifndef GEOMETRY_H
|
||||||
|
#define GEOMETRY_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define EPSILON 1e-10
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Useful functions to operate with angles in degrees
|
||||||
|
*/
|
||||||
|
float Cos(float x);
|
||||||
|
float Sin(float x);
|
||||||
|
float ATan2(float x, float y);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Vector
|
||||||
|
*
|
||||||
|
* @brief This class represents a position in the 2d space
|
||||||
|
*
|
||||||
|
* A position is represented by a x-axis coordinate and a
|
||||||
|
* y-axis coordinate or in polar coordinates (r, phi)
|
||||||
|
*
|
||||||
|
* @author Hugo Picado (hugopicado@ua.pt)
|
||||||
|
* @author Nuno Almeida (nuno.alm@ua.pt)
|
||||||
|
* Adapted - Miguel Abreu
|
||||||
|
*/
|
||||||
|
class Vector {
|
||||||
|
public:
|
||||||
|
Vector(float vx = 0, float vy = 0);
|
||||||
|
|
||||||
|
// overloaded arithmetic operators
|
||||||
|
Vector operator-() const;
|
||||||
|
Vector operator+(const float &d) const;
|
||||||
|
Vector operator+(const Vector &p) const;
|
||||||
|
Vector operator-(const float &d) const;
|
||||||
|
Vector operator-(const Vector &p) const;
|
||||||
|
Vector operator*(const float &d) const;
|
||||||
|
Vector operator*(const Vector &p) const;
|
||||||
|
Vector operator/(const float &d) const;
|
||||||
|
Vector operator/(const Vector &p) const;
|
||||||
|
void operator=(const float &d);
|
||||||
|
void operator+=(const Vector &p);
|
||||||
|
void operator+=(const float &d);
|
||||||
|
void operator-=(const Vector &p);
|
||||||
|
void operator-=(const float &d);
|
||||||
|
void operator*=(const Vector &p);
|
||||||
|
void operator*=(const float &d);
|
||||||
|
void operator/=(const Vector &p);
|
||||||
|
void operator/=(const float &d);
|
||||||
|
bool operator!=(const Vector &p);
|
||||||
|
bool operator!=(const float &d);
|
||||||
|
bool operator==(const Vector &p);
|
||||||
|
bool operator==(const float &d);
|
||||||
|
|
||||||
|
float getDistanceTo(const Vector p);
|
||||||
|
float crossProduct(const Vector p);
|
||||||
|
float length() const;
|
||||||
|
float innerProduct(const Vector &p) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // GEOMETRY_H
|
@ -0,0 +1,232 @@
|
|||||||
|
#include "Line6f.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Line6f::Line6f(const Vector3f &polar_s, const Vector3f &polar_e) :
|
||||||
|
startp(polar_s), endp(polar_e), startc(polar_s.toCartesian()), endc(polar_e.toCartesian()), length(startc.dist(endc)) {};
|
||||||
|
|
||||||
|
Line6f::Line6f(const Vector3f &cart_s, const Vector3f &cart_e, float length) :
|
||||||
|
startp(cart_s.toPolar()), endp(cart_e.toPolar()), startc(cart_s), endc(cart_e), length(length) {};
|
||||||
|
|
||||||
|
Line6f::Line6f(const Line6f &obj) :
|
||||||
|
startp(obj.startp), endp(obj.endp), startc(obj.startc), endc(obj.endc), length(obj.length) {};
|
||||||
|
|
||||||
|
Vector3f Line6f::linePointClosestToCartPoint(const Vector3f &cp) const{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equation of this line: (we want to find t, such that (cp-p) is perpendicular to this line)
|
||||||
|
* p = startc + t*(endc - startc)
|
||||||
|
*
|
||||||
|
* Let vecp=(cp-start) and vecline=(end-start)
|
||||||
|
* Scalar projection of vecp in the direction of vecline:
|
||||||
|
* sp = vecp.vecline/|vecline|
|
||||||
|
* Find the ratio t by dividing the scalar projection by the length of vecline:
|
||||||
|
* t = sp / |vecline|
|
||||||
|
* So the final expression becomes:
|
||||||
|
* t = vecp.vecline/|vecline|^2
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
Vector3f lvec(endc-startc); //this line's vector
|
||||||
|
float t = (cp-startc).innerProduct(lvec) / (length*length);
|
||||||
|
|
||||||
|
return startc + lvec*t;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Line6f::linePointClosestToPolarPoint(const Vector3f &pp) const{
|
||||||
|
return linePointClosestToCartPoint(pp.toCartesian());
|
||||||
|
}
|
||||||
|
|
||||||
|
float Line6f::lineDistToCartPoint(const Vector3f &cp) const{
|
||||||
|
return ((cp-startc).crossProduct(cp-endc)).length() / (endc-startc).length();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Line6f::lineDistToPolarPoint(const Vector3f &pp) const{
|
||||||
|
return lineDistToCartPoint(pp.toCartesian());
|
||||||
|
}
|
||||||
|
|
||||||
|
//source: http://geomalgorithms.com/a07-_distance.html#dist3D_Line_to_Line()
|
||||||
|
float Line6f::lineDistToLine(const Line6f &l) const{
|
||||||
|
Vector3f u = endc - startc;
|
||||||
|
Vector3f v = l.endc - l.startc;
|
||||||
|
Vector3f w = startc - l.startc;
|
||||||
|
float a = u.innerProduct(u); // always >= 0
|
||||||
|
float b = u.innerProduct(v);
|
||||||
|
float c = v.innerProduct(v); // always >= 0
|
||||||
|
float d = u.innerProduct(w);
|
||||||
|
float e = v.innerProduct(w);
|
||||||
|
float D = a*c - b*b; // always >= 0
|
||||||
|
float sc, tc;
|
||||||
|
|
||||||
|
// compute the line parameters of the two closest points
|
||||||
|
if (D < 1e-8f) { // the lines are almost parallel
|
||||||
|
sc = 0.0;
|
||||||
|
tc = (b>c ? d/b : e/c); // use the largest denominator
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sc = (b*e - c*d) / D;
|
||||||
|
tc = (a*e - b*d) / D;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the difference of the two closest points
|
||||||
|
Vector3f dP = w + (u * sc) - (v * tc); // = L1(sc) - L2(tc)
|
||||||
|
|
||||||
|
return dP.length(); // return the closest distance
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Line6f::segmentPointClosestToCartPoint(const Vector3f &cp) const{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equation of this line: (we want to find t, such that (cp-p) is perpendicular to this line)
|
||||||
|
* p = startc + t*(endc - startc)
|
||||||
|
*
|
||||||
|
* Let vecp=(cp-start) and vecline=(end-start)
|
||||||
|
* Scalar projection of vecp in the direction of vecline:
|
||||||
|
* sp = vecp.vecline/|vecline|
|
||||||
|
* Find the ratio t by dividing the scalar projection by the length of vecline:
|
||||||
|
* t = sp / |vecline|
|
||||||
|
* So the final expression becomes:
|
||||||
|
* t = vecp.vecline/|vecline|^2
|
||||||
|
* Since this version requires that p belongs to the line segment, there is an additional restriction:
|
||||||
|
* 0 < t < 1
|
||||||
|
*/
|
||||||
|
|
||||||
|
Vector3f lvec(endc-startc); //this line's vector
|
||||||
|
float t = (cp-startc).innerProduct(lvec) / (length*length);
|
||||||
|
|
||||||
|
if(t<0) t=0;
|
||||||
|
else if(t>1) t=1;
|
||||||
|
|
||||||
|
return startc + lvec*t;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Line6f::segmentPointClosestToPolarPoint(const Vector3f &pp) const{
|
||||||
|
return segmentPointClosestToCartPoint(pp.toCartesian());
|
||||||
|
}
|
||||||
|
|
||||||
|
float Line6f::segmentDistToCartPoint(const Vector3f &cp) const{
|
||||||
|
|
||||||
|
Vector3f v = endc - startc; //line segment vector
|
||||||
|
Vector3f w1 = cp - startc;
|
||||||
|
|
||||||
|
if ( w1.innerProduct(v) <= 0) return w1.length();
|
||||||
|
|
||||||
|
Vector3f w2 = cp - endc;
|
||||||
|
|
||||||
|
if ( w2.innerProduct(v) >= 0) return w2.length();
|
||||||
|
|
||||||
|
return v.crossProduct(w1).length() / this->length;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float Line6f::segmentDistToPolarPoint(const Vector3f &pp) const{
|
||||||
|
return segmentDistToCartPoint(pp.toCartesian());
|
||||||
|
}
|
||||||
|
|
||||||
|
//source: http://geomalgorithms.com/a07-_distance.html#dist3D_Segment_to_Segment()
|
||||||
|
float Line6f::segmentDistToSegment(const Line6f &other) const{
|
||||||
|
|
||||||
|
Vector3f u = endc - startc;
|
||||||
|
Vector3f v = other.endc - other.startc;
|
||||||
|
Vector3f w = startc - other.startc;
|
||||||
|
float a = u.innerProduct(u); // always >= 0
|
||||||
|
float b = u.innerProduct(v);
|
||||||
|
float c = v.innerProduct(v); // always >= 0
|
||||||
|
float d = u.innerProduct(w);
|
||||||
|
float e = v.innerProduct(w);
|
||||||
|
float D = a*c - b*b; // always >= 0
|
||||||
|
float sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0
|
||||||
|
float tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0
|
||||||
|
|
||||||
|
// compute the line parameters of the two closest points
|
||||||
|
if (D < 1e-8f) { // the lines are almost parallel
|
||||||
|
sN = 0.0; // force using point start on segment S1
|
||||||
|
sD = 1.0; // to prevent possible division by 0.0 later
|
||||||
|
tN = e;
|
||||||
|
tD = c;
|
||||||
|
}
|
||||||
|
else { // get the closest points on the infinite lines
|
||||||
|
sN = (b*e - c*d);
|
||||||
|
tN = (a*e - b*d);
|
||||||
|
if (sN < 0.0) { // sc < 0 => the s=0 edge is visible
|
||||||
|
sN = 0.0;
|
||||||
|
tN = e;
|
||||||
|
tD = c;
|
||||||
|
}
|
||||||
|
else if (sN > sD) { // sc > 1 => the s=1 edge is visible
|
||||||
|
sN = sD;
|
||||||
|
tN = e + b;
|
||||||
|
tD = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
|
||||||
|
tN = 0.0;
|
||||||
|
// recompute sc for this edge
|
||||||
|
if (-d < 0.0)
|
||||||
|
sN = 0.0;
|
||||||
|
else if (-d > a)
|
||||||
|
sN = sD;
|
||||||
|
else {
|
||||||
|
sN = -d;
|
||||||
|
sD = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tN > tD) { // tc > 1 => the t=1 edge is visible
|
||||||
|
tN = tD;
|
||||||
|
// recompute sc for this edge
|
||||||
|
if ((-d + b) < 0.0)
|
||||||
|
sN = 0;
|
||||||
|
else if ((-d + b) > a)
|
||||||
|
sN = sD;
|
||||||
|
else {
|
||||||
|
sN = (-d + b);
|
||||||
|
sD = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// finally do the division to get sc and tc
|
||||||
|
sc = (abs(sN) < 1e-8f ? 0.0 : sN / sD);
|
||||||
|
tc = (abs(tN) < 1e-8f ? 0.0 : tN / tD);
|
||||||
|
|
||||||
|
// get the difference of the two closest points
|
||||||
|
Vector3f dP = w + (u * sc) - (v * tc); // = S1(sc) - S2(tc)
|
||||||
|
|
||||||
|
return dP.length(); // return the closest distance
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vector3f Line6f::midPointCart() const{
|
||||||
|
return (startc+endc)/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Line6f::midPointPolar() const{
|
||||||
|
return midPointCart().toPolar();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Line6f::operator==(const Line6f& other) const {
|
||||||
|
return (startp == other.startp) && (endp == other.endp);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector3f &Line6f::get_polar_pt(const int index) const{
|
||||||
|
if(index==0) return startp;
|
||||||
|
return endp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector3f &Line6f::get_cart_pt(const int index) const{
|
||||||
|
if(index==0) return startc;
|
||||||
|
return endc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Line6f::get_polar_vector() const{
|
||||||
|
return endp-startp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Line6f::get_cart_vector() const{
|
||||||
|
return endc-startc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,185 @@
|
|||||||
|
/**
|
||||||
|
* FILENAME: Line6f
|
||||||
|
* DESCRIPTION: Simple line (segment) class
|
||||||
|
* AUTHOR: Miguel Abreu (m.abreu@fe.up.pt)
|
||||||
|
* DATE: 2021
|
||||||
|
*
|
||||||
|
* Immutable Class for:
|
||||||
|
* 3D Line composed of 2 Vector3f points
|
||||||
|
* Optimized for:
|
||||||
|
* - regular access to cartesian coordinates
|
||||||
|
* - regular access to line segment length
|
||||||
|
**/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "Vector3f.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Line6f {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polar start coordinate
|
||||||
|
*/
|
||||||
|
const Vector3f startp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polar end coordinate
|
||||||
|
*/
|
||||||
|
const Vector3f endp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cartesian start coordinate
|
||||||
|
*/
|
||||||
|
const Vector3f startc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cartesian end coordinate
|
||||||
|
*/
|
||||||
|
const Vector3f endc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of line segment limited by (start,end)
|
||||||
|
*/
|
||||||
|
const float length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param polar_s polar start point
|
||||||
|
* @param polar_e polar end point
|
||||||
|
*/
|
||||||
|
Line6f(const Vector3f &polar_s, const Vector3f &polar_e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param cart_s cartesian start point
|
||||||
|
* @param cart_e cartesian end point
|
||||||
|
* @param length line segment length
|
||||||
|
*/
|
||||||
|
Line6f(const Vector3f &cart_s, const Vector3f &cart_e, float length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy Constructor
|
||||||
|
* @param obj another line
|
||||||
|
*/
|
||||||
|
Line6f(const Line6f &obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find point in line defined by (start,end) closest to given point
|
||||||
|
* @param cp cartesian point
|
||||||
|
* @return point in this infinite line closest to given point
|
||||||
|
*/
|
||||||
|
Vector3f linePointClosestToCartPoint(const Vector3f &cp) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find point in line defined by (start,end) closest to given point
|
||||||
|
* @param pp polar point
|
||||||
|
* @return point in this infinite line closest to given point
|
||||||
|
*/
|
||||||
|
Vector3f linePointClosestToPolarPoint(const Vector3f &pp) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find distance between line defined by (start,end) and given point
|
||||||
|
* @param cp cartesian point
|
||||||
|
* @return distance between line defined by (start,end) and given point
|
||||||
|
*/
|
||||||
|
float lineDistToCartPoint(const Vector3f &cp) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find distance between line defined by (start,end) and given point
|
||||||
|
* @param pp polar point
|
||||||
|
* @return distance between line defined by (start,end) and given point
|
||||||
|
*/
|
||||||
|
float lineDistToPolarPoint(const Vector3f &pp) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find distance between line defined by (start,end) and given line
|
||||||
|
* @param l line
|
||||||
|
* @return distance between line defined by (start,end) and given line
|
||||||
|
*/
|
||||||
|
float lineDistToLine(const Line6f &l) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find point in line defined by (start,end) closest to given point
|
||||||
|
* @param cp cartesian point
|
||||||
|
* @return point in this infinite line closest to given point
|
||||||
|
*/
|
||||||
|
Vector3f segmentPointClosestToCartPoint(const Vector3f &cp) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find point in line defined by (start,end) closest to given point
|
||||||
|
* @param pp polar point
|
||||||
|
* @return point in this infinite line closest to given point
|
||||||
|
*/
|
||||||
|
Vector3f segmentPointClosestToPolarPoint(const Vector3f &pp) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find distance between line segment limited by (start,end) and given point
|
||||||
|
* @param cp cartesian point
|
||||||
|
* @return distance between line segment limited by (start,end) and given point
|
||||||
|
*/
|
||||||
|
float segmentDistToCartPoint(const Vector3f &cp) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find distance between line segment limited by (start,end) and given point
|
||||||
|
* @param pp polar point
|
||||||
|
* @return distance between line segment limited by (start,end) and given point
|
||||||
|
*/
|
||||||
|
float segmentDistToPolarPoint(const Vector3f &pp) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find distance between line segment limited by (start,end) and given line segment
|
||||||
|
* @param other line segment
|
||||||
|
* @return distance between line segment limited by (start,end) and given line segment
|
||||||
|
*/
|
||||||
|
float segmentDistToSegment(const Line6f &other) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find midpoint of line segment limited by (start,end)
|
||||||
|
* @return cartesian midpoint of line segment limited by (start,end)
|
||||||
|
*/
|
||||||
|
Vector3f midPointCart() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find midpoint of line segment limited by (start,end)
|
||||||
|
* @return polar midpoint of line segment limited by (start,end)
|
||||||
|
*/
|
||||||
|
Vector3f midPointPolar() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operator ==
|
||||||
|
* @param other line
|
||||||
|
* @return true if both lines are the same
|
||||||
|
*/
|
||||||
|
bool operator==(const Line6f& other) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get polar line ending by index
|
||||||
|
* @param index (0)->start or (1)->end
|
||||||
|
* @return polar line ending according to given index
|
||||||
|
*/
|
||||||
|
const Vector3f &get_polar_pt(const int index) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cartesian line ending by index
|
||||||
|
* @param index (0)->start or (1)->end
|
||||||
|
* @return cartesian line ending according to given index
|
||||||
|
*/
|
||||||
|
const Vector3f &get_cart_pt(const int index) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get polar vector (end-start)
|
||||||
|
* @return polar vector (end-start)
|
||||||
|
*/
|
||||||
|
Vector3f get_polar_vector() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cartesian vector (end-start)
|
||||||
|
* @return cartesian vector (end-start)
|
||||||
|
*/
|
||||||
|
Vector3f get_cart_vector() const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,379 @@
|
|||||||
|
/**
|
||||||
|
* FILENAME: LocalizerV2
|
||||||
|
* DESCRIPTION: main 6D localization algorithm
|
||||||
|
* AUTHOR: Miguel Abreu (m.abreu@fe.up.pt)
|
||||||
|
* DATE: 2021
|
||||||
|
*
|
||||||
|
* ===================================================================================
|
||||||
|
* WORKFLOW
|
||||||
|
* ===================================================================================
|
||||||
|
*
|
||||||
|
* References can be obtained from:
|
||||||
|
* - landmarks (which are identified by the server and can be corner flags or goal posts)
|
||||||
|
* - line segments (which are always on the ground) (hereinafter referred to as lines)
|
||||||
|
* - feet contact points (we are assuming the contact point is on the ground plane)
|
||||||
|
*
|
||||||
|
* WARNING:
|
||||||
|
* When partial information is available, it is used to change the head position (e.g. translation in x/y/z may be updated
|
||||||
|
* without visual information). However, the transformation matrix (including the translation) are not changed, because
|
||||||
|
* this would affect the perceived position of previously seen objects. For this reason, the worldstate should not rely
|
||||||
|
* on the head position to convert relative to absolute coordinates. Instead, it should only use transformation matrix,
|
||||||
|
* or internal conversion methods. The head position can still be used for other purposes, such as machine learning.
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------------------------------------
|
||||||
|
* 0. Perform basic tasks and checks
|
||||||
|
*
|
||||||
|
* Excluded scenarios:
|
||||||
|
* 0 landmarks & <2 lines - it's a requirement from step 1
|
||||||
|
* 0 lines - step 1 allows this if there are 3 ground refs but the only marginally common
|
||||||
|
* option would be 2 feet and a corner (which is undesirable)
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------------------------------------
|
||||||
|
* 1. Find the Z axis orientation vector (and add it to the preliminary transformation matrix):
|
||||||
|
* 1.1. there are >= 3 noncollinear ground references (z=0)
|
||||||
|
* ASSUMPTION: the ground references are not collinear. Why?
|
||||||
|
* If we see 2 lines their endpoints are never collinear.
|
||||||
|
* If we see one and we are on top of it, the feet contact points can cause collinearity but it is very rare.
|
||||||
|
* SOLUTION: Find the best fitting ground plane's normal vector using Singular Value Decomposition
|
||||||
|
*
|
||||||
|
* 1.2. there are < 3 noncollinear ground references (z=0)
|
||||||
|
*
|
||||||
|
* Possible combinations:
|
||||||
|
* If there is 1 corner flag, either we have >= 3 ground references, or it is impossible.
|
||||||
|
* So, below, we assume there are 0 corner flags.
|
||||||
|
*
|
||||||
|
* | 0 lines + 0/1/2 feet | 1 line + 0 feet |
|
||||||
|
* -------------|------------------------|------------------|
|
||||||
|
* 0 goalposts | ----- | ----- | (Only 1 line: there is no way of identifying it)
|
||||||
|
* 1 goalpost | ----- | A,C | (1 goalpost and 0/1/2 feet: infinite solutions)
|
||||||
|
* 2 goalposts | * | B,C |
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* If it sees 1 or 2 goalposts and only 1 line, we assume for A & B that it is the endline (aka goal line)
|
||||||
|
*
|
||||||
|
* SOLUTIONS:
|
||||||
|
* 1.2.A. IF IT IS THE GOALLINE. Find the line's nearest point (p) to the goalpost (g), Zvec = (g-p) / |Zvec|
|
||||||
|
* 1.2.B. IF IT IS THE GOALLINE. Find the line's nearest point (p) to the goalposts (g1,g2) midpoint (m), Zvec = (m-p) / |Zvec|
|
||||||
|
* (This solution is more accurate than using only 1 goalpost. Even m is more accurate, on average, than g1 or g2.)
|
||||||
|
* 1.2.C. IF IT IS NOT THE GOALLINE. There are 3 options:
|
||||||
|
* I - There are 2 goalposts (crossbar line) and an orthogonal line:
|
||||||
|
* Zvec = crossbar x line (or) line x crossbar (depending of the direction of both vectors)
|
||||||
|
* II - Other situation if the z translation coordinate was externally provided through machine learning:
|
||||||
|
* Find any horizontal line (e.g. line between 2 goalposts, or ground line)
|
||||||
|
* Let M be any mark with known absolute z, and let Z be the externally provided z coordinate:
|
||||||
|
* Find Zvec such that (HorLine.Zvec=0) and (Zvec.Mrel=Mabsz-Z)
|
||||||
|
* III - If z was not provided:
|
||||||
|
* Skip to last step.
|
||||||
|
* 1.2.*. This scenario was tested and it is not valid. In certain body positions there are two solutions, and even though
|
||||||
|
* one is correct and generally yields lower error, the other one is a local optimum outside the field. One could
|
||||||
|
* exclude the out-of-field solution with some mildly expensive modifications to the optimization's error function,
|
||||||
|
* but the out-of-field scenario is not unrealistic, so this is not the way. Adding an external z source could help
|
||||||
|
* increasing the error of the wrong local optimum, but it may not be enough. Another shortcoming of this scenario is
|
||||||
|
* when we see the goalposts from the opposite goal, creating large visual error.
|
||||||
|
*
|
||||||
|
* 1.3. impossible / not implemented: in this case skip to last step
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------------------------------------
|
||||||
|
* 2. Compute z:
|
||||||
|
*
|
||||||
|
* Here's what we know about the transformation matrix so far:
|
||||||
|
* | - | - | - | - |
|
||||||
|
* | - | - | - | - |
|
||||||
|
* | zx | zy | zz | ? | We want to know the translation in z
|
||||||
|
* | 0 | 0 | 0 | 1 |
|
||||||
|
*
|
||||||
|
* Given a random point (p) with known relative coordinates and known absolute z coordinate,
|
||||||
|
* we can find the translation in z:
|
||||||
|
* p.relx * zx + p.rely * zy + p.relz * zz + ? = p.absz
|
||||||
|
*
|
||||||
|
* If we do this for every point, we can then average z
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------------------------------------
|
||||||
|
* 3. Compute a rough estimate for entire transformation (2 first rows):
|
||||||
|
*
|
||||||
|
* Solution quality for possible combinations:
|
||||||
|
*
|
||||||
|
* short line (length < 0.5m) *hard to detect orientation, *generated displacement error is insuficient for optimization
|
||||||
|
* long line (length >= 0.5m)
|
||||||
|
*
|
||||||
|
* | 0 landmarks | 1 goalpost | 1 corner | >= 2 landmarks |
|
||||||
|
* -----------------|---------------|--------------|------------|----------------|
|
||||||
|
* 0 long lines | --- | --- | --- | A |
|
||||||
|
* 1 long line | --- | B+ | B | A |
|
||||||
|
* 2 long lines | B | B+ | B++ | A |
|
||||||
|
*
|
||||||
|
* SOLUTIONS:
|
||||||
|
* A - the solution is unique
|
||||||
|
* STEPS:
|
||||||
|
* - Compute the X-axis and Y-axis orientation from 2 landmarks
|
||||||
|
* - Average the translation for every visible landmark
|
||||||
|
* - Fine-tune XY translation/rotation
|
||||||
|
* B - there is more than 1 solution, so we use the last known position as reference
|
||||||
|
* Minimum Requirements:
|
||||||
|
* - longest line must be >1.6m so that we can extract the orientation (hor/ver) while not being mistaken for a ring line
|
||||||
|
* - there is exactly 1 plausible solution
|
||||||
|
* Notes:
|
||||||
|
* B+ (the solution is rarely unique)
|
||||||
|
* B++ (the solution is virtually unique but cannot be computed with the algorithm for A scenarios)
|
||||||
|
* STEPS:
|
||||||
|
* - Find 4 possible orientations based on the longest line (which should be either aligned with X or Y)
|
||||||
|
* - Determine reasonable initial translation:
|
||||||
|
* - If the agent sees 1 landmark: compute XY translation for each of the 4 orientations based on that 1 landmark
|
||||||
|
* - If the agent sees 0 landmarks: use last known position
|
||||||
|
* Note: Why not use always last known position if that is a criterion in the end?
|
||||||
|
* Because in this stage it would only delay the optimization.
|
||||||
|
* - Optimize the X/Y translation for every possible orientation
|
||||||
|
* - Perform quality assessment
|
||||||
|
* Plausible solution if:
|
||||||
|
* - Optimization converged to local minimum
|
||||||
|
* - Distance to last known position <50cm (applicable if no visible landmarks)
|
||||||
|
* - Mapping error <0.12m/point
|
||||||
|
* - Given the agent's FOV, inverse mapping error <0.2m/point (disabled in V2)
|
||||||
|
* NOTE: If there is 1 landmark, plausibility is defined by mapping errors, not distance to last known pos. So if
|
||||||
|
* one guess has the only acceptable mapping error, but is the farthest from previous position, it is still chosen.
|
||||||
|
* However, if >1 guess has acceptable mapping error, the 0.5m threshold is used to eliminate candidates.
|
||||||
|
* Likely if:
|
||||||
|
* - Plausible
|
||||||
|
* - Distance to last known position <30cm (applicable if no visible landmarks)
|
||||||
|
* - Mapping error <0.06m/point
|
||||||
|
* - Given the agent's FOV, inverse mapping error <0.1m/point (not currently active)
|
||||||
|
* - Choose likely solution if all others are not even plausible
|
||||||
|
* - Fine-tune XY translation/rotation
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------------------------------------
|
||||||
|
* 4. Identify visible elements and perform 2nd fine tune based on distance probabilites
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------------------------------------
|
||||||
|
* Last step. Analyze preliminary transformation matrix to update final matrices
|
||||||
|
*
|
||||||
|
* For the reasons stated in the beginning (see warning), if the preliminary matrix was not entirely set, the
|
||||||
|
* actual transformation matrix will not be changed. But the head position will always reflect the latest changes.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ===================================================================================
|
||||||
|
* LOCALIZATION BASED ON PROBABILITY DENSITIES
|
||||||
|
* ===================================================================================
|
||||||
|
* ================================PROBABILITY DENSITY================================
|
||||||
|
*
|
||||||
|
* For 1 distance measurement from RCSSSERVER3D:
|
||||||
|
*
|
||||||
|
* Error E = d/100 * A~N(0,0.0965^2) + B~U(-0.005,0.005)
|
||||||
|
* PDF[d/100 * A](w) = PDF[N(0,(d/100 * 0.0965)^2)](w)
|
||||||
|
* PDF[E](w) = PDF[N(0,(d/100 * 0.0965)^2)](w) convoluted with PDF[U(-0.005,0.005)](w)
|
||||||
|
*
|
||||||
|
* where d is the distance from a given [p]oint (px,py,pz) to the [o]bject (ox,oy,oz)
|
||||||
|
* and w is the [m]easurement error: w = d-measurement = sqrt((px-ox)^2+(py-oy)^2+(pz-oz)^2) - measurement
|
||||||
|
*
|
||||||
|
* PDF[E](w) -> PDF[E](p,o,m)
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* For n independent measurements:
|
||||||
|
*
|
||||||
|
* PDF[En](p,on,mn) = PDF[E1](p,o1,m1) * PDF[E2](p,o2,m2) * PDF[E3](p,o3,m3) * ...
|
||||||
|
* ---------------------------------------------------------------
|
||||||
|
* Adding z estimation:
|
||||||
|
*
|
||||||
|
* PDF[zE](wz) = PDF[N(mean,std^2)](wz)
|
||||||
|
* where wz is the zError = estz - pz
|
||||||
|
*
|
||||||
|
* PDF[zE](wz) -> PDF[zE](pz,estz)
|
||||||
|
* PDF[En](p,on,mn,estz) = PDF[En](p,on,mn) * PDF[zE](pz,estz)
|
||||||
|
* ===================================================================================
|
||||||
|
* =====================================GRADIENT======================================
|
||||||
|
*
|
||||||
|
* Grad(PDF[En](p,on,mn,estz)) wrt p = Grad( PDF[E1](p,o1,m1) * ... * PDF[E2](p,on,mn) * PDF[zE](pz,estz)) wrt {px,py,pz}
|
||||||
|
*
|
||||||
|
* Generalizing the product rule for n factors, we have:
|
||||||
|
*
|
||||||
|
* Grad(PDF[En](p,on,mn)) wrt p = sum(gradPDF[Ei] / PDF[Ei]) * prod(PDF[Ei])
|
||||||
|
* Grad(PDF[En](p,on,mn)) wrt p = sum(gradPDF[Ei] / PDF[Ei]) * PDF[En](p,on,mn)
|
||||||
|
*
|
||||||
|
* note that: gradPDF[zE](pz,estz) wrt {px,py,pz} = {0,0,d/d_pz}
|
||||||
|
* ===================================================================================
|
||||||
|
* */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "Singleton.h"
|
||||||
|
#include "Field.h"
|
||||||
|
#include "Matrix4D.h"
|
||||||
|
#include "FieldNoise.h"
|
||||||
|
|
||||||
|
#include <gsl/gsl_multifit.h> //Linear least-squares fitting
|
||||||
|
#include <gsl/gsl_linalg.h> //Singular value decomposition
|
||||||
|
#include <gsl/gsl_multimin.h> //Multidimensional minimization
|
||||||
|
|
||||||
|
class LocalizerV2 {
|
||||||
|
friend class Singleton<LocalizerV2>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute 3D position and 3D orientation
|
||||||
|
* sets "is_uptodate" to true if there is new information available (rotation+translation)
|
||||||
|
* If no new information is available, the last known position is provided
|
||||||
|
*/
|
||||||
|
void run();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print report (average errors + solution analysis)
|
||||||
|
*/
|
||||||
|
void print_report() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformation matrices
|
||||||
|
* They are initialized as 4x4 identity matrices
|
||||||
|
*/
|
||||||
|
const Matrix4D &headTofieldTransform = final_headTofieldTransform; // rotation + translation
|
||||||
|
const Matrix4D &headTofieldRotate = final_headTofieldRotate; // rotation
|
||||||
|
const Matrix4D &fieldToheadTransform = final_fieldToheadTransform; // rotation + translation
|
||||||
|
const Matrix4D &fieldToheadRotate = final_fieldToheadRotate; // rotation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Head position
|
||||||
|
* translation part of headTofieldTransform
|
||||||
|
*/
|
||||||
|
const Vector3f &head_position = final_translation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if head_position and the transformation matrices are up to date
|
||||||
|
* (false if this is not a visual step, or not enough elements are visible)
|
||||||
|
*/
|
||||||
|
const bool &is_uptodate = _is_uptodate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of simulation steps since last update (see is_uptodate)
|
||||||
|
* If (is_uptodate==true) then "steps_since_last_update" is zero
|
||||||
|
*/
|
||||||
|
const unsigned int &steps_since_last_update = _steps_since_last_update;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Head z coordinate
|
||||||
|
* This variable is often equivalent to head_position.z, but sometimes it differs.
|
||||||
|
* There are situations in which the rotation and translation cannot be computed,
|
||||||
|
* but the z-coordinate can still be found through vision.
|
||||||
|
* It should be used in applications which rely on z as an independent coordinate,
|
||||||
|
* such as detecting if the robot has fallen, or as machine learning observations.
|
||||||
|
* It should not be used for 3D transformations.
|
||||||
|
*/
|
||||||
|
const float &head_z = final_z;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since head_z can be computed in situations where self-location is impossible,
|
||||||
|
* this variable is set to True when head_z is up to date
|
||||||
|
*/
|
||||||
|
const bool &is_head_z_uptodate = _is_head_z_uptodate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform relative to absolute coordinates using headTofieldTransform
|
||||||
|
* @return absolute coordinates
|
||||||
|
*/
|
||||||
|
Vector3f relativeToAbsoluteCoordinates(const Vector3f relativeCoordinates) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform absolute to relative coordinates using fieldToheadTransform
|
||||||
|
* @return relative coordinates
|
||||||
|
*/
|
||||||
|
Vector3f absoluteToRelativeCoordinates(const Vector3f absoluteCoordinates) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get 3D velocity (based on last n 3D positions)
|
||||||
|
* @param n number of last positions to evaluate (min 1, max 9)
|
||||||
|
* Example for n=3:
|
||||||
|
* current position: p0 (current time step)
|
||||||
|
* last position: p1 (typically* 3 time steps ago)
|
||||||
|
* position before: p2 (typically* 6 time steps ago)
|
||||||
|
* position before: p3 (typically* 9 time steps ago)
|
||||||
|
* RETURN value: p0-p3
|
||||||
|
* *Note: number of actual time steps depends on server configuration and whether
|
||||||
|
* the agent was able to self-locate on the last n visual steps
|
||||||
|
* @return 3D velocity vector
|
||||||
|
*/
|
||||||
|
Vector3f get_velocity(unsigned int n) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get last known head z coordinate
|
||||||
|
* Note: this variable is based on head_z. It can be used as an independent coordinate,
|
||||||
|
* but it should not be used for 3D transformations, as it may be out of sync with
|
||||||
|
* the x and y coordinates. (see head_z)
|
||||||
|
* @return last known head z coordinate
|
||||||
|
*/
|
||||||
|
float get_last_head_z() const {return last_z;}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//============================================================================ main private methods
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
bool find_z_axis_orient_vec(); //returns true if successful
|
||||||
|
void fit_ground_plane();
|
||||||
|
|
||||||
|
void find_z(const Vector3f& Zvec);
|
||||||
|
bool find_xy();
|
||||||
|
bool guess_xy();
|
||||||
|
|
||||||
|
bool fine_tune_aux(float &initial_angle, float &initial_x, float &initial_y, bool use_probabilities);
|
||||||
|
bool fine_tune(float initial_angle, float initial_x, float initial_y);
|
||||||
|
|
||||||
|
static double map_error_logprob(const gsl_vector *v, void *params);
|
||||||
|
static double map_error_2d(const gsl_vector *v, void *params);
|
||||||
|
|
||||||
|
void commit_everything();
|
||||||
|
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//=================================================================== private transformation matrix
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
//PRELIMINARY MATRIX - where all operations are stored
|
||||||
|
//if the algorithm is not successful, the final matrix is not modified
|
||||||
|
void prelim_reset();
|
||||||
|
Matrix4D prelimHeadToField = Matrix4D(); //initialized as identity matrix
|
||||||
|
|
||||||
|
//FINAL MATRIX - the user has access to a public const reference of the variables below
|
||||||
|
Matrix4D final_headTofieldTransform; // rotation + translation
|
||||||
|
Matrix4D final_headTofieldRotate; // rotation
|
||||||
|
Matrix4D final_fieldToheadTransform; // rotation + translation
|
||||||
|
Matrix4D final_fieldToheadRotate; // rotation
|
||||||
|
|
||||||
|
Vector3f final_translation; //translation
|
||||||
|
|
||||||
|
float final_z; //independent z translation (may be updated more often)
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//=============================================================================== useful statistics
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
std::array<Vector3f, 10> position_history;
|
||||||
|
unsigned int position_history_ptr = 0;
|
||||||
|
|
||||||
|
float last_z = 0.5;
|
||||||
|
|
||||||
|
unsigned int _steps_since_last_update = 0;
|
||||||
|
bool _is_uptodate = false;
|
||||||
|
bool _is_head_z_uptodate = false;
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
//================================================================================ debug statistics
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
int stats_sample_position_error(const Vector3f sample, const Vector3f& cheat, double error_placeholder[]);
|
||||||
|
void stats_reset();
|
||||||
|
|
||||||
|
double errorSum_fineTune_before[7] = {0}; //[0,1,2]- xyz err sum, [3]-2D err sum, [4]-2D err sq sum, [5]-3D err sum, [6]-3D err sq sum
|
||||||
|
double errorSum_fineTune_euclidianDist[7] = {0}; //[0,1,2]- xyz err sum, [3]-2D err sum, [4]-2D err sq sum, [5]-3D err sum, [6]-3D err sq sum
|
||||||
|
double errorSum_fineTune_probabilistic[7] = {0}; //[0,1,2]- xyz err sum, [3]-2D err sum, [4]-2D err sq sum, [5]-3D err sum, [6]-3D err sq sum
|
||||||
|
double errorSum_ball[7] = {0}; //[0,1,2]- xyz err sum, [3]-2D err sum, [4]-2D err sq sum, [5]-3D err sum, [6]-3D err sq sum
|
||||||
|
int counter_fineTune = 0;
|
||||||
|
int counter_ball = 0;
|
||||||
|
|
||||||
|
enum STATE{NONE, RUNNING, MINFAIL, BLIND, FAILzNOgoal, FAILzLine, FAILz, FAILtune, FAILguessLine, FAILguessNone, FAILguessMany, FAILguessTest, DONE, ENUMSIZE};
|
||||||
|
STATE state = NONE;
|
||||||
|
|
||||||
|
void stats_change_state(enum STATE s);
|
||||||
|
int state_counter[STATE::ENUMSIZE] = {0};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef Singleton<LocalizerV2> SLocalizerV2;
|
@ -0,0 +1,15 @@
|
|||||||
|
src = $(wildcard *.cpp)
|
||||||
|
obj = $(src:.c=.o)
|
||||||
|
|
||||||
|
LDFLAGS = -lgsl -lgslcblas
|
||||||
|
CFLAGS = -O3 -shared -std=c++11 -fPIC -Wall $(PYBIND_INCLUDES)
|
||||||
|
|
||||||
|
all: $(obj)
|
||||||
|
g++ $(CFLAGS) -o localization.so $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
debug: $(filter-out lib_main.cpp,$(obj))
|
||||||
|
g++ -O0 -std=c++14 -Wall -g -o debug.bin debug_main.cc $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -f $(obj) all
|
@ -0,0 +1,303 @@
|
|||||||
|
#include "Matrix4D.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Matrix4D::Matrix4D() {
|
||||||
|
// identity matrix
|
||||||
|
const float tmp[M_LENGTH] = {
|
||||||
|
1, 0, 0, 0,
|
||||||
|
0, 1, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < M_LENGTH; i++)
|
||||||
|
content[i] = tmp[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D::Matrix4D(const float a[M_LENGTH]) {
|
||||||
|
// creates a matrix using a vector of floats
|
||||||
|
for (int i = 0; i < M_LENGTH; i++)
|
||||||
|
content[i] = a[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D::Matrix4D(const Matrix4D& other) {
|
||||||
|
// creates a matrix using another matrix
|
||||||
|
for (int i = 0; i < M_LENGTH; i++)
|
||||||
|
content[i] = other.content[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D::Matrix4D(const Vector3f& v) {
|
||||||
|
float x = v.getX();
|
||||||
|
float y = v.getY();
|
||||||
|
float z = v.getZ();
|
||||||
|
|
||||||
|
// gets a translation matrix from xyz coordinates
|
||||||
|
const float tmp[M_LENGTH] = {
|
||||||
|
1, 0, 0, x,
|
||||||
|
0, 1, 0, y,
|
||||||
|
0, 0, 1, z,
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < M_LENGTH; i++)
|
||||||
|
content[i] = tmp[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D::~Matrix4D() {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
void Matrix4D::set(unsigned i, float value) {
|
||||||
|
content[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Matrix4D::set(unsigned i, unsigned j, float value) {
|
||||||
|
content[M_COLS * i + j] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Matrix4D::get(unsigned i) const{
|
||||||
|
return content[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
float Matrix4D::get(unsigned i, unsigned j) const{
|
||||||
|
return content[M_COLS * i + j];
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D Matrix4D::operator+(const Matrix4D& other) const {
|
||||||
|
// sums two matrices
|
||||||
|
float tmp[M_LENGTH];
|
||||||
|
|
||||||
|
for (int i = 0; i < M_LENGTH; i++)
|
||||||
|
tmp[i] = this->content[i] + other.content[i];
|
||||||
|
|
||||||
|
return Matrix4D(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D Matrix4D::operator-(const Matrix4D& other) const {
|
||||||
|
// subtracts a matrix from another
|
||||||
|
float tmp[M_LENGTH];
|
||||||
|
|
||||||
|
for (int i = 0; i < M_LENGTH; i++)
|
||||||
|
tmp[i] = this->content[i] - other.content[i];
|
||||||
|
|
||||||
|
return Matrix4D(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D Matrix4D::operator*(const Matrix4D& other) const {
|
||||||
|
|
||||||
|
// multiplies two matrices
|
||||||
|
float tmp[M_LENGTH];
|
||||||
|
|
||||||
|
for (int i = 0; i < M_ROWS; i++) {
|
||||||
|
for (int j = 0; j < M_COLS; j++) {
|
||||||
|
tmp[M_COLS * i + j] = 0;
|
||||||
|
|
||||||
|
for (int k = 0; k < M_COLS; k++)
|
||||||
|
tmp[M_COLS * i + j] += this->content[M_COLS * i + k] * other.content[M_COLS * k + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Matrix4D(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Matrix4D::operator*(const Vector3f& vec) const {
|
||||||
|
// multiplies this matrix by a vector of four floats
|
||||||
|
// the first three are from vec and the remaining float is 1.0
|
||||||
|
float x = 0;
|
||||||
|
float y = 0;
|
||||||
|
float z = 0;
|
||||||
|
|
||||||
|
x = this->content[0] * vec.getX();
|
||||||
|
x += this->content[1] * vec.getY();
|
||||||
|
x += this->content[2] * vec.getZ();
|
||||||
|
x += this->content[3];
|
||||||
|
|
||||||
|
y = this->content[4] * vec.getX();
|
||||||
|
y += this->content[5] * vec.getY();
|
||||||
|
y += this->content[6] * vec.getZ();
|
||||||
|
y += this->content[7];
|
||||||
|
|
||||||
|
z = this->content[8] * vec.getX();
|
||||||
|
z += this->content[9] * vec.getY();
|
||||||
|
z += this->content[10] * vec.getZ();
|
||||||
|
z += this->content[11];
|
||||||
|
|
||||||
|
return Vector3f(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Matrix4D::operator=(const Matrix4D& other) {
|
||||||
|
// assigns another matrix to this one
|
||||||
|
for (int i = 0; i < M_LENGTH; i++)
|
||||||
|
content[i] = other.content[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Matrix4D::operator==(const Matrix4D& other) const {
|
||||||
|
// checks whether this matrix is equal to another
|
||||||
|
for (int i = 0; i < M_LENGTH; i++)
|
||||||
|
if (content[i] != other.content[i])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Matrix4D::operator+=(const Matrix4D& other) {
|
||||||
|
// sums this matrix to another and returns the result
|
||||||
|
for (int i = 0; i < M_LENGTH; i++)
|
||||||
|
content[i] += other.content[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Matrix4D::operator-=(const Matrix4D& other) {
|
||||||
|
// subtracts this matrix from another and returns the result
|
||||||
|
for (int i = 0; i < M_LENGTH; i++)
|
||||||
|
content[i] -= other.content[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
float& Matrix4D::operator[](const unsigned pos) {
|
||||||
|
// gets a value from position
|
||||||
|
return content[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Matrix4D::toVector3f() const {
|
||||||
|
// gets the translation vector from the matrix
|
||||||
|
float x = get(0, M_COLS - 1);
|
||||||
|
float y = get(1, M_COLS - 1);
|
||||||
|
float z = get(2, M_COLS - 1);
|
||||||
|
|
||||||
|
return Vector3f(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D Matrix4D::transpose() {
|
||||||
|
// returns the transpose of this matrix
|
||||||
|
Matrix4D result;
|
||||||
|
|
||||||
|
for (int i = 0; i < M_ROWS; i++)
|
||||||
|
for (int j = 0; j < M_COLS; j++)
|
||||||
|
result.set(j, i, get(i, j));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D Matrix4D::inverse_tranformation_matrix() const {
|
||||||
|
|
||||||
|
Matrix4D inv; //Initialized as identity matrix
|
||||||
|
inverse_tranformation_matrix(inv);
|
||||||
|
return inv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Matrix4D::inverse_tranformation_matrix(Matrix4D& inv) const {
|
||||||
|
|
||||||
|
//Rotation
|
||||||
|
inv[0] = content[0]; inv[1] = content[4]; inv[2] = content[8];
|
||||||
|
inv[4] = content[1]; inv[5] = content[5]; inv[6] = content[9];
|
||||||
|
inv[8] = content[2]; inv[9] = content[6]; inv[10] = content[10];
|
||||||
|
|
||||||
|
//Translation
|
||||||
|
inv[3] = -content[0]*content[3] - content[4]*content[7] - content[8]*content[11];
|
||||||
|
inv[7] = -content[1]*content[3] - content[5]*content[7] - content[9]*content[11];
|
||||||
|
inv[11] = -content[2]*content[3] - content[6]*content[7] - content[10]*content[11];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Matrix4D::inverse(Matrix4D& inverse_out) const{
|
||||||
|
// returns the inverse of this matrix
|
||||||
|
|
||||||
|
float inv[16], det;
|
||||||
|
const float* m = content;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10];
|
||||||
|
inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10];
|
||||||
|
inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9];
|
||||||
|
inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9];
|
||||||
|
inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10];
|
||||||
|
inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10];
|
||||||
|
inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9];
|
||||||
|
inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9];
|
||||||
|
inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6];
|
||||||
|
inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6];
|
||||||
|
inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5];
|
||||||
|
inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5];
|
||||||
|
inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6];
|
||||||
|
inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6];
|
||||||
|
inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5];
|
||||||
|
inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5];
|
||||||
|
|
||||||
|
det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
|
||||||
|
|
||||||
|
if (det == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
det = 1.0 / det;
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++)
|
||||||
|
inverse_out.set(i, inv[i] * det);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Matrix4D Matrix4D::rotationX(float angle) {
|
||||||
|
// rotates this matrix around the x-axis
|
||||||
|
const float tmp[M_LENGTH] = {
|
||||||
|
1, 0, 0, 0,
|
||||||
|
0, Cos(angle), -Sin(angle), 0,
|
||||||
|
0, Sin(angle), Cos(angle), 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
return Matrix4D(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D Matrix4D::rotationY(float angle) {
|
||||||
|
// rotates this matrix around the y-axis
|
||||||
|
const float tmp[M_LENGTH] = {
|
||||||
|
Cos(angle), 0, Sin(angle), 0,
|
||||||
|
0, 1, 0, 0,
|
||||||
|
-Sin(angle), 0, Cos(angle), 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
return Matrix4D(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D Matrix4D::rotationZ(float angle) {
|
||||||
|
// rotates this matrix around the z axis
|
||||||
|
const float tmp[M_LENGTH] = {
|
||||||
|
Cos(angle), -Sin(angle), 0, 0,
|
||||||
|
Sin(angle), Cos(angle), 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
return Matrix4D(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D Matrix4D::rotation(Vector3f axis, float angle) {
|
||||||
|
// assuming that axis is a unit vector
|
||||||
|
float x = axis.getX();
|
||||||
|
float y = axis.getY();
|
||||||
|
float z = axis.getZ();
|
||||||
|
|
||||||
|
const float tmp[M_LENGTH] = {
|
||||||
|
(x * x * (1 - Cos(angle)) + Cos(angle)), (x * y * (1 - Cos(angle)) - z * Sin(angle)), (x * z * (1 - Cos(angle)) + y * Sin(angle)), 0,
|
||||||
|
(x * y * (1 - Cos(angle)) + z * Sin(angle)), (y * y * (1 - Cos(angle)) + Cos(angle)), (y * z * (1 - Cos(angle)) - x * Sin(angle)), 0,
|
||||||
|
(x * z * (1 - Cos(angle)) - y * Sin(angle)), (y * z * (1 - Cos(angle)) + x * Sin(angle)), (z * z * (1 - Cos(angle)) + Cos(angle)), 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
return Matrix4D(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4D Matrix4D::translation(float x, float y, float z) {
|
||||||
|
// gets a translation matrix from xyz coordinates
|
||||||
|
const float tmp[M_LENGTH] = {
|
||||||
|
1, 0, 0, x,
|
||||||
|
0, 1, 0, y,
|
||||||
|
0, 0, 1, z,
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
return Matrix4D(tmp);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,224 @@
|
|||||||
|
#ifndef MATRIX4D_H_
|
||||||
|
#define MATRIX4D_H_
|
||||||
|
|
||||||
|
#include "Vector3f.h"
|
||||||
|
|
||||||
|
#define M_ROWS 4
|
||||||
|
#define M_COLS 4
|
||||||
|
#define M_LENGTH (M_ROWS * M_COLS)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Matrix4D
|
||||||
|
*
|
||||||
|
* This class represents a 4x4 matrix and contains methods
|
||||||
|
* to operate on it
|
||||||
|
*
|
||||||
|
* @author Nuno Almeida (nuno.alm@ua.pt)
|
||||||
|
* Adapted - Miguel Abreu
|
||||||
|
*/
|
||||||
|
class Matrix4D {
|
||||||
|
|
||||||
|
public:
|
||||||
|
float content[M_LENGTH]; // content of the matrix, vector-like
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor returns the identity matrix
|
||||||
|
*/
|
||||||
|
Matrix4D();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor returns a matrix from a vector of floats
|
||||||
|
*/
|
||||||
|
Matrix4D(const float[]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor
|
||||||
|
*/
|
||||||
|
Matrix4D(const Matrix4D& other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor returns a translation matrix
|
||||||
|
*/
|
||||||
|
Matrix4D(const Vector3f& v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~Matrix4D();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a value in some position (vector-like)
|
||||||
|
*/
|
||||||
|
void set(unsigned i, float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a value in some position (matrix-like)
|
||||||
|
*/
|
||||||
|
void set(unsigned i, unsigned j, float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a value from some position (vector-like)
|
||||||
|
*/
|
||||||
|
float get(unsigned i) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a value from some position (matrix-like)
|
||||||
|
*/
|
||||||
|
float get(unsigned i, unsigned j) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns another matrix to this one
|
||||||
|
*
|
||||||
|
* @param other Another matrix
|
||||||
|
*/
|
||||||
|
void operator=(const Matrix4D& other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sum of another vector with this one
|
||||||
|
*
|
||||||
|
* @param other Another matrix
|
||||||
|
*/
|
||||||
|
Matrix4D operator+(const Matrix4D& other) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sums this matrix to another
|
||||||
|
*
|
||||||
|
* @param other Another matrix
|
||||||
|
* @return Sum of this matrix with another
|
||||||
|
*/
|
||||||
|
void operator+=(const Matrix4D& other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the subtraction of another vector from this one
|
||||||
|
*
|
||||||
|
* @param other Another matrix
|
||||||
|
*/
|
||||||
|
Matrix4D operator-(const Matrix4D&) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtracts another matrix from this one
|
||||||
|
*
|
||||||
|
* @param other Another matrix
|
||||||
|
* @return This matrix minus another
|
||||||
|
*/
|
||||||
|
void operator-=(const Matrix4D& other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies two matrices
|
||||||
|
*
|
||||||
|
* @param other Another matrix
|
||||||
|
* @return Multiplication matrix
|
||||||
|
*/
|
||||||
|
Matrix4D operator*(const Matrix4D& other) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies a matrix with a vector
|
||||||
|
*
|
||||||
|
* @param other Another matrix
|
||||||
|
* @return Multiplication vector
|
||||||
|
*/
|
||||||
|
Vector3f operator*(const Vector3f& other) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this matrix is equal to another
|
||||||
|
*
|
||||||
|
* @param other Another matrix
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
bool operator==(const Matrix4D&) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the content of the position i (in vector representation) of this
|
||||||
|
* matrix
|
||||||
|
*
|
||||||
|
* @param pos Position
|
||||||
|
* @return Value in the position
|
||||||
|
*/
|
||||||
|
float& operator[](const unsigned pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the translation vector from this matrix
|
||||||
|
*
|
||||||
|
* @return Translation vector
|
||||||
|
*/
|
||||||
|
Vector3f toVector3f() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the transpose of this matrix
|
||||||
|
*
|
||||||
|
* @return Transpose
|
||||||
|
*/
|
||||||
|
Matrix4D transpose();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the inverse of this matrix (m.abreu@2020)
|
||||||
|
*
|
||||||
|
* @param inverse_out inverse matrix
|
||||||
|
* @return true if it exists
|
||||||
|
*/
|
||||||
|
bool inverse(Matrix4D& inverse_out) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the inverse of this matrix, (m.abreu@2020)
|
||||||
|
* assuming that it represents an affine transformation with only translation and rotation
|
||||||
|
* This method creates a new matrix
|
||||||
|
*
|
||||||
|
* @return inverse matrix
|
||||||
|
*/
|
||||||
|
Matrix4D inverse_tranformation_matrix() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the inverse of this matrix, (m.abreu@2020)
|
||||||
|
* assuming that it represents an affine transformation with only translation and rotation
|
||||||
|
* This method overwrites the given matrix
|
||||||
|
*
|
||||||
|
* @param inverse_out inverse matrix output
|
||||||
|
*/
|
||||||
|
void inverse_tranformation_matrix(Matrix4D& inverse_out) const;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the rotation matrix around x-axis
|
||||||
|
*
|
||||||
|
* @param angle Angle (degrees)
|
||||||
|
*/
|
||||||
|
static Matrix4D rotationX(float angle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the rotation matrix around y-axis
|
||||||
|
*
|
||||||
|
* @param angle Angle (degrees)
|
||||||
|
*/
|
||||||
|
static Matrix4D rotationY(float angle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the rotation matrix around z-axis
|
||||||
|
*
|
||||||
|
* @param angle Angle (degrees)
|
||||||
|
*/
|
||||||
|
static Matrix4D rotationZ(float angle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the rotation matrix around an arbitrary axis
|
||||||
|
*
|
||||||
|
* @param axis Axis (x, y and z)
|
||||||
|
* @param angle Angle (degrees)
|
||||||
|
*/
|
||||||
|
static Matrix4D rotation(Vector3f axis, float angle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the translation matrix
|
||||||
|
*
|
||||||
|
* @param x x-axis coordinate
|
||||||
|
* @param y y-axis coordinate
|
||||||
|
* @param z z-axis coordinate
|
||||||
|
*/
|
||||||
|
static Matrix4D translation(Vector3f v) {
|
||||||
|
return translation(v.getX(), v.getY(), v.getZ());
|
||||||
|
}
|
||||||
|
static Matrix4D translation(float x, float y, float z);
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MATRIX4D_H_
|
@ -0,0 +1,131 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include "RobovizLogger.h"
|
||||||
|
#include "robovizdraw.h"
|
||||||
|
|
||||||
|
#define ROBOVIZ_HOST "localhost"
|
||||||
|
#define ROBOVIZ_PORT "32769"
|
||||||
|
|
||||||
|
|
||||||
|
RobovizLogger* RobovizLogger::Instance() {
|
||||||
|
static RobovizLogger instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
RobovizLogger::RobovizLogger() {}
|
||||||
|
|
||||||
|
RobovizLogger::~RobovizLogger() {
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
int RobovizLogger::init() {
|
||||||
|
if (is_initialized) return 0;
|
||||||
|
struct addrinfo hints;
|
||||||
|
int rv;
|
||||||
|
int numbytes;
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof hints);
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
|
||||||
|
if ((rv = getaddrinfo(ROBOVIZ_HOST, ROBOVIZ_PORT, &hints, &servinfo)) != 0) {
|
||||||
|
//fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop through all the results and make a socket
|
||||||
|
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||||
|
if ((sockfd = socket(p->ai_family, p->ai_socktype,
|
||||||
|
p->ai_protocol)) == -1) {
|
||||||
|
perror("socket");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == NULL) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_initialized = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RobovizLogger::destroy() {
|
||||||
|
freeaddrinfo(servinfo);
|
||||||
|
servinfo=NULL;
|
||||||
|
close(sockfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RobovizLogger::swapBuffers(const string* setName) {
|
||||||
|
int bufSize = -1;
|
||||||
|
unsigned char* buf = newBufferSwap(setName, &bufSize);
|
||||||
|
sendto(sockfd, buf, bufSize, 0, p->ai_addr, p->ai_addrlen);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RobovizLogger::drawLine(float x1, float y1, float z1, float x2, float y2, float z2,
|
||||||
|
float thickness, float r, float g, float b, const string* setName) {
|
||||||
|
float pa[3] = {x1, y1, z1};
|
||||||
|
float pb[3] = {x2, y2, z2};
|
||||||
|
float color[3] = {r, g, b};
|
||||||
|
|
||||||
|
int bufSize = -1;
|
||||||
|
unsigned char* buf = newLine(pa, pb, thickness, color, setName, &bufSize);
|
||||||
|
sendto(sockfd, buf, bufSize, 0, p->ai_addr, p->ai_addrlen);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RobovizLogger::drawCircle(float x, float y, float radius, float thickness,
|
||||||
|
float r, float g, float b, const string* setName) {
|
||||||
|
float center[2] = {x, y};
|
||||||
|
float color[3] = {r, g, b};
|
||||||
|
|
||||||
|
int bufSize = -1;
|
||||||
|
unsigned char* buf = newCircle(center, radius, thickness, color, setName, &bufSize);
|
||||||
|
sendto(sockfd, buf, bufSize, 0, p->ai_addr, p->ai_addrlen);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RobovizLogger::drawSphere(float x, float y, float z, float radius,
|
||||||
|
float r, float g, float b, const string* setName) {
|
||||||
|
float center[3] = {x, y, z};
|
||||||
|
float color[3] = {r, g, b};
|
||||||
|
|
||||||
|
int bufSize = -1;
|
||||||
|
unsigned char* buf = newSphere(center, radius, color, setName, &bufSize);
|
||||||
|
sendto(sockfd, buf, bufSize, 0, p->ai_addr, p->ai_addrlen);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RobovizLogger::drawPoint(float x, float y, float z, float size,
|
||||||
|
float r, float g, float b, const string* setName) {
|
||||||
|
float center[3] = {x, y, z};
|
||||||
|
float color[3] = {r, g, b};
|
||||||
|
|
||||||
|
int bufSize = -1;
|
||||||
|
unsigned char* buf = newPoint(center, size, color, setName, &bufSize);
|
||||||
|
sendto(sockfd, buf, bufSize, 0, p->ai_addr, p->ai_addrlen);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RobovizLogger::drawPolygon(const float* v, int numVerts, float r, float g, float b,
|
||||||
|
float a, const string* setName) {
|
||||||
|
float color[4] = {r, g, b, a};
|
||||||
|
|
||||||
|
int bufSize = -1;
|
||||||
|
unsigned char* buf = newPolygon(v, numVerts, color, setName, &bufSize);
|
||||||
|
sendto(sockfd, buf, bufSize, 0, p->ai_addr, p->ai_addrlen);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RobovizLogger::drawAnnotation(const string* text, float x, float y, float z, float r,
|
||||||
|
float g, float b, const string* setName) {
|
||||||
|
float color[3] = {r, g, b};
|
||||||
|
float pos[3] = {x, y, z};
|
||||||
|
|
||||||
|
int bufSize = -1;
|
||||||
|
unsigned char* buf = newAnnotation(text, pos, color, setName, &bufSize);
|
||||||
|
sendto(sockfd, buf, bufSize, 0, p->ai_addr, p->ai_addrlen);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* RobovizLogger.h
|
||||||
|
*
|
||||||
|
* Created on: 2013/06/24
|
||||||
|
* Author: Rui Ferreira
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ROBOVIZLOGGER_H_
|
||||||
|
#define _ROBOVIZLOGGER_H_
|
||||||
|
|
||||||
|
#define RobovizLoggerInstance RobovizLogger::Instance()
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
class RobovizLogger {
|
||||||
|
private:
|
||||||
|
RobovizLogger();
|
||||||
|
RobovizLogger(const RobovizLogger&);
|
||||||
|
RobovizLogger& operator=(const RobovizLogger&);
|
||||||
|
virtual ~RobovizLogger();
|
||||||
|
bool is_initialized = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static RobovizLogger* Instance();
|
||||||
|
|
||||||
|
int init();
|
||||||
|
void destroy();
|
||||||
|
void swapBuffers(const std::string* setName);
|
||||||
|
|
||||||
|
void drawLine(float x1, float y1, float z1, float x2, float y2, float z2,
|
||||||
|
float thickness, float r, float g, float b, const std::string* setName);
|
||||||
|
void drawCircle(float x, float y, float radius, float thickness,
|
||||||
|
float r, float g, float b, const std::string* setName);
|
||||||
|
void drawSphere(float x, float y, float z, float radius,
|
||||||
|
float r, float g, float b, const std::string* setName);
|
||||||
|
void drawPoint(float x, float y, float z, float size,
|
||||||
|
float r, float g, float b, const std::string* setName);
|
||||||
|
void drawPolygon(const float* v, int numVerts, float r, float g, float b,
|
||||||
|
float a, const std::string* setName);
|
||||||
|
void drawAnnotation(const std::string* text, float x, float y, float z, float r,
|
||||||
|
float g, float b, const std::string* setName);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int sockfd;
|
||||||
|
struct addrinfo* p;
|
||||||
|
struct addrinfo* servinfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _ROBOVIZLOGGER_H_
|
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef SINGLETON_H
|
||||||
|
#define SINGLETON_H
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class Singleton {
|
||||||
|
public:
|
||||||
|
|
||||||
|
static T& getInstance() {
|
||||||
|
static T instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Singleton();
|
||||||
|
~Singleton();
|
||||||
|
Singleton(Singleton const&);
|
||||||
|
Singleton& operator=(Singleton const&);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SINGLETON_H
|
@ -0,0 +1,197 @@
|
|||||||
|
#include "Vector3f.h"
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
Vector3f::Vector3f() {
|
||||||
|
x = 0.0;
|
||||||
|
y = 0.0;
|
||||||
|
z = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f::Vector3f(float x, float y, float z) {
|
||||||
|
this->x = x;
|
||||||
|
this->y = y;
|
||||||
|
this->z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f::Vector3f(const Vector3f& other) {
|
||||||
|
x = other.x;
|
||||||
|
y = other.y;
|
||||||
|
z = other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f::Vector3f(const Vector& other) {
|
||||||
|
x = other.x;
|
||||||
|
y = other.y;
|
||||||
|
z = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f::~Vector3f() {
|
||||||
|
}
|
||||||
|
|
||||||
|
float Vector3f::getX() const {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vector3f::setX(float x) {
|
||||||
|
this->x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Vector3f::getY() const {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vector3f::setY(float y) {
|
||||||
|
this->y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Vector3f::getZ() const {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vector3f::setZ(float z) {
|
||||||
|
this->z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Vector3f::operator[](const int index) const {
|
||||||
|
float val=0.0;
|
||||||
|
switch (index) {
|
||||||
|
case 0: val = x;
|
||||||
|
break;
|
||||||
|
case 1: val = y;
|
||||||
|
break;
|
||||||
|
case 2: val = z;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator+(const Vector3f& other) const {
|
||||||
|
return Vector3f(x + other.x, y + other.y, z + other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator-(const Vector3f& other) const {
|
||||||
|
return Vector3f(x - other.x, y - other.y, z - other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator-() const {
|
||||||
|
return Vector3f() - * this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator*(const Vector3f& other) const {
|
||||||
|
return Vector3f(x * other.x, y * other.y, z * other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator/(const Vector3f& other) const {
|
||||||
|
return Vector3f(x / other.x, y / other.y, z / other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Vector3f::operator==(const Vector3f& other) const {
|
||||||
|
return x == other.x && y == other.y && z == other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator/(float factor) const {
|
||||||
|
return Vector3f(x / factor, y / factor, z / factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator+(float factor) const {
|
||||||
|
return Vector3f(x + factor, y + factor, z + factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator%(float factor) const {
|
||||||
|
return Vector3f(fmod(x, factor), fmod(y, factor), fmod(z, factor));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator*(float factor) const {
|
||||||
|
return Vector3f(x * factor, y * factor, z * factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator+=(const Vector3f& other) {
|
||||||
|
x += other.x;
|
||||||
|
y += other.y;
|
||||||
|
z += other.z;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator+=(float factor) {
|
||||||
|
x += factor;
|
||||||
|
y += factor;
|
||||||
|
z += factor;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator-=(const Vector3f& other) {
|
||||||
|
x -= other.x;
|
||||||
|
y -= other.y;
|
||||||
|
z -= other.z;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator-=(float factor) {
|
||||||
|
x -= factor;
|
||||||
|
y -= factor;
|
||||||
|
z -= factor;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::operator/=(float factor) {
|
||||||
|
x /= factor;
|
||||||
|
y /= factor;
|
||||||
|
z /= factor;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float Vector3f::innerProduct(const Vector3f& other) const {
|
||||||
|
return x * other.x + y * other.y + z * other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::crossProduct(const Vector3f& other) const {
|
||||||
|
Vector3f aux;
|
||||||
|
|
||||||
|
aux.x = this->y * other.z - this->z * other.y;
|
||||||
|
aux.y = this->z * other.x - this->x * other.z;
|
||||||
|
aux.z = this->x * other.y - this->y * other.x;
|
||||||
|
|
||||||
|
return aux;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Vector3f::length() const {
|
||||||
|
return sqrt(x * x + y * y + z * z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vector3f Vector3f::normalize(float len) const {
|
||||||
|
return (*this) * (len / this->length());
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::toCartesian() const {
|
||||||
|
// x = distance
|
||||||
|
// y = theta
|
||||||
|
// z = phi
|
||||||
|
return Vector3f(x * Cos(z) * Cos(y), x * Cos(z) * Sin(y), x * Sin(z));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::toPolar() const {
|
||||||
|
return Vector3f(this->length(), // distance
|
||||||
|
ATan2(y, x), // theta
|
||||||
|
ATan2(z, sqrt(x * x + y * y))); // phi
|
||||||
|
}
|
||||||
|
|
||||||
|
float Vector3f::dist(const Vector3f &other) const {
|
||||||
|
return (*this -other).length();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector Vector3f::to2d() const {
|
||||||
|
return Vector(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f Vector3f::determineMidpoint(Vector3f a, Vector3f b) {
|
||||||
|
return (a+b)/2; /* m.abreu@2020 */
|
||||||
|
}
|
@ -0,0 +1,187 @@
|
|||||||
|
#ifndef VECTOR3F_H
|
||||||
|
#define VECTOR3F_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include "Geometry.h"
|
||||||
|
#include <math.h>
|
||||||
|
//! Describes a vector of three floats
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \author Hugo Picado (hugopicado@ua.pt)
|
||||||
|
* \author Nuno Almeida (nuno.alm@ua.pt)
|
||||||
|
* Adapted - Miguel Abreu
|
||||||
|
*/
|
||||||
|
class Vector3f {
|
||||||
|
public:
|
||||||
|
//! Default constructor
|
||||||
|
Vector3f();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* \param x x-axis coordinate
|
||||||
|
* \param y y-axis coordinate
|
||||||
|
* \param z z-axis coordinate
|
||||||
|
*/
|
||||||
|
Vector3f(float x, float y, float z);
|
||||||
|
|
||||||
|
//! Copy constructor
|
||||||
|
Vector3f(const Vector3f& other);
|
||||||
|
|
||||||
|
Vector3f(const Vector& other);
|
||||||
|
|
||||||
|
//! Destructor
|
||||||
|
~Vector3f();
|
||||||
|
|
||||||
|
//! getX
|
||||||
|
float getX() const;
|
||||||
|
void setX(float x);
|
||||||
|
|
||||||
|
//! getY
|
||||||
|
float getY() const;
|
||||||
|
void setY(float y);
|
||||||
|
|
||||||
|
//! getZ
|
||||||
|
float getZ() const;
|
||||||
|
void setZ(float z);
|
||||||
|
|
||||||
|
//! Access X Y Z through indexes 0 1 2
|
||||||
|
float operator[](const int) const;
|
||||||
|
|
||||||
|
//! Sums this vector to another
|
||||||
|
Vector3f operator+(const Vector3f& other) const;
|
||||||
|
|
||||||
|
//! Subtracts another vector from this
|
||||||
|
Vector3f operator-(const Vector3f& other) const;
|
||||||
|
|
||||||
|
//! Negates the vector
|
||||||
|
Vector3f operator-() const;
|
||||||
|
|
||||||
|
//! Multiples this vector by another
|
||||||
|
Vector3f operator*(const Vector3f& other) const;
|
||||||
|
|
||||||
|
//! Divides this vector by another
|
||||||
|
Vector3f operator/(const Vector3f& other) const;
|
||||||
|
|
||||||
|
bool operator==(const Vector3f& other) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Multiples this vector by a scalar
|
||||||
|
*
|
||||||
|
* \param factor Scalar number
|
||||||
|
*/
|
||||||
|
Vector3f operator*(float factor) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Divides this vector by a scalar
|
||||||
|
*
|
||||||
|
* \param factor Scalar number
|
||||||
|
*/
|
||||||
|
Vector3f operator/(float factor) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Add this vector to a scalar
|
||||||
|
*
|
||||||
|
* \param factor Scalar number
|
||||||
|
*/
|
||||||
|
Vector3f operator+(float factor) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Integer remainder this vector by a scalar
|
||||||
|
*
|
||||||
|
* \param factor Scalar number
|
||||||
|
*/
|
||||||
|
Vector3f operator%(float factor) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sums this vector to another assuming the value of the result
|
||||||
|
*
|
||||||
|
* \param other Vector3f
|
||||||
|
*/
|
||||||
|
Vector3f operator+=(const Vector3f& other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add this vector to a scalar assuming the value of the result
|
||||||
|
*
|
||||||
|
* \param factor Scalar number
|
||||||
|
*/
|
||||||
|
Vector3f operator+=(float factor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtracts other vector from this vector assuming the value of the result
|
||||||
|
*
|
||||||
|
* \param other Vector3f
|
||||||
|
*/
|
||||||
|
Vector3f operator-=(const Vector3f& other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtracts a scalar from this vector assuming the value of the result
|
||||||
|
*
|
||||||
|
* \param factor Scalar number
|
||||||
|
*/
|
||||||
|
Vector3f operator-=(float factor);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Divides this vector by a scalar assuming the value of the result
|
||||||
|
*
|
||||||
|
* \param factor Scalar number
|
||||||
|
*/
|
||||||
|
Vector3f operator/=(float factor);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Computes the inner product of this vector with another
|
||||||
|
*
|
||||||
|
* \param other Vector to compute the inner product
|
||||||
|
* \return Resultant vector
|
||||||
|
*/
|
||||||
|
float innerProduct(const Vector3f& other) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Computes the cross product of this vector with another
|
||||||
|
*
|
||||||
|
* \param other Vector to compute the cross product
|
||||||
|
* \return Resultant vector
|
||||||
|
*/
|
||||||
|
Vector3f crossProduct(const Vector3f& other) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Gets the length of the vector
|
||||||
|
*
|
||||||
|
* \return Length of the vector
|
||||||
|
*/
|
||||||
|
float length() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Normalizes the vector to an arbitrary length (default is 1)
|
||||||
|
*
|
||||||
|
* \param len Length
|
||||||
|
*/
|
||||||
|
Vector3f normalize(float len = 1) const;
|
||||||
|
/*!
|
||||||
|
* Converts the vector coordinates from polar to cartesian
|
||||||
|
* It is assumed that the vector has the angular coordinates in degrees
|
||||||
|
*/
|
||||||
|
Vector3f toCartesian() const;
|
||||||
|
|
||||||
|
//! Converts the vector coordinates from cartesian to polar
|
||||||
|
Vector3f toPolar() const;
|
||||||
|
|
||||||
|
//! Converts the 3d vector to a 2d vector
|
||||||
|
Vector to2d() const;
|
||||||
|
|
||||||
|
//! Gets the distance between this vector and another
|
||||||
|
float dist(const Vector3f& other) const;
|
||||||
|
|
||||||
|
/* !Determines the midpoint between 2 points in a 3D space
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
static Vector3f determineMidpoint(Vector3f a, Vector3f b);
|
||||||
|
|
||||||
|
public:
|
||||||
|
float x; // x-axis coordinate
|
||||||
|
float y; // y-axis coordinate
|
||||||
|
float z; // z-axis coordinate
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VECTOR3F_H
|
@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* FILENAME: World
|
||||||
|
* DESCRIPTION: World data from Python
|
||||||
|
* AUTHOR: Miguel Abreu (m.abreu@fe.up.pt)
|
||||||
|
* DATE: 2021
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "Vector3f.h"
|
||||||
|
#include "Singleton.h"
|
||||||
|
#include "Matrix4D.h"
|
||||||
|
#include "Line6f.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
class World {
|
||||||
|
friend class Singleton<World>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
World(){};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//Feet variables: (0) left, (1) right
|
||||||
|
bool foot_touch[2]; // is foot touching ground
|
||||||
|
Vector3f foot_contact_rel_pos[2]; // foot_transform * translation(foot_contact_pt)
|
||||||
|
|
||||||
|
bool ball_seen;
|
||||||
|
Vector3f ball_rel_pos_cart;
|
||||||
|
Vector3f ball_cheat_abs_cart_pos;
|
||||||
|
Vector3f my_cheat_abs_cart_pos;
|
||||||
|
|
||||||
|
struct sLMark {
|
||||||
|
bool seen;
|
||||||
|
bool isCorner;
|
||||||
|
Vector3f pos;
|
||||||
|
Vector3f rel_pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
sLMark landmark[8];
|
||||||
|
|
||||||
|
struct sLine {
|
||||||
|
Vector3f start, end;
|
||||||
|
sLine(const Vector3f& s, const Vector3f& e) : start(s), end(e) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
vector<sLine> lines_polar;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Singleton<World> SWorld;
|
@ -0,0 +1,196 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include "Geometry.h"
|
||||||
|
#include "Vector3f.h"
|
||||||
|
#include "Matrix4D.h"
|
||||||
|
#include "FieldNoise.h"
|
||||||
|
#include "Line6f.h"
|
||||||
|
#include "World.h"
|
||||||
|
#include "Field.h"
|
||||||
|
#include "LocalizerV2.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
static LocalizerV2& loc = SLocalizerV2::getInstance();
|
||||||
|
|
||||||
|
void print_python_data(){
|
||||||
|
|
||||||
|
static World &world = SWorld::getInstance();
|
||||||
|
|
||||||
|
cout << "Foot touch: " << world.foot_touch[0] << " " << world.foot_touch[1] << endl;
|
||||||
|
cout << "LFoot contact rpos: " << world.foot_contact_rel_pos[0].x << " " << world.foot_contact_rel_pos[0].y << " " << world.foot_contact_rel_pos[0].z << endl;
|
||||||
|
cout << "RFoot contact rpos: " << world.foot_contact_rel_pos[1].x << " " << world.foot_contact_rel_pos[1].y << " " << world.foot_contact_rel_pos[1].z << endl;
|
||||||
|
cout << "Ball seen: " << world.ball_seen << endl;
|
||||||
|
cout << "Ball rpos cart: " << world.ball_rel_pos_cart.x << " " << world.ball_rel_pos_cart.y << " " << world.ball_rel_pos_cart.z << endl;
|
||||||
|
cout << "Ball cheat: " << world.ball_cheat_abs_cart_pos.x << " " << world.ball_cheat_abs_cart_pos.y << " " << world.ball_cheat_abs_cart_pos.z << endl;
|
||||||
|
cout << "Me cheat: " << world.my_cheat_abs_cart_pos.x << " " << world.my_cheat_abs_cart_pos.y << " " << world.my_cheat_abs_cart_pos.z << endl;
|
||||||
|
|
||||||
|
for(int i=0; i<8; i++){
|
||||||
|
cout << "Landmark " << i << ": " <<
|
||||||
|
world.landmark[i].seen << " " <<
|
||||||
|
world.landmark[i].isCorner << " " <<
|
||||||
|
world.landmark[i].pos.x << " " <<
|
||||||
|
world.landmark[i].pos.y << " " <<
|
||||||
|
world.landmark[i].pos.z << " " <<
|
||||||
|
world.landmark[i].rel_pos.x << " " <<
|
||||||
|
world.landmark[i].rel_pos.y << " " <<
|
||||||
|
world.landmark[i].rel_pos.z << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<world.lines_polar.size(); i++){
|
||||||
|
cout << "Line " << i << ": " <<
|
||||||
|
world.lines_polar[i].start.x << " " <<
|
||||||
|
world.lines_polar[i].start.y << " " <<
|
||||||
|
world.lines_polar[i].start.z << " " <<
|
||||||
|
world.lines_polar[i].end.x << " " <<
|
||||||
|
world.lines_polar[i].end.y << " " <<
|
||||||
|
world.lines_polar[i].end.z << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float *compute(bool lfoot_touch, bool rfoot_touch,
|
||||||
|
double feet_contact[],
|
||||||
|
bool ball_seen, double ball_pos[],
|
||||||
|
double me_pos[],
|
||||||
|
double landmarks[],
|
||||||
|
double lines[],
|
||||||
|
int lines_no){
|
||||||
|
|
||||||
|
// ================================================= 1. Parse data
|
||||||
|
|
||||||
|
static World &world = SWorld::getInstance();
|
||||||
|
world.foot_touch[0] = lfoot_touch;
|
||||||
|
world.foot_touch[1] = rfoot_touch;
|
||||||
|
|
||||||
|
//Structure of feet_contact {lfoot_contact_pt, rfoot_contact_pt, lfoot_contact_rel_pos, rfoot_contact_rel_pos}
|
||||||
|
|
||||||
|
world.foot_contact_rel_pos[0].x = feet_contact[0];
|
||||||
|
world.foot_contact_rel_pos[0].y = feet_contact[1];
|
||||||
|
world.foot_contact_rel_pos[0].z = feet_contact[2];
|
||||||
|
world.foot_contact_rel_pos[1].x = feet_contact[3];
|
||||||
|
world.foot_contact_rel_pos[1].y = feet_contact[4];
|
||||||
|
world.foot_contact_rel_pos[1].z = feet_contact[5];
|
||||||
|
|
||||||
|
world.ball_seen = ball_seen;
|
||||||
|
|
||||||
|
//Structure of ball_pos {ball_rel_pos_cart, ball_cheat_abs_cart_pos}
|
||||||
|
|
||||||
|
world.ball_rel_pos_cart.x = ball_pos[0];
|
||||||
|
world.ball_rel_pos_cart.y = ball_pos[1];
|
||||||
|
world.ball_rel_pos_cart.z = ball_pos[2];
|
||||||
|
world.ball_cheat_abs_cart_pos.x = ball_pos[3];
|
||||||
|
world.ball_cheat_abs_cart_pos.y = ball_pos[4];
|
||||||
|
world.ball_cheat_abs_cart_pos.z = ball_pos[5];
|
||||||
|
|
||||||
|
world.my_cheat_abs_cart_pos.x = me_pos[0];
|
||||||
|
world.my_cheat_abs_cart_pos.y = me_pos[1];
|
||||||
|
world.my_cheat_abs_cart_pos.z = me_pos[2];
|
||||||
|
|
||||||
|
for(int i=0; i<8; i++){
|
||||||
|
world.landmark[i].seen = (bool) landmarks[0];
|
||||||
|
world.landmark[i].isCorner = (bool) landmarks[1];
|
||||||
|
world.landmark[i].pos.x = landmarks[2];
|
||||||
|
world.landmark[i].pos.y = landmarks[3];
|
||||||
|
world.landmark[i].pos.z = landmarks[4];
|
||||||
|
world.landmark[i].rel_pos.x = landmarks[5];
|
||||||
|
world.landmark[i].rel_pos.y = landmarks[6];
|
||||||
|
world.landmark[i].rel_pos.z = landmarks[7];
|
||||||
|
landmarks += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
world.lines_polar.clear();
|
||||||
|
|
||||||
|
for(int i=0; i<lines_no; i++){
|
||||||
|
Vector3f s(lines[0],lines[1],lines[2]);
|
||||||
|
Vector3f e(lines[3],lines[4],lines[5]);
|
||||||
|
world.lines_polar.emplace_back(s, e);
|
||||||
|
lines += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_python_data();
|
||||||
|
|
||||||
|
// ================================================= 2. Compute 6D pose
|
||||||
|
|
||||||
|
loc.run();
|
||||||
|
|
||||||
|
// ================================================= 3. Prepare data to return
|
||||||
|
|
||||||
|
float retval[35];
|
||||||
|
float *ptr = retval;
|
||||||
|
|
||||||
|
for(int i=0; i<16; i++){
|
||||||
|
ptr[i] = loc.headTofieldTransform.content[i];
|
||||||
|
}
|
||||||
|
ptr += 16;
|
||||||
|
for(int i=0; i<16; i++){
|
||||||
|
ptr[i] = loc.fieldToheadTransform.content[i];
|
||||||
|
}
|
||||||
|
ptr += 16;
|
||||||
|
|
||||||
|
ptr[0] = (float) loc.is_uptodate;
|
||||||
|
ptr[1] = loc.head_z;
|
||||||
|
ptr[2] = (float) loc.is_head_z_uptodate;
|
||||||
|
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_report(){
|
||||||
|
loc.print_report();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_visible_elements(bool is_right_side){
|
||||||
|
Field& fd = SField::getInstance();
|
||||||
|
fd.draw_visible(loc.headTofieldTransform, is_right_side);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
|
||||||
|
double feet_contact[] = {0.02668597, 0.055 , -0.49031584, 0.02668597, -0.055 , -0.49031584};
|
||||||
|
double ball_pos[] = {22.3917517 , 4.91904904, -0.44419865, -0. , -0. , 0.04 };
|
||||||
|
double me_pos[] = {-22.8 , -2.44, 0.48};
|
||||||
|
double landmarks[] = { 1. , 1. , -15. , -10. , 0. , 10.88, -37.74, -2.42,
|
||||||
|
0. , 1. , -15. , 10. , 0. , 0. , 0. , 0. ,
|
||||||
|
1. , 1. , 15. , -10. , 0. , 38.56, -4.9 , -0.66,
|
||||||
|
1. , 1. , 15. , 10. , 0. , 39.75, 24.4 , -0.7 ,
|
||||||
|
1. , 0. , -15. , -1.05, 0.8 , 7.94, 16.31, 2.42,
|
||||||
|
1. , 0. , -15. , 1.05, 0.8 , 8.55, 30.15, 2.11,
|
||||||
|
1. , 0. , 15. , -1.05, 0.8 , 37.82, 8.16, 0.5 ,
|
||||||
|
1. , 0. , 15. , 1.05, 0.8 , 37.94, 11.77, 0.44 };
|
||||||
|
double lines[] = { 25.95, 35.02, -1.14, 24.02, -12.26, -1.12,
|
||||||
|
13.18, 59.93, -2.11, 10.87, -37.8 , -2.69,
|
||||||
|
39.78, 24.32, -0.75, 38.64, -5.05, -0.67,
|
||||||
|
10.89, -37.56, -2.6 , 38.52, -5.24, -0.68,
|
||||||
|
15.44, 59.85, -1.87, 39.76, 24.77, -0.88,
|
||||||
|
9.62, 3.24, -2.67, 11.02, 36.02, -2.54,
|
||||||
|
9.63, 2.82, -3.16, 7.82, 2.14, -3.67,
|
||||||
|
11.02, 36.09, -2.61, 9.51, 41.19, -2.94,
|
||||||
|
36.03, 5.33, -0.66, 36.46, 14.9 , -0.74,
|
||||||
|
35.94, 5.43, -0.72, 37.81, 5.26, -0.73,
|
||||||
|
36.42, 14.72, -0.83, 38.16, 14.68, -0.85,
|
||||||
|
20.93, 13.26, -1.33, 21.25, 9.66, -1.15,
|
||||||
|
21.21, 9.75, -1.6 , 22.18, 7.95, -1.19,
|
||||||
|
22.21, 7.94, -1.17, 23.43, 7.82, -1.11,
|
||||||
|
23.38, 7.55, -1.18, 24.42, 9.47, -1.16,
|
||||||
|
24.43, 9.37, -1.25, 24.9 , 11.72, -0.98,
|
||||||
|
24.89, 11.73, -1.2 , 24.68, 14.54, -1.05,
|
||||||
|
24.7 , 14.85, -1.06, 23.85, 16.63, -1.1 ,
|
||||||
|
23.82, 16.53, -1.14, 22.61, 17.14, -1.32,
|
||||||
|
22.65, 17.53, -1.23, 21.5 , 16.19, -1.34,
|
||||||
|
21.49, 15.92, -1.32, 20.95, 13.07, -1.32 };
|
||||||
|
|
||||||
|
int lines_no = sizeof(lines)/sizeof(lines[0])/6;
|
||||||
|
|
||||||
|
compute(true, // lfoot_touch
|
||||||
|
true, // rfoot_touch
|
||||||
|
feet_contact,
|
||||||
|
true, // ball_seen
|
||||||
|
ball_pos,
|
||||||
|
me_pos,
|
||||||
|
landmarks,
|
||||||
|
lines,
|
||||||
|
lines_no);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,182 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include "Geometry.h"
|
||||||
|
#include "Vector3f.h"
|
||||||
|
#include "Matrix4D.h"
|
||||||
|
#include "FieldNoise.h"
|
||||||
|
#include "Line6f.h"
|
||||||
|
#include "World.h"
|
||||||
|
#include "Field.h"
|
||||||
|
#include "LocalizerV2.h"
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/numpy.h>
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
static LocalizerV2& loc = SLocalizerV2::getInstance();
|
||||||
|
|
||||||
|
void print_python_data(){
|
||||||
|
|
||||||
|
static World &world = SWorld::getInstance();
|
||||||
|
|
||||||
|
cout << "Foot touch: " << world.foot_touch[0] << " " << world.foot_touch[1] << endl;
|
||||||
|
cout << "LFoot contact rpos: " << world.foot_contact_rel_pos[0].x << " " << world.foot_contact_rel_pos[0].y << " " << world.foot_contact_rel_pos[0].z << endl;
|
||||||
|
cout << "RFoot contact rpos: " << world.foot_contact_rel_pos[1].x << " " << world.foot_contact_rel_pos[1].y << " " << world.foot_contact_rel_pos[1].z << endl;
|
||||||
|
cout << "Ball seen: " << world.ball_seen << endl;
|
||||||
|
cout << "Ball rpos cart: " << world.ball_rel_pos_cart.x << " " << world.ball_rel_pos_cart.y << " " << world.ball_rel_pos_cart.z << endl;
|
||||||
|
cout << "Ball cheat: " << world.ball_cheat_abs_cart_pos.x << " " << world.ball_cheat_abs_cart_pos.y << " " << world.ball_cheat_abs_cart_pos.z << endl;
|
||||||
|
cout << "Me cheat: " << world.my_cheat_abs_cart_pos.x << " " << world.my_cheat_abs_cart_pos.y << " " << world.my_cheat_abs_cart_pos.z << endl;
|
||||||
|
|
||||||
|
for(int i=0; i<8; i++){
|
||||||
|
cout << "Landmark " << i << ": " <<
|
||||||
|
world.landmark[i].seen << " " <<
|
||||||
|
world.landmark[i].isCorner << " " <<
|
||||||
|
world.landmark[i].pos.x << " " <<
|
||||||
|
world.landmark[i].pos.y << " " <<
|
||||||
|
world.landmark[i].pos.z << " " <<
|
||||||
|
world.landmark[i].rel_pos.x << " " <<
|
||||||
|
world.landmark[i].rel_pos.y << " " <<
|
||||||
|
world.landmark[i].rel_pos.z << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<world.lines_polar.size(); i++){
|
||||||
|
cout << "Line " << i << ": " <<
|
||||||
|
world.lines_polar[i].start.x << " " <<
|
||||||
|
world.lines_polar[i].start.y << " " <<
|
||||||
|
world.lines_polar[i].start.z << " " <<
|
||||||
|
world.lines_polar[i].end.x << " " <<
|
||||||
|
world.lines_polar[i].end.y << " " <<
|
||||||
|
world.lines_polar[i].end.z << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
py::array_t<float> compute(
|
||||||
|
bool lfoot_touch, bool rfoot_touch,
|
||||||
|
py::array_t<double> feet_contact,
|
||||||
|
bool ball_seen, py::array_t<double> ball_pos,
|
||||||
|
py::array_t<double> me_pos,
|
||||||
|
py::array_t<double> landmarks,
|
||||||
|
py::array_t<double> lines){
|
||||||
|
|
||||||
|
// ================================================= 1. Parse data
|
||||||
|
|
||||||
|
static World &world = SWorld::getInstance();
|
||||||
|
world.foot_touch[0] = lfoot_touch;
|
||||||
|
world.foot_touch[1] = rfoot_touch;
|
||||||
|
|
||||||
|
//Structure of feet_contact {lfoot_contact_pt, rfoot_contact_pt, lfoot_contact_rel_pos, rfoot_contact_rel_pos}
|
||||||
|
|
||||||
|
py::buffer_info feet_contact_buf = feet_contact.request();
|
||||||
|
double *feet_contact_ptr = (double *) feet_contact_buf.ptr;
|
||||||
|
world.foot_contact_rel_pos[0].x = feet_contact_ptr[0];
|
||||||
|
world.foot_contact_rel_pos[0].y = feet_contact_ptr[1];
|
||||||
|
world.foot_contact_rel_pos[0].z = feet_contact_ptr[2];
|
||||||
|
world.foot_contact_rel_pos[1].x = feet_contact_ptr[3];
|
||||||
|
world.foot_contact_rel_pos[1].y = feet_contact_ptr[4];
|
||||||
|
world.foot_contact_rel_pos[1].z = feet_contact_ptr[5];
|
||||||
|
|
||||||
|
world.ball_seen = ball_seen;
|
||||||
|
|
||||||
|
//Structure of ball_pos {ball_rel_pos_cart, ball_cheat_abs_cart_pos}
|
||||||
|
|
||||||
|
py::buffer_info ball_pos_buf = ball_pos.request();
|
||||||
|
double *ball_pos_ptr = (double *) ball_pos_buf.ptr;
|
||||||
|
world.ball_rel_pos_cart.x = ball_pos_ptr[0];
|
||||||
|
world.ball_rel_pos_cart.y = ball_pos_ptr[1];
|
||||||
|
world.ball_rel_pos_cart.z = ball_pos_ptr[2];
|
||||||
|
world.ball_cheat_abs_cart_pos.x = ball_pos_ptr[3];
|
||||||
|
world.ball_cheat_abs_cart_pos.y = ball_pos_ptr[4];
|
||||||
|
world.ball_cheat_abs_cart_pos.z = ball_pos_ptr[5];
|
||||||
|
|
||||||
|
py::buffer_info me_pos_buf = me_pos.request();
|
||||||
|
double *me_pos_ptr = (double *) me_pos_buf.ptr;
|
||||||
|
world.my_cheat_abs_cart_pos.x = me_pos_ptr[0];
|
||||||
|
world.my_cheat_abs_cart_pos.y = me_pos_ptr[1];
|
||||||
|
world.my_cheat_abs_cart_pos.z = me_pos_ptr[2];
|
||||||
|
|
||||||
|
py::buffer_info landmarks_buf = landmarks.request();
|
||||||
|
double *landmarks_ptr = (double *) landmarks_buf.ptr;
|
||||||
|
|
||||||
|
for(int i=0; i<8; i++){
|
||||||
|
world.landmark[i].seen = (bool) landmarks_ptr[0];
|
||||||
|
world.landmark[i].isCorner = (bool) landmarks_ptr[1];
|
||||||
|
world.landmark[i].pos.x = landmarks_ptr[2];
|
||||||
|
world.landmark[i].pos.y = landmarks_ptr[3];
|
||||||
|
world.landmark[i].pos.z = landmarks_ptr[4];
|
||||||
|
world.landmark[i].rel_pos.x = landmarks_ptr[5];
|
||||||
|
world.landmark[i].rel_pos.y = landmarks_ptr[6];
|
||||||
|
world.landmark[i].rel_pos.z = landmarks_ptr[7];
|
||||||
|
landmarks_ptr += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
py::buffer_info lines_buf = lines.request();
|
||||||
|
int lines_len = lines_buf.shape[0];
|
||||||
|
double *lines_ptr = (double *) lines_buf.ptr;
|
||||||
|
world.lines_polar.clear();
|
||||||
|
|
||||||
|
for(int i=0; i<lines_len; i++){
|
||||||
|
Vector3f s(lines_ptr[0],lines_ptr[1],lines_ptr[2]);
|
||||||
|
Vector3f e(lines_ptr[3],lines_ptr[4],lines_ptr[5]);
|
||||||
|
world.lines_polar.emplace_back(s, e);
|
||||||
|
lines_ptr += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================= 2. Compute 6D pose
|
||||||
|
|
||||||
|
loc.run();
|
||||||
|
|
||||||
|
// ================================================= 3. Prepare data to return
|
||||||
|
|
||||||
|
py::array_t<float> retval = py::array_t<float>(35); //allocate
|
||||||
|
py::buffer_info buff = retval.request();
|
||||||
|
float *ptr = (float *) buff.ptr;
|
||||||
|
|
||||||
|
for(int i=0; i<16; i++){
|
||||||
|
ptr[i] = loc.headTofieldTransform.content[i];
|
||||||
|
}
|
||||||
|
ptr += 16;
|
||||||
|
for(int i=0; i<16; i++){
|
||||||
|
ptr[i] = loc.fieldToheadTransform.content[i];
|
||||||
|
}
|
||||||
|
ptr += 16;
|
||||||
|
|
||||||
|
ptr[0] = (float) loc.is_uptodate;
|
||||||
|
ptr[1] = loc.head_z;
|
||||||
|
ptr[2] = (float) loc.is_head_z_uptodate;
|
||||||
|
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_report(){
|
||||||
|
loc.print_report();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_visible_elements(bool is_right_side){
|
||||||
|
Field& fd = SField::getInstance();
|
||||||
|
fd.draw_visible(loc.headTofieldTransform, is_right_side);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
using namespace pybind11::literals; //to add informative argument names as -> "argname"_a
|
||||||
|
|
||||||
|
PYBIND11_MODULE(localization, m) { //the python module name, m is the interface to create bindings
|
||||||
|
m.doc() = "Probabilistic 6D localization algorithm"; // optional module docstring
|
||||||
|
|
||||||
|
//optional arguments names
|
||||||
|
m.def("compute", &compute, "Compute the 6D pose based on visual information and return transformation matrices and other relevant data",
|
||||||
|
"lfoot_touch"_a,
|
||||||
|
"rfoot_touch"_a,
|
||||||
|
"feet_contact"_a,
|
||||||
|
"ball_seen"_a,
|
||||||
|
"ball_pos"_a,
|
||||||
|
"me_pos"_a,
|
||||||
|
"landmarks"_a,
|
||||||
|
"lines"_a);
|
||||||
|
|
||||||
|
m.def("print_python_data", &print_python_data, "Print data received from Python");
|
||||||
|
m.def("print_report", &print_report, "Print localization report");
|
||||||
|
m.def("draw_visible_elements", &draw_visible_elements, "Draw all visible elements in RoboViz", "is_right_side"_a);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Justin Stoecker
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ROBOVIZDRAW_H_
|
||||||
|
#define _ROBOVIZDRAW_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
inline int writeCharToBuf(unsigned char* buf, unsigned char value) {
|
||||||
|
*buf = value;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int writeFloatToBuf(unsigned char* buf, float value) {
|
||||||
|
char temp[20];
|
||||||
|
sprintf(temp, "%6f", value);
|
||||||
|
memcpy(buf, temp, 6);
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int writeColorToBuf(unsigned char* buf, const float* color, int channels) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < channels; i++)
|
||||||
|
writeCharToBuf(buf + i, (unsigned char) (color[i]*255));
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int writeStringToBuf(unsigned char* buf, const string* text) {
|
||||||
|
long i = 0;
|
||||||
|
if (text != NULL)
|
||||||
|
i += text->copy((char*) buf + i, text->length(), 0);
|
||||||
|
i += writeCharToBuf(buf + i, 0);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* newBufferSwap(const string* name, int* bufSize) {
|
||||||
|
*bufSize = 3 + ((name != NULL) ? name->length() : 0);
|
||||||
|
unsigned char* buf = new unsigned char[*bufSize];
|
||||||
|
|
||||||
|
long i = 0;
|
||||||
|
i += writeCharToBuf(buf + i, 0);
|
||||||
|
i += writeCharToBuf(buf + i, 0);
|
||||||
|
i += writeStringToBuf(buf + i, name);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* newCircle(const float* center, float radius, float thickness,
|
||||||
|
const float* color, const string* setName, int* bufSize) {
|
||||||
|
|
||||||
|
*bufSize = 30 + ((setName != NULL) ? setName->length() : 0);
|
||||||
|
unsigned char* buf = new unsigned char[*bufSize];
|
||||||
|
|
||||||
|
long i = 0;
|
||||||
|
i += writeCharToBuf(buf + i, 1);
|
||||||
|
i += writeCharToBuf(buf + i, 0);
|
||||||
|
i += writeFloatToBuf(buf + i, center[0]);
|
||||||
|
i += writeFloatToBuf(buf + i, center[1]);
|
||||||
|
i += writeFloatToBuf(buf + i, radius);
|
||||||
|
i += writeFloatToBuf(buf + i, thickness);
|
||||||
|
i += writeColorToBuf(buf + i, color, 3);
|
||||||
|
i += writeStringToBuf(buf + i, setName);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* newLine(const float* a, const float* b, float thickness,
|
||||||
|
const float* color, const string* setName, int* bufSize) {
|
||||||
|
|
||||||
|
*bufSize = 48 + ((setName != NULL) ? setName->length() : 0);
|
||||||
|
unsigned char* buf = new unsigned char[*bufSize];
|
||||||
|
|
||||||
|
long i = 0;
|
||||||
|
i += writeCharToBuf(buf + i, 1);
|
||||||
|
i += writeCharToBuf(buf + i, 1);
|
||||||
|
i += writeFloatToBuf(buf + i, a[0]);
|
||||||
|
i += writeFloatToBuf(buf + i, a[1]);
|
||||||
|
i += writeFloatToBuf(buf + i, a[2]);
|
||||||
|
i += writeFloatToBuf(buf + i, b[0]);
|
||||||
|
i += writeFloatToBuf(buf + i, b[1]);
|
||||||
|
i += writeFloatToBuf(buf + i, b[2]);
|
||||||
|
i += writeFloatToBuf(buf + i, thickness);
|
||||||
|
i += writeColorToBuf(buf + i, color, 3);
|
||||||
|
i += writeStringToBuf(buf + i, setName);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* newPoint(const float* p, float size, const float* color,
|
||||||
|
const string* setName, int* bufSize) {
|
||||||
|
|
||||||
|
*bufSize = 30 + ((setName != NULL) ? setName->length() : 0);
|
||||||
|
unsigned char* buf = new unsigned char[*bufSize];
|
||||||
|
|
||||||
|
long i = 0;
|
||||||
|
i += writeCharToBuf(buf + i, 1);
|
||||||
|
i += writeCharToBuf(buf + i, 2);
|
||||||
|
i += writeFloatToBuf(buf + i, p[0]);
|
||||||
|
i += writeFloatToBuf(buf + i, p[1]);
|
||||||
|
i += writeFloatToBuf(buf + i, p[2]);
|
||||||
|
i += writeFloatToBuf(buf + i, size);
|
||||||
|
i += writeColorToBuf(buf + i, color, 3);
|
||||||
|
i += writeStringToBuf(buf + i, setName);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* newSphere(const float* p, float radius, const float* color,
|
||||||
|
const string* setName, int* bufSize) {
|
||||||
|
|
||||||
|
*bufSize = 30 + ((setName != NULL) ? setName->length() : 0);
|
||||||
|
unsigned char* buf = new unsigned char[*bufSize];
|
||||||
|
|
||||||
|
long i = 0;
|
||||||
|
i += writeCharToBuf(buf + i, 1);
|
||||||
|
i += writeCharToBuf(buf + i, 3);
|
||||||
|
i += writeFloatToBuf(buf + i, p[0]);
|
||||||
|
i += writeFloatToBuf(buf + i, p[1]);
|
||||||
|
i += writeFloatToBuf(buf + i, p[2]);
|
||||||
|
i += writeFloatToBuf(buf + i, radius);
|
||||||
|
i += writeColorToBuf(buf + i, color, 3);
|
||||||
|
i += writeStringToBuf(buf + i, setName);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* newPolygon(const float* v, int numVerts, const float* color,
|
||||||
|
const string* setName, int* bufSize) {
|
||||||
|
|
||||||
|
*bufSize = 18 * numVerts + 8 + ((setName != NULL) ? setName->length() : 0);
|
||||||
|
unsigned char* buf = new unsigned char[*bufSize];
|
||||||
|
|
||||||
|
long i = 0;
|
||||||
|
i += writeCharToBuf(buf + i, 1);
|
||||||
|
i += writeCharToBuf(buf + i, 4);
|
||||||
|
i += writeCharToBuf(buf + i, numVerts);
|
||||||
|
i += writeColorToBuf(buf + i, color, 4);
|
||||||
|
|
||||||
|
for (int j = 0; j < numVerts; j++) {
|
||||||
|
i += writeFloatToBuf(buf + i, v[j * 3 + 0]);
|
||||||
|
i += writeFloatToBuf(buf + i, v[j * 3 + 1]);
|
||||||
|
i += writeFloatToBuf(buf + i, v[j * 3 + 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += writeStringToBuf(buf + i, setName);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* newAnnotation(const string* text, const float* p,
|
||||||
|
const float* color, const string* setName, int* bufSize) {
|
||||||
|
|
||||||
|
*bufSize = 25 + text->length() + ((setName != NULL) ? setName->length() : 0);
|
||||||
|
unsigned char* buf = new unsigned char[*bufSize];
|
||||||
|
|
||||||
|
long i = 0;
|
||||||
|
i += writeCharToBuf(buf + i, 2);
|
||||||
|
i += writeCharToBuf(buf + i, 0);
|
||||||
|
i += writeFloatToBuf(buf + i, p[0]);
|
||||||
|
i += writeFloatToBuf(buf + i, p[1]);
|
||||||
|
i += writeFloatToBuf(buf + i, p[2]);
|
||||||
|
i += writeColorToBuf(buf + i, color, 3);
|
||||||
|
i += writeStringToBuf(buf + i, text);
|
||||||
|
i += writeStringToBuf(buf + i, setName);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* newAgentAnnotation(const string* text, bool leftTeam,
|
||||||
|
int agentNum, const float* color, int* bufSize) {
|
||||||
|
|
||||||
|
*bufSize = (text == NULL) ? 3 : 7 + text->length();
|
||||||
|
unsigned char* buf = new unsigned char[*bufSize];
|
||||||
|
|
||||||
|
long i = 0;
|
||||||
|
i += writeCharToBuf(buf + i, 2);
|
||||||
|
|
||||||
|
if (text == NULL) {
|
||||||
|
i += writeCharToBuf(buf + i, 2);
|
||||||
|
i += writeCharToBuf(buf + i, (leftTeam ? agentNum - 1 : agentNum + 127));
|
||||||
|
} else {
|
||||||
|
i += writeCharToBuf(buf + i, 1);
|
||||||
|
i += writeCharToBuf(buf + i, (leftTeam ? agentNum - 1 : agentNum + 127));
|
||||||
|
i += writeColorToBuf(buf + i, color, 3);
|
||||||
|
i += writeStringToBuf(buf + i, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,30 @@
|
|||||||
|
from agent.Base_Agent import Base_Agent as Agent
|
||||||
|
from math_ops.Math_Ops import Math_Ops as M
|
||||||
|
from scripts.commons.Script import Script
|
||||||
|
|
||||||
|
script = Script()
|
||||||
|
a = script.args
|
||||||
|
|
||||||
|
# Args: Server IP, Agent Port, Monitor Port, Uniform No., Robot Type, Team Name
|
||||||
|
player = Agent(a.i, a.p, a.m, a.u, a.r, a.t, enable_draw=True)
|
||||||
|
w = player.world
|
||||||
|
player.scom.unofficial_beam((-3,0,w.robot.beam_height), 0)
|
||||||
|
getting_up = False
|
||||||
|
while True:
|
||||||
|
player_2d = w.robot.loc_head_position[:2]
|
||||||
|
ball_2d = w.ball_abs_pos[:2]
|
||||||
|
goal_dir = M.vector_angle( (15,0)-player_2d ) # Goal direction
|
||||||
|
if player.behavior.is_ready("Get_Up") or getting_up:
|
||||||
|
getting_up = not player.behavior.execute("Get_Up") # True on completion
|
||||||
|
else:
|
||||||
|
if ball_2d[0] > 0: # kick if ball is on opponent's side (x>0)
|
||||||
|
player.behavior.execute("Basic_Kick", goal_dir)
|
||||||
|
elif M.distance_point_to_segment(player_2d,ball_2d, ball_2d+ M.normalize_vec( ball_2d-(15,0) ) ) > 0.1: # not aligned
|
||||||
|
next_pos, next_ori, dist = player.path_manager.get_path_to_ball(x_ori=goal_dir, x_dev=-0.3, torso_ori=goal_dir)
|
||||||
|
player.behavior.execute("Walk", next_pos, True, next_ori, True, dist)
|
||||||
|
else: # Robot is aligned
|
||||||
|
player.behavior.execute("Walk", (15,0), True, goal_dir, True, 0.5)
|
||||||
|
player.scom.commit_and_send( w.robot.get_command() )
|
||||||
|
player.scom.receive()
|
||||||
|
w.draw.annotation((*player_2d,0.6),"Hello!",w.draw.Color.white,"my_info",flush=False)
|
||||||
|
w.draw.line(player_2d, ball_2d, 3, w.draw.Color.yellow, "my_info", flush=True)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue