ZeroMQController.cpp
Go to the documentation of this file.
1#include "ZeroMQController.h"
2
3#include <Eigen/Dense>
4
7#include <ArmarXCore/interface/observers/ObserverInterface.h>
8#include <ArmarXCore/interface/observers/VariantBase.h>
10
11// for ARMARX_RT_LOGF_WARN
12#include <chrono>
13#include <string>
14
15#include <boost/date_time/posix_time/posix_time.hpp>
16
17#include <Ice/Current.h>
18#include <Ice/Object.h>
19
20#include <VirtualRobot/VirtualRobot.h>
21
25#include <RobotAPI/interface/units/RobotUnit/NJointController.h>
26#include <RobotAPI/interface/visualization/DebugDrawerInterface.h>
28
36
37#include <nlohmann/json_fwd.hpp>
38#include <zmq.h>
39#include <zmq.hpp>
40
42{
43 namespace base = armarx::control::njoint_controller::task_space;
44
45 armarx::NJointControllerRegistration<
50
56
62
68
70 base::NJointTaskspaceCollisionAvoidanceMixedImpedanceVelocityController>>
74
75 // Implement getClassName() method for templated NJointZeroMQTaskspaceController
76 template <>
77 std::string
84
85 template <>
86 std::string
93
94 template <>
95 std::string
102
103 template <>
104 std::string
106 class base::NJointTaskspaceCollisionAvoidanceImpedanceController>::
107 getClassName(const Ice::Current&) const
108 {
111 }
112
113 template <>
114 std::string
116 class base::NJointTaskspaceCollisionAvoidanceMixedImpedanceVelocityController>::
117 getClassName(const Ice::Current&) const
118 {
121 }
122
123 template <typename NJointTaskspaceController>
126 const armarx::NJointControllerConfigPtr& config,
127 const VirtualRobot::RobotPtr& robot) :
128 NJointTaskspaceController{robotUnit, config, robot}, context(1), socket(context, ZMQ_PULL)
129 {
130 init();
131 }
132
133 template <>
135 class base::NJointTaskspaceCollisionAvoidanceImpedanceController>::
136 NJointZeroMQTaskspaceController(const armarx::RobotUnitPtr& robotUnit,
137 const armarx::NJointControllerConfigPtr& config,
138 const VirtualRobot::RobotPtr& robot) :
139 base::NJointTaskspaceImpedanceController{robotUnit, config, robot},
140 base::NJointTaskspaceCollisionAvoidanceImpedanceController{robotUnit, config, robot},
141 context(1),
142 socket(context, ZMQ_PULL)
143 {
144 init();
145 }
146
147 template <>
148 NJointZeroMQTaskspaceController<
149 class base::NJointTaskspaceCollisionAvoidanceMixedImpedanceVelocityController>::
150 NJointZeroMQTaskspaceController(const armarx::RobotUnitPtr& robotUnit,
151 const armarx::NJointControllerConfigPtr& config,
152 const VirtualRobot::RobotPtr& robot) :
153 base::NJointTaskspaceMixedImpedanceVelocityController{robotUnit, config, robot},
154 base::NJointTaskspaceCollisionAvoidanceMixedImpedanceVelocityController{robotUnit,
155 config,
156 robot},
157 context(1),
158 socket(context, ZMQ_PULL)
159 {
160 init();
161 }
162
163 // Method(s) of NJointZeroMQTaskspaceController in general
164 template <typename NJointTaskspaceController>
165 void
167 {
168 // Bind to ZeroMQ socket
169 int port = 5556; // TODO(@rietsch): Make configurable
170 std::string ip_address = "*";
171 std::string bind_address = "tcp://" + ip_address + ":" + std::to_string(port);
172 try
173 {
174 socket.bind(bind_address);
175 ARMARX_INFO << "Controller bound to " << bind_address << " through ZeroMQ";
176 }
177 catch (const zmq::error_t& e)
178 {
179 ARMARX_INFO << "ZeroMQ bind error: " << e.what();
180 return;
181 }
182
183 time_last_packet = std::chrono::steady_clock::now();
184 }
185
186 template <typename NJointTaskspaceController>
187 void
189 {
190 zmq::message_t config_msg;
191 bool received = socket.recv(&config_msg, ZMQ_DONTWAIT); // Try receive message, don't wait
192 if (received)
193 {
194 // Measure time passed since last packet was received
195 auto currentTime = std::chrono::steady_clock::now();
196 std::chrono::duration<double> elapsed_seconds = currentTime - time_last_packet;
197 frequency_ms = elapsed_seconds.count() * 1000;
198 time_last_packet = currentTime;
199
200 // Parse config message into nlohmann::json
201 std::string config_json_str(static_cast<char*>(config_msg.data()), config_msg.size());
202 nlohmann::json config_json = nlohmann::json::parse(config_json_str);
203
204 // Calculate latency (might be imprecise due to clocks being not being synchronized?)
205 if (config_json.contains("timestamp") && config_json["timestamp"].is_number())
206 {
207 auto arrival_time = std::chrono::duration<double>(
208 std::chrono::system_clock::now().time_since_epoch())
209 .count();
210 double sender_time = config_json["timestamp"];
211 latency_ms = (arrival_time - sender_time) * 1000; // Convert to milliseconds
212 config_json.erase(
213 "timestamp"); // Important! Remove any data that is not part of the config
214 }
215
216 // Read into config data structure
218 typename NJointTaskspaceController::ConfigDict receivedConfig;
219 receivedConfig.read(reader, config_json);
220 auto prevCfg = this->userConfig;
221 for (auto& pair : this->limb)
222 {
223 auto limbName = pair.first;
224 auto& limb = pair.second;
225
226 auto limbConfig = prevCfg.limbs.at(limbName);
227 auto limbConfigNew = receivedConfig.limbs.at(limbName);
228 limbConfig.desiredPose = limbConfigNew.desiredPose;
229 limb->bufferConfigUserToNonRt.getWriteBuffer() =
230 limbConfig; // Overwrite user config
231 limb->bufferConfigUserToNonRt.commitWrite();
232 }
233
234 // Update hands
235 if (this->hands)
236 {
237 this->hands->updateConfig(receivedConfig.hands);
238 }
239 }
240
241 auto [rtTargetSafe, rtSafe] = this->additionalTaskUpdateStatus();
243 if (not rtTargetSafe)
244 {
246 }
247 }
248
249 template <typename NJointTaskspaceController>
250 void
252 const SensorAndControl&,
254 const DebugObserverInterfacePrx& debugObs)
255 {
256 // Log metrics
257 StringVariantBaseMap datafields;
258 datafields["frequency_ms"] = new Variant(frequency_ms);
259 datafields["latency_ms"] = new Variant(latency_ms);
260 debugObs->setDebugChannel(getClassName(), datafields);
261
262 // Do limb publish
263 for (auto& pair : this->limb)
264 {
265 if (not pair.second->rtReady.load())
266 continue;
267 this->limbPublish(pair.second, debugObs);
268 }
269 }
270} // namespace armarx::control::njoint_controller::task_space
The Variant class is described here: Variants.
Definition Variant.h:224
NJointTaskspaceController(const RobotUnitPtr &robotUnit, const NJointControllerConfigPtr &config, const VirtualRobot::RobotPtr &)
Definition Base.cpp:138
typename NJointTaskspaceControllerType::ConfigDict ConfigDict
Definition Base.h:64
void limbPublish(ArmPtr &arm, const DebugObserverInterfacePrx &debugObs)
Definition Base.cpp:819
void onPublish(const SensorAndControl &, const DebugDrawerInterfacePrx &, const DebugObserverInterfacePrx &) override
NJointZeroMQTaskspaceController(const armarx::RobotUnitPtr &robotUnit, const armarx::NJointControllerConfigPtr &config, const VirtualRobot::RobotPtr &robot)
std::string getClassName(const Ice::Current &=Ice::emptyCurrent) const final
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
std::shared_ptr< class Robot > RobotPtr
Definition Bus.h:19
const simox::meta::EnumNames< ControllerType > ControllerTypeNames
Definition type.h:170
armarx::NJointControllerRegistration< NJointSharedMemoryTaskspaceController< base::NJointTSMixImpVelColController > > registrationControllerNJointCollisionAvoidanceTSMixedImpedanceVelocityController(armarx::control::common::ControllerTypeNames.to_name(armarx::control::common::ControllerType::SharedMemoryTSMixImpVelCol))
armarx::NJointControllerRegistration< NJointSharedMemoryTaskspaceController< base::NJointTSMixImpVelController > > registrationControllerNJointTSMixedImpedanceVelocityController(armarx::control::common::ControllerTypeNames.to_name(armarx::control::common::ControllerType::SharedMemoryTSMixImpVel))
armarx::NJointControllerRegistration< NJointSharedMemoryTaskspaceController< base::NJointTSImpController > > registrationControllerNJointTSImpedanceController(armarx::control::common::ControllerTypeNames.to_name(armarx::control::common::ControllerType::SharedMemoryTSImp))
armarx::NJointControllerRegistration< NJointSharedMemoryTaskspaceController< base::NJointTSVelController > > registrationControllerNJointTSVelocityController(armarx::control::common::ControllerTypeNames.to_name(armarx::control::common::ControllerType::SharedMemoryTSVel))
armarx::NJointControllerRegistration< NJointSharedMemoryTaskspaceController< base::NJointTSImpColController > > registrationControllerNJointCollisionAvoidanceTSImpedanceController(armarx::control::common::ControllerTypeNames.to_name(armarx::control::common::ControllerType::SharedMemoryTSImpCol))
::IceInternal::ProxyHandle<::IceProxy::armarx::DebugObserverInterface > DebugObserverInterfacePrx
std::map< std::string, VariantBasePtr > StringVariantBaseMap
IceUtil::Handle< class RobotUnit > RobotUnitPtr
Definition FTSensor.h:34
::IceInternal::ProxyHandle<::IceProxy::armarx::DebugDrawerInterface > DebugDrawerInterfacePrx
detail::ControlThreadOutputBufferEntry SensorAndControl