Skip to content

LCM Usage

Carmen edited this page Jan 10, 2021 · 14 revisions

What is LCM

Lightweight Communications and Marshalling (LCM) is a set of libraries used for message passaging and
data marshalling. It provides a publish/subscribe message passing model, and is a form of UDP
communication - meaning if packets are lost we are not notified.

On the rover, it is used to facilitate communication between applications running on computers connected
by a shared Ethernet network.

Usage

A more thorough guide can be found on the mrover-workspace wiki.
The steps shown here are fairly brief and don't go beyond what we use LCM for on embedded software.

Define a Message

Message definitions are language independent, and thus can be used by rover applications of any language.
The syntax for type definition is similar to C.
Create a file called <message>.lcm in the rover_msgs folder in the mrover-workspace. If the message is a
command, its name should have the suffix Cmd. If it contains data, it should have the suffix Data.
For example, AccelData.lcm.

Here is an example of a struct that will contain data from the Accelerometer sensors

package rover_msgs; 

struct AccelData { 
    int8_t x; 
    int8_t y; 
    int8_t z; 
} 

Data can be sent and received from it via the "/accel_data" channel

Publishing Data

In your __main__.py file, you will publish to this channel.
You'll have some form of this in your code.

import lcm  # import lcm library
from rover_msgs import AccelData

lcm_ = lcm.LCM()  # initialize LCM


def main():
    <code to initialize accelerometer sensor>
    while(1):
        accel = AccelData()

        accel.x = <code to get x value from sensor>
        accel.y = <code to get y value from sensor>
        accel.z = <code to get z value from sensor>

       lcm_.publish("/accel_data", accel.encode())



if __name__ == "__main__":
    main()

Subscribing to Channels

In the file receiving LCM messages, you will have to subscribe to the channel you are getting data from.
In your __main__.py file you will have some form of this in your code.

import lcm  # import lcm library
from rover_msgs import AccelData

lcm_ = lcm.LCM()  # initialize LCM

def accel_data_callback(channel, msg):
    accel = AccelData.decode(msg)
    print("z: " + str(accel.x) + " y: " + str(accel.y) + " z: " + str(accel.z))

def main():
    lcm_.subscribe("/accel_data", accel_data_callback) 
   
    while(1):
        # the code hangs on this function until data is sent to the "/accel_data" channel
        lcm_.handle()

if __name__ == "__main__":
    main()

Testing subscribing/publishing

In order to test whether messages are actually being sent/receiving you have to execute your programs with the LCM_DEFAULT_URL prefix
It's easy to test this on the same host (for example the beaglebone).
In this example, we will continue with the accelerometer sensor code.
In the mrover-workspace
LCM_DEFAULT_URL="udpm://239.255.76.67:7667?ttl=255" ./jarvis exec beaglebone_accel
In another terminal you will want to run lcm_echo, which will display data being published to the channel you specify
LCM_DEFAULT_URL="udpm://239.255.76.67:7667?ttl=255" ./jarvis exec lcm_tools_echo AccelData "/accel_data"

To demonstrate sending commands, we will modify the accelerometer code a little bit.
Since we have four accelerometer sensors, we also want to send a struct to choose which sensor we will read data from
and for it to only publish data on command.
The channel that this data will be published/read from is called "/accel_cmd"

package rover_msgs;

struct AccelCmd {
    string sensor; \\ front_left, front_right, back_left, back_right for each side of the rover the accelerometer is on
}

In our previous accelerometer publishing file, we will also have to subscribe to the "/accel_cmd" channel
so we know when what sensor to display data from.

import lcm  # import lcm library
from rover_msgs import AccelData
from rover_msgs import AccelCmd

lcm_ = lcm.LCM()  # initialize LCM

def accel_cmd_callback(channel, msg):
    global sensor 
    sensor = AccelCmd.decode(msg)
   
def main():
    global sensor = 'none'
    lcm_.subscribe("/accel_cmd", accel_cmd_callback)

    <code to initialize the accelerometer sensors>
    while(1):
        lcm_.handle()
        <code to set the accelerometer sensor we are reading from to be front_left, front_right, back_left, or back_right>

        accel = AccelData()

        accel.x = <code to get x value from sensor>
        accel.y = <code to get y value from sensor>
        accel.z = <code to get z value from sensor>

       lcm_.publish("/accel_data", accel.encode())

if __name__ == "__main__":
    main()

In order to test this, in one terminal exec the accelerometer package.
LCM_DEFAULT_URL="udpm://239.255.76.67:7667?ttl=255" ./jarvis exec beaglebone_accel

In another terminal, run lcm_echo, to display data being published to "/accel_data"
LCM_DEFAULT_URL="udpm://239.255.76.67:7667?ttl=255" ./jarvis exec lcm_tools_echo AccelData "/accel_data"

This time, no data will be displayed until you send the command to do so.
Open another terminal and navigate to the mrover-workspace and run lcm send.
LCM_DEFAULT_URL="udpm://239.255.76.67:7667?ttl=255" ./jarvis exec lcm_tools_send "/accel_cmd" "{'type' : 'AccelCmd', 'sensor' : 'front_left'}"

Now in the lcm_echo terminal, the x, y, z data for the front_left accelerometer sensor will be displayed.

Project.ini

Each package should additionally have a project.ini file in the same level as the src folder.
Since most of our packages are in python, on ESW project.ini files will mostly be formatted like this:

[build]
lang=python
executable=True
deps=rover_msgs

Issues

LCM echo is displaying when the publisher/subscriber programs are run on the same host, but not on two different hosts (the beaglebone and a laptop)

Make sure both hosts are connected to the same wifi network and the the firewall is off on the laptop.
This issue has to do with how the messages are being routed.
sudo su on the host and then ifconfig
Find the variable associated with the ip address of the beaglebone 192.168.7.2
Copy and paste the variable name and type
bbbnet=<variable>
sudo ifconfig $bbbnet multicast
sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev $bbbnet

LCM send or echo doesn't exist

This likely means you are not in the mrover-workspace directory, so navigate to there, or that you need to build them.
Run ./jarvis build lcm_tools/echo and ./jarvis build lcm_tools/send

Other Resources

Here are some other linked unofficial guides and examples used for LCM.
Science guide
Odrive documentation example

Clone this wiki locally