Command line interface using the CreOS SDK client library.
The Command Line Interface (CLI) is a tool that uses the CreOS SDK client library to enable interactions with an Avular robot directly from a terminal. You can install the CLI by installing the creos-cli package and its dependencies as follows, replacing <version> with the version number of the CreOS SDK you want to install, and <architecture> with the architecture of your system (e.g., amd64, arm64):
15#include <avular_version.h>
16#include <fmt/chrono.h>
17#include <spdlog/sinks/stdout_color_sinks.h>
18#include <spdlog/spdlog.h>
19#include <creos/client.hpp>
23#include <nlohmann/json.hpp>
27#include <unordered_set>
30static void receiveData(
const std::string src,
const auto& data,
bool ndjson_format =
true) {
31 nlohmann::json data_json = data;
32 nlohmann::json json{{
"source", src}, {
"data", data_json}};
34 std::cout << json.dump() << std::endl;
36 std::cout << json.dump(4) << std::endl;
41 if (request_data.size() == 0) {
42 spdlog::debug(
"Entering low power mode");
44 }
else if (request_data.size() == 1) {
50 if (strptime(request_data[0].c_str(),
"%Y-%m-%dT%H:%M:%S", &tm) !=
nullptr) {
51 wake_time = mktime(&tm);
52 }
else if (strptime(request_data[0].c_str(),
"%Y-%m-%dT%H:%M:%S%z", &tm) !=
nullptr) {
53 wake_time = mktime(&tm);
56 wake_time = std::stoll(request_data[0]);
58 }
catch (std::exception& e) {
59 spdlog::error(
"Failed to parse wake_time, It must be a UNIX timestamp or an ISO8601 date time string");
62 spdlog::debug(
"Entering low power mode with wake_time: {}", wake_time);
65 spdlog::error(
"Invalid number of arguments, expected 0 or 1");
68 std::cout <<
"{}" << std::endl;
71void getSource(
auto interface,
auto method,
bool ndjson_format =
true,
int timeout_ms = 1500) {
72 auto res = (interface->*method)(timeout_ms);
73 nlohmann::json json = res;
75 std::cout << json.dump() << std::endl;
77 std::cout << json.dump(4) << std::endl;
81template <
typename MessageType,
typename PublishFunction>
82void publishMessage(
const std::string& sink,
const std::optional<std::string>& data,
83 const std::string& message_type_name, PublishFunction publishFunc,
84 const std::optional<MessageType>& default_message = std::nullopt) {
87 if (data.has_value()) {
88 nlohmann::json data_json = nlohmann::json::parse(data.value());
89 nlohmann::from_json(data_json, message);
91 spdlog::info(
"No data provided, using default message data");
92 if (default_message.has_value()) {
93 message = default_message.value();
96 }
catch (
const std::exception& e) {
97 spdlog::error(
"Invalid JSON format, run using: --publish {} --publish {} --publish'{}'", sink,
98 message_type_name, nlohmann::json(message).dump());
103 spdlog::info(
"Publishing {} message to: {}", message_type_name, sink);
104 publishFunc(message);
105 }
catch (
const std::exception& e) {
106 spdlog::error(
"Failed to publish message: {}", e.what());
114std::string argumentsToJsonStr(
const std::vector<std::string>& args) {
115 if (args.size() == 0) {
119 std::string json_str =
"";
120 for (
size_t i = 0; i < args.size(); ++i) {
135 bool ndjson_format,
bool statistics);
143int main(
int argc,
char** argv) {
145 auto console_sink = std::make_shared<spdlog::sinks::stderr_color_sink_st>();
146 spdlog::set_default_logger(std::make_shared<spdlog::logger>(
"console", console_sink));
148 cxxopts::Options options(argv[0],
"CLI tool to interact with avular robots");
150 options.add_options()
151 (
"h,host",
"Host address of the robot", cxxopts::value<std::string>())
152 (
"version",
"Print version")
153 (
"v,verbose",
"Enable verbose output")
154 (
"help",
"Print help")
155 (
"echo",
"Echo data received from the selected sources (to echo multiple sources at the same time, add "
156 "multiple --echo arguments: --echo source_1 --echo source_2)",
157 cxxopts::value<std::vector<std::string>>())
158 (
"statistics",
"Print statistics of the received data (can be used with multiple sources at the same time: "
159 "--statistics source_1 --statistics source_2)",
160 cxxopts::value<std::vector<std::string>>())
161 (
"window",
"Window size for statistics in messages", cxxopts::value<unsigned>()->default_value(
"1000"))
162 (
"list",
"List the available sources, sinks and callables")
163 (
"call",
"Call a callable", cxxopts::value<std::vector<std::string>>())
164 (
"get",
"Get data from a source", cxxopts::value<std::string>())
165 (
"publish",
"Publish data to a sink", cxxopts::value<std::vector<std::string>>())
166 (
"ndjson",
"Output JSON in NDJSON format (one line per JSON object) instead of pretty printed format", cxxopts::value<bool>()->default_value(
"false"))
170 cxxopts::ParseResult arguments;
172 arguments = options.parse(argc, argv);
173 }
catch (cxxopts::OptionException& e) {
174 spdlog::critical(
"Argument error: {}", e.what());
178 if (arguments.count(
"help") || argc == 1) {
179 std::cout << options.help() << std::endl;
183 if (arguments.count(
"version")) {
184 PrintAvularVersion();
188 if (arguments.count(
"verbose")) {
189 spdlog::set_level(spdlog::level::debug);
192 bool ndjson_format = arguments[
"ndjson"].as<
bool>();
194 std::unique_ptr<creos::Client> client;
195 if (arguments.count(
"host")) {
196 client = std::make_unique<creos::Client>(arguments[
"host"].as<std::string>());
198 client = std::make_unique<creos::Client>();
201 auto sensors = client->sensors();
202 auto power_management = client->power_management();
203 auto transforms = client->transforms();
204 auto system_info = client->system_info();
205 auto setpoint_control = client->setpoint_control();
206 auto diagnostics = client->diagnostics();
208 if (arguments.count(
"list")) {
209 handleList(sensors, transforms, system_info, setpoint_control, power_management, diagnostics);
211 if (arguments.count(
"echo")) {
212 handleSubscribe(arguments, sensors, transforms, system_info, setpoint_control, diagnostics, ndjson_format,
215 if (arguments.count(
"statistics")) {
216 handleSubscribe(arguments, sensors, transforms, system_info, setpoint_control, diagnostics, ndjson_format,
219 if (arguments.count(
"call")) {
220 handleCall(arguments, power_management, setpoint_control);
222 if (arguments.count(
"get")) {
223 handleGet(arguments, system_info, setpoint_control, diagnostics, ndjson_format);
225 if (arguments.count(
"publish")) {
226 handlePublish(arguments, setpoint_control);
234 std::cout <<
"Available data sources:" << std::endl;
236 std::cout <<
" - sensors.lidar_points.front" << std::endl;
237 std::cout <<
" - sensors.odometry" << std::endl;
238 std::cout <<
" - sensors.pose" << std::endl;
239 std::cout <<
" - sensors.gnss_fix" << std::endl;
240 std::cout <<
" - sensors.remote_control" << std::endl;
241 std::cout <<
" - sensors.wheel_states" << std::endl;
242 std::cout <<
" - sensors.magnetic_field.body" << std::endl;
243 std::cout <<
" - sensors.barometer" << std::endl;
244 std::cout <<
" - sensors.range.downwards_lidar" << std::endl;
245 std::cout <<
" - sensors.imu.body" << std::endl;
248 std::cout <<
" - transforms.dynamic_transforms" << std::endl;
249 std::cout <<
" - transforms.static_transforms" << std::endl;
252 std::cout <<
" - system_info.system_info" << std::endl;
253 std::cout <<
" - system_info.battery_status" << std::endl;
255 if (setpoint_control) {
256 std::cout <<
" - setpoint_control.current_control_source" << std::endl;
259 std::cout <<
" - diagnostics.user_events" << std::endl;
260 std::cout <<
" - diagnostics.state" << std::endl;
263 std::cout <<
"Available data sinks:" << std::endl;
264 if (setpoint_control) {
265 std::cout <<
" - setpoint_control.cmd_vel_user" << std::endl;
266 std::cout <<
" - setpoint_control.state_reference" << std::endl;
269 std::cout <<
"Available callables:" << std::endl;
270 if (power_management) {
271 std::cout <<
" - power_management.enter_low_power_mode[,wake_time]" << std::endl;
272 std::cout <<
" - power_management.exit_low_power_mode" << std::endl;
273 std::cout <<
" - power_management.shutdown" << std::endl;
274 std::cout <<
" - power_management.reboot" << std::endl;
276 if (setpoint_control) {
277 std::cout <<
" - setpoint_control.set_control_source" << std::endl;
278 std::cout <<
" - setpoint_control.command" << std::endl;
281 std::cout <<
"Available getters:" << std::endl;
283 std::cout <<
" - system_info.system_info" << std::endl;
284 std::cout <<
" - system_info.battery_status" << std::endl;
286 if (setpoint_control) {
287 std::cout <<
" - setpoint_control.current_control_source" << std::endl;
290 std::cout <<
" - diagnostics.state" << std::endl;
294using float_millis = std::chrono::duration<float, std::milli>;
299 bool ndjson_format,
bool statistics) {
300 auto sources = std::vector<std::string>();
302 sources = arguments[
"statistics"].as<std::vector<std::string>>();
304 sources = arguments[
"echo"].as<std::vector<std::string>>();
306 auto source_statistics = std::map<std::string, std::deque<float_millis>>();
307 for (
const auto& source : sources) {
308 source_statistics[source] = std::deque<float_millis>();
310 auto statistics_window = arguments[
"window"].as<
unsigned>();
311 std::unordered_set<std::string> registered_sources;
313 auto isSpecified = [&](
const std::string& expr,
const std::string& interfaceName,
const std::string& sourceName) {
318 auto separator_it = expr.find(
'.');
319 if (separator_it == std::string::npos) {
322 auto interface_part = expr.substr(0, separator_it);
323 auto source_part = expr.substr(separator_it + 1);
324 auto match_interface = interface_part ==
"*" || interface_part == interfaceName;
325 auto match_source = source_part ==
"*" || source_part == sourceName;
326 return match_interface && match_source;
329 auto subscribeIfSpecified = [&](
const std::string& expr,
const std::string& interfaceName,
330 const std::string& sourceName,
auto interface,
auto subscribeFunc,
331 bool& subscription_registered) {
332 if (!isSpecified(expr, interfaceName, sourceName)) {
335 if (registered_sources.find(expr) != registered_sources.end()) {
339 spdlog::info(
"Subscribing to: {}.{}", interfaceName, sourceName);
341 std::string key = interfaceName +
"." + sourceName;
342 registered_sources.insert(key);
345 (interface->*subscribeFunc)(
346 [key, ndjson_format, statistics, &source_statistics, &statistics_window,
347 last_time = std::chrono::system_clock::time_point()](
const auto& data)
mutable {
349 if (last_time.time_since_epoch().count() > 0) {
350 source_statistics[key].push_back(
351 std::chrono::duration_cast<float_millis>(std::chrono::system_clock::now() - last_time));
352 if (source_statistics[key].size() > statistics_window) {
353 source_statistics[key].pop_front();
356 last_time = std::chrono::system_clock::now();
358 receiveData(key, data, ndjson_format);
361 subscription_registered =
true;
363 spdlog::warn(
"Failed to subscribe to {}.{}: {}", interfaceName, sourceName, e.
what());
367 auto subscribeNamedIfSpecified = [&](
const std::string& expr,
const std::string& interfaceName,
368 const std::string& sourceName,
auto sourceId,
auto interface,
369 auto subscribeFunc,
bool& subscription_registered) {
370 if (!isSpecified(expr, interfaceName, sourceName)) {
373 if (registered_sources.find(expr) != registered_sources.end()) {
377 spdlog::info(
"Subscribing to: {}.{}", interfaceName, sourceName);
379 std::string key = interfaceName +
"." + sourceName;
380 registered_sources.insert(key);
381 (interface->*subscribeFunc)(
382 sourceId, [key, ndjson_format, statistics, &source_statistics, &statistics_window,
383 last_time = std::chrono::system_clock::time_point()](
const auto& data)
mutable {
385 if (last_time.time_since_epoch().count() > 0) {
386 source_statistics[key].push_back(
387 std::chrono::duration_cast<float_millis>(std::chrono::system_clock::now() - last_time));
388 if (source_statistics[key].size() > statistics_window) {
389 source_statistics[key].pop_front();
392 last_time = std::chrono::system_clock::now();
394 receiveData(key, data, ndjson_format);
397 subscription_registered =
true;
399 spdlog::warn(
"Failed to subscribe to {}.{}: {}", interfaceName, sourceName, e.
what());
403 for (
const auto& source : sources) {
404 bool subscription_registered =
false;
409 subscription_registered);
411 subscription_registered);
413 subscription_registered);
414 subscribeIfSpecified(source,
"sensors",
"remote_control", sensors,
416 subscribeIfSpecified(source,
"sensors",
"wheel_states", sensors,
420 subscription_registered);
422 subscription_registered);
423 subscribeNamedIfSpecified(source,
"sensors",
"range.downwards_lidar",
428 subscribeIfSpecified(source,
"transforms",
"dynamic_transforms", transforms,
430 subscribeIfSpecified(source,
"transforms",
"static_transforms", transforms,
432 subscribeIfSpecified(source,
"system_info",
"system_info", system_info,
434 subscribeIfSpecified(source,
"system_info",
"battery_status", system_info,
436 subscribeIfSpecified(source, creos::ISetpointControlInterface::name,
"current_control_source", setpoint_control,
438 subscription_registered);
439 subscribeIfSpecified(source,
"diagnostics",
"user_events", diagnostics,
441 subscribeIfSpecified(source,
"diagnostics",
"state", diagnostics,
444 if (!subscription_registered) {
445 spdlog::error(
"Unknown source: {}", source);
450 std::this_thread::sleep_for(std::chrono::seconds(1));
452 if (!ndjson_format) {
453 fmt::print(
"Statistics at {}:\n", std::chrono::system_clock::now());
455 for (
const auto& [source, durations] : source_statistics) {
456 if (durations.size() < 2) {
457 if (!ndjson_format) {
458 std::cout <<
"- " << source <<
": <not enough data>" << std::endl;
465 auto durations_view = std::views::drop(durations, 1);
466 auto total_duration = std::accumulate(durations_view.begin(), durations_view.end(), float_millis(0));
467 auto avg_duration = total_duration / durations_view.size();
468 auto frequency = avg_duration.count() > 0
469 ? std::chrono::duration<double>(1) /
470 std::chrono::duration_cast<std::chrono::duration<double>>(avg_duration)
472 auto min_duration = *std::min_element(durations_view.begin(), durations_view.end());
473 auto max_duration = *std::max_element(durations_view.begin(), durations_view.end());
474 auto variance = std::accumulate(durations_view.begin(), durations_view.end(), float_millis(0),
475 [avg_duration](
auto acc,
auto duration) {
476 return acc + float_millis((duration - avg_duration).count() *
477 (duration - avg_duration).count());
479 durations_view.size();
480 auto std_dev = float_millis(std::sqrt(variance.count()));
482 std::cout << nlohmann::json::object({
483 {
"timestamp", std::chrono::duration_cast<std::chrono::seconds>(
484 std::chrono::system_clock::now().time_since_epoch())
487 {
"total", durations.size()},
489 {
"avg", avg_duration.count()},
490 {
"min", min_duration.count()},
491 {
"max", max_duration.count()},
492 {
"sd", std_dev.count()},
496 std::cout << fmt::format(
497 "- {}: \n\t\ttotal={} \n\t\tfreq={:.2f}hz \n\t\tavg={}, min={}, max={}, sd={}",
498 source, durations.size(), frequency, avg_duration, min_duration, max_duration,
510 auto call_arguments = arguments[
"call"].as<std::vector<std::string>>();
512 auto callable = call_arguments.at(0);
513 auto callable_args = std::vector<std::string>(call_arguments.begin() + 1, call_arguments.end());
515 auto interface_name = callable.substr(0, callable.find(
'.'));
518 if (interface_name ==
"power_management") {
519 if (!power_management) {
520 spdlog::error(
"Power management interface not available");
523 if (callable ==
"power_management.enter_low_power_mode") {
524 callEnterLowPowerMode(power_management, callable_args);
525 }
else if (callable ==
"power_management.exit_low_power_mode") {
527 std::cout <<
"Success" << std::endl;
528 }
else if (callable ==
"power_management.shutdown") {
530 std::cout <<
"Success" << std::endl;
531 }
else if (callable ==
"power_management.reboot") {
532 power_management->
reboot();
533 std::cout <<
"Success" << std::endl;
535 spdlog::error(
"unknown callable: {}", callable);
539 else if (interface_name ==
"setpoint_control") {
540 if (!setpoint_control) {
541 spdlog::error(
"Setpoint control interface not available");
545 if (callable ==
"setpoint_control.set_control_source") {
546 if (callable_args.size() != 1) {
547 spdlog::error(
"Invalid number of arguments, expected 1");
554 std::cout <<
"Success" << std::endl;
555 }
else if (callable ==
"setpoint_control.command") {
556 if (callable_args.size() != 1) {
557 spdlog::error(
"Invalid number of arguments, expected 1");
562 command.
action = creos_messages::Command::Action(std::stoi(callable_args.at(0)));
564 std::cout <<
"Success" << std::endl;
566 spdlog::error(
"unknown callable: {}", callable);
569 spdlog::error(
"unknown interface: {}", interface_name);
573 std::cout <<
"Call failed[" <<
static_cast<int>(e.code()) <<
"]: " << e.
what() << std::endl;
575 std::cout <<
"Call timed out: " << e.
what() << std::endl;
577 std::cout <<
"Call unsupported: " << e.
what() << std::endl;
578 }
catch (
const std::exception& e) {
579 std::cout <<
"Call failed: " << e.what() << std::endl;
587 bool ndjson_format) {
588 auto source = arguments[
"get"].as<std::string>();
590 if (source ==
"system_info.system_info") {
592 }
else if (source ==
"system_info.battery_status") {
594 }
else if (source ==
"setpoint_control.current_control_source") {
596 }
else if (source ==
"diagnostics.state") {
599 spdlog::error(
"unknown source: {}", source);
605 auto pub_arguments = arguments[
"publish"].as<std::vector<std::string>>();
607 if (pub_arguments.size() < 2) {
609 "Invalid number of arguments, usage: --publish sink,message_type or --publish "
610 "sink,message_type,'json_data'");
614 auto pub_sink = pub_arguments.at(0);
615 auto pub_message_type = pub_arguments.at(1);
617 std::optional<std::string> pub_data = std::nullopt;
618 if (pub_arguments.size() >= 3) {
619 pub_data = argumentsToJsonStr(std::vector<std::string>(pub_arguments.begin() + 2, pub_arguments.end()));
622 auto interface_name = pub_sink.substr(0, pub_sink.find(
'.'));
625 if (interface_name ==
"setpoint_control") {
627 if (!setpoint_control) {
628 spdlog::error(
"Setpoint control interface not available");
632 if (pub_sink ==
"setpoint_control.cmd_vel_user") {
633 if (pub_message_type ==
"Twist") {
634 publishMessage<creos_messages::Twist>(
635 pub_sink, pub_data, pub_message_type,
638 spdlog::error(
"Invalid message type: {}", pub_message_type);
641 }
else if (pub_sink ==
"setpoint_control.state_reference") {
642 if (pub_message_type ==
"StateReference") {
645 creos_messages::StateReference::OrientationMode::kAngularVelocity;
646 default_message.
translation_mode = creos_messages::StateReference::TranslationMode::kPosition;
647 publishMessage<creos_messages::StateReference>(
648 pub_sink, pub_data, pub_message_type,
652 spdlog::error(
"Invalid message type: {}", pub_message_type);
656 spdlog::error(
"unknown sink: {}", pub_sink);
660 spdlog::error(
"unknown interface: {}", interface_name);
663 }
catch (
const std::exception& e) {
664 std::cerr << e.what() <<
'\n';
Exception class for the CreOS client library.
Definition errors.hpp:32
const char * what() const noexcept override
Returns the error message.
Definition errors.hpp:43
The interface for retrieving diagnostic information from an Avular robot.
Definition diagnostics_interface.hpp:28
virtual creos_messages::State getState(int timeout_ms=1500)=0
Get the state of the robot.
virtual SubscriptionId subscribeToUserEvents(const std::function< void(const creos_messages::UserEvent &)> &callback)=0
Subscribe to user events generated by the robot.
virtual SubscriptionId subscribeToState(const std::function< void(const creos_messages::State &)> &callback)=0
Subscribe to state information of the robot.
The interface for querying and managing the power state of an Avular robot.
Definition power_management_interface.hpp:27
virtual void exitLowPowerMode(int timeout_ms=5000)=0
Exit low power mode.
virtual void reboot(int timeout_ms=10000)=0
Reboot the robot.
virtual void enterLowPowerMode(int timeout_ms=5000)=0
Enter low power mode.
virtual void shutdown(int timeout_ms=5000)=0
Shutdown the robot.
The interface for retrieving sensor data from an Avular robot.
Definition sensors_interface.hpp:36
virtual SubscriptionId subscribeToRemoteController(const std::function< void(const creos_messages::ControllerState &)> &callback)=0
Subscribe to the remote controller state data.
virtual SubscriptionId subscribeToGnss(const std::function< void(const creos_messages::Gnss &)> &callback)=0
Subscribe to the GNSS data.
virtual SubscriptionId subscribeToOdometry(const std::function< void(const creos_messages::Odometry &)> &callback)=0
Subscribe to the odometry data.
virtual SubscriptionId subscribeToRangeSensor(creos_messages::RangeSensorId range_sensor_id, const std::function< void(const creos_messages::Range &)> &callback)=0
Subscribe to range sensor data.
virtual SubscriptionId subscribeToWheelState(const std::function< void(const creos_messages::WheelStates &)> &callback)=0
Subscribe to the wheel state data.
virtual SubscriptionId subscribeToImu(const creos_messages::ImuId imu_id, const std::function< void(const creos_messages::Imu &)> &callback)=0
Subscribe to the IMU sensor data.
virtual SubscriptionId subscribeToMagneticField(creos_messages::MagneticFieldId magnetic_field_id, const std::function< void(const creos_messages::MagneticField &)> &callback)=0
Subscribe to the magnetic field data.
virtual SubscriptionId subscribeToBarometer(const std::function< void(const creos_messages::Barometer &)> &callback)=0
Subscribe to the barometer data.
virtual SubscriptionId subscribeToPose(const std::function< void(const creos_messages::PoseWithCovarianceStamped &)> &callback)=0
Subscribe to the global pose data.
virtual SubscriptionId subscribeTo3dLidar(const creos_messages::Lidar3dId lidar_id, const std::function< void(const creos_messages::PointCloud2 &)> &callback)=0
Subscribe to the lidar point cloud data.
The interface for sending control setpoints to an Avular robot.
Definition setpoint_control_interface.hpp:30
virtual creos_messages::ControlSource getCurrentControlSource(int timeout_ms=1500)=0
Get the current control source of the robot.
virtual void publishStateReference(const creos_messages::StateReference &state_reference)=0
Publish a state reference to the robot.
virtual void publishVelocityCommand(const creos_messages::Twist &cmd_vel)=0
Publish a velocity command to the robot.
virtual void setControlSource(const creos_messages::ControlSource &source, int timeout_ms=5000)=0
Set the control source of the robot.
virtual void sendCommand(const creos_messages::Command command, int timeout_ms=5000)=0
Send a command to the robot to trigger a one-time action.
virtual SubscriptionId subscribeToCurrentControlSource(const std::function< void(const creos_messages::ControlSource &)> &callback)=0
Subscribe to the current control source of the robot.
The interface for retrieving general system information of an Avular robot.
Definition system_info_interface.hpp:28
virtual creos_messages::SystemInfo getSystemInfo(int timeout_ms=1500)=0
Get the system info.
virtual creos_messages::BatteryStatus getBatteryStatus(int timeout_ms=1500)=0
Get the battery status.
virtual SubscriptionId subscribeToSystemInfo(const std::function< void(const creos_messages::SystemInfo &)> &callback)=0
Subscribe to system info messages.
virtual SubscriptionId subscribeToBatteryStatus(const std::function< void(const creos_messages::BatteryStatus &)> &callback)=0
Subscribe to battery status updates.
static time_point from_time_t(std::time_t t) noexcept
Construct a time_point from a time_t.
Definition robot_clock.cpp:20
Service call failure.
Definition errors.hpp:105
Timeout error.
Definition errors.hpp:88
A part of the API is not supported by the connected robot.
Definition errors.hpp:120
@ kBody
IMU sensor on the body of the robot.
@ kDownwardsLidar
Downwards facing lidar.
@ kFront
The front lidar 3D sensor.
ControlSource
Control source message containing the control source of the robot.
Definition control_source.hpp:28
@ kBody
Magnetic field sensor on the body of the robot.
Command message containing the command to send to the robot.
Definition command.hpp:26
Action action
Construct action.
Definition command.hpp:41
State reference message containing the pose, velocity and acceleration of the robot.
Definition state_reference.hpp:31
TranslationMode translation_mode
Translation mode of the state reference.
Definition state_reference.hpp:74
OrientationMode orientation_mode
Orientation mode of the state reference.
Definition state_reference.hpp:79