Skip to content

RSDK-13982 - construct model table from urdf#645

Merged
nfranczak merged 23 commits into
viamrobotics:mainfrom
nfranczak:20250514-RSDK-13982-construct-model-table
Jun 3, 2026
Merged

RSDK-13982 - construct model table from urdf#645
nfranczak merged 23 commits into
viamrobotics:mainfrom
nfranczak:20250514-RSDK-13982-construct-model-table

Conversation

@nfranczak
Copy link
Copy Markdown
Member

@nfranczak nfranczak commented May 20, 2026

as follow up will do sva

@nfranczak nfranczak requested a review from a team as a code owner May 20, 2026 15:41
@nfranczak nfranczak requested review from lia-viam and njooma and removed request for a team May 20, 2026 15:41
@lia-viam
Copy link
Copy Markdown
Member

Mentioned this in the previous PR but I'm not convinced we need Eigen? I would be in favor of reusing Vector3 from linear_algebra.hpp or an appropriate xtensor type

Copy link
Copy Markdown
Member

@lia-viam lia-viam left a comment

Choose a reason for hiding this comment

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

lgtm, try running bin/run_clang_format.sh if linter still isn't passing

@nfranczak nfranczak requested a review from acmorrow May 26, 2026 19:39
Copy link
Copy Markdown
Member

@acmorrow acmorrow left a comment

Choose a reason for hiding this comment

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

This looks good, I have some comments to consider though.

Comment thread conanfile.py Outdated
self.requires(self._grpc_requires())
self.requires('protobuf/[>=3.17.1 <6.30.0]')
self.requires(self._xtensor_requires(), transitive_headers=True)
self.requires('eigen/[>=3.3.0]', transitive_headers=True)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this still true? Ditto below?

Comment thread src/viam/sdk/common/linear_algebra.hpp
Comment thread src/viam/sdk/referenceframe/kinematics_model_table.hpp Outdated
Comment thread src/viam/sdk/referenceframe/kinematics_model_table.hpp Outdated

