Skip to content

Commit 59ef331

Browse files
committed
new hmi for arm :)
1 parent a8da566 commit 59ef331

25 files changed

+2982
-3
lines changed

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,6 @@ qtcreator-*
5656
# Glade temp file
5757
#*.glade*
5858

59-
#other hmi things
60-
src/rover_hmi_v2
61-
6259
# Catkin custom files
6360
CATKIN_IGNORE
6461
install/

src/rover_hmi_arm/CMakeLists.txt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
cmake_minimum_required(VERSION 3.8)
2+
project(arm_hmi)
3+
4+
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5+
add_compile_options(-Wall -Wextra -Wpedantic)
6+
endif()
7+
8+
find_package(ament_cmake REQUIRED)
9+
find_package(rclcpp REQUIRED)
10+
find_package(rover_msgs REQUIRED)
11+
find_package(std_msgs REQUIRED)
12+
find_package(Qt5 REQUIRED COMPONENTS Widgets)
13+
find_package(ament_index_cpp REQUIRED)
14+
15+
set(CMAKE_AUTOMOC ON)
16+
17+
include_directories(include)
18+
19+
add_executable(moteus_gui
20+
src/moteus_gui.cpp
21+
src/moteus_data_bus.cpp
22+
src/plot_widget.cpp
23+
src/motor_status_module.cpp
24+
src/plotting_module.cpp
25+
src/send_command_module.cpp
26+
src/command_log_module.cpp
27+
src/motor_config_module.cpp
28+
src/tiling_container.cpp
29+
src/rviz_module.cpp
30+
include/moteus_data_bus.h
31+
include/plot_widget.h
32+
include/tiling_container.h
33+
include/rviz_module.h
34+
include/send_command_module.h
35+
)
36+
target_link_libraries(moteus_gui Qt5::Widgets)
37+
ament_target_dependencies(moteus_gui rclcpp rover_msgs std_msgs ament_index_cpp)
38+
39+
install(TARGETS moteus_gui DESTINATION lib/${PROJECT_NAME})
40+
install(DIRECTORY config DESTINATION share/${PROJECT_NAME})
41+
42+
ament_package()
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
Panels:
2+
- Class: rviz_common/Displays
3+
Help Height: 0
4+
Name: Displays
5+
Property Tree Widget:
6+
Expanded: ~
7+
Splitter Ratio: 0.5
8+
Tree Height: 0
9+
- Class: rviz_common/Views
10+
Expanded:
11+
- /Current View1
12+
Name: Views
13+
Splitter Ratio: 0.5
14+
Visualization Manager:
15+
Class: ""
16+
Displays:
17+
- Alpha: 0.5
18+
Cell Size: 0.25
19+
Class: rviz_default_plugins/Grid
20+
Color: 100; 100; 100
21+
Enabled: true
22+
Line Style:
23+
Line Width: 0.029999999329447746
24+
Value: Lines
25+
Name: Grid
26+
Normal Cell Count: 0
27+
Offset:
28+
X: 0
29+
Y: 0
30+
Z: 0
31+
Plane: XY
32+
Plane Cell Count: 20
33+
Reference Frame: <Fixed Frame>
34+
Value: true
35+
- Alpha: 1
36+
Class: rviz_default_plugins/RobotModel
37+
Collision Enabled: false
38+
Description File: ""
39+
Description Source: Topic
40+
Description Topic:
41+
Depth: 5
42+
Durability Policy: Volatile
43+
History Policy: Keep Last
44+
Reliability Policy: Reliable
45+
Value: /robot_description
46+
Enabled: true
47+
Links:
48+
All Links Enabled: true
49+
Expand Joint Details: false
50+
Expand Link Details: false
51+
Expand Tree: false
52+
Link Tree Style: Links in Alphabetic Order
53+
Mass Properties:
54+
Inertia: false
55+
Mass: false
56+
Name: RobotModel
57+
TF Prefix: ""
58+
Update Interval: 0
59+
Value: true
60+
Visual Enabled: true
61+
Enabled: true
62+
Global Options:
63+
Background Color: 30; 30; 46
64+
Fixed Frame: base_link
65+
Frame Rate: 30
66+
Name: root
67+
Tools:
68+
- Class: rviz_default_plugins/MoveCamera
69+
- Class: rviz_default_plugins/Interact
70+
Hide Inactive Objects: true
71+
Transformation:
72+
Current:
73+
Class: rviz_default_plugins/TF
74+
Value: true
75+
Views:
76+
Current:
77+
Class: rviz_default_plugins/Orbit
78+
Distance: 1.2
79+
Enable Stereo Rendering:
80+
Stereo Eye Separation: 0.05999999865889549
81+
Stereo Focal Distance: 1
82+
Swap Stereo Eyes: false
83+
Value: false
84+
Focal Point:
85+
X: 0
86+
Y: 0
87+
Z: 0.25
88+
Focal Shape Fixed Size: true
89+
Focal Shape Size: 0.05000000074505806
90+
Invert Z Axis: false
91+
Name: Current View
92+
Near Clip Distance: 0.009999999776482582
93+
Pitch: 0.5
94+
Target Frame: <Fixed Frame>
95+
Value: Orbit (rviz)
96+
Yaw: 0.8
97+
Saved: ~
98+
Window Geometry:
99+
Displays:
100+
collapsed: true
101+
Height: 600
102+
Hide Left Dock: true
103+
Hide Right Dock: true
104+
Views:
105+
collapsed: true
106+
Width: 800
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#pragma once
2+
3+
#include <QApplication>
4+
#include <QString>
5+
6+
// Pure black terminal aesthetic with white borders
7+
namespace theme {
8+
9+
constexpr const char* Bg = "#000000";
10+
constexpr const char* BgPanel = "#0a0a0a";
11+
constexpr const char* Border = "#ffffff";
12+
constexpr const char* BorderDim = "#333333";
13+
constexpr const char* Text = "#ffffff";
14+
constexpr const char* TextDim = "#777777";
15+
constexpr const char* HeaderBg = "#111111";
16+
constexpr const char* Green = "#00ff88";
17+
constexpr const char* Red = "#ff4466";
18+
constexpr const char* Yellow = "#ffcc00";
19+
constexpr const char* Cyan = "#44ddcc";
20+
21+
constexpr int BorderRadius = 12;
22+
constexpr int BorderWidth = 2;
23+
constexpr int FontSize = 15;
24+
constexpr int FontSizeLg = 18;
25+
constexpr int FontSizeSm = 13;
26+
27+
// Per-motor colors (indexed 0-5)
28+
constexpr const char* MotorColors[] = {
29+
"#5599ff", // blue
30+
"#cc77ff", // purple
31+
"#00ff88", // green
32+
"#ff9944", // orange
33+
"#ff4466", // red
34+
"#44ddcc", // teal
35+
};
36+
37+
inline void applyGlobalStylesheet(QApplication& app) {
38+
app.setStyleSheet(QString(R"(
39+
* { font-family: monospace; font-size: %1px; }
40+
QMainWindow { background: %2; }
41+
QLabel { color: %3; font-size: %1px; }
42+
QPushButton {
43+
background: %4; color: %3; border: 1px solid %5;
44+
border-radius: 6px; padding: 10px 16px; font-weight: bold;
45+
font-size: %1px;
46+
}
47+
QPushButton:hover { background: #1a1a1a; border-color: %3; }
48+
QPushButton:pressed { background: #222222; }
49+
QComboBox {
50+
background: %4; color: %3; border: 1px solid %5;
51+
border-radius: 6px; padding: 8px 12px; font-size: %1px;
52+
}
53+
QComboBox::drop-down { border: none; }
54+
QComboBox QAbstractItemView {
55+
background: %4; color: %3; selection-background-color: #222222;
56+
font-size: %1px;
57+
}
58+
QDoubleSpinBox, QSpinBox {
59+
background: %4; color: %3; border: 1px solid %5;
60+
border-radius: 6px; padding: 8px 12px; font-size: %1px;
61+
}
62+
QCheckBox { color: %3; spacing: 8px; font-size: %1px; }
63+
QCheckBox::indicator {
64+
width: 18px; height: 18px; border: 1px solid %5;
65+
border-radius: 3px; background: %4;
66+
}
67+
QCheckBox::indicator:checked { background: %3; border-color: %3; }
68+
QTabWidget::pane { border: 1px solid %5; background: %2; border-radius: 6px; }
69+
QTabBar::tab {
70+
background: %4; color: %3; padding: 10px 20px;
71+
border: 1px solid %5; border-bottom: none;
72+
border-top-left-radius: 6px; border-top-right-radius: 6px;
73+
font-size: %1px;
74+
}
75+
QTabBar::tab:selected { background: %2; border-color: %3; }
76+
QTabBar::tab:hover { background: #1a1a1a; }
77+
QTextEdit {
78+
background: %4; color: %3; border: none;
79+
font-family: monospace; font-size: %6px;
80+
selection-background-color: #333333;
81+
}
82+
)")
83+
.arg(FontSize).arg(Bg).arg(Text).arg(BgPanel).arg(BorderDim).arg(FontSizeSm));
84+
}
85+
86+
} // namespace theme
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#pragma once
2+
3+
#include "gui_module.h"
4+
#include "moteus_data_bus.h"
5+
6+
#include <QTextEdit>
7+
8+
class CommandLogModule : public GuiModule {
9+
public:
10+
std::string name() const override { return "Command Log"; }
11+
QWidget* createWidget(QWidget* parent) override;
12+
void start() override {}
13+
void stop() override {}
14+
15+
void setDataBus(MoteusDataBus* bus) override;
16+
17+
private:
18+
QTextEdit* log_ = nullptr;
19+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#pragma once
2+
3+
#include <QWidget>
4+
#include <string>
5+
#include <functional>
6+
7+
class MoteusDataBus;
8+
9+
// Base class for GUI modules.
10+
// Subclass this, implement the methods, and add to the modules vector in main.
11+
struct GuiModule {
12+
virtual ~GuiModule() = default;
13+
14+
// Display name shown in the module's dock widget
15+
virtual std::string name() const = 0;
16+
17+
// Create and return the module's widget. Called once at startup.
18+
virtual QWidget* createWidget(QWidget* parent) = 0;
19+
20+
// Called after the window is shown. Start timers/threads here.
21+
virtual void start() = 0;
22+
23+
// Called on shutdown.
24+
virtual void stop() = 0;
25+
26+
// Override to receive the shared data bus. Called before start().
27+
virtual void setDataBus(MoteusDataBus*) {}
28+
29+
// Override to start hidden (toggled off in sidebar by default).
30+
virtual bool defaultVisible() const { return true; }
31+
32+
// Override to receive sidebar toggle events.
33+
virtual std::function<void(bool)> toggleCallback() { return nullptr; }
34+
};
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#pragma once
2+
3+
#include <QObject>
4+
#include <QTimer>
5+
#include <QElapsedTimer>
6+
#include <QString>
7+
8+
#include <array>
9+
#include <memory>
10+
11+
#include "rclcpp/rclcpp.hpp"
12+
#include "rover_msgs/msg/arm_command.hpp"
13+
#include "rover_msgs/msg/moteus_arm_status.hpp"
14+
#include "rover_msgs/msg/bldc_servo_config.hpp"
15+
#include "std_msgs/msg/string.hpp"
16+
17+
constexpr int NUM_MOTORS = 6;
18+
19+
// Command codes (matching driver convention)
20+
constexpr char CMD_ABS_POS = 'P';
21+
constexpr char CMD_ABS_VEL = 'V';
22+
constexpr char CMD_STOP = 'S';
23+
constexpr char CMD_ZERO = 'Z'; // "d exact 0" — resets position reference in place
24+
25+
struct MotorState {
26+
int id = 0;
27+
int mode = 0;
28+
int fault = 0;
29+
double position = 0.0;
30+
double velocity = 0.0;
31+
double torque = 0.0;
32+
double voltage = 0.0;
33+
double temperature = 0.0;
34+
double timestamp = 0.0;
35+
};
36+
37+
struct MotorConfigInfo {
38+
float kp = 0, ki = 0, kd = 0;
39+
float max_current = 0;
40+
float max_velocity = 0;
41+
float max_acceleration = 0;
42+
float position_min = 0, position_max = 0;
43+
float max_voltage = 0, max_power = 0;
44+
float gear_reduction = 0;
45+
};
46+
47+
class MoteusDataBus : public QObject {
48+
Q_OBJECT
49+
public:
50+
explicit MoteusDataBus(rclcpp::Node::SharedPtr node, QObject* parent = nullptr);
51+
52+
void start();
53+
void stop();
54+
55+
void sendPosition(int motor_id, double position, double velocity = 0.0,
56+
double max_torque = NAN);
57+
void sendVelocity(int motor_id, double velocity);
58+
void sendStop(int motor_id);
59+
void sendStopAll();
60+
// Reset position counter to 0 at current physical position (no movement).
61+
// Equivalent to "d exact 0" in tview.
62+
void sendZero(int motor_id);
63+
64+
const std::array<MotorConfigInfo, NUM_MOTORS>& configs() const { return configs_; }
65+
66+
signals:
67+
void telemetryUpdated(const std::array<MotorState, NUM_MOTORS>& states);
68+
void configUpdated(const std::array<MotorConfigInfo, NUM_MOTORS>& configs);
69+
void commandLogged(const QString& cmd);
70+
71+
private:
72+
void spinOnce();
73+
void onFeedback(const rover_msgs::msg::MoteusArmStatus::SharedPtr msg);
74+
void onConfigLog(const std_msgs::msg::String::SharedPtr msg);
75+
void logCmd(const QString& cmd);
76+
77+
rclcpp::Node::SharedPtr node_;
78+
rclcpp::Subscription<rover_msgs::msg::MoteusArmStatus>::SharedPtr feedback_sub_;
79+
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr config_log_sub_;
80+
rclcpp::Publisher<rover_msgs::msg::ArmCommand>::SharedPtr command_pub_;
81+
82+
QTimer* spin_timer_ = nullptr;
83+
QElapsedTimer elapsed_;
84+
std::array<MotorConfigInfo, NUM_MOTORS> configs_{};
85+
bool config_received_ = false;
86+
};

0 commit comments

Comments
 (0)