DexHand Logger
The DexHand logger module provides data logging and analysis capabilities for hand operation.
Core Components
Log Entry Types
- class pyzlg_dexhand.dexhand_logger.LogEntry(timestamp, hand, entry_type)[source]
Base class for log entries
-
entry_type:
str
-
hand:
str
-
timestamp:
float
-
entry_type:
- class pyzlg_dexhand.dexhand_logger.CommandLogEntry(timestamp, hand, entry_type, command_type, joint_commands, control_mode)[source]
Log entry for a hand command
-
command_type:
str
-
control_mode:
ControlMode
-
joint_commands:
Dict
[str
,float
]
-
command_type:
- class pyzlg_dexhand.dexhand_logger.FeedbackLogEntry(timestamp, hand, entry_type, joints, tactile)[source]
Log entry for hand feedback
-
joints:
Dict
[str
,JointFeedback
]
-
tactile:
Dict
[str
,StampedTactileFeedback
]
-
joints:
Logger Classes
Main Logger
- class pyzlg_dexhand.dexhand_logger.DexHandLogger(log_dir='dexhand_logs')[source]
Logger for dexterous hand commands and feedback with background writing
- __init__(log_dir='dexhand_logs')[source]
Initialize hand logger
- Parameters:
log_dir (
str
) – Directory to store log files
- log_command(command_type, joint_commands, control_mode, hand, feedback=None)[source]
Log a command without blocking
- Parameters:
command_type (
str
) – Type of command (move_joints, reset_joints, etc)joint_commands (
Dict
[str
,float
]) – Dictionary of joint name to commanded positioncontrol_mode (
ControlMode
) – Control mode usedhand (
str
) – Which hand (‘left’ or ‘right’)feedback (
Optional
[HandFeedback
]) – Optional HandFeedback if feedback was collected
- log_feedback(feedback, hand)[source]
Log feedback without blocking
- Parameters:
feedback (
HandFeedback
) – HandFeedback from pollinghand (
str
) – Which hand (‘left’ or ‘right’)
- plot_session(hands=None, show=True, save=True)[source]
Plot command and feedback data from the session
This method should be called outside the control loop, typically during analysis or after the session.
- Parameters:
hands (
Optional
[List
[str
]]) – Which hands to plot (‘left’, ‘right’, or both)show (
bool
) – Whether to show plots interactivelysave (
bool
) – Whether to save plots to files
Background Writer
- class pyzlg_dexhand.dexhand_logger.LogWriter(session_dir)[source]
Background thread for writing log entries to files
- __init__(session_dir)[source]
This constructor should always be called with keyword arguments. Arguments are:
group should be None; reserved for future extension when a ThreadGroup class is implemented.
target is the callable object to be invoked by the run() method. Defaults to None, meaning nothing is called.
name is the thread name. By default, a unique name is constructed of the form “Thread-N” where N is a small decimal number.
args is a list or tuple of arguments for the target invocation. Defaults to ().
kwargs is a dictionary of keyword arguments for the target invocation. Defaults to {}.
If a subclass overrides the constructor, it must make sure to invoke the base class constructor (Thread.__init__()) before doing anything else to the thread.
Examples
Basic Logging
from pyzlg_dexhand import DexHandLogger, ControlMode
# Initialize logger
logger = DexHandLogger(log_dir="dexhand_logs")
# Log a command
logger.log_command(
command_type="move_joints",
joint_commands={"th_rot": 30, "th_mcp": 45},
control_mode=ControlMode.CASCADED_PID,
hand="right"
)
# Log feedback
logger.log_feedback(feedback_data, hand="right")
# Save session metadata
logger.save_metadata({
"operator": "user1",
"experiment": "grasp_test",
"conditions": "normal"
})
# Generate plots and close
logger.plot_session(show=False, save=True)
logger.close()
Visualization
# Plot specific hands
logger.plot_session(
hands=["right"],
show=True,
save=True
)
Analysis
# Get session statistics
with logger.buffer_lock:
cmd_count = len(logger.command_buffers["right"])
fb_count = len(logger.feedback_buffers["right"])
print(f"Commands: {cmd_count}, Feedback: {fb_count}")
Log File Structure
Directory Layout
Each logging session creates a timestamped directory:
dexhand_logs/
└── YYYYMMDD_HHMMSS/
├── metadata.json
├── left_commands.jsonl
├── left_feedback.jsonl
├── right_commands.jsonl
├── right_feedback.jsonl
├── left_joints.png
├── left_tactile.png
├── right_joints.png
└── right_tactile.png
File Formats
Command Log (JSONL):
{
"timestamp": 1234567890.123,
"hand": "right",
"entry_type": "command",
"command_type": "move_joints",
"joint_commands": {
"th_rot": 30.0,
"th_mcp": 45.0
},
"control_mode": "CASCADED_PID"
}
Feedback Log (JSONL):
{
"timestamp": 1234567890.123,
"hand": "right",
"entry_type": "feedback",
"joints": {
"th_rot": {
"timestamp": 1234567890.123,
"angle": 30.0,
"encoder_position": 1000
}
},
"tactile": {
"th": {
"timestamp": 1234567890.123,
"normal_force": 1.5,
"tangential_force": 0.5
}
}
}
Metadata (JSON):
{
"timestamp": "2024-01-01T12:00:00",
"statistics": {
"duration": 60.0,
"num_commands": {
"right": 100
},
"num_feedback": {
"right": 500
}
},
"experiment_info": {
"operator": "user1",
"conditions": "normal"
}
}
Visualization
Joint Plots
Command vs actual joint angles over time
Tracking error analysis
Multiple joints overlaid for comparison
Tactile Plots
Normal and tangential forces over time
Force vector visualization
Contact location heatmaps
Notes
Thread Safety
Uses background thread for file writing
Thread-safe buffers for data access
Safe shutdown with data flushing
Memory Management
In-memory buffers for fast access
Periodic flushing to disk
Configurable buffer sizes (coming soon)
Visualization Options
Real-time plotting (coming soon)
Customizable plot layouts
Export to various formats