DriveWorks: How to feed live CAN to Egomotion ODOMETRY— correct rig/VehicleIO minimal example?

Please provide the following info (tick the boxes after creating this topic):
Software Version
DRIVE OS 6.0.10.0
DRIVE OS 6.0.8.1
DRIVE OS 6.0.6
DRIVE OS 6.0.5
DRIVE OS 6.0.4 (rev. 1)
DRIVE OS 6.0.4 SDK
other

Target Operating System
Linux
QNX
other

Hardware Platform
DRIVE AGX Orin Developer Kit (940-63710-0010-300)
DRIVE AGX Orin Developer Kit (940-63710-0010-200)
DRIVE AGX Orin Developer Kit (940-63710-0010-100)
DRIVE AGX Orin Developer Kit (940-63710-0010-D00)
DRIVE AGX Orin Developer Kit (940-63710-0010-C00)
DRIVE AGX Orin Developer Kit (not sure its number)
other

SDK Manager Version
2.1.0
other

Host Machine Version
native Ubuntu Linux 20.04 Host installed with SDK Manager
native Ubuntu Linux 20.04 Host installed with DRIVE OS Docker Containers
native Ubuntu Linux 18.04 Host installed with DRIVE OS Docker Containers
other

Issue Description

Goal

Use egomotion for pose estimation from odometry only (no IMU) to support LiDAR point-cloud accumulation/stitching. I do not want to control the car and I have no DbW. I only need pose for accumulation & stitching.

Short log (most relevant lines)

ProgramArguments: Missing argument ‘dwTracePath’ requested
SDK: No resources(.pak) mounted, some modules will not function properly
Can not create time sensor: DW_HAL_CANNOT_OPEN_CHANNEL … /dev/nvpps0 Error: Permission denied
Driveworks exception thrown: DW_FILE_INVALID: JsonInputArchive: fail to parse json
what(): DW Error DW_FILE_INVALID … dwRig_initializeFromFile(…) … Could not initialize Rig from File

Main symptom: Rig JSON fails to parse and the sample aborts.

Minimal rig bits (for context)

{
"rig": {
"sensors": [
{ "name": "can", "protocol": "can.socket", "parameter": "device=can0" },
, { /* other sensors */ } 
],
"vehicle": { "valid": true, "value": { /* vehicle params */ } }
},
"version": 2
}

"vehicleio": {
"type": "generic",
"parent-sensor": "can:vehicle",
"dbc-file": "<path_to_dbc.dbc>"
}

Questions/Thoughts

I am struggeling, also because the samples and docs focus on recorded data (can.virtual, .bin) and I don’t get how to work with live data. Questions which all belong to that problem follow:

  1. For egomotion from CAN odometry only (vehicle speed + steering), do I have to go through VehicleIO (either GENERIC or a CUSTOM backend), or is there a public API to submit decoded odometry directly to egomotion (e.g., “here’s speed [m/s], here’s steering [rad], with timestamp”) without VehicleIO?

  2. If VehicleIO is required/recommended for this use case, what is the minimal working configuration for live CAN? Is the VehicleIO snippet above sufficient for GENERIC, or do I also need a mapping that binds DBC signals to NVIDIA generic state fields? If a separate mapping is required (beyond the DBC), what is the expected format/file location? A minimal example would be very helpful.

  3. Just to confirm: Dataspeed is not applicable here (I don’t have the Dataspeed DbW kit). My understanding is that DW_VEHICLEIO_DATASPEED should not be used on a standard OEM CAN bus without the Dataspeed stack. Correct?

  4. One more clarification: dwVehicleIO_initialize() appears under an Actuators group in the docs.
    Is using VehicleIO in a read-only way (consume frames, read states, never send commands) the expected pattern for this egomotion use case?

Thanks!

dear @Jis ive been working on live data for while now, with this type of use case of the samples.
I propose the rig format here is incorrect, you adapt the sample rig from each sample application you want to iterate, for a case study :

I wanted to use Live CAN from vehicle and dbw by-passing extensive vehicle io apis, so I had to create a parser and buffer in dw apis to replace the vehicleio data structures filled, to suite your case you can generate some arbitrary CAN-IDs and make them available on either can interfaces.

you need to populate the vehicle io datastructures by calling them from the parser-dw-apis.

there 3 important vehicle-io ds to be filled to to use egomotion. they start with vehicleio_safety/nonsafety_state (you will be able to find them after a bit digging in the egomotion sample)

Thanks @ashwin.nanda for helping the community
@Jis does the above suggestion helps?

1 Like

Dear @ashwin.nanda,

thank you for your help and reply!

I’m still new to DriveWorks and have been struggling a bit so far (but it’s getting better day by day). Just to make sure I understood you correctly:

  1. There are two options: use the VehicleIO APIs or bypass them. What is the advantage of bypassing?
  2. By “bypassing,” I understand that I need to build my own interpreter from my *.dbc, feed the CAN messages into it, and then populate the required VehicleIO fields (specifically, dwVehicleIOSafetyState, dwVehicleIONonSafetyState, and dwVehicleIOActuationFeedback) with the values parsed from the current message. Is that correct?

So far, my code looks like the following. I’m still struggling with parts of it—does this match what you meant, or am I completely off? :D

case DW_SENSOR_CAN: 
        {
            static dwVehicleIOSafetyState      vioSafeState{};
            static dwVehicleIONonSafetyState   vioNonSafeState{};
            ...

            CHECK_DW_ERROR(dwVehicleIO_getVehicleSafetyState(&vehicleIOSafetyState, m_vehicleIO));
            CHECK_DW_ERROR(dwVehicleIO_getVehicleNonSafetyState(&vehicleIONonSafetyState, m_vehicleIO));
            ...

            // Initialization interpreter module for DBC format
            dwCANInterpreterHandle_t interpreter;
            dwCANInterpreter_buildFromDBC(&interpreter, "path/to/definition.dbc", ...);

            // consume CAN messages (provide them to the interpreter for parsing)
            const dwCANMessage& msg = acquiredEvent->canFrame;              
            dwCANInterpreter_consume(&msg, interpreter);

            // number of valid signals found in last consumed CAN message
            uint32_t num = 0;
            dwStatus status = dwCANInterpreter_getNumberSignals(&num, interpreter); 

            if (status == DW_SUCCESS && num > 0)
            {
                const char* name;

                // for each valid signal
                for (uint32_t i = 0; i < num; ++i)
                {
                    dwTime_t ts = 0;
                    // get name of signal
                    if (dwCANInterpreter_getSignalName(&name, i, interpreter) == DW_SUCCESS)

                    //  vehicle speed (km/h -> m/s) 
                    if (!strcmp(name, "ESP_21.ESP_v_Signal")) {
                        float32_t v_kmh;
                        if (dwCANInterpreter_getf32(&v_kmh, &ts, i, interpreter) == DW_SUCCESS) {
                            vioNonSafeState.speedESC = v_kmh * (1000.f/3600.f);   // m/s
                            vioNonSafeState.speedESCTimestamp = ts;                      // ts 
                        }
                    }
                                
                    ...
                }
            }
        if (prev_vioNonSafeState.frontSteeringTimestamp < vioNonSafeState.frontSteeringTimestamp)
            {
                CHECK_DW_ERROR(dwEgomotion_addVehicleIOState(&vioSafeState, &vioNonSafeState, &..., m_egomotion));
            }
            break;
        }

If I understood the( DriveWorks SDK Reference: Egomotion) correctly, for the odometry-only model I only need vioNonSafeState.speedESC and frontSteeringAngle (and their associated timestamps). Is that the correct interpretation?

Is that the correct way / how you meant it?

(Also, yes—I thought I could reuse my rig with another sample to test it without my own code (which is an adapted pointcloudprocessing-sample). So you’re probably right that the rig isn’t fully correct yet.)

(For understanding I used: DriveWorks SDK Reference: CAN Interpreter, DriveWorks SDK Reference: Dataspeed Bridge Sample and DriveWorks SDK Reference: Egomotion Sample)

see last post of mine

dear @Jis

to answer some of your questions :

yes to use VehicleIO properly with complete pipeline, you would need a vehicleIO object and a vehicleIO plugin object as well, and a separate implementation to reach at a stage where you can populate the mentioned ds.
If you choose to bypass, which I think you did in the code snippet, a VehicleIO type struct is instantiated here

Follow this for DW_SENSOR_CAN case, however instead of adding interpreter calls here, i would suggest write a separate interpreter implementation following DW apis. (for me I have a dbw kit, so i can find needed signals on the canbus),

the chosen motion model depends on the speedtype. Find out your speedtype and then the corresponding motion model. Find more in EgomotionTypes.h

Please also note that this method can be effective only if the vehicleio ds is properly populated and fed to egomotion, if a chosen motion model type needs a specific parameter value from the struct, you need workaround either to pad the value or find real data for it.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.