Skip to main content

GR-Power

1. Overview

GR-Power is the DDS publishing module for robot power status. The runtime Fourierpower publishes power and battery status as DDS topics for upper-layer modules to subscribe to.

ElementValue
Runtime nameFourierpower
Domain ID123
Main DDS topicshal_battery_monitor_status, hal_bms_error_code

2. DDS Interface

2.1 Published Topics

TopicMessage TypeDescription
hal_battery_monitor_statusfourier_msgs/msg/BmsStatusMsgMain and sub-battery status list.
hal_bms_error_codefourier_msgs/msg/ErrorCodesPower / battery fault-code list.

2.2 Subscribed Topics

This module currently has no subscription entry points.

3. Message Definitions

3.1 BmsStatusMsg

Idl
struct BmsStatus {
double voltage;
double current;
double temperature;
uint8 level;
uint8 level_cap;
double mos_temperature;
boolean failure;
boolean battery_comm;
boolean output_mos_open;
boolean input_mos_open;
};

struct BmsStatusMsg {
std_msgs::msg::Header header;
sequence<BmsStatus> bms_state_list;
};

bms_state_list[0] is the main battery. bms_state_list[1] is the sub-battery.

3.2 ErrorCodes

Idl
struct ErrorCode {
uint32 high32;
uint32 low32;
};

struct ErrorCodes {
std_msgs::msg::Header header;
uint8 source; // 4 = power / battery
sequence<ErrorCode> error_codes;
};

high32 is encoded by GR-Power as:

Text
level = high32 & 0xF
code = (high32 >> 12) & 0xFFFF
type_flag = (high32 >> 28) & 0xF
type_flagMeaning
0BMS1 battery fault
1BMS2 battery fault

4. Basic Examples

4.1 Python - Subscribe to Battery Status

Python
import time

from fourierdds_py import DDSInterface, SubscriberQosProfile
import fourier_msgs.msg.BmsState as BmsState

dds = DDSInterface(domain_id=123)


def seq_items(seq):
size = seq.size() if hasattr(seq, "size") else len(seq)
return [seq[i] for i in range(size)]


def on_bms(msg):
states = seq_items(msg.bms_state_list())
if not states:
print("[bms] empty state list")
return

for index, state in enumerate(states):
name = "main" if index == 0 else f"sub{index}"
print(
f"[bms:{name}] "
f"level={state.level()}% "
f"voltage={state.voltage():.3f}V "
f"current={state.current():.3f}A "
f"temperature={state.temperature():.1f}C "
f"failure={state.failure()} "
f"comm={state.battery_comm()}"
)


dds.create_subscription(
BmsState.BmsStatusMsgPubSubType,
"hal_battery_monitor_status",
on_bms,
qos_profile=SubscriberQosProfile.default(),
)

while True:
time.sleep(0.1)

Run example:

Bash
python3 watch_grpower_status.py

If the runtime service is root-owned and callbacks are matched but not received, run while preserving the DDS environment:

Bash
sudo env "PYTHONPATH=$PYTHONPATH" "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" python3 -u watch_grpower_status.py

4.2 Python - Subscribe to Fault Codes

Python
import time

from fourierdds_py import DDSInterface, SubscriberQosProfile
import fourier_msgs.msg.ErrorCodes as ErrorCodes

dds = DDSInterface(domain_id=123)


def seq_items(seq):
size = seq.size() if hasattr(seq, "size") else len(seq)
return [seq[i] for i in range(size)]


def decode_high32(high32):
return {
"level": high32 & 0xF,
"code": (high32 >> 12) & 0xFFFF,
"type_flag": (high32 >> 28) & 0xF,
}


def on_error(msg):
print(f"[power_error] source={msg.source()} count={len(seq_items(msg.error_codes()))}")
for index, item in enumerate(seq_items(msg.error_codes())):
decoded = decode_high32(item.high32())
print(
f" [{index}] "
f"type_flag={decoded['type_flag']} "
f"level={decoded['level']} "
f"code=0x{decoded['code']:04X} "
f"high32=0x{item.high32():08X} "
f"low32=0x{item.low32():08X}"
)


dds.create_subscription(
ErrorCodes.ErrorCodesPubSubType,
"hal_bms_error_code",
on_error,
qos_profile=SubscriberQosProfile.default(),
)

while True:
time.sleep(0.1)

4.3 C++ - Subscribe to Battery Status

C++
#include <BmsState/BmsStatePubSubTypes.hpp>
#include "fourier_dds/dds_node.hpp"
#include "fourier_dds/dds_subscription.hpp"

#include <chrono>
#include <iostream>
#include <memory>
#include <thread>

int main() {
auto node = std::make_shared<fourier_dds::DdsNode>(123);

auto bms_sub = std::make_shared<
fourier_dds::DdsSubscription<fourier_msgs::msg::BmsStatusMsgPubSubType>>(
node,
"hal_battery_monitor_status",
[](const fourier_msgs::msg::BmsStatusMsg& msg) {
const auto& states = msg.bms_state_list();
for (size_t i = 0; i < states.size(); ++i) {
const auto& state = states[i];
std::cout << "[bms:" << i << "] "
<< "level=" << static_cast<int>(state.level()) << "% "
<< "voltage=" << state.voltage() << "V "
<< "current=" << state.current() << "A "
<< "failure=" << state.failure()
<< std::endl;
}
});

while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}

Build example:

Bash
g++ -std=c++17 watch_grpower_status.cpp -o watch_grpower_status \
-I/opt/fftai/include/fourier_dds \
-I/opt/fftai/fourier_dds_msgs/include/fourier_dds_msgs \
-L/opt/fftai/fourier_dds_msgs/lib \
-Wl,-rpath,/opt/fftai/fourier_dds_msgs/lib \
-lBmsState \
-lfastdds \
-lfastcdr \
-pthread

Run example:

Bash
sudo env "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" ./watch_grpower_status

5. Advanced - Runtime Workflow

5.1 Observe Battery Status

  1. Start Fourierpower on the robot.
  2. Run the status subscriber:
    Bash
    python3 watch_grpower_status.py
  3. Confirm the subscriber receives hal_battery_monitor_status, and verify that voltage, current, level, and communication state in bms_state_list are as expected.

5.2 Observe Fault Codes

  1. Start the fault-code subscriber:
    Bash
    python3 watch_grpower_errors.py
  2. Confirm the subscriber receives hal_bms_error_code with source=4.
  3. Use type_flag, level, code, and low32 to interpret the fault source and bit fields.

6. Troubleshooting & Reminders

No battery status received

Confirm Fourierpower is running, DDS domain is 123, and the subscriber uses the topic name and message type listed above. If the subscriber is matched but no callback is received, check the DDS environment variables, runtime user, and dynamic library path first.

No fault codes received

Confirm the subscriber uses hal_bms_error_code and fourier_msgs/msg/ErrorCodes, and that the DDS domain matches Fourierpower. ErrorCodes updates only when GR-Power publishes a fault.

source=4 in ErrorCodes

GR-Power sets ErrorCodes.source to 4, meaning the fault came from the power / battery module.