@prototwin/ros2-wrappers

1.0.0PublicPublished 11 days ago

Convenience wrappers for ROS2 publishers and action servers.

Latest Version: 1.0.0

Size: 23 KB

Publisher: ProtoTwin (contact@prototwin.com)

README.md

ROS2 Wrappers

Convenience wrappers for publishing common ROS2 messages and executing robot trajectories from ProtoTwin. ROS2 nodes and wrappers should be owned by a scripted component. They can be created directly as instance fields.

import { type Entity, Component, ROS2 } from "prototwin";
import { PosePublisher, SimulationTimePublisher } from "@prototwin/ros2-wrappers";

export class ROSPublishers extends Component {
    #node = ROS2.createNode("prototwin");
    #timePublisher = this.#node.create(SimulationTimePublisher, this.world);
    #posePublisher = this.#node.create(PosePublisher, { entity: this.entity });

    public constructor(entity: Entity) {
        super(entity);
    }
}

Wrappers can be created through ROS2.Node.create(). The node owns the wrapper and disposes it when the node is disposed.

Publishers with an autopublish option publish after every simulation timestep by default. Set autopublish to false to publish manually with publish(). Publisher options also accept ROS2 Quality of Service (QoS) settings and, except where noted, a custom topic name.

Simulation Time Publisher

The SimulationTimePublisher wrapper publishes the current simulation time as rosgraph_msgs/msg/Clock.

  • Default topic: /clock
  • Options: topicName, qos, autopublish
  • Manual publishing: publish()
const clock = node.create(SimulationTimePublisher, world);

Joint State Publisher

The JointStatePublisher wrapper publishes the joints controlled by a RobotControllerComponent as sensor_msgs/msg/JointState. Each message contains the joint names, positions, velocities and efforts. Unnamed joints use J1, J2, and so on.

  • Default topic: /joint_states
  • Options: controller, topicName, qos, autopublish
  • Manual publishing: publish(stamp?)

The controller can also be assigned through the controller property. When used by a component, you must assign the controller during initialization because other components may not yet be attached when its constructor runs.

const jointStates = node.create(JointStatePublisher, { controller: robotController });

Pose Publisher

The PosePublisher wrapper publishes an entity pose as geometry_msgs/msg/PoseStamped. The pose is expressed relative to the optional base entity, or in the world frame when no base is assigned.

  • Default topic: /pose
  • Options: entity, base, topicName, qos, autopublish
  • Manual publishing: publish(stamp?)
const pose = node.create(PosePublisher, { entity: target });

Entity Transform Publisher

The EntityTransformPublisher wrapper publishes entity transforms as tf2_msgs/msg/TFMessage.

  • Default topic: /tf
  • Options: entity, transforms, topicName, qos, autopublish
  • Manual publishing: publish(stamp?)

Use EntityTransformFlags to publish the selected entity, its descendants, or both.

const transforms = node.create(EntityTransformPublisher, {
    entity: robotBase,
    transforms: EntityTransformFlags.IncludeEntityAndDescendants
});

Static Entity Transform Publisher

The StaticEntityTransformPublisher wrapper publishes static entity transforms as tf2_msgs/msg/TFMessage. It uses /tf_static with transient-local durability by default.

  • Default topic: /tf_static
  • Options: entity, transforms, topicName, qos
  • Publishing: publish(stamp?)

Static transforms are published only when publish() is called.

const transforms = node.create(StaticEntityTransformPublisher, {
    entity: sensorMount,
    transforms: EntityTransformFlags.IncludeEntityAndDescendants
});

transforms.publish();

Odometry Publisher

The OdometryPublisher wrapper publishes an entity pose and velocity as nav_msgs/msg/Odometry. The pose is expressed relative to the optional base entity, or in the world frame when no base is assigned. Velocities are read from the physics body when available and otherwise estimated from changes in the transform.

  • Default topic: /odom
  • Options: entity, base, topicName, qos, autopublish
  • Manual publishing: publish(stamp?)
const odometry = node.create(OdometryPublisher, { entity: mobileChassis });

Follow Joint Trajectory Action Server

The FollowJointTrajectoryActionServer wrapper exposes a control_msgs/action/FollowJointTrajectory action server backed by a RobotControllerComponent instance.

The server validates joint names and trajectory positions, forwards path and goal tolerances to the robot controller, reports execution feedback, and supports cancellation. Only one trajectory can execute at a time.

  • Required argument: Action name
  • Options: controller, qos, validateLimits
  • Default limit validation: Disabled

The controller can also be assigned through the controller property. When used by a component, you must assign the controller during initialization because other components may not yet be attached when its constructor runs.

const trajectoryServer = node.create(FollowJointTrajectoryActionServer, "/arm_controller/follow_joint_trajectory", {
    controller: robotController,
    validateLimits: true
});

Image Publisher

The ImagePublisher wrapper renders from the perspective of an entity's pose using the ROS camera convention and publishes sensor_msgs/msg/Image.

  • Default topic: /camera/image_raw
  • Options: entity, width, height, intrinsics, near, far, tonemapping, format, topicName, qos, autopublish
  • Default format: RGB8
  • Supported formats: RGBA8, RGB8 and BGR8
  • Manual publishing: publish()

The message frame ID is the camera entity name, or world when no entity is assigned.

const image = node.create(ImagePublisher, world, { entity: cameraEntity });

Camera Publisher

The CameraPublisher wrapper renders from the perspective of an entity's pose using the ROS camera convention and publishes synchronized sensor_msgs/msg/Image and sensor_msgs/msg/CameraInfo messages. Use this wrapper when consumers also require camera calibration data.

  • Default namespace: /camera
  • Default topics: /camera/image_raw and /camera/camera_info
  • Options: name, entity, width, height, intrinsics, near, far, tonemapping, format, qos, autopublish
  • Default format: RGB8
  • Supported formats: CameraImageFormat.RGBA8, CameraImageFormat.RGB8 and CameraImageFormat.BGR8
  • Manual publishing: publish()

The message frame ID is the camera entity name, or world when no entity is assigned.

const camera = node.create(CameraPublisher, world, {
    name: "/front_camera",
    entity: cameraEntity,
    format: CameraImageFormat.RGB8
});

Changelog

1.0.0

Initial release.