Guide and general tips on using MASLAB software and other software to interact with sensors and actuators on the robot. Checkout kitbot how-to to learn how to connect the hardwares.
Raven board fully soldered with firmware deployed and the Pi.
MASLAB staffs maintain a Python library that supports using the motors and servos on the Raven board (raven
) along with an integrated IMU (icm42688
). The library is installed on the Pi as part of the kitbot Raven setup.
Important
Since these library is communicating with a hardware, no two instances may run at the same time. DO NOT have two different ROS2 nodes trying to use Raven. Instead, have a node subscribing to some Raven control messages and have other nodes publish messages for controlling.
The library is available at https://github.com/MASLAB/maslab-lib.
from raven import Raven
raven_board = Raven()
DC motors may come with encoders to estimate how many rotation has the motor rotated. To increase the resolution of the encoder, the encoder spins with some gear ratio with respect to the motor, which also has another gear ratio. For example, the provided motor is a 40:1 motor with an encoder that does 11 counts per revolution (also called pulse per revolution). This means the encoder will count 40 * 11 = 440 counts per motor rotation.
Raven supports up to 5 DC motors with encoders.
Important
For safety, Raven will stop the motors if it does not receive a motor command message at least every half a second.
With Raven, you can get the encoder values. You can also set them to a new initial value. Here is an example of how to use it:
raven_board.set_motor_encoder(Raven.MotorChannel.CH1, 0) # Set encoder count for motor 1 to zero
print(raven_board.get_motor_encoder(Raven.MotorChannel.CH1)) # Print encoder count = "0"
Important
Encoder count should matches with the motor direction. To test, run the motor with direct mode (see direct mode) and read out encoder reading. When motor is going forward (reverse=False
), encoder counts should go up. If not, swap the C1 and C2 wires. See motor connection for more information.
There are 3 ways to drive the motors:
- Direct - Motor moves according to a torque and speed factor. We recommend driving in direct mode first to get things started.
- Position (requires encoder) - Motor moves to an encoder position with PID control
- Velocity (requires encoder) - Motor moves with a set encoder velocity using PID control
- Disabled - Turn off the motor
To use the motors, make sure you have imported Raven. Then follow the following examples for each mode.
Tip
Once motor mode and PID values are set, you can set motor drive values without having to set the mode and PID values again.
In direct mode, you get to set the torque factor as 0% to 100% of available torque. You also get to set the speed as 0% to 100% of max speed and a direction of rotation. These conditions are subjected to the battery's voltage. Do this for most reliable actuation or custom controls. Also useful as a part of mechanical designs.
raven_board.set_motor_mode(Raven.MotorChannel.CH1, Raven.MotorMode.DIRECT) # Set motor mode to DIRECT
# Speed controlled:
raven_board.set_motor_torque_factor(Raven.MotorChannel.CH1, 100) # Let the motor use all the torque to get to speed factor
raven_board.set_motor_speed_factor(Raven.MotorChannel.CH1, 10, reverse=True) # Spin at 10% max speed in reverse
# Torque controlled:
raven_board.set_motor_speed_factor(Raven.MotorChannel.CH1, 100) # Make motor try to run at max speed forward
raven_board.set_motor_torque_factor(Raven.MotorChannel.CH1, 10) # Let it use up to 10% available torque
Important
The control loop for Raven motor runs at 1kHz (dt = 0.001). This will be important to playing with PID values.
Caution
The PID values provided are only as examples. They are untested and may not work for your motors. Here is some tip to tune the values:
- Start with some P and adjust until your motor starts going to desired target quickly but may not reach / little overshoot.
- Include I and adjust until your motor gets up to target set point quickly and settle to correct target point with some overshoot.
- Include D and adjust until your motor get to set point still quickly and correctly but does not overshoot.
Often time you only needs PI or PD to control your motors. Here is also a great video to demonstrate the effects of PID: https://www.youtube.com/watch?v=fusr9eTceEo
Tip
Check control theory lecture notes to refresh about PID control: https://maslab.mit.edu/2025/lectures
In position controlled mode, you get to set the PID value for the controller and a target in encoder counts.
raven_board.set_motor_encoder(Raven.MotorChannel.CH1, 0) # Reset encoder
raven_board.set_motor_mode(Raven.MotorChannel.CH1, Raven.MotorMode.POSITION) # Set motor mode to POSITION
raven_board.set_motor_pid(Raven.MotorChannel.CH1, p_gain = 100, i_gain = 0, d_gain = 0) # Set PID values
# Make the motor spin until 4400 counts (10 rev of wheel motor)
raven_board.set_motor_target(Raven.MotorChannel.CH1, 4400)
In velocity controlled mode, you also get to set PID value and target in encoder counts per second.
raven_board.set_motor_encoder(Raven.MotorChannel.CH1, 0) # Reset encoder
raven_board.set_motor_mode(Raven.MotorChannel.CH1, Raven.MotorMode.VELOCITY) # Set motor mode to POSITION
raven_board.set_motor_pid(Raven.MotorChannel.CH1, p_gain = 10, i_gain = 0, d_gain = 0) # Set PID values
# Make the motor spin at -4400 counts/second (-10 rev/sec of wheel motor)
raven_board.set_motor_target(Raven.MotorChannel.CH1, -4400)
Servos are position controlled motors that can move from -90 degree to 90 degree. Their signal is based on a timed pulse. Typically, 1000us means -90 degree and 2000us means 90 degree. Some servo may have different values.
Raven board supports up to 4 servos. For each servo, you get to set the position in degree. Optionally, you can set the minimum and maximum pulse microsecond as min_us
and max_us
for you appropriate motor. Otherwise, they are defaulted to min_us=1000
and max_us=2000
. Here is an example of how to use them once you have imported Raven.
# Set the servo 1 to -75 degrees with custom pulse microseconds
raven_board.set_servo_position(Raven.ServoChannel.CH1, -75, min_us=500, max_us=2500)
IMUs are important to estimate how fast your robot is moving. Typical IMU measures the linear acceleration using an accelerometer and rotational velocity using a gyroscope. Raven included IMU is located near center of the 40 pins connector. The orientation of the IMU with respect to the board is like this:
The orientation follows right-hand rule such that Z-axis points out of the picture, and rotation of each axis is counter-clockwise.
To use the IMU, make sure you import and initialize the IMU:
from icm42688 import ICM42688
import board
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
while not spi.try_lock():
pass
spi.configure(baudrate=5000000)
imu = ICM42688(spi)
imu.begin()
accel, gyro = imu.get_data()
# Returned linear acceleration is a tuple of (X, Y, Z) m/s^2
# Returned gyroscope reading is a tuple of (X, Y, Z) radian/s
Depends on how fast you expect the linear acceleration and rotational velocity of the robot, you can set the full scale and output data rate (ODR) of the IMU. High full scale can measure higher values but gets noisier signal for lower values. High ODR can measure values more quickly but also has more noise.
# Set gyro to measure up to 15.625 degree per second with 1kHz ODR
imu.set_gyro_fullscale_odr(ICM42688.GYRO_FS.FS15dps625, ICM42688.ODR.ODR1kHz)
# Set accel to measure up to 2g with 1kHz ODR
imu.set_accel_fullscale_odr(ICM42688.ACCEL_FS.FS2g, ICM42688.ODR.ODR1kHz)
accel, gyro = imu.get_data()
Here are the values for full scales and ODRs:
ICM42688.ODR:
ODR32kHz
ODR16kHz
ODR8kHz
ODR4kHz
ODR2kHz
ODR1kHz
ODR200Hz
ODR100Hz
ODR50Hz
ODR25Hz
ODR12Hz5
ODR500Hz
ICM42688.GYRO_FS:
FS2000dps
FS1000dps
FS500dps
FS250dps
FS125dps
FS62dps5
FS31dps25
FS15dps625
ICM42688.ACCEL_FS:
FS16g
FS8g
FS4g
FS2g
For switches/buttons or LED, Raven includes breakout to 5 Pi's GPIO pins. To use the pins, we have preinstalled gpiozero
on the Pi. The documentation for gpiozero
can be found here: https://gpiozero.readthedocs.io/en/stable/
Here are a couple useful examples:
- Button (also limit switch) - https://gpiozero.readthedocs.io/en/stable/api_input.html#button
- Light sensor - https://gpiozero.readthedocs.io/en/stable/api_input.html#lightsensor-ldr
- LED - https://gpiozero.readthedocs.io/en/stable/api_output.html#led
We also have CircuitPython digitalio
preinstalled. Here are a few other useful examples:
- Break beam - https://learn.adafruit.com/ir-breakbeam-sensors/circuitpython
- Ultrasonic distance sensor - https://github.com/adafruit/Adafruit_CircuitPython_HCSR04
Raven board also includes 2 qwiic connectors to use sensor boards with qwiic connections. For example, Adafruit's AS7341 color sensor board. You can use it with any i2c devices with qwiic connector breakout cables.
The Pi comes with Adafruit's Circuit Python library preinstalled such that it is compatible with Adafruit's Python libraries for communicating with the sensor board. Therefore, you can follow their documentations for using the sensor. For example, with the color sensor, you can checkout: https://learn.adafruit.com/adafruit-as7341-10-channel-light-color-sensor-breakout/python-circuitpython#circuitpython-installation-of-as7341-library-3069092
Important
The default qwiic connector to use and daisy chain is the left one (near the encoders). It is what Adafruit's busio uses to make an i2c connection. The right connector is for developing custom library for other i2c qwiic boards without a Python library.
Here are a few sensors you may be interested in:
- Color sensor - https://learn.adafruit.com/adafruit-as7341-10-channel-light-color-sensor-breakout
- Time-of-flight distance sensor - https://learn.adafruit.com/adafruit-vl53l0x-micro-lidar-distance-sensor-breakout
- IMU with orientation - https://www.adafruit.com/product/4754