Orientation Tracking Setup

OpenSim Moco is a software toolkit to solve optimal control problems with musculoskeletal models defined in OpenSim using the direct collocation method.
User avatar
Nicholas Vandenberg
Posts: 71
Joined: Wed Jan 20, 2021 12:47 pm

Re: Orientation Tracking Setup

Post by Nicholas Vandenberg » Tue Sep 05, 2023 7:48 am

Yeah, editing by hand never goes well. I tried using the API in my conversion code to fix the header, but I can't load it into a TimeSeriesTable when I specify a DataType in the header. I was able to load it into a table with the old header and wrote it to an .sto before.

I may just have to return to that 'older' version of my IMU conversion code, or maybe integrate the TimeSeriesTable export/edit into it?

User avatar
Nicholas Bianco
Posts: 1041
Joined: Thu Oct 04, 2012 8:09 pm

Re: Orientation Tracking Setup

Post by Nicholas Bianco » Tue Sep 05, 2023 11:24 am

Hi Nick,

At this point, to save you more pain, we should just construct your table manually through the Matlab API. I wrote some sample code to help get you started, let me know if you have any questions:

Code: Select all

import org.opensim.modeling.*;

% Create time vector
time = linspace(0, 2, 101);
timeVec = StdVectorDouble();
for i = 1:length(time) 
    timeVec.add(time(i));
end

% Create an empty table
table = TimeSeriesTableQuaternion(timeVec);

% Set one column (wrap in loop for more columns)
col = VectorQuaternion(length(time), Quaternion(0.0, 0.0, 0.0, 0.0));
for i = 1:length(time) 
    % Fill in your actual data here
    quat = Quaternion(1.0, 2.0, 3.0, 4.0); 
    col.set(i-1, quat);
end
table.appendColumn('quat1', col);

% From here, 2 options:

% 1. Write the table as-is
stoq = STOFileAdapterQuaternion();
stoq.write(table, 'table.sto');

% OR

% 2. Flatten the table and then write
tableFlat = TimeSeriesTable(table.flatten()); % flatten() returns a DataTable, but we need a TimeSeriesTable
sto = STOFileAdapter();
sto.write(tableFlat, 'tableFlat.sto');

User avatar
Nicholas Vandenberg
Posts: 71
Joined: Wed Jan 20, 2021 12:47 pm

Re: Orientation Tracking Setup

Post by Nicholas Vandenberg » Wed Sep 20, 2023 4:47 pm

Hi Nick, apologies for the slow response.

I wanted to thank you again for your help with this so far, but also wanted to pivot a bit because I think I may have found a simpler solution (that will require a similar process) and I was hoping to get your perspective. I was looking back through our data from Xsens and saw that it's possible to export in a different format, I'm trying an XLSX vs the MVNX which I can now load directly into matlab without needing any additional conversion codes. I then have this big beautiful structure:
Xsens_Data_Headers.jpg
Xsens_Data_Headers.jpg (71.6 KiB) Viewed 1685 times
I was initially thinking I could directly pull the quaternion data from here, but seeing these different headers, I'm wondering if I can instead define the angular pelvis DOFs from this 'SegmentOrientation0x2DEuler'. If I'm able to pull angular data to fill a table (even if it requires a conversion) as opposed to quaternion, I'm wondering can I create a blank states trajectory that I fill individual columns with from sections of this data structure? Is that possible? I'm not as savvy with using the API as I'd like to be but I feel like this doable, even if it needs to instead be a TimeSeriesTable -> STOFileAdapter. I appreciate whatever insight you've got.

Thanks again,
Nick

User avatar
Aaron Fox
Posts: 288
Joined: Sun Aug 06, 2017 10:54 pm

Re: Orientation Tracking Setup

Post by Aaron Fox » Wed Sep 20, 2023 4:55 pm

Hi Nicholas,

Thought I'd quickly chime in here just to flag that building a table like this is definitely possible. The approach is somewhat counterintuitive to me, as I think the best way is to build it row-by-row rather than column-by-column. I think the best example you could find is under the Matlab > Utilities folder that comes with OpenSIm install resources (in the code folder), there is a function osimTableFromStruct. I've used this as an example on a few occasions to build a table from Matlab/Python data.

Aaron

User avatar
Nicholas Vandenberg
Posts: 71
Joined: Wed Jan 20, 2021 12:47 pm

Re: Orientation Tracking Setup

Post by Nicholas Vandenberg » Thu Sep 21, 2023 9:35 am

Hi Aaron,

That's good to know, and thanks for the suggestion I'll definitely start there. I'm wondering if you could clarify why row-by-row vs col-by-col? My understanding is that I have way more data than I actually need in the structure as it is, so I'd be building a single table that is pulling data from the various sections to fill the different DOFs. Is this incorrect?

Best,
Nick

User avatar
Nicholas Vandenberg
Posts: 71
Joined: Wed Jan 20, 2021 12:47 pm

Re: Orientation Tracking Setup

Post by Nicholas Vandenberg » Fri Sep 22, 2023 12:21 pm

Quick update here. I was able to figure out how to pull the DOFs I wanted and construct an .sto to use as my input file. I put the OrientationTracking on the backburner for the moment in lieu of pulling SegmentOrientations for the pelvis angular kinematics. I appreciate the help with this though!

Thanks,
Nick

User avatar
Nicholas Bianco
Posts: 1041
Joined: Thu Oct 04, 2012 8:09 pm

