Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WB-Toolbox 3.0 #56

Merged
merged 89 commits into from
Jan 25, 2018
Merged

WB-Toolbox 3.0 #56

merged 89 commits into from
Jan 25, 2018

Conversation

diegoferigo
Copy link
Member

@diegoferigo diegoferigo commented Sep 11, 2017

Refer to #46 and this milestone for the task list.

This PR:

[WIP]

  • Update all the Simulink Masks
  • WBTPIDConfig still misses a get method returning a struct
  • Move other matlab scripts into the +WBToolbox package
  • Somewhere std::vector::assign() is used on non non-contiguous arrays, double check all blocks
  • Convert all remaining raw pointers to smart pointers
  • Improve doxygen documentation
  • Check if rad2deg is required in SetReferences.cpp
  • Double check buffered vector in YarpRead.cpp
  • Remove the wbt:: namespace nesting in the cpp files
  • Add warnings messages. Right now this new feature is still rarely used
  • Evaluate if removing Eigen from headers
  • Write a Changelog
  • Update README.md
  • Write documentation

iDynTree::Vector3* m_gravity;
iDynTree::Transform* m_world_T_base;
iDynTree::VectorDynSize* m_jointsVelocity;
iDynTree::VectorDynSize* m_jointsState;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In mechanical systems, "state" is tipically used to indicate position and velocity, so I would call this member m_jointsPosition or something similar.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't like the previous word configuration and I switched to State. Position is even more clear 👍

Copy link
Member

@traversaro traversaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it would make sense to add an explicitly call to setFrameVelocityRepresentation to make sure that the representation is the intended one?

class Twist;
class Transform;
class VectorDynSize;
typedef int FrameIndex;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the concern of avoiding too many includes to reduce compilation times, but I think redefining a typedef like this, as it will create problems if we have change the definition of FrameIndex.
However, if you prefer it like this no problem for me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to keep as much as possible the same behavior as the current state, and I agree with you on these forward declaration. For VectorFixSize I had to add the header since redefining templates was a bit too much imo, but applying this line to all the typedefs would make sense too

m_jointsVelocity = new double[dofs];

return m_basePose && m_centroidalMomentum && m_basePoseRaw && m_configuration && m_baseVelocity && m_jointsVelocity;
double m_gravityRaw[3] {0, 0, -9.81};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know where this is set at the moment, but for different reasons it could make sense to have this configurable at configure time/runtime eventually. A classical example is a fixed base robot mounted on a table, if the gravity acceleration of the table can be set from the outside it is trivial to compensate eventual skewness of the table.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far the only block with an optional input for gravity is InverseDynamics. So far I tried to maintain the current functionalities, without the need of editing the Simulink's masks. I'll add this in a future TODO list

}

