Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Python API

The python API is designed to take away some of the boilerplate around ROS. There are two versions of the API: mc_one and mc_one_async. mc_one is direct/blocking, and mc_one_async is designed with asyncio semantics.

mc_one_async is still in development.

General Structure

The general structure of a node tends to be:

direct/blocking:

from mc_one import Node

class MyNode(Node):
    def __init__(self):
        super().__init__('my_node',namespace='/my_package')
        self.logger = self.get_logger()
        self.parameters = self.use_parameters()
        # ...more initialization

def main(args=None):
    rclpy.init(args=args)
    run_rclpy(MyNode(),args)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

async:

from mc_one_async import Node

@asyncinit
class MyNode(Node):
    async def __init__(self):
        super().__init__('my_node',namespace='/my_package')
        self.logger = self.get_logger()
        self.parameters = self.use_parameters()
        # ...more initialization

async def async_main(args=None):
    node = await MyNode()
    await asyncio.Event().wait() # just do nothing here for now
    node.destroy_node()

def main(args=None):
    rclpy.init(args=args)
    run_async_rclpy(async_main,args)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Node

Node is a subclass of rclpy.Node with some added methods.

Parameters

Call use_parameters to access the parameters API (usually in __init__):

self.parameters = self.use_parameters()

Declaring Parameters

Parameters should be declared first. Each different type/style of parameter has a declare method.

Boolean

value = self.parameters.declare_bool(
    'name', # name of the parameter
    True, # default value
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Integer

value = self.parameters.declare_int(
    'name', # name of the parameter
    0, # default value
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Ranged Integer

value = self.parameters.declare_ranged_int(
    'name', # name of the parameter
    0, # default value
    0, # from-value
    10, # to-value
    2, # step
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Options Integer

value = self.parameters.declare_options_int(
    'name', # name of the parameter
    0, # default value
    [0,1,2], # options to choose from
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Double

value = self.parameters.declare_double(
    'name', # name of the parameter
    0.0, # default value
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Ranged Double

value = self.parameters.declare_ranged_double(
    'name', # name of the parameter
    0.0, # default value
    0.0, # from-value
    10.0, # to-value
    2.0, # step
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Options Double

value = self.parameters.declare_options_double(
    'name', # name of the parameter
    0.0, # default value
    [0.0,1.0,2.0], # options to choose from
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

String

value = self.parameters.declare_string(
    'name', # name of the parameter
    'hello', # default value
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    lines=1, # (optional) number of lines for the string
    private=True, # (optional) private indication
)

Options String

value = self.parameters.declare_options_string(
    'name', # name of the parameter
    'hello', # default value
    ['hello','world'], # options to choose from
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    lines=1, # (optional) number of lines for the string
    private=True, # (optional) private indication
)

Boolean Array

value = self.parameters.declare_bool_array(
    'name', # name of the parameter
    [True,False,True,False], # default values
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Integer Array

value = self.parameters.declare_int_array(
    'name', # name of the parameter
    [0,0,0,0], # default values
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Ranged Integer Array

value = self.parameters.declare_ranged_int_array(
    'name', # name of the parameter
    [0,0,0,0], # default values
    0, # from-value
    10, # to-value
    2, # step
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Options Integer Array

value = self.parameters.declare_options_int_array(
    'name', # name of the parameter
    [0,0,0,0], # default values
    [0,1,2], # options to choose from
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Double Array

value = self.parameters.declare_double_array(
    'name', # name of the parameter
    [0.0,0.0,0.0,0.0], # default values
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Ranged Double Array

value = self.parameters.declare_ranged_double(
    'name', # name of the parameter
    [0.0,0.0,0.0,0.0], # default values
    0.0, # from-value
    10.0, # to-value
    2.0, # step
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

Options Double Array

value = self.parameters.declare_options_double(
    'name', # name of the parameter
    [0.0,0.0,0.0,0.0], # default values
    [0.0,1.0,2.0], # options to choose from
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    private=True, # (optional) private indication
)

String Array

value = self.parameters.declare_string_array(
    'name', # name of the parameter
    ['hello','hello','hello','hello'], # default values
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    lines=1, # (optional) number of lines for the strings
    private=True, # (optional) private indication
)

Options String Array

value = self.parameters.declare_options_string_array(
    'name', # name of the parameter
    ['hello','hello','hello','hello'], # default value
    ['hello','world'], # options to choose from
    description='description', # (optional) description
    read_only=True, # (optional) read-only indication
    lines=1, # (optional) number of lines for the strings
    private=True, # (optional) private indication
)

Foreign Parameters

value = self.parameters.declare_foreign(
    'name', # name of the parameter
    'other_node', # name of the other node
    namespace='namespace', # (optional) namespace of the other node
)

Getting and Setting Parameters

To get or set the current value of a parameter, use get and set, or get_foreign and set_foreign for foreign parameters.

value = self.parameters.get('name')
self.parameters.set('name',value)
value = self.parameters.get_foreign('name','other_node',namespace='/other_package')
self.parameters.set_foreign('name',value,'other_node',namespace='/other_package')

Handling Parameter Updates

To handle changes in the declared parameters (including foreign parameters), register an update callback:

self.parameters.on_update(callback)

The callback takes the parameter name and the new value as parameter:

def callback(name,value):
    pass

Subscribing

Under Construction

For now use the ROS API like so:

self.create_subscription(
    message_type, # the message type (i.e. std_msgs.msg.String)
    topic, # the topic name
    callback, # the message callback
    10
)

The callback takes a raw ROS message:

def callback(message):
    pass

Publishing

Under Construction

For now use the ROS API like so:

publisher = self.create_publisher(
    message_type, # the message type (i.e. std_msgs.msg.String)
    topic, # the topic name
    10
)

And then to publish a message:

publisher.publish(message)

Services

Under Construction

Actions

Under Construction

Audio In

Call use_audio_in to access the audio in API (usually in __init__):

self.audio_in = self.use_audio_in(callback)

Audio samples will now stream in through the callback:

def callback(audio):
    # ...process audio.data
    pass

The message type is mc_core_interfaces.msg.AudioStamped:

std_msgs/Header header
int16[] data

Audio Out

Call use_audio_out to access the audio out API (usually in __init__):

self.audio_out = self.use_audio_out(status_callback)

The sample rate of the audio output is currently fixed, and can be retrieved by:

rate = self.audio_out.sample_rate()

Now,to play an audio sample:

self.audio_out.play(
    id, # unique ID to identify this sample
    data, # sample data (either raw bytes, or int16 np.array, or float np.array)
)

The audio samples get queued and played one by one. Once a sample is complete, the status callback is called:

def status_callback(status,id,offset):
    # status = 0: not played, 1: partially played until offset, 2: fully played
    # id = sample ID as passed to play() method
    # offset = (when status = 1) number of frames played (between 0 and len(data))
    pass

To cancel all enqueued audio right now:

self.audio_out.stop_all()

This will stop the currently playing sample and flush the queue, reporting back the status of each sample.

Video In

Call use_video_in to access the video in API (usually in __init__):

self.video_in = self.use_video_in(callback)

Video frames will now stream in through the callback. The message type is sensor_msgs.msg.CompressedImage. To use the image with OpenCV, decode the message like so:

def callback(message):
    buffer = np.frombuffer(message.data,np.uint8)
    image = cv2.imdecode(buffer,cv2.IMREAD_COLOR)
    pass

Monitor

Under Construction