Energy-Efficient Autonomous Navigation Benchmarking

:racing_car: Powersense_RC_Car: Energy-Efficient Autonomous Navigation Benchmarking

:bullseye: Project Overview

With generous help from Dr. Marco Brocanelli and his lab, I am building a research-oriented framework designed to profile the energy-to-performance trade-offs of autonomous platforms.

By integrating high-fidelity sensors (Intel RealSense & RPLidar) with variable computational and network constraints, this project aims to identify the sweet spot for power efficiency in edge robotics. (Hardware setup is generously provided by the lab and is mostly complete!)

:wrench: Hardware Setup:

  • Chassis: RC Chassis — Mobile platform
  • Vision: Intel RealSense D455 — Stereo depth & VSLAM
  • Lidar: RPLidar A2M* — 2D Obstacle detection
  • Compute: Jetson Nano — Onboard inference
  • Power Mon.: To be determined — Real-time power/current sensing

:waving_hand: About Me & This Log

I’m Rocky Shao, a freshman at The Ohio State University majoring in Computer Engineering.

To keep myself on track with building this project, I will be posting my daily progress right here. Follow along on my journey as I dive deep into the world of robotics!

:link: Check out the GitHub Repository

2 Likes

March 14

My current idea:

  • Get raw data from sensors (RealSense, LiDAR) and publish them to ROS topics.
  • Run a policy node that uses the camera image, depth map, IMU data, and LiDAR scan to auto-navigate.
  • Attach a power-measuring device to the battery.
  • Tweak CPU frequency, offload edge computing, and log the data.

What I did today: I spent some time wrestling with mixed dependencies between my Python virtual environment and the system-installed ROS 2 Jazzy. I also struggled to find my running ROS nodes, only to realize I just needed to run ros2 daemon stop to get them listening to each other.

After sorting out those dependency headaches, I successfully got the RealSense color image and depth map working:

Armed with a better understanding of the setup, getting the RPLIDAR A2M8 running was fairly quick:


So right now, I’ve got the raw sensor data successfully publishing via ROS topics!

Tomorrow’s Plan: I plan to dive deeper into researching the hardware setup, specifically focusing on the power supply for the RC chassis.

Stay tuned!

1 Like

March 16

Today is a big day for hardware!

To measure energy efficiency, I need sensors to track power consumption for the battery, the sensors, and the Jetson (assuming the power sensors themselves don’t use much power?). My mentor, Chavan Akshar, let me know that the Jetson can actually measure its own power consumption, which is super handy.

Looking for Goodies

I did a bit of “thrifting” around the lab, sorting through some old and unused tools and electronics. I also managed to score an empty table to set up as my dedicated test bench:

Found Goodies

First, I found an old test bench (pictured on top right) that I got permission to disassemble for parts. Scavenging through it, I found these power consumption sensors:

With Akshar’s help, I formulated the following plan:

  • INA 260 (small, blue ones): Can be connected between the motors and the battery.
  • INA 3221 (bigger, black ones): Has a lower current capacity and can be used to measure sensor power.

After sorting that out, I had to disassemble the car to expose its electronics. This took way longer than expected—mostly because I was scared I’d forget how to put it back together :sweat_smile:


Tomorrow’s Plan

Tonight, I’ll be reading the datasheets for both power sensors to figure out how to physically wire them up to the power sources and read their outputs. Tomorrow, I’ll take a deeper look at how the car’s electronics are connected, solder the power-measuring sensors, and wire up the data output pins.

I currently don’t have access to a Jetson (another lab member is using it for a project, but they should be finished soon).

Once the hardware is finalized, I’ll look into writing a policy node for self-navigation and make a plan for measuring power consumption under different conditions. (Though I’m miles away from that part, haha!)

March 17

Today I understood how to use the INA3221 power module.

I started by searching for documentation and tutorials for the INA3221 board. After glancing at the official datasheet from TI and getting pretty overwhelmed, I found this amazing, easy-to-understand, and well-written INA3221 tutorial.

I took some sweet time to understand how the INA3221 works under the hood. To measure the power of a load, it measures voltage twice:

  1. First, the voltage across a shunt resistor (a resistor with a very small resistance value) to get the current of the load.
  2. Second, the voltage difference from the negative terminal of the shunt resistor (which is the positive terminal of the load, since the load and shunt resistor are wired in series) to the common ground (shared by the power supply, load, and the INA3221 itself).

Multiply those two values together, and you get instantaneous power! The INA3221 also has its own internal clock to handle converging and averaging, plus other settings for operating modes, alerts, etc.

Here are my notes:

To test it out, I wired a 220-ohm resistor in series with the INA3221 power module. I connected the INA3221’s SDA and SCL pins to an ESP32 Freenove Wrover (SDA to pin 21), and used the ESP32 as both the power supply and common ground for the whole setup.

I used VSCode’s PlatformIO extension and threw together a quick file to print out the voltage and current for the resistor:

Voltage: 3.30 V  |  Current: 0.00 A  |  Power: 0.00 W
Voltage: 3.29 V  |  Current: 0.00 A  |  Power: 0.00 W
...

It said the load voltage was 3.3V (which I was supplying), but there was zero current. Did that mean the load was disconnected from rest of the circuit?

I double-checked my wiring, and it was correct. Turns out the current was just too small to show up on the default print formatting (3.3V and a 220-ohm resistor should give about 0.015 A). So, I forced it to print out more digits:

Serial.print(current, 4);

And it worked… but these numbers weren’t right!

Voltage: 3.29 V  |  Current: 0.0008 A  |  Power: 0.0026 W
Voltage: 3.29 V  |  Current: 0.0008 A  |  Power: 0.0026 W
...

Turns out I accidentally grabbed a 4k-ohm resistor, not a 220-ohm one. Whoops! After switching to the correct resistor:

Voltage: 3.28 V  |  Current: 0.0148 A  |  Power: 0.0485 W
Voltage: 3.28 V  |  Current: 0.0148 A  |  Power: 0.0485 W
...

Bingo! 3.28V / 220 ohms = ~0.0149 A. It lines up perfectly!

Later, I unplugged and replugged the ESP32 from my laptop, and the port changed. When I tried to upload the compiled code, it complained that /dev/ttyUSB0 didn’t exist.

Gemini taught me how to set up a permanent /dev/esp32 port using udev rules:

sudo nano /etc/udev/rules.d/99-esp32.rules

In the .rules file, I added:

SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="esp32", MODE="0666"

Save, close the file, and run:

sudo udevadm control --reload-rules && sudo udevadm trigger

Now the port never breaks when I unplug the ESP32. Hurray!

Tomorrow’s plans

Tomorrow I will explore how to use the INA62 module, and how to publish power-module readings to ROS topics (as it currently interface with esp32 but final version will be interfacing with jetson nano)

March 18

ESP32 Power Monitor Bridge & Dynamic ROS 2 Publishers

Today, I spent some time figuring out the architecture for my power monitoring setup. I evaluated two different paths for wiring up my INA3221 power monitors:

  • Path 1: Connect the INA3221 directly to the Jetson’s GPIO pins and use the Jetson for direct I2C communication.
  • Path 2: Use an ESP32 as a bridge. Connect the power monitors to the ESP32 via I2C, then connect the ESP32 to the Jetson via USB. The Jetson would then read the INA3221 data as serial output from the ESP32.

The Decision: I decided to go with Path 2 (ESP32 Bridge). This lets me leverage the existing Arduino libraries to easily interface with the INA3221. Plus, by connecting the ESP32 to the Jetson via USB, the Jetson can internally measure the power draw coming from the ESP32 via its own USB ports.

Serial Communication & Custom Formatting

Currently, the INA3221 is only reading 1 channel (as only one is connected to my test resistor). The ESP32 outputs the serial data in a CSV-style format. To handle multiple channels cleanly, I built a comma/semicolon-mixed data format that loops through all 3 channels:

  #Loop through all 3 channels
  Serial.print(SENSOR_ID);
  Serial.print(",");
  Serial.print(channel_idx + 1);
  Serial.print(",");
  Serial.print(busVoltage, 4);
  Serial.print(",");
  Serial.print(current, 4);
  Serial.print(",");
  Serial.print(power, 4);

The resulting serial output string looks like this:

0,1,12.1000,0.4500,5.4450;0,2,0.0000,0.0000,0.0000;0,3,11.9000,0.1200,1.4280

(Note: Sensor ID is always 0 right now since I only have one sensor active).

ROS 2 Integration & Dynamic Publishers

Next, I built a ROS 2 node using pyserial to read this incoming data. To keep things scalable, I set up a dictionary where the key is a tuple of (sensor_id, channel_id) and the value is a dedicated ROS publisher. This allows me to dynamically spin up publishers to different topics on the fly:

