GazeController.cpp
Go to the documentation of this file.
1#include "GazeController.h"
2
3#include <algorithm>
4#include <cmath>
5#include <string>
6
7#include <Ice/Current.h>
8#include <IceUtil/Time.h>
9
10#include <VirtualRobot/Nodes/RobotNode.h>
11#include <VirtualRobot/Robot.h>
12#include <VirtualRobot/RobotNodeSet.h>
13#include <VirtualRobot/VirtualRobot.h>
14
17#include <ArmarXCore/interface/observers/ObserverInterface.h>
18#include <ArmarXCore/interface/observers/VariantBase.h>
20
24#include <RobotAPI/components/units/RobotUnit/RobotUnit.h> // FIXME avoid thi>
26#include <RobotAPI/interface/aron/Aron.h>
27#include <RobotAPI/interface/units/RobotUnit/NJointController.h>
28#include <RobotAPI/interface/visualization/DebugDrawerInterface.h>
30
33#include <armarx/view_selection/gaze_controller/atan2/aron/ControllerConfig.aron.generated.h>
38
40{
41
44
46 const NJointControllerConfigPtr& config,
48 {
49 ARMARX_RT_LOGF("Creating gaze controller");
50 // config
51 ConfigPtrT cfg = ConfigPtrT::dynamicCast(config);
53 // ARMARX_CHECK_EXPRESSION(!cfg->nodeSetName.empty());
54
55 ARMARX_CHECK_EXPRESSION(robotUnit);
56
57 const armarx::view_selection::gaze_controller::atan2::arondto::Config configData =
58 arondto::Config::FromAron(cfg->config);
59
60 // robot
61 ARMARX_RT_LOGF("Setting up nodes");
62 {
63 _rtRobot = useSynchronizedRtRobot();
64 _rtRobot->setThreadsafe(false);
65
66 ARMARX_CHECK_NOT_NULL(_rtRobot);
67 _rtGazeNodeName = configData.params.gazeRootNodeName;
68 _rtCameraNode = _rtRobot->getRobotNode(configData.params.cameraNodeName);
69 _rtPitchNode = _rtRobot->getRobotNode(configData.params.pitchNodeName);
70 _rtYawNode = _rtRobot->getRobotNode(configData.params.yawNodeName);
71 // _rtTorsoNode = _rtRobot->getRobotNode(_config->torsoNodeName);
72 ARMARX_CHECK_NOT_NULL(_rtCameraNode);
73 ARMARX_CHECK_NOT_NULL(_rtPitchNode);
74 ARMARX_CHECK_NOT_NULL(_rtYawNode);
75 // ARMARX_CHECK_NOT_NULL(_rtTorsoNode);
76 // joint positions to be controlled
78 configData.params.pitchNodeName, ControlModes::Position1DoF);
79 ARMARX_CHECK_NOT_NULL(_rtPitchCtrlTarget);
80 //_rtPitchCtrlPos = &(_pitchCtrlTarget->position);
82 configData.params.yawNodeName, ControlModes::Position1DoF);
83 ARMARX_CHECK_NOT_NULL(_rtYawCtrlTarget);
84 //_rtYawCtrlPos = &(yawCtrlTarget->position);
85 }
86
87 ARMARX_RT_LOGF("Nodes set up successfully");
88 }
89
91
92 void
93 GazeController::updateConfig(const ::armarx::aron::data::dto::DictPtr& dto,
94 const Ice::Current& /*iceCurrent*/)
95 {
96 auto updateConfigDto = arondto::Config::FromAron(dto);
97
98 Config config;
99 fromAron(updateConfigDto, config);
100
101 _statusReporter.setTarget(config.targetId, config.target.name);
102
103 setControlStruct(config);
104 }
105
106 void
111
112 void
118
119 namespace
120 {
121 /**
122 * @brief The gaze ray for a pair of joint angles, inverting the parametrization rtRun uses.
123 *
124 * rtRun takes yaw = -atan2(x, y) and then pitch = -atan2(z', y') on the point rotated by
125 * -yaw about Z, which puts it in the y-z plane. Going back the same way keeps the residual
126 * in the controller's own convention, so no assumption about the camera's optical axis is
127 * needed here.
128 */
129 Eigen::Vector3f
130 gazeDirection(const float yaw, const float pitch)
131 {
132 const Eigen::Vector3f inYawPlane(0.F, std::cos(pitch), -std::sin(pitch));
133
134 return Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitZ()) * inYawPlane;
135 }
136 } // namespace
137
138 std::string
143
144 void
145 GazeController::rtRun(const IceUtil::Time& /*sensorValuesTimestamp*/,
146 const IceUtil::Time& /*timeSinceLastIteration*/)
147 {
148 const float currentYawAngle = _rtYawNode->getJointValue();
149 const float currentPitchAngle = _rtPitchNode->getJointValue();
150
151 // report debugging variables
152 _publishCurrentYawAngle = currentYawAngle;
153 _publishCurrentPitchAngle = currentPitchAngle;
154
155 auto const gazeTarget = rtGetControlStruct().target;
156 auto target = gazeTarget.position;
157 if (target.frame.empty())
158 {
159 target.frame = GlobalFrame;
160 }
161
162 target.changeFrame(_rtRobot, _rtGazeNodeName); // Change frame to gaze root frame.
163 Eigen::Vector3f targetPoint = target.toEigen(); // In gaze root frame.
164
165 // Against the requested target, not the clamped one: if it is out of reach the residual
166 // has to stay large, and targetReachable below says why.
167 const Eigen::Vector3f targetDirection = targetPoint;
168 const float targetDistance = targetPoint.norm();
169
170 // compute spherical targets: pitch and yaw
171 float targetYawAngle = -std::atan2(targetPoint.x(), targetPoint.y());
172
173 // The pitch depends on the yaw because in most but one case the head reference frame
174 // (Gaze_Root) is not axis aligned with the robot root frame. We need to rotate the target
175 // point by the negative yaw angle to account for that. If this is not done, the head will
176 // show a sinusoidal movement when the platform is rotated.
177 targetPoint = Eigen::AngleAxisf(-targetYawAngle, Eigen::Vector3f::UnitZ()) * targetPoint;
178
179 // Limit yaw range to avoid damage.
180 const bool yawTargetReachable = _rtYawNode->checkJointLimits(targetYawAngle);
181 if (not yawTargetReachable)
182 {
183 targetYawAngle = std::clamp(
184 targetYawAngle, _rtYawNode->getJointLimitLow(), _rtYawNode->getJointLimitHigh());
185 }
186
187 float targetPitchAngle = -std::atan2(targetPoint.z(), targetPoint.y());
188
189 // check reachability
190 const bool pitchTargetReachable = _rtPitchNode->checkJointLimits(targetPitchAngle);
191 if (not pitchTargetReachable)
192 {
193 targetPitchAngle = std::clamp(targetPitchAngle,
194 _rtPitchNode->getJointLimitLow(),
195 _rtPitchNode->getJointLimitHigh());
196 }
197
198 // report debugging variables
199 _publishTargetYawAngle = targetYawAngle;
200 _publishTargetPitchAngle = targetPitchAngle;
201
202 const GazeResidual residual =
203 computeGazeResidual(gazeDirection(currentYawAngle, currentPitchAngle),
204 targetDirection,
205 targetDistance);
206 _publishAngularError = residual.angularError;
207 _publishLateralError = residual.lateralError;
208 _statusReporter.update(
209 rtGetControlStruct().targetId, residual, yawTargetReachable and pitchTargetReachable);
210
211 // update control positions
212 _rtYawCtrlTarget->position = targetYawAngle;
213 _rtPitchCtrlTarget->position = targetPitchAngle;
214 }
215
216 void
221
222 void
227
228 void
231 const DebugObserverInterfacePrx& debugObserver)
232 {
233 const float currentPitchAngle = _publishCurrentPitchAngle;
234 const float currentYawAngle = _publishCurrentYawAngle;
235 const float targetPitchAngle = _publishTargetPitchAngle;
236 const float targetYawAngle = _publishTargetYawAngle;
237
238 StringVariantBaseMap datafields;
239 datafields["currentPitchAngle"] = new Variant(currentPitchAngle);
240 datafields["currentYawAngle"] = new Variant(currentYawAngle);
241 datafields["targetPitchAngle"] = new Variant(targetPitchAngle);
242 datafields["targetYawAngle"] = new Variant(targetYawAngle);
243 datafields["angularError"] = new Variant(_publishAngularError.load());
244 datafields["lateralError"] = new Variant(_publishLateralError.load());
245
246 if (_tripRt2NonRt.updateReadBuffer())
247 {
248 gaze_targets::GazeTarget target = _tripRt2NonRt.getReadBuffer();
249 // this->publishTarget(target);
250 }
251
252 debugObserver->setDebugChannel(getInstanceName(), datafields);
253
254 _statusReporter.report(_statusTopic);
255 }
256
258 GazeController::getConfig(const ::Ice::Current&)
259 {
260 ARMARX_ERROR << "NYI";
261 return nullptr;
262 }
263
264} // namespace armarx::view_selection::gaze_controller::atan2
#define ARMARX_RT_LOGF(...)
void offeringTopic(const std::string &name)
Registers a topic for retrival after initialization.
TopicProxyType getTopic(const std::string &name)
Returns a proxy of the specified topic.
const VirtualRobot::RobotPtr & useSynchronizedRtRobot(bool updateCollisionModel=false)
Requests a VirtualRobot for use in rtRun *.
std::string getInstanceName(const Ice::Current &=Ice::emptyCurrent) const final override
ControlTargetBase * useControlTarget(const std::string &deviceName, const std::string &controlMode)
Declares to calculate the ControlTarget for the given ControlDevice in the given ControlMode when rtR...
The Variant class is described here: Variants.
Definition Variant.h:224
std::string getClassName(const Ice::Current &=Ice::emptyCurrent) const override
::armarx::aron::data::dto::DictPtr getConfig(const ::Ice::Current &=::Ice::emptyCurrent) override
GazeController(RobotUnitPtr robotUnit, const NJointControllerConfigPtr &config, const VirtualRobot::RobotPtr &robot)
void onPublish(const SensorAndControl &, const DebugDrawerInterfacePrx &, const DebugObserverInterfacePrx &) override
void rtPostDeactivateController() override
This function is called after the controller is deactivated.
void rtRun(const IceUtil::Time &sensorValuesTimestamp, const IceUtil::Time &timeSinceLastIteration) override
TODO make protected and use attorneys.
void updateConfig(const ::armarx::aron::data::dto::DictPtr &dto, const Ice::Current &iceCurrent=Ice::emptyCurrent) override
void rtPreActivateController() override
This function is called before the controller is activated.
control::ConfigurableNJointControllerConfigPtr ConfigPtrT
Business Object (BO) class of GazeTarget.
Definition GazeTarget.h:22
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:194
std::string const GlobalFrame
Variable of the global coordinate system.
Definition FramedPose.h:65
std::shared_ptr< class Robot > RobotPtr
Definition Bus.h:19
::IceInternal::Handle< Dict > DictPtr
const simox::meta::EnumNames< ControllerType > ControllerTypeNames
const armarx::NJointControllerRegistration< GazeController > RegistrationControllerGazeController(common::ControllerTypeNames.to_name(common::ControllerType::GazeControllerAtan2))
void fromAron(const arondto::Config &dto, Config &bo)
const std::string GazeControllerStatusTopicName
Topic the low-level gaze controllers report their residual on.
Definition constants.h:10
GazeResidual computeGazeResidual(const Eigen::Vector3f &currentGazeDirection, const Eigen::Vector3f &targetDirection, const float targetDistance)
The gaze residual, in a form that means the same thing for every controller variant.
::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
How far the gaze currently is from a target.
float lateralError
Lateral miss distance at target range [mm].
float angularError
Angle between the gaze ray and the direction to the target [rad].
armarx::view_selection::gaze_controller::Target target
std::int64_t targetId
Identifies this control target; echoed back in the status reports.