else {
m_frameIndex = COM_LINK_ID;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The KinDynComputations class has separate methods to compute the COM position/jacobian/bias term. Passing a m_frameIndex of -1 (that is equivalent to iDynTree::FRAME_INVALID_INDEX` will result in a runtime error.

Copy link
Member Author

@diegoferigo diegoferigo Sep 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@traversaro In the output() method of all the blocks that use COM_LINK_ID the correct iDynTree's method for CoM is called. Since all integers <0 are treated as FRAME_INVALID_INDEX by iDynTree, do you have any suggestion on how to handle this case with a viarable?

I mean, when m_frameIndex = -1 and the frame is com, an error is thrown. If m_frameIndex = -1 and the frame is not com, the block goes further to the output() and the COM_LINK_ID is used to to select the alternative method for computations on CoM.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I missed the check on COM_LINK_ID . Everything should work fine indeed, but personally I would prefer to have a separated boolean variable to save the information "is this block computing COM-related stuff", rather than overloading the meaning of m_frameIndex . However, this is mostly a personal taste, so feel free to use the pattern that you prefer. The only advice that I would recommend is to define COM_LINK_ID to any negative number different from -1 , as -1 is exactly the value of iDynTree::FRAME_INVALID_INDEX.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the boolean variable idea. It is the most safe and clean in the long run

// OUTPUT
// ======

// Compute the dot{J}*dot{q}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically prefer \nu over dot{q} .

success = success && blockInfo->setInputPortMatrixSize(0, 4, 4);
success = success && blockInfo->setInputPortVectorSize(1, dofs);
success &= blockInfo->setInputPortMatrixSize(INPUT_IDX_BASE_POSE, 4, 4);
success &= blockInfo->setInputPortVectorSize(INPUT_IDX_JOINTCONF, dofs);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a bit fan of the use of bit field operations to represent compactly boolean operations, but I know it works so if for you it is ok it is ok also for me. : )

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found the previous version redundant, if @francesco-romano has no comments on this I'd like to use the new one.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

honestly I am against the use of &=

// =======================
model->setRobotState(*m_world_T_base,
*m_jointsState,
*m_baseVelocity,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default for performance reason iDynTree::VectorFixSize objects are not initialized to any value, so this call will effectively use not initialized memory. I suggest to explicitly add a call to m_baseVelocity->zero() in the initialization method to all the vector that you expect to be set to zero.
The VectorDynSize are indeed zeroed during initialization, but I suggest to add an explicit to zero() also for them for consistency and protect you from possible future changes in iDynTree .

@diegoferigo
Copy link
Member Author

Do you think it would make sense to add an explicitly call to setFrameVelocityRepresentation to make sure that the representation is the intended one?

@traversaro Can you please give me some further detail on this? What representation are we using inside the Toolbox? I'm a bit confused on this.

SetReferences subList parameter was erroneously converted to a boolean, while it is actually the index of the selected item in the Simulink mask dropdown.
Being the index >=1 the boolean conversion resulted always in a true value.

Fix robotology#57
Note: actually the std::cerr are warnings for the user, this is why they are not returned as an error.
We have to find a smarter way to return the warning to the user.
@diegoferigo
Copy link
Member Author

diegoferigo commented Nov 8, 2017

@francesco-romano @traversaro
First draft of the WB-Toolbox 3.0 pushed. For the time being only C++, ready for the first round of review.

* @param [out] option implementation-specific block option
* @return true if the option has been converted. False otherwise
* @param [in] key identifier of the block option
* @param [in] option implementation-specific block option
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an in or out parameter?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An out parameter, I don't even remember why I changed this

@francesco-romano
Copy link
Collaborator

I think we use Mixed representation, am I right @traversaro ?

Copy link
Collaborator

@francesco-romano francesco-romano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed F2F I reviewed until 0e99c6f (included)


properties
% Robot state
RobotName (1,:) char
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why capital case letters?

Is this a MATLAB convention?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

% Dependent properties
% --------------------

function set.ValidConfiguration(~,~)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not know anything of MATLAB classes but, isn't possible to have "read only" properties, i.e. only public getter?
This would avoid to declare a fake set method

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValidConfiguration is a Dependent property, so by default even without this set method an error is generated. Specifying this method gives the possibility to provide a custom error message, and in this case it is used exactly for this reason.

~isempty(obj.ControlBoardsNames) && ...
~isempty(obj.LocalName) && ...
~isequal(obj.GravityVector,[0, 0, 0]);
% Check if the urdf file has the right extension
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure that the extension is actually needed. I would drop this requirement.
It is then iDynTree that will validate the URDF file.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's be generic then

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(GNUInstallDirs)
option(BUILD_SHARED_LIBS OFF)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is not needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed


# Find Eigen
find_package(Eigen3 REQUIRED)
target_include_directories(${VARS_PREFIX} PUBLIC "${EIGEN3_INCLUDE_DIR}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, if Eigen is used only in cpp, this can have a PRIVATE include

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping @traversaro. Has Eigen been fixed on Windows when used in headers? Any comment / alternatives on this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "been fixed"? And how this is related to PRIVATE/PUBLIC?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@traversaro the actual question is: Eigen members in header files.
I know there were issues with exported symbols in Windows. Furthermore, I do not like them in header files (as they force you to have Eigen)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replied in #56 (comment) .

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final solution #56 (comment)

{
if (isConstPort) return;
unsigned dataSize = sizeof(bool);
const void* address = static_cast<const bool*>(data) + startIndex;

memcpy(contiguousData, address, dataSize * length);
memcpy(contiguousData, address, dataSize* length);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as before

// ========================

ToolboxSingleton();
ToolboxSingleton(const ToolboxSingleton&);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is a singleton both copy and assignment should be prohibited

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used = delete

* @return A \c shared_ptr to the iDynTree::KinDynComputations of the requested
* configuration
*/
const std::shared_ptr<iDynTree::KinDynComputations> model(const std::string& confKey);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent with the previous two methods I would rename this into getModel

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was an inheritance of the previous version, changed the name. This method might even be removed, it's never called. Right now the best way to gather the model is asking it directly from the parent WBBlock using getRobotInterface()->getKinDynComputations(). This allows the block not to know the existence of the confKey variable.

* @return Returns \c true if configure is successful, \c false otherwise
* @see ToolboxSingleton::isKeyValid
*/
bool configure(const std::string& confKey, const Configuration& config);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as before, I do not like the name configure

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to storeConfiguration()

// This may cause problems if the config block's mask is changed after the first compilation.
if (m_interfaces.find(confKey) == m_interfaces.end()) {
m_interfaces[confKey] = std::make_shared<RobotInterface>(config);
return m_interfaces[confKey].get();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment applies also to other uses of .get().
shared_ptr have an operator bool. This means that you can directly return return m_interfaces[confKey] to assess validity of the pointer.

Change this line and the other times you use get() as a boolean check

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conversion from shared_ptr to bool is explicit. An alternative to get() would be static_cast<bool>. Using this latter version.

This class contains and validates the configuration passed to every wbt::WBBlock object.

ValidConfiguration is a Dependent property and it is set to 1 only if the basic checks currently implemented in other properties don't fail. From this state, it is possible to call getSimulinkParameters() in order to obtain the struct which is passed to the S-Functions and then parsed in C++.

This class also contains utilities functions for obtainng a simple string representation of 1D vectors and 1D chars, required to serialize the data in the Config Block's mask. The deserialization is performed using eval().
Initial version of the library, which will become independent in the future.

MxAnyType handles the C / C++ mxArray pointer that represents any generic data type in Matlab. It wraps most of the C APIs, and it has optional support of strict type checking.
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(GNUInstallDirs)
option(BUILD_SHARED_LIBS OFF)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable only make sense if you are building shared libraries.

Copy link
Member Author

@diegoferigo diegoferigo Nov 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in the future this will become a shared library. For the time being it is static, but just for convenience.

wbt::Log is the new class that provides log support to the WBToolbox.
It now supports both Errors and Warnings.

For being more flexible, wbt::Log is a singleton. This approach simplifies dramatically the handling of warnings, and can be called from every method without the need of passing it though arguments. Since singletons cannot be deleted, wbt::Log state is reset in the early stage of the processing.

[Squash] toolbox.cpp
Its functionality are implemented by the new generic WBBlock, which is a refactored version of WBIBlock that access robot resources
in a lazy way. This approach allows avoiding the previous separation between WBIBlock and WBIModelBlock.
This class contains the data passed from Simulink.

It matches the data structure of Matlab's WBToolboxConfig.getSimulinkParameters() struct.
This class contains all the required data structures to operate on a robot, either through the model or the interfaces to the hardware / gazebo.

From the Simulink point of view, there is one instance of RobotInterface for every Configuration block added in the model. In fact, RobotInterface extends by composition the Configuration object.

It handles the pointers to its members in a lazy way, initializing on the fly the asked resource.
This is a temporary fix. The cpp file should be generated by its thrift source, which should be found already installed in the system. A new CMake option e.g. USE_GAZEBO should be added.
This commit fixes also some error in computing the address used in memcpy calls
Furthermore:

- All methods that read parameters return a bool value
- optionFromKey doesn't use anymore wbt::Data
- setNumberOfOuputPorts() became setNumberOfOutputPorts()
This new WBBlock implements the same functionalities of the former WBIBlock and WBIModelBlock blocks.
Resources are lazy-handled, and it is not anymore necessary having two different setups.

Furthermore:

- The parameters from Simulink are stored in the ToolboxSingleton in the configureSizeAndPorts() instead
  of initialize(). This is possible because singletons are not deleted by Simulink between these two steps
  (Blocks instead are deleted).
- Removed old Error parameter from methods
- Small documentation changes
- Small style edits
The new ToolboxSingleton class contains in a persistent wat Configuration and RobotInterface objects. Most of the logic previously contained in WBInterface has been moved in these two new classes.
It is still not clear what is the Matlab version which introduced this feature. For now I am reverting it, addressing robotology#66.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants