HumansXドキュメントセンター English公式サイトへ ↗
ナレッジベース › Wuji Hand 2 › マニュアル

Diagnostics and Logs

SDK Reference

このページは現在英語版のみです。日本語でのご説明が必要な場合は HumansX までお問い合わせください。

Fault Logs

After connection, the SDK logs faults without a joint_diagnostics subscription. For log locations, fault states, and code lookup, see Fault Logs and Error Codes.

After the device restarts, the SDK’s first received status records faults that the device saved as active before power loss, marked state=active_before_power_off. The device saves up to 15 active fault-code records and records how many did not fit. These records describe the state before power loss. They don’t establish that a fault is still active after restart or provide a complete event history from before connection.

When a reporting list is truncated, the log reports omissions. The SDK doesn’t treat an omitted fault as cleared solely because the list was truncated. Retain these messages and check the current state when troubleshooting.

joint_diagnostics (SUB)

Joint diagnostics stream: subscribe to per-joint status word, current, bus voltage, temperature, and error code. Frames are variable-length and contain only online joints — identify each entry by its nid. This stream and joint_states ride the same device stream, so lowering the push rate of one lowers both — see joint_states (SUB).

sub = hand.joint_diagnostics().subscribe() # → Subscription[JointDiagnosticsFrame]
frame = await sub.recv_async()
for j in frame.joints:
    print(j.nid, j.vbus_v_fb, j.mcu_temp_c_fb, f"0x{j.error_code_current:04X}")
sub.close()

Decode Error Codes

Use WujiHand2.describe_error(code) to get the fault cause and recommended action. Unknown codes return None. See Device Fault Codes.

Object Display Format

Python uses the following format for error_code_current in the repr() of JointDiagnosticsEntry:

Error Coderepr() Display
Known nonzero value0x2102(Overcurrent)
Zero0x0000
Unknown value0xFFFF(Unknown)

This changes only the display format. error_code_current remains an integer that you can pass to describe_error().

error_code_current is a native integer fault code. Look it up with WujiHand2.describe_error(code). Unknown codes return None. Printing a diagnostics entry shows the error code in hexadecimal.

For joint angles, use position from the joint_states stream — the diagnostics stream doesn’t carry joint angles.

comm_diag (GET, 1 Hz)

Communication diagnostics: returns the throughput / error rate of the 5 fingers, once per second.

diag = hand.comm_diag().get() # → HandCommunicationDiagnostics
for finger in diag.fingers: # throughput / error rate for each of the 5 fingers
    print(finger.tx_kbps, finger.rx_kbps, finger.error_per_sec)

Use it to troubleshoot communication between the joints and the control board.

SDK Log Level

Adjust the SDK log level for debugging:

import wuji_sdk

wuji_sdk.set_log_level("debug")

Choose one log level per call:

LevelOutput
traceAll logs
debugDebug and higher-severity logs
infoInfo and higher-severity logs (default)
warnWarnings and errors
errorErrors only
offNo logs

The SDK writes its log to the terminal and to ~/.wuji/logs/sdk_<date>.log. The kp / kd readback record for each connection uses the info level. For the format, see mit_params.

Retrieve the device firmware’s own runtime logs with Flash Log Export.

Flash Log Export (Diagnostics)

# This call blocks until the export finishes.
result = hand.export_flash_logs("./logs")
print(result["path"])      # str: absolute path to the exported JSONL file
print(result["frames"])    # int: number of exported log records

The SDK writes one flash_<serial>_<YYYY-MM-DD_HHMMSS>.jsonl file to out_dir. Omit out_dir to use ~/.wuji/logs.

The SDK writes to a temporary file and makes the JSONL file available only after the export finishes. If the export fails, the SDK removes the temporary file, so the output directory contains no partial export.

A device supports concurrent log exports.

When multiple sessions export logs from the same device, the SDK retries automatically. If the export still can’t finish, the call reports that the device is busy. The SDK removes the temporary files and doesn’t keep an unfinished log.

Data Types

JointDiagnosticsFrame / JointDiagnosticsEntry

Joint diagnostics frame: frame type of the hand.joint_diagnostics().subscribe() stream. Variable-length, online joints only.

class JointDiagnosticsFrame:
    header: FrameHeader # seq / timestamp_us / frame_id
    num_joints: int
    joints: list[JointDiagnosticsEntry]
    comm: Hand2CommSummary # frame-level comm health summary, 1 Hz refresh (see Hand2CommSummary)

class JointDiagnosticsEntry:
    nid: int # node ID — identifies the joint across frames
    status_word: StatusWord # status word
    current: float # current (A)
    vbus_v_fb: float # bus voltage (V)
    mcu_temp_c_fb: float # MCU temperature (°C)
    error_code_current: int # current error code (integer value, shown in hex when the entry is printed) — decode with describe_error()
    comm_response_rate_pct: int # bus response rate over the last second, 0–100
    comm_timeout_total: int # cumulative bus timeouts for this joint

HandCommunicationDiagnostics

Communication diagnostics data.

class HandCommunicationDiagnostics:
    fingers: list[FingerCommunicationDiagnostics] # length 5

class FingerCommunicationDiagnostics:
    tx_frame_total: int
    rx_frame_total: int
    tx_kbps: int
    rx_kbps: int
    error_per_sec: int
    crc_error_total: int
    frame_format_error_total: int
    uart_hw_error_total: int
    transfer_stats: list[TransferStats]
    nodes: list[NodeDiagnostics] # per-node: online / ms_since_last_response / response_rate_pct

StatusWord

Status word: the type of the status_word field on JointDiagnosticsEntry, the public status view exposed by the SDK.

FieldTypeDescription
ext_stateintExtended state value (such as Init / Ready / Enabled / Stopped)
ext_state_namestrSemantic name of ext_state
position_limit_activeboolPosition limit triggered
velocity_limit_activeboolVelocity limit triggered
current_limit_activeboolCurrent limit triggered

Hand2CommSummary

Frame-level communication health summary: the type of the comm field on JointDiagnosticsFrame, combining the device-internal bus view with the SDK-local end-to-end (Ethernet) view. The device-internal part comes from a snapshot the SDK refreshes automatically at 1 Hz — no polling required.

FieldTypeDescription
age_msintAge of the device-internal snapshot in milliseconds. 65535 = never sampled successfully (the device-internal fields are zeros). 65534 = saturated, the snapshot is 65.5 s or older
e2e_receivedintFrames received across all subscribed streams (cumulative)
e2e_lostintFrames lost on the Ethernet segment, detected via sequence gaps (cumulative)
e2e_reorderedintLate/reordered frames (cumulative)
e2e_duplicatesintDuplicate frames (cumulative)
e2e_window_loss_x100intLoss rate over the last second, in 0.01% units
rpc_totalintRequests sent (cumulative). 0 means this transport does not report RPC statistics (only the wuji-proto transport does, and a live wuji-proto connection has always sent at least one request), so a zero here is not the same as “no retries happened”
rpc_retriesintRequest retransmissions (cumulative)
rpc_timeoutsintRequests that ultimately timed out (cumulative)
comm_get_failuresintFailed internal snapshot refreshes (cumulative)
sdk_droppedintFrames dropped inside the SDK because one of its in-process hops fell behind (cumulative) — either your subscription consumer or the internal stream handler. Distinct from e2e_lost, which is network loss

Reading the device-internal fields. age_ms and the per-joint comm_* fields all come from one snapshot the SDK refreshes at 1 Hz. The refresh is deliberately skipped while a firmware upgrade is running, so a briefly stale snapshot is expected rather than a fault. Tell the two cases apart with comm_get_failures: a growing age_ms while comm_get_failures stays flat means the refresh is being skipped on purpose, while comm_get_failures increasing means the refresh itself is failing.

Counters saturate, they do not wrap. Every counter in this table stops at its maximum (65535 for the 16-bit ones — e2e_reordered, e2e_duplicates, rpc_retries, rpc_timeouts, comm_get_failures — and 4294967295 for the 32-bit ones) and stays there. On a 1 kHz stream a long-running session can reach those ceilings, so read these as deltas between two samples rather than as absolute lifetime totals.

Exception Handling

All SDK operation errors raise a unified WujiException whose message carries an error-type prefix (Disconnected, Timeout, PathNotFound, SchemaMismatch, SerializeError, …). Use try / except as needed.

from wuji_sdk import WujiException

try:
    hand.joint_states().subscribe()
except WujiException as e:
    print(f"SDK error: {e}")

For the full list of exception types, see the Wuji SDK appendix — Python Exception Types. For reconnecting after a drop, see Wuji SDK best practices — Reconnection Strategy.

Device Fault Codes

error_code_current in the joint diagnostics and error_code in the error history are both u16 device fault codes. Decode them with the static method WujiHand2.describe_error(code). The returned object contains:

  • code, the error name, and desc: error code, name, and description
  • cause and resolution: fault cause and recommended action
  • severity: Warning / DeferredStop / ImmediateStop / Fatal
  • clear_policy: AutoClear / ManualClear / NonClearable

Device fault codes lay their hex digits out as 0xSCNN, but don’t decode the digits yourself — the severity and clear_policy fields returned by describe_error() carry the same information as plain strings and stay correct if the layout ever changes. Treat the code as an opaque identifier: log it, show it to the user, quote it in a bug report. When decoding succeeds, use the returned severity and clear_policy to decide how to react. When describe_error() returns None (an unknown code), don’t access those fields — keep the original value for logging and bug reports.

  • Python

  • C

Python: See the Python API examples earlier on this page.

The C API matches the Python API on this page and uses the wuji_hand_2_ prefix. For initialization conventions, memory management, error codes, and build setup, see the C API Reference.

Subscribe to Joint Diagnostics

wuji_hand_2_subscribe_joint_diagnostics has the same callback shape as wuji_hand_2_subscribe_joint_states. Each WujiJointDiagnosticsEntry carries status_word, current, vbus_v_fb, mcu_temp_c_fb, error_code_current, and per-joint bus comm quality (comm_response_rate_pct, comm_timeout_total). The frame-level comm field (WujiHand2CommSummary) adds end-to-end stream loss, RPC retry and timeout counters.

Decode Joint Status Words

wuji_hand_2_decode_joint_status decodes WujiJointDiagnosticsEntry.status_word into WujiJointStatus. ext_state is 0 for initializing, 1 for ready, 2 for enabled, or 3 for stopped. enabled is true only when ext_state == 2. The remaining fields show whether the position, velocity, and current limits are active. The function doesn’t require a device connection.

void print_joint_status(uint32_t status_word) {
    WujiJointStatus status = {0};
    if (wuji_hand_2_decode_joint_status(status_word, &status) != WUJI_STATUS_OK) {
        return;
    }

    printf("state=%u enabled=%d limits=%d/%d/%d\n",
           status.ext_state,
           status.enabled,
           status.position_limit_active,
           status.velocity_limit_active,
           status.current_limit_active);
}

The function returns WUJI_STATUS_ERR_INVALID_ARG when out is NULL.

Flash Log Export

The C SDK provides the Flash log export API. After calling wuji_hand_2_export_flash_logs(), the current thread waits until the SDK reads the device logs and writes the output file before the function returns a result struct with path and frame_count:

WujiFlashLogExport result = {0};
if (wuji_hand_2_export_flash_logs(dev, NULL, &result) == WUJI_STATUS_OK) {
    printf("%u frames -> %s\n", result.frame_count, result.path);
    wuji_hand_2_flash_log_export_free(&result);
} else {
    fprintf(stderr, "export failed: %s\n", wuji_last_error());
}

Pass NULL as out_dir to write flash_<serial>_<date>_<time>.jsonl under ~/.wuji/logs/, or pass a directory path to choose the destination. Each call creates a new file. Exports created within the same second receive a numbered suffix instead of replacing an existing file.

The returned frame count can be 0 when the device log ring is empty. The empty output file is still a successful export.

Handle common export errors as follows:

  • C returns WUJI_STATUS_ERR_INVALID_ARG when dev or out is NULL, or when out_dir isn’t valid UTF-8.
  • C returns WUJI_STATUS_ERR_UNSUPPORTED when the device isn’t Wuji Hand 2 or its firmware doesn’t support the current protocol.
  • C returns WUJI_STATUS_ERR_TIMEOUT if another export is running or if log data is overwritten during export and repeated retries still can’t complete. Check wuji_last_error() for details.
  • C returns WUJI_STATUS_ERR_DISCONNECTED if the device disconnects during export.
  • C returns WUJI_STATUS_ERR_PROTOCOL when the device reports a flash-log protocol error or returns malformed data.
  • C returns WUJI_STATUS_ERR_INTERNAL when a file-system I/O operation fails. Check wuji_last_error() for details.

For other status values, call wuji_last_error() to get the cause. A failed export doesn’t create an output file.

Call wuji_hand_2_flash_log_export_free only after a successful export.