Rivanna3 Development Guide
This page is the main source for explanations about development decisions, and how implemented logic is meant to work at a high-level, for the Rivanna3 Embedded Codebase
Project Architecture
At a high level, the project is structured into folders as follows:
- Boards:
WheelBoard
,TelemetryBoard
, andPowerBoard
hold code for those respective boards Common
contains code that is used across the entire project; a significant portion of it is auto-generated- MbedOS:
mbed-os
andTARGET_...
folders contain MbedOS libraries and configuration cmake_build
contains the compiled code from across the project that is actually uploaded to the car- Legacy:
DriverBoard
,BatteryBoard
, andMotor
are legacy boards and code used in Rivanna2S, and are only shown in Rivanna3 for easy reference
CAN
Controller Area Network is a commonly used serial protocol in the automotive industry. We use this as our main mode of data transmission between components of the car for 2 main reasons:
- Multiple devices can be connected at once. Many serial protocols only support having 2 connected devices at once, like UART.
- All nodes can send and receive messages without central coordination. This removes the need for a ‘central’ hub node, unlike protocols using a Master-Slave Topology like I2C.
There are 2 other benefits as well that come with CAN by default, but could be easily replicated and are common:
- A robust error handling system for when messages fail to send.
- The hardware specification is built on differential signaling, which greatly reduces the impact of electrical noise and voltage supply imperfections.
Terminology
- Node: any device on the CAN network
- CAN bus: the physical communication network
- CAN network: the high-level concept of the CAN bus with all its nodes attached
- CAN frame: a serialized CAN message
- CAN message: data and identifier, in a readable format
- Signal: a specific piece of data in a CAN message, like a throttle value
- CAN Transceiver: special hardware that converts logic signals to and from the CAN bus’s physical specification
Defining CAN messages
Each of our CAN messages is defined in a DBC file. This file specifies what data a given CAN message will hold and its identifier; CAN messages cannot be defined ad-hoc, they must meet an already defined specification.
Our Messages are defined here. We use the Python package cantools to read and write CAN messages in Python, and to generate the C structs for each message used in the Embedded Codebase.
Sending and receiving CAN messages in the Embedded Codebase
Under <board name>/lib/src
for each board there is a file <board name>CANInterface.cpp
that represents the actual CAN connection for each board.
- To send messages: code in
main.cpp
calls thesend()
function, and the message is sent and logged. - To receive messages: the
message_handler()
function waits for CAN messages. When one is received, the function logs the message, and checks if the message is one of the types of CAN messages it is waiting for. If so, the function calls the appropriatehandle()
function inmain.cpp
; if not, the message is disregarded.
This is further discussed in an onboarding presentation.
Rivanna3 CAN Networks
The current CAN networks are as follows:
- Main CAN - almost everything is attached to this network, when people say ‘the CAN network’ they mean main CAN
- Motor CAN - connects the motor controller and the PowerBoard
- BMS Config - used solely by the Power subteam to configure the battery management system
Precharge
Precharge is a system used on the high-voltage part of the car’s electrical system to prevent inrush current. This section refers to the motor, but also applies to the MPPTs as well. Precharge logic and hardware is managed by the Power Subteam; Embedded only implements the logic in software.
Theory
When the motor is connected to the HV system, the circuit can be modeled as a simple RC circuit, because of the inherent capacitance and resistance of the motor. The time taken to charge the capacitor is proportional to resistance * capacitance
. By the inherent motor properties, the time constant is small, and the inherent resistance is very small. This results in very high current when the motor is first energized, as the capacitor is charging fast with little resistance; this is known as inrush current, which is bad because it causes extra wear and tear on the motor.
The solution: Add another resistor in series to increase the time constant, and therefore reduce the magnitude of current. This resistor is in parallel with a relay (switch) to enable and disable (short it) it, because:
- The resistor should be enabled when first energizing the motor, to reduce inrush current.
- The resistor should be disabled when the motor is in operation, so the motor can quickly draw the current it needs; the time constant must be minimized for the motor to be responsive.
Embedded implements the logic that controls this relay.
Logic
Normal Precharge operation:
- Open the relay (enabling the precharge resistor)
- Wait until contactor 12 is below xxx V, or xxx seconds have passed
- Close the relay (disabling the precharge resistor)
Edge cases: TBD