Re: Orientation Tracking Setup

Post by Nicholas Bianco » Wed Sep 27, 2023 1:24 pm

Hi Nick,

I know you have a decent alternative working, but I'd like to help you figure out the orientation tracking problem if it can be valuable to you.
I'm wondering if you could clarify why row-by-row vs col-by-col?
If you look at the code snippet I posted above, that is a column-by-column since I used "table.appendColumn(col);" to build the table, where "col" is a SimTK::Vector. You can also build a table row-by-row using "table.appendRow(time, row)" where "time" is the time value (not index) and row is a SimTK::RowVector. Note that if you're not working with scalar data (e.g., Vec3's) then you'll need other vector types (e.g., SimTK::VectorVec3 and SimTK::RowVectorVec3).
My understanding is that I have way more data than I actually need in the structure as it is, so I'd be building a single table that is pulling data from the various sections to fill the different DOFs.
Could you describe what you were hoping to accomplish with the orientation tracking problem again? You might have everything you need, but I'm starting to get fuzzy on the details.

Best,
Nick

User avatar
Nicholas Vandenberg
Posts: 71
Joined: Wed Jan 20, 2021 12:47 pm

Re: Orientation Tracking Setup

Post by Nicholas Vandenberg » Thu Sep 28, 2023 4:19 pm

Hey Nick,

Thanks for clarifying the difference and for sharing that bit of code, I used that snippet that you shared to build mine column-by-column. With the organization of the structure that I had, appending the columns that I needed seemed to be the most straightforward way.

My original plan with the pelvis orientation tracking goal was to pull the quaternion data for the segment orientation defining the pelvis in the Xsens output, then use that as a rotation input for the MocoOrientationTrackingGoal. I've had a success with pulling the Euler segment orientations for a tracking problem but I don't think it would hurt to have this working alongside that tracking (it's not a super accurate representation of the angular DOFs at the pelvis, especially considering patients with TFA). I haven't tried it yet but I believe that I'd be able to make an orientations quat.sto using that snippet you shared again, and drive the goal with that?

Thanks,
Nick

User avatar
Nicholas Bianco
Posts: 1041
Joined: Thu Oct 04, 2012 8:09 pm

Re: Orientation Tracking Setup

Post by Nicholas Bianco » Mon Oct 02, 2023 9:41 am

but I don't think it would hurt to have this working alongside that tracking (it's not a super accurate representation of the angular DOFs at the pelvis, especially considering patients with TFA). I haven't tried it yet but I believe that I'd be able to make an orientations quat.sto using that snippet you shared again, and drive the goal with that?
Yes, you absolutely can. In theory, there's no harm in having both tracking goals in the same problem, but you might have to experiment with weights to find the best convergence.

User avatar
Nicholas Vandenberg
Posts: 71
Joined: Wed Jan 20, 2021 12:47 pm

Re: Orientation Tracking Setup

Post by Nicholas Vandenberg » Mon Dec 11, 2023 1:30 pm

I got busy with other stuff and only came back to this orientation tracking goal recently, I was hoping to get another set of eyes on. I've used the previous code snippet to create a quaternion table for the pelvis segment

Code: Select all

Pel_Quat0 = Xsens.data.SegmentOrientation0x2DQuat(sel_win(1):sel_win(2),2);
Pel_Quat1 = Xsens.data.SegmentOrientation0x2DQuat(sel_win(1):sel_win(2),3);
Pel_Quat2 = Xsens.data.SegmentOrientation0x2DQuat(sel_win(1):sel_win(2),4);
Pel_Quat3 = Xsens.data.SegmentOrientation0x2DQuat(sel_win(1):sel_win(2),5);
% grabs a selection of the walking trial

% Create empty quaternion table
timeVec2 = StdVectorDouble();
for i = 1:length(time) 
    timeVec2.add(time(i));
end
Quat_Table = TimeSeriesTableQuaternion(timeVec2);

% Create pelvis quaternion columns and append to table
col = VectorQuaternion(length(time), Quaternion(0.0, 0.0, 0.0, 0.0));
for i = 1:length(time)
    quat = Quaternion(Pel_Quat0(i), Pel_Quat1(i), Pel_Quat2(i), Pel_Quat3(i));
    col.set(i-1, quat);
end
Quat_Table.appendColumn('Pelvis',col);

stoq = STOFileAdapterQuaternion();
stoq.write(Quat_Table, 'TFAB_02_SSWlk08_Pelvis_Quat.sto');
I'm then trying to bring it in for the orientation tracking code in my problem like this:

Code: Select all

% Track pelvis orientations from Xsens using quaternion rotations
pelvisGoal = MocoOrientationTrackingGoal('pelvisGoal',20);
pelvisTable = TimeSeriesTable('TFAB_02_SSWlk08_Pelvis_Quat.sto');
pelvisTableQuat = pelvisTable.packQuaternion();
pelvisGoal.setRotationReference(pelvisTableQuat);
pel_paths = StdVectorString();
pel_paths.add('/bodyset/pelvis');
pelvisGoal.setFramePaths(pel_paths);
problem.addGoal(pelvisGoal);
as well as directly using pelvisGoal.setRotationReferenceFile('TFAB_02_SSWlk08_Pelvis_Quat.sto');
but I'm getting the following error:
POT_err.png
POT_err.png (10.67 KiB) Viewed 1539 times
Not sure what I'm missing?

Best,
Nick

POST REPLY