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

IMU with Motion topic #11990

Merged
merged 26 commits into from
Jul 18, 2023
Merged

IMU with Motion topic #11990

merged 26 commits into from
Jul 18, 2023

Conversation

maloel
Copy link
Collaborator

@maloel maloel commented Jul 11, 2023

IMU DDS implementation is now combined into a single topic:

  • "Motion" topic has the ROS2 sensor_msgs::msg::Imu format, including orientation, linear acceleration (Accel), and angular velocity (Gyro) in one message
  • No metadata is sent for Motion messages
  • Currently, the implementation is similar to the ROS2 wrapper's unite_imu_method value 1 (copy) - added LRS-834 for interpolation
  • In LibRS, there is now a RS2_STREAM_MOTION in format RS2_FORMAT_COMBINED_MOTION stream

Along the way:

  • No more fisheye, pose, or the individual accel/gyro streams
  • Moved format out of general profiles and into video profiles (so no longer for motion)
  • Reduced device init timeout from 30 seconds to 5
  • Updated docs

Tracked on [LRS-796]

@maloel maloel requested a review from OhadMeir July 11, 2023 15:01
@maloel maloel marked this pull request as ready for review July 11, 2023 15:53
@@ -32,9 +32,9 @@ public enum StreamFormat {
Z16H(28),
FG(29),
Y411(30),
Y16I(31);
COMBINED_MOTION(31);
Copy link
Contributor

Choose a reason for hiding this comment

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

Was there a Y16I format at some point?
If changing this file, I think that Z16H is unused anymore

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Was there a Y16I format at some point?

There was (though I don't think it was released) -- and then was removed. The rs2_stream enum no longer has it, so I made sure this reflected the current state of the enum.

We cannot go and remove the old enum values (which were released).

imu_data().orientation_covariance() = { -1., 0., 0., 0., 0., 0., 0., 0., 0. };
// "you can initiate the sensor covariance matrix by giving some small values on the diagonal"
double const cov = 0.01; // TODO from context?
imu_data().linear_acceleration_covariance() = { cov, 0., 0., 0., cov, 0., 0., 0., cov };
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't the first element be set to -1 (as in orientation?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We do have linear acceleration (accel) and angular velocity (gyro).
This is a copy of the code from the ROS wrapper.

data.frame_number; // filled in only once metadata is known
data.timestamp // in ms
= static_cast< rs2_time_t >( realdds::time_to_double( imu.timestamp() ) * 1e3 );
data.timestamp_domain = RS2_TIMESTAMP_DOMAIN_GLOBAL_TIME; // leave default (hardware domain)
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment about HW domain does not match actual "global time" value

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'll take out the "global time" setting and leave at default. I changed it when trying stuff.

Bottom line:
Camera-generated domains will always be HW times and, when global time is enable, converted.
Adapter-sent domains may already be global (from the adapter's side) but will be treated as HW meaning may get converted to the client's global time.

if( video_stream )
{
auto video_profile = std::static_pointer_cast< realdds::dds_video_stream_profile >( profile );
auto added_stream_profile = sensor.add_video_stream(
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not call this raw_stream_profile? And the comment can apply to both motion and video

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK, changing

sid_index type_and_index( profile->get_stream_type(), profile->get_stream_index());
if( auto p = std::dynamic_pointer_cast< librealsense::video_stream_profile_interface >( profile ) )
{
LOG_DEBUG( " " << get_string( p->get_stream_type() ) << ' ' << p->get_stream_index() << ' '
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that framerate should follow the resolution and format after.
E.g. 1280x720@30 RGB8 (instead of 1280x720 RGB8 @ 30)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK :)

}

server->init_profiles( profiles, default_profile_index );

// Set stream intrinsics
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove this comment, it is now redundant and does not relate to metadata handling in this if statement

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed.

{
video_server->set_intrinsics( std::move( stream_name_to_video_intrinsics[stream_name] ) );
// Set stream metadata support (currently if the device supports metadata all streams does)
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment correct to video streams only

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah but it's inside a block only for video streams... You want me to change?

stream_profiles.end(),
[&]( const rs2::stream_profile & sp )
{
switch( sp.stream_type() )
Copy link
Contributor

Choose a reason for hiding this comment

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

Why switch and not if

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There were other cases, maybe? Changed to if.

if( auto video = std::dynamic_pointer_cast< realdds::dds_video_stream_server >( server ) )
{
// RS2_STREAM_DEPTH, RS2_STREAM_COLOR, RS2_STREAM_INFRARED
Copy link
Contributor

Choose a reason for hiding this comment

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

Confidence?
I think that this comment is redundant. Same for the motion comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed

}
{
// RS2_STREAM_GYRO, RS2_STREAM_ACCEL
static realdds::topics::imu_msg imu;
Copy link
Contributor

Choose a reason for hiding this comment

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

static variables here can be members.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah but then you have to #include the imu message, etc.
I chose to split the start() call into two parts and pass the IMU "context" (message plus mutex) in.

auto const stream_type = stream_profile.stream_type();
dds_time const timestamp // in sec.nsec
( static_cast< long double >( f.get_timestamp() ) / 1e3 );

if( auto video = std::dynamic_pointer_cast< realdds::dds_video_stream_server >( server ) )
Copy link
Contributor

Choose a reason for hiding this comment

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

Please move this logic to a helper function.
Let's keep out lamdas small :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Easier said than done. Right now I left it the same (though changed -- check it out).

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like all you need to pass the helper function is a reference to sensor

{
if( sp.stream_type() != RS2_STREAM_ACCEL )
continue;
if( sp.format() != rs2_profile.format() )
Copy link
Contributor

Choose a reason for hiding this comment

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

Is format check necessary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Probably not...


#### Metadata

A single metadata message is expected for all messages from the same sensor (so one for depth, IR left, IR right).
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that we send a metadata message per active stream, not per active sensor

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Right now, yes, you're correct. And after talking to Evgeni, I think I'll take that statement back.

@@ -464,6 +456,28 @@ find_profile( std::shared_ptr< realdds::dds_stream_server > const & stream,
}


std::shared_ptr< realdds::dds_stream_server >
lrs_device_controller::frame_to_streaming_server( rs2::frame const & f, rs2::stream_profile * p_profile ) const
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need p_profile and all this code handling it? Just to avoid calling f.get_profile() outside this function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Well, yes -- it's actually two calls into librealsense and populations of data. Why do something twice?

Copy link
Contributor

Choose a reason for hiding this comment

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

Much less readable, not relevant for all cases and I am not sure if it more efficient...
Had to read several times to understand all this handling with profile/p_profile/profile_.
Passing another parameter, creating a default initialized profile that is not always needed, if statement... just to avoid an API call...

std::mutex mutex;
};
sensor.start(
[this, imu = std::make_shared< imu_context >()]( rs2::frame f )
Copy link
Contributor

Choose a reason for hiding this comment

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

When called from two different threads imu will be the same object for both? Isn't another instance is created?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, there should be a new make_shared each time a sensor is started.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I.e., when the on_start_sensor lambda is called.

auto xyz = reinterpret_cast< float const * >( f.get_data() );
if( RS2_STREAM_ACCEL == stream_profile.stream_type() )
{
std::unique_lock< std::mutex > lock( imu->mutex );
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that std::lock_guard is more efficient

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Umm, yes, I don't know how it got there. But essentially the same, maybe slightly more overhead. I'll fix if other changes are needed.

Copy link
Contributor

@OhadMeir OhadMeir left a comment

Choose a reason for hiding this comment

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

LGTM

@maloel maloel merged commit 97f9b0e into IntelRealSense:dds Jul 18, 2023
15 checks passed
@maloel maloel deleted the dds branch July 18, 2023 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants