|
45 | 45 | namespace |
46 | 46 | { |
47 | 47 | constexpr char CUSTOM_HW_IF_SERVO[] = "servo"; |
| 48 | + |
| 49 | +// IMU sensor interface names - Quaternion orientation |
| 50 | +constexpr char CUSTOM_HW_IF_ORIENTATION_X[] = "orientation.x"; |
| 51 | +constexpr char CUSTOM_HW_IF_ORIENTATION_Y[] = "orientation.y"; |
| 52 | +constexpr char CUSTOM_HW_IF_ORIENTATION_Z[] = "orientation.z"; |
| 53 | +constexpr char CUSTOM_HW_IF_ORIENTATION_W[] = "orientation.w"; |
| 54 | + |
| 55 | +// IMU sensor interface names - Euler angles |
| 56 | +constexpr char CUSTOM_HW_IF_ROLL[] = "roll"; |
| 57 | +constexpr char CUSTOM_HW_IF_PITCH[] = "pitch"; |
| 58 | +constexpr char CUSTOM_HW_IF_YAW[] = "yaw"; |
| 59 | + |
| 60 | +// IMU sensor interface names - Angular velocity |
| 61 | +constexpr char CUSTOM_HW_IF_ANGULAR_VELOCITY_X[] = "angular_velocity.x"; |
| 62 | +constexpr char CUSTOM_HW_IF_ANGULAR_VELOCITY_Y[] = "angular_velocity.y"; |
| 63 | +constexpr char CUSTOM_HW_IF_ANGULAR_VELOCITY_Z[] = "angular_velocity.z"; |
| 64 | + |
| 65 | +// IMU sensor interface names - Linear acceleration |
| 66 | +constexpr char CUSTOM_HW_IF_LINEAR_ACCELERATION_X[] = "linear_acceleration.x"; |
| 67 | +constexpr char CUSTOM_HW_IF_LINEAR_ACCELERATION_Y[] = "linear_acceleration.y"; |
| 68 | +constexpr char CUSTOM_HW_IF_LINEAR_ACCELERATION_Z[] = "linear_acceleration.z"; |
| 69 | + |
| 70 | +// IMU sensor interface names - Magnetic field |
| 71 | +constexpr char CUSTOM_HW_IF_MAGNETIC_FIELD_X[] = "magnetic_field.x"; |
| 72 | +constexpr char CUSTOM_HW_IF_MAGNETIC_FIELD_Y[] = "magnetic_field.y"; |
| 73 | +constexpr char CUSTOM_HW_IF_MAGNETIC_FIELD_Z[] = "magnetic_field.z"; |
48 | 74 | } // namespace |
49 | 75 |
|
50 | 76 | namespace vesc_hardware |
@@ -189,13 +215,24 @@ hardware_interface::CallbackReturn VescHardware::on_init( |
189 | 215 | if (publish_raw_state_) { |
190 | 216 | hardware_values_publisher_ = |
191 | 217 | get_node()->create_publisher<vesc_msgs::msg::VescState>( |
192 | | - "~/hardware_values", 10); |
| 218 | + "~/vesc_state", 10); |
193 | 219 |
|
194 | 220 | // Create realtime publisher wrapper |
195 | 221 | realtime_hardware_values_publisher_ = |
196 | 222 | std::make_shared<realtime_tools::RealtimePublisher< |
197 | 223 | vesc_msgs::msg::VescState>>( |
198 | 224 | hardware_values_publisher_); |
| 225 | + |
| 226 | + // Create publisher for IMU data |
| 227 | + imu_publisher_ = |
| 228 | + get_node()->create_publisher<vesc_msgs::msg::VescImuStamped>( |
| 229 | + "~/vesc_imu", 10); |
| 230 | + |
| 231 | + // Create realtime publisher wrapper |
| 232 | + realtime_imu_publisher_ = |
| 233 | + std::make_shared<realtime_tools::RealtimePublisher< |
| 234 | + vesc_msgs::msg::VescImuStamped>>( |
| 235 | + imu_publisher_); |
199 | 236 | } |
200 | 237 |
|
201 | 238 | return hardware_interface::CallbackReturn::SUCCESS; |
@@ -288,9 +325,10 @@ VescHardware::read( |
288 | 325 | const rclcpp::Time & /*time*/, |
289 | 326 | const rclcpp::Duration & /*period*/) |
290 | 327 | { |
291 | | - // Request state from VESC (non-blocking) |
| 328 | + // Request state and IMU data from VESC (non-blocking) |
292 | 329 | if (vesc_interface_) { |
293 | 330 | vesc_interface_->requestState(); |
| 331 | + vesc_interface_->requestImuData(); |
294 | 332 | } |
295 | 333 |
|
296 | 334 | // Update state interfaces with current values from atomic variables |
@@ -372,6 +410,80 @@ void VescHardware::populate_state_definitions() |
372 | 410 | false, |
373 | 411 | [this]() {return hw_command_servo_;} |
374 | 412 | }; |
| 413 | + |
| 414 | + // IMU state interfaces - Quaternion orientation |
| 415 | + state_interfaces_[CUSTOM_HW_IF_ORIENTATION_X] = { |
| 416 | + false, |
| 417 | + [this]() {return hw_imu_orientation_x_.load(std::memory_order_relaxed);} |
| 418 | + }; |
| 419 | + state_interfaces_[CUSTOM_HW_IF_ORIENTATION_Y] = { |
| 420 | + false, |
| 421 | + [this]() {return hw_imu_orientation_y_.load(std::memory_order_relaxed);} |
| 422 | + }; |
| 423 | + state_interfaces_[CUSTOM_HW_IF_ORIENTATION_Z] = { |
| 424 | + false, |
| 425 | + [this]() {return hw_imu_orientation_z_.load(std::memory_order_relaxed);} |
| 426 | + }; |
| 427 | + state_interfaces_[CUSTOM_HW_IF_ORIENTATION_W] = { |
| 428 | + false, |
| 429 | + [this]() {return hw_imu_orientation_w_.load(std::memory_order_relaxed);} |
| 430 | + }; |
| 431 | + |
| 432 | + // IMU state interfaces - Euler angles |
| 433 | + state_interfaces_[CUSTOM_HW_IF_ROLL] = { |
| 434 | + false, |
| 435 | + [this]() {return hw_imu_roll_.load(std::memory_order_relaxed);} |
| 436 | + }; |
| 437 | + state_interfaces_[CUSTOM_HW_IF_PITCH] = { |
| 438 | + false, |
| 439 | + [this]() {return hw_imu_pitch_.load(std::memory_order_relaxed);} |
| 440 | + }; |
| 441 | + state_interfaces_[CUSTOM_HW_IF_YAW] = { |
| 442 | + false, |
| 443 | + [this]() {return hw_imu_yaw_.load(std::memory_order_relaxed);} |
| 444 | + }; |
| 445 | + |
| 446 | + // IMU state interfaces - Angular velocity |
| 447 | + state_interfaces_[CUSTOM_HW_IF_ANGULAR_VELOCITY_X] = { |
| 448 | + false, |
| 449 | + [this]() {return hw_imu_angular_velocity_x_.load(std::memory_order_relaxed);} |
| 450 | + }; |
| 451 | + state_interfaces_[CUSTOM_HW_IF_ANGULAR_VELOCITY_Y] = { |
| 452 | + false, |
| 453 | + [this]() {return hw_imu_angular_velocity_y_.load(std::memory_order_relaxed);} |
| 454 | + }; |
| 455 | + state_interfaces_[CUSTOM_HW_IF_ANGULAR_VELOCITY_Z] = { |
| 456 | + false, |
| 457 | + [this]() {return hw_imu_angular_velocity_z_.load(std::memory_order_relaxed);} |
| 458 | + }; |
| 459 | + |
| 460 | + // IMU state interfaces - Linear acceleration |
| 461 | + state_interfaces_[CUSTOM_HW_IF_LINEAR_ACCELERATION_X] = { |
| 462 | + false, |
| 463 | + [this]() {return hw_imu_linear_acceleration_x_.load(std::memory_order_relaxed);} |
| 464 | + }; |
| 465 | + state_interfaces_[CUSTOM_HW_IF_LINEAR_ACCELERATION_Y] = { |
| 466 | + false, |
| 467 | + [this]() {return hw_imu_linear_acceleration_y_.load(std::memory_order_relaxed);} |
| 468 | + }; |
| 469 | + state_interfaces_[CUSTOM_HW_IF_LINEAR_ACCELERATION_Z] = { |
| 470 | + false, |
| 471 | + [this]() {return hw_imu_linear_acceleration_z_.load(std::memory_order_relaxed);} |
| 472 | + }; |
| 473 | + |
| 474 | + // IMU state interfaces - Magnetic field |
| 475 | + state_interfaces_[CUSTOM_HW_IF_MAGNETIC_FIELD_X] = { |
| 476 | + false, |
| 477 | + [this]() {return hw_imu_magnetic_field_x_.load(std::memory_order_relaxed);} |
| 478 | + }; |
| 479 | + state_interfaces_[CUSTOM_HW_IF_MAGNETIC_FIELD_Y] = { |
| 480 | + false, |
| 481 | + [this]() {return hw_imu_magnetic_field_y_.load(std::memory_order_relaxed);} |
| 482 | + }; |
| 483 | + state_interfaces_[CUSTOM_HW_IF_MAGNETIC_FIELD_Z] = { |
| 484 | + false, |
| 485 | + [this]() {return hw_imu_magnetic_field_z_.load(std::memory_order_relaxed);} |
| 486 | + }; |
375 | 487 | } |
376 | 488 |
|
377 | 489 | void VescHardware::populate_command_definitions() |
@@ -418,6 +530,93 @@ void VescHardware::processValuesPacket( |
418 | 530 | publishVescState(*values_packet); |
419 | 531 | } |
420 | 532 |
|
| 533 | +void VescHardware::processImuPacket( |
| 534 | + const vesc_driver::VescPacketImu *imu_packet) |
| 535 | +{ |
| 536 | + if (!imu_packet) { |
| 537 | + return; |
| 538 | + } |
| 539 | + |
| 540 | + // Conversion lambda: degrees to radians |
| 541 | + auto deg_to_rad = [](double deg) { return deg * M_PI / 180.0; }; |
| 542 | + |
| 543 | + // Standard gravity constant for converting acceleration from 'g' to m/s² |
| 544 | + constexpr double STANDARD_GRAVITY = 9.80665; |
| 545 | + |
| 546 | + // Store quaternion orientation (already in correct units) |
| 547 | + hw_imu_orientation_x_.store(imu_packet->q_x(), std::memory_order_relaxed); |
| 548 | + hw_imu_orientation_y_.store(imu_packet->q_y(), std::memory_order_relaxed); |
| 549 | + hw_imu_orientation_z_.store(imu_packet->q_z(), std::memory_order_relaxed); |
| 550 | + hw_imu_orientation_w_.store(imu_packet->q_w(), std::memory_order_relaxed); |
| 551 | + |
| 552 | + // Store Euler angles (convert from degrees to radians) |
| 553 | + hw_imu_roll_.store(deg_to_rad(imu_packet->roll()), std::memory_order_relaxed); |
| 554 | + hw_imu_pitch_.store(deg_to_rad(imu_packet->pitch()), std::memory_order_relaxed); |
| 555 | + hw_imu_yaw_.store(deg_to_rad(imu_packet->yaw()), std::memory_order_relaxed); |
| 556 | + |
| 557 | + // Store angular velocity (convert from degrees/second to radians/second) |
| 558 | + hw_imu_angular_velocity_x_.store(deg_to_rad(imu_packet->gyr_x()), std::memory_order_relaxed); |
| 559 | + hw_imu_angular_velocity_y_.store(deg_to_rad(imu_packet->gyr_y()), std::memory_order_relaxed); |
| 560 | + hw_imu_angular_velocity_z_.store(deg_to_rad(imu_packet->gyr_z()), std::memory_order_relaxed); |
| 561 | + |
| 562 | + // Store linear acceleration (convert from 'g' to m/s²) |
| 563 | + hw_imu_linear_acceleration_x_.store(imu_packet->acc_x() * STANDARD_GRAVITY, std::memory_order_relaxed); |
| 564 | + hw_imu_linear_acceleration_y_.store(imu_packet->acc_y() * STANDARD_GRAVITY, std::memory_order_relaxed); |
| 565 | + hw_imu_linear_acceleration_z_.store(imu_packet->acc_z() * STANDARD_GRAVITY, std::memory_order_relaxed); |
| 566 | + |
| 567 | + // Store magnetic field (in raw units as received) |
| 568 | + hw_imu_magnetic_field_x_.store(imu_packet->mag_x(), std::memory_order_relaxed); |
| 569 | + hw_imu_magnetic_field_y_.store(imu_packet->mag_y(), std::memory_order_relaxed); |
| 570 | + hw_imu_magnetic_field_z_.store(imu_packet->mag_z(), std::memory_order_relaxed); |
| 571 | + |
| 572 | + // Publish raw IMU data |
| 573 | + publishVescImu(*imu_packet); |
| 574 | +} |
| 575 | + |
| 576 | +void VescHardware::publishVescImu( |
| 577 | + const vesc_driver::VescPacketImu & imu_packet) |
| 578 | +{ |
| 579 | + if (!realtime_imu_publisher_) { |
| 580 | + return; |
| 581 | + } |
| 582 | + |
| 583 | + if (realtime_imu_publisher_->trylock()) { |
| 584 | + auto & msg = realtime_imu_publisher_->msg_; |
| 585 | + |
| 586 | + // Set timestamp |
| 587 | + msg.header.stamp = get_node()->now(); |
| 588 | + msg.header.frame_id = ""; |
| 589 | + |
| 590 | + // Yaw, Pitch, Roll (in degrees as received) |
| 591 | + msg.imu.ypr.x = imu_packet.yaw(); |
| 592 | + msg.imu.ypr.y = imu_packet.pitch(); |
| 593 | + msg.imu.ypr.z = imu_packet.roll(); |
| 594 | + |
| 595 | + // Linear acceleration (in m/s²) |
| 596 | + msg.imu.linear_acceleration.x = imu_packet.acc_x(); |
| 597 | + msg.imu.linear_acceleration.y = imu_packet.acc_y(); |
| 598 | + msg.imu.linear_acceleration.z = imu_packet.acc_z(); |
| 599 | + |
| 600 | + // Angular velocity (in degrees/second as received) |
| 601 | + msg.imu.angular_velocity.x = imu_packet.gyr_x(); |
| 602 | + msg.imu.angular_velocity.y = imu_packet.gyr_y(); |
| 603 | + msg.imu.angular_velocity.z = imu_packet.gyr_z(); |
| 604 | + |
| 605 | + // Compass (in raw units as received) |
| 606 | + msg.imu.compass.x = imu_packet.mag_x(); |
| 607 | + msg.imu.compass.y = imu_packet.mag_y(); |
| 608 | + msg.imu.compass.z = imu_packet.mag_z(); |
| 609 | + |
| 610 | + // Orientation quaternion |
| 611 | + msg.imu.orientation.w = imu_packet.q_w(); |
| 612 | + msg.imu.orientation.x = imu_packet.q_x(); |
| 613 | + msg.imu.orientation.y = imu_packet.q_y(); |
| 614 | + msg.imu.orientation.z = imu_packet.q_z(); |
| 615 | + |
| 616 | + realtime_imu_publisher_->unlockAndPublish(); |
| 617 | + } |
| 618 | +} |
| 619 | + |
421 | 620 | void VescHardware::publishVescState( |
422 | 621 | const vesc_driver::VescPacketValues & values_packet) |
423 | 622 | { |
@@ -495,6 +694,10 @@ void VescHardware::vescPacketCallback( |
495 | 694 | fw_packet->paired() ? "yes" : "no" |
496 | 695 | ); |
497 | 696 | } |
| 697 | + } else if (packet->name() == "ImuData") { |
| 698 | + const auto *imu_packet = |
| 699 | + dynamic_cast<const vesc_driver::VescPacketImu *>(packet.get()); |
| 700 | + processImuPacket(imu_packet); |
498 | 701 | } |
499 | 702 | } |
500 | 703 |
|
|
0 commit comments