DriveWorks SDK Reference
3.5.78 Release
For Test and Development only

VehicleIO Plugins
Note
SW Release Applicability: This tutorial is applicable to modules in both NVIDIA DriveWorks and NVIDIA DRIVE Software releases.

VehicleIO Plugins Overview

The VehicleIO Plugin back-end enables writing fully customized drive-by-wire abstraction that will be represented as a VehicleIO interface for the rest of the software stack.

Implementing Custom VehicleIO Plugin

A VehicleIO plugin in DriveWorks requires implementing a set of pre-defined functions. The declarations of these functions are located in dw/control/vehicleio/plugins/VehicleIODriver.h.

In this tutorial, we are going to implement a simple drive-by-wire system that is responsible for steering wheel angle. See VehicleIO Plugin Sample for the corresponding sample code.

Initialization

In essence, our plugin is a dynamically loaded shared library, and the core VehicleIO module will first call _dwVehicleIODriver_initialize() function of this library, which should prepare all objects and state that is needed for the plugin to function:

{
dwStatus status;
dwStatus result = DW_SUCCESS;
dwContextParameters sdkParams = {};
dwInitialize(&gSdk, DW_VERSION, &sdkParams);
status = dwCANInterpreter_buildFromDBC(&gIntp, ("AutonomousVehicleCANSignals.dbc"), gSdk);
checkError(result, status, "cannot create DBC-based CAN message interpreter");
return result;
}

Above, dwCANInterpreter_buildFromDBC() function initializes an interpreter object that will be useful to work with our DBC file. See CAN Message Interpreter Sample for more information about CAN interpreter usage.

When using the plugin with, for example, VehicleIO Sample, in order for VehicleIO core to load the plugin, Rig file should specify "custom" VehicleIO node:

{
"rig": {
"vehicleio": [
{
"type": "custom",
"parent-sensor": "can:vehicle:custom",
"custom-lib": "../../../bin/libsample_vehicleio_plugin.so"
}
],
"sensors": [
{
"name": "can:vehicle:custom",
"parameter": "file=can-vio-generic.bin",
"protocol": "can.virtual",
"properties": null,
"nominalSensor2Rig": { "quaternion": [ 0.0, 0.0, 0.0, 1.0 ], "t": [ 0.0, 0.0, 0.0 ] },
"sensor2Rig": { "quaternion": [ 0.0, 0.0, 0.0, 1.0 ], "t": [ 0.0, 0.0, 0.0 ] }
}
]
}
}

Full example of Rig file is available in data/samples/vehicleio/rig-plugin.json, and exact commands how to run the VehicleIO sample with the plugin is available at VehicleIO Plugin Sample.

Releasing

When plugin is no longer needed, VehicleIO core will call _dwVehicleIODriver_release() method, which is an opportunity for the module to cleanup any created objects and state:

CAN messages consumption

CAN messages represent the input to the module, which plugin must parse and update its state, if needed. VehicleIO core will call plugin's _dwVehicleIODriver_consume() function when there is a new message to consume:

{
dwStatus status;
dwStatus result = DW_SUCCESS;
status = dwCANInterpreter_consume(msg, gIntp);
checkError(result ,status, "consume message failed");
parseSteeringReport(state);
return result;
}

In the example above, we simply parse current steering angle report using our CAN/DBC interpreter:

static void parseSteeringReport(dwVehicleIOState* state)
{
dwStatus status;
uint32_t num;
status = dwCANInterpreter_getNumberSignals(&num, gIntp);
if (status == DW_SUCCESS && num > 0)
{
float32_t value = 0;
dwTime_t timestamp = 0;
const char* name;
for (uint32_t i = 0; i < num; ++i)
{
if (dwCANInterpreter_getSignalName(&name, i, gIntp) == DW_SUCCESS)
{
if (dwCANInterpreter_getf32(&value, &timestamp, i, gIntp) == DW_SUCCESS)
{
if (0 == strcmp(name, GENERIC_ID_STEERING_WHEEL_ANGLE_REPORT))
state->steeringWheelAngle = value;
else if (0 == strcmp(name, GENERIC_ID_STEERING_WHEEL_TORQUE_REPORT))
state->steeringWheelTorque = value;
else if (0 == strcmp(name, GENERIC_ID_STEERING_WHEEL_SPEED_REPORT))
state->speed = value;
}
}
}
}
}

CAN messages generation

CAN messages are also the output of the module, thus when VehicleIO is requested to send a command, the core module will relay the request to the plugin, issuing two calls: _dwVehicleIODriver_sendCommand() and _dwVehicleIODriver_sendMiscCommand(). Each of the function should encode command arguments into CAN messages and send via a provided sensor handle:

{
dwCANMessage msgCAN{};
dwStatus status;
dwStatus result = DW_SUCCESS;
status = dwCANInterpreter_createMessageFromName(&msgCAN, GENERIC_MSG_STEERING_CMD, gIntp);
checkError(result, status, "create steering message failed");
status = encodeSteering(msgCAN, cmd);
checkError(result, status, "encode steering failed");
status = dwSensorCAN_sendMessage(&msgCAN, 1000000, sensor);
checkError(result, status, "send steering failed");
return result;
}
{
return DW_NOT_IMPLEMENTED;
}

If certain type of commands are not relevant, the plugin is permitted to return DW_NOT_IMPLEMENTED status, as was shown in _dwVehicleIODriver_sendMiscCommand() above.

And as in previous section, we use CAN interpreter to encode commands into CAN messages:

static dwStatus encodeSteering(dwCANMessage& msgCAN, const dwVehicleIOCommand* cmd)
{
dwStatus status;
dwStatus result = DW_SUCCESS;
GENERIC_ID_STEERING_WHEEL_ANGLE_COMMAND,
&msgCAN, gIntp);
checkError(result, status, "encode steering angle failed");
status = dwCANInterpreter_encodei32(cmd->steeringValid,
GENERIC_ID_STEERING_WHEEL_ANGLE_COMMAND_VALID,
&msgCAN, gIntp);
checkError(result, status, "encode steering valid failed");
status = dwCANInterpreter_encodef32(cmd->steeringSpeed,
GENERIC_ID_STEERING_WHEEL_STEER_SPEED,
&msgCAN, gIntp);
checkError(result, status, "encode steering speed failed");
status = dwCANInterpreter_encodei32(cmd->clearFaults ? true : false,
GENERIC_ID_STEERING_WHEEL_STEER_CLEAR_FAULT,
&msgCAN, gIntp);
checkError(result, status, "encode steering clear fault failed");
return result;
}

Building plugin as a dynamic library

The plugin code must be compiled into a dynamic library.

Firstly, we should create a folder under samples called vehicleio_plugin and create a CMakeLists.txt under this folder:

project(sample_vehicleio_plugin C CXX)
set(SOURCES
driverConf.hpp
driver.cpp
)
set(LIBRARIES
samples_framework
${Driveworks_LIBRARIES}
)
add_library(${PROJECT_NAME} SHARED ${SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBRARIES})
set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER "Samples")
get_property(TARGET_PACKAGE GLOBAL PROPERTY TARGET_PACKAGE)
install(TARGETS ${PROJECT_NAME}
COMPONENT dw-samples-${TARGET_PACKAGE} # targeted package (dw / dav / ..)
DESTINATION ${SDK_SAMPLE_DESTINATION}
)
sdk_add_sample(${PROJECT_NAME})

Run cmake and the preferred build system to compile the library. This will generate a libsample_vehicleio_plugin.so.

See VehicleIO Plugin Sample for more details.