/// @brief One row of the model table: the per-joint URDF fields.
/// @note `xyz`/`rpy`/`axis` are taken directly from the URDF.
struct JointRow {
Copy link
Copy Markdown
Member

@acmorrow acmorrow May 28, 2026

Choose a reason for hiding this comment

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

You could put JointType in here, so you had JointRow::[T|t]ype::k_[revolute,continuous,prismatic,fixed].

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ping?

Copy link
Copy Markdown
Member Author

@nfranczak nfranczak Jun 1, 2026

Choose a reason for hiding this comment

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

I am not a fan of this.
It feels like there is less cognitive overhead with keep it this way.
edit: and with your proposal the jointtype is hidden one level deeper right?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it comes down to this question: Is JointType something intrinsically useful, or is it just a discriminator to understand how to interpret the fields of JointRow. If it is intrinsically valuable, then I'm more OK with making it a top-level type on its own. If it is just the descriminator for JointRow, I'd be in favor of nesting it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

FYI now that they are both at least in ModelTable I'm less inclined to push JointType into JointRow though I do think the argument holds.

I'm resisting the urge to say that JointRow should be some sort of variant, because if I see it correctly, the only real structural variant is for the fixed case.

Comment thread src/viam/sdk/referenceframe/private/kinematics_model_table_internals.hpp Outdated
if (row.type == JointType::fixed) {
row.axis = Vector3{0, 0, 0};
} else if (parsed.axis_opt) {
if (magnitude(*parsed.axis_opt) < 1e-12) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why this value and not some other?

Comment thread src/viam/sdk/referenceframe/kinematics_model_table.cpp Outdated
@nfranczak nfranczak force-pushed the 20250514-RSDK-13982-construct-model-table branch from 60f7024 to 57560da Compare May 29, 2026 20:38
@nfranczak nfranczak requested a review from acmorrow May 29, 2026 20:48
Copy link
Copy Markdown
Member

@acmorrow acmorrow left a comment

Choose a reason for hiding this comment

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

Similar thoughts as the UR Arm review about pushing harder to make these classes, rather than structs with helper functions.

k_prismatic = 2,
k_fixed = 3,
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are there any connections between the set of possible values of these vectors, and the types of joints?

You could consider adding static factory methods on JointRow, like JointRow::fixed(std::string name, ...).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

xyz and rpy just tell us the position and orientation of "things", eg. links.
axis is our biggest hint here.
If it's (0, 0, 0) then we can deduce that it's a fixed joint with certainty.
But on the other hand if it's a non-zero unit vector we do not have a way of knowing if the joint type is revolute, continuous or prismatic.


/// @brief One row of the model table: the per-joint URDF fields.
/// @note `xyz`/`rpy`/`axis` are taken directly from the URDF.
struct JointRow {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ping?

Comment thread src/viam/sdk/referenceframe/kinematics_model_table.hpp Outdated
@@ -0,0 +1,43 @@
/// @file referenceframe/private/kinematics_model_table_internals.hpp
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think you should doc comment it if it is under private, since otherwise doxygen docs will I think end up picking it up, even though consumers of the SDK can't use it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You still ahve the rest of this file doc commented though.

It should be easy, just change /// to //

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sorry a bit confused by this: in the current version of the code I just removed all the lines which were prefixed with : ///

Comment thread src/viam/sdk/referenceframe/kinematics_model_table.cpp Outdated
Comment thread src/viam/sdk/referenceframe/kinematics_model_table.cpp Outdated
@nfranczak nfranczak requested a review from acmorrow June 2, 2026 00:06
Copy link
Copy Markdown
Member

@acmorrow acmorrow left a comment

Choose a reason for hiding this comment

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

LGTM with a few lingering comments. @lia-viam can you please also take a look?

@@ -0,0 +1,43 @@
/// @file referenceframe/private/kinematics_model_table_internals.hpp
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You still ahve the rest of this file doc commented though.

It should be easy, just change /// to //


/// @brief One row of the model table: the per-joint URDF fields.
/// @note `xyz`/`rpy`/`axis` are taken directly from the URDF.
struct JointRow {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

FYI now that they are both at least in ModelTable I'm less inclined to push JointType into JointRow though I do think the argument holds.

I'm resisting the urge to say that JointRow should be some sort of variant, because if I see it correctly, the only real structural variant is for the fixed case.

/// @brief URDF joint type, restricted to arm-relevant joints.
/// @note Underlying values are stable and form the wire encoding for
/// column 9 of the tensor produced by `to_tensor()`.
enum class JointType {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I suspect the linter is going to want you to give this a smaller integral type.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

A bit confused by this, the linter did not complain.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK, maybe we don't do that anymore.

Copy link
Copy Markdown
Member

@lia-viam lia-viam left a comment

Choose a reason for hiding this comment

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

mostly minor/optional stuff but still lgtm

std::vector<ParsedJoint> parse_urdf(const KinematicsDataURDF& urdf) {
namespace pt = boost::property_tree;
pt::ptree tree;
const std::string text(reinterpret_cast<const char*>(urdf.bytes.data()), urdf.bytes.size());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we have bytes_to_string for this

row.xyz = parsed.xyz;
row.rpy = parsed.rpy;

if (parsed.type_str == "revolute") {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we want a public to_string or joint_type_from_string ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I am not convinced that we need a public to_string or joint_type_from_string.
My reasoning is that users always get a parsed JointType values values via from(urdf) / from(tensor) and never see the strings.
The to_string has no user anywhere in this flow.

xt::xarray<double> out = xt::zeros<double>({rows_.size(), std::size_t{10}});
for (std::size_t i = 0; i < rows_.size(); ++i) {
const auto& r = rows_[i];
out(i, 0) = r.xyz.x();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't love this but struggling to come up with an alternative

    int idx = 0;
    for (const auto& vec3 : {std::cref(r.xyz), std::cref(r.rpy), std::cref(r.axis)}) {
        for (const auto& val : arr.data) {
            out(i, idx++) = val;
        }
    }

honestly not sure if this is preferable, your call

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

while more compact/abstract, I think that what we have now is slightly clearer to read.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

in that case maybe just a line break between the blocks where you're operating on the different vec3's

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done!

/// @brief URDF joint type, restricted to arm-relevant joints.
/// @note Underlying values are stable and form the wire encoding for
/// column 9 of the tensor produced by `to_tensor()`.
enum class JointType {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK, maybe we don't do that anymore.

namespace sdk {
namespace impl {

/// @brief Per-joint parse result, pre-validation.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here, for instance, there is still a doxygen style comment on an internal header.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

ok I can change.
does that mean that this for example is incorrect?
https://github.com/viamrobotics/viam-cpp-sdk/blob/main/src/viam/sdk/rpc/private/viam_grpc_channel.hpp#L14

@nfranczak nfranczak merged commit 1460e45 into viamrobotics:main Jun 3, 2026
5 checks passed
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.

3 participants