topic_name = f'/power/sensor_{sensor_id}/channel_{channel_id}'
self.channel_publishers[key] = self.create_publisher(PowerConsumption, topic_name, 10)

This successfully generated the following topics:

/power/sensor_0/channel_1
/power/sensor_0/channel_2
/power/sensor_0/channel_3

I also updated my custom ROS message (PowerConsumption.msg) to include identifiers for both the sensor and the channel:

uint8 sensor_id
uint8 channel_id
float64 voltage
float64 current
float64 power

Testing it out with ros2 topic echo /power/sensor_0/channel_1, everything works perfectly:

sensor_id: 0
channel_id: 1
voltage: 3.256
current: 0.0148
power: 0.0482

This architecture makes it super safe to scale to more channels per sensor, or even add entirely new INA3221 boards if I run out of channels.

Scaling Up: Adding the INA260

To test that scalability, I added an INA260 to the mix and connected it to the ESP32 using a very similar approach.

One hardware hurdle: The INA3221 was already occupying I2C address 0x40. To fix the conflict, I bridged the A1 soldering pads on the INA260, changing its address to 0x41.

The Final Polish

To make the ROS ecosystem more user-friendly, I created a config.json file to map these raw, dynamically generated topics into human-readable names. After some cleaning, refactoring, and storing the wiring constants, my final ROS topic list looks like this:

/power/by_name/ina260_aux/single_channel
/power/by_name/ina3221_main/accessory_rail
/power/by_name/ina3221_main/compute_rail
/power/by_name/ina3221_main/drive_motor

It’s feeling much more robust now. Let me know what you guys think of this approach!

Wiring

The current test board for the power modules are really messy XD.
But if it works, don’t touch it!

1 Like

March 19: Magic Smoke & INA260 Mysteries

Magical Smoke :dashing_away:

Yesterday I was able to measure a fixed resistor, so today I wanted to level up! My goal was to gather variable power-draw data over time and try to learn some tools to plot power vs. time.

I had one of these potentiometers on hand, and I connected it in series with my INA260.

My wiring went from: ESP32 3.3V positive → INA260 → B10k VCC → B10k Output → ESP32 ground, completely neglecting the B10K GND pin.

As I turned the knob, I saw the voltage varying nicely:

voltage: 3.8788
current: 1.0688
power: 4.15

Then… the magic smoke came out. :sweat_smile:

Turns out, if you turn the knob aggressively to one side, the resistance between B10k pin 1 and pin 2 goes to 0 ohms, effectively creating a dead short. What was even scarier is that I used a USB to connect the ESP32 to my laptop. Thankfully, my laptop was smart enough to cut power to the port, which saved both my ESP32 and my computer!

The INA260 Mystery :man_detective:

In addition, while playing with the INA260, I found that I have to make the current flow through the INA260 first, and then to the load (a 220-ohm fixed resistor) to get normal readings:

voltage: 4.965
current: 0.0225
power: 0.12

If the current flows through the load first and then to the INA260, I get these weird values:

voltage: 0.0012
current: 0.0237
power: 0.0

I have no idea why this happens. Theoretically, I feel like this shouldn’t happen, so I think I’m missing something really obvious.

CPU Frequency Set :high_voltage:

On a brighter note, AI helped me find this handy command to set a specific CPU frequency, which will be great for when I run other tests:

sudo cpupower frequency-set -d "${freq}GHz" -u "${freq}GHz"

I’m having a small headache from inhaling the magic smoke haha.

Up Next: Tomorrow I’ll explore more data collection and visualization methods to finally plot that power consumption over time graph!

March 29

Project development slowed down after Spring Break, due to mid-terms and homework.

From Jetson to Raspberry Pi 4

Due to availability of hardware, I’ve decicded to replace my planned Jetson Nano with Raspberry Pi 4.

I spend today cross referencing This amazing Documentation, example code provided by my lab members, and the wiring of the robot to revese engineering a schematic.

With the help of AI, I reconstructed this GPIO pin map:


But I still have very little understanding of the flow of power & data across the system.

(It’s quite discouraging to trace through this rat-nest of wires haha)

In the coming week I plan to carefully take out each component, part by part, and test each one individually to gain familiarity and confidence with each individual component and a deeper understanding of the system.

I also find it quite challenging to balance school work while doing this project. We shall see how it goes.